Опубликован: 30.07.2013 | Уровень: для всех | Доступ: свободно
Лекция 4:

Пакет IPv6

Идентификатор пакета занимал в IPv4 16 бит, и этого оказалось мало: при большой скорости трафика вероятны конфликты, которые приводят к перекрестной сборке пакетов [RFC 4963]. Хуже того, при дальнейшем росте скорости трафика неверно собранные пакеты возникают достаточно часто, чтобы начали сбоить контрольные суммы TCP и UDP: происходят коллизии, когда сбойный пакет обладает верной контрольной суммой. Поэтому давайте расширим это поле до 32 бит. Но будет ли их достаточно?

Чтобы дать на это ответ, мы должны сначала определить, какова область уникальности идентификатора. В IPv4 идентификатор ведь не был самостоятельным — он объединялся с адресами источника и назначения, а также номером протокола. Только весь этот кортеж должен был оставаться уникальным. Перенести это правило в IPv6 без изменений нам мешает то, что номер протокола больше не характеризует пакет как целое: теперь пакет состоит из модулей, каждому из которых отвечает свой номер протокола. Поэтому пусть в IPv6 идентификатор будет уникальным только для заданных адресов источника и назначения.

Если пакет содержит маршрутный заголовок, то это будут адрес источника и адрес конечного назначения [§4.5 RFC 2460], поскольку собирать пакет IPv6 будет его конечный адресат.

Если у фрагмента адрес источника или адрес назначения имеет ограниченную область действия, то индекс его зоны тоже придется учесть при идентификации такого фрагмента10 См. сообщение JINMEI Tatuya "Re: IPv6 Scoped Address Architecture (RFC 2710)" в списке рассылки IETF IPng: http://www.mail-archive.com/ipng@sunroof.eng.sun.com/msg00465.html . Если этого не сделать, то возможна перекрестная сборка пакетов из разных зон одной области. Скажем, фрагменты с адресом источника FE80::1, адресом назначения FE80::2 и идентификатором 42 — суть части разных исходных пакетов, если они получены из разных каналов. Это очевидное следствие из зонной адресной архитектуры IPv6 (§2.4).

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

Здесь бы нам пригодилась интерпретация поля TTL как времени жизни пакета, но мы от нее отказались по практическим соображениям [§8.2 RFC 2460].

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

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

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

Пакет IPv6, который фрагментирован согласно протоколу, состоит не менее чем из двух фрагментов, а длина первого фрагмента не менее 1280 байт. Длина же второго фрагмента не может опуститься ниже суммы длин заголовка IPv6 и заголовка фрагмента плюс один байт .11 Годный фрагмент не может быть пустым. Если мы чуть забежим вперед и скажем, что заголовок фрагмента занимает 8 байт, то наименьшая сумма длин фрагментов составит байт.

Сейчас мы не рассматриваем "патологическое" поведение источника, когда он создает пакеты из одного фрагмента или чрезмерно короткие фрагменты. Тем не менее, алгоритм сборки фрагментов должен быть устойчив к этим аномалиям, так как они явным образом ничему не противоречат. Так завещает нам принцип Постела.

Теперь мы без труда вычислим, что, например, при передаче со скоростью 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{бит}{с}.

Запас еще в несколько порядков, конечно, не помешал бы; но и такое соотношение скорости трафика и периода повтора идентификатора12 Если быть точным, постоянно произведение скорости трафика на период повтора идентификатора. можно назвать удовлетворительным, потому что этот трафик — не магистральный, создаваемый многими узлами, а только от одного узла к другому.

Реализация 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). Если в ходе сборки получится пакет-великан, то следует уничтожить его и, возможно, выслать источнику извещение об ошибке .13 Используя аналог ICMP, над которым мы еще поработаем.

Пусть читатель сам установит, какова максимальная длина такого пакета-великана в предположении, что 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.

Сборка выполняется в обратном порядке:

  1. получив на входе фрагмент, сборщик составляет кортеж: <адрес источника, адрес назначения, идентификатор>, — и использует его как ключ при поиске буфера сборки;

    а)

    если буфер сборки по этому ключу не найден, надо создать новый буфер, так как перед нами новый пакет;

  2. если смещение фрагмента равно нулю:

    а)

    его начало, вплоть до заголовка фрагментации, но не включая его, становится нефрагментируемой частью вновь собираемого пакета и хранится вне буфера сборки;

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

    б)

    сборщик запоминает значение "следующий заголовок" из заголовка фрагмента;

    §4.5 RFC 2460 допускает случай, когда фрагменты одного пакета содержат разные заголовки расширения и значения "следующий заголовок", хотя на практике такие вариации весьма и весьма подозрительны. Но какие заголовки и значения полей попадут в собранный пакет? Здесь преимущество будет у самого первого фрагмента — то есть с нулевым смещением, а не пришедшего раньше других.

  3. данные после заголовка фрагментации помещаются в буфер сборки по смещению, указанному в заголовке фрагментации;

  4. если фрагмент — последний (флаг M сброшен): длина буфера сборки "замораживается", чтобы прочие фрагменты не могли оказаться за последним;

  5. если все "пробелы" в буфере сборки заполнены: фрагментируемая часть пакета собрана; осталось поместить перед ней нефрагментируемую часть (см. шаг 2(а)) и провести обратную корректировку полей:

    а)

    "длина полезной нагрузки" — теперь относится к вновь собранному пакету;

    б)

    "следующий заголовок" в последнем заголовке нефрагментируемой части — используя значение, которое запомнили на шаге 2(б).

По ходу сборщик должен обнаруживать возможные ошибки, которые мы уже обсудили выше:

  • тайм-аут сборки, чтобы недособранные пакеты не занимали память навечно;

  • фрагмент с установленным флагом M, длина которого не кратна 8 байтам;

  • вновь собранный пакет-великан длиной более 65575 байт.

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

Мы говорим: "Возможно", — потому что извещения не стоит слать в ответ на групповые пакеты и другие извещения. Об этом мы еще поговорим в §4.3.

Кроме того, возможны отклонения, связанные с позицией и ролью фрагмента, как то:

  • больше одного фрагмента со сброшенным флагом M;
  • перекрывающиеся фрагменты.

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

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

В худшем же случае это может быть злонамеренная попытка обойти системы сетевой безопасности. Вот пример такой атаки. Допустим, веб-сервер защищен простым межсетевым экраном, так что входящие соединения TCP разрешены только с портом 80. Злоумышленник посылает первый фрагмент с заголовком TCP, где порт назначения равен 80, так что межсетевой экран считает этот пакет безопасным и пропускает его дальнейшие фрагменты. Тогда следующим шагом злоумышленник шлет перекрывающийся фрагмент, который заменяет порт назначения 80 на 22 (или любой другой) в буфере сборки веб-сервера.

Поэтому самым безопасным решением будет полностью запретить перекрытие фрагментов IPv6 [RFC 5722]:

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

Рассмотрите и другие возможные атаки на механизм сборки IPv6. Например: Злодей шлет фрагменты в обратном порядке: №2, а затем №1. У фрагмента №2 сумма смещения и длины не превышает дозволенного, и адресат создает буфер сборки. Затем приходит фрагмент №1, у которого нефрагментируемая часть максимально возможной длины или около того. Вместе эти фрагменты образуют пакет-великан, причем он "растет" не с конца, а с головы буфера сборки, за счет нефрагментируемой части. Это вполне может вызвать крах неаккуратной реализации. Конечно, для такой атаки нужен достаточно большой "потолок" PMTU.

Сергей Субботин
Сергей Субботин

"Теоретически канал с адресацией EUI 64 может соединить порядка 2^63 "

запись вида 2^63  не понятна и отнимает время на попытку ее осмыслить.

ее можно заменить например на записи вида  264  или 1,8 * 1019

 

Павел Афиногенов
Павел Афиногенов

Курс IPv6, в тексте имеются ссылки на параграфы. Разбиения курса на параграфы нет.

Сергей Смоляр
Сергей Смоляр
Россия, Ялта