Jag satte net.ipv6.conf.all.forwarding=1 på en Debian-gateway så att den kunde vidarebefordra VPN-klienttrafik, och ungefär en halvtimme senare hade maskinen ingen IPv6-standardrutt kvar. Uppströmsroutern var uppe, den länklokala grannen svarade fortfarande, och uplinken hade fortfarande sin globala adress. Det som hade upphört var kärnans vilja att fortsätta ta emot routerannonser på det gränssnittet. Rutten försvann inte när jag skrev sysctl-kommandot; den försvann när livstiden för den RA-inlärda standardrutten gick ut och inget förnyade den.

Inget såg trasigt ut vid första anblicken. Vidarebefordran fungerade, klienterna kom fortfarande igenom medan den gamla rutten låg i cachen, och felet kom senare än ändringen som orsakade det. Den fördröjningen är varför detta läggs på uppströms eller på en brandväggsregel som aldrig var inblandad.

Läs routingtabellen innan du rör någon sysctl

Börja med ip -6 route show default och läs attributen, inte bara gatewayen. En RA-inlärd standardrutt är märkt proto ra, bär en preferens och visar en expires-nedräkning. En rutt du lagt till för hand har inget av detta, och den skillnaden berättar vilken mekanism som äger rutten.

$ ip -6 route show default
default via fe80::1 dev eth0 proto ra metric 1024 expires 1432sec hoplimit 64 pref medium

Titta sedan på adressen: ip -6 addr show dev eth0. En global adress med föredragna och giltiga livstider mätta i timmar överlever en standardrutt vars routerlivstid mäts i minuter. Adress finns plus standardrutt saknas är signaturen för ett annonsproblem. Saknas adressen också är det länken, prefixinformationen eller dubblettadressdetektering du ska jaga först.

Varför vidarebefordran stänger av RA-behandling

En RA är ett ICMPv6-paket av typ 134, skickat från en länklokal källa med hoppgräns 255. Innan kärnan använder den konsulterar den gränssnittets accept_ra. Standardvärdet är 1, och 1 betyder inte ja. Det betyder: acceptera bara medan den här värden inte vidarebefordrar. Sätt forwarding=1, och det villkoret faller, så annonsen kastas innan något prefix eller någon standardrouter i den beaktas.

Det finns tre värden, och bara det tredje överlever vidarebefordran:

accept_raforwarding=0forwarding=1
0ignoredignored
1 (default)acceptedignored
2acceptedaccepted

Två andra flaggor avgör vad som faktiskt installeras. accept_ra_defrtr styr om standardroutern i annonsen blir en rutt, och accept_ra_pinfo styr om prefixinformation används för adresskonfiguration. Att stänga av accept_ra_defrtr är det vanliga sättet att behålla SLAAC-adresser på en vidarebefordrande värd utan att lära sig en standardrutt. Det är en annan spak än accept_ra, och det är den du tar till när du vill ha adresser men ingen rutt. Kärnans IP-sysctl-referens listar alla tre.

Bekräfta det med tre kommandon

Läs först de levande värdena i stället för filen du tror är inläst:

sysctl net.ipv6.conf.eth0.forwarding net.ipv6.conf.all.forwarding \
       net.ipv6.conf.eth0.accept_ra net.ipv6.conf.eth0.accept_ra_defrtr

Bevisa för det andra att annonserna fortfarande kommer in. Filtrering på ICMPv6-typbyten räcker, och det slipper läsa en vägg av grannupptäcktstrafik:

tcpdump -ni eth0 'icmp6 and ip6[40] == 134'

Om inget dyker upp under flera minuter är accept_ra inte ditt problem. Uppströms slutade annonsera, något på vägen filtrerar ICMPv6, eller så ligger inte det gränssnittet verkligen på det segment du tror. Begär för det tredje själv en annons och läs fälten:

rdisc6 eth0

rdisc6 finns i paketet ndisc6 på Debian och Ubuntu. Titta på routerlivstiden och preferensen i utdata innan du ändrar något annat. En annons med routerlivstid 0 är en instruktion att ta bort standardroutern, och inget accept_ra-värde gör det till en användbar rutt. Det är också så du fångar en uppströms CPE i bryggläge som delar ut prefix men aldrig erbjuder sig själv som router.

Gör uplinken till det enda tillåtande gränssnittet

Om uppströms ska vara standardroutern, acceptera annonser där trots vidarebefordran, och ingen annanstans:

# /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

Avgränsningen spelar roll eftersom IPv6-vidarebefordran är en global brytare i Linux, inte en per länk. Skriv 1 till net.ipv6.conf.wg0.forwarding och läs sedan net.ipv6.conf.all.forwarding: det globala värdet vippar och varje gränssnitt börjar vidarebefordra. Du kan inte vara router på tunneln och vanlig värd på LAN:et. Det du kan avgränsa är accepterandet. Håll accept_ra=2 på uplink och låt tunnel- och LAN-gränssnitten stå på 1 eller 0, så att en vilsegången annons över tunneln inte kan installera en standardrutt som pekar in i den.

Ett tillfälligt sysctl -w överlever inte nästa omstart, och både systemd-networkd och NetworkManager skriver dessa värden från sin egen konfiguration. I networkd sätter du IPv6Forwarding=yes och IPv6AcceptRA=yes i uplinkens .network-fil; networkd dokumenterar IPv6Forwarding som globalt av samma kärnskäl. Med NetworkManager är ipv6.method=auto på uplink det som driver accept_ra-värdet, så läs om sysctl efter nmcli connection up i stället för att lita på den första kontrollen.

Det statiska alternativet, ip -6 route add default via fe80::1 dev eth0, fungerar faktiskt, och det är ett rimligt svar när uppströmsroutern har en stabil länklokal adress. Var tydlig med vad du ger upp. En statisk standardrutt förnyas aldrig och dras aldrig tillbaka, så den fortsätter peka på en död granne efter att uppströms bytts ut. RA-rutten misslyckas säkert; den statiska misslyckas tyst.

Testa ett vidarebefordrat paket, inte gatewayens eget

Gatewayens egna ping kan lyckas på en rutt som ingen klient kan använda, och de kan också lyckas för att gatewayen är källan. Testa från en ansluten klient och fråga sedan gatewayen vad den skulle göra för den klientens adress:

# 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

Vänta sedan en routerlivstid och titta igen. Det stabila läge du vill ha är en standardrutt märkt proto ra vars expires-värde fortsätter nollställas, plus all.forwarding=1. Om expires bara räknar ned kastas annonser fortfarande någonstans, och den vanliga boven är en nätverkshanterare som skriver över accept_ra efter att sysctl vid uppstart redan körts.

Innan du avslutar detta, bekräfta fyra saker: standardrutten är proto ra och förnyas, accept_ra=2 gäller bara uplinken, vidarebefordran är fortfarande 1 efter en omstart och efter en hanteraromstart, och en klient i stället för gatewayen slutför en IPv6-ping. Om klienten fortfarande misslyckas med en frisk rutt är nästa misstänkta returvägen eller en brandvägg uppströms, inte annonsen.

Relaterade artiklar