Ik zette net.ipv6.conf.all.forwarding=1 op een Debian-gateway zodat hij VPN-clientverkeer kon doorsturen, en ongeveer een half uur later had de machine geen IPv6-standaardroute meer. De upstream-router was in de lucht, de link-local buur antwoordde nog, en de uplink had nog steeds zijn globale adres. Wat was gestopt, was de bereidheid van de kernel om op die interface nog router advertisements aan te nemen. De route verdween niet toen ik de sysctl typte; hij verdween toen de levensduur van de RA-geleerde standaard afliep en niets hem vernieuwde.

Niets leek op het eerste gezicht kapot. Forwarding werkte, clients kwamen er nog doorheen zolang de oude route in de cache stond, en de storing kwam later dan de wijziging die hem veroorzaakte. Die vertraging is waarom dit wordt toegeschreven aan de upstream of aan een firewallregel die er nooit bij betrokken was.

Lees de routetabel voordat je een sysctl aanraakt

Begin met ip -6 route show default en lees de attributen, niet alleen de gateway. Een RA-geleerde standaard is gemarkeerd proto ra, draagt een voorkeur en toont een expires-aftelling. Een route die je met de hand hebt toegevoegd heeft geen van beide, en dat verschil vertelt je welk mechanisme de route bezit.

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

Kijk dan naar het adres: ip -6 addr show dev eth0. Een globaal adres met preferred- en valid-levensduren in uren overleeft een standaardroute waarvan de router-levensduur in minuten wordt gemeten. Adres aanwezig plus standaard ontbreekt is de signatuur van een advertentieprobleem. Ontbreekt het adres ook, dan zijn de link, de prefixinformatie of duplicate address detection het eerste waar je achteraan moet.

Waarom forwarding de RA-verwerking uitschakelt

Een RA is een ICMPv6-pakket van type 134, verzonden vanaf een link-local bron met hop limit 255. Voordat de kernel hem gebruikt, raadpleegt hij de accept_ra van de interface. De standaardwaarde is 1, en 1 betekent niet ja. Het betekent: alleen accepteren zolang deze host niet forwardt. Zet forwarding=1 en die voorwaarde faalt, dus de advertentie wordt weggegooid voordat er een prefix of standaardrouter in wordt overwogen.

Er bestaan drie waarden, en alleen de derde overleeft forwarding:

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

Twee andere flags bepalen wat er werkelijk wordt geïnstalleerd. accept_ra_defrtr bepaalt of de standaardrouter in de advertentie een route wordt, en accept_ra_pinfo bepaalt of prefixinformatie wordt gebruikt voor adresconfiguratie. accept_ra_defrtr uitzetten is de gebruikelijke manier om SLAAC-adressen op een forwardende host te houden zonder een standaard te leren. Het is een andere hendel dan accept_ra, en het is degene die je pakt als je adressen wel en een route niet wilt. De kernel IP-sysctl-referentie noemt alle drie.

Bevestig het met drie commando's

Lees eerst de live waarden in plaats van het bestand waarvan je denkt dat het geladen is:

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

Bewijs ten tweede dat de advertenties nog aankomen. Filteren op de ICMPv6-typebyte is genoeg, en het voorkomt dat je een muur aan neighbor discovery-verkeer leest:

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

Als er in meerdere minuten niets verschijnt, is accept_ra niet je probleem. De upstream stopte met adverteren, iets op het pad filtert ICMPv6, of die interface zit niet echt op het segment dat je denkt. Vraag ten derde zelf een advertentie aan en lees de velden:

rdisc6 eth0

rdisc6 zit in het pakket ndisc6 op Debian en Ubuntu. Kijk in de uitvoer naar de router-levensduur en de voorkeur voordat je iets anders verandert. Een advertentie met router-levensduur 0 is een opdracht om de standaardrouter te verwijderen, en geen enkele accept_ra-waarde maakt daar een bruikbare route van. Zo vang je ook een upstream-CPE in bridge-modus die prefixen uitdeelt maar zichzelf nooit als router aanbiedt.

Maak de uplink de enige toestemmende interface

Als de upstream de standaardrouter hoort te zijn, accepteer daar dan advertenties ondanks forwarding, en nergens anders:

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

De afbakening is belangrijk omdat IPv6-forwarding in Linux één globale schakelaar is, niet één per link. Schrijf 1 naar net.ipv6.conf.wg0.forwarding en lees dan net.ipv6.conf.all.forwarding: de globale waarde klapt om en elke interface begint te forwarden. Je kunt niet router op de tunnel en gewone host op het LAN zijn. Wat je wel kunt afbakenen is acceptatie. Houd accept_ra=2 op de uplink en laat de tunnel- en LAN-interfaces op 1 of 0, zodat een verdwaalde advertentie over de tunnel geen standaard kan installeren die daarin wijst.

Een tijdelijke sysctl -w overleeft de volgende herstart niet, en zowel systemd-networkd als NetworkManager schrijven deze waarden vanuit hun eigen configuratie. Zet in networkd IPv6Forwarding=yes en IPv6AcceptRA=yes in het .network-bestand van de uplink; networkd documenteert IPv6Forwarding om dezelfde kernelreden als globaal. Bij NetworkManager is ipv6.method=auto op de uplink wat de accept_ra-waarde aanstuurt, dus lees de sysctl opnieuw na nmcli connection up in plaats van de eerste controle te vertrouwen.

Het statische alternatief, ip -6 route add default via fe80::1 dev eth0, werkt wel, en het is een redelijk antwoord wanneer de upstream-router een stabiel link-local adres heeft. Wees duidelijk over wat je opgeeft. Een statische standaard wordt nooit vernieuwd en nooit ingetrokken, dus hij blijft naar een dode buur wijzen nadat de upstream is vervangen. De RA-route faalt veilig; de statische faalt stil.

Test een doorgestuurd pakket, niet dat van de gateway zelf

De eigen pings van de gateway kunnen slagen op een route die geen client kan gebruiken, en ze kunnen ook slagen omdat de gateway de bron is. Test vanaf een verbonden client en vraag de gateway dan wat hij voor het adres van die client zou doen:

# 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

Wacht dan één router-levensduur en kijk opnieuw. De stabiele toestand die je wilt is een standaard gemarkeerd proto ra waarvan de expires-waarde steeds opnieuw begint, plus all.forwarding=1. Als expires alleen aftelt, worden advertenties nog ergens weggegooid, en de gebruikelijke schuldige is een netwerkbeheerder die accept_ra overschrijft nadat de sysctl bij het opstarten al is uitgevoerd.

Voordat je dit afsluit, bevestig vier dingen: de standaardroute is proto ra en wordt vernieuwd, accept_ra=2 geldt alleen voor de uplink, forwarding is nog 1 na een herstart en na een herstart van de beheerder, en een client in plaats van de gateway voltooit een IPv6-ping. Als de client met een gezonde route nog steeds faalt, is de volgende verdachte het retourpad of een upstream-firewall, niet de advertentie.

Gerelateerde artikelen