Сети для самых маленьких. Часть 13: IPv6 — адреса и заголовок

В пятой части мы закончили на том, что 4,3 миллиарда адресов IPv4 кончились в 2011 году, а в шестой — на NAT, которым эту нехватку латают. Ответ, задуманный ещё в 1990-х и ставший окончательным, — IPv6, RFC 8200. Это не «IPv4 с длинными адресами»: вместе с адресами переделали заголовок, убрали широковещание, запретили роутерам фрагментировать, встроили автонастройку и выкинули NAT как идею. Блок из четырёх частей начнём с адресов и заголовка; протоколы, которые делают IPv6 живым, — Neighbor Discovery, SLAAC и DHCPv6 — в следующих.

Серия про IPv4 была длинной не зря: почти всё из неё здесь пригодится, а отличия будут видны на контрасте.


Адрес: 128 бит

Адресов 2¹²⁸ — примерно 3,4 × 10³⁸. Это число не имеет смысла сравнивать с числом устройств; смысл в том, что адресов хватает, чтобы не экономить: каждой локальной сети выдавать 2⁶⁴ адресов, каждому абоненту — тысячи таких сетей, и ещё останется.

Записывают адрес восемью группами по 16 бит в шестнадцатеричном виде через двоеточие:

2001:0db8:1234:5678:0000:0000:0000:0001

Два правила сокращения:

  1. Ведущие нули в группе опускаются: 0db8 → db8, 0000 → 0.
  2. Одна самая длинная цепочка групп из нулей заменяется на ::, и только одна, иначе неоднозначно.
2001:0db8:1234:5678:0000:0000:0000:0001
2001:db8:1234:5678:0:0:0:1          после правила 1
2001:db8:1234:5678::1               после правила 2

Буквы — строчные (RFC 5952), :: — там, где нулей больше. В URL адрес берут в квадратные скобки, чтобы отличить от порта: https://[2001:db8::1]:443/. Префикс записывается как в CIDR: 2001:db8:1234:5678::/64.

Для примеров зарезервирован 2001:db8::/32 — аналог документационных 192.0.2.0/24; им и будем пользоваться.

Из чего состоит глобальный адрес

 ┌──────────────────────────┬──────────────┬──────────────────────────────────┐
 │ префикс от провайдера    │ подсеть      │ идентификатор интерфейса         │
 │ 48 бит                   │ 16 бит       │ 64 бита                          │
 │ 2001:db8:1234            │ 5678         │ ::1                              │
 └──────────────────────────┴──────────────┴──────────────────────────────────┘
                                 /64 — одна локальная сеть

Правило, которое делает IPv6 простым: каждая локальная сеть — это /64. Не бывает «/26 для серверов, /28 для принтеров»: подсчёт масок из пятой части здесь не нужен. Младшие 64 бита — идентификатор интерфейса, и их размер выбран так, чтобы хост мог сгенерировать его сам (из MAC-адреса или случайно), не спрашивая никого, — на этом стоит SLAAC, часть 15. Старшие 64 — префикс сети: провайдер выдаёт абоненту блок /48 (65 тысяч сетей) или /56 (256 сетей), а абонент, точнее, его роутер, раздаёт по /64 на каждую свою сеть. Домашняя сеть с /56 от провайдера — норма, а не роскошь.

Как роутер абонента узнаёт свой блок, prefix delegation через DHCPv6, — в части 16.


Типы адресов

В IPv4 было три вида: unicast, broadcast, multicast. В IPv6 — unicast, multicast и anycast, broadcast нет. Внутри unicast несколько видов с разной областью действия.

Префикс Что Область
2000::/3 глобальные unicast — обычные маршрутизируемые адреса, выдаются провайдерами весь интернет
fe80::/10 link-local — есть на каждом интерфейсе всегда, генерируется сам, за роутер не выходит один сегмент
fc00::/7 (на практике fd00::/8) ULA, unique local — аналог частных 10.0.0.0/8, но с случайными 40 битами после fd, чтобы две сети при объединении не пересеклись внутри организации
::1/128 loopback, как 127.0.0.1 хост
::/128 неопределённый, как 0.0.0.0: «адреса нет» —
::ffff:0:0/96 IPv4-mapped: ::ffff:192.0.2.1 — так IPv4-адрес выглядит внутри IPv6-сокета, чтобы одна программа обслуживала обе версии внутри хоста
ff00::/8 multicast по второму байту
2001:db8::/32 документационные —

Link-local — самое непривычное для пришедших из IPv4. Этот адрес у интерфейса есть до любой настройки, с момента включения, и он нужен для работы самого IPv6: по нему хосты находят роутер и друг друга (часть 14), и именно link-local адрес роутера, а не глобальный, обычно стоит шлюзом по умолчанию:

$ ip -6 route
default via fe80::da47:32ff:fe0a:b0c dev eth0 proto ra metric 1024

Поскольку fe80::/10 одинаков во всех сегментах, адрес сам по себе не говорит, через какой интерфейс идти, и к нему приписывают зону: fe80::1%eth0. ping fe80::1 без зоны на машине с двумя интерфейсами не сработает.

Multicast заменил широковещание. Адрес ff02::1 — «все узлы в сегменте», это и есть бывший broadcast; ff02::2 — все роутеры. Второй байт задаёт область: ff02 — сегмент, ff05 — сайт, ff0e — весь интернет. Группа ff02::1:ff00:0/104 — solicited-node, о ней в следующей части: это трюк, благодаря которому поиск соседа не беспокоит всех. На Ethernet multicast-адреса IPv6 отображаются в MAC 33:33:xx:xx:xx:xx с четырьмя младшими байтами адреса, как мы видели во второй части, и сетевая карта фильтрует чужие группы аппаратно: хост, не подписанный на группу, кадр даже не увидит.

Anycast — обычный unicast-адрес, назначенный нескольким узлам; маршрутизация доставляет пакет к ближайшему. Так работают корневые DNS-серверы, и так в IPv4 тоже делают, но в IPv6 это узаконено.

На интерфейсе всегда несколько адресов

$ ip -6 addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP
    inet6 2001:db8:1234:5678:a1b2:c3d4:e5f6:789a/64 scope global temporary dynamic
       valid_lft 86153sec preferred_lft 14153sec
    inet6 2001:db8:1234:5678:3e52:82ff:fe1a:2b3c/64 scope global dynamic mngtmpaddr
       valid_lft 86153sec preferred_lft 86153sec
    inet6 fd12:3456:789a:1::10/64 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::3e52:82ff:fe1a:2b3c/64 scope link
       valid_lft forever preferred_lft forever

Четыре адреса на одном интерфейсе, и это нормально:

  • scope link — link-local, был первым;
  • scope global … mngtmpaddr — глобальный, собранный из префикса сети и идентификатора интерфейса. Здесь 3e52:82ff:fe1a:2b3c сделан из MAC 3c:52:82:1a:2b:3c по схеме EUI-64: в середину вставлено ff:fe, а в первом байте инвертирован бит U/L из второй части (3c → 3e). Стабильный, по нему машину можно найти;
  • scope global temporary — временный адрес со случайным идентификатором: его стек использует для исходящих соединений и меняет раз в сутки, чтобы сайты не отслеживали машину по неизменной половине адреса. preferred_lft у него меньше: когда истечёт, адрес остаётся принимать ответы, но новые соединения пойдут с нового;
  • fd12:… — ULA, выданный для внутренних нужд, например, чтобы принтер был доступен по адресу, не зависящему от провайдера.

Какой из них подставить отправителем? Правила RFC 6724: предпочитать адрес той же области, что у получателя (link-local для link-local), с самым длинным общим префиксом, временный для исходящих. Ошибки в этом выборе — когда ULA-адрес уходит отправителем в интернет — одна из причин «IPv6 как будто работает, но нет».


Заголовок

Сорок байт, ровно, всегда:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
┌───────┬───────────────┬───────────────────────────────────────┐
│Version│ Traffic Class │              Flow Label               │
├───────┴───────────────┴───────┬───────────────┬───────────────┤
│        Payload Length         │  Next Header  │   Hop Limit   │
├───────────────────────────────┴───────────────┴───────────────┤
│                                                               │
│                     Source Address (128)                      │
│                                                               │
├───────────────────────────────────────────────────────────────┤
│                                                               │
│                   Destination Address (128)                   │
│                                                               │
└───────────────────────────────────────────────────────────────┘

Сравним с IPv4 из шестой части по полям:

Было в IPv4 Стало в IPv6
Version = 4 Version = 6
IHL, Options нет: заголовок фиксированный, расширения — цепочкой отдельных заголовков
DSCP, ECN Traffic Class, то же самое
— Flow Label: метка потока, по которой роутеры могут держать пакеты одного соединения на одном пути при балансировке, не заглядывая в порты
Total Length Payload Length — длина всего, что после 40 байт
Identification, Flags, Fragment Offset нет: фрагментация переехала в расширение
TTL Hop Limit — честное имя для того же счётчика хопов
Protocol Next Header — то же поле «что внутри», но теперь оно может указывать и на расширение
Header Checksum нет
адреса по 32 бита по 128

Три решения, стоящие за этой таблицей.

Контрольной суммы нет. Ethernet проверяет кадр, TCP и UDP проверяют свои сегменты вместе с псевдозаголовком, в который входят адреса. Проверять заголовок IP третий раз — лишняя работа для каждого роутера, которому к тому же приходилось пересчитывать сумму при каждом изменении Hop Limit. Цена: контрольная сумма UDP, в IPv4 необязательная, в IPv6 обязательна.

Заголовок фиксированной длины. Роутеру не нужно читать IHL, чтобы найти, где начинаются данные; 40 байт всегда. Всё необязательное вынесено в extension headers, которые идут цепочкой между заголовком IPv6 и транспортом, и каждый своим полем Next Header указывает на следующий:

┌──────────┬──────────────┬──────────────┬──────────┬─────────┐
│ IPv6     │ Hop-by-Hop   │ Fragment     │ TCP      │ данные  │
│ NH = 0   │ NH = 44      │ NH = 6       │          │         │
└──────────┴──────────────┴──────────────┴──────────┴─────────┘
Next Header Расширение Кто обрабатывает
0 Hop-by-Hop Options каждый роутер на пути — единственное такое
43 Routing указанные в нём узлы; почти не используется
44 Fragment получатель
50, 51 ESP, AH — шифрованный и подписанный пакет IPsec (часть 19) получатель
60 Destination Options получатель
59 No Next Header дальше ничего нет
6, 17, 58 TCP, UDP, ICMPv6 транспорт, конец цепочки

Роутер смотрит только на первые 40 байт и, если нет Hop-by-Hop, вообще не заглядывает дальше. Файрволу, которому нужны порты, приходится идти по цепочке до транспорта, и длинные цепочки расширений он, как и в случае с опциями IPv4, нередко просто отбрасывает.

Фрагментируют только отправители. Роутер, у которого пакет не влезает в MTU следующего линка, в IPv6 не режет его никогда: выбрасывает и шлёт отправителю ICMPv6 Packet Too Big с размером MTU. Отправитель, если ему очень нужно, сам режет пакет, добавляя заголовок Fragment, и собирает его только получатель. Чтобы жизнь была проще, минимальный MTU любого линка IPv6 — 1280 байт: пакет такого размера пройдёт где угодно без вопросов. Следствие для файрволов из восьмой части здесь строже: Path MTU Discovery — не опция, а единственный механизм, и фильтрация ICMPv6 Packet Too Big ломает IPv6 гарантированно.


Чего нет: широковещания и NAT

Широковещания нет, потому что multicast делает то же самое аккуратнее: пакет на ff02::1 получают все узлы сегмента, а пакет на группу solicited-node — только те, кому он может быть нужен. ARP с его broadcast-запросами, будившими каждую машину в сегменте, заменён Neighbor Discovery на multicast; подробности в следующей части.

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

  • «Значит, мой холодильник доступен из интернета?» Доступен по адресу — да; доступен по факту — только если файрвол на роутере пропускает. NAT никогда не был защитой, он был побочным эффектом; защита — это stateful-файрвол, пропускающий внутрь только ответы на исходящие соединения, и в домашних IPv6-роутерах он включён по умолчанию с ровно таким правилом. Разница в том, что при желании правило можно ослабить для одного устройства, не изобретая пробросы.
  • «А приватность?» Для неё временные адреса из раздела выше: исходящие соединения идут со случайного идентификатора, который меняется ежедневно.

Трансляция в IPv6 всё же существует, но для другого: NAT64 вместе с DNS64 позволяет сети, в которой есть только IPv6, ходить на серверы, у которых есть только IPv4. DNS64 подделывает AAAA-запись из A-записи, вкладывая IPv4-адрес в префикс 64:ff9b::/96, а NAT64 на границе разворачивает такие пакеты в IPv4. Так устроены мобильные сети большинства операторов: телефон живёт в чистом IPv6, а старые сайты достаются через NAT64. Ещё есть NPTv6 — замена префикса один в один без портов, для смены провайдера без перенумерации, но и он редкость.


Как IPv6 и IPv4 живут вместе

Переход не случился одним днём, и большинство сетей сейчас dual stack: у хоста есть оба адреса, у DNS-имени — и A, и AAAA. Браузер спрашивает обе записи, пробует IPv6 первым и, если за 250 мс соединение не установилось, параллельно пробует IPv4 — алгоритм называется Happy Eyeballs, и благодаря ему сломанный IPv6 почти не заметен пользователю, что одновременно и хорошо, и плохо: сломанным он остаётся годами.

Там, где провайдер IPv6 не даёт, его можно получить туннелем: пакеты IPv6 заворачиваются в IPv4 (Protocol 41) до брокера туннелей, у которого IPv6 есть. Это тот же приём, что у VXLAN и IPsec из последнего блока серии: протокол внутри протокола.


Смотрим руками

Адреса и их области:

$ ip -6 addr

Маршруты. Обратите внимание на шлюз link-local с пометкой proto ra — он получен не из настроек, а из объявления роутера (часть 14), и на маршрут multicast:

$ ip -6 route
2001:db8:1234:5678::/64 dev eth0 proto ra metric 1024 expires 86153sec
fe80::/64 dev eth0 proto kernel metric 1024
default via fe80::da47:32ff:fe0a:b0c dev eth0 proto ra metric 1024 expires 1753sec
ff00::/8 dev eth0 metric 256

Самая наглядная демонстрация отсутствия broadcast — пинг всех узлов сегмента через multicast:

$ ping -c 2 ff02::1%eth0
PING ff02::1%eth0(ff02::1%eth0) 56 data bytes
64 bytes from fe80::3e52:82ff:fe1a:2b3c%eth0: icmp_seq=1 ttl=64 time=0.05 ms
64 bytes from fe80::da47:32ff:fe0a:b0c%eth0: icmp_seq=1 ttl=64 time=0.41 ms (DUP!)
64 bytes from fe80::1a2b:3cff:fe4d:5e6f%eth0: icmp_seq=1 ttl=64 time=0.88 ms (DUP!)

Отвечают все, кто есть в сегменте, своими link-local адресами; DUP! — потому что ping ждал один ответ на запрос. Так за секунду видно всех соседей, хотя IPv6 у них может быть «не настроен»: link-local есть всегда.

Проверить, что стек выберет отправителем и через кого пойдёт:

$ ip -6 route get 2001:db8:9999::1
2001:db8:9999::1 from :: via fe80::da47:32ff:fe0a:b0c dev eth0 src 2001:db8:1234:5678:a1b2:c3d4:e5f6:789a metric 1024

src — временный адрес: стек предпочёл его для исходящего соединения. Захват — tcpdump -i eth0 -n ip6, а в Wireshark заголовок IPv6 раскрывается в те же восемь полей, с цепочкой Next Header под ним.

На Windows ipconfig показывает «IPv6-адрес», «Временный IPv6-адрес» и «Локальный IPv6-адрес канала» — те же три вида, а netsh interface ipv6 show route — маршруты.


Что запомнить

  • 128 бит, восемь групп hex, нули сокращаются, :: один раз. В URL — в скобках. Примеры из 2001:db8::/32.
  • Глобальный адрес: 48 бит от провайдера, 16 бит подсети, 64 бита интерфейса. Любая сеть — /64, абонент получает /48 или /56.
  • На интерфейсе несколько адресов: link-local fe80:: всегда, глобальный стабильный, временный для исходящих, иногда ULA fd00::/8. Шлюз — link-local адрес роутера.
  • Broadcast заменён multicast: ff02::1 — все узлы, MAC 33:33:…, карта фильтрует чужие группы.
  • Заголовок 40 байт без контрольной суммы и без полей фрагментации; всё необязательное — в цепочке extension headers через Next Header. Hop Limit вместо TTL.
  • Роутеры не фрагментируют, минимальный MTU 1280, Packet Too Big обязан проходить.
  • NAT не нужен и его нет; защита — stateful-файрвол; приватность — временные адреса; NAT64/DNS64 — для выхода из IPv6-only в IPv4-мир.

Дальше — Часть 14: ICMPv6 и Neighbor Discovery: протокол, который в IPv6 делает работу ICMP, ARP и ещё кое-что сверху. Как хост находит соседа через solicited-node multicast, как находит роутер, что такое Router Advertisement, как проверяется уникальность адреса и почему резать ICMPv6 нельзя ни в каком виде.

Оставить комментарий могут только зарегистрированные пользователи.

Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.