Маршрут по умолчанию IPv6 исчез после включения пересылки в Linux
Когда включена пересылка IPv6, ядро игнорирует router advertisements на uplink, и выученный маршрут по умолчанию истекает. Исправьте это через accept_ra.
Я установил net.ipv6.conf.all.forwarding=1 на шлюзе с Debian, чтобы он мог пересылать трафик VPN-клиентов, и примерно через полчаса у машины не осталось маршрута по умолчанию IPv6. Верхний маршрутизатор работал, link-local сосед всё ещё отвечал, и uplink по-прежнему держал свой глобальный адрес. Прекратилось то, что ядро больше не хотело принимать router advertisements на этом интерфейсе. Маршрут не исчез в момент, когда я ввёл sysctl; он исчез, когда истёк срок жизни маршрута по умолчанию, выученного из RA, и ничто его не обновило.
На первый взгляд ничего не выглядело сломанным. Пересылка работала, клиенты проходили, пока старый маршрут был ещё в кэше, а отказ пришёл позже изменения, которое его вызвало. Именно из-за этой задержки всё списывают на верхнюю сторону или на правило фильтра, которое никогда не участвовало.
Прочитайте таблицу маршрутов, прежде чем трогать любой sysctl
Начните с ip -6 route show default и читайте атрибуты, а не только шлюз. Маршрут по умолчанию, выученный из RA, помечен proto ra, несёт предпочтение и показывает обратный отсчёт expires. Маршрут, добавленный вручную, не имеет ни того, ни другого, и эта разница говорит, какой механизм владеет маршрутом.
$ ip -6 route show default
default via fe80::1 dev eth0 proto ra metric 1024 expires 1432sec hoplimit 64 pref mediumЗатем посмотрите на адрес: ip -6 addr show dev eth0. Глобальный адрес с предпочтительным и действительным сроками жизни, измеренными в часах, переживёт маршрут по умолчанию, срок жизни маршрутизатора которого измеряется в минутах. Адрес есть, а маршрута по умолчанию нет, это подпись проблемы с объявлениями. Если нет и адреса, сначала нужно разбираться со звеном, информацией о префиксе или обнаружением дублирующихся адресов.
Почему пересылка отключает обработку RA
RA это пакет ICMPv6 типа 134, отправленный из link-local источника с ограничением перехода 255. Прежде чем ядро его использует, оно проверяет accept_ra интерфейса. Значение по умолчанию 1, и 1 не означает да. Оно означает: принимай, только пока этот хост не пересылает. Задайте forwarding=1, и это условие перестаёт выполняться, поэтому объявление отбрасывается ещё до того, как в нём будет рассмотрен какой-либо префикс или маршрутизатор по умолчанию.
Существуют три значения, и пересылку переживает только третье:
accept_ra | forwarding=0 | forwarding=1 |
|---|---|---|
| 0 | ignored | ignored |
| 1 (default) | accepted | ignored |
| 2 | accepted | accepted |
Два других флага решают, что в итоге установится. accept_ra_defrtr управляет тем, станет ли маршрутизатор по умолчанию из объявления маршрутом, а accept_ra_pinfo тем, будут ли сведения о префиксе использоваться для настройки адреса. Отключение accept_ra_defrtr это обычный способ сохранить адреса SLAAC на пересылающем хосте, не выучивая маршрут по умолчанию. Это другой рычаг, чем accept_ra, и к нему прибегают, когда нужны адреса, но не маршрут. Справочник ядра по IP sysctl перечисляет все три.
Подтвердите это тремя командами
Сначала прочитайте живые значения вместо файла, который, по вашему мнению, загружен:
sysctl net.ipv6.conf.eth0.forwarding net.ipv6.conf.all.forwarding \
net.ipv6.conf.eth0.accept_ra net.ipv6.conf.eth0.accept_ra_defrtrВо-вторых, докажите, что объявления всё ещё приходят. Фильтрации по байту типа ICMPv6 достаточно, и она избавляет от чтения стены трафика обнаружения соседей:
tcpdump -ni eth0 'icmp6 and ip6[40] == 134'Если за несколько минут ничего не появится, accept_ra не ваша проблема. Верхняя сторона перестала объявлять, что-то на пути фильтрует ICMPv6, или этот интерфейс на самом деле не в том сегменте, о котором вы думаете. В-третьих, запросите объявление сами и прочитайте его поля:
rdisc6 eth0rdisc6 поставляется в пакете ndisc6 на Debian и Ubuntu. В выводе посмотрите на срок жизни маршрутизатора и предпочтение, прежде чем менять что-то ещё. Объявление со сроком жизни маршрутизатора 0 это указание удалить маршрутизатор по умолчанию, и никакое значение accept_ra не превратит это в пригодный маршрут. Так же вы поймаете верхний CPE в режиме моста, который раздаёт префиксы, но никогда не предлагает себя как маршрутизатор.
Сделайте uplink единственным снисходительным интерфейсом
Если верхняя сторона должна быть маршрутизатором по умолчанию, принимайте объявления там, несмотря на пересылку, и больше нигде:
# /etc/sysctl.d/60-ipv6-router.conf
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.eth0.accept_ra = 2
net.ipv6.conf.eth0.accept_ra_defrtr = 1Ограничение важно, потому что пересылка IPv6 в Linux это один глобальный переключатель, а не по одному на канал. Запишите 1 в net.ipv6.conf.wg0.forwarding и затем прочитайте net.ipv6.conf.all.forwarding: глобальное значение переворачивается, и каждый интерфейс начинает пересылать. Вы не можете быть маршрутизатором в туннеле и обычным хостом в LAN. Ограничить можно приём. Держите accept_ra=2 на uplink, а интерфейсы туннеля и LAN оставьте на 1 или 0, чтобы заблудившееся объявление, пришедшее по туннелю, не могло установить маршрут по умолчанию, указывающий в него.
Временный sysctl -w не переживёт следующую загрузку, и как systemd-networkd, так и NetworkManager записывают эти значения из своей конфигурации. В networkd задайте IPv6Forwarding=yes и IPv6AcceptRA=yes в файле .network для uplink; networkd документирует IPv6Forwarding как глобальное по той же причине в ядре. В NetworkManager именно ipv6.method=auto на uplink управляет значением accept_ra, поэтому после nmcli connection up прочитайте sysctl заново, вместо того чтобы доверять первой проверке.
Статическая альтернатива, ip -6 route add default via fe80::1 dev eth0, действительно работает и является разумным ответом, когда у верхнего маршрутизатора есть стабильный link-local адрес. Будьте ясны в том, чем жертвуете. Статический маршрут по умолчанию никогда не обновляется и никогда не отзывается, поэтому после замены верхней стороны он продолжает указывать на мёртвого соседа. Маршрут из RA отказывает безопасно; статический отказывает тихо.
Проверяйте пересланный пакет, а не собственный пакет шлюза
Собственные пинги шлюза могут успешно проходить по маршруту, которым не может воспользоваться ни один клиент, и могут проходить ещё и потому, что шлюз является источником. Проверяйте с подключённого клиента, а затем спросите шлюз, что он сделал бы для адреса этого клиента:
# on the VPN client
ping -6 -c 3 2001:4860:4860::8888
# on the gateway, using a client address
ip -6 route get 2001:4860:4860::8888 from fd42:20::2Затем подождите один срок жизни маршрутизатора и посмотрите снова. Стабильное состояние, которое вам нужно, это маршрут по умолчанию с пометкой proto ra, значение expires которого постоянно сбрасывается, плюс all.forwarding=1. Если expires только отсчитывает вниз, объявления всё ещё где-то отбрасываются, и обычный виновник это менеджер сети, перезаписывающий accept_ra после того, как sysctl при загрузке уже выполнился.
Прежде чем закрыть это, подтвердите четыре вещи: маршрут по умолчанию это proto ra и он обновляется, accept_ra=2 действует только для uplink, пересылка по-прежнему 1 после перезагрузки и после перезапуска менеджера, и IPv6-пинг завершает клиент, а не шлюз. Если клиент со здоровым маршрутом всё ещё не работает, следующий подозреваемый это обратный путь или верхний фильтр, а не объявление.