La route par défaut IPv6 a disparu après l'activation du transfert sous Linux
Activer le transfert IPv6 fait que le noyau ignore les router advertisements sur le lien montant, et la route par défaut apprise expire. Corrigez cela avec accept_ra.
J'ai mis net.ipv6.conf.all.forwarding=1 sur une passerelle Debian pour qu'elle puisse transférer le trafic des clients VPN, et environ une demi-heure plus tard la machine n'avait plus aucune route par défaut IPv6. Le routeur amont répondait, le voisin link-local répondait encore et le lien montant conservait son adresse globale. Ce qui s'était arrêté, c'était la volonté du noyau de continuer à accepter les router advertisements sur cette interface. La route n'a pas disparu au moment où j'ai saisi le sysctl ; elle a disparu quand la durée de vie de la route par défaut apprise par RA a expiré et que rien ne l'a renouvelée.
Rien ne semblait cassé au premier abord. Le transfert fonctionnait, les clients passaient encore tant que l'ancienne route était en cache, et la panne est arrivée plus tard que le changement qui l'avait causée. Ce décalage explique qu'on l'attribue au lien amont ou à une règle de pare-feu qui n'a jamais été en cause.
Lisez la table de routage avant de toucher au moindre sysctl
Commencez par ip -6 route show default et lisez les attributs, pas seulement la passerelle. Une route par défaut apprise par RA est marquée proto ra, porte une préférence et affiche un compte à rebours expires. Une route ajoutée à la main n'a ni l'un ni l'autre, et cette différence vous dit quel mécanisme possède la route.
$ ip -6 route show default
default via fe80::1 dev eth0 proto ra metric 1024 expires 1432sec hoplimit 64 pref mediumRegardez ensuite l'adresse : ip -6 addr show dev eth0. Une adresse globale dont les durées de vie préférée et valide se comptent en heures survivra à une route par défaut dont la durée de vie du routeur se compte en minutes. Adresse présente et route par défaut absente, c'est la signature d'un problème d'annonces. Si l'adresse manque aussi, il faut d'abord traiter le lien, les informations de préfixe ou la détection d'adresses dupliquées.
Pourquoi le transfert désactive le traitement des RA
Un RA est un paquet ICMPv6 de type 134, émis depuis une source link-local avec une limite de saut de 255. Avant de l'utiliser, le noyau consulte le accept_ra de l'interface. La valeur par défaut est 1, et 1 ne veut pas dire oui. Cela veut dire : accepte seulement tant que cet hôte ne transfère pas. Mettez forwarding=1 et cette condition échoue, donc l'annonce est rejetée avant que le moindre préfixe ou routeur par défaut qu'elle contient soit examiné.
Il existe trois valeurs, et seule la troisième survit au transfert :
accept_ra | forwarding=0 | forwarding=1 |
|---|---|---|
| 0 | ignored | ignored |
| 1 (default) | accepted | ignored |
| 2 | accepted | accepted |
Deux autres indicateurs décident de ce qui est réellement installé. accept_ra_defrtr contrôle si le routeur par défaut de l'annonce devient une route, et accept_ra_pinfo contrôle si les informations de préfixe servent à configurer l'adresse. Désactiver accept_ra_defrtr est la manière habituelle de garder des adresses SLAAC sur un hôte qui transfère sans apprendre de route par défaut. C'est un levier différent d'accept_ra, et c'est celui qu'on utilise quand on veut des adresses mais pas de route. La référence du noyau sur IP sysctl les liste toutes les trois.
Vérifiez-le avec trois commandes
D'abord, lisez les valeurs actives plutôt que le fichier que vous croyez chargé :
sysctl net.ipv6.conf.eth0.forwarding net.ipv6.conf.all.forwarding \
net.ipv6.conf.eth0.accept_ra net.ipv6.conf.eth0.accept_ra_defrtrEnsuite, prouvez que les annonces arrivent encore. Filtrer sur l'octet de type ICMPv6 suffit, et cela évite de lire un mur de trafic de découverte de voisins :
tcpdump -ni eth0 'icmp6 and ip6[40] == 134'Si rien n'apparaît pendant plusieurs minutes, accept_ra n'est pas votre problème. Le lien amont a cessé d'annoncer, quelque chose sur le chemin filtre ICMPv6, ou cette interface n'est pas vraiment sur le segment que vous croyez. Troisièmement, demandez vous-même une annonce et lisez ses champs :
rdisc6 eth0rdisc6 est fourni dans le paquet ndisc6 sous Debian et Ubuntu. Dans la sortie, regardez la durée de vie du routeur et la préférence avant de changer quoi que ce soit d'autre. Une annonce avec une durée de vie de routeur à 0 est l'instruction de supprimer le routeur par défaut, et aucune valeur d'accept_ra n'en fait une route utilisable. C'est aussi ainsi qu'on repère un CPE amont en mode pont qui distribue des préfixes mais ne se propose jamais comme routeur.
Faites du lien montant la seule interface permissive
Si le lien amont doit être le routeur par défaut, acceptez-y les annonces malgré le transfert, et nulle part ailleurs :
# /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 = 1Le périmètre compte parce que le transfert IPv6 sous Linux est un seul interrupteur global, pas un par lien. Écrivez 1 dans net.ipv6.conf.wg0.forwarding puis lisez net.ipv6.conf.all.forwarding : la valeur globale bascule et chaque interface se met à transférer. Vous ne pouvez pas être routeur sur le tunnel et hôte ordinaire sur le LAN. Ce que vous pouvez restreindre, c'est l'acceptation. Gardez accept_ra=2 sur le lien montant et laissez les interfaces du tunnel et du LAN à 1 ou 0, pour qu'une annonce égarée arrivant par le tunnel ne puisse pas installer une route par défaut qui pointe vers lui.
Un sysctl -w temporaire ne survivra pas au prochain démarrage, et systemd-networkd comme NetworkManager écrivent ces valeurs depuis leur propre configuration. Dans networkd, mettez IPv6Forwarding=yes et IPv6AcceptRA=yes dans le fichier .network du lien montant ; networkd documente IPv6Forwarding comme global pour la même raison côté noyau. Avec NetworkManager, c'est ipv6.method=auto sur le lien montant qui pilote la valeur d'accept_ra, donc relisez le sysctl après nmcli connection up au lieu de vous fier au premier contrôle.
L'alternative statique, ip -6 route add default via fe80::1 dev eth0, fonctionne vraiment, et c'est une réponse raisonnable quand le routeur amont a une adresse link-local stable. Soyez clair sur ce que vous abandonnez. Une route par défaut statique n'est jamais renouvelée ni retirée, elle continue donc de pointer vers un voisin mort après le remplacement du lien amont. La route apprise par RA échoue en sécurité ; la statique échoue en silence.
Testez un paquet transféré, pas celui de la passerelle elle-même
Les ping de la passerelle peuvent réussir sur une route qu'aucun client ne peut utiliser, et ils peuvent aussi réussir parce que la passerelle est la source. Testez depuis un client connecté, puis demandez à la passerelle ce qu'elle ferait pour l'adresse de ce client :
# 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::2Attendez ensuite une durée de vie de routeur et regardez de nouveau. L'état stable que vous voulez est une route par défaut marquée proto ra dont la valeur expires se réinitialise sans cesse, plus all.forwarding=1. Si expires ne fait que décroître, des annonces sont encore rejetées quelque part, et le coupable habituel est un gestionnaire de réseau qui réécrit accept_ra après que le sysctl de démarrage a déjà été exécuté.
Avant de conclure, confirmez quatre choses : la route par défaut est proto ra et se renouvelle, accept_ra=2 ne s'applique qu'au lien montant, le transfert vaut toujours 1 après un redémarrage et après un redémarrage du gestionnaire, et c'est un client, pas la passerelle, qui termine un ping IPv6. Si le client échoue encore avec une route saine, le suspect suivant est le chemin de retour ou un pare-feu amont, pas l'annonce.