Сети для самых маленьких. Часть 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
Два правила сокращения:
- Ведущие нули в группе опускаются:
0db8→db8,0000→0. - Одна самая длинная цепочка групп из нулей заменяется на
::, и только одна, иначе неоднозначно.
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сделан из MAC3c: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::всегда, глобальный стабильный, временный для исходящих, иногда ULAfd00::/8. Шлюз — link-local адрес роутера. - Broadcast заменён multicast:
ff02::1— все узлы, MAC33: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 нельзя ни в каком виде.
Оставить комментарий могут только зарегистрированные пользователи.
Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.