Разобравшись в первом приближении с адресом, мы можем перейти к следующему ключевому элементу IPv6 — пакету.
Общепринятое сегодня деление пакета на заголовок и полезную нагрузку если и не является оптимальным в каждом возможном случае, то, по крайней мере, всех устраивает как самый простой компромисс. Поэтому мы можем не изобретать велосипед, а сохранить структуру пакета в самом общем ее виде, показанном на рис. 3.1.
(рис 3.1) Структура пакета IPv6 в первом приближении вполне традиционна
Следующим встает такой вопрос: как устроить заголовок, чтобы он был достаточно гибким и расширяемым? Ведь, как хорошо известно, без этих качеств наш протокол долго не протянет. В IPv4 для этой цели существовал механизм опций, который вполне справлялся со своей задачей. Чего в нем не хватало?
В первую очередь, более четкой структуры опций. Вообще говоря, опции IP могут быть двух видов. Опции первого вида представляют интерес для каждого узла по трассе пакета, а опции второго вида важны только для адресата. Первые мы назовем пошаговыми опциями (hop-by-hop options), а вторые — опциями адресата (destination options). Если опции этих видов перемежаются, то маршрутизаторам приходится просматривать их список полностью, чтобы найти в нем все пошаговые опции. Эта лишняя работа замедляет продвижение пакетов. Данную проблему еще можно было бы обойти, запретив источнику размещать пошаговую опцию после опции адресата.
Далее, сегодня есть спрос на сквозную защиту IP. То есть информацию пакета надо защищать непосредственно на уровне IP, а не выше или ниже по стеку; а значит, защитные данные, такие как цифровая подпись, должны находиться в заголовке IP или его расширении.
Здесь под защитой IP мы понимаем аутентификацию, шифрование и гарантию целостности пакетов. В TCP/IP эту задачу решает семейство протоколов IPsec, которого мы кратко коснемся в §3.3.5.
Определение "сквозная" означает, что в такой защите IP участвуют только источник и адресат, а транзитные узлы даже не отличают защищенные пакеты от обычных и обрабатывают их по одним и тем же правилам.
Допустим на минутку, что в нашем распоряжении — только опции IP. Тогда элементы защиты нам придется тоже выполнить в виде опций. Между тем, если в пакете уже есть опции адресата, то их можно было бы зашифровать, чтобы скрыть от посторонних глаз; ведь промежуточным узлам не нужно анализировать их. В этом случае часть заголовка IP окажется открытой, а часть зашифрованной, что неизбежно собьет с толку маршрутизаторы. Чтобы такой подход мог хоть как-то работать, опции адресата должны были бы находиться строго после опций защиты, а все маршрутизаторы обязаны были бы знать о существовании опций защиты: тогда сканирование списка опций маршрутизатор прерывал бы на первой же опции защиты, потому что дальше может идти шифрованная "абракадабра". Однако данный подход в открытую нарушает сквозной характер защиты: она теряет свою прозрачность для транзитных узлов. Кроме того, подобное решение "в лоб" начисто лишено технического изящества.
Поэтому мы не станем ограничивать себя опциями и пересмотрим саму структуру заголовка в пакете IPv6. Пусть этот заголовок будет модульным и состоит из более мелких, простых заголовков, каждый из которых решает ровно одну задачу. Такой составной заголовок схематически показан на рис. 3.2.
(рис 3.2) Составной заголовок пакета IPv6Чтобы число простых заголовков было практически неограниченным, их можно "сцепить" вместе способом, в котором различимы мотивы связанного списка и кодирования TLV. А именно каждый простой заголовок содержит поле "следующий заголовок" (Next Header), в котором закодирован тип заголовка, идущего непосредственно за этим [§4 RFC 2460], как показано на рис. 3.3. Кроме того, заголовки переменной длины содержат поле длины данного заголовка, тогда как длина фиксированных заголовков заранее задана протоколом. Сведения о длине понадобятся, чтобы однозначно определить, где заканчивается текущий заголовок и начинается следующий.
(рис 3.3) Тип простого заголовка IPv6 надо искать в предыдущем заголовке (с.з. = следующий заголовок)
А теперь обратите внимание: в отличие от кодирования TLV, где тип объекта содержится в самом объекте, тип заголовка IPv6 находится в предшествующем заголовке. Как же тогда получатель пакета IPv6 узнает, каков тип самого первого простого заголовка? Ему не придется гадать, если протокол заранее зафиксирует этот тип. Действительно, нам в любом случае потребуется обязательный заголовок, куда мы поместим адреса источника и назначения. Без этого заголовка не обойдется ни один пакет IPv6, так что пускай он и будет самым первым. Именно этот простой заголовок и называют заголовком IPv6 (the IPv6 header), тогда как прочие простые заголовки называют заголовками расширения IPv6 (IPv6 extension headers). Чтобы избежать двусмысленности, мы довольно часто будем уточнять: основной заголовок IPv6, — в противовес заголовкам расширения.
А что содержит поле "следующий заголовок" в самом последнем заголовке? Это поле отлично подходит для типа полезной нагрузки, которая
расположена за последним заголовком. Для этих типов уже существует реестр протоколов Internet
(рис 3.4) Соединение составного заголовка IPv6 с полезной нагрузкой
Отсюда немедленно следует длина поля "следующий заголовок" — 8 битов, — потому что в реестре протоколов IP всего 256 значений. Это совсем немного, так что мы должны ограничить свои аппетиты небольшим числом разных типов простых заголовков.
Мы сознательно сказали "типы простых заголовков", а не "типы заголовков расширения". Основному заголовку IPv6 тоже назначен отдельный код, 41. Это пригодится нам позже, в § 4.2, для туннельной инкапсуляции.
К счастью, на сегодняшний день в реестре протоколов Internet еще достаточно свободных значений: из них занято чуть больше половины.
Как мы увидим в §3.3.5, заголовки защиты IPsec — это всего лишь частный случай заголовков расширения. Но если это так, каким образом IPsec применяют к IPv4? Нетрудно увидеть, что цепочку заголовков расширения вполне можно прицепить и к заголовку IPv4. Ведь реестр протоколов один независимо от версии IP. Другое дело, что большинство заголовков расширения имеет отношение только к IPv6 и лишено смысла в среде IPv4. Заголовки IPsec — важное исключение из этого.
Собрав воедино сказанное в этом разделе, мы получаем структуру пакета IPv6, изображенную на рис. 3.5.
(рис 3.5) Окончательная структура пакета IPv6 (число заголовков расширения переменно)
Итак, каждый пакет IPv6 начинается с заголовка IPv6 (неожиданно, правда?) Но теперь, в отличие от IPv4, все необязательные элементы мы можем перенести из основного заголовка в заголовки расширения. Без каких полей мы абсолютно не сможем обойтись в основном заголовке? Используя наш опыт IPv4, а также не забывая сказанное в §1 и §3.1, составим предварительный неупорядоченный список:
Теперь давайте рассмотрим подробнее эти поля и, если понадобится, скорректируем их свойства.
Целочисленные поля, "созданные" нами по ходу нашей работы, будут по умолчанию в беззнаковом коде. Как вы думаете, почему отрицательные и дробные числа редко встречаются в сетевых протоколах?
Как мы уже обсудили в §1, поле версии IP поможет надежно отличить пакет IPv6 от пакета IPv4 в переходных условиях, пока новая версия будет работать наряду со старой. Учитывая масштабы применения IPv4 и финансовые вложения в технологии на его основе, этот переходный период может растянуться на многие годы. Очевидно, что длина и позиция поля версии обязаны быть такими же, как в заголовке IPv4. То есть длина этого поля — 4 бита, а находится оно в самом начале заголовка (учитывая сетевой порядок битов и байтов). Теперь его новое значение — 6.
Также нам никуда не деться без двух адресов IPv6, источника и назначения. Вместе они занимают 256 бит, или 32 байта, что весьма немало. Не надо ли нам попытаться сжать их двоичное представление путем какого-то кодирования, подобно тому как мы поступили с текстовым представлением? Нет, делать этого ни в коем случае не стоит; иначе ради выигрыша в несколько байтов мы пожертвуем простотой заголовка и затрудним его обработку, особенно на аппаратном уровне. Что мы еще должны сказать об этих адресах? Прежде всего, у пакета ровно один интерфейс источника, и поэтому адрес источника не может быть групповым. Если узел принял пакет с групповым адресом источника, его необходимо отбросить, никак не реагируя на него, потому что любая реакция по групповому адресу может открыть лазейку для атаки типа "отказ в обслуживании" против других узлов. Далее, адрес обратной связи ::1 можно использовать только в трафике через интерфейс типа "петля", будь то адрес источника или назначения, — прийти в пакете извне узла он не имеет права. Прочие требования к адресам в заголовке IPv6 мы сформулируем по ходу работы.
Кстати, именно благодаря тому, что адрес источника в пакете IP не бывает групповым, мы можем говорить о типе всего пакета в зависимости от типа его адреса назначения, как то "индивидуальный пакет" и "групповой пакет".
Без поля TTL или аналогичного ему возникает риск бесконечного зацикливания пакетов в сети. При неудачном стечении обстоятельств это может также сопровождаться дублированием пакетов, что означает экспоненциальный рост паразитного трафика и полный крах сети. Так что без эквивалента TTL нам никак не обойтись. Однако, перенося поле TTL в заголовок IPv6, мы внесем в его свойства долгожданную поправку. Как показал опыт работы с IPv4 [§5.3.1 RFC 1812], измерить время удержания транзитного пакета на узле затруднительно и, к тому же, оно редко превышает одну секунду. Де-факто поле TTL ведет обратный отсчет не секунд жизни пакета, а его шагов по транзитным узлам. Мы не можем не учесть этот опыт в IPv6, и нам остается только переименовать поле сообразно его функции: "предельное число шагов" (Hop Limit). Длина его по-прежнему будет 8 бит. По смыслу это поле не может быть дробным или отрицательным, поэтому оно целое беззнаковое.
По первоначальному замыслу, поле TTL в IPv4 отсчитывало именно время, а не шаги, чтобы заодно управлять интервалами таймеров на узле назначения. Так, в первом эталонном алгоритме сборки пакета IPv4 из фрагментов интервал таймера сборки был функцией от остаточного значения TTL [§3.2 RFC 791]. Однако впоследствии от этого подхода отказались в пользу других методов. В частности, значение тайм-аута сборки стало постоянным [§3.3.2 RFC 1122].
Правила работы с полем "предельное число шагов" [§3 RFC 2460] не отличаются от общепринятой практики IPv4:
конечный адресат пакета игнорирует это поле.
В каких комментариях нуждаются эти простые требования?
Поле длины пакета необходимо на тот случай, если канальный уровень не сохраняет точную длину полезной нагрузки кадра. Хрестоматийный пример — это Ethernet, где длина полезной нагрузки не может быть ниже 46 байт из-за наследия CSMA/CD. Другой возможный пример — это канальный протокол, длина полезной нагрузки в котором должна быть кратна, скажем, восьми байтам.
Подобное ограничение можно встретить в защищенных протоколах с блочным шифрованием, но оно не всегда заметно их потребителям. Например, подсистема шифрования PPP маскирует ее, сохраняя и восстанавливая точную длину полезной нагрузки с помощью особой набивки, в которой закодирована информация о числе добавленных в конец полезной нагрузки байтов [§6.1 RFC 2419]. Располагая такой информацией, приемник PPP может точно "срезать" набивку без вреда для полезной нагрузки.
Трасса пакета заранее неизвестна, так что поле длины потребуется в каждом пакете — ведь никто знает, сквозь какие каналы ему придется пройти. Пусть единицей измерения длины пакета остается байт, а пакет состоит из целого числа байтов; ведь это очень удобно, учитывая ориентацию всей технологии обработки данных на байты.
Хотя исторически байт не всегда был одной и той же длины, и потому возник альтернативный термин "октет", обозначавший именно цепочку из 8 бит, по состоянию на сегодняшний день это полные синонимы. Мы предпочитаем термин "байт", так как он понятен большей аудитории, а "октет" уже звучит старомодно, почти как французские реплики героев Льва Толстого.
При целом беззнаковом кодировании полю длины вполне хватит 16 битов, так как, с одной стороны, существующие канальные протоколы не поддерживают MTU больше $$ \sim2^{16}$$ байт, а с другой стороны, транспортные протоколы TCP и UDP рассчитаны на максимальную длину пакета $$ \sim2^{16}$$ байт. Кроме того, чем длиннее пакет, тем выше вероятность его повреждения при передаче. В этом-то и состоит работа пакетных сетей, чтобы даже огромные порции данных передавать в виде пакетов разумного размера.
А в чем же заключается зависимость транспортных протоколов от максимальной длины пакета? Так, в заголовке UDP есть поле длины, и оно длиной 16 бит. Зависимость TCP не столь очевидна, поскольку TCP не хранит длину сегмента, полагаясь на IP в этом вопросе. Единственное ее проявление в заголовке TCP — это неотложный указатель (urgent pointer) длиной 16 бит, так что неотложные данные не смогут простираться далее $$ \sim2^{16}$$ байт от начала сегмента. Однако сама длина сегмента не может превышать 65535 байт ввиду 16 битного поля MSS в одноименной опции.
Тем не менее, RFC 2675 предлагает опцию IPv6 "пакет-слон" (jumbogram), расширяющую поле длины до 32 бит. Тот же RFC обсуждает препятствия на пути к применению этой опции совместно с TCP и UDP. Эталонная реализация IPv6 KAME на всякий случай поддерживает опцию "пакет-слон".
Опций в пакете вполне может не быть, так что мы их отправим в необязательные заголовки расширения. В результате число и длина полей в заголовке IPv6 оказываются строго постоянными.
Мы уже заняли совершенно необходимыми полями 36,5 байта. Чтобы помочь современным вычислительным системам, давайте выровняем конец заголовка IPv6 на границу 32 битного слова. Мы не можем выбросить из заголовка даже полбайта, так что придется нарастить заголовок до 40 байт. Тогда конец заголовка IPv6 окажется выровнен на границу 64 битного слова, от чего особенно выиграют 64-разрядные платформы. После выравнивания у нас образуются незанятые три с половиной байта.
Давайте уступим моде на управление качеством обслуживания (QoS) и отдадим свободные разряды полям "класс трафика" (8 бит) и "метка потока" (20 бит). Первое из них аналогично полю TOS в IPv4, а второе позволяет, пометив последовательность пакетов одним и тем же значением, превратить их в единый объект политики обслуживания (поток). Уточнять подробности применения этих полей мы сейчас не будем.
Как мы помним, сегодня общепринятая интерпретация поля TOS основана на DiffServ [RFC 2474] и ECN [RFC 3168]. То же самое касается и поля "класс трафика".
Общие правила работы с полем "метка потока" изложены в [RFC 6437]. Одно из перспективных направлений его возможного применения — это оптимизация распределения нагрузки между каналами [RFC 6438] и серверами [draft-carpenter-v6ops-label-balance].
Мы вовсе не хотели сказать, что управление качеством обслуживания в сети — это фикция или шарлатанство. Однако нельзя не признать, что слишком часто за него берутся именно из-за моды, без должного понимания его причин, законов и механизмов. По иронии, противники QoS зачастую столь же невинны, как и их оппоненты.
И, наконец, последний штрих: раз длина заголовка IPv6 всегда составляет 40 байт, то стоит ли включать эту величину в значение поля длины пакета? Пусть она входит туда неявно, и тогда максимальный пакет сможет стать длиннее на 40 байт. Так что давайте переименуем это поле в "длину полезной нагрузки" (Payload Length). В данном контексте "полезная нагрузка" — это пакет за вычетом основного заголовка IPv6; заголовки расширения входят в "полезную нагрузку". Максимальная длина такой "полезной нагрузки" — 65535 байт, а всего пакета — на 40 байт больше, то есть 65575 байт. Это ограничение чисто теоретическое, оно следует из того, как мы кодируем поле длины; об ограничениях практического характера мы поговорим в §6.5.
Сканируя заголовки, реализация IPv6 обязана следить, чтобы их цепочка не вышла за границы полезной нагрузки. Если байты полезной нагрузки уже исчерпаны, а заголовки еще не закончились, то пакет, очевидно, поврежден.
Теперь давайте составим полный, но еще неупорядоченный список полей в заголовке IPv6:
В каком порядке их лучше всего разместить? В современных вычислительных системах надлежащее выравнивание данных значительно повышает скорость доступа к ним, так что начать анализ нам надо с самых востребованных полей, а именно с адресов. В идеале, их надо было бы выровнять на границу 128 битного слова; однако тогда нам пришлось бы поместить их в самое начало заголовка, а эта позиция уже прочно занята коротеньким полем "версия IP". Значит, нам придется довольствоваться границей 64 битного слова, проходящей в заголовке по смещению 8 байт; то есть адреса отправляются в конец заголовка. Далее, поле "длина полезной нагрузки" следует выровнять на границу 16 битного слова. Для этого достаточно поместить между ним и полем "версия IP" поля качества обслуживания. Наконец, в зазор между полем "длина полезной нагрузки" и адресами умещаются поля "следующий заголовок" и "предельное число шагов". Вот мы и получили полный формат заголовка IPv6 [§3 RFC 2460], показанный на рис. 3.6.
(рис 3.6) Основной заголовок IPv6
Какие поля, знакомые нам по IPv4, не вошли в заголовок IPv6? Во-первых, больше не нужна длина заголовка: она стала постоянной величиной 40 байт. Заголовок IPv4 был переменной длины, так как включал в себя опции; теперь же опции IPv6 займут отдельный заголовок расширения (или даже несколько таких заголовков — см. §3.3.2 и §3.3.6).
Во-вторых, исчезли все поля, относящиеся к фрагментации: идентификатор, смещение, флаги. Так как большинство пакетов при оптимальной работе сети не фрагментировано, эти поля стоит вынести в собственный заголовок расширения. О факте фрагментации будет говорить само наличие такого заголовка. А как же флаг DF, запрещающий фрагментацию? Вопрос резонный; о судьбе флага DF мы скажем в §3.3.4 и §6.5.
В-третьих, совсем пропала контрольная сумма заголовка. Это решение вызвано несколькими причинам. Прежде всего, модульная структура заголовков в пакете IPv6 размывает границу между их уровнями: если конечный адресат пакета еще может разобрать все заголовки сетевого уровня и определить, где начинаются данные следующего уровня, например, TCP, то транзитный узел не может и не должен делать этого. Тогда контрольная сумма уже не может защищать все заголовки сетевого уровня, и область ее покрытия придется ограничить только основным заголовком IPv6. Так само ее существование в значительной мере теряет смысл. Затем, современные канальные технологии сами обеспечивают пошаговую защиту пакетов от повреждения, а протоколы транспортного уровня защищают важнейшие поля заголовка IP сквозным образом. Благодаря этому, собственная роль контрольной суммы IP не так уж велика. И, наконец, отказ от контрольной суммы IP ускоряет аппаратное продвижение пакетов и делает проще соответствующие интегральные схемы.
Пусть читатель изобразит форматы заголовков IPv4 и IPv6, а затем раскрасит их поля в зависимости от того, сохранилось ли поле при смене версии IP, исчезло или возникло. Еще одним упражнением будет выяснить, какие поля заголовка окажутся повреждены, если модуль маршрутизатора "забудет" проверить поле "версия" и обработает пакет IPv6, как если бы это был пакет IPv4. Рассмотрите два конкретных сценария: а) декремент поля TTL перед продвижением пакета; б) перезапись значения DSCP правилом QoS. (Не забудьте, что в обоих случаях также произойдет пересчет контрольной суммы заголовка.) Затем определите, какие поля заголовка будут повреждены, если пакет IPv4 будет по ошибке обработан согласно формату заголовка IPv6. При этом ограничьтесь одним сценарием: декремент поля "предельное число шагов".
Представим себе пакет IPv6, который состоит из одного основного заголовка. Хотя нам не совсем ясно, зачем такой пакет мог бы понадобиться, формального повода запретить его у нас тоже нет.
Обсуждая заголовки IPsec в §3.3.5, мы найдем применение даже такому вот "пустому" пакету: обеспечение конфиденциальности на уровне потока пакетов.
Однако само существование пакета без полезной нагрузки поднимает любопытный вопрос: чему в нем должно быть равно значение поля "следующий заголовок"? Мы могли бы сказать, что оно неважно, потому что длина полезной нагрузки равна нулю; но зачем нам лишняя неоднозначность? Чтобы не провоцировать разночтения стандарта и несовместимость реализаций, давайте назначим этому случаю вполне определенный код из реестра протоколов IP. Это код 59, "следующего заголовка нет" (No Next Header) [§4.7 RFC 2460]. Таким кодом можно завершить и цепочку заголовков расширения, если за ней в пакете ничего нет, как это показано на рис. 3.7.
(рис 3.7) Пакет без полезной нагрузки
Теперь представим себе, что получатель сканирует пакет и обнаруживает заголовок со значением 59 в поле "следующий заголовок", а затем еще какие-то байты (об этом скажет длина полезной нагрузки IPv6). Хотя такая ситуация неоднозначна, в ней нет явного криминала: она не ведет к осложнениям или опасным последствиям, если обработать ее правильно. А принцип Постела подсказывает, как именно следует поступить: пусть транзитные узлы сохраняют эти концевые байты, а конечный адресат игнорирует их [§4.7 RFC 2460].
Напомним себе современную трактовку принципа Постела (Postel’s principle), также известного как принцип устойчивости (robustness principle). Основоположник Internet Джонатан Постел (Jonathan Postel) сформулировал этот принцип одной лаконичной фразой: "Будь консервативен в собственных действиях и либерален к тому, что принимаешь от других" [§2.10 RFC 793]. Сегодня мы добавим к этому: "…но никогда не забывай о безопасности". Этот принцип требовательности к себе и терпимости к другим — один из главных в TCP/IP. Если сторона протокола следует ему, она не только соблюдает протокол, но также приобретает гибкость и устойчивость к неполадкам, а эти качества особенно важны, когда разработчики протокола не могут заранее предусмотреть все варианты развития событий.
Мы предварительно отметили в §3.1, что опции можно поделить на пошаговые и актуальные только для адресата. Требования к обработке этих видов опций существенно разнятся. Во-первых, маршрутизаторы в целях производительности должны сканировать только пошаговые опции. Во-вторых, когда у IP появятся собственные механизмы сквозной защиты, опции адресата можно будет шифровать. В-третьих, при фрагментации пакета пошаговые опции надо целиком копировать в каждый фрагмент, тогда как опции адресата достаточно привести один раз и даже можно разбить на части, если они окажутся слишком длинными и пересекут границы одного фрагмента.
Пока что мы лишь излагаем общие соображения, а точные правила фрагментации заголовков мы сформулируем, когда будем готовы к этому, в §3.3.4.
По всей видимости, каждый вид опций заслуживает собственного заголовка расширения. Форматы этих заголовков могут полностью совпадать, но им будут отвечать разные коды "следующий заголовок". Так, заголовок опций адресата получает код 60 [§4.6 RFC 2460].
Заголовок пошаговых опций, ввиду своей особой роли, получает отличительный код 0 [§4.3 RFC 2460] и позицию непосредственно после заголовка IPv6. Иными словами, код 0 может встретиться только в поле "следующий заголовок" основного заголовка IPv6. Если же пакет составлен правильно, но первое поле "следующий заголовок" ненулевое, то в пакете пошаговых опций нет. С помощью этой простой логики маршрутизаторы могут быстро обнаруживать пошаговые опции в транзитных пакетах.
Пусть по своему формату заголовки опций будут контейнерами, в которые можно вложить больше одной опции. Если для отдельных опций мы применим кодирование TLV, то в фиксированной части заголовка нам потребуются только поля "следующий заголовок" и "длина" — ведь число опций может быть переменным.
Будь у нас такое желание, мы могли бы обойтись без поля длины за счет подходящего кодирования опций. Но благодаря полю длины мы сможем просто и однозначно указать конец списка опций, так что давайте не будем создавать себе трудности. Кроме того, поле длины позволяет "перескочить" через заголовок опций, не сканируя его.
Поле "следующий заголовок" занимает один байт. Пусть поле "длина" тоже займет один байт, и тогда действительное начало опций будет выровнено на границу 16 битного слова. Код обоих полей будет целый беззнаковый. Но не слишком ли мало 255 байт для всего списка опций? Удлинить его можно, если измерять длину не в байтах, а в больших единицах. Заодно так мы выровняем конец заголовка опций. Конец заголовка IPv6 выровнен на границу 64 битного слова, и большего выравнивания мы уже не достигнем. Поэтому пусть длина заголовка опций измеряется в 8 байтных единицах. Еще заметим, что длина такого заголовка не меньше единицы из-за обязательных полей. Эту единицу можно учесть неявно и выиграть еще 8 байт. То есть поле "длина" заголовка опций ведет учет 8 байтных отрезков, начиная со второго. Тогда длина всего заголовка в байтах может меняться от 8 до 2048 с шагом 8 байт. Окончательный формат заголовка опций показан на рис. 3.8.
(рис 3.8) Заголовок опций IPv6
Сами опции мы станем кодировать широко известным способом TLV: поле типа, поле длины, данные, — как показано на рис. 3.9. Поля типа и длины данных занимают по одному байту. Длина данных измеряется в байтах и, по традиции IPv6, не включает фиксированные поля типа и длины.
(рис 3.9) Общий вид опции IPv6
Когда получатель пакета сканирует список опций, то он вполне может встретить незнакомую опцию. Благодаря одинаковому кодированию TLV, через опцию-незнакомку легко "перескочить"; но где гарантия, что ее можно безопасно игнорировать? Чтобы избежать этой неоднозначности, давайте сразу же закодируем требуемое поведение в старших двух битах типа опции, как показано в рис. 3.1.
| Биты | Предписания |
|---|---|
| 00 | "Перескочить" через незнакомую опцию и продолжить обработку заголовка |
| 01 | Уничтожить пакет; не высылать никаких извещений |
| 10 | Уничтожить пакет; выслать извещение "ICMP версии 6" (независимо от адреса назначения исходного пакета) |
| 11 | Уничтожить пакет; выслать извещение "ICMP версии 6", только если адрес назначения исходного пакета не групповой |
Еще одна трудность возникнет, когда на уровне IP появится сквозной механизм защиты пакетов от умышленной модификации. Сможет ли он защищать опции? Если транзитные узлы модифицируют защищенную опцию, то конечному адресату пакет покажется подложным. Напротив, если опция по своему определению не изменяется в пути, то она вполне подлежит защите. Отражает это свойство опции третий по старшинству бит ее типа: если он сброшен, то опция заведомо не меняется по пути, а если установлен, то может меняться. Теперь механизму защиты не нужно знать свойства отдельных опций — ему достаточно проверить этот бит в поле типа, чтобы узнать, можно ли защищать данную опцию.
Например, реакция механизма защиты IPsec AH [RFC 4302] выглядит так: если данный бит установлен, то механизм подставляет вместо данных опции нулевые байты, когда вычисляет для пакета его защитную цепочку. Таким образом, под защитой AH оказываются хотя бы код и значение длины, а также фактическая длина опции. Предполагается, что длина опции не меняется, а алгоритм защиты чувствителен к числу нулевых байтов, идущих подряд.
(рис 3.10) Кодирование свойств опции в ее типе
В трех старших битах типа (см. рис. 3.10) закодированы неотъемлемые свойства каждой опции, и посему эти биты нельзя рассматривать
как отдельное поле. Когда новой опции назначают численное значение типа, то в нем сразу же учитывают ее свойства. Например, если
мы увидим опцию с десятичным кодом 95, то
Если же мы, к примеру, инвертируем в этом коде бит "меняется по пути", то получим совершенно другой тип опции, никак не связанный с первоначальным.
Вычислите десятичное значение кода новой опции. (127)
О поле типа опции осталось сказать, что пошаговые опции и опции адресата хранятся в пакете раздельно, но получают численные коды
из одного и того же
Суммарная длина списка опций может быть любой с точностью до байта, тогда как заголовок опций должен занимать число байт, кратное восьми, и оставляет байт на, собственно, опции. Кроме того, для некоторых опций может быть желательно то или иное выравнивание относительно начала заголовка. Чтобы удовлетворить эти требования, давайте "создадим" две опции-пустышки, обязательных к реализации и опознанию.
Первая из них, Pad1, в виде исключения займет ровно один байт, к чему и сводится вся ее функция (см. рис. 3.11). Формально тип опции Pad1 — 0, но она нарушает формат TLV: в ней нет полей, кроме поля типа.
(рис 3.11) Опция Pad1
Вторая же опция, PadN, вполне отвечает формату TLV (см. рис. 3.12). Ее тип — 1, и она может занять от 2 до 257 байт. Если ее поле "длина" больше нуля, то пусть поле данных ради единообразия будет заполнено нулевыми байтами.
(рис 3.12) Опция PadN
Согласно своей роли, опции Pad1 и PadN могут встречаться в заголовке опций несколько раз и располагаться в начале заголовка, в его конце, или же перемежать другие опции. Они могут возникать и среди пошаговых опций, и среди опций адресата.
Проанализируйте свойства опций Pad1 и PadN, закодированные в старших битах их типов.
Все остальные опции IPv6 — суть расширения базового протокола; их технические условия приведены в отдельных документах RFC. Поэтому сейчас мы их рассматривать не будем, но надеемся, что теперь читатель сможет разобраться в них самостоятельно, когда ему это понадобится.
В §6.4 нам встретится опция "сигнал маршрутизатору".
Важное следствие сквозной модели, принятой в Internet, состоит в том, что конечные узлы явным образом не участвуют в маршрутизации пакетов. Источник создает пакет IP и отправляет его в сеть, а та уже сама выбирает подходящую трассу, чтобы пакет дошел до адресата. Конечно, инженер понимает, что простирающаяся между конечными узлами сеть состоит из каналов и маршрутизаторов, работающих по вполне определенным правилам, однако узлу-источнику это неинтересно; все, что от него требуется, — это создать пакет и выбрать для него первый шаг. В этой модели источник управляет дальнейшей маршрутизацией пакета только неявно, устанавливая в нем адрес назначения.
В общем случае маршрутизаторы IP могут рассматривать и другие поля заголовка и параметры пакета, например, класс обслуживания или полную длину. Это естественное обобщение простого алгоритма маршрутизации, ответ которого (следующий шаг пакета) зависит только от адреса назначения. Такую обобщенную маршрутизацию часто называют маршрутизация согласно политике (policy routing). Тем не менее, и в этом случае источник пакета влияет на его трассу только неявно.
Тем не менее, еще давным-давно разработчики IPv4 подумали: а вдруг источник иногда бывает осведомлен лучше сети? Например, из-за сбоя или атаки протокол маршрутизации распространил ложные сведения, и маршрутизаторы направляют пакеты не туда, куда надо. Тогда источник мог бы подсказать им верную трассу для своих пакетов, включив эту информацию в заголовок IP. Так появились опции IPv4 LSRR (нестрогая маршрутизация от источника) и SSRR (строгая маршрутизация от источника) [§3.1 RFC 791].
Если обратиться к
Как мы помним, суть работы SRR была довольно простой. Допустим, источник И хотел направить пакет конечному адресату К через транзитные узлы T1, Т2, Т3,…ТN. Тогда он составлял заголовок пакета так:
устанавливал указатель опции SRR так, чтобы тот указывал на первый адрес в списке, то есть Т2.
В результате узел Т1 значился адресатом пакета и поэтому получал его первым. Он поступал с заголовком так:
Далее пакет приходил узлу Т2, и тот действовал аналогичным образом… И так далее, пока пакет не добирался наконец до узла К. Разница между строгим и нестрогим режимами состояла в том, мог ли пакет посещать транзитные узлы вне заданного источником списка (в нестрогом мог, а в строгом не мог).
Сегодня мы спросили бы разработчиков этого механизма: а почему сеть должна настолько доверять источнику? Действительно, злонамеренный источник вполне может использовать опции SRR, по меньшей мере, для двух типов атак:
Пусть читатель сам вычислит максимально достижимый коэффициент усиления атаки $$N_{max}$$ в IPv4.
Решение: Максимальная длина заголовка IPv4 — 60 байт (4-битное поле, 4-байтная единица длины). Из них 20 байт занимает обязательная часть, так что на опции остается самое большее 40 байт. Заголовок LSRR занимает 3 байта, но наименьшее допустимое значение смещения — 4 байта [§3.1 RFC 791], поэтому список адресов занимает до 36 байт, а это означает 9 адресов IPv4. Еще один адрес — это адрес назначения в основном заголовке. Так что каждый из адресов А и Б может встретиться самое большее 5 раз. Это и будет искомое значение $$N_{max}$$
Поэтому маршрутизация от источника в глобальной сети приносит больше вреда, чем пользы, и было бы неразумно повторить в IPv6 ошибки IPv4. Из этого затруднения мы выкрутимся вот как: давайте в самом общем виде зададим гибкий формат заголовка, управляющего маршрутизацией пакета IPv6, а всю конкретику оставим на усмотрение дополнительных протоколов. Тогда, в случае чего, нас винить будет не в чем.
Искомый заголовок расширения IPv6 мы назовем маршрутный заголовок (routing header) [§4.4 RFC 2460]. Ему отвечает значение "следующий заголовок" 43 — разумеется, в предшествующем ему заголовке, а не в нем самом.
Так как длина маршрутной информации заранее неизвестна, маршрутный заголовок будет переменной длины. Значит, помимо обязательного поля "следующий заголовок", маршрутному заголовку потребуется поле длины. По аналогии с заголовками опций из §3.3.2, пусть это поле длиной один байт хранит длину в 8-байтных словах, причем первое слово не учитывается, так как оно всегда присутствует и частично занято фиксированной частью заголовка.
Далее мы поместим поле маршрутного типа, которое и определит, как надо трактовать дальнейшие данные в этом заголовке. Ему будет
вполне достаточно одного байта. Конечно же, для общепринятых значений этого поля понадобится
Как быть получателю пакета, если он встретил неизвестный маршрутный тип? Придется ли ему безусловно отбросить этот пакет? Ответить на этот вопрос нам поможет опыт маршрутизации от источника IPv4. (Да, учиться можно и на ошибках!) Когда список транзитных узлов в опции SRR исчерпан и пакет направляется к конечному адресату К, опция SRR отработала и уже не имеет значения для маршрутизации пакета. В этом случае опцию SRR можно игнорировать.
Чтобы перенести эту схему в маршрутный заголовок IPv6, нам придется принять определенную модель его данных. Пусть они состоят из последовательности сегментов, где каждый сегмент может быть, например, адресом транзитного узла. Как обозначить в этой модели, что все сегменты уже обработаны и дальнейшие получатели пакета могут игнорировать маршрутный заголовок, независимо от его типа? Вот одно из возможных решений: хранить в фиксированной части маршрутного заголовка число еще необработанных сегментов. Когда значение этого поля упадет до нуля, получатель пакета надежно определит, что маршрутный заголовок уже отработал свое, даже если получатель не знаком с этим маршрутным типом и не знает, как устроен его сегмент. Если же это поле ненулевое, то пакет с незнакомым типом маршрутного заголовка придется отбросить.
В результате мы приходим к формату маршрутного заголовка, изображенному на рис. 3.13.
(рис 3.13) Маршрутный заголовок
Из уже существующих маршрутных типов мы упомянем типы 0 (т.н. RH0 — Routing Header 0) и 2 (RH2). RH0 — это прямой аналог LSRR. Неудивительно, что в конце концов его упразднили ради безопасности [RFC 5095]. Об RH2 см. примечание в конце раздела.
Одно отличие RH0 от LSRR состояло в том, что в текущий сегмент RH0 переносился предыдущий адрес назначения из основного заголовка IPv6, а не адрес выходного интерфейса. Таким образом, в RH0 достаточно было поменять местами адрес назначения и адрес из текущего сегмента, а затем провести декремент счетчика "осталось сегментов". Как следствие, в RH0 происходит простой циклический сдвиг списка адресов, тогда как опция LSRR записывала фактические интерфейсы, в которые продвигался пакет. Точно так же поступала опция SSRR, а нужно это было именно для нее, чтобы потом можно было послать ответный пакет с записанным строгим маршрутом от источника.
При должной политике безопасности на границах опорной сети, запрещающей проникновение сомнительных RH0 извне, внутри такой
сети RH0 мог бы стать ценным инструментом для обнаружения скрытых дефектов. Ведь с его помощью можно было бы из одной точки
проверять разные пути сквозь сеть, включая неактивные в данный момент, запасные. Корреляция результатов подобных проверок
вдоль множества путей позволяет довольно точно определить, где локализован
Существование маршрутного заголовка IPv6 заставляет нас пересмотреть такое понятие, как адресат пакета. До сих пор мы полагали, что у пакета ровно один адресат, хотя он может быть индивидуальным узлом или группой. Теперь же нам придется различать два типа адресатов. Если в пакете вообще нет маршрутного заголовка или же он есть, но его поле "осталось сегментов" равно нулю, то в основном заголовке IPv6 записан конечный адресат пакета. Это узел, который поглотит пакет и обработает его полезную нагрузку. Если же в маршрутном заголовке все еще есть активные сегменты, то в основном заголовке IPv6 значится текущий адресат пакета. Этот узел примет пакет, только чтобы сменить в нем адрес назначения и передать дальше; он будет транзитным на пути пакета. При этом конечный адрес назначения пакета скрыт в сегменте маршрутного заголовка.
Вообще говоря, ни один из адресов назначения в пакете с маршрутным заголовком не должен быть групповым. Это следует из соображений безопасности.
Нужны ли дополнительные правила, если у адреса назначения в маршрутном заголовке область действия не глобальная? На самом деле, нет, достаточно уже знакомого нам принципа изоляции зон из §2.4. Однако, строго говоря, этот принцип должен выполняться — а значит, и проверяться — для всех адресов назначения одновременно. В противном случае возможна атака засылкой пакета в удаленную зону. Например, если текущий адресат пакета глобальный, а конечный адресат внутриканальный, то произойдет засылка внутриканального пакета в канал, не подключенный непосредственно к источнику пакета.
Нормативный документ [§9 RFC 4007] требует, чтобы следующий адрес назначения из маршрутного заголовка имел область не меньшей величины, чем у текущего адреса назначения в основном заголовке IPv6. Пока принцип изоляции зон выполняется и все интерфейсы назначения пакета находятся в зоне его адреса источника, это требование избыточно. К примеру, вполне возможна "эстафета" по адресам, область которых сужается, если все они назначены интерфейсам в тот же канал, где находится интерфейс-источник.
Маршрутизация от источника обрела новую жизнь в MPLS-TE, где вполне можно задать трассу LSP по узлам, перечислив их адреса в ERO. Конечно, этот механизм выгодно отличается от RH0 в IPv6 в плане безопасности, так как пограничный узел (LER), устанавливающий LSP, — заведомо доверенный, в отличие от абстрактного источника IPv6. Однако на поверку MPLS не столь уж независим от IP: не имея собственной адресации, он опирается в этом на традиционный протокол сетевого уровня, и обычно это IP. Поэтому, если узлам MPLS назначены зонные адреса IPv6, то ERO подчиняется все тому же принципу изоляции зон! А именно входной LER должен иметь возможность адресовать каждый узел по трассе LSP, иначе ERO будет лишен смысла. В частности, именно поэтому нельзя проложить трассу LSP через несколько каналов, используя только внутриканальные адреса: для составления ERO потребуются адреса большей области, по крайней мере, начиная со второго узла вдоль трассы. Это в высшей степени любопытно, что наши разработки для маршрутизации от источника IPv6 также применимы к MPLS!
Как мы знаем, узел может адресовать пакеты IP самому себе. Точно так же, текущий и конечный адреса назначения могут принадлежать одному и тому же узлу. Но зачем это нужно? Например, чтобы текущий адрес назначения обладал свойствами локатора, а конечный — идентификатора. Это полезно, если узел мобильный и перемещается по Internet. Текущий адрес мобильного узла M в каждый момент времени доступен обычным порядком, с помощью маршрутизации, и может меняться, когда узел меняет свое местоположение. В то же время, его постоянный адрес D, с которым работают вышестоящие протоколы, доступен только посредством маршрутного заголовка. Тогда перемещения узла будут прозрачны для протоколов уровня выше IP, как показано на рис. 3.14. Очевидно, что такой маршрутный заголовок может содержать ровно один сегмент, причем адрес в нем обязан совпасть с постоянным адресом текущего адресата D. В противном случае налицо сбой или нарушение безопасности. Даная разновидность маршрутного заголовка получила маршрутный код 2 и потому известна как RH2. Подробнее с ней можно познакомиться в RFC 3775, посвященном мобильности узлов IPv6.
(рис 3.14) Принцип работы RH2
Как мы помним, в IPv4 модуль фрагментации был обязательным компонентом стека и в хостах, и в маршрутизаторах, потому что фрагментировать длинный пакет должен был узел, стоящий непосредственно на входе в канал с низким MTU. Это усложняло логику маршрутизаторов IPv4: они не могли рассматривать транзитный пакет как элементарную единицу обмена данными, которую надо или целиком продвинуть дальше, или отбросить. Кроме того, нагрузить маршрутизаторы фрагментацией было не самой лучшей мыслью, потому что они обрабатывают трафик многих хостов, а сами хосты обслуживают только собственные нужды (по крайней мере, на сетевом уровне стека).
Вспоминается пословица: "На одного с сошкой семеро с ложкой".
Теперь, когда у нас есть приятная возможность перевернуть все вверх дном, мы можем "примерить" на каркас IPv6 альтернативные правила фрагментации:
Так вся работа по фрагментации ложится на источники пакетов, что не только справедливо, но и выгодно с точки зрения производительности сети.
Но не стоит сомневаться, что новый подход к фрагментации создаст дополнительные проблемы и поставит неожиданные вопросы на уровне работы протокола. Что ж, от этого никуда не уйти, такова цена прогресса. Нам остается только проанализировать возникшие затруднения и найти решения для них.
Если длина пакета L (цельного или фрагмента) превращается в сквозную характеристику
(1)$$ L\leq PMTU $$
Здесь PMTU — MTU трассы от данного источника до конечного адресата пакета:
(2)$$PMTU=\min_{i} MTU_i$$
где индекс i пробегает все выходные интерфейсы этой трассы.
В новых условиях сквозную величину L задает источник пакета, так что выполнение условия (1) становится его личной заботой, а это связано с поиском значения PMTU, или хотя бы его оценки снизу $$\underline{PMTU}$$:
(3)$$L\leq \underline{PMTU}\leq PMTU \Rightarrow L\leq PMTU$$.
Простые арифметические соображения подсказывают нам, что есть две стратегии выполнения условия (3). Первая из них состоит в том, чтобы источник приложил усилия и нашел хорошее приближение PMTU, вплоть до точного значения. Для этого существует процедура PMTUD. Чтобы она работала, источник должен получать извещения, когда его пакеты уничтожаются из-за чрезмерной длины. В IPv4 эту роль выполняли сообщения ICMP "пакет слишком велик"; подобный механизм нам понадобится и в IPv6. Сразу же заметим, что PMTUD сходится быстрее, если каждое извещение подсказывает очередную величину )$$MTU_i $$: узел, высылающий это извещение, включает в него значение MTU того интерфейса, куда вышел бы пакет, если бы он не оказался слишком длинным. В IPv4 это было необязательным расширением [§4 RFC 1191], но теперь нам стоит ввести это в стандарт — отметим себе на будущее (§4.3). Далее, PMTUD предполагает, что протокол более высокого уровня устойчив или безразличен к потере пакетов. Это условие не ново, потому что пакеты в сетях TCP/IP теряются по самым разным причинам. Наконец, PMTUD работает надежно только при том условии, что хотя бы часть извещений доходит до источника, или же что вышестоящий протокол предполагает отклик удаленной стороны. В противном случае возникает "черная дыра PMTUD", куда бесследно, с точки зрения источника, пропадают длинные пакеты.
Для образования классической "черной дыры PMTUD" достаточно блокировать извещения ICMP, однако для двунаправленного протокола не все еще потеряно. Отклик удаленной стороны (точнее, его неожиданное отсутствие) позволяет обнаружить и обойти "черную дыру", уменьшив длину пакета. Если же вышестоящий протокол однонаправленный, такой как Syslog или Netflow, то "черная дыра" станет поглощать пакеты до тех пор, пока извещения ICMP не будут разблокированы.
Вторая стратегия значительно проще и потому подходит для источников, которые не хотят или не могут обременять себя процедурой PMTUD. Она заключается в том, чтобы использовать минимальное значение MTU, указанное протоколом IP, как "габаритные ворота" для исходящих пакетов и их фрагментов:
(4)$$ L\leq MTU_{min} $$ (4)
Этот трюк работает, потому что минимальное значение MTU служит оценкой снизу для MTU любой трассы, пока не нарушен протокол:
(5)$$\forall i, MTU_{min}\leq MTU_i \Rightarrow MTU_{min}\leq PMTU$$. (5)
Чтобы при этом не страдала эффективность передачи данных, минимальное значение MTU должно быть разумно большим. Как мы помним, в IPv4 это была микроскопическая величина 68 байт. Теперь же, в IPv6, MTU канала не может опуститься ниже 1280 байт [§5 RFC 2460].
Мы точно не знаем, почему была выбрана именно величина 1280 байт. Из ее очевидных численных свойств можно заметить разве что такое: $$1280=2^10+2^8$$. Но практически важное свойство данного выбора состоит в том, что он оставляет весьма приличный допуск на многоуровневую инкапсуляцию, например, туннели, при распространенном значении MTU физического канала 1500 байт.
А как нам быть с каналом, который по своему устройству неспособен переместить пакет длиной 1280 байт? (Например, см. [RFC 4919]. ) Пусть расширение эффективного MTU такого канала до требуемой величины, например, за счет фрагментации кадров, будет задачей нижележащего уровня. Обычно это часть протокола канальной инкапсуляции (пример см. в [RFC 4944]).
Выше, в §3.2, мы спросили себя, куда из заголовка IPv6 потерялся флаг DF. Теперь мы видим, что необходимость в нем отпала. Можно сказать, что флаг DF неявно установлен в каждом пакете IPv6, потому что транзитные узлы фрагментировать его не могут.
Остальные поля, управляющие сборкой фрагментов, мы решили переместить в отдельный заголовок расширения. Он называется заголовок фрагмента (fragment header). Его код в реестре протоколов IP — 44. Если в пакете IPv6 этот заголовок есть, то перед нами фрагмент, а если нет, то цельный пакет.
Так как собирает пакет только его конечный адресат, каждый фрагмент должны сопровождать служебные сведения, представляющие интерес для транзитных узлов. Это необходимо, потому что каждый фрагмент — это тоже пакет IPv6, путешествующий сквозь сеть независимо от других фрагментов.
Какие это будут сведения? В первую очередь, это основной заголовок IPv6, так как он содержит важнейшие сведения о пакете. Далее, пошаговые опции по самому их смыслу также интересны всем узлам на пути фрагмента. Маршрутный заголовок, если он есть, заведомо нужен промежуточным адресатам, хотя маршрутизаторы его не рассматривают. Наконец, об опциях адресата мы заранее не можем сказать, будут ли они нужны промежуточным адресатам. Возможно, появятся опции двух видов: первые будут интересны всем адресатам подряд, а вторые — только последнему из них, то есть конечному. Тогда будет достаточно снабдить каждый фрагмент опциями первого типа, заключив их в отдельный заголовок расширения "опции адресата". Остальные заголовки и полезная нагрузка нужны только конечному адресату, так что их вполне можно разбить между фрагментами.
Обратите внимание, что конечному адресату интересны все заголовки расширения. Опции для всех адресатов и опции для конечного адресата будут заключены в заголовки расширения одного и того же типа, "опции адресата" (код 60). Отличие будет только в том, как они размещены относительно других заголовков. А именно первые должны предшествовать заголовку фрагмента, а последние — идти за ним.
Мы питаем слабость к обобщениям, так что давайте сначала опишем эту картину в целом, а затем уже перейдем к ее деталям. Итак, исходный пакет IPv6 состоит из двух частей, нефрагментируемой (unfragmentable) и фрагментируемой (fragmentable), как показано на рис. 3.15. Мы уже выяснили, что нефрагментируемая часть состоит из таких заголовков:
Все прочие заголовки расширения, если они есть в данном пакете, а также полезная нагрузка составляют его фрагментируемую часть.
(рис 3.15) Структура пакета IPv6 с точки зрения его фрагментации
Пусть читатель подумает, где и в каком виде существует исходный пакет, если по сети передаются только его фрагменты. (Как образ в памяти источника и конечного адресата.)
Первым делом в процессе фрагментации вторую часть делят на фрагменты ненулевой длины, так чтобы окончательная длина каждого фрагмента после инкапсуляции не превысила текущей оценки PMTU, — эта операция схематически представлена на рис. 3.16. Но как связать длины фрагмента до и после инкапсуляции? Ответ на этот вопрос мы получим, рассмотрев следующий шаг процедуры фрагментации.
(рис 3.16) Деление пакета IPv6 на фрагменты (с.з. = следующий заголовок)
Следующим шагом каждый фрагмент превращают в полноценный пакет IPv6. Для этого к нему спереди добавляют заголовок фрагмента, а перед ним — полную копию нефрагментируемой части, как изображено на рис. 3.17. Чтобы такой пакет стал годным, остается скорректировать пару полей в нем, а именно поле "длина полезной нагрузки" в заголовке IPv6 и поле "следующий заголовок" в заголовке перед заголовком фрагмента. Первая корректировка нужна, так как поле "длина полезной нагрузки" всегда относится к текущему пакету, даже если это фрагмент. Второй корректировкой мы учитываем тот факт, что цепочка заголовков изменилась и теперь после нефрагментируемой части идет заголовок фрагмента.
(рис 3.17) Фрагмент после инкапсуляции (с.з. = следующий заголовок)
В других стеках встречаются протоколы сетевого уровня, заголовки которых отдельно хранят длины данного фрагмента и исходного пакета. Пример такого протокола — ISO CLNP.
Теперь мы видим, что первоначальная длина фрагмента до инкапсуляции должна быть не больше, чем текущая оценка PMTU за вычетом длин нефрагментируемой части и заголовка фрагмента. Насколько сложно будет провести это вычисление заранее, до начала фрагментации? Длина нефрагментируемой части зависит только от структуры исходного пакета, она не изменяется от фрагмента к фрагменту. Что касается заголовка фрагмента, то в наших силах сделать его длину фиксированной и тем упростить алгоритм фрагментации.
Законопослушный узел на выходе не должен менять состав и содержание нефрагментируемой части от фрагмента к фрагменту. Ведь зачем это ему? Тем не менее, на входе может оказаться, что у разных фрагментов одного исходного пакета разные нефрагментируемые части. Как мы скажем, когда дойдем до процедуры сборки, в этом случае безусловное преимущество у фрагмента со смещением ноль.
Пришло время подумать над форматом заголовка фрагмента. Он зависит от той системы параметров, которой мы станем описывать каждый фрагмент. Впрочем, готовую систему мы вполне можем взять из IPv4, потому что она удовлетворяет ключевому требованию: устойчивости к нарушению порядка фрагментов. Итак, вот параметры фрагмента IPv6:
К этим полям мы также должны добавить поле "следующий заголовок", чтобы обеспечить сцепление заголовков и полезной нагрузки в пакете IPv6. Таким образом, общее число полей постоянно, а значит, и заголовок фрагмента сможет обладать постоянной длиной. Теперь давайте рассмотрим каждый параметр по очереди и уточним его свойства.
Идентификатор пакета занимал в IPv4 16 бит, и этого оказалось мало: при большой скорости трафика вероятны конфликты, которые приводят к перекрестной сборке пакетов [RFC 4963]. Хуже того, при дальнейшем росте скорости трафика неверно собранные пакеты возникают достаточно часто, чтобы начали сбоить контрольные суммы TCP и UDP: происходят коллизии, когда сбойный пакет обладает верной контрольной суммой. Поэтому давайте расширим это поле до 32 бит. Но будет ли их достаточно?
Чтобы дать на это ответ, мы должны сначала определить, какова область уникальности идентификатора. В IPv4 идентификатор ведь не был самостоятельным — он объединялся с адресами источника и назначения, а также номером протокола. Только весь этот кортеж должен был оставаться уникальным. Перенести это правило в IPv6 без изменений нам мешает то, что номер протокола больше не характеризует пакет как целое: теперь пакет состоит из модулей, каждому из которых отвечает свой номер протокола. Поэтому пусть в IPv6 идентификатор будет уникальным только для заданных адресов источника и назначения.
Если пакет содержит маршрутный заголовок, то это будут адрес источника и адрес конечного назначения [§4.5 RFC 2460], поскольку собирать пакет IPv6 будет его конечный адресат.
Если у фрагмента адрес источника или адрес назначения имеет ограниченную область действия, то индекс его зоны тоже придется
учесть при идентификации такого
Во времени же идентификатор должен оставаться уникальным, пока "живы" фрагменты данного пакета. Оценить время жизни фрагментов сверху можно суммой времени доставки фрагмента по сети и величиной тайм-аута сборки. Если вторая величина — настраиваемый параметр сетевого стека, то первая может принимать сколь угодно большие значения, по крайней мере, в теории.
Здесь бы нам пригодилась интерпретация поля TTL как времени жизни пакета, но мы от нее отказались по практическим соображениям [§8.2 RFC 2460].
Поэтому нам остается сделать эмпирическое предположение: чем выше скорость трафика, тем меньше вероятная задержка пакета в сети. Здесь мы имеем в виду не время распространения сигнала, а непроизводительные расходы времени, такие как ожидание в буферах транзитных узлов. При большой скорости трафика пакет скорее будет потерян, нежели задержится где-то на многие секунды.
Значит, в случае большой скорости трафика — а именно он нам интересен — главным фактором в управлении надежной сборкой будет ее тайм-аут. Он должен быть существенно меньше, чем период, за который идентификатор заведомо повторится. Этот период сокращается при росте скорости трафика, так что его можно оценить снизу, зная верхнюю оценку скорости, с которой один источник готов передавать пакеты в адрес данного узла.
Попробуем оценить худший случай, когда скорость расходования значений идентификатора максимальна при постоянной скорости трафика, выраженной, например, в бит/с. Одно значение идентификатора расходуется на один исходный пакет, так что мы ищем условие максимальной частоты исходных пакетов при постоянной скорости трафика. Для этого необходимо, чтобы сумма длин фрагментов была минимальной.
Пакет IPv6, который фрагментирован согласно протоколу, состоит не менее чем из двух фрагментов, а длина первого фрагмента не менее
1280 байт. Длина же второго фрагмента не может опуститься ниже суммы длин заголовка IPv6 и заголовка фрагмента плюс один байт
Сейчас мы не рассматриваем "патологическое" поведение источника, когда он создает пакеты из одного фрагмента или чрезмерно короткие фрагменты. Тем не менее, алгоритм сборки фрагментов должен быть устойчив к этим аномалиям, так как они явным образом ничему не противоречат. Так завещает нам принцип Постела.
Теперь мы без труда вычислим, что, например, при передаче со скоростью 1 Тбит/с значение 32 битного идентификатора неизбежно повторится только через такой интервал времени:
$$\frac{8{\frac{бит}{байт}}\times 1329{\frac{байт}{пакет}}\times 2^{32}_{пакет}}{10^{12}\frac{бит}{с}}\approx 46_c$$ .
Также можно вычислить, какая скорость фрагментированного трафика станет опасной, если тайм-аут сборки составляет 60 секунд, как предписано в §4.5 RFC 2460:
$$\frac{8{\frac{бит}{байт}}\times 1329{\frac{байт}{пакет}}\times 2^{32}_{пакет}}{60_{с}}\approx \frac {46}{60}\times 10^{12}\frac{бит}{с}\approx 7,6\times 10^{11}\frac{бит}{с}$$.
Запас еще в несколько порядков, конечно, не помешал бы; но и такое соотношение скорости трафика и периода повтора
Реализация IPv6 KAME пошла на сознательное упрощение. Вместо того чтобы генерировать независимые цепочки идентификаторов для каждой пары адресов источника и назначения, она использует одну цепочку на весь стек. Раньше это был простой счетчик, тогда как современное развитие KAME в FreeBSD применяет генератор псевдослучайных чисел с заведомо большим периодом. Конечно, такой трюк не может не привести к сокращению периода повтора идентификатора.
Смещение фрагмента, с нашей тягой к экономии единиц, пусть отсчитывается не от начала пакета, а от начала фрагментируемой
части, как показано на рис 3.18(рис 3.18) Как вычисляется смещение фрагментов (с.з. = следующий заголовок)
Побочным результатом этого выбора будет то, что теперь смещение фрагмента не может указать внутрь основного заголовка IPv6.
Как мы помним, в IPv4 смещение фрагмента отсчитывалось от самого начала пакета, и это позволяло злоумышленнику составить такую
последовательность фрагментов, в которой последующие фрагменты накладывались на важнейшие поля заголовка, в частности, протокол и
адреса источника и назначения. Чуть позже мы сделаем фрагментацию IPv6 еще безопаснее, наложив полный запрет на перекрывающиеся
фрагменты.
Единицу смещения и длину поля "смещение" мы оставим такими же, как в IPv4: 8 байт и 13 бит, соответственно. Это позволит смещению принимать значения вплоть до $$(2^{13}-1)\times 8=65528$$ байт включительно. Такого диапазона достаточно, потому что фрагментируемая часть исходного пакета не длиннее максимальной "полезной нагрузки" IPv6, составляющей 65535 байт; а 7 байт разницы заведомо поместятся в один фрагмент, если нефрагментируемая часть состоит только из основного заголовка IPv6 — условие максимально длинной фрагментируемой части.
Как мы помним, значение "длина полезной нагрузки" в основном заголовке IPv6 включает заголовки расширения.
Полученный диапазон смещений даже чуть шире, чем нужно: он позволяет создать последовательность фрагментов, которая соберется в
пакет-великан длиной более 65575 байт. Для этого достаточно, чтобы у любого фрагмента сумма его смещения в байтах и длины без
заголовков превысила 65575 байт за вычетом длины нефрагментируемой части (см. рис. 3.16 и рис. 3.17). Если в ходе сборки получится
пакет-великан, то следует уничтожить его и, возможно, выслать источнику извещение об ошибке
Пусть читатель сам установит, какова максимальная длина такого пакета-великана в предположении, что PMTU не меньше 65575 байт. Указания к решению: Прочтите до конца раздел. Затем докажите формулу:
$$L=L_1-F_1-8+8O_N+F_N$$ ,
где L — длина полезной нагрузки исходного пакета, $$L_1$$ — длина полезной нагрузки первого фрагмента, $$F_1$$ — длина первого фрагмента без заголовков, $$O_N$$ — смещение последнего фрагмента в 8-байтных словах, $$F_N$$— длина последнего фрагмента без заголовков. Максимизируем все параметры, кроме $$F_1$$, который минимизируем, поскольку он со знаком "минус": $$max L_1=65535$$,$$min F_1=8$$ (из-за выбора единицы смещения), $$max O_N=8191$$ (13 бит), $$max F_N = 65535 - 8 = 65527 $$ (учитываем длину заголовка фрагмента 8 байт). В результате получаем, что максимальная полезная нагрузка пакета-великана составляет 196574 байт, а весь пакет еще на 40 байт длиннее! Такая последовательность фрагментов основана на трюке с разным составом нефрагментируемой части: в первом фрагменте она будет максимально длинной, например, за счет опций PadN (§3.3.2), а в последнем там будет только заголовок IPv6. Очевидно, что послать такую серию фрагментов может только экспериментатор или злоумышленник, потому что она — испытание узла-адресата на устойчивость.
Флаг "еще фрагменты" (M) обладает той же семантикой, что и флаг MF в IPv4: если он установлен, то это не последний фрагмент, а если сброшен, то последний.
Длина фрагмента до инкапсуляции (см. рис. 3.16) должна быть кратна 8 байтам из-за выбора единицы смещения, если только это не самый последний фрагмент. Ведь иначе смещение следующего фрагмента не будет делиться на 8. Поэтому, если вдруг адресат получит фрагмент с установленным флагом "еще фрагменты", и его длина без заголовков (см. рис. 3.17) окажется не кратной 8 байтам, то значит, где-то произошел сбой. Такой фрагмент надо отбросить, а в адрес источника выслать извещение об ошибке.
Конечно же, характеристики "последний" и "не последний" относятся здесь к позиции фрагмента в исходном пакете, а не к порядку поступления фрагментов из сети. Работа механизма фрагментации и сборки IP не зависит от порядка фрагментов, потому что сеть вправе непредсказуемо нарушать его.
Все эти поля мы уместим в заголовок длиной 8 байт, ради выравнивания, как показано на рис. 3.19. Это и будет заголовок фрагмента [§4.5 RFC 2460].
(рис 3.19) Заголовок фрагмента
Поле "смещение" расположено так, чтобы удобно было переводить его значение в байты: достаточно прочесть все выровненное 16 битное слово, а затем обнулить три младших бита операцией "логическое И" с маской 0xFFF8. Это эквивалентно сдвигу значения "смещение" влево на 3 двоичных разряда, то есть умножению его на 8.
Сборка выполняется в обратном порядке:
получив на входе фрагмент, сборщик составляет кортеж: <адрес источника, адрес назначения, идентификатор>, — и использует его как ключ при поиске буфера сборки;
а)если буфер сборки по этому ключу не найден, надо создать новый буфер, так как перед нами новый пакет;
если смещение фрагмента равно нулю:
а)его начало, вплоть до заголовка фрагментации, но не включая его, становится нефрагментируемой частью вновь собираемого пакета и хранится вне буфера сборки;
В принципе, реализация может хранить нефрагментируемую часть и в буфере сборки, с тем чтобы в итоге сборки получить полный пакет. Однако тогда на шаге 3 ей придется внести поправку в позицию фрагмента внутри буфера. Ведь в заголовке фрагмента смещение указано относительно начала фрагментируемой части, а не всего пакета. Кроме того, понадобится буфер сборки с подвижным началом, так как длина нефрагментируемой части неизвестна до приема фрагмента со смещением ноль, а он может прийти не первым, если сеть переупорядочит фрагменты.
б)сборщик запоминает значение "следующий заголовок" из заголовка фрагмента;
§4.5 RFC 2460 допускает случай, когда фрагменты одного пакета содержат разные заголовки расширения и значения "следующий заголовок", хотя на практике такие вариации весьма и весьма подозрительны. Но какие заголовки и значения полей попадут в собранный пакет? Здесь преимущество будет у самого первого фрагмента — то есть с нулевым смещением, а не пришедшего раньше других.
данные после заголовка фрагментации помещаются в буфер сборки по смещению, указанному в заголовке фрагментации;
если фрагмент — последний (флаг M сброшен): длина буфера сборки "замораживается", чтобы прочие фрагменты не могли оказаться за последним;
если все "пробелы" в буфере сборки заполнены: фрагментируемая часть пакета собрана; осталось поместить перед ней нефрагментируемую часть (см. шаг 2(а)) и провести обратную корректировку полей:
а)"длина полезной нагрузки" — теперь относится к вновь собранному пакету;
б)"следующий заголовок" в последнем заголовке нефрагментируемой части — используя значение, которое запомнили на шаге 2(б).
По ходу сборщик должен обнаруживать возможные ошибки, которые мы уже обсудили выше:
тайм-аут сборки, чтобы недособранные пакеты не занимали память навечно;
фрагмент с установленным флагом M, длина которого не кратна 8 байтам;
вновь собранный пакет-великан длиной более 65575 байт.
Естественной реакцией на эти ошибки будет отбросить пакет и, возможно, выслать источнику извещение об ошибке.
Мы говорим: "Возможно", — потому что извещения не стоит слать в ответ на групповые пакеты и другие извещения. Об этом мы еще поговорим в §4.3.
Кроме того, возможны отклонения, связанные с позицией и ролью фрагмента, как то:
В обоих случаях, чтобы вызвать подозрение, это должны быть разные фрагменты, а не дубликаты одного и того же фрагмента, возникшие в сети.
В лучшем случае их может вызвать совпадение кортежа <адрес источника, адрес назначения, идентификатор > у разных фрагментированных пакетов, а это чревато перекрестной сборкой и повреждением данных.
В худшем же случае это может быть злонамеренная попытка обойти системы сетевой безопасности. Вот пример такой атаки. Допустим, веб-сервер защищен простым межсетевым экраном, так что входящие соединения TCP разрешены только с портом 80. Злоумышленник посылает первый фрагмент с заголовком TCP, где порт назначения равен 80, так что межсетевой экран считает этот пакет безопасным и пропускает его дальнейшие фрагменты. Тогда следующим шагом злоумышленник шлет перекрывающийся фрагмент, который заменяет порт назначения 80 на 22 (или любой другой) в буфере сборки веб-сервера.
Поэтому самым безопасным решением будет полностью запретить перекрытие фрагментов IPv6 [RFC 5722]:
Рассмотрите и другие возможные атаки на механизм сборки IPv6. Например: Злодей шлет фрагменты в обратном порядке: №2, а затем №1. У фрагмента №2 сумма смещения и длины не превышает дозволенного, и адресат создает буфер сборки. Затем приходит фрагмент №1, у которого нефрагментируемая часть максимально возможной длины или около того. Вместе эти фрагменты образуют пакет-великан, причем он "растет" не с конца, а с головы буфера сборки, за счет нефрагментируемой части. Это вполне может вызвать крах неаккуратной реализации. Конечно, для такой атаки нужен достаточно большой "потолок" PMTU.
В контексте заголовков расширения IPv6 нам будем уместно упомянуть IPsec (IP security, безопасность IP). Ведь IPsec — это рекомендованный компонент всякого узла IPv6 [§11.1 RFC 6434].
В предыдущей редакции технических условий узла IPv6 поддержка IPsec была даже обязательной [§8.1 RFC 4294]. Хотя в новой редакции [§11.1 RFC 6434] она стала рекомендованной, а не обязательной, это вовсе не перечеркнуло давней связи между IPv6 и IPsec. IPsec был "разжалован" ради двух вполне оправданных целей [§11 RFC 6434]. Во-первых, IPsec — это не панацея и не монопольный поставщик безопасности в среде TCP/IP. В некоторых случаях стоит предпочесть безопасность на другом уровне, скажем, транспортном (TLS) или прикладном (SSH). А во-вторых, обязательность IPsec — это серьезное препятствие на пути встроенных реализаций IPv6 для платформ с ограниченными ресурсами, например, кофеварок и сенсоров. Уместить IPsec в такую минимальную реализацию практически невозможно, а без него реализация IPv6 не соответствует действующему стандарту. Новая редакция технических условий узла IPv6 разрешила это противоречие.
В целом IPsec [RFC 4301] — это архитектура, то есть целая система моделей, механизмов, алгоритмов и протоколов, направленная на сквозную защиту пакетов IP. Слово "сквозная" здесь значит, что защиту пакета выполняют только его источник и конечный адресат, как то: источник подписывает пакет, а конечный адресат проверяет подпись; источник шифрует пакет, а конечный адресат расшифровывает его. Между тем, транзитные узлы, будь то маршрутизаторы или промежуточные адресаты, обрабатывают защищенные пакеты точно так же, как обычные.
Хотя исторически IPsec возник именно для защиты IPv6 и только затем был адаптирован к среде IPv4, подробно рассматривать его мы не будем хотя бы потому, что он заслуживает отдельного курса лекций. Сейчас мы ограничимся только тем, что посмотрим, какие черты объединяют заголовки безопасности IPsec с другими заголовками IPv6.
Среди протоколов IPsec с IPv6 непосредственно соприкасаются протоколы AH (authenticated header, аутентифицированный заголовок) [RFC 4302] и ESP (encapsulating security payload, инкапсулирующая защитная полезная нагрузка) [RFC 4303]. Первый из них, AH, обеспечивает только аутентификацию и защиту целостности пакетов, тогда как ESP позволяет, кроме того, зашифровать большую часть пакета. Так как ESP умеет все, на что способен AH, и даже сверх того, применять их вместе смысла нет. Давайте посмотрим, сможем ли мы составить заголовки расширения для этих протоколов, исходя из общих соображений.
По одной из версий, AH возник отдельно от ESP, только чтобы обойти ограничения на экспорт средств шифрования из
Какие поля потребуются заголовку AH? В первую очередь, по смыслу AH, это цифровая подпись пакета (или же, в терминологии IPsec, значение защитного кода — integrity check value, ICV). Алгоритмы безопасности и способы атаки на них находятся в постоянном развитии, подобно броне и снарядам; поэтому нам не следует заранее фиксировать длину цифровой подписи и алгоритм ее вычисления. Как следствие, заголовок AH будет переменной длины, и ему понадобится поле "длина". В отличие от других заголовков расширения IPv6, единицей длины AH служит 32 битное слово.
В подражание другим заголовкам, значение поля длины AH — это полная длина заголовка AH минус два слова [§2.2 RFC 4302]. Конечно, логичнее было бы вычесть длину постоянной части AH, то есть три слова. С другой стороны, вычитая только два слова, мы гарантируем, что значение поля длины AH никогда не будет нулевым. Предубеждение против нулевых значений основано на том, что во многих протоколах нулевое значение означает "нет сведений". См., например, поле контрольной суммы UDP. Кроме того, в IPv6 длина заголовка AH должна быть кратна 64 битам, чтобы сохранить выравнивание последующих данных [§2.2 RFC 4302].
Называется поле длины AH "длина полезной нагрузки" [§2.2 RFC 4302] — наверное, чтобы сбить врагов с толку.
Как нам быть с алгоритмом подписи и его возможными параметрами, такими как секретный ключ? Модель IPsec полагает, что эти сведения заранее находятся в памяти источника и адресата как специальная запись, связь безопасности (security association, SA) [§4 RFC 4301]. Если провести параллель с кинофильмом "про шпионов", обмен шифроблокнотами происходит за кадром, потому что так безопаснее. В простейшем случае, связь безопасности вводят вручную на каждом из узлов.
Конечно, на практике связями безопасности удобнее управлять автоматически, с помощью дополнительного протокола, такого как IKE.
Поэтому, когда приходит время передавать данные, источнику достаточно сослаться на нужную связь (у шпионов это была бы строка в шифроблокноте), чтобы адресат смог правильно трактовать заголовок безопасности. Такой ссылкой служит 32 битный индекс параметров безопасности (security parameter index, SPI). Он явно заслуживает отдельного поля в заголовке AH [§2.4 RFC 4302].
Далее, чтобы злоумышленник не смог провести атаку воспроизведением, послав копию аутентичного пакета, полезно будет включить в заголовок AH порядковый номер, монотонно растущий на протяжении жизни данной связи безопасности [§2.5 RFC 4302].
На первый взгляд, атака воспроизведением основана на свойстве неидемпотентности некоторых сообщений. Например, дважды предъявив копии чека на 1 рубль, мы получим 2 рубля. Поэтому чеки обычно номерные. С другой стороны, повторный сигнал "поднять железнодорожный семафор" ничего не изменит, потому что семафор уже поднят. Тем не менее, злоумышленник может сохранить копию этого сигнала и послать ее позже, когда семафор закроется, вызвав тем самым крушение поезда. Поэтому неудивительно, что система безопасного обмена данными должна противостоять таким атакам.
Так как сеть способна изменять порядок доставки пакетов, нельзя надеяться, что порядковый номер будет монотонно расти в принимаемых пакетах. Поэтому проверка порядкового номера AH адресатом происходит при помощи скользящего окна [§3.4.3 RFC 4302]: пакеты с номером меньше нижней границы окна игнорируются, в пределах окна запоминается каждый принятый номер, а получение номера больше верхней границы окна сдвигает окно вперед.
Наконец, AH — это заголовок расширения, за которым могут находиться другие заголовки и полезная нагрузка. Поэтому в заголовке AH необходимо поле "следующий заголовок", чтобы адресат смог правильно интерпретировать последующие данные. Как можно убедиться в [§2 RFC 4302], наши общие соображения позволили обосновать все поля заголовка AH. Его точный формат показан на рис 3.20(рис 3.20) Заголовок AH
Для полноты картины осталось заметить, что цифровая подпись ICV защищает не только "хвост" пакета, начиная с заголовка AH, но и предшествующие заголовки — по мере возможности, конечно. Трудность здесь в том, что некоторые поля в этих заголовках могут меняться по пути следования пакета. Если бы все их удалось идентифицировать, то при вычислении ICV через них можно было бы "перескочить" или подставить вместо данных то же число нулевых битов. Мы выберем замещение нулями, потому что оно защищает хотя бы длину переменного поля, при условии что значение ICV зависит от числа замещающих нулевых битов. Вернемся к идентификации переменных полей. Мы уже решили эту задачу для опций IPv6, закодировав нужную информацию в одном бите кода опции. Заголовки IPv6, в отличие от опций, — стандартные, и их свойства можно просто перечислись в соответствующем документе [Приложение A2 RFC 4302]. Здесь у нас возникает искушение провести троичную, а не двоичную классификацию полей:
Новый класс, предсказуемые поля и заголовки, — это те, значение которых меняется предсказуемым образом, так что источник, вычисляя ICV, может использовать их финальное значение, которое увидит конечный адресат. На практике, единственным стандартным представителем этого класса оказывается маршрутный заголовок RH0 (и связанный с ним адрес назначения).
Любитель биологии заметил бы, что даже этот представитель — ныне ископаемый.
Окончательно классифицируем поля основного заголовка IPv6 в табл. 3.2.
| Неизменные | Предсказуемые | Непредсказуемые |
|---|---|---|
| "версия" | "адрес назначения" — при наличии маршрутного заголовка | "класс трафика" |
| "длина полезной нагрузки" | "метка потока" | |
| "следующий заголовок" | ""предельное число шагов"" | |
| "адрес источника" | ||
| "адрес назначения" — в отсутствие маршрутного заголовка |
Что касается заголовков расширения, то здесь работы нам практически не осталось. В заголовках опций эта задача решена на уровне отельных опций; предсказуемый заголовок RH0 упразднен; а заголовок фрагмента вообще не подлежит защите, потому что фрагментация происходит после защиты, а сборка, соответственно, перед проверкой AH [Приложение A2 RFC 4302], так что модулю IPsec никогда не встретится заголовок фрагмента, если только не произошел сбой в стеке.
Пакет-фрагмент может попасть на вход защищенного туннеля, но тогда он подвергнется туннельной инкапсуляции, и полученный туннельный пакет уже не будет фрагментом. Это применимо и к туннельному режиму IPsec, который лишь объединяет логические операции туннелирования и защиты в одну удобную услугу.
Перейдем теперь к ESP. Новой задачей, по сравнению с AH, будет скрыть содержимое пакета от посторонних глаз, применив шифрование. Чтобы сторонний наблюдатель мог извлечь как можно меньше сведений о содержимом пакета, следует зашифровать не только байты, следующие за заголовком ESP, но и сведения об их смысле, то есть поле "следующий заголовок". Тогда, в теории, наблюдателю придется только гадать, что же содержится в данном пакете. Однако на практике выбор значений поля "следующий заголовок" не так уж велик. Более того, сегодня в прикладных пакетах чаще всего это будет 6 (TCP) или, заметно реже, 17 (UDP), а в туннельных 4 (IPv4) или 41 (IPv6). Если начать шифрование с этого поля, то ограниченный набор его вероятных значений откроет лазейку для атак на шифротекст, потому что многие алгоритмы шифрования работают над входными данными последовательно, байт за байтом или блок за блоком.
Это касается даже шифрования в режиме сцепления блоков (CBC), потому что первый блок ни с чем не сцеплен, и предсказуемое начало входного текста все так же понижает надежность защиты.
Чтобы этого не произошло, применим простой трюк: пусть поле "следующий заголовок" находится в ESP после защищаемых данных и потому шифруется последним [§2 RFC 4303]. Туда же, в конец шифротекста, мы поместим поля "набивка" и "длина набивки", необходимые для блочных шифров. И тогда перед шифротекстом достаточно будет поместить только SPI и порядковый номер — конечно, открытым текстом.
Если данная связь безопасности предписывает не только шифровать пакеты, но и проверять их подлинность, то цифровую подпись ICV можно добавить после шифротекста. Тогда сторонний наблюдатель, не имея доступа к параметрам безопасности, даже не сможет определить, подписан ли данный пакет.
Именно этой структуре пакета, представленной на рис. 3.21, ESP обязан своим названием. В отличие от AH, он не встраивается в цепочку заголовков расширения, а полностью инкапсулирует защищаемую часть пакета, превращая ее для стороннего наблюдателя в непроницаемые данные, лишенные очевидной интерпретации.
(рис 3.21) Инкапсуляция ESP
Опытный взломщик, не имея доступа к самим данным, может делать выводы из размера пакетов и их распределения по оси времени. Полное сокрытие полезной нагрузки в ESP позволяет перекрыть и этот канал утечки информации. Вариации размера пакетов можно маскировать достаточно объемистой набивкой [§2.7 RFC 4303]. А чтобы не было очевидных закономерностей в ритме обмена пакетами, стороны могут вставлять в поток пакеты-пустышки, когда нет настоящих данных для передачи. На эту роль прекрасно подойдут пакеты, в которых цепочка заголовков завершается кодом 59 — "следующего заголовка нет" (§3.3.1). Вместе эти приемы обеспечивают конфиденциальность на уровне потока пакетов (traffic flow confidentiality, TFC).
У такой изоляции ESP есть и обратная сторона. В отличие AH, в ESP цифровая подпись ICV не защищает внешний заголовок IP, предшествующий данным ESP [§2 и §3.3.2 RFC 4303]. Поэтому информации из внешнего заголовка в пакете ESP слепо доверять нельзя. Тем не менее, возможности злоумышленника по его подделке не так уж велики. Если поле SPI указало на годную связь безопасности SA в памяти адресата, а проверка целостности и расшифровка прошли успешно, то адресат пакета может сделать следующие выводы:
Архитектура IPsec допускает, но не требует, чтобы связь SA была привязана к конкретным адресам источника и назначения [§4.4.1 RFC 4301].
Тем не менее, злоумышленник может проводить ограниченные атаки на протоколы, чувствительные к точным значениям адресов источника и назначения в пакете. К примеру, модифицируя эти адреса, можно перемещать сегменты TCP из одного соединения в другое, при условии, что эти соединения различаются только адресами, но не портами.
Как мы помним, идентификатором сеанса TCP выступает кортеж <адрес №1, порт №1, адрес №2, порт №2>. Поэтому вполне возможна одновременная работа двух сеансов TCP, которые отличаются, скажем, только адресом №1.
Чтобы блокировать и этот вектор атаки, нам достаточно превратить заголовок IP в полезную нагрузку перед тем, как над ним поработает ESP. Мы уже умеем это делать с помощью туннельной инкапсуляции, так что сейчас нам достаточно соединить вместе два решения: туннель и IPsec. Если мы ограничимся простейшим туннелем "IP-в-IP", то получим конструкцию, известную как туннельный режим (tunnel mode). Очевидно, что он применим не только к ESP, но и к AH, хотя для ESP он важнее. В противоположность ему, непосредственная защита прикладных пакетов называется транспортный режим (transport mode), возможно, потому что в этом режиме под защитой чаще всего оказываются дейтаграммы транспортных протоколов.
На практике под защитой IPsec вполне могут быть и другие туннельные протоколы, например, GRE или L2TP. Ведь для IPsec безразлично, что защищать, пока это IP.
Пусть читатель самостоятельно изобразит структуру пакетов AH и ESP в туннельном режиме. Какие значения полей "следующий заголовок" в них возникнут? Рассмотрите IPv4 и IPv6.
Теперь, обезопасив себя от подделки адресов, спокойно посмотрим на ту же проблему с другой стороны. Кто еще, кроме злодеев, может вносить изменения во внешний заголовок IP? Конечно же, трансляторы NAT. В некоторых случаях NAT — это полезный инструмент, однако его работа основана на подмене одних адресов IP другими. И если ESP спокойно отнесется к трансляции адресов во внешнем заголовке, то AH неизбежно забракует пакет, прошедший NAT.
Искушенный читатель может вспомнить еще одно средство подружить IPsec и NAT: NAT T. Однако в действительности протокол NAT T решает довольно узкую задачу: он позволяет IPsec проходить сквозь трансляторы NAT в режиме маскарада, когда трансляция происходит по принципу "многие в один" (сюръекция). Очевидно, что при этом происходит потеря адресной информации на уровне IP, и недостающие сведения транслятор скрывает в номерах портов TCP или UDP (или других аналогичных полях, таких как идентификатор ICMP ping). Поэтому пакеты IPsec приходится дополнительно инкапсулировать в UDP. Принципиальной несовместимости между AH и NAT это не решает, так что инкапсуляция NAT T определена только для ESP [RFC 3948].
Теперь мы составили полную картину заголовков расширения IPv6 и назначили каждому из них определенную роль. Весьма вероятно, что эта роль определяет не только трактовку данного заголовка, но и его предпочтительную позицию в пакете. В то же время, единообразный формат заголовков расширения IPv6 никак не ограничивает их число и порядок. Действительно, в каждом таком заголовке есть поле "следующий заголовок", и это позволяет источнику пакета сцепить заголовки в произвольном порядке и даже повторить заголовок данного типа несколько раз. Вопрос в том, всегда ли будет иметь смысл такая перестановка. Вероятно, нет. Поэтому сейчас нам надо найти и сформулировать дополнительные правила, которые продиктуют порядок заголовков расширения в пакете IPv6. При этом мы, конечно же, воспользуемся нашими предыдущими наработками.
Для начала заметим, что существует ровно один заголовок расширения, который представляет интерес для всех узлов на пути пакета; это заголовок пошаговых опций. Один такой заголовок может включать в себя переменное число опций, так что ему совершенно незачем встречаться в пакете больше одного раза. Значит, мы вправе повторить сказанное выше: заголовок пошаговых опций, если он вообще есть в пакете, обязан быть вторым по порядку, следуя непосредственно за основным заголовком IPv6. Это правило упростит логику работы маршрутизаторов IPv6, так как им незачем заглядывать дальше первого заголовка расширения.
Еще два заголовка расширения нужны промежуточным адресатам: заголовок опций адресата и маршрутный заголовок. Маршрутный заголовок с ненулевым числом активных сегментов — это сигнал текущему адресату, что пакет надо продвинуть дальше. Поэтому будет вполне естественно, если промежуточный адресат остановится на маршрутном заголовке и не станет заглядывать дальше. Следовательно, опции, предназначенные всем адресатам, надо расположить перед маршрутным заголовком.
Все остальные заголовки расширения и полезная нагрузка несут информацию только конечному адресату. Если исходный пакет подлежит фрагментации, то они составляют его фрагментируемую часть и становятся, вообще говоря, недоступны транзитным узлам. Среди них особо выделяются заголовки безопасности, с помощью которых источник может защитить пакет. В частности, шифрование способно скрыть остаток пакета от посторонних глаз без какого-либо вреда для работы протокола. Поэтому естественное место для заголовков безопасности — в самом начале фрагментируемой части.
Это же положение заголовков безопасности гарантирует нам, что последующие данные заведомо не изменяются по пути следования пакета, — одно из требований IPsec.
Наконец, в §3.1 мы допустили, что возникнут опции, имеющие смысл только для конечного адресата. Их будет разумно поместить в отельный заголовок опций адресата, расположенный после заголовка безопасности и защищенный им.
Нам осталось указать место заголовку фрагмента, но мы фактически уже сделали это выше: он возникает непосредственно за нефрагментируемой частью исходного пакета, то есть после маршрутного заголовка.
Конечно же, ни один из заголовков расширения не обязан встречаться в пакете, поэтому установленный нами порядок заголовков IPv6 — относительный, а не абсолютный. Вот его окончательный вид [§4.1 RFC 2460]:
Таким образом выходит, что ни один заголовок расширения не должен встретиться в пакете IPv6 больше одного раза, кроме заголовка опций адресата, который может встретиться самое большее дважды [§4.1 RFC 2460].
В завершение раздела отметим, что при этом порядке заголовков расширения в пакете IPv6 их можно обрабатывать последовательно, без необходимости забегать вперед или возвращаться назад за информацией.
Отклонение от указанного выше порядка заголовков может вызывать нежелательные последствия. Например, если маршрутный заголовок окажется скрыт за заголовком фрагмента, то текущий адресат сначала соберет пакет, а затем среагирует на маршрутный заголовок. Такое поведение не только грубо противоречит правилам фрагментации и сборки IPv6, но и нарушает работу PMTUD: источник фрагментирует пакет, чтобы он прошел трассу, а текущий адресат сводит его работу на "нет". Конечно, в этом примере скорее виноват сам источник, который разместил заголовки расширения в неверном порядке. Тем не менее, "игры" с порядком заголовков могут быть использованы в злонамеренных целях, и поэтому адресат обязан оставаться начеку. К сожалению, эталонная реализация KAME не проверяет порядка заголовков в принятых пакетах, а слепо обрабатывает их подряд. Стандарт, в свою очередь, ограничивается рекомендациями и не выдвигает четких требований к порядку заголовков в пакете IPv6. [§4.1 RFC 2460] даже требует принимать и обрабатывать заголовки в любом порядке. Эта деталь работы IPv6 явно требует уточнения.
Модульная структура пакета IPv6 предполагает, что появятся и новые типы заголовков расширения. Следовательно, устаревшие узлы будут сталкиваться с незнакомыми заголовками. Какова будет безопасная реакция узла на это событие? Сможет ли он сигнализировать источнику о проблеме? Удастся ли ему перескочить через неизвестный заголовок? Давайте разберем эти вопросы, чтобы поставить точку в нашей работе над заголовками IPv6.
Прежде всего, давайте определимся, какие именно типы узлов на пути пакета могут столкнуться с подобной проблемой. Вряд ли это будет источник, ведь он сам составил этот пакет и знает смысл каждого заголовка в нем. Обычные маршрутизаторы не анализируют пакет дальше заголовка пошаговых опций, который они обязаны поддерживать, так что они просто не доберутся до незнакомого заголовка. А вот текущий адресат пакета обязан пройти по цепочке заголовков IPv6 до тех пор, пока он не встретит маршрутный заголовок или полезную нагрузку; вот тут-то его и может поджидать неизвестный заголовок расширения IPv6. Кроме того, в сети могут быть различные "теневые" устройства, например, межсетевые экраны или системы обнаружения и предотвращения атак. Такое устройство обычно маскируется под маршрутизатор или даже коммутатор (мост), однако ведет подробный анализ и учет протоколов на нескольких уровнях стека, отслеживая не только отдельные пакеты, но также транспортные соединения и прикладные сеансы. С появлением IPv6 ему придется разбирать всю цепочку заголовков расширения в каждом пакете, чтобы добраться до полезной нагрузки, а заодно проверить весь пакет на соответствие многоуровневой политике безопасности. Без сомнения, заголовок нового типа может озадачить программу такого устройства, если она была выпущена раньше.
Следующим шагом мы рассмотрим возможность перескочить через неизвестный заголовок расширения. Для этого представим себе процесс разбора пакета IPv6. Его алгоритм, если опустить детали, довольно прост:
для текущего заголовка:
Чтобы выполнить пункты 2а и 2б, надо знать формат текущего заголовка. Действительно, разные заголовки расширения по-разному кодируют свою длину и размещают поле "следующий заголовок" по разным смещениям. Это очевидное, но не главное препятствие к перескоку через неизвестный заголовок.
Главное же препятствие скрыто в пункте 3. Допустим, мы знаем формат текущего заголовка и успешно выполнили пункты 2а–2в, но тип следующего заголовка (п. 2а) нам ничего не говорит. Поскольку заголовки IPv6 и протоколы следующего уровня заносятся в один и тот же реестр (см. §3.1), мы даже не сможем определить, что представляет собой следующий заголовок, расширение IPv6 или его полезную нагрузку. Ведь перед нами только значение байта "следующий заголовок", например, 254. Это с равным успехом может быть как новый заголовок расширения IPv6, так и экспериментальный транспортный протокол.
Невозможность перескочить через неопознанный заголовок вполне согласуется с тем, что заголовки расширения IPv6 несут в себе инструкции, обязательные для выполнения. Это отличает их от опций.
Поэтому, когда адресат пакета встречает незнакомое значение "следующий заголовок", у него нет другого выбора, как отбросить этот пакет и, возможно, выслать источнику служебное извещение "неизвестный следующий заголовок".
Как обычно, мы говорим: "Возможно", — потому что в ряде случаев извещение высылать не следует из соображений устойчивости и безопасности. Например, плохой идеей будет извещение в ответ на групповой пакет или другое извещение. Подробности этого мы еще обсудим во время работы над протоколом служебных извещений IPv6.
А как быть в том же случае межсетевому экрану? Он не сможет провести глубокий анализ пакета, так что ответную реакцию ему должна подсказать текущая политика безопасности. Если эта политика либеральна, то пакет можно пропустить, надеясь на лучшее. Если же политика безопасности строгая, то пакет будет уничтожен.
Обратите внимание, что реакцию адресата здесь диктует протокол, а реакцию межсетевого экрана — политика.
Напомним себе разницу между протоколом и политикой. Протокол позволяет разным сторонам вести разговор и однозначно понимать друг друга, а политика накладывает на ход их разговора дополнительные ограничения, из протокола не следующие. К примеру, русский язык — это протокол, тогда как дипломатический протокол, с нашей колокольни, никакой не протокол, а всего лишь политика. Помимо этого, политика способна указать, каким образом надо установить параметры протокола, допускающие настройку. Так, политика может потребовать определенного значения TCP MSS - или запретить громкие разговоры после отбоя.
Чтобы реакция межсетевых экранов и подобных им "теневых" устройств была более гибкой, можно инкапсулировать все новые заголовки расширения в ровно один новый мета-заголовок с элементами TLV [draft-krishnan-ipv6-exthdr]. Впрочем, нам не совсем ясно, чем это лучше механизма опций, который работает по тому же принципу и уже готов к использованию.
Разобравшись в первом приближении с адресом, мы можем перейти к следующему ключевому элементу IPv6 — пакету.
Общепринятое сегодня деление пакета на заголовок и полезную нагрузку если и не является оптимальным в каждом возможном случае, то, по крайней мере, всех устраивает как самый простой компромисс. Поэтому мы можем не изобретать велосипед, а сохранить структуру пакета в самом общем ее виде, показанном на рис. 3.1.
(рис 3.1) Структура пакета IPv6 в первом приближении вполне традиционна
Следующим встает такой вопрос: как устроить заголовок, чтобы он был достаточно гибким и расширяемым? Ведь, как хорошо известно, без этих качеств наш протокол долго не протянет. В IPv4 для этой цели существовал механизм опций, который вполне справлялся со своей задачей. Чего в нем не хватало?
В первую очередь, более четкой структуры опций. Вообще говоря, опции IP могут быть двух видов. Опции первого вида представляют интерес для каждого узла по трассе пакета, а опции второго вида важны только для адресата. Первые мы назовем пошаговыми опциями (hop-by-hop options), а вторые — опциями адресата (destination options). Если опции этих видов перемежаются, то маршрутизаторам приходится просматривать их список полностью, чтобы найти в нем все пошаговые опции. Эта лишняя работа замедляет продвижение пакетов. Данную проблему еще можно было бы обойти, запретив источнику размещать пошаговую опцию после опции адресата.
Далее, сегодня есть спрос на сквозную защиту IP. То есть информацию пакета надо защищать непосредственно на уровне IP, а не выше или ниже по стеку; а значит, защитные данные, такие как цифровая подпись, должны находиться в заголовке IP или его расширении.
Здесь под защитой IP мы понимаем аутентификацию, шифрование и гарантию целостности пакетов. В TCP/IP эту задачу решает семейство протоколов IPsec, которого мы кратко коснемся в §3.3.5.
Определение "сквозная" означает, что в такой защите IP участвуют только источник и адресат, а транзитные узлы даже не отличают защищенные пакеты от обычных и обрабатывают их по одним и тем же правилам.
Допустим на минутку, что в нашем распоряжении — только опции IP. Тогда элементы защиты нам придется тоже выполнить в виде опций. Между тем, если в пакете уже есть опции адресата, то их можно было бы зашифровать, чтобы скрыть от посторонних глаз; ведь промежуточным узлам не нужно анализировать их. В этом случае часть заголовка IP окажется открытой, а часть зашифрованной, что неизбежно собьет с толку маршрутизаторы. Чтобы такой подход мог хоть как-то работать, опции адресата должны были бы находиться строго после опций защиты, а все маршрутизаторы обязаны были бы знать о существовании опций защиты: тогда сканирование списка опций маршрутизатор прерывал бы на первой же опции защиты, потому что дальше может идти шифрованная "абракадабра". Однако данный подход в открытую нарушает сквозной характер защиты: она теряет свою прозрачность для транзитных узлов. Кроме того, подобное решение "в лоб" начисто лишено технического изящества.
Поэтому мы не станем ограничивать себя опциями и пересмотрим саму структуру заголовка в пакете IPv6. Пусть этот заголовок будет модульным и состоит из более мелких, простых заголовков, каждый из которых решает ровно одну задачу. Такой составной заголовок схематически показан на рис. 3.2.
(рис 3.2) Составной заголовок пакета IPv6Чтобы число простых заголовков было практически неограниченным, их можно "сцепить" вместе способом, в котором различимы мотивы связанного списка и кодирования TLV. А именно каждый простой заголовок содержит поле "следующий заголовок" (Next Header), в котором закодирован тип заголовка, идущего непосредственно за этим [§4 RFC 2460], как показано на рис. 3.3. Кроме того, заголовки переменной длины содержат поле длины данного заголовка, тогда как длина фиксированных заголовков заранее задана протоколом. Сведения о длине понадобятся, чтобы однозначно определить, где заканчивается текущий заголовок и начинается следующий.
(рис 3.3) Тип простого заголовка IPv6 надо искать в предыдущем заголовке (с.з. = следующий заголовок)
А теперь обратите внимание: в отличие от кодирования TLV, где тип объекта содержится в самом объекте, тип заголовка IPv6 находится в предшествующем заголовке. Как же тогда получатель пакета IPv6 узнает, каков тип самого первого простого заголовка? Ему не придется гадать, если протокол заранее зафиксирует этот тип. Действительно, нам в любом случае потребуется обязательный заголовок, куда мы поместим адреса источника и назначения. Без этого заголовка не обойдется ни один пакет IPv6, так что пускай он и будет самым первым. Именно этот простой заголовок и называют заголовком IPv6 (the IPv6 header), тогда как прочие простые заголовки называют заголовками расширения IPv6 (IPv6 extension headers). Чтобы избежать двусмысленности, мы довольно часто будем уточнять: основной заголовок IPv6, — в противовес заголовкам расширения.
А что содержит поле "следующий заголовок" в самом последнем заголовке? Это поле отлично подходит для типа полезной нагрузки, которая
расположена за последним заголовком. Для этих типов уже существует реестр протоколов Internet
(рис 3.4) Соединение составного заголовка IPv6 с полезной нагрузкой
Отсюда немедленно следует длина поля "следующий заголовок" — 8 битов, — потому что в реестре протоколов IP всего 256 значений. Это совсем немного, так что мы должны ограничить свои аппетиты небольшим числом разных типов простых заголовков.
Мы сознательно сказали "типы простых заголовков", а не "типы заголовков расширения". Основному заголовку IPv6 тоже назначен отдельный код, 41. Это пригодится нам позже, в § 4.2, для туннельной инкапсуляции.
К счастью, на сегодняшний день в реестре протоколов Internet еще достаточно свободных значений: из них занято чуть больше половины.
Как мы увидим в §3.3.5, заголовки защиты IPsec — это всего лишь частный случай заголовков расширения. Но если это так, каким образом IPsec применяют к IPv4? Нетрудно увидеть, что цепочку заголовков расширения вполне можно прицепить и к заголовку IPv4. Ведь реестр протоколов один независимо от версии IP. Другое дело, что большинство заголовков расширения имеет отношение только к IPv6 и лишено смысла в среде IPv4. Заголовки IPsec — важное исключение из этого.
Собрав воедино сказанное в этом разделе, мы получаем структуру пакета IPv6, изображенную на рис. 3.5.
(рис 3.5) Окончательная структура пакета IPv6 (число заголовков расширения переменно)
Итак, каждый пакет IPv6 начинается с заголовка IPv6 (неожиданно, правда?) Но теперь, в отличие от IPv4, все необязательные элементы мы можем перенести из основного заголовка в заголовки расширения. Без каких полей мы абсолютно не сможем обойтись в основном заголовке? Используя наш опыт IPv4, а также не забывая сказанное в §1 и §3.1, составим предварительный неупорядоченный список:
Теперь давайте рассмотрим подробнее эти поля и, если понадобится, скорректируем их свойства.
Целочисленные поля, "созданные" нами по ходу нашей работы, будут по умолчанию в беззнаковом коде. Как вы думаете, почему отрицательные и дробные числа редко встречаются в сетевых протоколах?
Как мы уже обсудили в §1, поле версии IP поможет надежно отличить пакет IPv6 от пакета IPv4 в переходных условиях, пока новая версия будет работать наряду со старой. Учитывая масштабы применения IPv4 и финансовые вложения в технологии на его основе, этот переходный период может растянуться на многие годы. Очевидно, что длина и позиция поля версии обязаны быть такими же, как в заголовке IPv4. То есть длина этого поля — 4 бита, а находится оно в самом начале заголовка (учитывая сетевой порядок битов и байтов). Теперь его новое значение — 6.
Также нам никуда не деться без двух адресов IPv6, источника и назначения. Вместе они занимают 256 бит, или 32 байта, что весьма немало. Не надо ли нам попытаться сжать их двоичное представление путем какого-то кодирования, подобно тому как мы поступили с текстовым представлением? Нет, делать этого ни в коем случае не стоит; иначе ради выигрыша в несколько байтов мы пожертвуем простотой заголовка и затрудним его обработку, особенно на аппаратном уровне. Что мы еще должны сказать об этих адресах? Прежде всего, у пакета ровно один интерфейс источника, и поэтому адрес источника не может быть групповым. Если узел принял пакет с групповым адресом источника, его необходимо отбросить, никак не реагируя на него, потому что любая реакция по групповому адресу может открыть лазейку для атаки типа "отказ в обслуживании" против других узлов. Далее, адрес обратной связи ::1 можно использовать только в трафике через интерфейс типа "петля", будь то адрес источника или назначения, — прийти в пакете извне узла он не имеет права. Прочие требования к адресам в заголовке IPv6 мы сформулируем по ходу работы.
Кстати, именно благодаря тому, что адрес источника в пакете IP не бывает групповым, мы можем говорить о типе всего пакета в зависимости от типа его адреса назначения, как то "индивидуальный пакет" и "групповой пакет".
Без поля TTL или аналогичного ему возникает риск бесконечного зацикливания пакетов в сети. При неудачном стечении обстоятельств это может также сопровождаться дублированием пакетов, что означает экспоненциальный рост паразитного трафика и полный крах сети. Так что без эквивалента TTL нам никак не обойтись. Однако, перенося поле TTL в заголовок IPv6, мы внесем в его свойства долгожданную поправку. Как показал опыт работы с IPv4 [§5.3.1 RFC 1812], измерить время удержания транзитного пакета на узле затруднительно и, к тому же, оно редко превышает одну секунду. Де-факто поле TTL ведет обратный отсчет не секунд жизни пакета, а его шагов по транзитным узлам. Мы не можем не учесть этот опыт в IPv6, и нам остается только переименовать поле сообразно его функции: "предельное число шагов" (Hop Limit). Длина его по-прежнему будет 8 бит. По смыслу это поле не может быть дробным или отрицательным, поэтому оно целое беззнаковое.
По первоначальному замыслу, поле TTL в IPv4 отсчитывало именно время, а не шаги, чтобы заодно управлять интервалами таймеров на узле назначения. Так, в первом эталонном алгоритме сборки пакета IPv4 из фрагментов интервал таймера сборки был функцией от остаточного значения TTL [§3.2 RFC 791]. Однако впоследствии от этого подхода отказались в пользу других методов. В частности, значение тайм-аута сборки стало постоянным [§3.3.2 RFC 1122].
Правила работы с полем "предельное число шагов" [§3 RFC 2460] не отличаются от общепринятой практики IPv4:
конечный адресат пакета игнорирует это поле.
В каких комментариях нуждаются эти простые требования?
Поле длины пакета необходимо на тот случай, если канальный уровень не сохраняет точную длину полезной нагрузки кадра. Хрестоматийный пример — это Ethernet, где длина полезной нагрузки не может быть ниже 46 байт из-за наследия CSMA/CD. Другой возможный пример — это канальный протокол, длина полезной нагрузки в котором должна быть кратна, скажем, восьми байтам.
Подобное ограничение можно встретить в защищенных протоколах с блочным шифрованием, но оно не всегда заметно их потребителям. Например, подсистема шифрования PPP маскирует ее, сохраняя и восстанавливая точную длину полезной нагрузки с помощью особой набивки, в которой закодирована информация о числе добавленных в конец полезной нагрузки байтов [§6.1 RFC 2419]. Располагая такой информацией, приемник PPP может точно "срезать" набивку без вреда для полезной нагрузки.
Трасса пакета заранее неизвестна, так что поле длины потребуется в каждом пакете — ведь никто знает, сквозь какие каналы ему придется пройти. Пусть единицей измерения длины пакета остается байт, а пакет состоит из целого числа байтов; ведь это очень удобно, учитывая ориентацию всей технологии обработки данных на байты.
Хотя исторически байт не всегда был одной и той же длины, и потому возник альтернативный термин "октет", обозначавший именно цепочку из 8 бит, по состоянию на сегодняшний день это полные синонимы. Мы предпочитаем термин "байт", так как он понятен большей аудитории, а "октет" уже звучит старомодно, почти как французские реплики героев Льва Толстого.
При целом беззнаковом кодировании полю длины вполне хватит 16 битов, так как, с одной стороны, существующие канальные протоколы не поддерживают MTU больше $$ \sim2^{16}$$ байт, а с другой стороны, транспортные протоколы TCP и UDP рассчитаны на максимальную длину пакета $$ \sim2^{16}$$ байт. Кроме того, чем длиннее пакет, тем выше вероятность его повреждения при передаче. В этом-то и состоит работа пакетных сетей, чтобы даже огромные порции данных передавать в виде пакетов разумного размера.
А в чем же заключается зависимость транспортных протоколов от максимальной длины пакета? Так, в заголовке UDP есть поле длины, и оно длиной 16 бит. Зависимость TCP не столь очевидна, поскольку TCP не хранит длину сегмента, полагаясь на IP в этом вопросе. Единственное ее проявление в заголовке TCP — это неотложный указатель (urgent pointer) длиной 16 бит, так что неотложные данные не смогут простираться далее $$ \sim2^{16}$$ байт от начала сегмента. Однако сама длина сегмента не может превышать 65535 байт ввиду 16 битного поля MSS в одноименной опции.
Тем не менее, RFC 2675 предлагает опцию IPv6 "пакет-слон" (jumbogram), расширяющую поле длины до 32 бит. Тот же RFC обсуждает препятствия на пути к применению этой опции совместно с TCP и UDP. Эталонная реализация IPv6 KAME на всякий случай поддерживает опцию "пакет-слон".
Опций в пакете вполне может не быть, так что мы их отправим в необязательные заголовки расширения. В результате число и длина полей в заголовке IPv6 оказываются строго постоянными.
Мы уже заняли совершенно необходимыми полями 36,5 байта. Чтобы помочь современным вычислительным системам, давайте выровняем конец заголовка IPv6 на границу 32 битного слова. Мы не можем выбросить из заголовка даже полбайта, так что придется нарастить заголовок до 40 байт. Тогда конец заголовка IPv6 окажется выровнен на границу 64 битного слова, от чего особенно выиграют 64-разрядные платформы. После выравнивания у нас образуются незанятые три с половиной байта.
Давайте уступим моде на управление качеством обслуживания (QoS) и отдадим свободные разряды полям "класс трафика" (8 бит) и "метка потока" (20 бит). Первое из них аналогично полю TOS в IPv4, а второе позволяет, пометив последовательность пакетов одним и тем же значением, превратить их в единый объект политики обслуживания (поток). Уточнять подробности применения этих полей мы сейчас не будем.
Как мы помним, сегодня общепринятая интерпретация поля TOS основана на DiffServ [RFC 2474] и ECN [RFC 3168]. То же самое касается и поля "класс трафика".
Общие правила работы с полем "метка потока" изложены в [RFC 6437]. Одно из перспективных направлений его возможного применения — это оптимизация распределения нагрузки между каналами [RFC 6438] и серверами [draft-carpenter-v6ops-label-balance].
Мы вовсе не хотели сказать, что управление качеством обслуживания в сети — это фикция или шарлатанство. Однако нельзя не признать, что слишком часто за него берутся именно из-за моды, без должного понимания его причин, законов и механизмов. По иронии, противники QoS зачастую столь же невинны, как и их оппоненты.
И, наконец, последний штрих: раз длина заголовка IPv6 всегда составляет 40 байт, то стоит ли включать эту величину в значение поля длины пакета? Пусть она входит туда неявно, и тогда максимальный пакет сможет стать длиннее на 40 байт. Так что давайте переименуем это поле в "длину полезной нагрузки" (Payload Length). В данном контексте "полезная нагрузка" — это пакет за вычетом основного заголовка IPv6; заголовки расширения входят в "полезную нагрузку". Максимальная длина такой "полезной нагрузки" — 65535 байт, а всего пакета — на 40 байт больше, то есть 65575 байт. Это ограничение чисто теоретическое, оно следует из того, как мы кодируем поле длины; об ограничениях практического характера мы поговорим в §6.5.
Сканируя заголовки, реализация IPv6 обязана следить, чтобы их цепочка не вышла за границы полезной нагрузки. Если байты полезной нагрузки уже исчерпаны, а заголовки еще не закончились, то пакет, очевидно, поврежден.
Теперь давайте составим полный, но еще неупорядоченный список полей в заголовке IPv6:
В каком порядке их лучше всего разместить? В современных вычислительных системах надлежащее выравнивание данных значительно повышает скорость доступа к ним, так что начать анализ нам надо с самых востребованных полей, а именно с адресов. В идеале, их надо было бы выровнять на границу 128 битного слова; однако тогда нам пришлось бы поместить их в самое начало заголовка, а эта позиция уже прочно занята коротеньким полем "версия IP". Значит, нам придется довольствоваться границей 64 битного слова, проходящей в заголовке по смещению 8 байт; то есть адреса отправляются в конец заголовка. Далее, поле "длина полезной нагрузки" следует выровнять на границу 16 битного слова. Для этого достаточно поместить между ним и полем "версия IP" поля качества обслуживания. Наконец, в зазор между полем "длина полезной нагрузки" и адресами умещаются поля "следующий заголовок" и "предельное число шагов". Вот мы и получили полный формат заголовка IPv6 [§3 RFC 2460], показанный на рис. 3.6.
(рис 3.6) Основной заголовок IPv6
Какие поля, знакомые нам по IPv4, не вошли в заголовок IPv6? Во-первых, больше не нужна длина заголовка: она стала постоянной величиной 40 байт. Заголовок IPv4 был переменной длины, так как включал в себя опции; теперь же опции IPv6 займут отдельный заголовок расширения (или даже несколько таких заголовков — см. §3.3.2 и §3.3.6).
Во-вторых, исчезли все поля, относящиеся к фрагментации: идентификатор, смещение, флаги. Так как большинство пакетов при оптимальной работе сети не фрагментировано, эти поля стоит вынести в собственный заголовок расширения. О факте фрагментации будет говорить само наличие такого заголовка. А как же флаг DF, запрещающий фрагментацию? Вопрос резонный; о судьбе флага DF мы скажем в §3.3.4 и §6.5.
В-третьих, совсем пропала контрольная сумма заголовка. Это решение вызвано несколькими причинам. Прежде всего, модульная структура заголовков в пакете IPv6 размывает границу между их уровнями: если конечный адресат пакета еще может разобрать все заголовки сетевого уровня и определить, где начинаются данные следующего уровня, например, TCP, то транзитный узел не может и не должен делать этого. Тогда контрольная сумма уже не может защищать все заголовки сетевого уровня, и область ее покрытия придется ограничить только основным заголовком IPv6. Так само ее существование в значительной мере теряет смысл. Затем, современные канальные технологии сами обеспечивают пошаговую защиту пакетов от повреждения, а протоколы транспортного уровня защищают важнейшие поля заголовка IP сквозным образом. Благодаря этому, собственная роль контрольной суммы IP не так уж велика. И, наконец, отказ от контрольной суммы IP ускоряет аппаратное продвижение пакетов и делает проще соответствующие интегральные схемы.
Пусть читатель изобразит форматы заголовков IPv4 и IPv6, а затем раскрасит их поля в зависимости от того, сохранилось ли поле при смене версии IP, исчезло или возникло. Еще одним упражнением будет выяснить, какие поля заголовка окажутся повреждены, если модуль маршрутизатора "забудет" проверить поле "версия" и обработает пакет IPv6, как если бы это был пакет IPv4. Рассмотрите два конкретных сценария: а) декремент поля TTL перед продвижением пакета; б) перезапись значения DSCP правилом QoS. (Не забудьте, что в обоих случаях также произойдет пересчет контрольной суммы заголовка.) Затем определите, какие поля заголовка будут повреждены, если пакет IPv4 будет по ошибке обработан согласно формату заголовка IPv6. При этом ограничьтесь одним сценарием: декремент поля "предельное число шагов".
Представим себе пакет IPv6, который состоит из одного основного заголовка. Хотя нам не совсем ясно, зачем такой пакет мог бы понадобиться, формального повода запретить его у нас тоже нет.
Обсуждая заголовки IPsec в §3.3.5, мы найдем применение даже такому вот "пустому" пакету: обеспечение конфиденциальности на уровне потока пакетов.
Однако само существование пакета без полезной нагрузки поднимает любопытный вопрос: чему в нем должно быть равно значение поля "следующий заголовок"? Мы могли бы сказать, что оно неважно, потому что длина полезной нагрузки равна нулю; но зачем нам лишняя неоднозначность? Чтобы не провоцировать разночтения стандарта и несовместимость реализаций, давайте назначим этому случаю вполне определенный код из реестра протоколов IP. Это код 59, "следующего заголовка нет" (No Next Header) [§4.7 RFC 2460]. Таким кодом можно завершить и цепочку заголовков расширения, если за ней в пакете ничего нет, как это показано на рис. 3.7.
(рис 3.7) Пакет без полезной нагрузки
Теперь представим себе, что получатель сканирует пакет и обнаруживает заголовок со значением 59 в поле "следующий заголовок", а затем еще какие-то байты (об этом скажет длина полезной нагрузки IPv6). Хотя такая ситуация неоднозначна, в ней нет явного криминала: она не ведет к осложнениям или опасным последствиям, если обработать ее правильно. А принцип Постела подсказывает, как именно следует поступить: пусть транзитные узлы сохраняют эти концевые байты, а конечный адресат игнорирует их [§4.7 RFC 2460].
Напомним себе современную трактовку принципа Постела (Postel’s principle), также известного как принцип устойчивости (robustness principle). Основоположник Internet Джонатан Постел (Jonathan Postel) сформулировал этот принцип одной лаконичной фразой: "Будь консервативен в собственных действиях и либерален к тому, что принимаешь от других" [§2.10 RFC 793]. Сегодня мы добавим к этому: "…но никогда не забывай о безопасности". Этот принцип требовательности к себе и терпимости к другим — один из главных в TCP/IP. Если сторона протокола следует ему, она не только соблюдает протокол, но также приобретает гибкость и устойчивость к неполадкам, а эти качества особенно важны, когда разработчики протокола не могут заранее предусмотреть все варианты развития событий.
Мы предварительно отметили в §3.1, что опции можно поделить на пошаговые и актуальные только для адресата. Требования к обработке этих видов опций существенно разнятся. Во-первых, маршрутизаторы в целях производительности должны сканировать только пошаговые опции. Во-вторых, когда у IP появятся собственные механизмы сквозной защиты, опции адресата можно будет шифровать. В-третьих, при фрагментации пакета пошаговые опции надо целиком копировать в каждый фрагмент, тогда как опции адресата достаточно привести один раз и даже можно разбить на части, если они окажутся слишком длинными и пересекут границы одного фрагмента.
Пока что мы лишь излагаем общие соображения, а точные правила фрагментации заголовков мы сформулируем, когда будем готовы к этому, в §3.3.4.
По всей видимости, каждый вид опций заслуживает собственного заголовка расширения. Форматы этих заголовков могут полностью совпадать, но им будут отвечать разные коды "следующий заголовок". Так, заголовок опций адресата получает код 60 [§4.6 RFC 2460].
Заголовок пошаговых опций, ввиду своей особой роли, получает отличительный код 0 [§4.3 RFC 2460] и позицию непосредственно после заголовка IPv6. Иными словами, код 0 может встретиться только в поле "следующий заголовок" основного заголовка IPv6. Если же пакет составлен правильно, но первое поле "следующий заголовок" ненулевое, то в пакете пошаговых опций нет. С помощью этой простой логики маршрутизаторы могут быстро обнаруживать пошаговые опции в транзитных пакетах.
Пусть по своему формату заголовки опций будут контейнерами, в которые можно вложить больше одной опции. Если для отдельных опций мы применим кодирование TLV, то в фиксированной части заголовка нам потребуются только поля "следующий заголовок" и "длина" — ведь число опций может быть переменным.
Будь у нас такое желание, мы могли бы обойтись без поля длины за счет подходящего кодирования опций. Но благодаря полю длины мы сможем просто и однозначно указать конец списка опций, так что давайте не будем создавать себе трудности. Кроме того, поле длины позволяет "перескочить" через заголовок опций, не сканируя его.
Поле "следующий заголовок" занимает один байт. Пусть поле "длина" тоже займет один байт, и тогда действительное начало опций будет выровнено на границу 16 битного слова. Код обоих полей будет целый беззнаковый. Но не слишком ли мало 255 байт для всего списка опций? Удлинить его можно, если измерять длину не в байтах, а в больших единицах. Заодно так мы выровняем конец заголовка опций. Конец заголовка IPv6 выровнен на границу 64 битного слова, и большего выравнивания мы уже не достигнем. Поэтому пусть длина заголовка опций измеряется в 8 байтных единицах. Еще заметим, что длина такого заголовка не меньше единицы из-за обязательных полей. Эту единицу можно учесть неявно и выиграть еще 8 байт. То есть поле "длина" заголовка опций ведет учет 8 байтных отрезков, начиная со второго. Тогда длина всего заголовка в байтах может меняться от 8 до 2048 с шагом 8 байт. Окончательный формат заголовка опций показан на рис. 3.8.
(рис 3.8) Заголовок опций IPv6
Сами опции мы станем кодировать широко известным способом TLV: поле типа, поле длины, данные, — как показано на рис. 3.9. Поля типа и длины данных занимают по одному байту. Длина данных измеряется в байтах и, по традиции IPv6, не включает фиксированные поля типа и длины.
(рис 3.9) Общий вид опции IPv6
Когда получатель пакета сканирует список опций, то он вполне может встретить незнакомую опцию. Благодаря одинаковому кодированию TLV, через опцию-незнакомку легко "перескочить"; но где гарантия, что ее можно безопасно игнорировать? Чтобы избежать этой неоднозначности, давайте сразу же закодируем требуемое поведение в старших двух битах типа опции, как показано в рис. 3.1.
| Биты | Предписания |
|---|---|
| 00 | "Перескочить" через незнакомую опцию и продолжить обработку заголовка |
| 01 | Уничтожить пакет; не высылать никаких извещений |
| 10 | Уничтожить пакет; выслать извещение "ICMP версии 6" (независимо от адреса назначения исходного пакета) |
| 11 | Уничтожить пакет; выслать извещение "ICMP версии 6", только если адрес назначения исходного пакета не групповой |
Еще одна трудность возникнет, когда на уровне IP появится сквозной механизм защиты пакетов от умышленной модификации. Сможет ли он защищать опции? Если транзитные узлы модифицируют защищенную опцию, то конечному адресату пакет покажется подложным. Напротив, если опция по своему определению не изменяется в пути, то она вполне подлежит защите. Отражает это свойство опции третий по старшинству бит ее типа: если он сброшен, то опция заведомо не меняется по пути, а если установлен, то может меняться. Теперь механизму защиты не нужно знать свойства отдельных опций — ему достаточно проверить этот бит в поле типа, чтобы узнать, можно ли защищать данную опцию.
Например, реакция механизма защиты IPsec AH [RFC 4302] выглядит так: если данный бит установлен, то механизм подставляет вместо данных опции нулевые байты, когда вычисляет для пакета его защитную цепочку. Таким образом, под защитой AH оказываются хотя бы код и значение длины, а также фактическая длина опции. Предполагается, что длина опции не меняется, а алгоритм защиты чувствителен к числу нулевых байтов, идущих подряд.
(рис 3.10) Кодирование свойств опции в ее типе
В трех старших битах типа (см. рис. 3.10) закодированы неотъемлемые свойства каждой опции, и посему эти биты нельзя рассматривать
как отдельное поле. Когда новой опции назначают численное значение типа, то в нем сразу же учитывают ее свойства. Например, если
мы увидим опцию с десятичным кодом 95, то
Если же мы, к примеру, инвертируем в этом коде бит "меняется по пути", то получим совершенно другой тип опции, никак не связанный с первоначальным.
Вычислите десятичное значение кода новой опции. (127)
О поле типа опции осталось сказать, что пошаговые опции и опции адресата хранятся в пакете раздельно, но получают численные коды
из одного и того же
Суммарная длина списка опций может быть любой с точностью до байта, тогда как заголовок опций должен занимать число байт, кратное восьми, и оставляет байт на, собственно, опции. Кроме того, для некоторых опций может быть желательно то или иное выравнивание относительно начала заголовка. Чтобы удовлетворить эти требования, давайте "создадим" две опции-пустышки, обязательных к реализации и опознанию.
Первая из них, Pad1, в виде исключения займет ровно один байт, к чему и сводится вся ее функция (см. рис. 3.11). Формально тип опции Pad1 — 0, но она нарушает формат TLV: в ней нет полей, кроме поля типа.
(рис 3.11) Опция Pad1
Вторая же опция, PadN, вполне отвечает формату TLV (см. рис. 3.12). Ее тип — 1, и она может занять от 2 до 257 байт. Если ее поле "длина" больше нуля, то пусть поле данных ради единообразия будет заполнено нулевыми байтами.
(рис 3.12) Опция PadN
Согласно своей роли, опции Pad1 и PadN могут встречаться в заголовке опций несколько раз и располагаться в начале заголовка, в его конце, или же перемежать другие опции. Они могут возникать и среди пошаговых опций, и среди опций адресата.
Проанализируйте свойства опций Pad1 и PadN, закодированные в старших битах их типов.
Все остальные опции IPv6 — суть расширения базового протокола; их технические условия приведены в отдельных документах RFC. Поэтому сейчас мы их рассматривать не будем, но надеемся, что теперь читатель сможет разобраться в них самостоятельно, когда ему это понадобится.
В §6.4 нам встретится опция "сигнал маршрутизатору".
Важное следствие сквозной модели, принятой в Internet, состоит в том, что конечные узлы явным образом не участвуют в маршрутизации пакетов. Источник создает пакет IP и отправляет его в сеть, а та уже сама выбирает подходящую трассу, чтобы пакет дошел до адресата. Конечно, инженер понимает, что простирающаяся между конечными узлами сеть состоит из каналов и маршрутизаторов, работающих по вполне определенным правилам, однако узлу-источнику это неинтересно; все, что от него требуется, — это создать пакет и выбрать для него первый шаг. В этой модели источник управляет дальнейшей маршрутизацией пакета только неявно, устанавливая в нем адрес назначения.
В общем случае маршрутизаторы IP могут рассматривать и другие поля заголовка и параметры пакета, например, класс обслуживания или полную длину. Это естественное обобщение простого алгоритма маршрутизации, ответ которого (следующий шаг пакета) зависит только от адреса назначения. Такую обобщенную маршрутизацию часто называют маршрутизация согласно политике (policy routing). Тем не менее, и в этом случае источник пакета влияет на его трассу только неявно.
Тем не менее, еще давным-давно разработчики IPv4 подумали: а вдруг источник иногда бывает осведомлен лучше сети? Например, из-за сбоя или атаки протокол маршрутизации распространил ложные сведения, и маршрутизаторы направляют пакеты не туда, куда надо. Тогда источник мог бы подсказать им верную трассу для своих пакетов, включив эту информацию в заголовок IP. Так появились опции IPv4 LSRR (нестрогая маршрутизация от источника) и SSRR (строгая маршрутизация от источника) [§3.1 RFC 791].
Если обратиться к
Как мы помним, суть работы SRR была довольно простой. Допустим, источник И хотел направить пакет конечному адресату К через транзитные узлы T1, Т2, Т3,…ТN. Тогда он составлял заголовок пакета так:
устанавливал указатель опции SRR так, чтобы тот указывал на первый адрес в списке, то есть Т2.
В результате узел Т1 значился адресатом пакета и поэтому получал его первым. Он поступал с заголовком так:
Далее пакет приходил узлу Т2, и тот действовал аналогичным образом… И так далее, пока пакет не добирался наконец до узла К. Разница между строгим и нестрогим режимами состояла в том, мог ли пакет посещать транзитные узлы вне заданного источником списка (в нестрогом мог, а в строгом не мог).
Сегодня мы спросили бы разработчиков этого механизма: а почему сеть должна настолько доверять источнику? Действительно, злонамеренный источник вполне может использовать опции SRR, по меньшей мере, для двух типов атак:
Пусть читатель сам вычислит максимально достижимый коэффициент усиления атаки $$N_{max}$$ в IPv4.
Решение: Максимальная длина заголовка IPv4 — 60 байт (4-битное поле, 4-байтная единица длины). Из них 20 байт занимает обязательная часть, так что на опции остается самое большее 40 байт. Заголовок LSRR занимает 3 байта, но наименьшее допустимое значение смещения — 4 байта [§3.1 RFC 791], поэтому список адресов занимает до 36 байт, а это означает 9 адресов IPv4. Еще один адрес — это адрес назначения в основном заголовке. Так что каждый из адресов А и Б может встретиться самое большее 5 раз. Это и будет искомое значение $$N_{max}$$
Поэтому маршрутизация от источника в глобальной сети приносит больше вреда, чем пользы, и было бы неразумно повторить в IPv6 ошибки IPv4. Из этого затруднения мы выкрутимся вот как: давайте в самом общем виде зададим гибкий формат заголовка, управляющего маршрутизацией пакета IPv6, а всю конкретику оставим на усмотрение дополнительных протоколов. Тогда, в случае чего, нас винить будет не в чем.
Искомый заголовок расширения IPv6 мы назовем маршрутный заголовок (routing header) [§4.4 RFC 2460]. Ему отвечает значение "следующий заголовок" 43 — разумеется, в предшествующем ему заголовке, а не в нем самом.
Так как длина маршрутной информации заранее неизвестна, маршрутный заголовок будет переменной длины. Значит, помимо обязательного поля "следующий заголовок", маршрутному заголовку потребуется поле длины. По аналогии с заголовками опций из §3.3.2, пусть это поле длиной один байт хранит длину в 8-байтных словах, причем первое слово не учитывается, так как оно всегда присутствует и частично занято фиксированной частью заголовка.
Далее мы поместим поле маршрутного типа, которое и определит, как надо трактовать дальнейшие данные в этом заголовке. Ему будет
вполне достаточно одного байта. Конечно же, для общепринятых значений этого поля понадобится
Как быть получателю пакета, если он встретил неизвестный маршрутный тип? Придется ли ему безусловно отбросить этот пакет? Ответить на этот вопрос нам поможет опыт маршрутизации от источника IPv4. (Да, учиться можно и на ошибках!) Когда список транзитных узлов в опции SRR исчерпан и пакет направляется к конечному адресату К, опция SRR отработала и уже не имеет значения для маршрутизации пакета. В этом случае опцию SRR можно игнорировать.
Чтобы перенести эту схему в маршрутный заголовок IPv6, нам придется принять определенную модель его данных. Пусть они состоят из последовательности сегментов, где каждый сегмент может быть, например, адресом транзитного узла. Как обозначить в этой модели, что все сегменты уже обработаны и дальнейшие получатели пакета могут игнорировать маршрутный заголовок, независимо от его типа? Вот одно из возможных решений: хранить в фиксированной части маршрутного заголовка число еще необработанных сегментов. Когда значение этого поля упадет до нуля, получатель пакета надежно определит, что маршрутный заголовок уже отработал свое, даже если получатель не знаком с этим маршрутным типом и не знает, как устроен его сегмент. Если же это поле ненулевое, то пакет с незнакомым типом маршрутного заголовка придется отбросить.
В результате мы приходим к формату маршрутного заголовка, изображенному на рис. 3.13.
(рис 3.13) Маршрутный заголовок
Из уже существующих маршрутных типов мы упомянем типы 0 (т.н. RH0 — Routing Header 0) и 2 (RH2). RH0 — это прямой аналог LSRR. Неудивительно, что в конце концов его упразднили ради безопасности [RFC 5095]. Об RH2 см. примечание в конце раздела.
Одно отличие RH0 от LSRR состояло в том, что в текущий сегмент RH0 переносился предыдущий адрес назначения из основного заголовка IPv6, а не адрес выходного интерфейса. Таким образом, в RH0 достаточно было поменять местами адрес назначения и адрес из текущего сегмента, а затем провести декремент счетчика "осталось сегментов". Как следствие, в RH0 происходит простой циклический сдвиг списка адресов, тогда как опция LSRR записывала фактические интерфейсы, в которые продвигался пакет. Точно так же поступала опция SSRR, а нужно это было именно для нее, чтобы потом можно было послать ответный пакет с записанным строгим маршрутом от источника.
При должной политике безопасности на границах опорной сети, запрещающей проникновение сомнительных RH0 извне, внутри такой
сети RH0 мог бы стать ценным инструментом для обнаружения скрытых дефектов. Ведь с его помощью можно было бы из одной точки
проверять разные пути сквозь сеть, включая неактивные в данный момент, запасные. Корреляция результатов подобных проверок
вдоль множества путей позволяет довольно точно определить, где локализован
Существование маршрутного заголовка IPv6 заставляет нас пересмотреть такое понятие, как адресат пакета. До сих пор мы полагали, что у пакета ровно один адресат, хотя он может быть индивидуальным узлом или группой. Теперь же нам придется различать два типа адресатов. Если в пакете вообще нет маршрутного заголовка или же он есть, но его поле "осталось сегментов" равно нулю, то в основном заголовке IPv6 записан конечный адресат пакета. Это узел, который поглотит пакет и обработает его полезную нагрузку. Если же в маршрутном заголовке все еще есть активные сегменты, то в основном заголовке IPv6 значится текущий адресат пакета. Этот узел примет пакет, только чтобы сменить в нем адрес назначения и передать дальше; он будет транзитным на пути пакета. При этом конечный адрес назначения пакета скрыт в сегменте маршрутного заголовка.
Вообще говоря, ни один из адресов назначения в пакете с маршрутным заголовком не должен быть групповым. Это следует из соображений безопасности.
Нужны ли дополнительные правила, если у адреса назначения в маршрутном заголовке область действия не глобальная? На самом деле, нет, достаточно уже знакомого нам принципа изоляции зон из §2.4. Однако, строго говоря, этот принцип должен выполняться — а значит, и проверяться — для всех адресов назначения одновременно. В противном случае возможна атака засылкой пакета в удаленную зону. Например, если текущий адресат пакета глобальный, а конечный адресат внутриканальный, то произойдет засылка внутриканального пакета в канал, не подключенный непосредственно к источнику пакета.
Нормативный документ [§9 RFC 4007] требует, чтобы следующий адрес назначения из маршрутного заголовка имел область не меньшей величины, чем у текущего адреса назначения в основном заголовке IPv6. Пока принцип изоляции зон выполняется и все интерфейсы назначения пакета находятся в зоне его адреса источника, это требование избыточно. К примеру, вполне возможна "эстафета" по адресам, область которых сужается, если все они назначены интерфейсам в тот же канал, где находится интерфейс-источник.
Маршрутизация от источника обрела новую жизнь в MPLS-TE, где вполне можно задать трассу LSP по узлам, перечислив их адреса в ERO. Конечно, этот механизм выгодно отличается от RH0 в IPv6 в плане безопасности, так как пограничный узел (LER), устанавливающий LSP, — заведомо доверенный, в отличие от абстрактного источника IPv6. Однако на поверку MPLS не столь уж независим от IP: не имея собственной адресации, он опирается в этом на традиционный протокол сетевого уровня, и обычно это IP. Поэтому, если узлам MPLS назначены зонные адреса IPv6, то ERO подчиняется все тому же принципу изоляции зон! А именно входной LER должен иметь возможность адресовать каждый узел по трассе LSP, иначе ERO будет лишен смысла. В частности, именно поэтому нельзя проложить трассу LSP через несколько каналов, используя только внутриканальные адреса: для составления ERO потребуются адреса большей области, по крайней мере, начиная со второго узла вдоль трассы. Это в высшей степени любопытно, что наши разработки для маршрутизации от источника IPv6 также применимы к MPLS!
Как мы знаем, узел может адресовать пакеты IP самому себе. Точно так же, текущий и конечный адреса назначения могут принадлежать одному и тому же узлу. Но зачем это нужно? Например, чтобы текущий адрес назначения обладал свойствами локатора, а конечный — идентификатора. Это полезно, если узел мобильный и перемещается по Internet. Текущий адрес мобильного узла M в каждый момент времени доступен обычным порядком, с помощью маршрутизации, и может меняться, когда узел меняет свое местоположение. В то же время, его постоянный адрес D, с которым работают вышестоящие протоколы, доступен только посредством маршрутного заголовка. Тогда перемещения узла будут прозрачны для протоколов уровня выше IP, как показано на рис. 3.14. Очевидно, что такой маршрутный заголовок может содержать ровно один сегмент, причем адрес в нем обязан совпасть с постоянным адресом текущего адресата D. В противном случае налицо сбой или нарушение безопасности. Даная разновидность маршрутного заголовка получила маршрутный код 2 и потому известна как RH2. Подробнее с ней можно познакомиться в RFC 3775, посвященном мобильности узлов IPv6.
(рис 3.14) Принцип работы RH2
Как мы помним, в IPv4 модуль фрагментации был обязательным компонентом стека и в хостах, и в маршрутизаторах, потому что фрагментировать длинный пакет должен был узел, стоящий непосредственно на входе в канал с низким MTU. Это усложняло логику маршрутизаторов IPv4: они не могли рассматривать транзитный пакет как элементарную единицу обмена данными, которую надо или целиком продвинуть дальше, или отбросить. Кроме того, нагрузить маршрутизаторы фрагментацией было не самой лучшей мыслью, потому что они обрабатывают трафик многих хостов, а сами хосты обслуживают только собственные нужды (по крайней мере, на сетевом уровне стека).
Вспоминается пословица: "На одного с сошкой семеро с ложкой".
Теперь, когда у нас есть приятная возможность перевернуть все вверх дном, мы можем "примерить" на каркас IPv6 альтернативные правила фрагментации:
Так вся работа по фрагментации ложится на источники пакетов, что не только справедливо, но и выгодно с точки зрения производительности сети.
Но не стоит сомневаться, что новый подход к фрагментации создаст дополнительные проблемы и поставит неожиданные вопросы на уровне работы протокола. Что ж, от этого никуда не уйти, такова цена прогресса. Нам остается только проанализировать возникшие затруднения и найти решения для них.
Если длина пакета L (цельного или фрагмента) превращается в сквозную характеристику
(1)$$ L\leq PMTU $$
Здесь PMTU — MTU трассы от данного источника до конечного адресата пакета:
(2)$$PMTU=\min_{i} MTU_i$$
где индекс i пробегает все выходные интерфейсы этой трассы.
В новых условиях сквозную величину L задает источник пакета, так что выполнение условия (1) становится его личной заботой, а это связано с поиском значения PMTU, или хотя бы его оценки снизу $$\underline{PMTU}$$:
(3)$$L\leq \underline{PMTU}\leq PMTU \Rightarrow L\leq PMTU$$.
Простые арифметические соображения подсказывают нам, что есть две стратегии выполнения условия (3). Первая из них состоит в том, чтобы источник приложил усилия и нашел хорошее приближение PMTU, вплоть до точного значения. Для этого существует процедура PMTUD. Чтобы она работала, источник должен получать извещения, когда его пакеты уничтожаются из-за чрезмерной длины. В IPv4 эту роль выполняли сообщения ICMP "пакет слишком велик"; подобный механизм нам понадобится и в IPv6. Сразу же заметим, что PMTUD сходится быстрее, если каждое извещение подсказывает очередную величину )$$MTU_i $$: узел, высылающий это извещение, включает в него значение MTU того интерфейса, куда вышел бы пакет, если бы он не оказался слишком длинным. В IPv4 это было необязательным расширением [§4 RFC 1191], но теперь нам стоит ввести это в стандарт — отметим себе на будущее (§4.3). Далее, PMTUD предполагает, что протокол более высокого уровня устойчив или безразличен к потере пакетов. Это условие не ново, потому что пакеты в сетях TCP/IP теряются по самым разным причинам. Наконец, PMTUD работает надежно только при том условии, что хотя бы часть извещений доходит до источника, или же что вышестоящий протокол предполагает отклик удаленной стороны. В противном случае возникает "черная дыра PMTUD", куда бесследно, с точки зрения источника, пропадают длинные пакеты.
Для образования классической "черной дыры PMTUD" достаточно блокировать извещения ICMP, однако для двунаправленного протокола не все еще потеряно. Отклик удаленной стороны (точнее, его неожиданное отсутствие) позволяет обнаружить и обойти "черную дыру", уменьшив длину пакета. Если же вышестоящий протокол однонаправленный, такой как Syslog или Netflow, то "черная дыра" станет поглощать пакеты до тех пор, пока извещения ICMP не будут разблокированы.
Вторая стратегия значительно проще и потому подходит для источников, которые не хотят или не могут обременять себя процедурой PMTUD. Она заключается в том, чтобы использовать минимальное значение MTU, указанное протоколом IP, как "габаритные ворота" для исходящих пакетов и их фрагментов:
(4)$$ L\leq MTU_{min} $$ (4)
Этот трюк работает, потому что минимальное значение MTU служит оценкой снизу для MTU любой трассы, пока не нарушен протокол:
(5)$$\forall i, MTU_{min}\leq MTU_i \Rightarrow MTU_{min}\leq PMTU$$. (5)
Чтобы при этом не страдала эффективность передачи данных, минимальное значение MTU должно быть разумно большим. Как мы помним, в IPv4 это была микроскопическая величина 68 байт. Теперь же, в IPv6, MTU канала не может опуститься ниже 1280 байт [§5 RFC 2460].
Мы точно не знаем, почему была выбрана именно величина 1280 байт. Из ее очевидных численных свойств можно заметить разве что такое: $$1280=2^10+2^8$$. Но практически важное свойство данного выбора состоит в том, что он оставляет весьма приличный допуск на многоуровневую инкапсуляцию, например, туннели, при распространенном значении MTU физического канала 1500 байт.
А как нам быть с каналом, который по своему устройству неспособен переместить пакет длиной 1280 байт? (Например, см. [RFC 4919]. ) Пусть расширение эффективного MTU такого канала до требуемой величины, например, за счет фрагментации кадров, будет задачей нижележащего уровня. Обычно это часть протокола канальной инкапсуляции (пример см. в [RFC 4944]).
Выше, в §3.2, мы спросили себя, куда из заголовка IPv6 потерялся флаг DF. Теперь мы видим, что необходимость в нем отпала. Можно сказать, что флаг DF неявно установлен в каждом пакете IPv6, потому что транзитные узлы фрагментировать его не могут.
Остальные поля, управляющие сборкой фрагментов, мы решили переместить в отдельный заголовок расширения. Он называется заголовок фрагмента (fragment header). Его код в реестре протоколов IP — 44. Если в пакете IPv6 этот заголовок есть, то перед нами фрагмент, а если нет, то цельный пакет.
Так как собирает пакет только его конечный адресат, каждый фрагмент должны сопровождать служебные сведения, представляющие интерес для транзитных узлов. Это необходимо, потому что каждый фрагмент — это тоже пакет IPv6, путешествующий сквозь сеть независимо от других фрагментов.
Какие это будут сведения? В первую очередь, это основной заголовок IPv6, так как он содержит важнейшие сведения о пакете. Далее, пошаговые опции по самому их смыслу также интересны всем узлам на пути фрагмента. Маршрутный заголовок, если он есть, заведомо нужен промежуточным адресатам, хотя маршрутизаторы его не рассматривают. Наконец, об опциях адресата мы заранее не можем сказать, будут ли они нужны промежуточным адресатам. Возможно, появятся опции двух видов: первые будут интересны всем адресатам подряд, а вторые — только последнему из них, то есть конечному. Тогда будет достаточно снабдить каждый фрагмент опциями первого типа, заключив их в отдельный заголовок расширения "опции адресата". Остальные заголовки и полезная нагрузка нужны только конечному адресату, так что их вполне можно разбить между фрагментами.
Обратите внимание, что конечному адресату интересны все заголовки расширения. Опции для всех адресатов и опции для конечного адресата будут заключены в заголовки расширения одного и того же типа, "опции адресата" (код 60). Отличие будет только в том, как они размещены относительно других заголовков. А именно первые должны предшествовать заголовку фрагмента, а последние — идти за ним.
Мы питаем слабость к обобщениям, так что давайте сначала опишем эту картину в целом, а затем уже перейдем к ее деталям. Итак, исходный пакет IPv6 состоит из двух частей, нефрагментируемой (unfragmentable) и фрагментируемой (fragmentable), как показано на рис. 3.15. Мы уже выяснили, что нефрагментируемая часть состоит из таких заголовков:
Все прочие заголовки расширения, если они есть в данном пакете, а также полезная нагрузка составляют его фрагментируемую часть.
(рис 3.15) Структура пакета IPv6 с точки зрения его фрагментации
Пусть читатель подумает, где и в каком виде существует исходный пакет, если по сети передаются только его фрагменты. (Как образ в памяти источника и конечного адресата.)
Первым делом в процессе фрагментации вторую часть делят на фрагменты ненулевой длины, так чтобы окончательная длина каждого фрагмента после инкапсуляции не превысила текущей оценки PMTU, — эта операция схематически представлена на рис. 3.16. Но как связать длины фрагмента до и после инкапсуляции? Ответ на этот вопрос мы получим, рассмотрев следующий шаг процедуры фрагментации.
(рис 3.16) Деление пакета IPv6 на фрагменты (с.з. = следующий заголовок)
Следующим шагом каждый фрагмент превращают в полноценный пакет IPv6. Для этого к нему спереди добавляют заголовок фрагмента, а перед ним — полную копию нефрагментируемой части, как изображено на рис. 3.17. Чтобы такой пакет стал годным, остается скорректировать пару полей в нем, а именно поле "длина полезной нагрузки" в заголовке IPv6 и поле "следующий заголовок" в заголовке перед заголовком фрагмента. Первая корректировка нужна, так как поле "длина полезной нагрузки" всегда относится к текущему пакету, даже если это фрагмент. Второй корректировкой мы учитываем тот факт, что цепочка заголовков изменилась и теперь после нефрагментируемой части идет заголовок фрагмента.
(рис 3.17) Фрагмент после инкапсуляции (с.з. = следующий заголовок)
В других стеках встречаются протоколы сетевого уровня, заголовки которых отдельно хранят длины данного фрагмента и исходного пакета. Пример такого протокола — ISO CLNP.
Теперь мы видим, что первоначальная длина фрагмента до инкапсуляции должна быть не больше, чем текущая оценка PMTU за вычетом длин нефрагментируемой части и заголовка фрагмента. Насколько сложно будет провести это вычисление заранее, до начала фрагментации? Длина нефрагментируемой части зависит только от структуры исходного пакета, она не изменяется от фрагмента к фрагменту. Что касается заголовка фрагмента, то в наших силах сделать его длину фиксированной и тем упростить алгоритм фрагментации.
Законопослушный узел на выходе не должен менять состав и содержание нефрагментируемой части от фрагмента к фрагменту. Ведь зачем это ему? Тем не менее, на входе может оказаться, что у разных фрагментов одного исходного пакета разные нефрагментируемые части. Как мы скажем, когда дойдем до процедуры сборки, в этом случае безусловное преимущество у фрагмента со смещением ноль.
Пришло время подумать над форматом заголовка фрагмента. Он зависит от той системы параметров, которой мы станем описывать каждый фрагмент. Впрочем, готовую систему мы вполне можем взять из IPv4, потому что она удовлетворяет ключевому требованию: устойчивости к нарушению порядка фрагментов. Итак, вот параметры фрагмента IPv6:
К этим полям мы также должны добавить поле "следующий заголовок", чтобы обеспечить сцепление заголовков и полезной нагрузки в пакете IPv6. Таким образом, общее число полей постоянно, а значит, и заголовок фрагмента сможет обладать постоянной длиной. Теперь давайте рассмотрим каждый параметр по очереди и уточним его свойства.
Идентификатор пакета занимал в IPv4 16 бит, и этого оказалось мало: при большой скорости трафика вероятны конфликты, которые приводят к перекрестной сборке пакетов [RFC 4963]. Хуже того, при дальнейшем росте скорости трафика неверно собранные пакеты возникают достаточно часто, чтобы начали сбоить контрольные суммы TCP и UDP: происходят коллизии, когда сбойный пакет обладает верной контрольной суммой. Поэтому давайте расширим это поле до 32 бит. Но будет ли их достаточно?
Чтобы дать на это ответ, мы должны сначала определить, какова область уникальности идентификатора. В IPv4 идентификатор ведь не был самостоятельным — он объединялся с адресами источника и назначения, а также номером протокола. Только весь этот кортеж должен был оставаться уникальным. Перенести это правило в IPv6 без изменений нам мешает то, что номер протокола больше не характеризует пакет как целое: теперь пакет состоит из модулей, каждому из которых отвечает свой номер протокола. Поэтому пусть в IPv6 идентификатор будет уникальным только для заданных адресов источника и назначения.
Если пакет содержит маршрутный заголовок, то это будут адрес источника и адрес конечного назначения [§4.5 RFC 2460], поскольку собирать пакет IPv6 будет его конечный адресат.
Если у фрагмента адрес источника или адрес назначения имеет ограниченную область действия, то индекс его зоны тоже придется
учесть при идентификации такого
Во времени же идентификатор должен оставаться уникальным, пока "живы" фрагменты данного пакета. Оценить время жизни фрагментов сверху можно суммой времени доставки фрагмента по сети и величиной тайм-аута сборки. Если вторая величина — настраиваемый параметр сетевого стека, то первая может принимать сколь угодно большие значения, по крайней мере, в теории.
Здесь бы нам пригодилась интерпретация поля TTL как времени жизни пакета, но мы от нее отказались по практическим соображениям [§8.2 RFC 2460].
Поэтому нам остается сделать эмпирическое предположение: чем выше скорость трафика, тем меньше вероятная задержка пакета в сети. Здесь мы имеем в виду не время распространения сигнала, а непроизводительные расходы времени, такие как ожидание в буферах транзитных узлов. При большой скорости трафика пакет скорее будет потерян, нежели задержится где-то на многие секунды.
Значит, в случае большой скорости трафика — а именно он нам интересен — главным фактором в управлении надежной сборкой будет ее тайм-аут. Он должен быть существенно меньше, чем период, за который идентификатор заведомо повторится. Этот период сокращается при росте скорости трафика, так что его можно оценить снизу, зная верхнюю оценку скорости, с которой один источник готов передавать пакеты в адрес данного узла.
Попробуем оценить худший случай, когда скорость расходования значений идентификатора максимальна при постоянной скорости трафика, выраженной, например, в бит/с. Одно значение идентификатора расходуется на один исходный пакет, так что мы ищем условие максимальной частоты исходных пакетов при постоянной скорости трафика. Для этого необходимо, чтобы сумма длин фрагментов была минимальной.
Пакет IPv6, который фрагментирован согласно протоколу, состоит не менее чем из двух фрагментов, а длина первого фрагмента не менее
1280 байт. Длина же второго фрагмента не может опуститься ниже суммы длин заголовка IPv6 и заголовка фрагмента плюс один байт
Сейчас мы не рассматриваем "патологическое" поведение источника, когда он создает пакеты из одного фрагмента или чрезмерно короткие фрагменты. Тем не менее, алгоритм сборки фрагментов должен быть устойчив к этим аномалиям, так как они явным образом ничему не противоречат. Так завещает нам принцип Постела.
Теперь мы без труда вычислим, что, например, при передаче со скоростью 1 Тбит/с значение 32 битного идентификатора неизбежно повторится только через такой интервал времени:
$$\frac{8{\frac{бит}{байт}}\times 1329{\frac{байт}{пакет}}\times 2^{32}_{пакет}}{10^{12}\frac{бит}{с}}\approx 46_c$$ .
Также можно вычислить, какая скорость фрагментированного трафика станет опасной, если тайм-аут сборки составляет 60 секунд, как предписано в §4.5 RFC 2460:
$$\frac{8{\frac{бит}{байт}}\times 1329{\frac{байт}{пакет}}\times 2^{32}_{пакет}}{60_{с}}\approx \frac {46}{60}\times 10^{12}\frac{бит}{с}\approx 7,6\times 10^{11}\frac{бит}{с}$$.
Запас еще в несколько порядков, конечно, не помешал бы; но и такое соотношение скорости трафика и периода повтора
Реализация IPv6 KAME пошла на сознательное упрощение. Вместо того чтобы генерировать независимые цепочки идентификаторов для каждой пары адресов источника и назначения, она использует одну цепочку на весь стек. Раньше это был простой счетчик, тогда как современное развитие KAME в FreeBSD применяет генератор псевдослучайных чисел с заведомо большим периодом. Конечно, такой трюк не может не привести к сокращению периода повтора идентификатора.
Смещение фрагмента, с нашей тягой к экономии единиц, пусть отсчитывается не от начала пакета, а от начала фрагментируемой
части, как показано на рис 3.18(рис 3.18) Как вычисляется смещение фрагментов (с.з. = следующий заголовок)
Побочным результатом этого выбора будет то, что теперь смещение фрагмента не может указать внутрь основного заголовка IPv6.
Как мы помним, в IPv4 смещение фрагмента отсчитывалось от самого начала пакета, и это позволяло злоумышленнику составить такую
последовательность фрагментов, в которой последующие фрагменты накладывались на важнейшие поля заголовка, в частности, протокол и
адреса источника и назначения. Чуть позже мы сделаем фрагментацию IPv6 еще безопаснее, наложив полный запрет на перекрывающиеся
фрагменты.
Единицу смещения и длину поля "смещение" мы оставим такими же, как в IPv4: 8 байт и 13 бит, соответственно. Это позволит смещению принимать значения вплоть до $$(2^{13}-1)\times 8=65528$$ байт включительно. Такого диапазона достаточно, потому что фрагментируемая часть исходного пакета не длиннее максимальной "полезной нагрузки" IPv6, составляющей 65535 байт; а 7 байт разницы заведомо поместятся в один фрагмент, если нефрагментируемая часть состоит только из основного заголовка IPv6 — условие максимально длинной фрагментируемой части.
Как мы помним, значение "длина полезной нагрузки" в основном заголовке IPv6 включает заголовки расширения.
Полученный диапазон смещений даже чуть шире, чем нужно: он позволяет создать последовательность фрагментов, которая соберется в
пакет-великан длиной более 65575 байт. Для этого достаточно, чтобы у любого фрагмента сумма его смещения в байтах и длины без
заголовков превысила 65575 байт за вычетом длины нефрагментируемой части (см. рис. 3.16 и рис. 3.17). Если в ходе сборки получится
пакет-великан, то следует уничтожить его и, возможно, выслать источнику извещение об ошибке
Пусть читатель сам установит, какова максимальная длина такого пакета-великана в предположении, что PMTU не меньше 65575 байт. Указания к решению: Прочтите до конца раздел. Затем докажите формулу:
$$L=L_1-F_1-8+8O_N+F_N$$ ,
где L — длина полезной нагрузки исходного пакета, $$L_1$$ — длина полезной нагрузки первого фрагмента, $$F_1$$ — длина первого фрагмента без заголовков, $$O_N$$ — смещение последнего фрагмента в 8-байтных словах, $$F_N$$— длина последнего фрагмента без заголовков. Максимизируем все параметры, кроме $$F_1$$, который минимизируем, поскольку он со знаком "минус": $$max L_1=65535$$,$$min F_1=8$$ (из-за выбора единицы смещения), $$max O_N=8191$$ (13 бит), $$max F_N = 65535 - 8 = 65527 $$ (учитываем длину заголовка фрагмента 8 байт). В результате получаем, что максимальная полезная нагрузка пакета-великана составляет 196574 байт, а весь пакет еще на 40 байт длиннее! Такая последовательность фрагментов основана на трюке с разным составом нефрагментируемой части: в первом фрагменте она будет максимально длинной, например, за счет опций PadN (§3.3.2), а в последнем там будет только заголовок IPv6. Очевидно, что послать такую серию фрагментов может только экспериментатор или злоумышленник, потому что она — испытание узла-адресата на устойчивость.
Флаг "еще фрагменты" (M) обладает той же семантикой, что и флаг MF в IPv4: если он установлен, то это не последний фрагмент, а если сброшен, то последний.
Длина фрагмента до инкапсуляции (см. рис. 3.16) должна быть кратна 8 байтам из-за выбора единицы смещения, если только это не самый последний фрагмент. Ведь иначе смещение следующего фрагмента не будет делиться на 8. Поэтому, если вдруг адресат получит фрагмент с установленным флагом "еще фрагменты", и его длина без заголовков (см. рис. 3.17) окажется не кратной 8 байтам, то значит, где-то произошел сбой. Такой фрагмент надо отбросить, а в адрес источника выслать извещение об ошибке.
Конечно же, характеристики "последний" и "не последний" относятся здесь к позиции фрагмента в исходном пакете, а не к порядку поступления фрагментов из сети. Работа механизма фрагментации и сборки IP не зависит от порядка фрагментов, потому что сеть вправе непредсказуемо нарушать его.
Все эти поля мы уместим в заголовок длиной 8 байт, ради выравнивания, как показано на рис. 3.19. Это и будет заголовок фрагмента [§4.5 RFC 2460].
(рис 3.19) Заголовок фрагмента
Поле "смещение" расположено так, чтобы удобно было переводить его значение в байты: достаточно прочесть все выровненное 16 битное слово, а затем обнулить три младших бита операцией "логическое И" с маской 0xFFF8. Это эквивалентно сдвигу значения "смещение" влево на 3 двоичных разряда, то есть умножению его на 8.
Сборка выполняется в обратном порядке:
получив на входе фрагмент, сборщик составляет кортеж: <адрес источника, адрес назначения, идентификатор>, — и использует его как ключ при поиске буфера сборки;
а)если буфер сборки по этому ключу не найден, надо создать новый буфер, так как перед нами новый пакет;
если смещение фрагмента равно нулю:
а)его начало, вплоть до заголовка фрагментации, но не включая его, становится нефрагментируемой частью вновь собираемого пакета и хранится вне буфера сборки;
В принципе, реализация может хранить нефрагментируемую часть и в буфере сборки, с тем чтобы в итоге сборки получить полный пакет. Однако тогда на шаге 3 ей придется внести поправку в позицию фрагмента внутри буфера. Ведь в заголовке фрагмента смещение указано относительно начала фрагментируемой части, а не всего пакета. Кроме того, понадобится буфер сборки с подвижным началом, так как длина нефрагментируемой части неизвестна до приема фрагмента со смещением ноль, а он может прийти не первым, если сеть переупорядочит фрагменты.
б)сборщик запоминает значение "следующий заголовок" из заголовка фрагмента;
§4.5 RFC 2460 допускает случай, когда фрагменты одного пакета содержат разные заголовки расширения и значения "следующий заголовок", хотя на практике такие вариации весьма и весьма подозрительны. Но какие заголовки и значения полей попадут в собранный пакет? Здесь преимущество будет у самого первого фрагмента — то есть с нулевым смещением, а не пришедшего раньше других.
данные после заголовка фрагментации помещаются в буфер сборки по смещению, указанному в заголовке фрагментации;
если фрагмент — последний (флаг M сброшен): длина буфера сборки "замораживается", чтобы прочие фрагменты не могли оказаться за последним;
если все "пробелы" в буфере сборки заполнены: фрагментируемая часть пакета собрана; осталось поместить перед ней нефрагментируемую часть (см. шаг 2(а)) и провести обратную корректировку полей:
а)"длина полезной нагрузки" — теперь относится к вновь собранному пакету;
б)"следующий заголовок" в последнем заголовке нефрагментируемой части — используя значение, которое запомнили на шаге 2(б).
По ходу сборщик должен обнаруживать возможные ошибки, которые мы уже обсудили выше:
тайм-аут сборки, чтобы недособранные пакеты не занимали память навечно;
фрагмент с установленным флагом M, длина которого не кратна 8 байтам;
вновь собранный пакет-великан длиной более 65575 байт.
Естественной реакцией на эти ошибки будет отбросить пакет и, возможно, выслать источнику извещение об ошибке.
Мы говорим: "Возможно", — потому что извещения не стоит слать в ответ на групповые пакеты и другие извещения. Об этом мы еще поговорим в §4.3.
Кроме того, возможны отклонения, связанные с позицией и ролью фрагмента, как то:
В обоих случаях, чтобы вызвать подозрение, это должны быть разные фрагменты, а не дубликаты одного и того же фрагмента, возникшие в сети.
В лучшем случае их может вызвать совпадение кортежа <адрес источника, адрес назначения, идентификатор > у разных фрагментированных пакетов, а это чревато перекрестной сборкой и повреждением данных.
В худшем же случае это может быть злонамеренная попытка обойти системы сетевой безопасности. Вот пример такой атаки. Допустим, веб-сервер защищен простым межсетевым экраном, так что входящие соединения TCP разрешены только с портом 80. Злоумышленник посылает первый фрагмент с заголовком TCP, где порт назначения равен 80, так что межсетевой экран считает этот пакет безопасным и пропускает его дальнейшие фрагменты. Тогда следующим шагом злоумышленник шлет перекрывающийся фрагмент, который заменяет порт назначения 80 на 22 (или любой другой) в буфере сборки веб-сервера.
Поэтому самым безопасным решением будет полностью запретить перекрытие фрагментов IPv6 [RFC 5722]:
Рассмотрите и другие возможные атаки на механизм сборки IPv6. Например: Злодей шлет фрагменты в обратном порядке: №2, а затем №1. У фрагмента №2 сумма смещения и длины не превышает дозволенного, и адресат создает буфер сборки. Затем приходит фрагмент №1, у которого нефрагментируемая часть максимально возможной длины или около того. Вместе эти фрагменты образуют пакет-великан, причем он "растет" не с конца, а с головы буфера сборки, за счет нефрагментируемой части. Это вполне может вызвать крах неаккуратной реализации. Конечно, для такой атаки нужен достаточно большой "потолок" PMTU.
В контексте заголовков расширения IPv6 нам будем уместно упомянуть IPsec (IP security, безопасность IP). Ведь IPsec — это рекомендованный компонент всякого узла IPv6 [§11.1 RFC 6434].
В предыдущей редакции технических условий узла IPv6 поддержка IPsec была даже обязательной [§8.1 RFC 4294]. Хотя в новой редакции [§11.1 RFC 6434] она стала рекомендованной, а не обязательной, это вовсе не перечеркнуло давней связи между IPv6 и IPsec. IPsec был "разжалован" ради двух вполне оправданных целей [§11 RFC 6434]. Во-первых, IPsec — это не панацея и не монопольный поставщик безопасности в среде TCP/IP. В некоторых случаях стоит предпочесть безопасность на другом уровне, скажем, транспортном (TLS) или прикладном (SSH). А во-вторых, обязательность IPsec — это серьезное препятствие на пути встроенных реализаций IPv6 для платформ с ограниченными ресурсами, например, кофеварок и сенсоров. Уместить IPsec в такую минимальную реализацию практически невозможно, а без него реализация IPv6 не соответствует действующему стандарту. Новая редакция технических условий узла IPv6 разрешила это противоречие.
В целом IPsec [RFC 4301] — это архитектура, то есть целая система моделей, механизмов, алгоритмов и протоколов, направленная на сквозную защиту пакетов IP. Слово "сквозная" здесь значит, что защиту пакета выполняют только его источник и конечный адресат, как то: источник подписывает пакет, а конечный адресат проверяет подпись; источник шифрует пакет, а конечный адресат расшифровывает его. Между тем, транзитные узлы, будь то маршрутизаторы или промежуточные адресаты, обрабатывают защищенные пакеты точно так же, как обычные.
Хотя исторически IPsec возник именно для защиты IPv6 и только затем был адаптирован к среде IPv4, подробно рассматривать его мы не будем хотя бы потому, что он заслуживает отдельного курса лекций. Сейчас мы ограничимся только тем, что посмотрим, какие черты объединяют заголовки безопасности IPsec с другими заголовками IPv6.
Среди протоколов IPsec с IPv6 непосредственно соприкасаются протоколы AH (authenticated header, аутентифицированный заголовок) [RFC 4302] и ESP (encapsulating security payload, инкапсулирующая защитная полезная нагрузка) [RFC 4303]. Первый из них, AH, обеспечивает только аутентификацию и защиту целостности пакетов, тогда как ESP позволяет, кроме того, зашифровать большую часть пакета. Так как ESP умеет все, на что способен AH, и даже сверх того, применять их вместе смысла нет. Давайте посмотрим, сможем ли мы составить заголовки расширения для этих протоколов, исходя из общих соображений.
По одной из версий, AH возник отдельно от ESP, только чтобы обойти ограничения на экспорт средств шифрования из
Какие поля потребуются заголовку AH? В первую очередь, по смыслу AH, это цифровая подпись пакета (или же, в терминологии IPsec, значение защитного кода — integrity check value, ICV). Алгоритмы безопасности и способы атаки на них находятся в постоянном развитии, подобно броне и снарядам; поэтому нам не следует заранее фиксировать длину цифровой подписи и алгоритм ее вычисления. Как следствие, заголовок AH будет переменной длины, и ему понадобится поле "длина". В отличие от других заголовков расширения IPv6, единицей длины AH служит 32 битное слово.
В подражание другим заголовкам, значение поля длины AH — это полная длина заголовка AH минус два слова [§2.2 RFC 4302]. Конечно, логичнее было бы вычесть длину постоянной части AH, то есть три слова. С другой стороны, вычитая только два слова, мы гарантируем, что значение поля длины AH никогда не будет нулевым. Предубеждение против нулевых значений основано на том, что во многих протоколах нулевое значение означает "нет сведений". См., например, поле контрольной суммы UDP. Кроме того, в IPv6 длина заголовка AH должна быть кратна 64 битам, чтобы сохранить выравнивание последующих данных [§2.2 RFC 4302].
Называется поле длины AH "длина полезной нагрузки" [§2.2 RFC 4302] — наверное, чтобы сбить врагов с толку.
Как нам быть с алгоритмом подписи и его возможными параметрами, такими как секретный ключ? Модель IPsec полагает, что эти сведения заранее находятся в памяти источника и адресата как специальная запись, связь безопасности (security association, SA) [§4 RFC 4301]. Если провести параллель с кинофильмом "про шпионов", обмен шифроблокнотами происходит за кадром, потому что так безопаснее. В простейшем случае, связь безопасности вводят вручную на каждом из узлов.
Конечно, на практике связями безопасности удобнее управлять автоматически, с помощью дополнительного протокола, такого как IKE.
Поэтому, когда приходит время передавать данные, источнику достаточно сослаться на нужную связь (у шпионов это была бы строка в шифроблокноте), чтобы адресат смог правильно трактовать заголовок безопасности. Такой ссылкой служит 32 битный индекс параметров безопасности (security parameter index, SPI). Он явно заслуживает отдельного поля в заголовке AH [§2.4 RFC 4302].
Далее, чтобы злоумышленник не смог провести атаку воспроизведением, послав копию аутентичного пакета, полезно будет включить в заголовок AH порядковый номер, монотонно растущий на протяжении жизни данной связи безопасности [§2.5 RFC 4302].
На первый взгляд, атака воспроизведением основана на свойстве неидемпотентности некоторых сообщений. Например, дважды предъявив копии чека на 1 рубль, мы получим 2 рубля. Поэтому чеки обычно номерные. С другой стороны, повторный сигнал "поднять железнодорожный семафор" ничего не изменит, потому что семафор уже поднят. Тем не менее, злоумышленник может сохранить копию этого сигнала и послать ее позже, когда семафор закроется, вызвав тем самым крушение поезда. Поэтому неудивительно, что система безопасного обмена данными должна противостоять таким атакам.
Так как сеть способна изменять порядок доставки пакетов, нельзя надеяться, что порядковый номер будет монотонно расти в принимаемых пакетах. Поэтому проверка порядкового номера AH адресатом происходит при помощи скользящего окна [§3.4.3 RFC 4302]: пакеты с номером меньше нижней границы окна игнорируются, в пределах окна запоминается каждый принятый номер, а получение номера больше верхней границы окна сдвигает окно вперед.
Наконец, AH — это заголовок расширения, за которым могут находиться другие заголовки и полезная нагрузка. Поэтому в заголовке AH необходимо поле "следующий заголовок", чтобы адресат смог правильно интерпретировать последующие данные. Как можно убедиться в [§2 RFC 4302], наши общие соображения позволили обосновать все поля заголовка AH. Его точный формат показан на рис 3.20(рис 3.20) Заголовок AH
Для полноты картины осталось заметить, что цифровая подпись ICV защищает не только "хвост" пакета, начиная с заголовка AH, но и предшествующие заголовки — по мере возможности, конечно. Трудность здесь в том, что некоторые поля в этих заголовках могут меняться по пути следования пакета. Если бы все их удалось идентифицировать, то при вычислении ICV через них можно было бы "перескочить" или подставить вместо данных то же число нулевых битов. Мы выберем замещение нулями, потому что оно защищает хотя бы длину переменного поля, при условии что значение ICV зависит от числа замещающих нулевых битов. Вернемся к идентификации переменных полей. Мы уже решили эту задачу для опций IPv6, закодировав нужную информацию в одном бите кода опции. Заголовки IPv6, в отличие от опций, — стандартные, и их свойства можно просто перечислись в соответствующем документе [Приложение A2 RFC 4302]. Здесь у нас возникает искушение провести троичную, а не двоичную классификацию полей:
Новый класс, предсказуемые поля и заголовки, — это те, значение которых меняется предсказуемым образом, так что источник, вычисляя ICV, может использовать их финальное значение, которое увидит конечный адресат. На практике, единственным стандартным представителем этого класса оказывается маршрутный заголовок RH0 (и связанный с ним адрес назначения).
Любитель биологии заметил бы, что даже этот представитель — ныне ископаемый.
Окончательно классифицируем поля основного заголовка IPv6 в табл. 3.2.
| Неизменные | Предсказуемые | Непредсказуемые |
|---|---|---|
| "версия" | "адрес назначения" — при наличии маршрутного заголовка | "класс трафика" |
| "длина полезной нагрузки" | "метка потока" | |
| "следующий заголовок" | ""предельное число шагов"" | |
| "адрес источника" | ||
| "адрес назначения" — в отсутствие маршрутного заголовка |
Что касается заголовков расширения, то здесь работы нам практически не осталось. В заголовках опций эта задача решена на уровне отельных опций; предсказуемый заголовок RH0 упразднен; а заголовок фрагмента вообще не подлежит защите, потому что фрагментация происходит после защиты, а сборка, соответственно, перед проверкой AH [Приложение A2 RFC 4302], так что модулю IPsec никогда не встретится заголовок фрагмента, если только не произошел сбой в стеке.
Пакет-фрагмент может попасть на вход защищенного туннеля, но тогда он подвергнется туннельной инкапсуляции, и полученный туннельный пакет уже не будет фрагментом. Это применимо и к туннельному режиму IPsec, который лишь объединяет логические операции туннелирования и защиты в одну удобную услугу.
Перейдем теперь к ESP. Новой задачей, по сравнению с AH, будет скрыть содержимое пакета от посторонних глаз, применив шифрование. Чтобы сторонний наблюдатель мог извлечь как можно меньше сведений о содержимом пакета, следует зашифровать не только байты, следующие за заголовком ESP, но и сведения об их смысле, то есть поле "следующий заголовок". Тогда, в теории, наблюдателю придется только гадать, что же содержится в данном пакете. Однако на практике выбор значений поля "следующий заголовок" не так уж велик. Более того, сегодня в прикладных пакетах чаще всего это будет 6 (TCP) или, заметно реже, 17 (UDP), а в туннельных 4 (IPv4) или 41 (IPv6). Если начать шифрование с этого поля, то ограниченный набор его вероятных значений откроет лазейку для атак на шифротекст, потому что многие алгоритмы шифрования работают над входными данными последовательно, байт за байтом или блок за блоком.
Это касается даже шифрования в режиме сцепления блоков (CBC), потому что первый блок ни с чем не сцеплен, и предсказуемое начало входного текста все так же понижает надежность защиты.
Чтобы этого не произошло, применим простой трюк: пусть поле "следующий заголовок" находится в ESP после защищаемых данных и потому шифруется последним [§2 RFC 4303]. Туда же, в конец шифротекста, мы поместим поля "набивка" и "длина набивки", необходимые для блочных шифров. И тогда перед шифротекстом достаточно будет поместить только SPI и порядковый номер — конечно, открытым текстом.
Если данная связь безопасности предписывает не только шифровать пакеты, но и проверять их подлинность, то цифровую подпись ICV можно добавить после шифротекста. Тогда сторонний наблюдатель, не имея доступа к параметрам безопасности, даже не сможет определить, подписан ли данный пакет.
Именно этой структуре пакета, представленной на рис. 3.21, ESP обязан своим названием. В отличие от AH, он не встраивается в цепочку заголовков расширения, а полностью инкапсулирует защищаемую часть пакета, превращая ее для стороннего наблюдателя в непроницаемые данные, лишенные очевидной интерпретации.
(рис 3.21) Инкапсуляция ESP
Опытный взломщик, не имея доступа к самим данным, может делать выводы из размера пакетов и их распределения по оси времени. Полное сокрытие полезной нагрузки в ESP позволяет перекрыть и этот канал утечки информации. Вариации размера пакетов можно маскировать достаточно объемистой набивкой [§2.7 RFC 4303]. А чтобы не было очевидных закономерностей в ритме обмена пакетами, стороны могут вставлять в поток пакеты-пустышки, когда нет настоящих данных для передачи. На эту роль прекрасно подойдут пакеты, в которых цепочка заголовков завершается кодом 59 — "следующего заголовка нет" (§3.3.1). Вместе эти приемы обеспечивают конфиденциальность на уровне потока пакетов (traffic flow confidentiality, TFC).
У такой изоляции ESP есть и обратная сторона. В отличие AH, в ESP цифровая подпись ICV не защищает внешний заголовок IP, предшествующий данным ESP [§2 и §3.3.2 RFC 4303]. Поэтому информации из внешнего заголовка в пакете ESP слепо доверять нельзя. Тем не менее, возможности злоумышленника по его подделке не так уж велики. Если поле SPI указало на годную связь безопасности SA в памяти адресата, а проверка целостности и расшифровка прошли успешно, то адресат пакета может сделать следующие выводы:
Архитектура IPsec допускает, но не требует, чтобы связь SA была привязана к конкретным адресам источника и назначения [§4.4.1 RFC 4301].
Тем не менее, злоумышленник может проводить ограниченные атаки на протоколы, чувствительные к точным значениям адресов источника и назначения в пакете. К примеру, модифицируя эти адреса, можно перемещать сегменты TCP из одного соединения в другое, при условии, что эти соединения различаются только адресами, но не портами.
Как мы помним, идентификатором сеанса TCP выступает кортеж <адрес №1, порт №1, адрес №2, порт №2>. Поэтому вполне возможна одновременная работа двух сеансов TCP, которые отличаются, скажем, только адресом №1.
Чтобы блокировать и этот вектор атаки, нам достаточно превратить заголовок IP в полезную нагрузку перед тем, как над ним поработает ESP. Мы уже умеем это делать с помощью туннельной инкапсуляции, так что сейчас нам достаточно соединить вместе два решения: туннель и IPsec. Если мы ограничимся простейшим туннелем "IP-в-IP", то получим конструкцию, известную как туннельный режим (tunnel mode). Очевидно, что он применим не только к ESP, но и к AH, хотя для ESP он важнее. В противоположность ему, непосредственная защита прикладных пакетов называется транспортный режим (transport mode), возможно, потому что в этом режиме под защитой чаще всего оказываются дейтаграммы транспортных протоколов.
На практике под защитой IPsec вполне могут быть и другие туннельные протоколы, например, GRE или L2TP. Ведь для IPsec безразлично, что защищать, пока это IP.
Пусть читатель самостоятельно изобразит структуру пакетов AH и ESP в туннельном режиме. Какие значения полей "следующий заголовок" в них возникнут? Рассмотрите IPv4 и IPv6.
Теперь, обезопасив себя от подделки адресов, спокойно посмотрим на ту же проблему с другой стороны. Кто еще, кроме злодеев, может вносить изменения во внешний заголовок IP? Конечно же, трансляторы NAT. В некоторых случаях NAT — это полезный инструмент, однако его работа основана на подмене одних адресов IP другими. И если ESP спокойно отнесется к трансляции адресов во внешнем заголовке, то AH неизбежно забракует пакет, прошедший NAT.
Искушенный читатель может вспомнить еще одно средство подружить IPsec и NAT: NAT T. Однако в действительности протокол NAT T решает довольно узкую задачу: он позволяет IPsec проходить сквозь трансляторы NAT в режиме маскарада, когда трансляция происходит по принципу "многие в один" (сюръекция). Очевидно, что при этом происходит потеря адресной информации на уровне IP, и недостающие сведения транслятор скрывает в номерах портов TCP или UDP (или других аналогичных полях, таких как идентификатор ICMP ping). Поэтому пакеты IPsec приходится дополнительно инкапсулировать в UDP. Принципиальной несовместимости между AH и NAT это не решает, так что инкапсуляция NAT T определена только для ESP [RFC 3948].
Теперь мы составили полную картину заголовков расширения IPv6 и назначили каждому из них определенную роль. Весьма вероятно, что эта роль определяет не только трактовку данного заголовка, но и его предпочтительную позицию в пакете. В то же время, единообразный формат заголовков расширения IPv6 никак не ограничивает их число и порядок. Действительно, в каждом таком заголовке есть поле "следующий заголовок", и это позволяет источнику пакета сцепить заголовки в произвольном порядке и даже повторить заголовок данного типа несколько раз. Вопрос в том, всегда ли будет иметь смысл такая перестановка. Вероятно, нет. Поэтому сейчас нам надо найти и сформулировать дополнительные правила, которые продиктуют порядок заголовков расширения в пакете IPv6. При этом мы, конечно же, воспользуемся нашими предыдущими наработками.
Для начала заметим, что существует ровно один заголовок расширения, который представляет интерес для всех узлов на пути пакета; это заголовок пошаговых опций. Один такой заголовок может включать в себя переменное число опций, так что ему совершенно незачем встречаться в пакете больше одного раза. Значит, мы вправе повторить сказанное выше: заголовок пошаговых опций, если он вообще есть в пакете, обязан быть вторым по порядку, следуя непосредственно за основным заголовком IPv6. Это правило упростит логику работы маршрутизаторов IPv6, так как им незачем заглядывать дальше первого заголовка расширения.
Еще два заголовка расширения нужны промежуточным адресатам: заголовок опций адресата и маршрутный заголовок. Маршрутный заголовок с ненулевым числом активных сегментов — это сигнал текущему адресату, что пакет надо продвинуть дальше. Поэтому будет вполне естественно, если промежуточный адресат остановится на маршрутном заголовке и не станет заглядывать дальше. Следовательно, опции, предназначенные всем адресатам, надо расположить перед маршрутным заголовком.
Все остальные заголовки расширения и полезная нагрузка несут информацию только конечному адресату. Если исходный пакет подлежит фрагментации, то они составляют его фрагментируемую часть и становятся, вообще говоря, недоступны транзитным узлам. Среди них особо выделяются заголовки безопасности, с помощью которых источник может защитить пакет. В частности, шифрование способно скрыть остаток пакета от посторонних глаз без какого-либо вреда для работы протокола. Поэтому естественное место для заголовков безопасности — в самом начале фрагментируемой части.
Это же положение заголовков безопасности гарантирует нам, что последующие данные заведомо не изменяются по пути следования пакета, — одно из требований IPsec.
Наконец, в §3.1 мы допустили, что возникнут опции, имеющие смысл только для конечного адресата. Их будет разумно поместить в отельный заголовок опций адресата, расположенный после заголовка безопасности и защищенный им.
Нам осталось указать место заголовку фрагмента, но мы фактически уже сделали это выше: он возникает непосредственно за нефрагментируемой частью исходного пакета, то есть после маршрутного заголовка.
Конечно же, ни один из заголовков расширения не обязан встречаться в пакете, поэтому установленный нами порядок заголовков IPv6 — относительный, а не абсолютный. Вот его окончательный вид [§4.1 RFC 2460]:
Таким образом выходит, что ни один заголовок расширения не должен встретиться в пакете IPv6 больше одного раза, кроме заголовка опций адресата, который может встретиться самое большее дважды [§4.1 RFC 2460].
В завершение раздела отметим, что при этом порядке заголовков расширения в пакете IPv6 их можно обрабатывать последовательно, без необходимости забегать вперед или возвращаться назад за информацией.
Отклонение от указанного выше порядка заголовков может вызывать нежелательные последствия. Например, если маршрутный заголовок окажется скрыт за заголовком фрагмента, то текущий адресат сначала соберет пакет, а затем среагирует на маршрутный заголовок. Такое поведение не только грубо противоречит правилам фрагментации и сборки IPv6, но и нарушает работу PMTUD: источник фрагментирует пакет, чтобы он прошел трассу, а текущий адресат сводит его работу на "нет". Конечно, в этом примере скорее виноват сам источник, который разместил заголовки расширения в неверном порядке. Тем не менее, "игры" с порядком заголовков могут быть использованы в злонамеренных целях, и поэтому адресат обязан оставаться начеку. К сожалению, эталонная реализация KAME не проверяет порядка заголовков в принятых пакетах, а слепо обрабатывает их подряд. Стандарт, в свою очередь, ограничивается рекомендациями и не выдвигает четких требований к порядку заголовков в пакете IPv6. [§4.1 RFC 2460] даже требует принимать и обрабатывать заголовки в любом порядке. Эта деталь работы IPv6 явно требует уточнения.
Модульная структура пакета IPv6 предполагает, что появятся и новые типы заголовков расширения. Следовательно, устаревшие узлы будут сталкиваться с незнакомыми заголовками. Какова будет безопасная реакция узла на это событие? Сможет ли он сигнализировать источнику о проблеме? Удастся ли ему перескочить через неизвестный заголовок? Давайте разберем эти вопросы, чтобы поставить точку в нашей работе над заголовками IPv6.
Прежде всего, давайте определимся, какие именно типы узлов на пути пакета могут столкнуться с подобной проблемой. Вряд ли это будет источник, ведь он сам составил этот пакет и знает смысл каждого заголовка в нем. Обычные маршрутизаторы не анализируют пакет дальше заголовка пошаговых опций, который они обязаны поддерживать, так что они просто не доберутся до незнакомого заголовка. А вот текущий адресат пакета обязан пройти по цепочке заголовков IPv6 до тех пор, пока он не встретит маршрутный заголовок или полезную нагрузку; вот тут-то его и может поджидать неизвестный заголовок расширения IPv6. Кроме того, в сети могут быть различные "теневые" устройства, например, межсетевые экраны или системы обнаружения и предотвращения атак. Такое устройство обычно маскируется под маршрутизатор или даже коммутатор (мост), однако ведет подробный анализ и учет протоколов на нескольких уровнях стека, отслеживая не только отдельные пакеты, но также транспортные соединения и прикладные сеансы. С появлением IPv6 ему придется разбирать всю цепочку заголовков расширения в каждом пакете, чтобы добраться до полезной нагрузки, а заодно проверить весь пакет на соответствие многоуровневой политике безопасности. Без сомнения, заголовок нового типа может озадачить программу такого устройства, если она была выпущена раньше.
Следующим шагом мы рассмотрим возможность перескочить через неизвестный заголовок расширения. Для этого представим себе процесс разбора пакета IPv6. Его алгоритм, если опустить детали, довольно прост:
для текущего заголовка:
Чтобы выполнить пункты 2а и 2б, надо знать формат текущего заголовка. Действительно, разные заголовки расширения по-разному кодируют свою длину и размещают поле "следующий заголовок" по разным смещениям. Это очевидное, но не главное препятствие к перескоку через неизвестный заголовок.
Главное же препятствие скрыто в пункте 3. Допустим, мы знаем формат текущего заголовка и успешно выполнили пункты 2а–2в, но тип следующего заголовка (п. 2а) нам ничего не говорит. Поскольку заголовки IPv6 и протоколы следующего уровня заносятся в один и тот же реестр (см. §3.1), мы даже не сможем определить, что представляет собой следующий заголовок, расширение IPv6 или его полезную нагрузку. Ведь перед нами только значение байта "следующий заголовок", например, 254. Это с равным успехом может быть как новый заголовок расширения IPv6, так и экспериментальный транспортный протокол.
Невозможность перескочить через неопознанный заголовок вполне согласуется с тем, что заголовки расширения IPv6 несут в себе инструкции, обязательные для выполнения. Это отличает их от опций.
Поэтому, когда адресат пакета встречает незнакомое значение "следующий заголовок", у него нет другого выбора, как отбросить этот пакет и, возможно, выслать источнику служебное извещение "неизвестный следующий заголовок".
Как обычно, мы говорим: "Возможно", — потому что в ряде случаев извещение высылать не следует из соображений устойчивости и безопасности. Например, плохой идеей будет извещение в ответ на групповой пакет или другое извещение. Подробности этого мы еще обсудим во время работы над протоколом служебных извещений IPv6.
А как быть в том же случае межсетевому экрану? Он не сможет провести глубокий анализ пакета, так что ответную реакцию ему должна подсказать текущая политика безопасности. Если эта политика либеральна, то пакет можно пропустить, надеясь на лучшее. Если же политика безопасности строгая, то пакет будет уничтожен.
Обратите внимание, что реакцию адресата здесь диктует протокол, а реакцию межсетевого экрана — политика.
Напомним себе разницу между протоколом и политикой. Протокол позволяет разным сторонам вести разговор и однозначно понимать друг друга, а политика накладывает на ход их разговора дополнительные ограничения, из протокола не следующие. К примеру, русский язык — это протокол, тогда как дипломатический протокол, с нашей колокольни, никакой не протокол, а всего лишь политика. Помимо этого, политика способна указать, каким образом надо установить параметры протокола, допускающие настройку. Так, политика может потребовать определенного значения TCP MSS - или запретить громкие разговоры после отбоя.
Чтобы реакция межсетевых экранов и подобных им "теневых" устройств была более гибкой, можно инкапсулировать все новые заголовки расширения в ровно один новый мета-заголовок с элементами TLV [draft-krishnan-ipv6-exthdr]. Впрочем, нам не совсем ясно, чем это лучше механизма опций, который работает по тому же принципу и уже готов к использованию.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.