Маршрутът по подразбиране 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, затова прочетете sysctl отново след nmcli connection up, вместо да вярвате на първата проверка.
Статичната алтернатива, 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 пинг. Ако клиентът продължава да се проваля със здрав маршрут, следващият заподозрян е обратният път или горна защитна стена, а не обявлението.