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

IPv6 в стеке протоколов

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

Инкапсуляция на канальном уровне

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

Возьмем, к примеру, протокол SLIP [RFC 1055]: он уже был готов передавать пакеты IPv6, когда IPv6 еще не было и в помине. Более того, SLIP способен передавать вперемежку пакеты любых версий IP, так как приемник всегда может узнать версию IP данного пакета из первого полубайта в его заголовке.

Как мы помним, в SLIP тип инкапсулируемого протокола указывается неявно, по предварительному соглашению сторон. Поэтому канал SLIP без перенастройки сторон может передавать только один протокол сетевого уровня. Однако это не значит, что в кадр SLIP можно вложить только пакет IP: например, без малого 20 лет назад будущий автор этого курса на спор написал драйвер MS DOS, инкапсулирующий IPX в SLIP, и потом сыграл с друзьями-спорщиками немало партий в Doom по этому каналу, вполне отвечавшему модели OSI.

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

Ethernet

Из тех же соображений мы могли бы сказать, что инкапсуляция IP в кадры Ethernet [RFC 894] применима к любой версии IP. Однако в этом случае практика вносит свои коррективы. Оказывается, существуют сетевые устройства, так называемые "коммутаторы четвертого уровня", которые анализируют разные поля в заголовках Ethernet, IPv4, TCP, UDP, но забывают проверить поле версии IP [См. сообщение Steve Deering "Why a new etype of IPv6" в списке рассылки internet-history http://www.postel.org/pipermail/internet-history/2010-September/001437.html] ! Radia Perlman. Interconnections: Bridges, Routers, Switches, and Internetworking Protocols (2nd Edition). §9.8.1. То есть такое устройство, видя значение 0x800 в поле Ether Type, немедленно решает, что внутри кадра содержится пакет IPv4. Масштаб этой проблемы оказывается достаточно большим, так что придется оставить Ether Type 0x800 для IPv4 и назначить IPv6 отдельное значение Ether Type: 0x86DD [§3 RFC 2464].

Радя Перлман в своей традиционной лекции Radia Perlman. Mythology and Folklore of Network Protocol Design. http://www.isoc-au.org.au/Events/PerlmanNov05.pdf сетует, что текст стандартов IPv4 [RFC 791] и IPv6 [RFC 2460] нигде явным образом не предписывает проверять поле версии IP на входе. Мы готовы согласиться с уважаемой Радей, что это явная недоработка. Видимо, виновата здесь опять история. Ведь изначально стандарты Internet были ориентированы на высококвалифицированных и здравомыслящих специалистов, для которых строгий и безопасный алгоритм приема однозначно следовал из формата передаваемых данных . Именно поэтому "протокол" и "формат сообщения" в TCP/IP долгое время были синонимами. Увы, сегодня уровень среднего читателя RFC заметно понизился по сравнению с 1970-м годом, и это можно компенсировать только четкостью и строгостью самих стандартов, не оставляющих места разночтениям . Справедливости ради заметим, что достаточно сложный протокол уже нельзя свести к формату его сообщений — необходимы также явные правила поведения сторон. Самостоятельно обсудите, как следует на входе обрабатывать пакет IPv6, принятый с Ether Type 0x800, и пакет IPv4, пришедший с Ether Type 0x86DD. Оптимальное решение вам подскажет принцип Постела.

Еще один аспект инкапсуляции IP в Ethernet — это разрешение групповых адресов. Наивным решением было бы привлечь для этой цели ARP или аналогичный механизм, когда источник группового пакета сначала спрашивал бы, кто состоит в данной группе, и узнавал бы таким способом адреса MAC 48 ее членов. Однако на поверку это решение никуда не годится, потому что групповое вещание — основа для автоматического обнаружения сетевых ресурсов. Необходимость ARP в данном случае будет означать обнаружение с целью обнаружения — верный путь к "проблеме курицы и яйца", когда узлу надо послать пакет, чтобы запросить недостающие сведения, а эти сведения нужны, чтобы послать пакет. Поэтому давайте выдвинем такое требование: пусть групповое вещание IP привлекает только локальные механизмы разрешения адреса. Источник группового пакета должен быть способен преобразовать групповой адрес назначения IP в отвечающий ему канальный адрес MAC 48 своими силами, не опрашивая другие узлы. Тогда исходящий групповой пакет можно будет передать на канальный уровень сразу же, опираясь только на информацию, которая уже содержится в нем.

Как мы помним, групповое вещание IPv4 вполне отвечало этому требованию, достигая разрешения групповых адресов простой подстановкой битов. А именно 23 младших бита группового адреса IPv4 помещались в соответствующие младшие биты группового адреса MAC 01-00-5E-00-00-00 [§6.4 RFC 1112]. Такое преобразование не было взаимно однозначным, поскольку в групповом адресе IPv4 переменные 28 бит . 4 старших бита фиксированы и составляют префикс 1110 класса D. В результате узел мог получить чужой групповой пакет, и поэтому он обязательно проверял адрес назначения: действительно ли он состоит в данной группе IP на данном интерфейсе?

Давайте оставим подобный механизм разрешения групповых адресов и в IPv6. Но, чтобы уменьшить вероятность конфликтов, мы расширим поле соответствия. Пусть из группового адреса IPv6 в групповой адрес MAC попадают 4 младших байта, то есть 32 бита. Тогда нам потребуется другой префикс MAC, длиной 16 бит. На эту роль назначен префикс 0x3333, отвечающий адресам от 33-33-00-00-00-00 до 33-33-FF-FF-FF-FF.

Убедитесь, что перед вами групповые адреса MAC.

Еще можно заметить, что это локальные, а не глобальные адреса MAC. В отличие от 01-00-5E, новый диапазон OUI 33-33-XX не пришлось покупать у IEEE.

"3333 Coyote Hill Road" — это был адрес Исследовательского центра Xerox в Пало-Альто, откуда появился Ethernet [§2.3.1 RFC 5342].

Обсудите недостатки использования в данном случае локальных адресов MAC. (Возможен конфликт с протоколами местного значения; однако фильтрация по адресам сетевого уровня предотвратит нежелательные последствия.)

Вот пример преобразования: групповым адресам IPv6 FF02::C001:DEAD:BEEF и FF0E::BA1D:DEAD:BEEF отвечает один и тот же групповой адрес MAC 48 33-33-DE-AD-BE-EF.

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

  • общепринятые групповые адреса: 0x0000 0000–0x3FFF FFFF;
  • постоянно назначенные группы: 0x4000 0000–0x7FFF FFFF;
  • временно занятые адреса: 0x8000 0000–0xFFFF FFFF.
  • Разница между общепринятыми групповыми адресами и постоянно назначенными группами такова: общепринятые групповые адреса назначены в системных целях и нужны для работы протоколов стека IPv6, тогда как постоянно назначенные группы просто созданы для поддержки глобальных проектов, важных для всего Internet. Например, 0x0000 0001 ("все узлы") — это общепринятый групповой адрес, тогда как 0x4040 4040 (NTP) — всего лишь постоянно назначенная группа.

    Те же самые требования закреплены в реестре IANA для групп IPv6 http://www.iana.org/assignments/ipv6-multicast-addresses/ .

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

    PPP

    Пример канального протокола, поддержка IPv6 в котором неизбежно требует дополнительной работы, — это PPP. Чтобы обеспечить высокий уровень услуг, PPP готовит канал к работе каждого сетевого протокола, например, согласуя адреса на его концах. Это задание PPP делегирует дочернему протоколу семейства NCP. Уже существующий NCP для IP, IPCP [RFC 1332], может работать только с адресами длиной 32 бита, так что он не годится для IPv6. Посему для IPv6 необходим свой собственный NCP — IPV6CP [RFC 5072].

    Прежде всего, назначим IPv6 и его NCP уникальные номера из реестра параметров PPP http://www.iana.org/assignments/ppp-numbers , чтобы кадры PPP с ними можно было однозначно демультиплексировать. У IPv6 это 0x0057, а номер его NCP по традиции на 0x8000 больше, то есть 0x8057. Теперь мы можем инкапсулировать оба протокола в PPP, и пора подумать над функциями IPv6CP.

    Какое задание, специфичное именно для IPv6, мы можем поставить перед IPV6CP? По нашей задумке (см. §2.7), сетевому интерфейсу IPv6 в высшей степени полезно иметь какой-то эквивалент адреса EUI 64, чтобы на его основе можно было создать условно уникальный идентификатор интерфейса. Хорошо, когда у интерфейса есть настоящий, глобально уникальный EUI 64. Канальный адрес MAC 48 — тоже неплохой вариант, потому что его можно превратить в EUI 64 по общепринятому рецепту, который мы обсудили в §2.7. Но как быть с PPP, где явных канальных адресов вообще нет? Ведь каналу PPP вполне достаточно неявной адресации: всякий кадр PPP, переданный в канал, будет принят узлом на другом конце канала, и только им. Вот пусть это и будет работой IPV6CP: согласовать два уникальных идентификатора на концах канала PPP. Так как в конечном итоге нам нужны идентификаторы интерфейса в формате "модифицированный EUI 64", то пусть IPV6CP согласует непосредственно их, а не промежуточные EUI 64 или MAC 48.

    Каким требованиям должны удовлетворять эти идентификаторы [§4.1 RFC 5072]?

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

    Зона уникальности полученных адресов IPv6 будет зависеть от области действия префикса подсети: из глобального префикса мы получим глобально уникальные адреса, из внутрисайтового префикса — адреса, уникальные в пределах данного сайта, а из внутриканального префикса — внутриканальные адреса для данного канала PPP.

  • Желательно, чтобы идентификаторы были постоянными и не менялись, к примеру, при перезагрузке узлов. Каждый узел вправе использовать все доступные ему источники уникальности, чтобы построить такой идентификатор. В первую очередь, на эту роль претендуют аппаратные адреса EUI 64 и MAC 48 других сетевых интерфейсов узла. Если таковых нет, то следующий кандидат — это серийные номера основных компонентов узла. Наконец, как крайнее средство, идентификатор все же может быть случайным.
  • Составляя идентификатор, узел обязан соблюдать формат "модифицированный EUI 64" (см. §2.7). В частности, бит U/L нужно установить в единицу тогда, и только тогда, когда идентификатор создан на основе настоящего глобального адреса EUI 64 или MAC 48, назначенного именно этому узлу. Само преобразование EUI 64 и MAC 48 в "модифицированный EUI 64" мы уже подробно обсудили там же, в §2.7.
  • Само согласование идентификаторов представляет собой простой и доступный пример работы механизма опций PPP. В данном случае достаточно сообщений Conf Req, Conf Rej, Conf Nak и Conf Ack (см. Табл. 4.1), а код соответствующей опции IPV6CP — 1 [§4 RFC 5072].

    Сообщения IPV6CP при согласовании идентификатора интерфейса
    Сообщение Смысл Допустимые варианты ответа
    Запрос
    Conf-Req Источник хотел бы присвоить себе (не удаленной стороне!) такой-то идентификатор интерфейса (прилагается к сообщению). Пока что это пробный идентификатор (tentative identifier), потому что источник только испытывает его. Если идентификатор в сообщении нулевой, то источник не может сам справиться с выбором и просит удаленную сторону о помощи. Conf Rej, Conf Nak, Conf Ack
    Варианты ответа
    Conf-Rej Источник вообще не поддерживает данную опцию. Удаленная сторона должна продолжить согласование сообщением Conf-Req без этой опции или прервать сеанс. Conf-Req
    Conf-Nak Источник поддерживает опцию, но не удовлетворен ее значением. Видимо, значение было нулевым, или источник сам хотел назначить себе такой же идентификатор. Поэтому источник предлагает удаленной стороне свой вариант ее, удаленной стороны, идентификатора (прилагается к сообщению). Удаленная сторона должна рассмотреть этот вариант и послать новый Conf-Req. Conf-Req
    Conf-Ack Источник отвечает, что согласен с выбором удаленной стороны, поскольку он не нулевой и отличается от пробного идентификатора самого источника. На этом согласование завершено. Нет

    Как мы помним, согласование опции PPP проходит независимо в каждом из двух возможных направлений. Поэтому стороны А и Б ведут два, на первый взгляд, независимых диалога IPV6CP. Итогом одного из них будет идентификатор стороны А, а другого — идентификатор стороны Б.

    Механизм согласования IPV6CP допускает следующий сценарий: узел Б объявляет нулевой идентификатор, и тогда узел А подбирает идентификатор не только для себя, но и для удаленного узла Б. В этом случае бит U/L в идентификаторе для Б надо сбросить, даже если он получен из глобального EUI 64. Ведь этот EUI 64 принадлежит узлу А, а не узлу Б. (Как исключение, этот бит можно установить в единицу, если узел А имел в запасе еще один глобальный EUI 64, а теперь передает его узлу Б в безраздельное пользование на время данного сеанса PPP [§4.1 RFC 5072].)

    Чтобы процесс согласования идентификаторов не зациклился, сформулируем несколько дополнительных правил поведения стороны IPV6CP:

  • не повторяться, то есть не предлагать один и тот же идентификатор дважды в Conf Req или Conf Nak;

    Тем не менее, идентификатор, который уже фигурировал в Conf-Nak, вполне может появиться в ответном Conf-Req. Однако в этом случае идентификатор придет от удаленной стороны, а мы говорим о поведении одной стороны, которую мы по традиции считаем нашей локальной.

  • не капризничать, то есть соглашаться с первым же подходящим идентификатором:

    если идентификатор в принятом нами Conf Req не совпадает с нашим собственным пробным значением, которое мы объявили в исходящем Conf Req, то мы немедленно соглашаемся с ним, отвечая Conf Ack. Так мы соглашаемся с выбором удаленной стороны для нее самой;

    если идентификатор во входящем Conf-Nak не совпадает с тем, что мы сами предложили удаленной стороне в последнем переданном нами Conf Nak, то мы обязаны принять его в качестве нашего пробного значения и выслать об этом Conf Req. Здесь мы соглашаемся с предложением удаленной стороны для нас.

    Обратите внимание, как между встречными диалогами IPV6CP возникает скрытая зависимость. Чтобы правильно ответить на входящее сообщение Conf Req, нам надо помнить наш собственный пробный идентификатор, который мы наверняка сами объявили в исходящем Conf Req. А правильная реакция на входящий Conf Nak возможна только при условии, что мы запомнили значение опции из посланного нами самими Conf Nak.

  • Эти правила склоняют согласование идентификаторов к одному из двух вероятных путей:

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

    Поэтому, если обе стороны передают нулевые значения в своих сообщениях Conf Req, согласование надо немедленно прекратить ради устойчивости протокола [§4.1 RFC 5072].

    Гораздо большее влияние на ход процедуры IPV6CP окажет то, насколько одновременны встреченные диалоги. Поэтому, чтобы лучше почувствовать работу механизма IPV6CP, давайте рассмотрим процессы нулевого и первого порядков (немедленное согласие; конфликт, а затем согласие с предложением удаленной стороны) в двух крайних случаях: когда встречные диалоги совершенно синхронны или строго последовательны.

    Если стороны с самого начала выбрали себе разные пробные идентификаторы, то встречные диалоги независимы, а поэтому не имеет значения, одновременны ли они, или же один следует за другим. Итог будет один: стороны утвердят самостоятельно выбранные идентификаторы.

    (рис 4.1) IPV6CP: Параллельное согласование без конфликта

    Параллельный сценарий (рис. 4.1) в этом случае преобразуется в свой последовательный эквивалент (рис. 4.2) простым сдвигом одного из диалогов вдоль оси времени.

    (рис 4.2) IPV6CP: Последовательное согласование без конфликта

    Если же стороны случайно выбрали один и тот же идентификатор, исход согласования зависит от того, как встречные диалоги расположены друг относительно друга на оси времени. Так, если они одновременны (рис. 4.3), то каждая из сторон согласится с подсказкой удаленной стороны и выберет именно ее собственным идентификатором.

    (рис 4.3) IPV6CP: Параллельное согласование и преодоление конфликта

    Ну, а если диалоги будут вестись последовательно (рис. 4.4), то сторона А, открывшая согласование первой, будет вынуждена принять подсказку удаленной стороны Б, тогда как сторона Б сможет настоять на своем первоначальном выборе идентификатора.

    (рис 4.4) IPV6CP: Последовательное согласование и преодоление конфликта

    Пусть читатель самостоятельно составит такие же две схемы для случая, когда одна из сторон просит своего визави о помощи в выборе идентификатора, объявляя в Conf Req нулевое значение.

    На практике сторона IPV6CP может прибегать к безобидным трюкам, чтобы обойтись без конфликта уже в нулевом порядке согласования. Так, она может отложить выбор своего пробного идентификатора до тех пор, пока не придет запрос Conf Req удаленной стороны. После этого ей достаточно инвертировать хотя бы один бит в чужом идентификаторе, чтобы получить заведомо уникальный идентификатор для себя самой. Читатель может составить схему такого диалога самостоятельно. Конечно, в этом случае встает вопрос, кто пошлет Conf Req первым и как избежать возможного тупика в развитии сеанса, когда ни одна из сторон не проявляет инициативу. Хотя, судя по выдачам tcpdump, этот трюк использует Cisco IOS версии 12.4, независимый обмен запросами Conf Req нам кажется более надежным.

    Взаимодействие с вышестоящими протоколами

    В стеке TCP/IP на уровень IP непосредственно опираются протоколы нескольких видов: управляющие, то есть ICMP; транспортные, такие как TCP, UDP и SCTP; туннельные, например, IP IP, GRE, EtherIP. О том, как нам быть с ICMP, мы поговорим в §4.3, а сейчас давайте рассмотрим вопрос инкапсуляции IPv6 в целом, сделав ударение на протоколах транспортного уровня.

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

    Прежде всего, мы должны сказать, как блок данных протокола (БДП), стоящего над IP, следует инкапсулировать в пакет IPv6. На самом деле, правила этой инкапсуляции мы уже задали, когда говорили в §3.1 о структуре заголовков пакета IPv6. Тогда мы сказали, что пакет IPv6 содержит в своем начале цепочку из одного или более простых заголовков. Каждый простой заголовок содержит поле "следующий заголовок", возможные значения которого хранятся в реестре протоколов IP http://www.iana.org/assignments/protocol-numbers/ . Значит, последний заголовок в цепочке может ссылаться на тип полезной нагрузки пакета точно так же, как это делал заголовок IPv4. Например, если пакет IPv6 содержит в себе сегмент TCP, то последнее поле "следующий заголовок" содержит значение 6; если дейтаграмму UDP, то 17; а если пакет IPv4, то 4. Эти примеры иллюстрирует рис 4.5(рис 4.5) Примеры инкапсуляции IPv6 (с.з. = следующий заголовок)

    Следующим шагом будет найти и исправить все явные и неявные зависимости вышестоящих протоколов от IPv4. К счастью, у стандартных и активно применяемых протоколов их оказывается немного.

    Кстати, была предпринята попытка составить полный список зависимостей от IPv4 у протоколов, описанных в RFC. Отчет занял восемь (!) документов RFC, 3789–3796.

    Главная зависимость TCP и UDP от IPv4 заключается в формате псевдозаголовка, который служит для вычисления контрольной суммы. Этот псевдозаголовок включает в себя адреса IPv4 источника и назначения. Чтобы TCP и UDP смогли работать поверх IPv6, потребуется новый формат псевдозаголовка, который учтет специфику нового протокола [§8.1 RFC 2460], как это показано на рис. 4.6.

    Самостоятельно изучите вопрос, нужно ли задавать новый псевдозаголовок для SCTP. (Подсказка: начните с вводной статьи R. Stewart, M. Tüxen, P. Lei. SCTP: What is it, and how to use it? BSDCan 2008. http://www.bsdcan.org/2008/schedule/attachments/44_bsdcan_sctp.pdf .)

    (рис 4.6) Псевдозаголовок IPv6 для вычисления контрольных

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

    "В лоб" это вычисление можно провести, вычтя длины всех заголовков расширения IPv6 в данном пакете — не считая основного заголовка! — из значения поля "длина полезной нагрузки" в его основном заголовке IPv6. Альтернативный способ, близкий знатокам языка Си, — это найти разность указателей на конец пакета и заголовок TCP, приведенных к типу char *. Главное — не перепутать при этом конец пакета с последним байтом пакета, потому что их смещения отличаются на один байт.

    Что касается поля "следующий заголовок", то оно теперь хранит тип вышестоящего протокола, а не просто копирует такое же поле основного заголовка IP; ведь в пакете могут быть заголовки расширения, а тогда тип вышестоящего протокола будет скрыт в самом последнем из них.

    Кроме того, протокол UDP неявно полагается на контрольную сумму IPv4, когда объявляет вычисление контрольной суммы UDP необязательным: он предполагает, что заголовок IP защищает себя от повреждения. Но заголовок IPv6 сам по себе не защищен, и поэтому вычисление контрольной суммы UDP при работе поверх IPv6 становится обязательным [§8.1 RFC 2460]. Если дейтаграмма UDP с нулевым значением в поле "контрольная сумма" получена в пакете IPv6, то ее следует уничтожить.

    Освежим нашу память и вспомним несколько фактов. Контрольная сумма TCP/IP вычисляется в 16 битном обратном коде, где есть два арифметически эквивалентных нулевых значения, 0x0000 и 0xFFFF. Значение поля "контрольная сумма" в TCP и UDP — это результат побитовой инверсии вычисленной суммы. (В обратном коде инверсия битов приводит к смене знака числа, так что неудивительно, что сумма всех слов сообщения, включая его контрольную сумму, должна быть нулевой.) Поэтому мы можем отличить нулевую контрольную сумму от неизвестной, задав дополнительное правило. Для этого мы предписываем хранить истинно нулевую сумму только как 0x0000, что после инверсии даст нам 0xFFFF. А значение суммы 0xFFFF, что после инверсии дает 0x0000, мы назначаем особому случаю, когда источник дейтаграммы сумму вообще не посчитал. Просто и изящно, не правда ли?

    Трансляцию NAT вполне можно обобщить на обе версии IP и применить, чтобы обеспечить взаимодействие хостов IPv4 с хостами IPv6. Но как быть транслятору NAT46 или NAT64, если со стороны IPv4 он получает дейтаграмму UDP с неопределенной контрольной суммой? В этом случае при ее трансляции в IPv6 придется вычислить значение контрольной суммы и поместить в заголовок UDP. Тем самым транслятор заверит целостность дейтаграммы без достаточных на то оснований, ведь дейтаграмма могла поступить к транслятору на вход уже поврежденной! Вот нам еще один пример, как NAT нарушает сквозной принцип, лежащий в основе архитектуры Internet.

    А как появление IPv6 скажется на работе туннельных протоколов? Если такой протокол сконструирован надлежащим образом, то изменений в нем не потребуется. Так, например, протоколу GRE достаточно учесть, что IPv6 получил свой собственный Ether Type 0x86DD. Еще при туннелировании IPv6 будет полезно включить вычисление контрольной суммы GRE, чтобы обеспечить дополнительную защиту пакетов от повреждения.

    Туннельный протокол IP IP тоже готов к работе с IPv6 как снаружи [RFC 2473], так и внутри [§3 RFC 3056, §3.1 RFC 4213] туннеля. Достаточно учесть при инкапсуляции, что исходному пакету IPv4 отвечает номер протокола IP 4, а исходному пакету IPv6 — 41 http://www.iana.org/assignments/protocol-numbers/ [ ]. Вполне можно строить и смешанные туннели "IPv4 внутри IPv6" и "IPv6 внутри IPv4". Сводная таблица таких способов инкапсуляции — Табл. 4.2.

    Туннельная инкапсуляция IP-IP после появления IPv6
    Заголовки IPv6-в-IPv6 IPv6-в-IPv4 IPv4-в-IPv6
    Внешний IPv6: "след. заголовок" = 41 IPv4: "протокол" = 41 IPv6: "след. заголовок" = 4
    Внутренний IPv6 IPv6 IPv4

    Наконец, у всех типов туннелей есть общая важная деталь — управление фрагментацией. Когда внешний протокол туннеля — IPv4, то узел на входе в туннель (инкапсулятор) может разрешить или запретить транзитную фрагментацию туннельных пакетов с помощью флага DF. Напротив, в среде IPv6 транзитная фрагментация всегда запрещена; это касается и туннельных пакетов, внешний протокол которых — IPv6, а поэтому их длиной обязан управлять сам инкапсулятор. Здесь его поджидает, как минимум, одна сложность: внутренний MTU туннеля обязан быть не меньше 1280 байт, тогда как и внешний MTU трассы (PMTU) туннеля может оказаться равным 1280 байт. Инкапсуляция исходного пакета длиной 1280 байт заведомо даст туннельный пакет длиной более 1280 байт, который не пройдет данную трассу без фрагментации. Это означает, что в среде IPv6 инкапсулятор обязан быть готов к фрагментации туннельных пакетов, потому что в общем случае без нее не обойтись. Самый простой, хотя и не слишком эффективный подход к выбору длины туннельного пакета — это принять нижнюю оценку PMTU туннеля равной 1280 байт и фрагментировать туннельные пакеты исходя из этого. Тогда внутренний MTU туннеля будет ограничен только диапазоном длины пакета IPv6 до фрагментации за вычетом длины туннельных заголовков: 65575 - X. Подход посложнее - это выполнение процедуры PMTUD инкапсулятором.

    В RFC 4459 можно найти более обширный анализ теории и практики управления фрагментацией в туннеле.

    Управление

    Хотя ICMP [RFC 792] вполне можно было бы "притянуть за уши" к управлению IPv6, новая версия IP — это хороший повод пересмотреть и сопряженный с ним протокол управления. Чтобы не заботиться о совместимости с ICMP, давайте назначим новому протоколу другой номер, 58, из реестра протоколов IP. Новорожденный протокол мы назовем "ICMP версии 6", а кратко — ICMPv6 [RFC 4443], хотя заголовок ICMP и не содержит поля версии. Чтобы избежать разночтений, "старый" ICMP для IPv4 мы теперь обозначим как ICMPv4, по крайней мере, при его сравнении с ICMPv6.

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

    Какие требования мы предъявим к протоколу управления IPv6? В первую очередь, он не должен отставать от IPv6 в плане гибкости и расширяемости. Это требование мы выполним, "собрав" сообщение ICMPv6 из двух частей. У первой части формат будет постоянным, а у второй — переменным, как изображено на рис 4.7(рис 4.7) Общая структура сообщения ICMPv6

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

    Поскольку ICMPv6 — отдельный протокол, номера его типов никак не связаны с номерами типов ICMPv4. Номера типов ICMPv6 мы будем распределять с нуля, так что у нас есть возможность придать им определенную структуру. В ICMPv4 типы извещений об ошибках шли вперемежку с чисто справочными типами. Теперь ради удобства давайте проведем между ними границу. Пусть типы извещений об ошибках получают номера из диапазона 0–127, а справочные типы — из диапазона 128–255. Тогда, чтобы узнать характер сообщения ICMPv6, достаточно взглянуть на старший бит в его поле "тип": 0 означает произошедшую ошибку, а 1 — справочные сведения [§2.1 RFC 4443].

    Толкование поля "код" зависит от типа сообщения. Например, с его помощью можно поделить сообщения одного типа на более узкие группы, чем мы воспользуемся в текущем разделе.

    Формат переменной части также целиком зависит от типа сообщения. Эту часть мы назовем телом сообщения (message body). Она простирается до конца пакета, а длину ее можно вычислить, исходя из длины полезной нагрузки в заголовке IPv6.

    Согласно принятой в IPv6 модели защиты данных от повреждения, контрольная сумма ICMPv6 должна защищать не только само сообщение, но и заголовок IPv6. Достичь этого можно, используя при вычислении контрольной суммы псевдозаголовок стандартного формата (см. §4.2). В остальном правила обычные: на время расчета контрольной суммы псевдозаголовок условно помещается перед заголовком ICMPv6, а само поле "контрольная сумма" явно или "мысленно" обнуляется [§2.3 RFC 4443].

    Какое значение надо поместить в поле "следующий заголовок" такого псевдозаголовка? (Ответ: 58 — ICMPv6.)

    Заметим, что ICMPv4 не использовал псевдозаголовка и вычислял только контрольную сумму собственного БДП, вложенного в пакет IPv4. Так что применение псевдозаголовка в ICMP — это новшество IPv6, вызванное отсутствием контрольной суммы в основном заголовке IPv6.

    Сообщение ICMPv6 составляет всю полезную нагрузку пакета IPv6, так что поле длины заголовку ICMPv6 не требуется: длину сообщения ICMPv6 можно вычислить, зная длину всего пакета и структуру его заголовков. Это вычисление понадобится, чтобы заполнить псевдозаголовок, так как он содержит длину блока данных вышестоящего протокола (см. рис. 4.6).

    Напомним о двусмысленности термина "полезная нагрузка" в IPv6. В общеупотребительном смысле, это последняя секция пакета, после основного заголовка и заголовков расширения IPv6, занятая блоком данных вышестоящего протокола: дейтаграммой UDP, сегментом TCP, сообщением ICMPv6 . В узком смысле поля "длина полезной нагрузки" из основного заголовка IPv6 это весь пакет за вычетом основного заголовка. При заполнении псевдозаголовка эти два толкования встречаются, так как в заголовке IPv6 указана "длина полезной нагрузки" (смысл №2), а надо найти длину блока данных вышестоящего протокола (смысл №1). Это касается, в первую очередь, узла назначения, так как он должен разобрать и проверить входящий пакет.

    Перейдем теперь к типам сообщений ICMPv6. Для начала, в Табл. 4.3, мы определим только самые необходимые из них [§2.1 RFC 4443], а для будущих назначений создадим реестр http://www.iana.org/assignments/icmpv6-parameters .

    Основные типы сообщений ICMPv6
    Тип Название (рус.) Название (англ.) Ссылка
    Извещения об ошибках
    1 Адресат недоступен Destination Unreachable [§3.1 RFC 4443]
    2 Пакет слишком велик Packet Too Big [§3.2 RFC 4443]
    3 Время истекло Time Exceeded [§3.3 RFC 4443]
    4 Проблема в параметре Parameter Problem [§3.4 RFC 4443]
    100 Для частных экспериментов [§2.1 RFC 4443]
    101 Для частных экспериментов [§2.1 RFC 4443]
    127 Резерв для расширения [§2.1 RFC 4443]
    Справочные сообщения
    128 Запрос эха Echo Request [§4.1 RFC 4443]
    129 Ответ эхом Echo Reply [§4.2 RFC 4443]
    200 Для частных экспериментов [§2.1 RFC 4443]
    201 Для частных экспериментов [§2.1 RFC 4443]
    255 Резерв для расширения [§2.1 RFC 4443]

    Тип 0 не назначен, скорее всего, из-за уже знакомого нам предубеждения перед нулевыми значениями, и это вполне оправдано: ноль часто означает "нет данных".

    Что еще изменилось по сравнению с основными типами ICMPv4? Прежде всего, извещения "пакет слишком велик" и "время истекло" получили отдельные типы. Теперь "адресат недоступен" говорит только о невозможности доставить пакет из-за проблем на уровне адресации и маршрутизации, или же ввиду ограничений политики [§3.1 RFC 4443], например:

  • код 0: нет маршрута к адресу назначения, то есть неизвестно, куда продвигать пакет;
  • код 1: обмен пакетами с адресатом запрещен в административном порядке, например, политикой сетевой безопасности;
  • код 2: адресат находится за пределами зоны источника, то есть предотвращено нарушение границ зоны, такое как попытка направить пакет узлу совершенно другой сети, используя внутриканальный адрес источника;
  • прочие коды "адресат недоступен" приведены в [§3.1 RFC 4443] и реестре http://www.iana.org/assignments/icmpv6-parameters [ ].
  • Какую практическую пользу принесет то, что у сообщения "пакет слишком велик" теперь есть свой тип, а не только код в рамках типа "адресат недоступен", как это было в ICMPv4? Прежде всего, инженерам по сетевой безопасности будет легче составлять политики, которые запрещают произвольные извещения "адресат недоступен", но при этом не блокируют работу PMTUD. В IPv4 это было серьезной проблемой. Во-первых, сами инженеры часто забывали разрешить именно это сочетание типа и кода ICMPv4. А во-вторых, было много примеров сетевого программного обеспечения, которое позволяло фильтровать ICMPv4 только по значению поля "тип", но не "код", так что невозможно было избирательно разрешить PMTUD. В ICMPv6 этот опыт нельзя было не учесть, так как важность правильной работы PMTUD возросла многократно из-за новых правил фрагментации, когда решение о размере пакета принимает только его источник (см. §3.3.4).

    В идеале, фильтрация ICMP не должна представлять сложности для сетевого экрана, полноценно отслеживающего прикладные сеансы. Ему достаточно пропускать сообщения ICMP, относящиеся к существующим сеансам, и блокировать "залетные" сообщения. К сожалению, не все межсетевые экраны рассматривают сигнализацию ICMP как неотъемлемую часть сеанса, но это всего лишь практическое ограничение, которое можно было бы устранить доработкой программы. Более фундаментальная проблема состоит в том, что сообщение ICMPv4 может содержать недостаточно сведений, чтобы точно отнести его к определенному прикладному сеансу. Мы устраним эту проблему в ICMPv6 еще до конца данного раздела.

    Далее, как мы уже отметили, говоря о фрагментации и сборке в §3.3.4, извещение "пакет слишком велик" обязано содержать в себе значение MTU выходного канала, куда "не уместился" пакет. Благодаря этой информации, процедура PMTUD сойдется гораздо быстрее.

    Извещение "проблема в параметре" подходит для того, чтобы сообщать источнику, помимо прочих проблем в составленном им пакете, о неопознанных опциях и неизвестных значениях "следующий заголовок". Чтобы различать эти проблемы, можно назначить им разные коды [§3.4 RFC 4443]:

  • код 0: недопустимое значение какого-либо поля в заголовке либо неверно сформированный пакет, например, длина фрагмента с M = 1 не кратна восьми;
  • код 1: неопознанное значение "следующий заголовок" — возможно, адресат не поддерживает это расширение или протокол ;
  • код 2: неопознанная опция IPv6 — высылается, только если старшие биты кода опции требуют такого извещения (см. §3.3.2).
  • Но почему тип 3 называется "время истекло"? Ведь в §3.2 мы приняли решение, что теперь вместо поля "время жизни пакета" будет поле "предельное число шагов". Конечно, это так, но тип 3 охватывает два разных события, и второе из них по-прежнему связано со временем [§3.3 RFC 4443]:

  • код 0: превышено предельное число шагов, то есть пакет предположительно зациклился в сети, отчего значение его поля "предельное число шагов" достигло нуля;
  • код 1: истек тайм-аут сборки, то есть адресат не дождался прихода всех фрагментов пакета.
  • К сожалению, текст [§3.3 RFC 4443] можно трактовать так, будто маршрутизатор должен проверить поле "предельное число шагов" во входящем пакете, как только он принят из сети. Как мы знаем из §3.2 настоящего курса, на самом деле этому обязательно предшествует вывод, что пакет транзитный и подлежит продвижению, так как иначе поле "предельное число шагов" вообще не проверяется. Видимо, авторы [RFC 4443] руководствовались моделью идеального маршрутизатора, у которого все пакеты — транзитные, но забыли сделать на этом акцент. Увы, в наши дни такие недомолвки чреваты неправильными реализациями.

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

    Модель управления ошибками IPv6 остается такая же, как и в IPv4: извещение об ошибке высылается только при невозможности обработать принятый пакет IP. То есть за каждым сообщением об ошибке стоит определенный пакет-виновник. А значит, к извещению об ошибке обязательно надо приложить хотя бы начало пакета-виновника, чтобы его источник смог опознать пакет и принять необходимые меры. Это правило касается всех извещений об ошибках ICMPv6. Оно не ново — подобное правило было и в ICMPv4.

    По одной из точек зрения, даже минимальный MTU IPv4 был выбран в связи с сигнализацией ошибок таким образом. Ведь в 68 байт как раз умещается внешний заголовок IPv4 (20 байт), заголовок ICMPv4 (8 байт), заголовок IPv4 виновника (20 байт), и остается еще 20 байт для заголовка TCP.

    Однако теперь у нас есть шанс устранить одну застарелую проблему. Она была в том, что сообщение ICMPv4 содержало слишком мало байтов из пакета-виновника: стандартом требовались лишь его заголовок IPv4 и 8 байт полезной нагрузки [§3.2.2 RFC 1122]. Когда в сети возникали сложные схемы многоуровневой инкапсуляции, например, с привлечением туннелей, информативные части пакета-виновника просто не помещались в сообщение.

    Теперь же мы вправе — и обязаны — потребовать, чтобы всякое сообщение об ошибке ICMPv6 содержало как можно большую часть пакета-виновника [§2.4(c) RFC 4443]. Очевидно, что это должно быть начало пакета [§2.1 RFC 4443], а не середина или конец, потому что именно начало пакета дает ключ к его содержимому.

    Подумайте, что можно включить в копию пакета-виновника, когда происходит тайм-аут сборки, а первый фрагмент так и не получен. (Вариант: образец нефрагментируемой части и заголовок фрагмента.)

    Насколько велика может быть эта часть? Мы заметили в §3.3.4, что ICMPv6 поможет нам управлять фрагментацией. Но если извещения ICMPv6 сами будут подвержены фрагментации, то наша схема зациклится. Поэтому фрагментировать извещения ICMPv6, вообще говоря, нельзя. Следовательно, ограничением сверху здесь будет минимальный MTU IPv6, 1280 байт. Конечно, в этот MTU должен умещаться весь пакет ICMPv6, включая заголовок IPv6 и возможные заголовки расширения.

    Нетрудно убедиться, что вместе эти два условия (как можно больше, но не больше чем) допускают только два случая:

  • извещение содержит пакет-виновник целиком (в том виде как он был доступен извещающему узлу);
  • извещение содержит начало пакета-виновника, а весь пакет ICMPv6 занимает 1280 байт.
  • В результате извещения ICMPv6 будут иметь единый формат, представленный на рис 4.8(рис 4.8) Структура извещения об ошибке ICMPv6

    Однако полнота сообщений об ошибках оборачивается другой проблемой. Теперь сообщения об ошибках имеют относительно большой размер, а значит, они смогут расходовать заметную часть ресурсов сети, если частота ошибок будет высокой, например, вследствие атаки. Хуже того, возможны косвенные атаки, когда злоумышленник "бомбардирует" узел А сбойными пакетами от имени источника Б, вызывая шквал ICMPv6 от А к Б. Предотвратить эту проблему можно, если все узлы IPv6 ограничат частоту, с которой они высылают сообщения об ошибках. Пусть это ограничение будет обязательным [§2.4(f) RFC 4443], хотя конкретный алгоритм мы оставим на усмотрение реализаций.

    Пример такого алгоритма в общих чертах описан в [§2.4(f) RFC 4443].

    Продолжая наш ход мысли, мы приходим к общему вопросу [§2.4(e) RFC 4443]: в каких случаях узел IPv6 обязан воздержаться от уведомления об ошибке? Прежде всего, чтобы избежать бесконечной "игры в пинг-понг", извещение об ошибке ни в коем случае нельзя высылать в ответ на другое извещение об ошибке. Возможно, в будущем у нас возникнут и справочные типы сообщений ICMPv6, которые могут спровоцировать "пинг-понг", так что за этим моментом надо внимательно следить.

    Например, в §5.2 нам встретится сообщение типа "переадресовка", которое высылается в ответ на другой пакет, хотя и не является извещением об ошибке.

    Также не следует отвечать ошибкой на пакет, адрес источника в котором не индивидуальный. Ведь извещение об ошибке уходит по адресу, который был источником пакета-виновника. Например, если пакет отправлен с группового адреса, то налицо явное нарушение протокола (см. §3.2); если это не результат сбоя, то попытка атаки "отказ в обслуживании". Помимо того, в некоторых вполне законных случаях — с ними мы встретимся ниже, это будут DAD в §5.4.1 и MLD в §6.4 — адрес источника в пакете может быть неопределенным, ::. На такой пакет отвечать ошибкой тоже нельзя, потому что это означало бы указать неопределенный адрес назначения в пакете ICMPv6.

    Также не следует в общем случае отвечать на групповые и широковещательные пакеты, поскольку иначе один ошибочный пакет вызовет целую бурю ответов ICMPv6. Чтобы определить, относится ли пакет к групповым или широковещательным, недостаточно проверить адрес источника в заголовке IPv6. Необходимо также убедиться, что пакет не был разослан группе или всем узлам на канальном уровне. К примеру, если пакет получен из канала Ethernet, то надо проверить адрес MAC назначения в заголовке кадра. Таким образом, для данной проверки нужна помощь со стороны канального уровня сетевого стека.

    Например, в сетевом стеке BSD Unix канальный уровень помечает принятый кадр как групповой или широковещательный с помощью битовых флагов M_MCAST и M_BCAST [ Gary R. Wright, W. Richard Stevens. TCP/IP Illustrated, Volume 2: The Implementation. Addison Wesley, 1995. p. 39. ], так что сетевому уровню достаточно проверить эти флаги — ему не нужно изучать заголовок кадра самостоятельно.

    Из последнего правила есть всего пара исключений [§2.4(e) RFC 4443]. Во-первых, если код нераспознанной опции IPv6 содержит двоичный префикс 10, то сообщение об ошибке требуется независимо от сетевого и канального адресов назначения пакета (см. §3.3.2). Во-вторых, ошибка "пакет слишком велик" высылается независимо от адресов назначения пакета [§3.2 RFC 4443], чтобы процедуру PMTUD можно было применять и к групповому трафику.

    Когда маршрутизатор отвечает извещением ICMPv6, его адрес назначения очевиден: это адрес источника из пакета-виновника. А какой надо выбрать адрес источника для самого извещения? В общем случае это нетривиальный вопрос [§2.2(b) RFC 4443]; ответ на него дает процедура DAS, с которой мы познакомимся в §6.6. Главная сложность здесь в том, чтобы зона адреса источника позволяла коммуникацию с адресом назначения. Этой особенности не было в IPv4, где маршрутизатор просто использовал адрес выходного интерфейса извещения [§4.3.2.4 RFC 1812]. Также сравните это со случаем высылающего ICMPv6 хоста: ему достаточно поменять местами адреса источника и назначения, при условии что пакет-виновник был адресован индивидуально хосту, а не группе [§2.2(a) RFC 4443].

    Страницы:

    Инкапсуляция на канальном уровне

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

    Возьмем, к примеру, протокол SLIP [RFC 1055]: он уже был готов передавать пакеты IPv6, когда IPv6 еще не было и в помине. Более того, SLIP способен передавать вперемежку пакеты любых версий IP, так как приемник всегда может узнать версию IP данного пакета из первого полубайта в его заголовке.

    Как мы помним, в SLIP тип инкапсулируемого протокола указывается неявно, по предварительному соглашению сторон. Поэтому канал SLIP без перенастройки сторон может передавать только один протокол сетевого уровня. Однако это не значит, что в кадр SLIP можно вложить только пакет IP: например, без малого 20 лет назад будущий автор этого курса на спор написал драйвер MS DOS, инкапсулирующий IPX в SLIP, и потом сыграл с друзьями-спорщиками немало партий в Doom по этому каналу, вполне отвечавшему модели OSI.

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

    Ethernet

    Из тех же соображений мы могли бы сказать, что инкапсуляция IP в кадры Ethernet [RFC 894] применима к любой версии IP. Однако в этом случае практика вносит свои коррективы. Оказывается, существуют сетевые устройства, так называемые "коммутаторы четвертого уровня", которые анализируют разные поля в заголовках Ethernet, IPv4, TCP, UDP, но забывают проверить поле версии IP [См. сообщение Steve Deering "Why a new etype of IPv6" в списке рассылки internet-history http://www.postel.org/pipermail/internet-history/2010-September/001437.html] ! Radia Perlman. Interconnections: Bridges, Routers, Switches, and Internetworking Protocols (2nd Edition). §9.8.1. То есть такое устройство, видя значение 0x800 в поле Ether Type, немедленно решает, что внутри кадра содержится пакет IPv4. Масштаб этой проблемы оказывается достаточно большим, так что придется оставить Ether Type 0x800 для IPv4 и назначить IPv6 отдельное значение Ether Type: 0x86DD [§3 RFC 2464].

    Радя Перлман в своей традиционной лекции Radia Perlman. Mythology and Folklore of Network Protocol Design. http://www.isoc-au.org.au/Events/PerlmanNov05.pdf сетует, что текст стандартов IPv4 [RFC 791] и IPv6 [RFC 2460] нигде явным образом не предписывает проверять поле версии IP на входе. Мы готовы согласиться с уважаемой Радей, что это явная недоработка. Видимо, виновата здесь опять история. Ведь изначально стандарты Internet были ориентированы на высококвалифицированных и здравомыслящих специалистов, для которых строгий и безопасный алгоритм приема однозначно следовал из формата передаваемых данных . Именно поэтому "протокол" и "формат сообщения" в TCP/IP долгое время были синонимами. Увы, сегодня уровень среднего читателя RFC заметно понизился по сравнению с 1970-м годом, и это можно компенсировать только четкостью и строгостью самих стандартов, не оставляющих места разночтениям . Справедливости ради заметим, что достаточно сложный протокол уже нельзя свести к формату его сообщений — необходимы также явные правила поведения сторон. Самостоятельно обсудите, как следует на входе обрабатывать пакет IPv6, принятый с Ether Type 0x800, и пакет IPv4, пришедший с Ether Type 0x86DD. Оптимальное решение вам подскажет принцип Постела.

    Еще один аспект инкапсуляции IP в Ethernet — это разрешение групповых адресов. Наивным решением было бы привлечь для этой цели ARP или аналогичный механизм, когда источник группового пакета сначала спрашивал бы, кто состоит в данной группе, и узнавал бы таким способом адреса MAC 48 ее членов. Однако на поверку это решение никуда не годится, потому что групповое вещание — основа для автоматического обнаружения сетевых ресурсов. Необходимость ARP в данном случае будет означать обнаружение с целью обнаружения — верный путь к "проблеме курицы и яйца", когда узлу надо послать пакет, чтобы запросить недостающие сведения, а эти сведения нужны, чтобы послать пакет. Поэтому давайте выдвинем такое требование: пусть групповое вещание IP привлекает только локальные механизмы разрешения адреса. Источник группового пакета должен быть способен преобразовать групповой адрес назначения IP в отвечающий ему канальный адрес MAC 48 своими силами, не опрашивая другие узлы. Тогда исходящий групповой пакет можно будет передать на канальный уровень сразу же, опираясь только на информацию, которая уже содержится в нем.

    Как мы помним, групповое вещание IPv4 вполне отвечало этому требованию, достигая разрешения групповых адресов простой подстановкой битов. А именно 23 младших бита группового адреса IPv4 помещались в соответствующие младшие биты группового адреса MAC 01-00-5E-00-00-00 [§6.4 RFC 1112]. Такое преобразование не было взаимно однозначным, поскольку в групповом адресе IPv4 переменные 28 бит . 4 старших бита фиксированы и составляют префикс 1110 класса D. В результате узел мог получить чужой групповой пакет, и поэтому он обязательно проверял адрес назначения: действительно ли он состоит в данной группе IP на данном интерфейсе?

    Давайте оставим подобный механизм разрешения групповых адресов и в IPv6. Но, чтобы уменьшить вероятность конфликтов, мы расширим поле соответствия. Пусть из группового адреса IPv6 в групповой адрес MAC попадают 4 младших байта, то есть 32 бита. Тогда нам потребуется другой префикс MAC, длиной 16 бит. На эту роль назначен префикс 0x3333, отвечающий адресам от 33-33-00-00-00-00 до 33-33-FF-FF-FF-FF.

    Убедитесь, что перед вами групповые адреса MAC.

    Еще можно заметить, что это локальные, а не глобальные адреса MAC. В отличие от 01-00-5E, новый диапазон OUI 33-33-XX не пришлось покупать у IEEE.

    "3333 Coyote Hill Road" — это был адрес Исследовательского центра Xerox в Пало-Альто, откуда появился Ethernet [§2.3.1 RFC 5342].

    Обсудите недостатки использования в данном случае локальных адресов MAC. (Возможен конфликт с протоколами местного значения; однако фильтрация по адресам сетевого уровня предотвратит нежелательные последствия.)

    Вот пример преобразования: групповым адресам IPv6 FF02::C001:DEAD:BEEF и FF0E::BA1D:DEAD:BEEF отвечает один и тот же групповой адрес MAC 48 33-33-DE-AD-BE-EF.

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

  • общепринятые групповые адреса: 0x0000 0000–0x3FFF FFFF;
  • постоянно назначенные группы: 0x4000 0000–0x7FFF FFFF;
  • временно занятые адреса: 0x8000 0000–0xFFFF FFFF.
  • Разница между общепринятыми групповыми адресами и постоянно назначенными группами такова: общепринятые групповые адреса назначены в системных целях и нужны для работы протоколов стека IPv6, тогда как постоянно назначенные группы просто созданы для поддержки глобальных проектов, важных для всего Internet. Например, 0x0000 0001 ("все узлы") — это общепринятый групповой адрес, тогда как 0x4040 4040 (NTP) — всего лишь постоянно назначенная группа.

    Те же самые требования закреплены в реестре IANA для групп IPv6 http://www.iana.org/assignments/ipv6-multicast-addresses/ .

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

    PPP

    Пример канального протокола, поддержка IPv6 в котором неизбежно требует дополнительной работы, — это PPP. Чтобы обеспечить высокий уровень услуг, PPP готовит канал к работе каждого сетевого протокола, например, согласуя адреса на его концах. Это задание PPP делегирует дочернему протоколу семейства NCP. Уже существующий NCP для IP, IPCP [RFC 1332], может работать только с адресами длиной 32 бита, так что он не годится для IPv6. Посему для IPv6 необходим свой собственный NCP — IPV6CP [RFC 5072].

    Прежде всего, назначим IPv6 и его NCP уникальные номера из реестра параметров PPP http://www.iana.org/assignments/ppp-numbers , чтобы кадры PPP с ними можно было однозначно демультиплексировать. У IPv6 это 0x0057, а номер его NCP по традиции на 0x8000 больше, то есть 0x8057. Теперь мы можем инкапсулировать оба протокола в PPP, и пора подумать над функциями IPv6CP.

    Какое задание, специфичное именно для IPv6, мы можем поставить перед IPV6CP? По нашей задумке (см. §2.7), сетевому интерфейсу IPv6 в высшей степени полезно иметь какой-то эквивалент адреса EUI 64, чтобы на его основе можно было создать условно уникальный идентификатор интерфейса. Хорошо, когда у интерфейса есть настоящий, глобально уникальный EUI 64. Канальный адрес MAC 48 — тоже неплохой вариант, потому что его можно превратить в EUI 64 по общепринятому рецепту, который мы обсудили в §2.7. Но как быть с PPP, где явных канальных адресов вообще нет? Ведь каналу PPP вполне достаточно неявной адресации: всякий кадр PPP, переданный в канал, будет принят узлом на другом конце канала, и только им. Вот пусть это и будет работой IPV6CP: согласовать два уникальных идентификатора на концах канала PPP. Так как в конечном итоге нам нужны идентификаторы интерфейса в формате "модифицированный EUI 64", то пусть IPV6CP согласует непосредственно их, а не промежуточные EUI 64 или MAC 48.

    Каким требованиям должны удовлетворять эти идентификаторы [§4.1 RFC 5072]?

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

    Зона уникальности полученных адресов IPv6 будет зависеть от области действия префикса подсети: из глобального префикса мы получим глобально уникальные адреса, из внутрисайтового префикса — адреса, уникальные в пределах данного сайта, а из внутриканального префикса — внутриканальные адреса для данного канала PPP.

  • Желательно, чтобы идентификаторы были постоянными и не менялись, к примеру, при перезагрузке узлов. Каждый узел вправе использовать все доступные ему источники уникальности, чтобы построить такой идентификатор. В первую очередь, на эту роль претендуют аппаратные адреса EUI 64 и MAC 48 других сетевых интерфейсов узла. Если таковых нет, то следующий кандидат — это серийные номера основных компонентов узла. Наконец, как крайнее средство, идентификатор все же может быть случайным.
  • Составляя идентификатор, узел обязан соблюдать формат "модифицированный EUI 64" (см. §2.7). В частности, бит U/L нужно установить в единицу тогда, и только тогда, когда идентификатор создан на основе настоящего глобального адреса EUI 64 или MAC 48, назначенного именно этому узлу. Само преобразование EUI 64 и MAC 48 в "модифицированный EUI 64" мы уже подробно обсудили там же, в §2.7.
  • Само согласование идентификаторов представляет собой простой и доступный пример работы механизма опций PPP. В данном случае достаточно сообщений Conf Req, Conf Rej, Conf Nak и Conf Ack (см. Табл. 4.1), а код соответствующей опции IPV6CP — 1 [§4 RFC 5072].

    Сообщения IPV6CP при согласовании идентификатора интерфейса
    Сообщение Смысл Допустимые варианты ответа
    Запрос
    Conf-Req Источник хотел бы присвоить себе (не удаленной стороне!) такой-то идентификатор интерфейса (прилагается к сообщению). Пока что это пробный идентификатор (tentative identifier), потому что источник только испытывает его. Если идентификатор в сообщении нулевой, то источник не может сам справиться с выбором и просит удаленную сторону о помощи. Conf Rej, Conf Nak, Conf Ack
    Варианты ответа
    Conf-Rej Источник вообще не поддерживает данную опцию. Удаленная сторона должна продолжить согласование сообщением Conf-Req без этой опции или прервать сеанс. Conf-Req
    Conf-Nak Источник поддерживает опцию, но не удовлетворен ее значением. Видимо, значение было нулевым, или источник сам хотел назначить себе такой же идентификатор. Поэтому источник предлагает удаленной стороне свой вариант ее, удаленной стороны, идентификатора (прилагается к сообщению). Удаленная сторона должна рассмотреть этот вариант и послать новый Conf-Req. Conf-Req
    Conf-Ack Источник отвечает, что согласен с выбором удаленной стороны, поскольку он не нулевой и отличается от пробного идентификатора самого источника. На этом согласование завершено. Нет

    Как мы помним, согласование опции PPP проходит независимо в каждом из двух возможных направлений. Поэтому стороны А и Б ведут два, на первый взгляд, независимых диалога IPV6CP. Итогом одного из них будет идентификатор стороны А, а другого — идентификатор стороны Б.

    Механизм согласования IPV6CP допускает следующий сценарий: узел Б объявляет нулевой идентификатор, и тогда узел А подбирает идентификатор не только для себя, но и для удаленного узла Б. В этом случае бит U/L в идентификаторе для Б надо сбросить, даже если он получен из глобального EUI 64. Ведь этот EUI 64 принадлежит узлу А, а не узлу Б. (Как исключение, этот бит можно установить в единицу, если узел А имел в запасе еще один глобальный EUI 64, а теперь передает его узлу Б в безраздельное пользование на время данного сеанса PPP [§4.1 RFC 5072].)

    Чтобы процесс согласования идентификаторов не зациклился, сформулируем несколько дополнительных правил поведения стороны IPV6CP:

  • не повторяться, то есть не предлагать один и тот же идентификатор дважды в Conf Req или Conf Nak;

    Тем не менее, идентификатор, который уже фигурировал в Conf-Nak, вполне может появиться в ответном Conf-Req. Однако в этом случае идентификатор придет от удаленной стороны, а мы говорим о поведении одной стороны, которую мы по традиции считаем нашей локальной.

  • не капризничать, то есть соглашаться с первым же подходящим идентификатором:

    если идентификатор в принятом нами Conf Req не совпадает с нашим собственным пробным значением, которое мы объявили в исходящем Conf Req, то мы немедленно соглашаемся с ним, отвечая Conf Ack. Так мы соглашаемся с выбором удаленной стороны для нее самой;

    если идентификатор во входящем Conf-Nak не совпадает с тем, что мы сами предложили удаленной стороне в последнем переданном нами Conf Nak, то мы обязаны принять его в качестве нашего пробного значения и выслать об этом Conf Req. Здесь мы соглашаемся с предложением удаленной стороны для нас.

    Обратите внимание, как между встречными диалогами IPV6CP возникает скрытая зависимость. Чтобы правильно ответить на входящее сообщение Conf Req, нам надо помнить наш собственный пробный идентификатор, который мы наверняка сами объявили в исходящем Conf Req. А правильная реакция на входящий Conf Nak возможна только при условии, что мы запомнили значение опции из посланного нами самими Conf Nak.

  • Эти правила склоняют согласование идентификаторов к одному из двух вероятных путей:

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

    Поэтому, если обе стороны передают нулевые значения в своих сообщениях Conf Req, согласование надо немедленно прекратить ради устойчивости протокола [§4.1 RFC 5072].

    Гораздо большее влияние на ход процедуры IPV6CP окажет то, насколько одновременны встреченные диалоги. Поэтому, чтобы лучше почувствовать работу механизма IPV6CP, давайте рассмотрим процессы нулевого и первого порядков (немедленное согласие; конфликт, а затем согласие с предложением удаленной стороны) в двух крайних случаях: когда встречные диалоги совершенно синхронны или строго последовательны.

    Если стороны с самого начала выбрали себе разные пробные идентификаторы, то встречные диалоги независимы, а поэтому не имеет значения, одновременны ли они, или же один следует за другим. Итог будет один: стороны утвердят самостоятельно выбранные идентификаторы.

    (рис 4.1) IPV6CP: Параллельное согласование без конфликта

    Параллельный сценарий (рис. 4.1) в этом случае преобразуется в свой последовательный эквивалент (рис. 4.2) простым сдвигом одного из диалогов вдоль оси времени.

    (рис 4.2) IPV6CP: Последовательное согласование без конфликта

    Если же стороны случайно выбрали один и тот же идентификатор, исход согласования зависит от того, как встречные диалоги расположены друг относительно друга на оси времени. Так, если они одновременны (рис. 4.3), то каждая из сторон согласится с подсказкой удаленной стороны и выберет именно ее собственным идентификатором.

    (рис 4.3) IPV6CP: Параллельное согласование и преодоление конфликта

    Ну, а если диалоги будут вестись последовательно (рис. 4.4), то сторона А, открывшая согласование первой, будет вынуждена принять подсказку удаленной стороны Б, тогда как сторона Б сможет настоять на своем первоначальном выборе идентификатора.

    (рис 4.4) IPV6CP: Последовательное согласование и преодоление конфликта

    Пусть читатель самостоятельно составит такие же две схемы для случая, когда одна из сторон просит своего визави о помощи в выборе идентификатора, объявляя в Conf Req нулевое значение.

    На практике сторона IPV6CP может прибегать к безобидным трюкам, чтобы обойтись без конфликта уже в нулевом порядке согласования. Так, она может отложить выбор своего пробного идентификатора до тех пор, пока не придет запрос Conf Req удаленной стороны. После этого ей достаточно инвертировать хотя бы один бит в чужом идентификаторе, чтобы получить заведомо уникальный идентификатор для себя самой. Читатель может составить схему такого диалога самостоятельно. Конечно, в этом случае встает вопрос, кто пошлет Conf Req первым и как избежать возможного тупика в развитии сеанса, когда ни одна из сторон не проявляет инициативу. Хотя, судя по выдачам tcpdump, этот трюк использует Cisco IOS версии 12.4, независимый обмен запросами Conf Req нам кажется более надежным.

    Взаимодействие с вышестоящими протоколами

    В стеке TCP/IP на уровень IP непосредственно опираются протоколы нескольких видов: управляющие, то есть ICMP; транспортные, такие как TCP, UDP и SCTP; туннельные, например, IP IP, GRE, EtherIP. О том, как нам быть с ICMP, мы поговорим в §4.3, а сейчас давайте рассмотрим вопрос инкапсуляции IPv6 в целом, сделав ударение на протоколах транспортного уровня.

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

    Прежде всего, мы должны сказать, как блок данных протокола (БДП), стоящего над IP, следует инкапсулировать в пакет IPv6. На самом деле, правила этой инкапсуляции мы уже задали, когда говорили в §3.1 о структуре заголовков пакета IPv6. Тогда мы сказали, что пакет IPv6 содержит в своем начале цепочку из одного или более простых заголовков. Каждый простой заголовок содержит поле "следующий заголовок", возможные значения которого хранятся в реестре протоколов IP http://www.iana.org/assignments/protocol-numbers/ . Значит, последний заголовок в цепочке может ссылаться на тип полезной нагрузки пакета точно так же, как это делал заголовок IPv4. Например, если пакет IPv6 содержит в себе сегмент TCP, то последнее поле "следующий заголовок" содержит значение 6; если дейтаграмму UDP, то 17; а если пакет IPv4, то 4. Эти примеры иллюстрирует рис 4.5(рис 4.5) Примеры инкапсуляции IPv6 (с.з. = следующий заголовок)

    Следующим шагом будет найти и исправить все явные и неявные зависимости вышестоящих протоколов от IPv4. К счастью, у стандартных и активно применяемых протоколов их оказывается немного.

    Кстати, была предпринята попытка составить полный список зависимостей от IPv4 у протоколов, описанных в RFC. Отчет занял восемь (!) документов RFC, 3789–3796.

    Главная зависимость TCP и UDP от IPv4 заключается в формате псевдозаголовка, который служит для вычисления контрольной суммы. Этот псевдозаголовок включает в себя адреса IPv4 источника и назначения. Чтобы TCP и UDP смогли работать поверх IPv6, потребуется новый формат псевдозаголовка, который учтет специфику нового протокола [§8.1 RFC 2460], как это показано на рис. 4.6.

    Самостоятельно изучите вопрос, нужно ли задавать новый псевдозаголовок для SCTP. (Подсказка: начните с вводной статьи R. Stewart, M. Tüxen, P. Lei. SCTP: What is it, and how to use it? BSDCan 2008. http://www.bsdcan.org/2008/schedule/attachments/44_bsdcan_sctp.pdf .)

    (рис 4.6) Псевдозаголовок IPv6 для вычисления контрольных

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

    "В лоб" это вычисление можно провести, вычтя длины всех заголовков расширения IPv6 в данном пакете — не считая основного заголовка! — из значения поля "длина полезной нагрузки" в его основном заголовке IPv6. Альтернативный способ, близкий знатокам языка Си, — это найти разность указателей на конец пакета и заголовок TCP, приведенных к типу char *. Главное — не перепутать при этом конец пакета с последним байтом пакета, потому что их смещения отличаются на один байт.

    Что касается поля "следующий заголовок", то оно теперь хранит тип вышестоящего протокола, а не просто копирует такое же поле основного заголовка IP; ведь в пакете могут быть заголовки расширения, а тогда тип вышестоящего протокола будет скрыт в самом последнем из них.

    Кроме того, протокол UDP неявно полагается на контрольную сумму IPv4, когда объявляет вычисление контрольной суммы UDP необязательным: он предполагает, что заголовок IP защищает себя от повреждения. Но заголовок IPv6 сам по себе не защищен, и поэтому вычисление контрольной суммы UDP при работе поверх IPv6 становится обязательным [§8.1 RFC 2460]. Если дейтаграмма UDP с нулевым значением в поле "контрольная сумма" получена в пакете IPv6, то ее следует уничтожить.

    Освежим нашу память и вспомним несколько фактов. Контрольная сумма TCP/IP вычисляется в 16 битном обратном коде, где есть два арифметически эквивалентных нулевых значения, 0x0000 и 0xFFFF. Значение поля "контрольная сумма" в TCP и UDP — это результат побитовой инверсии вычисленной суммы. (В обратном коде инверсия битов приводит к смене знака числа, так что неудивительно, что сумма всех слов сообщения, включая его контрольную сумму, должна быть нулевой.) Поэтому мы можем отличить нулевую контрольную сумму от неизвестной, задав дополнительное правило. Для этого мы предписываем хранить истинно нулевую сумму только как 0x0000, что после инверсии даст нам 0xFFFF. А значение суммы 0xFFFF, что после инверсии дает 0x0000, мы назначаем особому случаю, когда источник дейтаграммы сумму вообще не посчитал. Просто и изящно, не правда ли?

    Трансляцию NAT вполне можно обобщить на обе версии IP и применить, чтобы обеспечить взаимодействие хостов IPv4 с хостами IPv6. Но как быть транслятору NAT46 или NAT64, если со стороны IPv4 он получает дейтаграмму UDP с неопределенной контрольной суммой? В этом случае при ее трансляции в IPv6 придется вычислить значение контрольной суммы и поместить в заголовок UDP. Тем самым транслятор заверит целостность дейтаграммы без достаточных на то оснований, ведь дейтаграмма могла поступить к транслятору на вход уже поврежденной! Вот нам еще один пример, как NAT нарушает сквозной принцип, лежащий в основе архитектуры Internet.

    А как появление IPv6 скажется на работе туннельных протоколов? Если такой протокол сконструирован надлежащим образом, то изменений в нем не потребуется. Так, например, протоколу GRE достаточно учесть, что IPv6 получил свой собственный Ether Type 0x86DD. Еще при туннелировании IPv6 будет полезно включить вычисление контрольной суммы GRE, чтобы обеспечить дополнительную защиту пакетов от повреждения.

    Туннельный протокол IP IP тоже готов к работе с IPv6 как снаружи [RFC 2473], так и внутри [§3 RFC 3056, §3.1 RFC 4213] туннеля. Достаточно учесть при инкапсуляции, что исходному пакету IPv4 отвечает номер протокола IP 4, а исходному пакету IPv6 — 41 http://www.iana.org/assignments/protocol-numbers/ [ ]. Вполне можно строить и смешанные туннели "IPv4 внутри IPv6" и "IPv6 внутри IPv4". Сводная таблица таких способов инкапсуляции — Табл. 4.2.

    Туннельная инкапсуляция IP-IP после появления IPv6
    Заголовки IPv6-в-IPv6 IPv6-в-IPv4 IPv4-в-IPv6
    Внешний IPv6: "след. заголовок" = 41 IPv4: "протокол" = 41 IPv6: "след. заголовок" = 4
    Внутренний IPv6 IPv6 IPv4

    Наконец, у всех типов туннелей есть общая важная деталь — управление фрагментацией. Когда внешний протокол туннеля — IPv4, то узел на входе в туннель (инкапсулятор) может разрешить или запретить транзитную фрагментацию туннельных пакетов с помощью флага DF. Напротив, в среде IPv6 транзитная фрагментация всегда запрещена; это касается и туннельных пакетов, внешний протокол которых — IPv6, а поэтому их длиной обязан управлять сам инкапсулятор. Здесь его поджидает, как минимум, одна сложность: внутренний MTU туннеля обязан быть не меньше 1280 байт, тогда как и внешний MTU трассы (PMTU) туннеля может оказаться равным 1280 байт. Инкапсуляция исходного пакета длиной 1280 байт заведомо даст туннельный пакет длиной более 1280 байт, который не пройдет данную трассу без фрагментации. Это означает, что в среде IPv6 инкапсулятор обязан быть готов к фрагментации туннельных пакетов, потому что в общем случае без нее не обойтись. Самый простой, хотя и не слишком эффективный подход к выбору длины туннельного пакета — это принять нижнюю оценку PMTU туннеля равной 1280 байт и фрагментировать туннельные пакеты исходя из этого. Тогда внутренний MTU туннеля будет ограничен только диапазоном длины пакета IPv6 до фрагментации за вычетом длины туннельных заголовков: 65575 - X. Подход посложнее - это выполнение процедуры PMTUD инкапсулятором.

    В RFC 4459 можно найти более обширный анализ теории и практики управления фрагментацией в туннеле.

    Управление

    Хотя ICMP [RFC 792] вполне можно было бы "притянуть за уши" к управлению IPv6, новая версия IP — это хороший повод пересмотреть и сопряженный с ним протокол управления. Чтобы не заботиться о совместимости с ICMP, давайте назначим новому протоколу другой номер, 58, из реестра протоколов IP. Новорожденный протокол мы назовем "ICMP версии 6", а кратко — ICMPv6 [RFC 4443], хотя заголовок ICMP и не содержит поля версии. Чтобы избежать разночтений, "старый" ICMP для IPv4 мы теперь обозначим как ICMPv4, по крайней мере, при его сравнении с ICMPv6.

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

    Какие требования мы предъявим к протоколу управления IPv6? В первую очередь, он не должен отставать от IPv6 в плане гибкости и расширяемости. Это требование мы выполним, "собрав" сообщение ICMPv6 из двух частей. У первой части формат будет постоянным, а у второй — переменным, как изображено на рис 4.7(рис 4.7) Общая структура сообщения ICMPv6

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

    Поскольку ICMPv6 — отдельный протокол, номера его типов никак не связаны с номерами типов ICMPv4. Номера типов ICMPv6 мы будем распределять с нуля, так что у нас есть возможность придать им определенную структуру. В ICMPv4 типы извещений об ошибках шли вперемежку с чисто справочными типами. Теперь ради удобства давайте проведем между ними границу. Пусть типы извещений об ошибках получают номера из диапазона 0–127, а справочные типы — из диапазона 128–255. Тогда, чтобы узнать характер сообщения ICMPv6, достаточно взглянуть на старший бит в его поле "тип": 0 означает произошедшую ошибку, а 1 — справочные сведения [§2.1 RFC 4443].

    Толкование поля "код" зависит от типа сообщения. Например, с его помощью можно поделить сообщения одного типа на более узкие группы, чем мы воспользуемся в текущем разделе.

    Формат переменной части также целиком зависит от типа сообщения. Эту часть мы назовем телом сообщения (message body). Она простирается до конца пакета, а длину ее можно вычислить, исходя из длины полезной нагрузки в заголовке IPv6.

    Согласно принятой в IPv6 модели защиты данных от повреждения, контрольная сумма ICMPv6 должна защищать не только само сообщение, но и заголовок IPv6. Достичь этого можно, используя при вычислении контрольной суммы псевдозаголовок стандартного формата (см. §4.2). В остальном правила обычные: на время расчета контрольной суммы псевдозаголовок условно помещается перед заголовком ICMPv6, а само поле "контрольная сумма" явно или "мысленно" обнуляется [§2.3 RFC 4443].

    Какое значение надо поместить в поле "следующий заголовок" такого псевдозаголовка? (Ответ: 58 — ICMPv6.)

    Заметим, что ICMPv4 не использовал псевдозаголовка и вычислял только контрольную сумму собственного БДП, вложенного в пакет IPv4. Так что применение псевдозаголовка в ICMP — это новшество IPv6, вызванное отсутствием контрольной суммы в основном заголовке IPv6.

    Сообщение ICMPv6 составляет всю полезную нагрузку пакета IPv6, так что поле длины заголовку ICMPv6 не требуется: длину сообщения ICMPv6 можно вычислить, зная длину всего пакета и структуру его заголовков. Это вычисление понадобится, чтобы заполнить псевдозаголовок, так как он содержит длину блока данных вышестоящего протокола (см. рис. 4.6).

    Напомним о двусмысленности термина "полезная нагрузка" в IPv6. В общеупотребительном смысле, это последняя секция пакета, после основного заголовка и заголовков расширения IPv6, занятая блоком данных вышестоящего протокола: дейтаграммой UDP, сегментом TCP, сообщением ICMPv6 . В узком смысле поля "длина полезной нагрузки" из основного заголовка IPv6 это весь пакет за вычетом основного заголовка. При заполнении псевдозаголовка эти два толкования встречаются, так как в заголовке IPv6 указана "длина полезной нагрузки" (смысл №2), а надо найти длину блока данных вышестоящего протокола (смысл №1). Это касается, в первую очередь, узла назначения, так как он должен разобрать и проверить входящий пакет.

    Перейдем теперь к типам сообщений ICMPv6. Для начала, в Табл. 4.3, мы определим только самые необходимые из них [§2.1 RFC 4443], а для будущих назначений создадим реестр http://www.iana.org/assignments/icmpv6-parameters .

    Основные типы сообщений ICMPv6
    Тип Название (рус.) Название (англ.) Ссылка
    Извещения об ошибках
    1 Адресат недоступен Destination Unreachable [§3.1 RFC 4443]
    2 Пакет слишком велик Packet Too Big [§3.2 RFC 4443]
    3 Время истекло Time Exceeded [§3.3 RFC 4443]
    4 Проблема в параметре Parameter Problem [§3.4 RFC 4443]
    100 Для частных экспериментов [§2.1 RFC 4443]
    101 Для частных экспериментов [§2.1 RFC 4443]
    127 Резерв для расширения [§2.1 RFC 4443]
    Справочные сообщения
    128 Запрос эха Echo Request [§4.1 RFC 4443]
    129 Ответ эхом Echo Reply [§4.2 RFC 4443]
    200 Для частных экспериментов [§2.1 RFC 4443]
    201 Для частных экспериментов [§2.1 RFC 4443]
    255 Резерв для расширения [§2.1 RFC 4443]

    Тип 0 не назначен, скорее всего, из-за уже знакомого нам предубеждения перед нулевыми значениями, и это вполне оправдано: ноль часто означает "нет данных".

    Что еще изменилось по сравнению с основными типами ICMPv4? Прежде всего, извещения "пакет слишком велик" и "время истекло" получили отдельные типы. Теперь "адресат недоступен" говорит только о невозможности доставить пакет из-за проблем на уровне адресации и маршрутизации, или же ввиду ограничений политики [§3.1 RFC 4443], например:

  • код 0: нет маршрута к адресу назначения, то есть неизвестно, куда продвигать пакет;
  • код 1: обмен пакетами с адресатом запрещен в административном порядке, например, политикой сетевой безопасности;
  • код 2: адресат находится за пределами зоны источника, то есть предотвращено нарушение границ зоны, такое как попытка направить пакет узлу совершенно другой сети, используя внутриканальный адрес источника;
  • прочие коды "адресат недоступен" приведены в [§3.1 RFC 4443] и реестре http://www.iana.org/assignments/icmpv6-parameters [ ].
  • Какую практическую пользу принесет то, что у сообщения "пакет слишком велик" теперь есть свой тип, а не только код в рамках типа "адресат недоступен", как это было в ICMPv4? Прежде всего, инженерам по сетевой безопасности будет легче составлять политики, которые запрещают произвольные извещения "адресат недоступен", но при этом не блокируют работу PMTUD. В IPv4 это было серьезной проблемой. Во-первых, сами инженеры часто забывали разрешить именно это сочетание типа и кода ICMPv4. А во-вторых, было много примеров сетевого программного обеспечения, которое позволяло фильтровать ICMPv4 только по значению поля "тип", но не "код", так что невозможно было избирательно разрешить PMTUD. В ICMPv6 этот опыт нельзя было не учесть, так как важность правильной работы PMTUD возросла многократно из-за новых правил фрагментации, когда решение о размере пакета принимает только его источник (см. §3.3.4).

    В идеале, фильтрация ICMP не должна представлять сложности для сетевого экрана, полноценно отслеживающего прикладные сеансы. Ему достаточно пропускать сообщения ICMP, относящиеся к существующим сеансам, и блокировать "залетные" сообщения. К сожалению, не все межсетевые экраны рассматривают сигнализацию ICMP как неотъемлемую часть сеанса, но это всего лишь практическое ограничение, которое можно было бы устранить доработкой программы. Более фундаментальная проблема состоит в том, что сообщение ICMPv4 может содержать недостаточно сведений, чтобы точно отнести его к определенному прикладному сеансу. Мы устраним эту проблему в ICMPv6 еще до конца данного раздела.

    Далее, как мы уже отметили, говоря о фрагментации и сборке в §3.3.4, извещение "пакет слишком велик" обязано содержать в себе значение MTU выходного канала, куда "не уместился" пакет. Благодаря этой информации, процедура PMTUD сойдется гораздо быстрее.

    Извещение "проблема в параметре" подходит для того, чтобы сообщать источнику, помимо прочих проблем в составленном им пакете, о неопознанных опциях и неизвестных значениях "следующий заголовок". Чтобы различать эти проблемы, можно назначить им разные коды [§3.4 RFC 4443]:

  • код 0: недопустимое значение какого-либо поля в заголовке либо неверно сформированный пакет, например, длина фрагмента с M = 1 не кратна восьми;
  • код 1: неопознанное значение "следующий заголовок" — возможно, адресат не поддерживает это расширение или протокол ;
  • код 2: неопознанная опция IPv6 — высылается, только если старшие биты кода опции требуют такого извещения (см. §3.3.2).
  • Но почему тип 3 называется "время истекло"? Ведь в §3.2 мы приняли решение, что теперь вместо поля "время жизни пакета" будет поле "предельное число шагов". Конечно, это так, но тип 3 охватывает два разных события, и второе из них по-прежнему связано со временем [§3.3 RFC 4443]:

  • код 0: превышено предельное число шагов, то есть пакет предположительно зациклился в сети, отчего значение его поля "предельное число шагов" достигло нуля;
  • код 1: истек тайм-аут сборки, то есть адресат не дождался прихода всех фрагментов пакета.
  • К сожалению, текст [§3.3 RFC 4443] можно трактовать так, будто маршрутизатор должен проверить поле "предельное число шагов" во входящем пакете, как только он принят из сети. Как мы знаем из §3.2 настоящего курса, на самом деле этому обязательно предшествует вывод, что пакет транзитный и подлежит продвижению, так как иначе поле "предельное число шагов" вообще не проверяется. Видимо, авторы [RFC 4443] руководствовались моделью идеального маршрутизатора, у которого все пакеты — транзитные, но забыли сделать на этом акцент. Увы, в наши дни такие недомолвки чреваты неправильными реализациями.

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

    Модель управления ошибками IPv6 остается такая же, как и в IPv4: извещение об ошибке высылается только при невозможности обработать принятый пакет IP. То есть за каждым сообщением об ошибке стоит определенный пакет-виновник. А значит, к извещению об ошибке обязательно надо приложить хотя бы начало пакета-виновника, чтобы его источник смог опознать пакет и принять необходимые меры. Это правило касается всех извещений об ошибках ICMPv6. Оно не ново — подобное правило было и в ICMPv4.

    По одной из точек зрения, даже минимальный MTU IPv4 был выбран в связи с сигнализацией ошибок таким образом. Ведь в 68 байт как раз умещается внешний заголовок IPv4 (20 байт), заголовок ICMPv4 (8 байт), заголовок IPv4 виновника (20 байт), и остается еще 20 байт для заголовка TCP.

    Однако теперь у нас есть шанс устранить одну застарелую проблему. Она была в том, что сообщение ICMPv4 содержало слишком мало байтов из пакета-виновника: стандартом требовались лишь его заголовок IPv4 и 8 байт полезной нагрузки [§3.2.2 RFC 1122]. Когда в сети возникали сложные схемы многоуровневой инкапсуляции, например, с привлечением туннелей, информативные части пакета-виновника просто не помещались в сообщение.

    Теперь же мы вправе — и обязаны — потребовать, чтобы всякое сообщение об ошибке ICMPv6 содержало как можно большую часть пакета-виновника [§2.4(c) RFC 4443]. Очевидно, что это должно быть начало пакета [§2.1 RFC 4443], а не середина или конец, потому что именно начало пакета дает ключ к его содержимому.

    Подумайте, что можно включить в копию пакета-виновника, когда происходит тайм-аут сборки, а первый фрагмент так и не получен. (Вариант: образец нефрагментируемой части и заголовок фрагмента.)

    Насколько велика может быть эта часть? Мы заметили в §3.3.4, что ICMPv6 поможет нам управлять фрагментацией. Но если извещения ICMPv6 сами будут подвержены фрагментации, то наша схема зациклится. Поэтому фрагментировать извещения ICMPv6, вообще говоря, нельзя. Следовательно, ограничением сверху здесь будет минимальный MTU IPv6, 1280 байт. Конечно, в этот MTU должен умещаться весь пакет ICMPv6, включая заголовок IPv6 и возможные заголовки расширения.

    Нетрудно убедиться, что вместе эти два условия (как можно больше, но не больше чем) допускают только два случая:

  • извещение содержит пакет-виновник целиком (в том виде как он был доступен извещающему узлу);
  • извещение содержит начало пакета-виновника, а весь пакет ICMPv6 занимает 1280 байт.
  • В результате извещения ICMPv6 будут иметь единый формат, представленный на рис 4.8(рис 4.8) Структура извещения об ошибке ICMPv6

    Однако полнота сообщений об ошибках оборачивается другой проблемой. Теперь сообщения об ошибках имеют относительно большой размер, а значит, они смогут расходовать заметную часть ресурсов сети, если частота ошибок будет высокой, например, вследствие атаки. Хуже того, возможны косвенные атаки, когда злоумышленник "бомбардирует" узел А сбойными пакетами от имени источника Б, вызывая шквал ICMPv6 от А к Б. Предотвратить эту проблему можно, если все узлы IPv6 ограничат частоту, с которой они высылают сообщения об ошибках. Пусть это ограничение будет обязательным [§2.4(f) RFC 4443], хотя конкретный алгоритм мы оставим на усмотрение реализаций.

    Пример такого алгоритма в общих чертах описан в [§2.4(f) RFC 4443].

    Продолжая наш ход мысли, мы приходим к общему вопросу [§2.4(e) RFC 4443]: в каких случаях узел IPv6 обязан воздержаться от уведомления об ошибке? Прежде всего, чтобы избежать бесконечной "игры в пинг-понг", извещение об ошибке ни в коем случае нельзя высылать в ответ на другое извещение об ошибке. Возможно, в будущем у нас возникнут и справочные типы сообщений ICMPv6, которые могут спровоцировать "пинг-понг", так что за этим моментом надо внимательно следить.

    Например, в §5.2 нам встретится сообщение типа "переадресовка", которое высылается в ответ на другой пакет, хотя и не является извещением об ошибке.

    Также не следует отвечать ошибкой на пакет, адрес источника в котором не индивидуальный. Ведь извещение об ошибке уходит по адресу, который был источником пакета-виновника. Например, если пакет отправлен с группового адреса, то налицо явное нарушение протокола (см. §3.2); если это не результат сбоя, то попытка атаки "отказ в обслуживании". Помимо того, в некоторых вполне законных случаях — с ними мы встретимся ниже, это будут DAD в §5.4.1 и MLD в §6.4 — адрес источника в пакете может быть неопределенным, ::. На такой пакет отвечать ошибкой тоже нельзя, потому что это означало бы указать неопределенный адрес назначения в пакете ICMPv6.

    Также не следует в общем случае отвечать на групповые и широковещательные пакеты, поскольку иначе один ошибочный пакет вызовет целую бурю ответов ICMPv6. Чтобы определить, относится ли пакет к групповым или широковещательным, недостаточно проверить адрес источника в заголовке IPv6. Необходимо также убедиться, что пакет не был разослан группе или всем узлам на канальном уровне. К примеру, если пакет получен из канала Ethernet, то надо проверить адрес MAC назначения в заголовке кадра. Таким образом, для данной проверки нужна помощь со стороны канального уровня сетевого стека.

    Например, в сетевом стеке BSD Unix канальный уровень помечает принятый кадр как групповой или широковещательный с помощью битовых флагов M_MCAST и M_BCAST [ Gary R. Wright, W. Richard Stevens. TCP/IP Illustrated, Volume 2: The Implementation. Addison Wesley, 1995. p. 39. ], так что сетевому уровню достаточно проверить эти флаги — ему не нужно изучать заголовок кадра самостоятельно.

    Из последнего правила есть всего пара исключений [§2.4(e) RFC 4443]. Во-первых, если код нераспознанной опции IPv6 содержит двоичный префикс 10, то сообщение об ошибке требуется независимо от сетевого и канального адресов назначения пакета (см. §3.3.2). Во-вторых, ошибка "пакет слишком велик" высылается независимо от адресов назначения пакета [§3.2 RFC 4443], чтобы процедуру PMTUD можно было применять и к групповому трафику.

    Когда маршрутизатор отвечает извещением ICMPv6, его адрес назначения очевиден: это адрес источника из пакета-виновника. А какой надо выбрать адрес источника для самого извещения? В общем случае это нетривиальный вопрос [§2.2(b) RFC 4443]; ответ на него дает процедура DAS, с которой мы познакомимся в §6.6. Главная сложность здесь в том, чтобы зона адреса источника позволяла коммуникацию с адресом назначения. Этой особенности не было в IPv4, где маршрутизатор просто использовал адрес выходного интерфейса извещения [§4.3.2.4 RFC 1812]. Также сравните это со случаем высылающего ICMPv6 хоста: ему достаточно поменять местами адреса источника и назначения, при условии что пакет-виновник был адресован индивидуально хосту, а не группе [§2.2(a) RFC 4443].

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