Сети для самых маленьких. Часть 14: ICMPv6 и Neighbor Discovery
В тринадцатой части мы узнали, что в IPv6 нет широковещания, что на каждом интерфейсе всегда есть link-local адрес и что шлюз по умолчанию хост получает не из настроек, а «из объявления роутера». Всё это держится на одном протоколе — ICMPv6, RFC 4443, который в IPv6 делает работу сразу трёх протоколов из блока про IPv4: ICMP (ошибки и ping), ARP (поиск MAC-адреса соседа) и той части DHCP, которая сообщает хосту о шлюзе. Для последних двух задач внутри ICMPv6 живёт набор сообщений, который называют Neighbor Discovery Protocol, NDP, RFC 4861.
Именно поэтому фраза «запретить ICMP на файрволе», которая в IPv4 ломала только Path MTU Discovery, в IPv6 ломает всё: без ICMPv6 хост не найдёт ни соседа, ни роутер.
ICMPv6: формат и типы
ICMPv6 — это Next Header 58 в заголовке IPv6. Формат тот же, что у ICMP: тип, код, контрольная сумма (здесь обязательная и с псевдозаголовком из адресов, как у UDP и TCP), дальше тело по типу:
байты: 1 1 2
┌──────┬──────┬──────────┬──────────────────────┐
│ Type │ Code │ Checksum │ тело │
└──────┴──────┴──────────┴──────────────────────┘
Типы поделены по старшему биту: 0–127 — ошибки, 128–255 — информационные. Ошибки, как в IPv4, несут внутри начало исходного пакета, по которому стек находит виноватый сокет.
| Type | Code | Что |
|---|---|---|
| 1 | 0 | Destination Unreachable: нет маршрута |
| 1 | 1 | запрещено администратором (файрвол) |
| 1 | 3 | адрес недостижим: сосед не ответил на поиск — аналог «Host Unreachable» |
| 1 | 4 | порт недостижим (UDP) |
| 2 | 0 | Packet Too Big — с MTU следующего хопа; единственный механизм PMTUD в IPv6 |
| 3 | 0 | Time Exceeded — Hop Limit дошёл до нуля; на нём работает traceroute |
| 3 | 1 | не дождались всех фрагментов |
| 4 | 0–2 | Parameter Problem — ошибка в заголовке, в том числе незнакомый Next Header |
| 128, 129 | Echo Request, Echo Reply — ping | |
| 130, 131, 132, 143 | MLD: запрос, отчёт, выход из группы, отчёт MLDv2 | |
| 133 | Router Solicitation — хост ищет роутер | |
| 134 | Router Advertisement — роутер объявляет о себе | |
| 135 | Neighbor Solicitation — хост ищет соседа (бывший ARP-запрос) | |
| 136 | Neighbor Advertisement — сосед отвечает (бывший ARP-ответ) | |
| 137 | Redirect |
Типы 133–137 — это и есть NDP. У них общее правило: Hop Limit = 255, и получатель отбрасывает любое из них с меньшим значением. Логика простая: пакет с 255 не мог пройти ни одного роутера (тот уменьшил бы его до 254), значит, он пришёл точно из своего сегмента, и подделать его из интернета нельзя.
Параметры NDP-сообщений передаются опциями в формате «тип, длина, значение»: 1 — MAC отправителя, 2 — MAC искомого, 3 — информация о префиксе, 4 — копия перенаправленного пакета, 5 — MTU, 25 — адреса DNS-серверов, 31 — домены поиска. Встретим их ниже.
Поиск соседа: NS и NA
Задача та же, что у ARP: по IPv6-адресу соседа узнать его MAC. Но broadcast-а нет, и спрашивать «всех» нельзя. Вместо этого — трюк с solicited-node multicast.
Для каждого своего unicast-адреса хост подписан на групповой адрес, который получается из младших 24 бит этого адреса:
адрес: fe80::da47:32ff:fe0a:b0c
младшие 24 бита: 0a:0b:0c
solicited-node: ff02::1:ff0a:b0c
MAC группы: 33:33:ff:0a:0b:0c
Хост, которому нужен MAC роутера fe80::da47:32ff:fe0a:b0c, вычисляет эту группу и шлёт запрос в неё. Кадр с MAC-адресом 33:33:ff:0a:0b:0c сетевые карты остальных хостов отбросят аппаратно: они на эту группу не подписаны. Два разных адреса могут совпасть в младших 24 битах и попасть в одну группу, но это редкость, а лишний пакет не страшен: хост сравнит искомый адрес с своими и промолчит.
хост fe80::3e52:82ff:fe1a:2b3c роутер fe80::da47:32ff:fe0a:b0c
MAC 3c:52:82:1a:2b:3c MAC d8:47:32:0a:0b:0c
│ │
├── NS, на ff02::1:ff0a:b0c (MAC 33:33:ff:0a:0b:0c) ─────►│ (только подписанные)
│ Target = fe80::da47:32ff:fe0a:b0c │
│ опция 1: мой MAC 3c:52:82:1a:2b:3c │
│ │
│◄── NA, unicast на fe80::3e52:82ff:fe1a:2b3c ────────────┤
│ Target = fe80::da47:32ff:fe0a:b0c, флаги R S O │
│ опция 2: мой MAC d8:47:32:0a:0b:0c │
Neighbor Solicitation (135) несёт в теле искомый адрес (Target) и опцию 1 с MAC отправителя — чтобы отвечающему не пришлось искать его встречным запросом, ровно как SHA в ARP.
Neighbor Advertisement (136) уходит unicast-ом, в теле тот же Target и опция 2 с его MAC. Три флага: S (solicited) — это ответ на запрос, а не объявление по своей инициативе; R (router) — отвечающий является роутером; O (override) — можно перезаписать существующую запись в кеше. Объявление без S, посланное на ff02::1, — аналог gratuitous ARP: так сообщают о смене MAC-адреса.
В tcpdump:
$ sudo tcpdump -i eth0 -n icmp6
IP6 fe80::3e52:82ff:fe1a:2b3c > ff02::1:ff0a:b0c: ICMP6, neighbor solicitation, who has fe80::da47:32ff:fe0a:b0c, length 32
IP6 fe80::da47:32ff:fe0a:b0c > fe80::3e52:82ff:fe1a:2b3c: ICMP6, neighbor advertisement, tgt is fe80::da47:32ff:fe0a:b0c, length 32
Результат попадает в ту же таблицу соседей, что и ARP, с теми же состояниями из седьмой части: INCOMPLETE, REACHABLE, STALE, DELAY, PROBE, FAILED. В IPv6 проверка живости записи названа явно — Neighbor Unreachability Detection: STALE-запись перед использованием подтверждается unicast-NS или ответом верхнего уровня, и только если три NS подряд остались без NA, запись считается мёртвой. В Linux одна команда показывает обе версии:
$ ip -6 neigh
fe80::da47:32ff:fe0a:b0c dev eth0 lladdr d8:47:32:0a:0b:0c router REACHABLE
2001:db8:1234:5678::20 dev eth0 lladdr 00:11:22:33:44:55 STALE
Пометка router — из флага R.
Поиск роутера: RS и RA
В IPv4 хост узнавал шлюз из DHCP или настроек. В IPv6 роутер сам сообщает о себе всем в сегменте, и это самое важное отличие в автонастройке.
Router Advertisement (134) роутер рассылает на ff02::1 (все узлы) периодически, обычно раз в несколько минут, и в ответ на запрос. В нём всё, что хосту нужно знать о сегменте:
$ sudo tcpdump -i eth0 -n -v icmp6 and 'ip6[40] == 134'
IP6 (hlim 255, next-header ICMPv6 (58)) fe80::da47:32ff:fe0a:b0c > ff02::1: [icmp6 sum ok] ICMP6, router advertisement, length 96
hop limit 64, Flags [none], pref medium, router lifetime 1800s, reachable time 0ms, retrans timer 0ms
prefix info option (3), length 32 (4): 2001:db8:1234:5678::/64, Flags [onlink, auto], valid time 86400s, pref. time 14400s
mtu option (5), length 8 (1): 1500
rdnss option (25), length 24 (3): lifetime 600s, addr: 2001:db8:1234:5678::1
dnssl option (31), length 24 (3): lifetime 600s, domain(s): example.com.
source link-address option (1), length 8 (1): d8:47:32:0a:0b:0c
По строкам:
- Отправитель — link-local адрес роутера. Именно он становится шлюзом по умолчанию у всех, кто это услышал:
default via fe80::da47:32ff:fe0a:b0c proto raиз прошлой части. Глобальный адрес роутера хостам знать не нужно. - hop limit 64 — какой Hop Limit ставить в свои пакеты.
- Flags — два бита, которые решают, нужен ли в сети DHCPv6: M (managed) — «адреса получайте по DHCPv6», O (other) — «адреса делайте сами, но за DNS и прочим идите в DHCPv6». Оба сняты — вся настройка из RA. Подробно в частях 15 и 16.
- router lifetime 1800s — сколько считать этот роутер шлюзом по умолчанию, если RA перестанут приходить. Ноль означает «я роутер, но не шлюз по умолчанию»: так объявляют префикс, не предлагая себя как выход в интернет.
- prefix info — префикс сегмента с двумя флагами: onlink (L) — адреса из этого префикса соседи, слать напрямую, без шлюза; auto (A) — из этого префикса можно собрать себе адрес. Это и есть вход в SLAAC. Два срока: valid — сколько адрес из префикса годен вообще, preferred — сколько его можно использовать для новых соединений.
- mtu — MTU сегмента, если он не стандартный.
- rdnss, dnssl — адреса DNS-серверов и домены поиска. Появились позже (RFC 8106), чтобы не поднимать DHCPv6 ради одной опции.
- source link-address — MAC роутера, чтобы сразу положить его в таблицу соседей.
Router Solicitation (133) хост шлёт на ff02::2 (все роутеры) при включении интерфейса, чтобы не ждать следующего периодического RA. Роутер отвечает RA немедленно, unicast-ом или на всех.
Если роутеров несколько, хост ведёт список шлюзов по умолчанию, и в RA есть поле предпочтения (pref medium, high, low). Так в сегменте с основным и резервным роутером переключение на резерв происходит само: основной перестаёт слать RA, через router lifetime его маршрут истекает. Чаще, впрочем, резервный роутер не ждёт этого, а просто берёт на себя тот же link-local адрес.
DAD: проверка, что адрес свободен
Перед тем как использовать любой unicast-адрес — link-local, собранный по SLAAC, выданный DHCPv6 или введённый руками, — хост обязан проверить, что его никто не занял. Это Duplicate Address Detection, и это тот же NS, только особенный:
IP6 :: > ff02::1:ff1a:2b3c: ICMP6, neighbor solicitation, who has 2001:db8:1234:5678:3e52:82ff:fe1a:2b3c, length 24
- Отправитель —
::, неопределённый адрес: своего у хоста ещё нет, а кандидат использовать нельзя. - Получатель — solicited-node группа кандидата: если адрес у кого-то есть, тот на эту группу подписан.
- Опции с MAC отправителя нет: нечего обновлять в чужих таблицах, адрес не подтверждён.
Хост ждёт секунду. Пришёл NA с этим Target или чужой NS с тем же кандидатом от :: (двое проверяют одно и то же одновременно) — адрес занят, он помечается dadfailed и не используется; стек выберет другой идентификатор, если умеет. Тишина — адрес переходит из tentative в рабочий. В ip -6 addr на только что поднятом интерфейсе первую секунду видно флаг tentative, и именно поэтому IPv6-адрес появляется чуть позже, чем интерфейс.
Если адрес, проверенный DAD, кто-то займёт позже, конфликт проявится как в IPv4: две NA на один NS, скачущий MAC в таблице соседей.
MLD: кто какие группы слушает
Поскольку NDP построен на multicast, кто-то должен знать, какие группы в сегменте кому нужны: коммутатор с MLD snooping не рассылает групповые кадры во все порты, а только туда, где есть подписчики, и роутер пересылает групповой трафик из других сетей только в те сегменты, где его ждут. Для этого хосты отчитываются о подписках протоколом MLD (Multicast Listener Discovery, типы 130–132 и 143): роутер периодически шлёт Query «кто что слушает», хосты отвечают Report со списком групп, а при подписке на новую группу шлют Report сразу. В IPv4 ту же работу делает отдельный протокол IGMP: хосты так же отчитываются роутеру о группах, на которые подписаны; в IPv6 его встроили в ICMPv6 и переименовали.
Знать об MLD нужно по одной причине: коммутатор с MLD snooping, но без MLD-роутера, который задавал бы вопросы, может через некоторое время «забыть» подписки и перестать доставлять NS в solicited-node группы. IPv6 в сегменте перестаёт работать частично и непредсказуемо, а в логах пусто. Если IPv6 ломается через несколько минут после включения коммутатора — проверять MLD snooping.
Redirect
Тип 137, то же, что в ICMP: роутер сообщает хосту, что до адреса X ближе через другой роутер в том же сегменте или что X вообще сосед. В теле — адрес лучшего next hop и копия перенаправленного пакета (опция 4). Правила приёма те же: только от текущего шлюза, только с Hop Limit 255, и во многих системах выключено.
Чем это небезопасно
NDP, как и ARP, никому не верит и никого не проверяет. Атаки те же, что в седьмой части, плюс одна новая и очень частая — rogue RA: любое устройство, пославшее Router Advertisement, становится шлюзом по умолчанию для всего сегмента. Чаще это не злоумышленник, а ошибка: Windows с включённым общим доступом к интернету, виртуальная машина с настроенным radvd, домашний роутер, принесённый в офис. Все хосты получают второй шлюз, половина трафика уходит в никуда, и, в отличие от DHCP, где клиент берёт один ответ, здесь хост честно пользуется обоими роутерами по очереди.
Защита — на коммутаторе: RA Guard (RFC 6105) пропускает RA только с доверенных портов, как DHCP snooping из одиннадцатой части. Существует и криптографическая версия NDP, SEND (Secure Neighbor Discovery): адрес выводится из открытого ключа хоста, а сообщения подписываются, так что подделать NA или RA от чужого имени нельзя. Поддержки в массовых системах почти нет, и в жизни SEND не встречается.
Что нельзя фильтровать
Памятка из восьмой части, переписанная для IPv6 (по RFC 4890):
Обязаны проходить везде, иначе IPv6 не работает:
- 1 Destination Unreachable, 2 Packet Too Big, 3 Time Exceeded, 4 Parameter Problem — ошибки;
- 133–136 — RS, RA, NS, NA — в пределах сегмента (они и так не выйдут за него из-за Hop Limit 255 и link-local адресов, но файрвол на самом хосте может их зарезать);
- 130–132, 143 — MLD, если в сети есть коммутаторы со snooping.
Можно ограничить: 128 Echo Request снаружи (как и в IPv4, лишая себя диагностики), 137 Redirect.
Правило «deny icmp any any» в IPv6 на хосте приводит к тому, что хост перестаёт отвечать на NS и через полминуты исчезает для соседей: записи о нём устаревают, NUD не получает подтверждения, и даже роутер больше не может доставить ему пакет. Это самая частая причина «IPv6 работал и перестал» после включения файрвола.
Смотрим руками
Таблица соседей и её состояния:
$ ip -6 neigh show nud reachable
$ ip -6 neigh show nud failed
Спросить роутеры вручную и увидеть RA с разбором (пакет ndisc6):
$ sudo rdisc6 eth0
Soliciting ff02::2 (ff02::2) on eth0...
Hop limit : 64 ( 0x40)
Stateful address conf. : No
Stateful other conf. : No
Router preference : medium
Router lifetime : 1800 (0x00000708) seconds
Prefix : 2001:db8:1234:5678::/64
On-link : Yes
Autonomous address conf.: Yes
Valid time : 86400 (0x00015180) seconds
Pref. time : 14400 (0x00003840) seconds
MTU : 1500 bytes (valid)
Recursive DNS server : 2001:db8:1234:5678::1
Source link-layer address: D8:47:32:0A:0B:0C
from fe80::da47:32ff:fe0a:b0c
Если rdisc6 молчит — в сегменте нет роутера IPv6 или его RA не доходят. Два ответа от разных адресов — rogue RA, и второй стоит найти по MAC.
Поиск соседа вручную — ndisc6 fe80::da47:32ff:fe0a:b0c eth0, аналог arping. Отключить приём RA на машине, которая сама роутер: sysctl net.ipv6.conf.eth0.accept_ra=0. И полный захват NDP — tcpdump -i eth0 -n -v icmp6: за первые секунды после ip link set eth0 up там будут DAD для link-local, RS, RA, DAD для глобального адреса и первые NS/NA — весь протокол в десяти строках.
Что запомнить
- ICMPv6 (Next Header 58) объединяет ошибки, ping, поиск соседей и роутеров, MLD. Ошибки — типы до 127, остальное — от 128.
- Поиск соседа: NS в solicited-node группу
ff02::1:ffXX:XXXXпо младшим 24 битам адреса, кадр на33:33:ff:XX:XX:XX, чужие карты отбрасывают его сами; NA unicast-ом с флагами S, R, O. Таблица соседей и NUD — как у ARP. - Роутер объявляет себя RA на
ff02::1: link-local отправителя становится шлюзом, флаги M и O решают про DHCPv6, prefix info с флагом A — вход в SLAAC, RDNSS — DNS. RS ускоряет первый RA. - DAD: NS от
::на свой будущий адрес; тишина секунду — адрес рабочий. - Все NDP-сообщения с Hop Limit 255, иначе отбрасываются. Защиты от подделки нет: rogue RA — частая авария, RA Guard — лечение.
- Фильтровать ICMPv6 целиком нельзя: типы 1–4 и 133–136 обязаны проходить.
Дальше — Часть 15: SLAAC: как из префикса в RA и идентификатора интерфейса хост собирает себе адрес без единого сервера, откуда берётся идентификатор (EUI-64, стабильный случайный, временный), что значат сроки valid и preferred, как адреса устаревают и заменяются, и как это настроить на роутере с Linux.
Оставить комментарий могут только зарегистрированные пользователи.
Войдите на сайт или зарегистрируйтесь, чтобы оставить комментарий.