IPv6 для профессионалов

Вспомогательные механизмы

Разбить на страницы
Показывать лекцию целиком

Фрагментировать или нет?

Механизм управления фрагментацией IPv6 требует дополнительных соображений и комментариев, потому что он существенным образом отличается от его аналога в IPv4. Главное отличие, конечно же, в том, что фрагментацию IPv6 может вести только источник, хотя информация об оптимальной длине пакета скрыта дальше в сети.

В первую очередь, такая особенность механизма фрагментации IP влияет на работу транспортных протоколов. Начнем с хрестоматийного примера TCP. Раньше, в среде IPv4, перед TCP было два основных пути:

  • сбрасывать флаг DF и делить поток прикладных данных на сегменты произвольного размера, поручив их дальнейшую фрагментацию сети;
  • устанавливать флаг DF и выполнять PMTUD, избегая фрагментации IP и пытаясь оптимизировать размер сегмента.
  • Более хитроумная реализация TCP могла пробовать второй путь, но переключаться на первый, если PMTUD не работает, например, из-за слишком строгой фильтрации пакетов ICMP в сети.

    В среде IPv6 первый путь полностью закрыт, потому что флаг DF неявно установлен всегда, а пакеты, длина которых больше PMTU, не достигнут адресата ни при каких условиях. Теперь сам источник пакета должен выбрать его подходящий размер. Как следствие, при переходе к IPv6 растет важность правильной и надежной работы PMTUD.

    В первую очередь, этот факт касается сетевых администраторов, увлеченных борьбой с "нежелательным" трафиком ICMP.

    Тем не менее, даже ненадежная сигнализация ICMP не мешает проводить PMTUD, используя сквозной принцип "Надейся в первую очередь на себя, затем на удаленную сторону, и только в последнюю на транзитные узлы". и некоторую эвристику. Например, если "тройное рукопожатие" TCP проходит успешно, но затем оказывается, что полноразмерные сегменты куда-то пропадают — удаленная сторона не квитирует новые данные, — то здесь явно замешан PMTU, и источнику TCP следует понизить размер сегмента. Хотя подсказок в виде очередного значения MTU он не получит, его выручит двоичный поиск, ограниченный снизу заведомо "габаритным" значением (1280 байт в IPv6), а сверху — MTU локального интерфейса. Пробное значение можно повышать, пока сегменты достигают удаленной стороны. Как только оценка окажется завышенной, сегменты перестанут проходить, и надо будет перейти к понижению пробной величины. На этой идее основана так называемая процедура PMTUD уровня пакетизации [RFC 4821].

    Тем не менее, PMTUD — не абсолютная необходимость даже в среде IPv6. Теперь минимальный размер MTU равен относительно большой величине, 1280 байт, так что примитивная реализация TCP может себе позволить ориентироваться на эту постоянную. Так же может поступить и более сложный вариант TCP, если он обнаружит, что PMTUD все-таки не работает.

    Итак, в среде IPv6 перед TCP открываются следующие пути:

  • сегментировать поток, исходя из величины PMTU 1280 байт;
  • оптимизировать размер сегмента с помощью PMTUD.
  • TCP — это пример транспортного протокола, который может плавно варьировать размер сегмента и поэтому не нуждается во фрагментации на уровне IPv6. Совсем другую картину мы увидим, если перенесем наш взгляд на блочные транспортные протоколы и их потребителей.

    Представим себе, что некий прикладной протокол работает поверх UDP. В этом случае размер исходящей дейтаграммы задает приложение, и размер этот может достигать внушительной величины. Это не наша фантазия: в реально существующих протоколах, например, в NFS, возникают сообщения длиной порядка нескольких тысяч байт. Как подобная дейтаграмма достигнет адресата?

    Первый подход мы уже нащупали: приложение может посредством сетевого API попросить локальный модуль IPv6, чтобы тот фрагментировал пакеты исходя из MTU 1280 байт. Это не самый эффективный, зато простой и надежный путь: из-за своего чрезмерного размера пакеты теряться заведомо не будут.

    Тем не менее, пакеты теряются еще по ряду причин, и прикладной протокол должен быть устойчив (или безразличен) к таким потерям. Данное свойство прикладных протоколов, работающих поверх UDP, позволяет оптимизировать размер фрагмента IPv6. Для этого надо, чтобы модуль IPv6 сам выполнял PMTUD. Как это будет работать? Например, так:

  • приложение шлет данному адресату свое первое большое сообщение;
  • модуль IPv6 фрагментирует составленный пакет, взяв первым приближением PMTU величину MTU выходного интерфейса;
  • возможно, такие фрагменты не пройдут всю трассу; но модуль IPv6 примет извещения ICMPv6 "пакет слишком велик" и получит второе приближение PMTU;
  • Это приближение будет довольно точным благодаря обязательному значению MTU в сообщении "пакет слишком велик". В классическом IPv4 источнику приходилось вести вместо этого поиск вслепую, так как маршрутизатор не подсказывал значения MTU. Впрочем, двоичный поиск сходится довольно быстро. Подумайте, в каком случае двоичный поиск вслепую выиграет у поиска с подсказкой маршрутизатора. (Например, когда число шагов достаточно велико, а MTU каждого нового шага меньше предыдущего - "телескопическая трасса").

  • приложение обнаружит потерю сообщения, например, не получив отклика удаленной стороны в течение какого-то времени, и повторит сообщение (или пошлет следующее, если в данном протоколе потери можно игнорировать);
  • модуль IPv6 фрагментирует пакет, используя новое приближение PMTU;
  • возможно, фрагменты опять не пройдут; но в результате модуль IPv6 снова уточнит величину PMTU;
  • приложение еще раз повторит сообщение (или пошлет следующее)…
  • И так до тех пор, пока приближение PMTU не станет меньше или равно его истинному значению для данной трассы. После этого фрагментированные пакеты начнут доходить до адресата, и прикладной сеанс продолжится. Чтобы подобная схема работала, модуль IPv6 должен кэшировать значения PMTU для своих недавних адресатов. Подходящее место для этих сведений — кэш адресатов, DC [§5.2 RFC 1981].

    Как мы помним, трасса — это путь по сети, от источника к адресату через транзитные узлы, который проходит (или прошел бы) пакет в данный момент времени. Для простоты мы сейчас не будем говорить о маршрутизации от источника, когда трассу выбирает источник пакета, и маршрутизации согласно политике, когда выбор следующего шага основан на произвольных характеристиках пакета и внешних параметрах. Тогда трасса IP зависит от узла-источника и от адреса назначения пакета, а также от настроек источника и каждого транзитного узла в момент обработки пакета (грубо говоря, от их таблиц маршрутов). Конечно, в "живой" сети трасса может со временем меняться вместе с настройками источника и транзитных узлов. Однако, пока сеть стабильна, трасса остается постоянной хотя бы какое-то время. Именно поэтому источник имеет право кэшировать значения PMTU, используя в роли ключа адрес назначения.

    Однозначность соответствия между адресатом и наблюдаемой величиной PMTU может оказаться нарушена, если сеть использует ECMP и пакеты одному адресату могут приходить по разным путям с неодинаковым значением PMTU. Между тем, существующие реализации хоста IP кэшируют одно значение PMTU на адресата еще со времен IPv4, а IPv6 лишь стандартизует эту практику. Поэтому надо с осторожностью применять распределяющие хэш-функции ECMP, зависящие от чего-либо сверх адреса назначения пакета, например, номера вышестоящего протокола или портов TCP/UDP. (Обсудите самостоятельно, насколько допустимо, чтобы такая хэш-функция зависела от адреса источника пакета.)

    Еще один путь открывается, когда прикладной протокол позволяет варьировать размер сообщений, например, если приложение сегментирует некий поток байтов или более крупных единиц данных. В этом случае приложение могло бы выбрать такой размер своих сообщений, чтобы их фрагментация на данной сетевой трассе не требовалась . Этот прием иногда называют "семантическая фрагментация", так как он требует хотя бы минимального понимания, что значат передаваемые данные. Например, мы его уже встретили в MLDv2 (§6.4). Но, чтобы приложение узнало подходящий размер, требуется обратная связь от локального модуля IPv6 к приложению. Ее тоже можно обеспечить в рамках сетевого API. Скажем, приложение может запрашивать у модуля IPv6 текущее приближение PMTU для данного адресата. Или же модуль IPv6 может подсказывать приложению PMTU по своей инициативе, в виде служебной структуры , Это cmsghdr в терминах API сокетов Беркли. когда приложение ожидает ответа удаленной стороны, а вместо него приходит извещение ICMPv6 "пакет слишком велик".

    Наконец, оптимистичное приложение, которое верит в возможности PMTUD, захочет запретить модулю IPv6 фрагментацию своих исходящих пакетов. Такое приложение должно активно следить за текущим приближением PMTU и корректировать размер своих сообщений. Модуль IPv6 и приложение проводят PMTUD в паре: модуль IPv6 обрабатывает извещения ICMPv6 и оценивает PMTU, а приложение генерирует новые пробные пакеты согласно последней оценке.

    Именно эти идеи нашли отражение в расширенном API для IPv6, основанном на сокетах Беркли [RFC 3542]. Приложение управляет фрагментацией и PMTUD с помощью опций сокета из Табл. 9.1 на уровне IPPROTO_IPV6 [§11 RFC 3542]. Некоторые опции можно избирательно устанавливать для отдельных сообщений с помощью служебной структуры cmsghdr.

    Опции сокета, управляющие фрагментацией IPv6
    Опция Смысл
    IPV6_USE_MIN_MTU Использовать минимальный MTU (1280 байт)
    IPV6_PATHMTU Запросить приближение PMTU для подключенного сокета
    IPV6_RECVPATHMTU Разрешить возврат приближения PMTU из вызова recvfrom()

    Обсудите преимущества и недостатки альтернативного подхода к определению PMTU, когда каждый транзитный узел по трассе пакета записывает фактическое значение MTU в специальную пошаговую опцию, если ее текущее значение больше MTU [draft-park-pmtu-ipv6-option-header] , так что прошедший всю трассу короткий пакет содержит в себе точное значение PMTU.

    Еще один практический способ понизить долю фрагментированных пакетов — это не занижать MTU интерфейсов без необходимости [§5 RFC 2460]. Сегодня стандарт де-факто для подключения конечных пользователей на канальном уровне — это Ethernet, а он диктует значение MTU 1500 байт. Это же значение мы встречаем и в других канальных протоколах, например, в PPP [§2 RFC 1661]. В центре сети может понадобиться запас MTU сверх 1500 байт, чтобы пакеты от конечных пользователей можно было подвергать дополнительной инкапсуляции, например, туннелировать, не вызывая фрагментации. Конечно, в сложной сети с несколькими административными зонами выбрать для каждого канала оптимальное значение MTU может быть непросто. Тем не менее, опускать его ниже 1500 байт точно не стоит.

    Еще мы до сих пор не задали минимальный размер буфера сборки IPv6. Как мы помним, в IPv4 он составлял 576 байт. Теперь мы видим значение, которое лучше подходит современным условиям: 1500 байт. Иными словами, всякий узел IPv6 обязан собрать адресованный ему пакет, если длина пакета после сборки не превышает 1500 байт [§5 RFC 2460]. Какой вывод должны сделать вышестоящие протоколы? Не следует создавать пакеты длиннее 1500 байт, если нет уверенности, что адресат готов принимать их. Например, в TCP подобные сведения можно извлечь из опции MSS.

    В качестве упражнения вычислите наиболее вероятные длины полноразмерных сегментов TCP, которые можно будет увидеть в сети после перехода на IPv6. Не забудьте, что фактическая длина полноразмерного сегмента TCP может оказаться меньше значения опции MSS [RFC 6691]. (Подсказка: учтите возможность расширений из [RFC 1323].)

    Работа с множеством локальных адресов. Выбор адресов по умолчанию

    Как мы сказали в §2.5, у хоста IPv6 может быть несколько сетевых адресов. На самом деле, один адрес бывает только у хоста IPv6, который не подключен к сети, — это адрес обратной связи ::1. Если у хоста есть хотя бы один активный сетевой интерфейс кроме "петли", то число его адресов возрастает до двух, так как этому интерфейсу обязательно назначен внутриканальный адрес IPv6. А если интерфейс хоста получает еще и глобальный адрес IPv6, то адресов у хоста становится три. Как мы видим, три индивидуальных адреса — это минимум для хоста IPv6, подключенного к Internet.

    Однако три адреса — это еще не предел. Положим, у хоста всего один сетевой интерфейс. Он находится в разных зонах, и каждой зоне может отвечать, по меньшей мере, один локальный адрес. Хотя на практике мы встречаем только внутриканальную и глобальную области, адресация IPv6 вполне допускает области промежуточной величины и отвечающие им зоны.

    Мы помним из §2.4, что у всякого численного адреса IPv6 ровно одна область действия, а назначенный интерфейсу адрес принадлежит ровно одной зоне. В то же время, сетевой интерфейс находится в зонах всех возможных областей, независимо от назначенных ему адресов.

    Далее, сетевой интерфейс хоста может получить несколько адресов из одной зоны. Для чего это может быть полезно? Адреса из одной подсети назначают, когда, например, функции хоста А переходят хосту Б, а хост А прекращает свое существование. В этом случае можно назначить хосту Б, кроме его собственного адреса, бывший адрес хоста А, и тогда не придется менять настройки других элементов сети: хост Б станет отвечать от имени хоста А, и никто не заметит изменений. Адреса из разных подсетей открывают путь к тому, чтобы использовать одновременные подключения к нескольким провайдерам Internet.

    Конечно, полноценная работа с несколькими подключениями — это существенно более сложная задача. Ее анализу посвящен [RFC 4177], а мы вкратце рассмотрим ее в §6.8 настоящей книги.

    Нетрудно предвидеть, что ситуация только усложнится, когда у хоста будет несколько активных сетевых интерфейсов. Каждый из них получит, помимо обязательного внутриканального адреса, ноль или больше адресов согласно адресному плану сети.

    Рассмотрим с этих позиций типовой сценарий: локальный хост Л по своей инициативе собирается послать пакет удаленному хосту У. Допустим, что приложение, запущенное на хосте Л, хочет направить в адрес У дейтаграмму UDP или установить с У соединение TCP. Чтобы составить такой пакет, сетевой стек хоста Л — не без помощи приложения и даже самого пользователя — должен перейти от абстрактных названий "Л" и "У" к паре адресов, источника и назначения пакета.

    Нам сейчас интересен именно случай, когда у хоста Л есть выбор, какие адреса указать в пакете. Если же хост Л отвечает на пакет хоста У, то свободы у него существенно меньше. В классических протоколах единственно верным поведением хоста Л будет использовать уже приведенные в пакете адреса, просто переставив их местами. В противном случае хост У, скорее всего, не примет такой ответ. Современные протоколы, например, Shim6 (§6.8) и SCTP, поддерживают больше одного адреса на каждом конце сеанса, но их выбором руководят соображения доступности адреса в данный момент времени. Нас же сейчас интересует первоначальный выбор на основании априорных данных, таких как тип и область действия адреса.

    Первым делом пользователь сообщает приложению какой-то идентификатор хоста У, пригодный к машинной обработке. Сегодня в этой роли мы используем имена хостов, так что приложение на входе получит имя У, такое как y.example.org. Обратившись к DNS, приложение получит список адресов, отвечающих данному имени. Как мы уже говорили в §2.11, в общедоступных зонах DNS мы надеемся увидеть только глобальные адреса; между тем, DNS — не единственная база имен, наряду с ней применяют NIS, WINS и даже файл HOSTS.TXT… К чему мы ведем нашу мысль? В общем случае приложение получит множество адресов хоста У, а область действия некоторых из них будет меньше глобальной.

    Со своей стороны, хосту Л тоже назначено некое множество адресов. Если во множестве "Л" всего N адресов, во множестве "У" всего M адресов, то можно составить упорядоченных пар адресов, потенциальных кандидатов.

    В некоторых случаях конкретную пару выбирает пользователь или какой-то внешний механизм, и тогда приложение посредством сетевого API просит стек хоста Л использовать именно эту пару адресов для связи с хостом У. Например, команда ping6 позволяет указать оба адреса явным образом в командной строке:

    ping6 -S FE80::1 2001:DB8::1

    Но обычно полагают, что приложение и сетевой стек сами сделают оптимальный выбор адресов по умолчанию, сокращенно DAS (default address selection), для передачи данных между локальным и удаленным хостами [RFC 6724].

    На самом деле, эта аббревиатура "DAS" встречается в литературе редко, но мы используем ее, чтобы не говорить "ВАПУ".

    В IPv4 процедура выбора сводилась к тому, что приложение перебирало удаленные адреса по одному, пытаясь установить сеанс. Например, так себя ведет клиент telnet:

    $ telnet host.example.org

    Trying 192.0.2.23...

    telnet: connect to address 192.0.2.23: Operation timed out

    Trying 198.51.100.7...

    telnet: connect to address 198.51.100.7: No route to host

    Trying 203.0.113.5...

    Connected to host.example.org.

    Escape character is '^]'.

    NetBSD/vax (host.example.org) (ttyp0)

    login:

    Сетевой стек, в свою очередь, подбирал локальный адрес для указанного приложением удаленного адреса. К примеру, стек BSD Unix находил по таблице маршрутов выходной интерфейс, а затем брал из списка адресов интерфейса самый первый G. R. Wright, W. R. Stevens. TCP/IP Illustrated, Volume 2: The Implementation. Addison Wesley, 1995. §22.8, "in_pcbconnect Function". . Эта процедура продолжалась до тех пор, пока приложению не удавалось установить сеанс или пока оно не исчерпывало список удаленных адресов.

    У процедуры DAS в среде IPv4 есть и нормативные документы [§3.3.4.3 RFC 1122 и §2.3 RFC 1123]. Поведение стека BSD вполне отвечает им: приложение перебирает адреса назначения, а стек подбирает адрес источника, руководствуясь таблицей маршрутов и найденным с ее помощью выходным интерфейсом для данного пакета или соединения. Для соединений TCP это выполнялось единожды, перед открытием соединения, чтобы потом изменения маршрутов не вызвали смену локального адреса и, как следствие, сбой протокола.

    Когда у каждого адреса появляется область действия, такая простая схема DAS может завести в тупик. Достаточно представить себе, что список адресов выходного интерфейса возглавляет внутриканальный адрес, назначенный раньше всех остальных. В этом случае хост Л сможет общаться только со своими соседями по каналу, даже если у него будет не только внутриканальный, но и глобальный адрес. Ведь в §2.4 мы сформулировали такое ключевое правило: чтобы пакет IPv6 достиг адресата, интерфейс назначения должен находиться в зоне адреса источника. Чтобы выполнить это правило, алгоритм DAS должен рассмотреть все локальные адреса и по очереди сопоставить каждый из них с удаленным адресом.

    Если никакой выбор адресов не способен удовлетворить это требование, то обмен данными между хостами Л и У попросту невозможен. Например, это произойдет, если у хоста Л есть только внутриканальные адреса, а все адреса хоста У находятся "вне канала" (см. §5.2). Однако в общем случае на стадии DAS нет сведений о том, где находится удаленный адрес, так как привести в действие механизм ND может только передача пакета, а она еще даже и не начиналась — источник только заполняет заголовок. Поэтому источнику доступны лишь априорные критерии выбора оптимальной пары адресов.

    Вторая половина того же правила из §2.4 гласит, что выходной интерфейс источника должен находится в зоне адреса назначения пакета. Так как из опубликованного в DNS адреса следует только его область, но не зона, источник вправе полагать, что этот адрес автоматически попадает в одну зону с выходным интерфейсом. Ведь мы постановили в §2.4, что сетевой интерфейс находится в одной зоне каждой возможной области! Например, если запрос DNS вернул внутрисайтовый адрес FEC0::1, то хост сделает вывод, что это его локальный сайт, а не сайт соседней организации. Поэтому надо быть вдвойне осторожным, публикуя адреса ограниченной области в DNS.

    Но какой именно критерий надо использовать при таком сопоставлении? Иными словами, какой из локальных адресов лучше всего подходит данному удаленному адресу? Чтобы критерий, который мы сейчас предложим, было проще алгоритмизировать, давайте сформулируем его в виде транзитивного отношения "лучше/хуже" между двумя потенциальными адресами источника, SA и SB, при данном адресе назначения D. Тогда список локальных адресов можно будет отсортировать по этому критерию, и наилучший адрес сам "всплывет" в начало списка.

    Конечно, в данном случае сортировку можно заменить обычным линейным поиском. Двоичный поиск этой задаче не подходит, так как порядок элементов зависит от переменного параметра D, а значит, список нельзя отсортировать раз и навсегда.

    Первым мы рассмотрим случай, когда область адреса назначения Обл(D) — не больше , То есть меньшей или равной величины. чем области обоих адресов источника, Обл(SA) и Обл(SB): Обл(D) $$\le$$ Обл(SA) $$\le$$ Обл(SB) или Обл(D) $$\le$$ Обл(SB) $$\le$$ Обл(SA). Тогда шансы на успех передачи при любом выборе довольно велики. В этом случае имеет смысл выбрать адрес источника, область которого поменьше, а значит поближе к области адреса назначения. Благодаря этому, скажем, диалог с удаленным внутриканальным адресом будет вестись с локального внутриканального адреса. Ведь политика безопасности удаленного хоста может быть более либеральной к внутриканальным адресам, чем к глобальным адресам.

    Когда область адреса назначения Обл(D) — промежуточной величины: Обл(SA) $$<$$ Обл(D) $$\le$$ Обл(SB) или Обл(SB) $$<$$ Обл(D) $$\le$$ Обл(SA), — то шансы на успех больше у адреса источника большей величины. И наоборот, у адреса источника меньшей величины они явно недостаточны, потому что его область строго меньше, чем область адреса назначения. (Случай, когда они равновелики, мы уже включили в предыдущий вариант.)

    Наконец, возможна ситуация, когда у адреса назначения область строго больше, чем у потенциальных адресов источника: Обл(SA) $$\le$$ Обл(SB) $$<$$ Обл(D) или Обл(SB) $$\le$$ Обл(SA) $$<$$ Обл(D). Но даже в этом случае еще не все потеряно. Ведь может оказаться, что интерфейс назначения находится в зоне адреса источника, например, глобальный адрес назначения находится "на канале" (§5.2). Чтобы повысить вероятность благоприятного исхода, надо выбрать адрес источника, область которого больше , а значит, потенциально охватывает больше интерфейсов. Это лучшее, что может сделать источник пакета в таких условиях.

    Чтобы лучше почувствовать эти правила, давайте изобразим их графически, отложив по одной оси область SA, а по другой — область SB, как показано на рис. 9.2. Тогда все пространство задачи можно разбить на четыре участка согласно нашим правилам выбора лучшего адреса источника. В двух из них выбор уже предопределен, а еще в двух зависит от отношения величин областей SA и SB между собой.

    (рис 9.2) Выбор адреса источника по его области — начальная схема

    Если через начало координат этого графика провести прямую с угловым коэффициентом 1 (см. рис. 9.3), то она разобьет все пространство задачи на две половины так, что в верхней область SA будет меньше, чем область SB, а в нижней — наоборот. Фактически, эта прямая может провести для нас выбор лучшего адреса там, где он все еще условный. Поэтому мы можем пересмотреть нашу графическую схему так, что на каждом участке сразу указан окончательный выбор адреса источника.

    (рис 9.3) Выбор адреса источника по его области — промежуточная схема

    На первый взгляд у нас получилось шесть разных участков. Однако, присмотревшись внимательнее, мы заметим, что их можно свести к четырем (см. рис. 9.4), поскольку у двух участков SA есть общая граница, и то же самое справедливо для двух участков SB.

    (рис 9.4) Выбор адреса источника по его области — окончательная схема

    А уже руководствуясь последней схемой, нетрудно записать оптимальный алгоритм выбора [§5 RFC 6724]:

    Если Обл(SA) $$<$$ Обл(SB) то:

    Если Обл(SA) $$<$$ Обл(D) то:

    Предпочесть SB.

    Иначе:

    Предпочесть SA.

    Иначе Если Обл(SA) $$>$$ Обл(SB) то:

    Если Обл(SB) $$<$$ Обл(D) то:

    Предпочесть SA.

    Иначе:

    Предпочесть SB.

    Иначе:

    Области SA и SB равны - ответ не определен.

    Пусть читатель докажет, что такой критерий сравнения транзитивен: если SA лучше SB, а SB лучше SC, то SA лучше SC.

    Как быть, если области SA и SB окажутся равны? В этом случае придется применить дополнительные критерии выбора. Так, один из них можно связать с жизненным циклом локального адреса IPv6. Такой адрес может устареть, а выбрать устаревший локальный адрес можно, только если нет равноценного ему предпочтительного адреса.

    Еще один критерий мы найдем, если попытаемся ответить на вот какой вопрос. Мы обнаружили в §2.5, что для выполнения своей основной функции , то есть продвижения транзитных пакетов, маршрутизатору достаточно внутриканальных адресов. Действительно, если каждый активный сетевой интерфейс маршрутизатора получит внутриканальный адрес, то его соседи по каналу смогут ссылаться на него как следующий шаг. Кроме того, маршрутизатор сможет участвовать на этом канале в розыске соседей и маршрутизаторов (§5), а также играть роль генератора запросов MLD (§6.4), потому что при участии в работе этих протоколов маршрутизатор не просто может, а даже обязан слать свои сообщения с внутриканального адреса.

    Однако есть как минимум один случай, когда маршрутизатору может понадобиться адрес большей области действия. Представьте себе, что маршрутизатор попытался продвинуть транзитный пакет в канал, MTU которого меньше длины пакета. В этом случае пакет пропадает, а маршрутизатору надо выслать извещение "пакет слишком велик" в адрес источника пакета. Вообще говоря, область этого адреса может быть любой, в том числе глобальной.

    Нетрудно видеть, к чему мы клоним. Даже маршрутизатору иногда приходится выполнять функции хоста, когда он создает пакет с извещением ICMPv6. Высылая такое извещение, он направляет его по адресу источника пакета-виновника. Чтобы такое извещение дошло, его собственный адрес источника должен быть подходящей области действия. Например, если пакет-виновник был послан с глобального адреса, то извещению в общем случае тоже необходим глобальный адрес источника. Ведь в противном случае нет никаких гарантий, что извещение достигнет адресата.

    Откуда маршрутизатор возьмет глобальный адрес? Придется ли назначить один глобальный адрес на каждый интерфейс маршрутизатора? На самом деле, будет достаточно ровно одного адреса на весь маршрутизатор, например, на интерфейсе "петля", если маршрутизатор сможет выбрать адрес источника из всех своих адресов, а не только из адресов выходного интерфейса. Ведь тогда главный критерий выбора, основанный на сравнении областей, автоматически предпочтет глобальный адрес источника.

    Обычному хосту, у которого есть несколько сетевых интерфейсов, тоже может пригодиться эта возможность выбирать из всех своих адресов, хотя ему рекомендовано ограничиться адресами выходного интерфейса [§4 RFC 6724].

    В этом разделе мы говорим "хост", а не "узел", чтобы подчеркнуть, что речь идет об одной из основных функций хоста IP — создавать новые пакеты на выходе. Вторая его функция — это потреблять адресованные ему пакеты на входе. В то же время, для маршрутизатора это нетипичные, дополнительные функции. Главная функция маршрутизатора — продвигать транзитные пакеты. Только особенности управления IP заставляют маршрутизатор заниматься иногда делом, свойственным хосту.

    С другой стороны, пока сам выходной интерфейс обладает подходящим адресом, следует предпочесть его. Это необходимо, в первую очередь, для правильной работы механизма переадресовок из §5.2. Ведь маршрутизатор вернет переадресовку, только если адрес источника в заголовке пакета указывает на соседа по каналу. Поэтому пусть это будет дополнительным критерием: если один адрес-кандидат принадлежит выходному интерфейсу, а другой нет, то побеждает первый из них.

    Также нам следует учесть, что расширения для конфиденциальности из §6.2 делят локальные адреса хоста на два класса: публичные и временные. Эти расширения по умолчанию должны быть выключены, так что их включение на хосте однозначно говорит о желании их активно использовать. Поэтому по умолчанию временный адрес выигрывает у публичного. Тем не менее, у приложения должна быть возможность запросить выбор публичного адреса посредством сетевого API.

    Наконец, если иерархии распределения адресов и маршрутизации IPv6 совпадают, то можно приближенно судить об удаленности двух интерфейсов друг от друга по длине общего префикса их адресов в одной зоне: чем длиннее общий префикс, тем, вероятно, короче трасса между интерфейсами . Поэтому есть определенный резон в том, чтобы предпочесть из двух кандидатов SA и SB тот, у которого длиннее общий префикс с адресом назначения D, по крайней мере, если более сильные критерии выбора дают "ничью". Например, если адрес SB в одной подсети с D, а адрес SA нет, то предпочесть стоит SB.

    Такое сравнение следует ограничить префиксом подсети локального адреса, потому как сравнение идентификаторов интерфейсов не даст требуемой информации. Грубо говоря, если первые 64 бита у сравниваемых адресов оказались равны, то сравнивать последующие биты не имеет смысла.

    Именно эти критерии и именно в таком порядке составляют костяк процедуры выбора адреса источника [§5 RFC 6724]. Кратко перечислим их еще раз:

  • сопоставление областей;
  • предпочтительный адрес против устаревшего;
  • адрес выходного интерфейса пакета против прочих адресов;
  • временный адрес против публичного;
  • сравнение длины префиксов, общих с адресом назначения.
  • Данная процедура возвращает самый подходящий из локальных адресов хоста, но только для заданного адреса назначения D. Обозначим ее как Ист(D) — источник для D.

    Полный список критериев [§5 RFC 6724] длиннее. Прежде всего, он предпочитает адрес источника, равный адресу назначения, в угоду локальной передаче пакетов в пределах хоста. Ведь равенство адресов источника и назначения — самый простой и надежный признак того, что пакет локальный. Прочие критерии помогают работе механизма мобильности узлов, а также позволяют вручную подстраивать DAS с помощью меток и численных весов. Все они, безусловно, важны на практике, но не несут фундаментального значения, и поэтому мы их подробно не рассматриваем. Читатель без труда разберется в них самостоятельно.

    Теперь нам пора вспомнить, что хост также располагает множеством потенциальных адресов назначения. Среди них нельзя выбрать ровно один, самый лучший, потому заранее неизвестно, по какому адресу в итоге ответит удаленная сторона. Хост обязан перебирать их до тех пор, пока попытка связи не приведет к успеху. Тем не менее, адреса назначения тоже не помешает упорядочить так, чтобы наиболее вероятные претенденты на успех оказались в начале списка.

    Значит, нам надо также сформулировать транзитивные критерии сравнения двух адресов назначения, DA и DB. Сделать это можно, опираясь на уже проверенные нами приемы, потому что удаленные адреса все равно видны хосту через призму его собственных сетевых интерфейсов и локально назначенных адресов. Неудивительно, что процедура выбора адреса назначения активно пользуется процедурой Ист(D) и сравнивает характеристики адресов источника, чтобы принять решение насчет потенциальных адресов назначения [§6 RFC 6724].

    Прежде всего, поддержим зонную архитектуру IPv6 таким критерием: выигрывает адрес назначения, адрес источника для которого имеет ту же область. Например, если Обл(DB) = Обл(Ист(DB)), а Обл(DA) $$\ne$$ Обл(Ист(DA)), то выигрывает DB. Безусловно, это не окончательный критерий, так как области могут совпадать, или не совпадать, у обоих кандидатов.

    Если предыдущий критерий дал ничью, то проигрывает адрес назначения, адрес источника для которого устарел. Например, если Ист(DB) устарел, а Ист(DA) нет, то выигрывает DA. Конечно, и здесь может быть ничья, если оба адреса источника находятся на одном этапе жизненного цикла.

    Следующим снова вступает в игру критерий, связанный с областями адресов. Область адреса назначения косвенно говорит о том, насколько длинен и тернист путь к сетевому интерфейсу назначения. Например, если адрес назначения — внутриканальный, то до соответствующего интерфейса всего один шаг, а если он — внутрисайтовый, то трасса до него, по крайней мере, заключена в пределах одной административной зоны, а значит, меньше вероятность сбоя из-за несогласованной политики на границах сетей. Поэтому, при прочих равных, есть смысл предпочесть адрес назначения, область которого меньшей величины.

    Если остальные критерии вернули ничью, то остается только предпочесть адрес назначения, у которого длиннее общий префикс с отвечающим ему адресом источника.

    И снова мы перечислили не все критерии выбора [§6 RFC 6724], обратив внимание только на самые основные из них.

    Нет ли в "созданной" нами процедуре DAS скрытых противоречий? Одна ее деталь, которая вызовет подозрение у любого инженера, знакомого с IPv4, — это использование информации о выходном интерфейсе пакета при выборе адреса источника, то есть до того, как окончательно установлен адрес назначения. Ведь реалии IPv4 таковы, что выбор выходного интерфейса (маршрутизация) проходит на основании адреса назначения пакета.

    Конечно, эту неувязку можно обойти, рассматривая выходной интерфейс как функцию от адреса назначения D, который процедура Ист(D) получает на вход в виде аргумента. В реалиях IPv4 это просто означало бы, что хост использует таблицу маршрутов, чтобы найти выходной интерфейс по адресу назначения.

    Тем не менее, в архитектуру IPv6 заложен иной подход, который у нас постепенно выкристаллизовывается. Путь к нему мы начали, работая над ND и SLAAC, когда установили, что все структуры хоста, включая список маршрутизаторов по умолчанию, привязаны к определенному сетевому интерфейсу, а выбор интерфейса из нескольких доступных вариантов находится за пределами задач ND (см. §5.2). Окончательного решения мы достигнем в §6.8, а сейчас только сделаем очередной необходимый шаг по направлению к нему.

    В §5.2 мы подробно обсудили, что модель хоста IPv6 существенно отличается от того, как устроены реализации хостов и маршрутизаторов IPv4. В то же время, у нас до сих пор нет модели маршрутизатора IPv6. Давайте допустим, что маршрутизатор IPv6 не столь существенно отличается от своего аналога IPv4 и его работа основана на такой же таблице маршрутов, ставящей в соответствие удаленным префиксам адреса следующего шага.

    Пока что единственный случай, когда процедура DAS нужна маршрутизатору IPv6, — это отправка извещения ICMPv6. В этом случае маршрутизатор IPv6 не встретит препятствий, потому что адрес назначения извещения однозначно известен: это адрес источника в пакете-виновнике, вызвавшем извещение. Поэтому маршрутизатор вполне способен определить выходной интерфейс извещения, руководствуясь своей таблицей маршрутов, и только затем провести выбор адреса источника.

    На зону, в которой находится адресат извещения, указывает входной интерфейс пакета-виновника. Если физический маршрутизатор состоит из нескольких виртуальных маршрутизаторов, то этот интерфейс также указывает на тот виртуальный маршрутизатор, чьей таблицей маршрутов надо воспользоваться при отправке извещения ICMPv6.

    Таким образом, случай многих адресов назначения на стадии DAS возникает только в работе хостов IPv6, и нам достаточно сосредоточить внимание на них. Как это ни странно звучит для эксперта по IPv4, но та архитектура хоста IPv6, к которой мы постепенно движемся, вполне допускает, что выбор выходного интерфейса происходит до маршрутизации пакета [§7 RFC 6724]. Например, приложение может само выбрать один из сетевых интерфейсов хоста и вести все свои сетевые диалоги строго через него.

    Обратите внимание, что процедура DAS сработает и в том случае, когда удаленные адреса-кандидаты — групповые, а не индивидуальные. Конечно, в этом случае пробное множество локальных адресов включает в себя только адреса, назначенные выходному интерфейсу, или другому интерфейсу в тот же самый канал, чтобы удовлетворить требованиям групповой маршрутизации [§4 RFC 6724].

    Безусловно, у приложения должна быть возможность управлять аспектами DAS, скажем, предпочесть публичные адреса временным. Для этого даже существует эталонный API [RFC 5014].

    Мы сознательно обошли вниманием тот случай, список удаленных адресов смешанный и содержит как адреса IPv6, так и адреса IPv4. Обсуждение механизмов перехода между IPv4 и IPv6 — очень важная тема, но она выходит за рамки нашего курса. Кроме того, для полноценного знакомства с ней не помешает сперва разобраться, как работает IPv6 в чистом виде. Тем не менее, полезно иметь в виду, что механизм DAS готов справиться и со смешанным случаем.

    Механизм DAS — это по-прежнему объект исследования, поскольку есть мнение, что в сложных сценариях он не всегда дает удовлетворительный результат [RFC 5220, RFC 5221].

    В завершение раздела соберем вместе и приведем все адреса, которые хост IPv6 обязан полагать своими собственными [§2.8 RFC 4291]:

  • адрес обратной связи ::1 (§2.3);
  • по одному обязательному внутриканальному адресу на каждый интерфейс (§2.5);
  • дополнительные индивидуальные адреса (§2.6) и адреса anycast (§5.3), настроенные на интерфейсах хоста вручную (§5.4.1) или автоматически (§5.4.2);
  • общепринятые групповые адреса "все узлы" (§2.8);
  • групповой адрес искомого узла для каждого назначенного хосту индивидуального адреса или адреса anycast (§5.1);
  • адреса всех групп, в которых хост на данный момент состоит (§2.8).

    А маршрутизатор IPv6, помимо всех предписанных хосту адресов, обязан считать своими собственными еще и такие:

  • адреса anycast "маршрутизатор подсети" на всех интерфейсах, где включена функция маршрутизатора (§6.1);
  • общепринятые групповые адреса "все маршрутизаторы" (§2.8).
  • Разрешение доменных имен в среде IPv6

    Предположим, серверы и клиенты DNS учли изменения, предложенные в §2.11. После этого приложение, обращаясь к DNS посредством сетевой библиотеки, может встречаться не только с адресами IPv4, но также с адресами IPv6. Более того, в переходный период вероятен "винегрет" из адресов, когда одному имени сопоставлены адреса обоих семейств. Например:

    www.example.org. IN A 192.0.2.77

    AAAA

    2001:db8:c001::beef

    Правильная работа с адресами из разных семейств пусть будет на совести программистов. Во-первых, они должны избавиться от ложного убеждения , будто все сетевые адреса — IPv4. Во-вторых, им следует принять во внимание правила выбора адресов, к которым мы пришли §6.6. Мы же сейчас позаботимся, чтобы приложение получило по своему запросу полный список адресов, отвечающих данному доменному имени.

    Между клиентом DNS в составе сетевой библиотеки и приложением обычно находится дополнительный уровень абстракции, который позволяет обращаться посредством одного простого API к разным системам разрешения имен: HOSTS.TXT, DNS, NIS и т.п. Скажем, в классическом API BSD Unix приложения использовали функцию gethostbyname(). Оттуда эта функция проникла в стандарт POSIX и в API Windows Sockets. К сожалению, функция gethostbyname() не готова к работе со смешанными семействами сетевых адресов: она может вернуть список адресов только из одного семейства. По умолчанию она возвращает адреса IPv4, а способ переключить ее на IPv6 системно-зависим.

    Например, реализация gethostbyname(), популяризованная пакетом BIND, возвращает адреса IPv6, если была выбрана опция RES_USE_INET6 библиотеки resolver. Эту опцию можно указать под ее текстовым именем inet6 в файле resolv.conf или переменной окружения RES_OPTIONS.

    По всей видимости, нужны новые функции для доступа к системам разрешения имен. Как показывает опыт, пересматривать общепринятый API еще труднее и опаснее, чем модифицировать сетевой протокол. Но раз уж нам придется это сделать, новые функции должны быть максимально гибкими и универсальными. По этой причине мы не можем просто добавить к уже существующей поддержке IPv4 поддержку IPv6.

    Новый API обязан одновременно работать с любыми семействами адресов. С точки зрения программиста, добиться этого несложно: достаточно снабдить каждый адрес обязательным атрибутом, его семейством. Для этого можно, к примеру, хранить двоичное представление адреса не само по себе, а в составе структуры, где еще одно поле указывает семейство данного адреса.

    Этот подход хорошо знаком тем, кто использовал API сокетов Беркли.

    Именно так поступает новая функция getaddrinfo() [§6.1 RFC 3493]. Значение поля "семейство адреса", возвращаемое этой функцией, можно прозрачно передавать функциям из API сокетов Беркли [RFC 3493], так что приложениям не нужно обрабатывать каждое известное им семейство отдельно. Теперь правильно написанное приложение не потребует никаких изменений после появления следующей версии IP или даже перехода на новый стек протоколов с похожими свойствами.

    Каковы будут отношения нового API и зонной адресной архитектуры IPv6? Если узел использует не DNS, а другую систему разрешения имен, которая каким-то образом позволяет ему получать правильные идентификаторы зон zone_id в адресах, то функция getaddrinfo() готова дать ему доступ к этой информации. Она возвращает адреса IPv6 в виде структур sockaddr_in6, где есть поле идентификатора зоны.

    Если же адрес IPv6 получен из записи AAAA, опубликованной в DNS, то доступно только его численное значение, не уточненное идентификатором зоны zone_id. Причины этого были изложены в §2.11. Тем не менее, отсутствие zone_id в записи AAAA еще не значит, что такая запись не может содержать в правой части адрес IPv6 с ограниченной областью действия. К примеру, если организация использует внутрисайтовые адреса, то все они заведомо принадлежат одной зоне — внутренней сети организации. С одной стороны, такие адреса вполне можно поместить в частную зону DNS этой организации. С другой стороны, они не имеют смысла вне данной организации.

    Утечка записей с внутрисайтовыми адресами в Internet может иметь неприятные последствия. Рассмотрим следующий пример. Организация А поместила в свою публичную зону DNS такие записи:

    www.example.org. IN AAAA 2001:db8::beef

    AAAA fec0::beef

    Организация Б тоже использует внутрисайтовые адреса. Ее хост Х собирается обратиться к www.example.org. У хоста Х есть внутрисайтовый адрес, и по правилам выбора (§6.6) хост Х предпочтет удаленный адрес FEC0::BEEF, потому что у него наименьшая область. Но, с точки зрения хоста Х, зона адреса FEC0::BEEF — сайт Б, а не сайт А! Когда хост Х попытается установить сеанс по этому адресу (см. рис. 9.5), возможны три основных исхода, один другого хуже:

  • хост Х не сможет установить сеанс, потому что в сети Б нет узла с адресом FEC0::BEEF; этот исход не фатальный, так как хост Х перейдет к следующему адресу www.example.org, 2001:DB8::BEEF, по тем же правилам выбора;
  • хост Х установит сеанс с легитимным узлом сети Б, чей адрес — FEC0::BEEF; результат будет непредсказуемым;
  • злоумышленник, забравшийся в сеть Б, присвоит себе свободный адрес FEC0::BEEF и без труда перехватит сеанс; вдобавок, хосты могут применять разные политики безопасности к сеансам внутри и вне сайта; например, хост Х может отказаться от аутентификации и шифрования сеанса, полагая, что ведет диалог с доверенным узлом; в результате безопасность пострадает еще сильнее. (рис 9.5) Последствия необдуманной публикации внутрисайтового адреса в DNS
  • Выводы напрашиваются сами собой:

  • >в общедоступной зоне DNS следует регистрировать только глобальные адреса IPv6;
  • клиент DNS не должен возвращать приложению запись из общедоступной зоны DNS, если она содержит адрес IPv6 с ограниченной областью.
  • Выполнить второе правило в общем случае непросто, так как клиент DNS должен правильно классифицировать зоны на доверенные и общедоступные. Однако в типовой частной сети эту задачу можно переложить на рекурсивный сервер DNS. Такой сервер, обрабатывая запросы конечных клиентов, мог бы фильтровать записи в ответах других серверов; в то же время, собственные авторитетные ответы он фильтрации подвергать не будет. Конечно, в этом случае надо запретить обращение клиентов к внешним серверам DNS, например, на уровне политики безопасности сети.

    Работа хоста с несколькими подключениями

    Осторожно: вы вступаете в область исследования и экспериментов. Данный аспект работы IPv6 еще находится на стадии развития.

    Мы завершим нашу умозрительную работу над основами IPv6, рассмотрев такой простой вопрос: каким образом хост IPv6 с несколькими сетевыми подключениями сможет эффективно использовать их? Ведь до сих пор мы старательно закладывали фундамент для ответа на этот вопрос, но от самого ответа столь же старательно уходили.

    Чтобы в полной мере оценить значение этого вопроса, давайте мысленно вернемся во времена господства IPv4 и посмотрим, как тогда обеспечивали отказоустойчивое подключение отдельной сети к Internet.

    Мы, конечно же, помним, что Internet — это "сеть сетей".

    Главным рецептом тогда, как и теперь, было дублирование и резервирование элементов, в данном случае, подключений к другим сетям. Проще говоря, сети было необходимо несколько подключений, желательно, к разным партнерам. Однако, чтобы эти подключения на самом деле работали, надо было каким-то образом донести информацию об адресном пространстве сети в глобальное облако маршрутизации Internet через каждое из подключений.

    Как нетрудно убедиться, в модели маршрутизации IP прикладной трафик распространяется навстречу маршрутной информации.

    Если сеть принадлежала достаточно крупному провайдеру, у которого были статус LIR, автономная система и прочие атрибуты серьезного игрока в этом бизнесе, то сеть объявляла о себе с помощью BGP, и все образовывалось как бы само собой. Это вполне устоявшаяся схема, и LIR IPv6 сообщают о себе точно так же при помощи MBGP.

    Но что если сеть принадлежала абстрактной организации, применявшей свои подключения к Internet для сугубо внутренних целей? Пока адреса IPv4 еще были относительно доступны, такая организация могла получить прямиком из рук RIR автономную систему и отдельный блок адресов, не привязанный к определенному провайдеру , Известный как PI (Provider Independent). после чего подключиться к нескольким провайдерам и, по взаимному с ними соглашению, объявить о себе по BGP. Альтернативой этому было получение блока адресов из пространства провайдера №1 и анонсирование этого блока, по соглашению с провайдером №1, прочими провайдерами той же организации. Независимо от выбранного пути, глобальным следствием этой практики было дробление адресного пространства и рост числа маршрутов в глобальном облаке Internet. Ведь каждая такая сеть была представлена, как минимум, одним маршрутом, а число независимых сетей может на порядки превышать число провайдеров. Неудивительно, что независимые сети попали в немилость современных политик распределения адресов IPv6, принятых RIR.

    Самая "интересная" участь ожидала те сети, которые не были достаточно велики или весомы, чтобы оказаться представленными в глобальном облаке маршрутизации Internet. Им не оставалось ничего иного, кроме как подключиться к нескольким провайдерам, получить от каждого из них по блоку адресов и после этого заняться сооружением пирамиды из различных ухищрений, чтобы обеспечить работу сети по нескольким подключениям. Вот лишь краткий и неполный список препятствий, которые им приходилось преодолевать:

  • Один провайдер вряд ли пропустит от клиента транзитный трафик, адрес источника в котором принадлежит другому провайдеру. Это вполне обоснованное ограничение в рамках борьбы с подделкой адресной информации.
  • Отказоустойчивость на входе, например, для публично доступного сервера внутри сети, можно обеспечить, назначив ему и опубликовав в DNS адреса от разных провайдеров. Однако тогда надо следить, чтобы ответный трафик возвращался в соответствующее подключение — иначе см. первый пункт. Так как обычная маршрутизация не учитывает адрес источника пакета, приходится использовать нестандартные трюки, например, маршрутизацию согласно политике.
  • Отказоустойчивость на выходе, чтобы пользователи сети сохранили доступ к Internet при отказе одного из подключений, возможна только путем сочетания NAT и каких-то доморощенных механизмов наблюдения за состоянием подключений в стиле: "Ping на Google не прошел, значит, провайдер "упал". Переключаем default route…" Ведь у каждого пользовательского хоста IPv4 всего один адрес.
  • Но даже эти ухищрения обладали весьма ограниченными возможностями. В частности, никакими трюками нельзя было сохранить обычное соединение TCP при переключении между провайдерами, потому что менялся локальный адрес сокета. В результате, чем меньше была сеть, тем трудней ей давалось надежное подключение к Internet.

    Поэтому сейчас, когда пришло время перемен, стоит перенести фокус с масштабных механизмов отказоустойчивости, таких как BGP, на средства, доступные индивидуальному пользователю. Иными словами, мы хотим осчастливить все сети независимо от их размера, а самая маленькая сеть — это один хост. Именно на хосте IPv6 мы и заострим свое внимание.

    Поставим себя на место владельца хоста IPv6, которому необходимо надежное подключение к Internet, работоспособность которого не зависит от одного провайдера. Как бы мы поступили в этой ситуации?

    Первый вариант (рис. 9.6) состоит в том, чтобы установить два (или больше) сетевых интерфейса и одновременно подключиться к двум (или больше) разным провайдерам (в надежде, что они независимы). При этом каждый сетевой интерфейс получит адрес из блока соответствующего провайдера. Эталонный хост IPv6 на базовом уровне уже готов к работе в такой конфигурации, потому что с каждым его интерфейсом связан отдельный список маршрутизаторов по умолчанию.

    Можно сказать, что эталонный хост IPv6 с самого начала поддерживает виртуализацию сетевого стека.

    (рис 9.6) Множественное подключение по разным каналам

    Второй вариант (рис. 9.7) возможен, если есть канал, на котором уже присутствуют маршрутизаторы нескольких провайдеров (или маршрутизаторы, подключенные к разным провайдерам). Тогда достаточно подключить хост к одному каналу, но назначить его сетевому интерфейсу несколько адресов, по одному от каждого провайдера. В этом случае у хоста будет всего один список маршрутизаторов по умолчанию, но в нем будут представлены все провайдеры.

    (рис 9.7) Множественное подключение по одному каналу

    На самом деле, это только первый шаг к отказоустойчивому подключению. В обоих вариантах он известен как множественное подключение (multihoming). Но что он дает хосту? Непосредственно хост может обнаружить только сбой канала или маршрутизатора, используя механизм NUD (§5.1). Что ж, и это неплохо, так как хост может переключиться на другого провайдера, используя стандартные механизмы.

    Если же сбой будет где-то дальше в сети провайдера, то явного указания на это хост не получит. Что же произойдет в этом случае? Рассмотрим для примера два сценария, в которых хост принимает соединения TCP извне или же устанавливает их с внешними хостами сам.

    Допустим, что в первом сценарии все адреса хоста опубликованы в DNS под одним именем. Тогда удаленное приложение, получив на входе имя, станет пробовать эти адреса по очереди, в порядке, предписанном DAS (§6.6). Так как трасса к разным адресам назначения будет проходить через разных провайдеров ввиду самого устройства маршрутизации IP, рано или поздно удаленная сторона выберет доступный адрес назначения, и первый пакет <SYN> дойдет до хоста, скажем, через провайдера №2. Теперь хосту надо позаботиться, чтобы ответный пакет <SYN,ACK> ушел через того же провайдера №2, потому что именно он работоспособен и, вероятно, готов пропустить такой пакет. Эта задача сводится к выбору выходного интерфейса (вариант рис. 9.6) или маршрутизатора (вариант рис. 9.7). В первом случае решение таково: сокет IPv6 должен быть привязан к определенному сетевому интерфейсу хоста. Например, в данном случае пакет <SYN> придет по адресу, который назначен интерфейсу №2, а значит, и ответный пакет <SYN,ACK> надо направить в тот же интерфейс. Во втором случае хосту понадобится дополнительное правило при выборе маршрутизатора, чтобы пакет с адресом источника от провайдера №2 ушел именно через маршрутизатор №2. Благодаря записи в кэше адресатов DC, которая возникнет в результате этого выбора, последующие пакеты данного соединения пойдут через маршрутизатор №2 естественным порядком.

    В нормативных документах IPv6 такого правила пока что нет, хотя оно обсуждалось [draft-huitema-multi6-hosts , Kenji Ohira. IPv6 Address Assignment and Route Selection for End-to-End Multihoming. IETF Meeting №57. http://ops.ietf.org/multi6/ietf57/day1/IETF-57-ohira-multi6.ppt .

    Если один и тот же удаленный адрес одновременно установит соединения с разными адресами локального хоста через разных провайдеров, эта схема даст сбой. Чтобы восстановить ее работоспособность, запись DC должна включать в себя адрес источника, чтобы разным локальным адресам отвечали разные записи DC даже при одном и том же удаленном адресе назначения.

    Перейдем теперь ко второму сценарию, когда хост сам хочет установить соединение TCP с удаленной стороной, послав для начала пакет <SYN>. Конечно, может оказаться, что у удаленного хоста тоже есть несколько адресов и только некоторые из них недоступны через текущего провайдера. Тогда поможет обычный перебор удаленных адресов. Но как быть, если произошел полный сбой текущего провайдера? Как мы уже сказали, хост может обнаружить его только по косвенным признакам. Здесь одна из возможных линий поведения хоста — попробовать все свои выходные интерфейсы (вариантрис. 9.6) или все маршрутизаторы (вариант рис. 9.7) по очереди. В первом случае процедура DAS выберет правильный адрес источника для пакета <SYN>. Во втором случае надо, чтобы выбор маршрутизатора влиял на выбор адреса источника, а это вполне допустимое расширение процедуры DAS [§5 и §7 RFC 6724].

    Таким образом, множественное подключение хоста IPv6 можно использовать, практически не выходя за рамки стандартных механизмов. Тем не менее, мы так и не обеспечили одной простой возможности: сохранить уже установленное соединение TCP при переключении хоста между провайдерами. Как нетрудно убедиться, такое переключение по-прежнему вызывает смену локального адреса, и соединение TCP рвется. С этим нельзя ничего поделать, по крайней мере, на уровне IP.

    От этой проблемы проще всего отмахнуться, сказав, что прикладные протоколы должны быть устойчивы к сбоям на транспортном уровне. Несомненно, это так. Скажем, протоколы FTP и HTTP поддерживают "докачку", и эта возможность не чувствительна к адресам IP: клиент просто откроет новый сеанс и снова запросит тот же файл или объект, начиная с энного байта.

    Однако это скучный подход, который не делает чести нашему воображению. Давайте лучше применим фантазию и в общих чертах набросаем решение поинтереснее. Прийти к нему несложно. Для этого представим себе, что приложение открывает одно соединение TCP сразу с множеством адресов удаленной стороны. Действительно, зачем перебирать их по одному, если можно было бы послать по пакету <SYN> сразу им всем. Тогда и локальный конец соединения мог бы иметь не один адрес, а целое их множество.

    "В лоб" такое решение можно сымитировать, открыв M*N соединений TCP, где N — число локальных адресов, а M — число удаленных адресов. Однако это непрактичный подход, так как, во-первых, число таких соединений может быть велико, а во-вторых, ими надо дополнительно управлять, чтобы сохранить порядок байтов в потоке. Ведь получателю надо как-то узнать, в каком порядке были посланы байты, пришедшие по разным соединениям.

    Поэтому нам понадобится более целостное решение. Так, TCP можно заменить новым транспортным протоколом, который с самого начала станет поддерживать больше одного адреса на каждом из концов соединения. Конечно, пакет IPv6 по-прежнему содержит только один адрес источника и один адрес назначения, так что вся работа по слежению за доступностью адресов и переключению между ними ляжет на этот новый транспортный протокол. На самом деле, такой протокол уже создан: SCTP. Прелесть этого решения в том, что оно сквозное: хостам безразлично, где именно в сети произошел сбой, и пока существует хотя бы одна пара адресов, межу которой пакеты все еще ходят, транспортное соединение будет жить.

    Однако новый транспортный протокол — это довольное радикальное новшество, которое подходит только для новых приложений. Кроме того, с новым транспортным протоколом связана та трудность, что всевозможные межсетевые экраны, системы предотвращения вторжений и прочие "ящики" (middleboxes), ведущие глубокий анализ транзитного трафика, уже хорошо знакомы с TCP, но новый протокол наверняка вызовет проблемы. Поэтому нельзя ли перенести все те же идеи обратно в старый добрый TCP, чтобы и существующие приложения выиграли от новой архитектуры множественных подключений?

    В основу такого "многопутевого TCP" (multipath TCP, MPTCP) следует заложить принципы совместимости с приложениями и сетью [RFC 6182]. Что это значит? С одной стороны, сетевой API, например, сокеты Беркли, должен остаться, по возможности, без изменений, чтобы уже существующие приложения не пришлось даже перекомпилировать, не говоря уже о том, чтобы их переписывать. А с другой стороны, для стороннего наблюдателя в сети каждый путь MPTCP (известный как под-поток, sub-flow) должен выглядеть как обычное соединение TCP. Это нужно, чтобы MPTCP беспрепятственно проходил сквозь различные "ящики".

    В частности, у каждого под-потока своя, независимая от других под-потоков и непрерывная, как и положено в TCP, нумерация байтов, то есть порядковые номера и номера квитанций. Если бы нумерация байтов была одна на все под-потоки, протокол стал бы проще, но достаточно строгий "ящик" вполне мог бы блокировать под-поток с "прорехами" в нумерации байтов, потому что тот нарушает протокол TCP. Соответствие между номерами байтов в под-потоке и всем соединении MPTCP задает та дополнительная информация, которую несут прозрачные для "ящиков" опции MPTCP.

    Фактически мы вернулись к идее множественных соединений TCP между разными парами локальных и удаленных адресов, но под управлением четко заданного механизма. Чтобы работать по-настоящему эффективно, MPTCP должен управлять не только взаимной доступностью разных пар адресов, но и перегрузкой вдоль разных путей, перенося трафик на те пути, где перегрузка меньше [RFC 6356]. Для переноса служебной информации MPTCP вполне подходят опции TCP, которые активно используются и для других целей, а потому без труда преодолевают "ящики" [draft-ietf-mptcp-multiaddressed]. Благодаря дополнительной информации, MPTCP вновь собирает исходный поток байтов из нескольких под-потоков, а потому он способен вести одновременную передачу данных вдоль нескольких путей.

    Конечно, MPTCP не зависит от особенностей IPv6. Более того, он позволяет соединению TCP одновременно использовать пути IPv4 и IPv6.

    А как насчет других протоколов, уже работающих непосредственно поверх IPv6? В их случае можно испытать иной подход. Представим себе, что между всеми этими протоколами и уровнем IPv6 появляется "прокладка", которая манипулирует адресами в пакетах, так что вышестоящий протокол всегда имеет дело с одной парой адресов (локальный и удаленный), как и раньше. Этой "прокладке", известной как Shim6 (Прокладка №6) [RFC 5533 , A. Garcia-Martinez, M. Bagnulo, I. van Beijnum. The Shim6 architecture for IPv6 multihoming. IEEE Communications Magazine, 2010, v. 48, issue 9, pp. 152–157 http://www.networks.imdea.org/Portals/8/Downloads/Publications/Shim6-Arquitecture-2010-EN.pdf ], тогда придется динамически транслировать неизменные адреса "сверху" в зависящие от текущей обстановки в сети адреса "снизу" и наоборот. Эта трансляция превращает адреса, видные вышестоящим протоколам, в идентификаторы, не зависящие от сетевой топологии, тогда как адреса, действительно попадающие в сеть, становятся локаторами, чувствительными к сетевой обстановке. Поэтому первые из них получили название "идентификаторы вышележащего уровня" (Upper-Layer Identifier, ULID). Конечно, адрес ULID остается обычным адресом IPv6 и может выполнять заодно роль локатора, пока соответствующий сетевой интерфейс доступен. Так как Shim6 тесно соприкасается с IPv6, свою служебную информацию он передает в виде заголовков расширения. Информацию о доступности адресов-локаторов для нужд Shim6 поставляет вспомогательный протокол REAP (Reachability Protocol ) [RFC 5534].

    Самостоятельно обсудите, вызовет ли Shim6 те же трудности, что и NAT, у прикладных протоколов, передающих адреса IP внутри своих сообщений. К таким протоколам относятся, например, FTP и SIP.

    Мы не станем сейчас разбирать технические детали MPTCP и Shim6, потому что они довольно сложны и выходят за рамки нашего курса. Тем не менее, уже сказанного достаточно, чтобы увидеть, как снова победила сквозная модель: эффективное использование независимых подключений оказалось по силам самим хостам, а IPv6 предлагает почти все необходимые для этого механизмы.

    Конечно, передача данных по независимым подключениям создает и новые проблемы. Одна из них состоит в том, как локальный хост убедится, что все завяленные адреса принадлежат одному удаленному хосту. Ведь иначе злоумышленник может перехватить поток данных, выдав себя за один из адресов удаленного хоста. Это в особенности касается протоколов вроде Shim6, в которых стороны не обмениваются информацией обо всех своих адресах заранее.

    Связать воедино несколько адресов можно, снова воспользовавшись идентификатором интерфейса как носителем защитной информации. Допустим, хосту необходимо назначить $$N$$ адресов, все в разных подсетях. То есть на входе мы получаем список префиксов подсетей $$\big\{P_i \big\}$$, где$$1\leqslant i\leqslant N$$. Тогда идентификатор интерфейса$$L_j$$ для префикса$$P_j$$ можно вычислить как хэш-функцию всех префиксов, например, так:

    $$L_j=H(M\mid P_j\mid\big\{P_i \big\})$$.

    То есть хэш-функция $$H$$ вычисляется от цепочки битов, полученной сцеплением какого-то дополнительного параметра M, известного обеим сторонам протокола, данного префикса $$P_j$$ и всего списка префиксов$$\big\{P_i \big\}$$. В результате мы получаем набор из $$N$$ адресов вида:

    $$A_j=P_j\mid L_j$$.

    То есть каждый адрес получен сцеплением префикса подсети и отвечающего ему идентификатора интерфейса, вычисленного по вышеприведенному рецепту. Составленный таким образом адрес известен как адрес, основанный на хэше (Hash-Based Address , HBA) [RFC 5535]. Он имеет смысл только в составе всего набора $$\big\{A_i \big\}$$, известного как набор HBA (HBA-Set). Точнее, вне набора это обычный адрес IPv6 без дополнительных защитных свойств.

    Параметр M нужен, как минимум, для того, чтобы разные хосты, подключенные к одним и тем же подсетям, могли получить разные наборы HBA. Кроме того, адрес HBA случайный, а значит, есть вероятность конфликта с уже назначенным адресом. В этом случае конфликт обнаружит процедура DAD (§5.4.1). Но, чтобы разрешить конфликт, придется варьировать параметр M и повторить вычисление набора HBA. На практике параметр M составной и содержит в себе несколько полей, отвечающих за разные свойства набора HBA: уникальность, устойчивость к конфликтам, совместимость с CGA (§6.3).

    Если хэш-функция надежная, то порядок сцепления отдельных элементов в цепочку-аргумент неважен, пока один и тот же порядок принят всеми сторонами протокола. На практике одно поле M, а именно счетчик коллизий, возникает между префиксом $$P_j$$ и списком префиксов ради совместимости с CGA. Это поле увеличивается на единицу, если процедура DAD обнаружила коллизию. Благодаря этому повторное вычисление дает другие случайные адреса.

    Теперь мысленно перенесемся на другую сторону протокола и подумаем, как с помощью механизма HBA проверить адрес$$A$$, на который претендует удаленная сторона. Пусть этот адрес состоит из префикса подсети$$P$$ и идентификатора интерфейса $$L$$. Суть проверки сводится вот к чему: принадлежит ли данный адрес $$A$$ данному набору HBA? Чтобы провести такую проверку, проверяющей стороне потребуется параметр $$M$$ и список префиксов $$\big\{P_i \big\}$$. Именно эти два элемента и определяют набор HBA, потому что все остальное можно вычислить на месте. Так, проверяющая сторона первым делом проверяет, содержится ли префикс $$P$$ из адреса$$A$$ в списке $$\big\{P_i \big\}$$. Если это не так, то налицо подлог или обычный сбой. Если же префикс — в списке, то осталось вычислить его ожидаемый идентификатор интерфейса HBA:

    $$L_{HBA}=H(M\mid P_j\mid\big\{P_i \big\})$$.

    Если адрес $$A$$ действительно из данного набора HBA, то его идентификатор интерфейса $$L$$ обязан совпасть с вычисленным значением $$L_{HBA}$$, с точностью до битов U/L и I/G.

    Дело осталось за малым: передать параметр M и список префиксов $$\big\{P_i \big\}$$ проверяющей стороне. Оказывается, что для этого не нужен новый протокол, так как всю необходимую информацию можно поместить в блок параметров CGA (рис. 9.3). Формат этого блока достаточно гибок и расширяем, чтобы в нем нашлось место для списка префиксов, а модификатор уже в нем содержится. В результате можно составлять адреса, которые одновременно удовлетворяют нормативам CGA и HBA, потому что их идентификаторы интерфейса стандартным образом зависят и от открытого ключа хоста, и от его списка префиксов. Это чудесным образом избавляет нас от конфликта между механизмами CGA и HBA.

    В блоке параметров CGA уже есть случайный модификатор, префикс подсети P, и счетчик коллизий — они отвечают подстроке $$M\mid P$$ в аргументе хэш-функции. Список префиксов отправится в конец блока как расширение CGA. В результате весь аргумент хэш-функции будет отформатирован как блок параметров CGA, а HBA превратится в совместимое расширение CGA.

    Конечно, простой пакет HBA не пройдет криптографическую проверку CGA, поскольку он не подписан закрытым ключом. Вместо настоящего ключа HBA-без-CGA использует случайную цепочку битов в формате ключа RSA. Тем не менее, адрес HBA содержит в себе годный отпечаток этого случайного ключа.

    Как и можно было ожидать, HBA наследует у CGA ограничение на длину префикса подсети P: она обязана равняться 64 битам.

    Страницы:

    Фрагментировать или нет?

    Механизм управления фрагментацией IPv6 требует дополнительных соображений и комментариев, потому что он существенным образом отличается от его аналога в IPv4. Главное отличие, конечно же, в том, что фрагментацию IPv6 может вести только источник, хотя информация об оптимальной длине пакета скрыта дальше в сети.

    В первую очередь, такая особенность механизма фрагментации IP влияет на работу транспортных протоколов. Начнем с хрестоматийного примера TCP. Раньше, в среде IPv4, перед TCP было два основных пути:

  • сбрасывать флаг DF и делить поток прикладных данных на сегменты произвольного размера, поручив их дальнейшую фрагментацию сети;
  • устанавливать флаг DF и выполнять PMTUD, избегая фрагментации IP и пытаясь оптимизировать размер сегмента.
  • Более хитроумная реализация TCP могла пробовать второй путь, но переключаться на первый, если PMTUD не работает, например, из-за слишком строгой фильтрации пакетов ICMP в сети.

    В среде IPv6 первый путь полностью закрыт, потому что флаг DF неявно установлен всегда, а пакеты, длина которых больше PMTU, не достигнут адресата ни при каких условиях. Теперь сам источник пакета должен выбрать его подходящий размер. Как следствие, при переходе к IPv6 растет важность правильной и надежной работы PMTUD.

    В первую очередь, этот факт касается сетевых администраторов, увлеченных борьбой с "нежелательным" трафиком ICMP.

    Тем не менее, даже ненадежная сигнализация ICMP не мешает проводить PMTUD, используя сквозной принцип "Надейся в первую очередь на себя, затем на удаленную сторону, и только в последнюю на транзитные узлы". и некоторую эвристику. Например, если "тройное рукопожатие" TCP проходит успешно, но затем оказывается, что полноразмерные сегменты куда-то пропадают — удаленная сторона не квитирует новые данные, — то здесь явно замешан PMTU, и источнику TCP следует понизить размер сегмента. Хотя подсказок в виде очередного значения MTU он не получит, его выручит двоичный поиск, ограниченный снизу заведомо "габаритным" значением (1280 байт в IPv6), а сверху — MTU локального интерфейса. Пробное значение можно повышать, пока сегменты достигают удаленной стороны. Как только оценка окажется завышенной, сегменты перестанут проходить, и надо будет перейти к понижению пробной величины. На этой идее основана так называемая процедура PMTUD уровня пакетизации [RFC 4821].

    Тем не менее, PMTUD — не абсолютная необходимость даже в среде IPv6. Теперь минимальный размер MTU равен относительно большой величине, 1280 байт, так что примитивная реализация TCP может себе позволить ориентироваться на эту постоянную. Так же может поступить и более сложный вариант TCP, если он обнаружит, что PMTUD все-таки не работает.

    Итак, в среде IPv6 перед TCP открываются следующие пути:

  • сегментировать поток, исходя из величины PMTU 1280 байт;
  • оптимизировать размер сегмента с помощью PMTUD.
  • TCP — это пример транспортного протокола, который может плавно варьировать размер сегмента и поэтому не нуждается во фрагментации на уровне IPv6. Совсем другую картину мы увидим, если перенесем наш взгляд на блочные транспортные протоколы и их потребителей.

    Представим себе, что некий прикладной протокол работает поверх UDP. В этом случае размер исходящей дейтаграммы задает приложение, и размер этот может достигать внушительной величины. Это не наша фантазия: в реально существующих протоколах, например, в NFS, возникают сообщения длиной порядка нескольких тысяч байт. Как подобная дейтаграмма достигнет адресата?

    Первый подход мы уже нащупали: приложение может посредством сетевого API попросить локальный модуль IPv6, чтобы тот фрагментировал пакеты исходя из MTU 1280 байт. Это не самый эффективный, зато простой и надежный путь: из-за своего чрезмерного размера пакеты теряться заведомо не будут.

    Тем не менее, пакеты теряются еще по ряду причин, и прикладной протокол должен быть устойчив (или безразличен) к таким потерям. Данное свойство прикладных протоколов, работающих поверх UDP, позволяет оптимизировать размер фрагмента IPv6. Для этого надо, чтобы модуль IPv6 сам выполнял PMTUD. Как это будет работать? Например, так:

  • приложение шлет данному адресату свое первое большое сообщение;
  • модуль IPv6 фрагментирует составленный пакет, взяв первым приближением PMTU величину MTU выходного интерфейса;
  • возможно, такие фрагменты не пройдут всю трассу; но модуль IPv6 примет извещения ICMPv6 "пакет слишком велик" и получит второе приближение PMTU;
  • Это приближение будет довольно точным благодаря обязательному значению MTU в сообщении "пакет слишком велик". В классическом IPv4 источнику приходилось вести вместо этого поиск вслепую, так как маршрутизатор не подсказывал значения MTU. Впрочем, двоичный поиск сходится довольно быстро. Подумайте, в каком случае двоичный поиск вслепую выиграет у поиска с подсказкой маршрутизатора. (Например, когда число шагов достаточно велико, а MTU каждого нового шага меньше предыдущего - "телескопическая трасса").

  • приложение обнаружит потерю сообщения, например, не получив отклика удаленной стороны в течение какого-то времени, и повторит сообщение (или пошлет следующее, если в данном протоколе потери можно игнорировать);
  • модуль IPv6 фрагментирует пакет, используя новое приближение PMTU;
  • возможно, фрагменты опять не пройдут; но в результате модуль IPv6 снова уточнит величину PMTU;
  • приложение еще раз повторит сообщение (или пошлет следующее)…
  • И так до тех пор, пока приближение PMTU не станет меньше или равно его истинному значению для данной трассы. После этого фрагментированные пакеты начнут доходить до адресата, и прикладной сеанс продолжится. Чтобы подобная схема работала, модуль IPv6 должен кэшировать значения PMTU для своих недавних адресатов. Подходящее место для этих сведений — кэш адресатов, DC [§5.2 RFC 1981].

    Как мы помним, трасса — это путь по сети, от источника к адресату через транзитные узлы, который проходит (или прошел бы) пакет в данный момент времени. Для простоты мы сейчас не будем говорить о маршрутизации от источника, когда трассу выбирает источник пакета, и маршрутизации согласно политике, когда выбор следующего шага основан на произвольных характеристиках пакета и внешних параметрах. Тогда трасса IP зависит от узла-источника и от адреса назначения пакета, а также от настроек источника и каждого транзитного узла в момент обработки пакета (грубо говоря, от их таблиц маршрутов). Конечно, в "живой" сети трасса может со временем меняться вместе с настройками источника и транзитных узлов. Однако, пока сеть стабильна, трасса остается постоянной хотя бы какое-то время. Именно поэтому источник имеет право кэшировать значения PMTU, используя в роли ключа адрес назначения.

    Однозначность соответствия между адресатом и наблюдаемой величиной PMTU может оказаться нарушена, если сеть использует ECMP и пакеты одному адресату могут приходить по разным путям с неодинаковым значением PMTU. Между тем, существующие реализации хоста IP кэшируют одно значение PMTU на адресата еще со времен IPv4, а IPv6 лишь стандартизует эту практику. Поэтому надо с осторожностью применять распределяющие хэш-функции ECMP, зависящие от чего-либо сверх адреса назначения пакета, например, номера вышестоящего протокола или портов TCP/UDP. (Обсудите самостоятельно, насколько допустимо, чтобы такая хэш-функция зависела от адреса источника пакета.)

    Еще один путь открывается, когда прикладной протокол позволяет варьировать размер сообщений, например, если приложение сегментирует некий поток байтов или более крупных единиц данных. В этом случае приложение могло бы выбрать такой размер своих сообщений, чтобы их фрагментация на данной сетевой трассе не требовалась . Этот прием иногда называют "семантическая фрагментация", так как он требует хотя бы минимального понимания, что значат передаваемые данные. Например, мы его уже встретили в MLDv2 (§6.4). Но, чтобы приложение узнало подходящий размер, требуется обратная связь от локального модуля IPv6 к приложению. Ее тоже можно обеспечить в рамках сетевого API. Скажем, приложение может запрашивать у модуля IPv6 текущее приближение PMTU для данного адресата. Или же модуль IPv6 может подсказывать приложению PMTU по своей инициативе, в виде служебной структуры , Это cmsghdr в терминах API сокетов Беркли. когда приложение ожидает ответа удаленной стороны, а вместо него приходит извещение ICMPv6 "пакет слишком велик".

    Наконец, оптимистичное приложение, которое верит в возможности PMTUD, захочет запретить модулю IPv6 фрагментацию своих исходящих пакетов. Такое приложение должно активно следить за текущим приближением PMTU и корректировать размер своих сообщений. Модуль IPv6 и приложение проводят PMTUD в паре: модуль IPv6 обрабатывает извещения ICMPv6 и оценивает PMTU, а приложение генерирует новые пробные пакеты согласно последней оценке.

    Именно эти идеи нашли отражение в расширенном API для IPv6, основанном на сокетах Беркли [RFC 3542]. Приложение управляет фрагментацией и PMTUD с помощью опций сокета из Табл. 9.1 на уровне IPPROTO_IPV6 [§11 RFC 3542]. Некоторые опции можно избирательно устанавливать для отдельных сообщений с помощью служебной структуры cmsghdr.

    Опции сокета, управляющие фрагментацией IPv6
    Опция Смысл
    IPV6_USE_MIN_MTU Использовать минимальный MTU (1280 байт)
    IPV6_PATHMTU Запросить приближение PMTU для подключенного сокета
    IPV6_RECVPATHMTU Разрешить возврат приближения PMTU из вызова recvfrom()

    Обсудите преимущества и недостатки альтернативного подхода к определению PMTU, когда каждый транзитный узел по трассе пакета записывает фактическое значение MTU в специальную пошаговую опцию, если ее текущее значение больше MTU [draft-park-pmtu-ipv6-option-header] , так что прошедший всю трассу короткий пакет содержит в себе точное значение PMTU.

    Еще один практический способ понизить долю фрагментированных пакетов — это не занижать MTU интерфейсов без необходимости [§5 RFC 2460]. Сегодня стандарт де-факто для подключения конечных пользователей на канальном уровне — это Ethernet, а он диктует значение MTU 1500 байт. Это же значение мы встречаем и в других канальных протоколах, например, в PPP [§2 RFC 1661]. В центре сети может понадобиться запас MTU сверх 1500 байт, чтобы пакеты от конечных пользователей можно было подвергать дополнительной инкапсуляции, например, туннелировать, не вызывая фрагментации. Конечно, в сложной сети с несколькими административными зонами выбрать для каждого канала оптимальное значение MTU может быть непросто. Тем не менее, опускать его ниже 1500 байт точно не стоит.

    Еще мы до сих пор не задали минимальный размер буфера сборки IPv6. Как мы помним, в IPv4 он составлял 576 байт. Теперь мы видим значение, которое лучше подходит современным условиям: 1500 байт. Иными словами, всякий узел IPv6 обязан собрать адресованный ему пакет, если длина пакета после сборки не превышает 1500 байт [§5 RFC 2460]. Какой вывод должны сделать вышестоящие протоколы? Не следует создавать пакеты длиннее 1500 байт, если нет уверенности, что адресат готов принимать их. Например, в TCP подобные сведения можно извлечь из опции MSS.

    В качестве упражнения вычислите наиболее вероятные длины полноразмерных сегментов TCP, которые можно будет увидеть в сети после перехода на IPv6. Не забудьте, что фактическая длина полноразмерного сегмента TCP может оказаться меньше значения опции MSS [RFC 6691]. (Подсказка: учтите возможность расширений из [RFC 1323].)

    Работа с множеством локальных адресов. Выбор адресов по умолчанию

    Как мы сказали в §2.5, у хоста IPv6 может быть несколько сетевых адресов. На самом деле, один адрес бывает только у хоста IPv6, который не подключен к сети, — это адрес обратной связи ::1. Если у хоста есть хотя бы один активный сетевой интерфейс кроме "петли", то число его адресов возрастает до двух, так как этому интерфейсу обязательно назначен внутриканальный адрес IPv6. А если интерфейс хоста получает еще и глобальный адрес IPv6, то адресов у хоста становится три. Как мы видим, три индивидуальных адреса — это минимум для хоста IPv6, подключенного к Internet.

    Однако три адреса — это еще не предел. Положим, у хоста всего один сетевой интерфейс. Он находится в разных зонах, и каждой зоне может отвечать, по меньшей мере, один локальный адрес. Хотя на практике мы встречаем только внутриканальную и глобальную области, адресация IPv6 вполне допускает области промежуточной величины и отвечающие им зоны.

    Мы помним из §2.4, что у всякого численного адреса IPv6 ровно одна область действия, а назначенный интерфейсу адрес принадлежит ровно одной зоне. В то же время, сетевой интерфейс находится в зонах всех возможных областей, независимо от назначенных ему адресов.

    Далее, сетевой интерфейс хоста может получить несколько адресов из одной зоны. Для чего это может быть полезно? Адреса из одной подсети назначают, когда, например, функции хоста А переходят хосту Б, а хост А прекращает свое существование. В этом случае можно назначить хосту Б, кроме его собственного адреса, бывший адрес хоста А, и тогда не придется менять настройки других элементов сети: хост Б станет отвечать от имени хоста А, и никто не заметит изменений. Адреса из разных подсетей открывают путь к тому, чтобы использовать одновременные подключения к нескольким провайдерам Internet.

    Конечно, полноценная работа с несколькими подключениями — это существенно более сложная задача. Ее анализу посвящен [RFC 4177], а мы вкратце рассмотрим ее в §6.8 настоящей книги.

    Нетрудно предвидеть, что ситуация только усложнится, когда у хоста будет несколько активных сетевых интерфейсов. Каждый из них получит, помимо обязательного внутриканального адреса, ноль или больше адресов согласно адресному плану сети.

    Рассмотрим с этих позиций типовой сценарий: локальный хост Л по своей инициативе собирается послать пакет удаленному хосту У. Допустим, что приложение, запущенное на хосте Л, хочет направить в адрес У дейтаграмму UDP или установить с У соединение TCP. Чтобы составить такой пакет, сетевой стек хоста Л — не без помощи приложения и даже самого пользователя — должен перейти от абстрактных названий "Л" и "У" к паре адресов, источника и назначения пакета.

    Нам сейчас интересен именно случай, когда у хоста Л есть выбор, какие адреса указать в пакете. Если же хост Л отвечает на пакет хоста У, то свободы у него существенно меньше. В классических протоколах единственно верным поведением хоста Л будет использовать уже приведенные в пакете адреса, просто переставив их местами. В противном случае хост У, скорее всего, не примет такой ответ. Современные протоколы, например, Shim6 (§6.8) и SCTP, поддерживают больше одного адреса на каждом конце сеанса, но их выбором руководят соображения доступности адреса в данный момент времени. Нас же сейчас интересует первоначальный выбор на основании априорных данных, таких как тип и область действия адреса.

    Первым делом пользователь сообщает приложению какой-то идентификатор хоста У, пригодный к машинной обработке. Сегодня в этой роли мы используем имена хостов, так что приложение на входе получит имя У, такое как y.example.org. Обратившись к DNS, приложение получит список адресов, отвечающих данному имени. Как мы уже говорили в §2.11, в общедоступных зонах DNS мы надеемся увидеть только глобальные адреса; между тем, DNS — не единственная база имен, наряду с ней применяют NIS, WINS и даже файл HOSTS.TXT… К чему мы ведем нашу мысль? В общем случае приложение получит множество адресов хоста У, а область действия некоторых из них будет меньше глобальной.

    Со своей стороны, хосту Л тоже назначено некое множество адресов. Если во множестве "Л" всего N адресов, во множестве "У" всего M адресов, то можно составить упорядоченных пар адресов, потенциальных кандидатов.

    В некоторых случаях конкретную пару выбирает пользователь или какой-то внешний механизм, и тогда приложение посредством сетевого API просит стек хоста Л использовать именно эту пару адресов для связи с хостом У. Например, команда ping6 позволяет указать оба адреса явным образом в командной строке:

    ping6 -S FE80::1 2001:DB8::1

    Но обычно полагают, что приложение и сетевой стек сами сделают оптимальный выбор адресов по умолчанию, сокращенно DAS (default address selection), для передачи данных между локальным и удаленным хостами [RFC 6724].

    На самом деле, эта аббревиатура "DAS" встречается в литературе редко, но мы используем ее, чтобы не говорить "ВАПУ".

    В IPv4 процедура выбора сводилась к тому, что приложение перебирало удаленные адреса по одному, пытаясь установить сеанс. Например, так себя ведет клиент telnet:

    $ telnet host.example.org

    Trying 192.0.2.23...

    telnet: connect to address 192.0.2.23: Operation timed out

    Trying 198.51.100.7...

    telnet: connect to address 198.51.100.7: No route to host

    Trying 203.0.113.5...

    Connected to host.example.org.

    Escape character is '^]'.

    NetBSD/vax (host.example.org) (ttyp0)

    login:

    Сетевой стек, в свою очередь, подбирал локальный адрес для указанного приложением удаленного адреса. К примеру, стек BSD Unix находил по таблице маршрутов выходной интерфейс, а затем брал из списка адресов интерфейса самый первый G. R. Wright, W. R. Stevens. TCP/IP Illustrated, Volume 2: The Implementation. Addison Wesley, 1995. §22.8, "in_pcbconnect Function". . Эта процедура продолжалась до тех пор, пока приложению не удавалось установить сеанс или пока оно не исчерпывало список удаленных адресов.

    У процедуры DAS в среде IPv4 есть и нормативные документы [§3.3.4.3 RFC 1122 и §2.3 RFC 1123]. Поведение стека BSD вполне отвечает им: приложение перебирает адреса назначения, а стек подбирает адрес источника, руководствуясь таблицей маршрутов и найденным с ее помощью выходным интерфейсом для данного пакета или соединения. Для соединений TCP это выполнялось единожды, перед открытием соединения, чтобы потом изменения маршрутов не вызвали смену локального адреса и, как следствие, сбой протокола.

    Когда у каждого адреса появляется область действия, такая простая схема DAS может завести в тупик. Достаточно представить себе, что список адресов выходного интерфейса возглавляет внутриканальный адрес, назначенный раньше всех остальных. В этом случае хост Л сможет общаться только со своими соседями по каналу, даже если у него будет не только внутриканальный, но и глобальный адрес. Ведь в §2.4 мы сформулировали такое ключевое правило: чтобы пакет IPv6 достиг адресата, интерфейс назначения должен находиться в зоне адреса источника. Чтобы выполнить это правило, алгоритм DAS должен рассмотреть все локальные адреса и по очереди сопоставить каждый из них с удаленным адресом.

    Если никакой выбор адресов не способен удовлетворить это требование, то обмен данными между хостами Л и У попросту невозможен. Например, это произойдет, если у хоста Л есть только внутриканальные адреса, а все адреса хоста У находятся "вне канала" (см. §5.2). Однако в общем случае на стадии DAS нет сведений о том, где находится удаленный адрес, так как привести в действие механизм ND может только передача пакета, а она еще даже и не начиналась — источник только заполняет заголовок. Поэтому источнику доступны лишь априорные критерии выбора оптимальной пары адресов.

    Вторая половина того же правила из §2.4 гласит, что выходной интерфейс источника должен находится в зоне адреса назначения пакета. Так как из опубликованного в DNS адреса следует только его область, но не зона, источник вправе полагать, что этот адрес автоматически попадает в одну зону с выходным интерфейсом. Ведь мы постановили в §2.4, что сетевой интерфейс находится в одной зоне каждой возможной области! Например, если запрос DNS вернул внутрисайтовый адрес FEC0::1, то хост сделает вывод, что это его локальный сайт, а не сайт соседней организации. Поэтому надо быть вдвойне осторожным, публикуя адреса ограниченной области в DNS.

    Но какой именно критерий надо использовать при таком сопоставлении? Иными словами, какой из локальных адресов лучше всего подходит данному удаленному адресу? Чтобы критерий, который мы сейчас предложим, было проще алгоритмизировать, давайте сформулируем его в виде транзитивного отношения "лучше/хуже" между двумя потенциальными адресами источника, SA и SB, при данном адресе назначения D. Тогда список локальных адресов можно будет отсортировать по этому критерию, и наилучший адрес сам "всплывет" в начало списка.

    Конечно, в данном случае сортировку можно заменить обычным линейным поиском. Двоичный поиск этой задаче не подходит, так как порядок элементов зависит от переменного параметра D, а значит, список нельзя отсортировать раз и навсегда.

    Первым мы рассмотрим случай, когда область адреса назначения Обл(D) — не больше , То есть меньшей или равной величины. чем области обоих адресов источника, Обл(SA) и Обл(SB): Обл(D) $$\le$$ Обл(SA) $$\le$$ Обл(SB) или Обл(D) $$\le$$ Обл(SB) $$\le$$ Обл(SA). Тогда шансы на успех передачи при любом выборе довольно велики. В этом случае имеет смысл выбрать адрес источника, область которого поменьше, а значит поближе к области адреса назначения. Благодаря этому, скажем, диалог с удаленным внутриканальным адресом будет вестись с локального внутриканального адреса. Ведь политика безопасности удаленного хоста может быть более либеральной к внутриканальным адресам, чем к глобальным адресам.

    Когда область адреса назначения Обл(D) — промежуточной величины: Обл(SA) $$<$$ Обл(D) $$\le$$ Обл(SB) или Обл(SB) $$<$$ Обл(D) $$\le$$ Обл(SA), — то шансы на успех больше у адреса источника большей величины. И наоборот, у адреса источника меньшей величины они явно недостаточны, потому что его область строго меньше, чем область адреса назначения. (Случай, когда они равновелики, мы уже включили в предыдущий вариант.)

    Наконец, возможна ситуация, когда у адреса назначения область строго больше, чем у потенциальных адресов источника: Обл(SA) $$\le$$ Обл(SB) $$<$$ Обл(D) или Обл(SB) $$\le$$ Обл(SA) $$<$$ Обл(D). Но даже в этом случае еще не все потеряно. Ведь может оказаться, что интерфейс назначения находится в зоне адреса источника, например, глобальный адрес назначения находится "на канале" (§5.2). Чтобы повысить вероятность благоприятного исхода, надо выбрать адрес источника, область которого больше , а значит, потенциально охватывает больше интерфейсов. Это лучшее, что может сделать источник пакета в таких условиях.

    Чтобы лучше почувствовать эти правила, давайте изобразим их графически, отложив по одной оси область SA, а по другой — область SB, как показано на рис. 9.2. Тогда все пространство задачи можно разбить на четыре участка согласно нашим правилам выбора лучшего адреса источника. В двух из них выбор уже предопределен, а еще в двух зависит от отношения величин областей SA и SB между собой.

    (рис 9.2) Выбор адреса источника по его области — начальная схема

    Если через начало координат этого графика провести прямую с угловым коэффициентом 1 (см. рис. 9.3), то она разобьет все пространство задачи на две половины так, что в верхней область SA будет меньше, чем область SB, а в нижней — наоборот. Фактически, эта прямая может провести для нас выбор лучшего адреса там, где он все еще условный. Поэтому мы можем пересмотреть нашу графическую схему так, что на каждом участке сразу указан окончательный выбор адреса источника.

    (рис 9.3) Выбор адреса источника по его области — промежуточная схема

    На первый взгляд у нас получилось шесть разных участков. Однако, присмотревшись внимательнее, мы заметим, что их можно свести к четырем (см. рис. 9.4), поскольку у двух участков SA есть общая граница, и то же самое справедливо для двух участков SB.

    (рис 9.4) Выбор адреса источника по его области — окончательная схема

    А уже руководствуясь последней схемой, нетрудно записать оптимальный алгоритм выбора [§5 RFC 6724]:

    Если Обл(SA) $$<$$ Обл(SB) то:

    Если Обл(SA) $$<$$ Обл(D) то:

    Предпочесть SB.

    Иначе:

    Предпочесть SA.

    Иначе Если Обл(SA) $$>$$ Обл(SB) то:

    Если Обл(SB) $$<$$ Обл(D) то:

    Предпочесть SA.

    Иначе:

    Предпочесть SB.

    Иначе:

    Области SA и SB равны - ответ не определен.

    Пусть читатель докажет, что такой критерий сравнения транзитивен: если SA лучше SB, а SB лучше SC, то SA лучше SC.

    Как быть, если области SA и SB окажутся равны? В этом случае придется применить дополнительные критерии выбора. Так, один из них можно связать с жизненным циклом локального адреса IPv6. Такой адрес может устареть, а выбрать устаревший локальный адрес можно, только если нет равноценного ему предпочтительного адреса.

    Еще один критерий мы найдем, если попытаемся ответить на вот какой вопрос. Мы обнаружили в §2.5, что для выполнения своей основной функции , то есть продвижения транзитных пакетов, маршрутизатору достаточно внутриканальных адресов. Действительно, если каждый активный сетевой интерфейс маршрутизатора получит внутриканальный адрес, то его соседи по каналу смогут ссылаться на него как следующий шаг. Кроме того, маршрутизатор сможет участвовать на этом канале в розыске соседей и маршрутизаторов (§5), а также играть роль генератора запросов MLD (§6.4), потому что при участии в работе этих протоколов маршрутизатор не просто может, а даже обязан слать свои сообщения с внутриканального адреса.

    Однако есть как минимум один случай, когда маршрутизатору может понадобиться адрес большей области действия. Представьте себе, что маршрутизатор попытался продвинуть транзитный пакет в канал, MTU которого меньше длины пакета. В этом случае пакет пропадает, а маршрутизатору надо выслать извещение "пакет слишком велик" в адрес источника пакета. Вообще говоря, область этого адреса может быть любой, в том числе глобальной.

    Нетрудно видеть, к чему мы клоним. Даже маршрутизатору иногда приходится выполнять функции хоста, когда он создает пакет с извещением ICMPv6. Высылая такое извещение, он направляет его по адресу источника пакета-виновника. Чтобы такое извещение дошло, его собственный адрес источника должен быть подходящей области действия. Например, если пакет-виновник был послан с глобального адреса, то извещению в общем случае тоже необходим глобальный адрес источника. Ведь в противном случае нет никаких гарантий, что извещение достигнет адресата.

    Откуда маршрутизатор возьмет глобальный адрес? Придется ли назначить один глобальный адрес на каждый интерфейс маршрутизатора? На самом деле, будет достаточно ровно одного адреса на весь маршрутизатор, например, на интерфейсе "петля", если маршрутизатор сможет выбрать адрес источника из всех своих адресов, а не только из адресов выходного интерфейса. Ведь тогда главный критерий выбора, основанный на сравнении областей, автоматически предпочтет глобальный адрес источника.

    Обычному хосту, у которого есть несколько сетевых интерфейсов, тоже может пригодиться эта возможность выбирать из всех своих адресов, хотя ему рекомендовано ограничиться адресами выходного интерфейса [§4 RFC 6724].

    В этом разделе мы говорим "хост", а не "узел", чтобы подчеркнуть, что речь идет об одной из основных функций хоста IP — создавать новые пакеты на выходе. Вторая его функция — это потреблять адресованные ему пакеты на входе. В то же время, для маршрутизатора это нетипичные, дополнительные функции. Главная функция маршрутизатора — продвигать транзитные пакеты. Только особенности управления IP заставляют маршрутизатор заниматься иногда делом, свойственным хосту.

    С другой стороны, пока сам выходной интерфейс обладает подходящим адресом, следует предпочесть его. Это необходимо, в первую очередь, для правильной работы механизма переадресовок из §5.2. Ведь маршрутизатор вернет переадресовку, только если адрес источника в заголовке пакета указывает на соседа по каналу. Поэтому пусть это будет дополнительным критерием: если один адрес-кандидат принадлежит выходному интерфейсу, а другой нет, то побеждает первый из них.

    Также нам следует учесть, что расширения для конфиденциальности из §6.2 делят локальные адреса хоста на два класса: публичные и временные. Эти расширения по умолчанию должны быть выключены, так что их включение на хосте однозначно говорит о желании их активно использовать. Поэтому по умолчанию временный адрес выигрывает у публичного. Тем не менее, у приложения должна быть возможность запросить выбор публичного адреса посредством сетевого API.

    Наконец, если иерархии распределения адресов и маршрутизации IPv6 совпадают, то можно приближенно судить об удаленности двух интерфейсов друг от друга по длине общего префикса их адресов в одной зоне: чем длиннее общий префикс, тем, вероятно, короче трасса между интерфейсами . Поэтому есть определенный резон в том, чтобы предпочесть из двух кандидатов SA и SB тот, у которого длиннее общий префикс с адресом назначения D, по крайней мере, если более сильные критерии выбора дают "ничью". Например, если адрес SB в одной подсети с D, а адрес SA нет, то предпочесть стоит SB.

    Такое сравнение следует ограничить префиксом подсети локального адреса, потому как сравнение идентификаторов интерфейсов не даст требуемой информации. Грубо говоря, если первые 64 бита у сравниваемых адресов оказались равны, то сравнивать последующие биты не имеет смысла.

    Именно эти критерии и именно в таком порядке составляют костяк процедуры выбора адреса источника [§5 RFC 6724]. Кратко перечислим их еще раз:

  • сопоставление областей;
  • предпочтительный адрес против устаревшего;
  • адрес выходного интерфейса пакета против прочих адресов;
  • временный адрес против публичного;
  • сравнение длины префиксов, общих с адресом назначения.
  • Данная процедура возвращает самый подходящий из локальных адресов хоста, но только для заданного адреса назначения D. Обозначим ее как Ист(D) — источник для D.

    Полный список критериев [§5 RFC 6724] длиннее. Прежде всего, он предпочитает адрес источника, равный адресу назначения, в угоду локальной передаче пакетов в пределах хоста. Ведь равенство адресов источника и назначения — самый простой и надежный признак того, что пакет локальный. Прочие критерии помогают работе механизма мобильности узлов, а также позволяют вручную подстраивать DAS с помощью меток и численных весов. Все они, безусловно, важны на практике, но не несут фундаментального значения, и поэтому мы их подробно не рассматриваем. Читатель без труда разберется в них самостоятельно.

    Теперь нам пора вспомнить, что хост также располагает множеством потенциальных адресов назначения. Среди них нельзя выбрать ровно один, самый лучший, потому заранее неизвестно, по какому адресу в итоге ответит удаленная сторона. Хост обязан перебирать их до тех пор, пока попытка связи не приведет к успеху. Тем не менее, адреса назначения тоже не помешает упорядочить так, чтобы наиболее вероятные претенденты на успех оказались в начале списка.

    Значит, нам надо также сформулировать транзитивные критерии сравнения двух адресов назначения, DA и DB. Сделать это можно, опираясь на уже проверенные нами приемы, потому что удаленные адреса все равно видны хосту через призму его собственных сетевых интерфейсов и локально назначенных адресов. Неудивительно, что процедура выбора адреса назначения активно пользуется процедурой Ист(D) и сравнивает характеристики адресов источника, чтобы принять решение насчет потенциальных адресов назначения [§6 RFC 6724].

    Прежде всего, поддержим зонную архитектуру IPv6 таким критерием: выигрывает адрес назначения, адрес источника для которого имеет ту же область. Например, если Обл(DB) = Обл(Ист(DB)), а Обл(DA) $$\ne$$ Обл(Ист(DA)), то выигрывает DB. Безусловно, это не окончательный критерий, так как области могут совпадать, или не совпадать, у обоих кандидатов.

    Если предыдущий критерий дал ничью, то проигрывает адрес назначения, адрес источника для которого устарел. Например, если Ист(DB) устарел, а Ист(DA) нет, то выигрывает DA. Конечно, и здесь может быть ничья, если оба адреса источника находятся на одном этапе жизненного цикла.

    Следующим снова вступает в игру критерий, связанный с областями адресов. Область адреса назначения косвенно говорит о том, насколько длинен и тернист путь к сетевому интерфейсу назначения. Например, если адрес назначения — внутриканальный, то до соответствующего интерфейса всего один шаг, а если он — внутрисайтовый, то трасса до него, по крайней мере, заключена в пределах одной административной зоны, а значит, меньше вероятность сбоя из-за несогласованной политики на границах сетей. Поэтому, при прочих равных, есть смысл предпочесть адрес назначения, область которого меньшей величины.

    Если остальные критерии вернули ничью, то остается только предпочесть адрес назначения, у которого длиннее общий префикс с отвечающим ему адресом источника.

    И снова мы перечислили не все критерии выбора [§6 RFC 6724], обратив внимание только на самые основные из них.

    Нет ли в "созданной" нами процедуре DAS скрытых противоречий? Одна ее деталь, которая вызовет подозрение у любого инженера, знакомого с IPv4, — это использование информации о выходном интерфейсе пакета при выборе адреса источника, то есть до того, как окончательно установлен адрес назначения. Ведь реалии IPv4 таковы, что выбор выходного интерфейса (маршрутизация) проходит на основании адреса назначения пакета.

    Конечно, эту неувязку можно обойти, рассматривая выходной интерфейс как функцию от адреса назначения D, который процедура Ист(D) получает на вход в виде аргумента. В реалиях IPv4 это просто означало бы, что хост использует таблицу маршрутов, чтобы найти выходной интерфейс по адресу назначения.

    Тем не менее, в архитектуру IPv6 заложен иной подход, который у нас постепенно выкристаллизовывается. Путь к нему мы начали, работая над ND и SLAAC, когда установили, что все структуры хоста, включая список маршрутизаторов по умолчанию, привязаны к определенному сетевому интерфейсу, а выбор интерфейса из нескольких доступных вариантов находится за пределами задач ND (см. §5.2). Окончательного решения мы достигнем в §6.8, а сейчас только сделаем очередной необходимый шаг по направлению к нему.

    В §5.2 мы подробно обсудили, что модель хоста IPv6 существенно отличается от того, как устроены реализации хостов и маршрутизаторов IPv4. В то же время, у нас до сих пор нет модели маршрутизатора IPv6. Давайте допустим, что маршрутизатор IPv6 не столь существенно отличается от своего аналога IPv4 и его работа основана на такой же таблице маршрутов, ставящей в соответствие удаленным префиксам адреса следующего шага.

    Пока что единственный случай, когда процедура DAS нужна маршрутизатору IPv6, — это отправка извещения ICMPv6. В этом случае маршрутизатор IPv6 не встретит препятствий, потому что адрес назначения извещения однозначно известен: это адрес источника в пакете-виновнике, вызвавшем извещение. Поэтому маршрутизатор вполне способен определить выходной интерфейс извещения, руководствуясь своей таблицей маршрутов, и только затем провести выбор адреса источника.

    На зону, в которой находится адресат извещения, указывает входной интерфейс пакета-виновника. Если физический маршрутизатор состоит из нескольких виртуальных маршрутизаторов, то этот интерфейс также указывает на тот виртуальный маршрутизатор, чьей таблицей маршрутов надо воспользоваться при отправке извещения ICMPv6.

    Таким образом, случай многих адресов назначения на стадии DAS возникает только в работе хостов IPv6, и нам достаточно сосредоточить внимание на них. Как это ни странно звучит для эксперта по IPv4, но та архитектура хоста IPv6, к которой мы постепенно движемся, вполне допускает, что выбор выходного интерфейса происходит до маршрутизации пакета [§7 RFC 6724]. Например, приложение может само выбрать один из сетевых интерфейсов хоста и вести все свои сетевые диалоги строго через него.

    Обратите внимание, что процедура DAS сработает и в том случае, когда удаленные адреса-кандидаты — групповые, а не индивидуальные. Конечно, в этом случае пробное множество локальных адресов включает в себя только адреса, назначенные выходному интерфейсу, или другому интерфейсу в тот же самый канал, чтобы удовлетворить требованиям групповой маршрутизации [§4 RFC 6724].

    Безусловно, у приложения должна быть возможность управлять аспектами DAS, скажем, предпочесть публичные адреса временным. Для этого даже существует эталонный API [RFC 5014].

    Мы сознательно обошли вниманием тот случай, список удаленных адресов смешанный и содержит как адреса IPv6, так и адреса IPv4. Обсуждение механизмов перехода между IPv4 и IPv6 — очень важная тема, но она выходит за рамки нашего курса. Кроме того, для полноценного знакомства с ней не помешает сперва разобраться, как работает IPv6 в чистом виде. Тем не менее, полезно иметь в виду, что механизм DAS готов справиться и со смешанным случаем.

    Механизм DAS — это по-прежнему объект исследования, поскольку есть мнение, что в сложных сценариях он не всегда дает удовлетворительный результат [RFC 5220, RFC 5221].

    В завершение раздела соберем вместе и приведем все адреса, которые хост IPv6 обязан полагать своими собственными [§2.8 RFC 4291]:

  • адрес обратной связи ::1 (§2.3);
  • по одному обязательному внутриканальному адресу на каждый интерфейс (§2.5);
  • дополнительные индивидуальные адреса (§2.6) и адреса anycast (§5.3), настроенные на интерфейсах хоста вручную (§5.4.1) или автоматически (§5.4.2);
  • общепринятые групповые адреса "все узлы" (§2.8);
  • групповой адрес искомого узла для каждого назначенного хосту индивидуального адреса или адреса anycast (§5.1);
  • адреса всех групп, в которых хост на данный момент состоит (§2.8).

    А маршрутизатор IPv6, помимо всех предписанных хосту адресов, обязан считать своими собственными еще и такие:

  • адреса anycast "маршрутизатор подсети" на всех интерфейсах, где включена функция маршрутизатора (§6.1);
  • общепринятые групповые адреса "все маршрутизаторы" (§2.8).
  • Разрешение доменных имен в среде IPv6

    Предположим, серверы и клиенты DNS учли изменения, предложенные в §2.11. После этого приложение, обращаясь к DNS посредством сетевой библиотеки, может встречаться не только с адресами IPv4, но также с адресами IPv6. Более того, в переходный период вероятен "винегрет" из адресов, когда одному имени сопоставлены адреса обоих семейств. Например:

    www.example.org. IN A 192.0.2.77

    AAAA

    2001:db8:c001::beef

    Правильная работа с адресами из разных семейств пусть будет на совести программистов. Во-первых, они должны избавиться от ложного убеждения , будто все сетевые адреса — IPv4. Во-вторых, им следует принять во внимание правила выбора адресов, к которым мы пришли §6.6. Мы же сейчас позаботимся, чтобы приложение получило по своему запросу полный список адресов, отвечающих данному доменному имени.

    Между клиентом DNS в составе сетевой библиотеки и приложением обычно находится дополнительный уровень абстракции, который позволяет обращаться посредством одного простого API к разным системам разрешения имен: HOSTS.TXT, DNS, NIS и т.п. Скажем, в классическом API BSD Unix приложения использовали функцию gethostbyname(). Оттуда эта функция проникла в стандарт POSIX и в API Windows Sockets. К сожалению, функция gethostbyname() не готова к работе со смешанными семействами сетевых адресов: она может вернуть список адресов только из одного семейства. По умолчанию она возвращает адреса IPv4, а способ переключить ее на IPv6 системно-зависим.

    Например, реализация gethostbyname(), популяризованная пакетом BIND, возвращает адреса IPv6, если была выбрана опция RES_USE_INET6 библиотеки resolver. Эту опцию можно указать под ее текстовым именем inet6 в файле resolv.conf или переменной окружения RES_OPTIONS.

    По всей видимости, нужны новые функции для доступа к системам разрешения имен. Как показывает опыт, пересматривать общепринятый API еще труднее и опаснее, чем модифицировать сетевой протокол. Но раз уж нам придется это сделать, новые функции должны быть максимально гибкими и универсальными. По этой причине мы не можем просто добавить к уже существующей поддержке IPv4 поддержку IPv6.

    Новый API обязан одновременно работать с любыми семействами адресов. С точки зрения программиста, добиться этого несложно: достаточно снабдить каждый адрес обязательным атрибутом, его семейством. Для этого можно, к примеру, хранить двоичное представление адреса не само по себе, а в составе структуры, где еще одно поле указывает семейство данного адреса.

    Этот подход хорошо знаком тем, кто использовал API сокетов Беркли.

    Именно так поступает новая функция getaddrinfo() [§6.1 RFC 3493]. Значение поля "семейство адреса", возвращаемое этой функцией, можно прозрачно передавать функциям из API сокетов Беркли [RFC 3493], так что приложениям не нужно обрабатывать каждое известное им семейство отдельно. Теперь правильно написанное приложение не потребует никаких изменений после появления следующей версии IP или даже перехода на новый стек протоколов с похожими свойствами.

    Каковы будут отношения нового API и зонной адресной архитектуры IPv6? Если узел использует не DNS, а другую систему разрешения имен, которая каким-то образом позволяет ему получать правильные идентификаторы зон zone_id в адресах, то функция getaddrinfo() готова дать ему доступ к этой информации. Она возвращает адреса IPv6 в виде структур sockaddr_in6, где есть поле идентификатора зоны.

    Если же адрес IPv6 получен из записи AAAA, опубликованной в DNS, то доступно только его численное значение, не уточненное идентификатором зоны zone_id. Причины этого были изложены в §2.11. Тем не менее, отсутствие zone_id в записи AAAA еще не значит, что такая запись не может содержать в правой части адрес IPv6 с ограниченной областью действия. К примеру, если организация использует внутрисайтовые адреса, то все они заведомо принадлежат одной зоне — внутренней сети организации. С одной стороны, такие адреса вполне можно поместить в частную зону DNS этой организации. С другой стороны, они не имеют смысла вне данной организации.

    Утечка записей с внутрисайтовыми адресами в Internet может иметь неприятные последствия. Рассмотрим следующий пример. Организация А поместила в свою публичную зону DNS такие записи:

    www.example.org. IN AAAA 2001:db8::beef

    AAAA fec0::beef

    Организация Б тоже использует внутрисайтовые адреса. Ее хост Х собирается обратиться к www.example.org. У хоста Х есть внутрисайтовый адрес, и по правилам выбора (§6.6) хост Х предпочтет удаленный адрес FEC0::BEEF, потому что у него наименьшая область. Но, с точки зрения хоста Х, зона адреса FEC0::BEEF — сайт Б, а не сайт А! Когда хост Х попытается установить сеанс по этому адресу (см. рис. 9.5), возможны три основных исхода, один другого хуже:

  • хост Х не сможет установить сеанс, потому что в сети Б нет узла с адресом FEC0::BEEF; этот исход не фатальный, так как хост Х перейдет к следующему адресу www.example.org, 2001:DB8::BEEF, по тем же правилам выбора;
  • хост Х установит сеанс с легитимным узлом сети Б, чей адрес — FEC0::BEEF; результат будет непредсказуемым;
  • злоумышленник, забравшийся в сеть Б, присвоит себе свободный адрес FEC0::BEEF и без труда перехватит сеанс; вдобавок, хосты могут применять разные политики безопасности к сеансам внутри и вне сайта; например, хост Х может отказаться от аутентификации и шифрования сеанса, полагая, что ведет диалог с доверенным узлом; в результате безопасность пострадает еще сильнее. (рис 9.5) Последствия необдуманной публикации внутрисайтового адреса в DNS
  • Выводы напрашиваются сами собой:

  • >в общедоступной зоне DNS следует регистрировать только глобальные адреса IPv6;
  • клиент DNS не должен возвращать приложению запись из общедоступной зоны DNS, если она содержит адрес IPv6 с ограниченной областью.
  • Выполнить второе правило в общем случае непросто, так как клиент DNS должен правильно классифицировать зоны на доверенные и общедоступные. Однако в типовой частной сети эту задачу можно переложить на рекурсивный сервер DNS. Такой сервер, обрабатывая запросы конечных клиентов, мог бы фильтровать записи в ответах других серверов; в то же время, собственные авторитетные ответы он фильтрации подвергать не будет. Конечно, в этом случае надо запретить обращение клиентов к внешним серверам DNS, например, на уровне политики безопасности сети.

    Работа хоста с несколькими подключениями

    Осторожно: вы вступаете в область исследования и экспериментов. Данный аспект работы IPv6 еще находится на стадии развития.

    Мы завершим нашу умозрительную работу над основами IPv6, рассмотрев такой простой вопрос: каким образом хост IPv6 с несколькими сетевыми подключениями сможет эффективно использовать их? Ведь до сих пор мы старательно закладывали фундамент для ответа на этот вопрос, но от самого ответа столь же старательно уходили.

    Чтобы в полной мере оценить значение этого вопроса, давайте мысленно вернемся во времена господства IPv4 и посмотрим, как тогда обеспечивали отказоустойчивое подключение отдельной сети к Internet.

    Мы, конечно же, помним, что Internet — это "сеть сетей".

    Главным рецептом тогда, как и теперь, было дублирование и резервирование элементов, в данном случае, подключений к другим сетям. Проще говоря, сети было необходимо несколько подключений, желательно, к разным партнерам. Однако, чтобы эти подключения на самом деле работали, надо было каким-то образом донести информацию об адресном пространстве сети в глобальное облако маршрутизации Internet через каждое из подключений.

    Как нетрудно убедиться, в модели маршрутизации IP прикладной трафик распространяется навстречу маршрутной информации.

    Если сеть принадлежала достаточно крупному провайдеру, у которого были статус LIR, автономная система и прочие атрибуты серьезного игрока в этом бизнесе, то сеть объявляла о себе с помощью BGP, и все образовывалось как бы само собой. Это вполне устоявшаяся схема, и LIR IPv6 сообщают о себе точно так же при помощи MBGP.

    Но что если сеть принадлежала абстрактной организации, применявшей свои подключения к Internet для сугубо внутренних целей? Пока адреса IPv4 еще были относительно доступны, такая организация могла получить прямиком из рук RIR автономную систему и отдельный блок адресов, не привязанный к определенному провайдеру , Известный как PI (Provider Independent). после чего подключиться к нескольким провайдерам и, по взаимному с ними соглашению, объявить о себе по BGP. Альтернативой этому было получение блока адресов из пространства провайдера №1 и анонсирование этого блока, по соглашению с провайдером №1, прочими провайдерами той же организации. Независимо от выбранного пути, глобальным следствием этой практики было дробление адресного пространства и рост числа маршрутов в глобальном облаке Internet. Ведь каждая такая сеть была представлена, как минимум, одним маршрутом, а число независимых сетей может на порядки превышать число провайдеров. Неудивительно, что независимые сети попали в немилость современных политик распределения адресов IPv6, принятых RIR.

    Самая "интересная" участь ожидала те сети, которые не были достаточно велики или весомы, чтобы оказаться представленными в глобальном облаке маршрутизации Internet. Им не оставалось ничего иного, кроме как подключиться к нескольким провайдерам, получить от каждого из них по блоку адресов и после этого заняться сооружением пирамиды из различных ухищрений, чтобы обеспечить работу сети по нескольким подключениям. Вот лишь краткий и неполный список препятствий, которые им приходилось преодолевать:

  • Один провайдер вряд ли пропустит от клиента транзитный трафик, адрес источника в котором принадлежит другому провайдеру. Это вполне обоснованное ограничение в рамках борьбы с подделкой адресной информации.
  • Отказоустойчивость на входе, например, для публично доступного сервера внутри сети, можно обеспечить, назначив ему и опубликовав в DNS адреса от разных провайдеров. Однако тогда надо следить, чтобы ответный трафик возвращался в соответствующее подключение — иначе см. первый пункт. Так как обычная маршрутизация не учитывает адрес источника пакета, приходится использовать нестандартные трюки, например, маршрутизацию согласно политике.
  • Отказоустойчивость на выходе, чтобы пользователи сети сохранили доступ к Internet при отказе одного из подключений, возможна только путем сочетания NAT и каких-то доморощенных механизмов наблюдения за состоянием подключений в стиле: "Ping на Google не прошел, значит, провайдер "упал". Переключаем default route…" Ведь у каждого пользовательского хоста IPv4 всего один адрес.
  • Но даже эти ухищрения обладали весьма ограниченными возможностями. В частности, никакими трюками нельзя было сохранить обычное соединение TCP при переключении между провайдерами, потому что менялся локальный адрес сокета. В результате, чем меньше была сеть, тем трудней ей давалось надежное подключение к Internet.

    Поэтому сейчас, когда пришло время перемен, стоит перенести фокус с масштабных механизмов отказоустойчивости, таких как BGP, на средства, доступные индивидуальному пользователю. Иными словами, мы хотим осчастливить все сети независимо от их размера, а самая маленькая сеть — это один хост. Именно на хосте IPv6 мы и заострим свое внимание.

    Поставим себя на место владельца хоста IPv6, которому необходимо надежное подключение к Internet, работоспособность которого не зависит от одного провайдера. Как бы мы поступили в этой ситуации?

    Первый вариант (рис. 9.6) состоит в том, чтобы установить два (или больше) сетевых интерфейса и одновременно подключиться к двум (или больше) разным провайдерам (в надежде, что они независимы). При этом каждый сетевой интерфейс получит адрес из блока соответствующего провайдера. Эталонный хост IPv6 на базовом уровне уже готов к работе в такой конфигурации, потому что с каждым его интерфейсом связан отдельный список маршрутизаторов по умолчанию.

    Можно сказать, что эталонный хост IPv6 с самого начала поддерживает виртуализацию сетевого стека.

    (рис 9.6) Множественное подключение по разным каналам

    Второй вариант (рис. 9.7) возможен, если есть канал, на котором уже присутствуют маршрутизаторы нескольких провайдеров (или маршрутизаторы, подключенные к разным провайдерам). Тогда достаточно подключить хост к одному каналу, но назначить его сетевому интерфейсу несколько адресов, по одному от каждого провайдера. В этом случае у хоста будет всего один список маршрутизаторов по умолчанию, но в нем будут представлены все провайдеры.

    (рис 9.7) Множественное подключение по одному каналу

    На самом деле, это только первый шаг к отказоустойчивому подключению. В обоих вариантах он известен как множественное подключение (multihoming). Но что он дает хосту? Непосредственно хост может обнаружить только сбой канала или маршрутизатора, используя механизм NUD (§5.1). Что ж, и это неплохо, так как хост может переключиться на другого провайдера, используя стандартные механизмы.

    Если же сбой будет где-то дальше в сети провайдера, то явного указания на это хост не получит. Что же произойдет в этом случае? Рассмотрим для примера два сценария, в которых хост принимает соединения TCP извне или же устанавливает их с внешними хостами сам.

    Допустим, что в первом сценарии все адреса хоста опубликованы в DNS под одним именем. Тогда удаленное приложение, получив на входе имя, станет пробовать эти адреса по очереди, в порядке, предписанном DAS (§6.6). Так как трасса к разным адресам назначения будет проходить через разных провайдеров ввиду самого устройства маршрутизации IP, рано или поздно удаленная сторона выберет доступный адрес назначения, и первый пакет <SYN> дойдет до хоста, скажем, через провайдера №2. Теперь хосту надо позаботиться, чтобы ответный пакет <SYN,ACK> ушел через того же провайдера №2, потому что именно он работоспособен и, вероятно, готов пропустить такой пакет. Эта задача сводится к выбору выходного интерфейса (вариант рис. 9.6) или маршрутизатора (вариант рис. 9.7). В первом случае решение таково: сокет IPv6 должен быть привязан к определенному сетевому интерфейсу хоста. Например, в данном случае пакет <SYN> придет по адресу, который назначен интерфейсу №2, а значит, и ответный пакет <SYN,ACK> надо направить в тот же интерфейс. Во втором случае хосту понадобится дополнительное правило при выборе маршрутизатора, чтобы пакет с адресом источника от провайдера №2 ушел именно через маршрутизатор №2. Благодаря записи в кэше адресатов DC, которая возникнет в результате этого выбора, последующие пакеты данного соединения пойдут через маршрутизатор №2 естественным порядком.

    В нормативных документах IPv6 такого правила пока что нет, хотя оно обсуждалось [draft-huitema-multi6-hosts , Kenji Ohira. IPv6 Address Assignment and Route Selection for End-to-End Multihoming. IETF Meeting №57. http://ops.ietf.org/multi6/ietf57/day1/IETF-57-ohira-multi6.ppt .

    Если один и тот же удаленный адрес одновременно установит соединения с разными адресами локального хоста через разных провайдеров, эта схема даст сбой. Чтобы восстановить ее работоспособность, запись DC должна включать в себя адрес источника, чтобы разным локальным адресам отвечали разные записи DC даже при одном и том же удаленном адресе назначения.

    Перейдем теперь ко второму сценарию, когда хост сам хочет установить соединение TCP с удаленной стороной, послав для начала пакет <SYN>. Конечно, может оказаться, что у удаленного хоста тоже есть несколько адресов и только некоторые из них недоступны через текущего провайдера. Тогда поможет обычный перебор удаленных адресов. Но как быть, если произошел полный сбой текущего провайдера? Как мы уже сказали, хост может обнаружить его только по косвенным признакам. Здесь одна из возможных линий поведения хоста — попробовать все свои выходные интерфейсы (вариантрис. 9.6) или все маршрутизаторы (вариант рис. 9.7) по очереди. В первом случае процедура DAS выберет правильный адрес источника для пакета <SYN>. Во втором случае надо, чтобы выбор маршрутизатора влиял на выбор адреса источника, а это вполне допустимое расширение процедуры DAS [§5 и §7 RFC 6724].

    Таким образом, множественное подключение хоста IPv6 можно использовать, практически не выходя за рамки стандартных механизмов. Тем не менее, мы так и не обеспечили одной простой возможности: сохранить уже установленное соединение TCP при переключении хоста между провайдерами. Как нетрудно убедиться, такое переключение по-прежнему вызывает смену локального адреса, и соединение TCP рвется. С этим нельзя ничего поделать, по крайней мере, на уровне IP.

    От этой проблемы проще всего отмахнуться, сказав, что прикладные протоколы должны быть устойчивы к сбоям на транспортном уровне. Несомненно, это так. Скажем, протоколы FTP и HTTP поддерживают "докачку", и эта возможность не чувствительна к адресам IP: клиент просто откроет новый сеанс и снова запросит тот же файл или объект, начиная с энного байта.

    Однако это скучный подход, который не делает чести нашему воображению. Давайте лучше применим фантазию и в общих чертах набросаем решение поинтереснее. Прийти к нему несложно. Для этого представим себе, что приложение открывает одно соединение TCP сразу с множеством адресов удаленной стороны. Действительно, зачем перебирать их по одному, если можно было бы послать по пакету <SYN> сразу им всем. Тогда и локальный конец соединения мог бы иметь не один адрес, а целое их множество.

    "В лоб" такое решение можно сымитировать, открыв M*N соединений TCP, где N — число локальных адресов, а M — число удаленных адресов. Однако это непрактичный подход, так как, во-первых, число таких соединений может быть велико, а во-вторых, ими надо дополнительно управлять, чтобы сохранить порядок байтов в потоке. Ведь получателю надо как-то узнать, в каком порядке были посланы байты, пришедшие по разным соединениям.

    Поэтому нам понадобится более целостное решение. Так, TCP можно заменить новым транспортным протоколом, который с самого начала станет поддерживать больше одного адреса на каждом из концов соединения. Конечно, пакет IPv6 по-прежнему содержит только один адрес источника и один адрес назначения, так что вся работа по слежению за доступностью адресов и переключению между ними ляжет на этот новый транспортный протокол. На самом деле, такой протокол уже создан: SCTP. Прелесть этого решения в том, что оно сквозное: хостам безразлично, где именно в сети произошел сбой, и пока существует хотя бы одна пара адресов, межу которой пакеты все еще ходят, транспортное соединение будет жить.

    Однако новый транспортный протокол — это довольное радикальное новшество, которое подходит только для новых приложений. Кроме того, с новым транспортным протоколом связана та трудность, что всевозможные межсетевые экраны, системы предотвращения вторжений и прочие "ящики" (middleboxes), ведущие глубокий анализ транзитного трафика, уже хорошо знакомы с TCP, но новый протокол наверняка вызовет проблемы. Поэтому нельзя ли перенести все те же идеи обратно в старый добрый TCP, чтобы и существующие приложения выиграли от новой архитектуры множественных подключений?

    В основу такого "многопутевого TCP" (multipath TCP, MPTCP) следует заложить принципы совместимости с приложениями и сетью [RFC 6182]. Что это значит? С одной стороны, сетевой API, например, сокеты Беркли, должен остаться, по возможности, без изменений, чтобы уже существующие приложения не пришлось даже перекомпилировать, не говоря уже о том, чтобы их переписывать. А с другой стороны, для стороннего наблюдателя в сети каждый путь MPTCP (известный как под-поток, sub-flow) должен выглядеть как обычное соединение TCP. Это нужно, чтобы MPTCP беспрепятственно проходил сквозь различные "ящики".

    В частности, у каждого под-потока своя, независимая от других под-потоков и непрерывная, как и положено в TCP, нумерация байтов, то есть порядковые номера и номера квитанций. Если бы нумерация байтов была одна на все под-потоки, протокол стал бы проще, но достаточно строгий "ящик" вполне мог бы блокировать под-поток с "прорехами" в нумерации байтов, потому что тот нарушает протокол TCP. Соответствие между номерами байтов в под-потоке и всем соединении MPTCP задает та дополнительная информация, которую несут прозрачные для "ящиков" опции MPTCP.

    Фактически мы вернулись к идее множественных соединений TCP между разными парами локальных и удаленных адресов, но под управлением четко заданного механизма. Чтобы работать по-настоящему эффективно, MPTCP должен управлять не только взаимной доступностью разных пар адресов, но и перегрузкой вдоль разных путей, перенося трафик на те пути, где перегрузка меньше [RFC 6356]. Для переноса служебной информации MPTCP вполне подходят опции TCP, которые активно используются и для других целей, а потому без труда преодолевают "ящики" [draft-ietf-mptcp-multiaddressed]. Благодаря дополнительной информации, MPTCP вновь собирает исходный поток байтов из нескольких под-потоков, а потому он способен вести одновременную передачу данных вдоль нескольких путей.

    Конечно, MPTCP не зависит от особенностей IPv6. Более того, он позволяет соединению TCP одновременно использовать пути IPv4 и IPv6.

    А как насчет других протоколов, уже работающих непосредственно поверх IPv6? В их случае можно испытать иной подход. Представим себе, что между всеми этими протоколами и уровнем IPv6 появляется "прокладка", которая манипулирует адресами в пакетах, так что вышестоящий протокол всегда имеет дело с одной парой адресов (локальный и удаленный), как и раньше. Этой "прокладке", известной как Shim6 (Прокладка №6) [RFC 5533 , A. Garcia-Martinez, M. Bagnulo, I. van Beijnum. The Shim6 architecture for IPv6 multihoming. IEEE Communications Magazine, 2010, v. 48, issue 9, pp. 152–157 http://www.networks.imdea.org/Portals/8/Downloads/Publications/Shim6-Arquitecture-2010-EN.pdf ], тогда придется динамически транслировать неизменные адреса "сверху" в зависящие от текущей обстановки в сети адреса "снизу" и наоборот. Эта трансляция превращает адреса, видные вышестоящим протоколам, в идентификаторы, не зависящие от сетевой топологии, тогда как адреса, действительно попадающие в сеть, становятся локаторами, чувствительными к сетевой обстановке. Поэтому первые из них получили название "идентификаторы вышележащего уровня" (Upper-Layer Identifier, ULID). Конечно, адрес ULID остается обычным адресом IPv6 и может выполнять заодно роль локатора, пока соответствующий сетевой интерфейс доступен. Так как Shim6 тесно соприкасается с IPv6, свою служебную информацию он передает в виде заголовков расширения. Информацию о доступности адресов-локаторов для нужд Shim6 поставляет вспомогательный протокол REAP (Reachability Protocol ) [RFC 5534].

    Самостоятельно обсудите, вызовет ли Shim6 те же трудности, что и NAT, у прикладных протоколов, передающих адреса IP внутри своих сообщений. К таким протоколам относятся, например, FTP и SIP.

    Мы не станем сейчас разбирать технические детали MPTCP и Shim6, потому что они довольно сложны и выходят за рамки нашего курса. Тем не менее, уже сказанного достаточно, чтобы увидеть, как снова победила сквозная модель: эффективное использование независимых подключений оказалось по силам самим хостам, а IPv6 предлагает почти все необходимые для этого механизмы.

    Конечно, передача данных по независимым подключениям создает и новые проблемы. Одна из них состоит в том, как локальный хост убедится, что все завяленные адреса принадлежат одному удаленному хосту. Ведь иначе злоумышленник может перехватить поток данных, выдав себя за один из адресов удаленного хоста. Это в особенности касается протоколов вроде Shim6, в которых стороны не обмениваются информацией обо всех своих адресах заранее.

    Связать воедино несколько адресов можно, снова воспользовавшись идентификатором интерфейса как носителем защитной информации. Допустим, хосту необходимо назначить $$N$$ адресов, все в разных подсетях. То есть на входе мы получаем список префиксов подсетей $$\big\{P_i \big\}$$, где$$1\leqslant i\leqslant N$$. Тогда идентификатор интерфейса$$L_j$$ для префикса$$P_j$$ можно вычислить как хэш-функцию всех префиксов, например, так:

    $$L_j=H(M\mid P_j\mid\big\{P_i \big\})$$.

    То есть хэш-функция $$H$$ вычисляется от цепочки битов, полученной сцеплением какого-то дополнительного параметра M, известного обеим сторонам протокола, данного префикса $$P_j$$ и всего списка префиксов$$\big\{P_i \big\}$$. В результате мы получаем набор из $$N$$ адресов вида:

    $$A_j=P_j\mid L_j$$.

    То есть каждый адрес получен сцеплением префикса подсети и отвечающего ему идентификатора интерфейса, вычисленного по вышеприведенному рецепту. Составленный таким образом адрес известен как адрес, основанный на хэше (Hash-Based Address , HBA) [RFC 5535]. Он имеет смысл только в составе всего набора $$\big\{A_i \big\}$$, известного как набор HBA (HBA-Set). Точнее, вне набора это обычный адрес IPv6 без дополнительных защитных свойств.

    Параметр M нужен, как минимум, для того, чтобы разные хосты, подключенные к одним и тем же подсетям, могли получить разные наборы HBA. Кроме того, адрес HBA случайный, а значит, есть вероятность конфликта с уже назначенным адресом. В этом случае конфликт обнаружит процедура DAD (§5.4.1). Но, чтобы разрешить конфликт, придется варьировать параметр M и повторить вычисление набора HBA. На практике параметр M составной и содержит в себе несколько полей, отвечающих за разные свойства набора HBA: уникальность, устойчивость к конфликтам, совместимость с CGA (§6.3).

    Если хэш-функция надежная, то порядок сцепления отдельных элементов в цепочку-аргумент неважен, пока один и тот же порядок принят всеми сторонами протокола. На практике одно поле M, а именно счетчик коллизий, возникает между префиксом $$P_j$$ и списком префиксов ради совместимости с CGA. Это поле увеличивается на единицу, если процедура DAD обнаружила коллизию. Благодаря этому повторное вычисление дает другие случайные адреса.

    Теперь мысленно перенесемся на другую сторону протокола и подумаем, как с помощью механизма HBA проверить адрес$$A$$, на который претендует удаленная сторона. Пусть этот адрес состоит из префикса подсети$$P$$ и идентификатора интерфейса $$L$$. Суть проверки сводится вот к чему: принадлежит ли данный адрес $$A$$ данному набору HBA? Чтобы провести такую проверку, проверяющей стороне потребуется параметр $$M$$ и список префиксов $$\big\{P_i \big\}$$. Именно эти два элемента и определяют набор HBA, потому что все остальное можно вычислить на месте. Так, проверяющая сторона первым делом проверяет, содержится ли префикс $$P$$ из адреса$$A$$ в списке $$\big\{P_i \big\}$$. Если это не так, то налицо подлог или обычный сбой. Если же префикс — в списке, то осталось вычислить его ожидаемый идентификатор интерфейса HBA:

    $$L_{HBA}=H(M\mid P_j\mid\big\{P_i \big\})$$.

    Если адрес $$A$$ действительно из данного набора HBA, то его идентификатор интерфейса $$L$$ обязан совпасть с вычисленным значением $$L_{HBA}$$, с точностью до битов U/L и I/G.

    Дело осталось за малым: передать параметр M и список префиксов $$\big\{P_i \big\}$$ проверяющей стороне. Оказывается, что для этого не нужен новый протокол, так как всю необходимую информацию можно поместить в блок параметров CGA (рис. 9.3). Формат этого блока достаточно гибок и расширяем, чтобы в нем нашлось место для списка префиксов, а модификатор уже в нем содержится. В результате можно составлять адреса, которые одновременно удовлетворяют нормативам CGA и HBA, потому что их идентификаторы интерфейса стандартным образом зависят и от открытого ключа хоста, и от его списка префиксов. Это чудесным образом избавляет нас от конфликта между механизмами CGA и HBA.

    В блоке параметров CGA уже есть случайный модификатор, префикс подсети P, и счетчик коллизий — они отвечают подстроке $$M\mid P$$ в аргументе хэш-функции. Список префиксов отправится в конец блока как расширение CGA. В результате весь аргумент хэш-функции будет отформатирован как блок параметров CGA, а HBA превратится в совместимое расширение CGA.

    Конечно, простой пакет HBA не пройдет криптографическую проверку CGA, поскольку он не подписан закрытым ключом. Вместо настоящего ключа HBA-без-CGA использует случайную цепочку битов в формате ключа RSA. Тем не менее, адрес HBA содержит в себе годный отпечаток этого случайного ключа.

    Как и можно было ожидать, HBA наследует у CGA ограничение на длину префикса подсети P: она обязана равняться 64 битам.

    Вернуться к учебному плану