Механизм управления фрагментацией IPv6 требует дополнительных соображений и комментариев, потому что он существенным образом отличается от его аналога в IPv4. Главное отличие, конечно же, в том, что фрагментацию IPv6 может вести только источник, хотя информация об оптимальной длине пакета скрыта дальше в сети.
В первую очередь, такая особенность механизма фрагментации IP влияет на работу транспортных протоколов. Начнем с хрестоматийного примера TCP. Раньше, в среде IPv4, перед TCP было два основных пути:
Более хитроумная реализация TCP могла пробовать второй путь, но переключаться на первый, если PMTUD не работает, например, из-за слишком строгой фильтрации пакетов ICMP в сети.
В среде IPv6 первый путь полностью закрыт, потому что флаг DF неявно установлен всегда, а пакеты, длина которых больше PMTU, не достигнут адресата ни при каких условиях. Теперь сам источник пакета должен выбрать его подходящий размер. Как следствие, при переходе к IPv6 растет важность правильной и надежной работы PMTUD.
В первую очередь, этот факт касается сетевых администраторов, увлеченных борьбой с "нежелательным" трафиком ICMP.
Тем не менее, даже ненадежная сигнализация ICMP не мешает проводить PMTUD, используя сквозной
Тем не менее, PMTUD — не абсолютная необходимость даже в среде IPv6. Теперь минимальный размер MTU равен относительно большой величине, 1280 байт, так что примитивная реализация TCP может себе позволить ориентироваться на эту постоянную. Так же может поступить и более сложный вариант TCP, если он обнаружит, что PMTUD все-таки не работает.
Итак, в среде IPv6 перед TCP открываются следующие пути:
TCP — это пример транспортного протокола, который может плавно варьировать размер сегмента и поэтому не нуждается во фрагментации на уровне IPv6. Совсем другую картину мы увидим, если перенесем наш взгляд на блочные транспортные протоколы и их потребителей.
Представим себе, что некий прикладной протокол работает поверх UDP. В этом случае размер исходящей дейтаграммы задает приложение, и размер этот может достигать внушительной величины. Это не наша фантазия: в реально существующих протоколах, например, в NFS, возникают сообщения длиной порядка нескольких тысяч байт. Как подобная дейтаграмма достигнет адресата?
Первый подход мы уже нащупали: приложение может посредством сетевого API попросить локальный модуль IPv6, чтобы тот фрагментировал пакеты исходя из MTU 1280 байт. Это не самый эффективный, зато простой и надежный путь: из-за своего чрезмерного размера пакеты теряться заведомо не будут.
Тем не менее, пакеты теряются еще по ряду причин, и прикладной протокол должен быть устойчив (или безразличен) к таким потерям. Данное свойство прикладных протоколов, работающих поверх UDP, позволяет оптимизировать размер фрагмента IPv6. Для этого надо, чтобы модуль IPv6 сам выполнял PMTUD. Как это будет работать? Например, так:
Это приближение будет довольно точным благодаря обязательному значению MTU в сообщении "пакет слишком велик". В классическом IPv4 источнику приходилось вести вместо этого поиск вслепую, так как маршрутизатор не подсказывал значения MTU. Впрочем, двоичный поиск сходится довольно быстро. Подумайте, в каком случае двоичный поиск вслепую выиграет у поиска с подсказкой маршрутизатора. (Например, когда число шагов достаточно велико, а MTU каждого нового шага меньше предыдущего - "телескопическая трасса").
И так до тех пор, пока приближение PMTU не станет меньше или равно его истинному значению для данной трассы. После этого фрагментированные пакеты начнут доходить до адресата, и прикладной сеанс продолжится. Чтобы подобная схема работала, модуль IPv6 должен кэшировать значения PMTU для своих недавних адресатов. Подходящее место для этих сведений — кэш адресатов, DC [§5.2 RFC 1981].
Как мы помним, трасса — это путь по сети, от источника к адресату через транзитные узлы, который проходит (или прошел бы) пакет в данный момент времени. Для простоты мы сейчас не будем говорить о маршрутизации от источника, когда трассу выбирает источник пакета, и маршрутизации согласно политике, когда выбор следующего шага основан на произвольных характеристиках пакета и внешних параметрах. Тогда трасса IP зависит от узла-источника и от адреса назначения пакета, а также от настроек источника и каждого транзитного узла в момент обработки пакета (грубо говоря, от их таблиц маршрутов). Конечно, в "живой" сети трасса может со временем меняться вместе с настройками источника и транзитных узлов. Однако, пока сеть стабильна, трасса остается постоянной хотя бы какое-то время. Именно поэтому источник имеет право кэшировать значения PMTU, используя в роли ключа адрес назначения.
Однозначность соответствия между адресатом и наблюдаемой величиной PMTU может оказаться нарушена, если сеть использует ECMP и пакеты одному адресату могут приходить по разным путям с неодинаковым значением PMTU. Между тем, существующие реализации хоста IP кэшируют одно значение PMTU на адресата еще со времен IPv4, а IPv6 лишь стандартизует эту практику. Поэтому надо с осторожностью применять распределяющие хэш-функции ECMP, зависящие от чего-либо сверх адреса назначения пакета, например, номера вышестоящего протокола или портов TCP/UDP. (Обсудите самостоятельно, насколько допустимо, чтобы такая хэш-функция зависела от адреса источника пакета.)
Еще один путь открывается, когда прикладной протокол позволяет варьировать размер сообщений, например, если приложение сегментирует некий
поток байтов или более крупных единиц данных. В этом случае приложение могло бы выбрать такой размер своих сообщений, чтобы их
фрагментация на данной сетевой трассе не требовалась
Наконец, оптимистичное приложение, которое верит в возможности PMTUD, захочет запретить модулю IPv6 фрагментацию своих исходящих пакетов. Такое приложение должно активно следить за текущим приближением PMTU и корректировать размер своих сообщений. Модуль IPv6 и приложение проводят PMTUD в паре: модуль IPv6 обрабатывает извещения ICMPv6 и оценивает PMTU, а приложение генерирует новые пробные пакеты согласно последней оценке.
Именно эти идеи нашли отражение в расширенном API для IPv6, основанном на сокетах Беркли [RFC 3542]. Приложение управляет фрагментацией и PMTUD с помощью опций сокета из Табл. 9.1 на уровне IPPROTO_IPV6 [§11 RFC 3542]. Некоторые опции можно избирательно устанавливать для отдельных сообщений с помощью служебной структуры cmsghdr.
| Опция | Смысл |
|---|---|
| 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
находил по таблице маршрутов выходной интерфейс, а затем брал из списка адресов интерфейса самый
У процедуры 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) — не больше
Когда область адреса назначения Обл(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]:
А маршрутизатор 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 системно-зависим.
По всей видимости, нужны новые функции для доступа к системам разрешения имен. Как показывает опыт, пересматривать общепринятый API еще труднее и опаснее, чем модифицировать сетевой протокол. Но раз уж нам придется это сделать, новые функции должны быть максимально гибкими и универсальными. По этой причине мы не можем просто добавить к уже существующей поддержке IPv4 поддержку IPv6.
Новый 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), возможны три основных исхода, один другого хуже:
(рис 9.5) Последствия необдуманной публикации внутрисайтового адреса в DNS
Выводы напрашиваются сами собой:
Выполнить второе правило в общем случае непросто, так как клиент DNS должен правильно классифицировать зоны на доверенные и общедоступные. Однако в типовой частной сети эту задачу можно переложить на рекурсивный сервер DNS. Такой сервер, обрабатывая запросы конечных клиентов, мог бы фильтровать записи в ответах других серверов; в то же время, собственные авторитетные ответы он фильтрации подвергать не будет. Конечно, в этом случае надо запретить обращение клиентов к внешним серверам DNS, например, на уровне политики безопасности сети.
Осторожно: вы вступаете в область исследования и экспериментов. Данный аспект работы IPv6 еще находится на стадии развития.
Мы завершим нашу умозрительную работу над основами IPv6, рассмотрев такой простой вопрос: каким образом хост IPv6 с несколькими сетевыми подключениями сможет эффективно использовать их? Ведь до сих пор мы старательно закладывали фундамент для ответа на этот вопрос, но от самого ответа столь же старательно уходили.
Чтобы в полной мере оценить значение этого вопроса, давайте мысленно вернемся во времена господства IPv4 и посмотрим, как тогда обеспечивали отказоустойчивое подключение отдельной сети к Internet.
Мы, конечно же, помним, что Internet — это "сеть сетей".
Главным рецептом тогда, как и теперь, было дублирование и резервирование элементов, в данном случае, подключений к другим сетям. Проще говоря, сети было необходимо несколько подключений, желательно, к разным партнерам. Однако, чтобы эти подключения на самом деле работали, надо было каким-то образом донести информацию об адресном пространстве сети в глобальное облако маршрутизации Internet через каждое из подключений.
Как нетрудно убедиться, в модели маршрутизации IP прикладной трафик распространяется навстречу маршрутной информации.
Если сеть принадлежала достаточно крупному провайдеру, у которого были статус LIR, автономная система и прочие атрибуты серьезного игрока в этом бизнесе, то сеть объявляла о себе с помощью BGP, и все образовывалось как бы само собой. Это вполне устоявшаяся схема, и LIR IPv6 сообщают о себе точно так же при помощи MBGP.
Но что если сеть принадлежала абстрактной организации, применявшей свои подключения к Internet для сугубо внутренних целей? Пока
адреса IPv4 еще были относительно доступны, такая организация могла получить прямиком из рук RIR автономную систему и отдельный блок
адресов, не привязанный к определенному провайдеру
Самая "интересная" участь ожидала те сети, которые не были достаточно велики или весомы, чтобы оказаться представленными в глобальном облаке маршрутизации Internet. Им не оставалось ничего иного, кроме как подключиться к нескольким провайдерам, получить от каждого из них по блоку адресов и после этого заняться сооружением пирамиды из различных ухищрений, чтобы обеспечить работу сети по нескольким подключениям. Вот лишь краткий и неполный список препятствий, которые им приходилось преодолевать:
Но даже эти ухищрения обладали весьма ограниченными возможностями. В частности, никакими трюками нельзя было сохранить обычное соединение 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
Если один и тот же удаленный адрес одновременно установит соединения с разными адресами локального хоста через разных провайдеров, эта схема даст сбой. Чтобы восстановить ее работоспособность, запись 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
Самостоятельно обсудите, вызовет ли 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 было два основных пути:
Более хитроумная реализация TCP могла пробовать второй путь, но переключаться на первый, если PMTUD не работает, например, из-за слишком строгой фильтрации пакетов ICMP в сети.
В среде IPv6 первый путь полностью закрыт, потому что флаг DF неявно установлен всегда, а пакеты, длина которых больше PMTU, не достигнут адресата ни при каких условиях. Теперь сам источник пакета должен выбрать его подходящий размер. Как следствие, при переходе к IPv6 растет важность правильной и надежной работы PMTUD.
В первую очередь, этот факт касается сетевых администраторов, увлеченных борьбой с "нежелательным" трафиком ICMP.
Тем не менее, даже ненадежная сигнализация ICMP не мешает проводить PMTUD, используя сквозной
Тем не менее, PMTUD — не абсолютная необходимость даже в среде IPv6. Теперь минимальный размер MTU равен относительно большой величине, 1280 байт, так что примитивная реализация TCP может себе позволить ориентироваться на эту постоянную. Так же может поступить и более сложный вариант TCP, если он обнаружит, что PMTUD все-таки не работает.
Итак, в среде IPv6 перед TCP открываются следующие пути:
TCP — это пример транспортного протокола, который может плавно варьировать размер сегмента и поэтому не нуждается во фрагментации на уровне IPv6. Совсем другую картину мы увидим, если перенесем наш взгляд на блочные транспортные протоколы и их потребителей.
Представим себе, что некий прикладной протокол работает поверх UDP. В этом случае размер исходящей дейтаграммы задает приложение, и размер этот может достигать внушительной величины. Это не наша фантазия: в реально существующих протоколах, например, в NFS, возникают сообщения длиной порядка нескольких тысяч байт. Как подобная дейтаграмма достигнет адресата?
Первый подход мы уже нащупали: приложение может посредством сетевого API попросить локальный модуль IPv6, чтобы тот фрагментировал пакеты исходя из MTU 1280 байт. Это не самый эффективный, зато простой и надежный путь: из-за своего чрезмерного размера пакеты теряться заведомо не будут.
Тем не менее, пакеты теряются еще по ряду причин, и прикладной протокол должен быть устойчив (или безразличен) к таким потерям. Данное свойство прикладных протоколов, работающих поверх UDP, позволяет оптимизировать размер фрагмента IPv6. Для этого надо, чтобы модуль IPv6 сам выполнял PMTUD. Как это будет работать? Например, так:
Это приближение будет довольно точным благодаря обязательному значению MTU в сообщении "пакет слишком велик". В классическом IPv4 источнику приходилось вести вместо этого поиск вслепую, так как маршрутизатор не подсказывал значения MTU. Впрочем, двоичный поиск сходится довольно быстро. Подумайте, в каком случае двоичный поиск вслепую выиграет у поиска с подсказкой маршрутизатора. (Например, когда число шагов достаточно велико, а MTU каждого нового шага меньше предыдущего - "телескопическая трасса").
И так до тех пор, пока приближение PMTU не станет меньше или равно его истинному значению для данной трассы. После этого фрагментированные пакеты начнут доходить до адресата, и прикладной сеанс продолжится. Чтобы подобная схема работала, модуль IPv6 должен кэшировать значения PMTU для своих недавних адресатов. Подходящее место для этих сведений — кэш адресатов, DC [§5.2 RFC 1981].
Как мы помним, трасса — это путь по сети, от источника к адресату через транзитные узлы, который проходит (или прошел бы) пакет в данный момент времени. Для простоты мы сейчас не будем говорить о маршрутизации от источника, когда трассу выбирает источник пакета, и маршрутизации согласно политике, когда выбор следующего шага основан на произвольных характеристиках пакета и внешних параметрах. Тогда трасса IP зависит от узла-источника и от адреса назначения пакета, а также от настроек источника и каждого транзитного узла в момент обработки пакета (грубо говоря, от их таблиц маршрутов). Конечно, в "живой" сети трасса может со временем меняться вместе с настройками источника и транзитных узлов. Однако, пока сеть стабильна, трасса остается постоянной хотя бы какое-то время. Именно поэтому источник имеет право кэшировать значения PMTU, используя в роли ключа адрес назначения.
Однозначность соответствия между адресатом и наблюдаемой величиной PMTU может оказаться нарушена, если сеть использует ECMP и пакеты одному адресату могут приходить по разным путям с неодинаковым значением PMTU. Между тем, существующие реализации хоста IP кэшируют одно значение PMTU на адресата еще со времен IPv4, а IPv6 лишь стандартизует эту практику. Поэтому надо с осторожностью применять распределяющие хэш-функции ECMP, зависящие от чего-либо сверх адреса назначения пакета, например, номера вышестоящего протокола или портов TCP/UDP. (Обсудите самостоятельно, насколько допустимо, чтобы такая хэш-функция зависела от адреса источника пакета.)
Еще один путь открывается, когда прикладной протокол позволяет варьировать размер сообщений, например, если приложение сегментирует некий
поток байтов или более крупных единиц данных. В этом случае приложение могло бы выбрать такой размер своих сообщений, чтобы их
фрагментация на данной сетевой трассе не требовалась
Наконец, оптимистичное приложение, которое верит в возможности PMTUD, захочет запретить модулю IPv6 фрагментацию своих исходящих пакетов. Такое приложение должно активно следить за текущим приближением PMTU и корректировать размер своих сообщений. Модуль IPv6 и приложение проводят PMTUD в паре: модуль IPv6 обрабатывает извещения ICMPv6 и оценивает PMTU, а приложение генерирует новые пробные пакеты согласно последней оценке.
Именно эти идеи нашли отражение в расширенном API для IPv6, основанном на сокетах Беркли [RFC 3542]. Приложение управляет фрагментацией и PMTUD с помощью опций сокета из Табл. 9.1 на уровне IPPROTO_IPV6 [§11 RFC 3542]. Некоторые опции можно избирательно устанавливать для отдельных сообщений с помощью служебной структуры cmsghdr.
| Опция | Смысл |
|---|---|
| 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
находил по таблице маршрутов выходной интерфейс, а затем брал из списка адресов интерфейса самый
У процедуры 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) — не больше
Когда область адреса назначения Обл(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]:
А маршрутизатор 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 системно-зависим.
По всей видимости, нужны новые функции для доступа к системам разрешения имен. Как показывает опыт, пересматривать общепринятый API еще труднее и опаснее, чем модифицировать сетевой протокол. Но раз уж нам придется это сделать, новые функции должны быть максимально гибкими и универсальными. По этой причине мы не можем просто добавить к уже существующей поддержке IPv4 поддержку IPv6.
Новый 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), возможны три основных исхода, один другого хуже:
(рис 9.5) Последствия необдуманной публикации внутрисайтового адреса в DNS
Выводы напрашиваются сами собой:
Выполнить второе правило в общем случае непросто, так как клиент DNS должен правильно классифицировать зоны на доверенные и общедоступные. Однако в типовой частной сети эту задачу можно переложить на рекурсивный сервер DNS. Такой сервер, обрабатывая запросы конечных клиентов, мог бы фильтровать записи в ответах других серверов; в то же время, собственные авторитетные ответы он фильтрации подвергать не будет. Конечно, в этом случае надо запретить обращение клиентов к внешним серверам DNS, например, на уровне политики безопасности сети.
Осторожно: вы вступаете в область исследования и экспериментов. Данный аспект работы IPv6 еще находится на стадии развития.
Мы завершим нашу умозрительную работу над основами IPv6, рассмотрев такой простой вопрос: каким образом хост IPv6 с несколькими сетевыми подключениями сможет эффективно использовать их? Ведь до сих пор мы старательно закладывали фундамент для ответа на этот вопрос, но от самого ответа столь же старательно уходили.
Чтобы в полной мере оценить значение этого вопроса, давайте мысленно вернемся во времена господства IPv4 и посмотрим, как тогда обеспечивали отказоустойчивое подключение отдельной сети к Internet.
Мы, конечно же, помним, что Internet — это "сеть сетей".
Главным рецептом тогда, как и теперь, было дублирование и резервирование элементов, в данном случае, подключений к другим сетям. Проще говоря, сети было необходимо несколько подключений, желательно, к разным партнерам. Однако, чтобы эти подключения на самом деле работали, надо было каким-то образом донести информацию об адресном пространстве сети в глобальное облако маршрутизации Internet через каждое из подключений.
Как нетрудно убедиться, в модели маршрутизации IP прикладной трафик распространяется навстречу маршрутной информации.
Если сеть принадлежала достаточно крупному провайдеру, у которого были статус LIR, автономная система и прочие атрибуты серьезного игрока в этом бизнесе, то сеть объявляла о себе с помощью BGP, и все образовывалось как бы само собой. Это вполне устоявшаяся схема, и LIR IPv6 сообщают о себе точно так же при помощи MBGP.
Но что если сеть принадлежала абстрактной организации, применявшей свои подключения к Internet для сугубо внутренних целей? Пока
адреса IPv4 еще были относительно доступны, такая организация могла получить прямиком из рук RIR автономную систему и отдельный блок
адресов, не привязанный к определенному провайдеру
Самая "интересная" участь ожидала те сети, которые не были достаточно велики или весомы, чтобы оказаться представленными в глобальном облаке маршрутизации Internet. Им не оставалось ничего иного, кроме как подключиться к нескольким провайдерам, получить от каждого из них по блоку адресов и после этого заняться сооружением пирамиды из различных ухищрений, чтобы обеспечить работу сети по нескольким подключениям. Вот лишь краткий и неполный список препятствий, которые им приходилось преодолевать:
Но даже эти ухищрения обладали весьма ограниченными возможностями. В частности, никакими трюками нельзя было сохранить обычное соединение 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
Если один и тот же удаленный адрес одновременно установит соединения с разными адресами локального хоста через разных провайдеров, эта схема даст сбой. Чтобы восстановить ее работоспособность, запись 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
Самостоятельно обсудите, вызовет ли 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 битам.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.