Стандартний маршрут 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-пінг завершує клієнт, а не шлюз. Якщо клієнт із здоровим маршрутом усе ще не працює, наступний підозрюваний це зворотний шлях або верхній фільтр, а не оголошення.