Que j’aurais aussi pu titrer « Pourquoi aucun opérateur ne fait de collecte IPv6 en PPP ».
Tout d’abord, un rappel du fonctionnement d’une collecte PPP s’impose
- L’abonné entre ses identifiants PPP dans son modem,
- De l’autre côté, le BAS regarde le realm, et l’envoie à qui de droit en fonction. Par exemple un realm en @neufpnp ira chez SFR alors qu’un @grifon.ptel.ipadsl ira chez grifon,
- Le LNS de l’opérateur vérifie les identifiants depuis une base RADIUS,
- Et s’ils sont corrects, envoie la configuration au client en allant également la chercher dans une base RADIUS.
Fonctionnement en IPv4
C’est tout simple, le LNS prend une IP (soit fixe soit dynamique en
fonction de votre FAI), un résolveur DNS, et envoie tout ça en IPCP dans le
PPP.
Le routeur a l’autre bout, il a son IP pour faire son NAT et tout, il peut
partager une IP entre tous les membres de la maison, il est content (nous
non, parce que le NAT, mais c’est une autre histoire).
Et en IPv6
Et bien ce n’est pas du tout la même chose !
Il existe bien sûr IPv6CP (RFC5072, section 4), sauf que ça ne permet que de
transmettre les 64 derniers bits de l’adresse que doit prendre l’abonné.
Mais quid du range utilisé pour cette adresse, des ranges éventuellement
délégués et des résolveurs DNS à utiliser ?
C’est là que ça devient intéressant, parce que tout ça doit être géré par
des programmes extérieurs à PPP, qui injecteront tout cela dans le tunnel
PPP. D’un côté c’est intéressant car on peut continuer à utiliser les
implémentations dont on a l’habitude, mais d’un autre côté c’est embêtant,
parce que l’on doit extraire les informations du RADIUS, pour configurer
des RA et du DHCPv6 à la volée, et les implémentations ne sont pas
forcément prévues pour.
Mais surtout, c’est plein de bugs partout, vous allez voir.
Père Castor, raconte nous une histoire
Voici donc mon histoire. Nous sommes un FAI, Grifon, et j’y ai mis en place un routeur de collecte ADSL. On trouve donc un opérateur de collecte, on se branche, on récupère le L2TP, tout va bien… jusqu’au moment où j’attaque IPv6.
Les attributs RADIUS
Je regarde les RFCs et mets donc les attributs qui vont bien pour ma connexion.
Framed-IPv6-Prefix = 2a00:5884:1100:1::/64,
Framed-IPv6-Address = 2a00:5884:1100:1::2,
Framed-Interface-Id = 0:0:0:2,
Delegated-IPv6-Prefix = 2a00:5884:1101::/48
Et là, rien ne se passe. Parce que dans le dictionnaire d’attributs utilisé par pppd, ils ne sont pas présents. Je les ajoute.
#IPv6
ATTRIBUTE Framed-IPv6-Prefix 97 ipv6prefix
ATTRIBUTE Framed-IPv6-Address 168 ipv6addr
ATTRIBUTE Framed-Interface-Id 96 string
ATTRIBUTE Delegated-IPv6-Prefix 123 ipv6prefix
Je recommence. Et :
Sep 21 22:18:23 judicael-adsl pppd[5239]: rc_avpair_gen: received unknown attribute 65 of length 4: 0x00000001
Sep 21 22:18:23 judicael-adsl pppd[5239]: rc_avpair_gen: received unknown attribute 67 of length 13: 0x38392E3233342E3138362E3131
Sep 21 22:18:23 judicael-adsl pppd[5239]: rc_avpair_gen: received unknown attribute 64 of length 4: 0x00000003
Sep 21 22:18:23 judicael-adsl pppd[5239]: rc_avpair_gen: received unknown attribute 97 of length 18: 0x00402A005884110000010000000000000000
Sep 21 22:18:23 judicael-adsl pppd[5239]: rc_avpair_gen: received unknown attribute 168 of length 16: 0x2A005884110000010000000000000001
Sep 21 22:18:23 judicael-adsl pppd[5239]: rc_avpair_gen: received unknown attribute 96 of length 8: 0x0000000000000002
Là, ça commence déjà à devenir plus sérieux. Mais qu’à cela ne tienne, dans
notre jeunesse nous avions déjà eu ce souci sur notre première solution VPN
(à base d’IPsec pour chiffrer et de L2TP et PPP pour se rapprocher d’une
collecte ADSL) et nous avions publié un patch, c’est juste dommage qu’en un an et demi il
n’ait pas été intégré.
Je le reprends donc, je remercie gentoo de permettre de patcher facilement
le code et je recommence une nouvelle fois, et je ne reçois rien.
Extraire les informations du RADIUS
Et oui, parce que PPP n’envoie aucune information utile pour que je puisse
configurer ma pile IPv6 de mon côté. Se pose alors la question de comment
récupérer les attributs utiles. Je peux bien sûr parser le fichier de la
base de données RADIUS, car c’est un fichier plat, mais rien ne prouve
qu’il sera toujours sur cette machine et je ne sais pas dire a priori quel
identifiant est sur quelle interface.
Heureusement, en cherchant un peu, il existe le module radattr.so qui
permet d’écrire les attributs RADIUS dans un fichier.
Service-Type Framed-User
Framed-Protocol PPP
Framed-Routing None
Framed-IP-Address 89.234.186.21
Framed-IP-Netmask 255.255.255.255
Framed-MTU 1492
Framed-IPv6-Prefix 2a00:5884:1100:1::/64
Framed-IPv6-Address 2a00:5884:1100:1::1
Framed-Interface-Id
Delegated-IPv6-Prefix 2a00:5884:1101::/48
Voilà qui est facilement parsable, c’est parfait. Mais, comment je mets ça
dans radvd ? Je peux echo >> /etc/radvd.conf
mais ça va être
compliqué de retirer une configuration quand quelqu’un se déconnecte.
La solution retenue a donc été d’écrire un fichier de configuration
basique, d’écrire un fichier par utilisateur, et de faire cat
/etc/radvd.conf.base > /etc/radvd.conf; cat /run/vpn/radvd-user.*.conf >>
/etc/radvd.conf
puis de recharger la conf à chaque nouvelle
connexion.
Je retente donc une nouvelle fois, et cette fois-ci j’ai une IP et une
route par défaut ! Je commençais à désespérer… Mais seulement, je n’ai
qu’une IP dans le range d’interco (le /64). Évidemment, je n’ai rien fait
de mon range routé dans la configuration de radvd. Il faut donc que je
configure cette partie à la main si je veux redistribuer des IPv6 à mes
machines. Ce n’est pas très grave pour moi, mais c’est plus grave si je
veux que ça marche automatiquement chez tout le monde.
Histoire de vérifier un peu ma configuration quand même, je demande à un copain qui a un modem linksys censé supporter IPv6 de tester. Et évidemment, l’IPv4 monte directement et l’IPv6 reste bloquée.
Rajouter une couche de DHCPv6
En regardant les trames de plus près, je me rends compte que son modem
demande du DHCPv6. Je me dis que ce n’est pas si grave, je prends une
implémentation connue, type celle de l’ISC, j’utilise la même méthode de
configuration qu’avec radvd, et ça va le faire.
Sauf que non, parce qu’il échoue en disans « Unsupported device type 512
for "ppp0" ».
Il me faut alors chercher une autre implémentation, par exemple kea, qui
elle supporte le PPP. Par contre, la configuration est en JSON et fait
environ 70 lignes là où celle de radvd en fait 10. Je ne peux donc pas
toutes les concaténer dans le même fichier, ça deviendrait rapidement
inexploitable si pour une raison ou une autre je dois aller regarder
dedans.
J’écris donc un template, que je sed avec les vraies valeurs et que j’écris
dans un autre fichier, que j’utilise comme configuration pour kea.
Documentation technique
Si jamais vous souhaitez savoir ce que donne la configuration complète et comment les différentes briques se mettent place, vous pouvez lire la page de wiki que j’ai écrite à ce sujet.
Conclusion
Pour faire marcher IPv6 sur PPP, il m’a donc fallu :
- Ajouter les attributs RADIUS dans le dictionnaire,
- patcher ppp,
- extraire la configuration IPv6 qui passe dans le PPP,
- bricoler la configuration de radvd,
- faire de même avec kea, en me prenant un bug sur isc-dhcp au passage. .
Je suis sacrément attaché à IPv6, donc j’ai bien creusé pour le faire marcher, mais à plusieurs moments je me suis dit que faire de l’IPv4-only ce ne serait peut-être pas si mal.
Après, il est vrai que j’aurais pu utiliser l2tpns, mais il a aussi dû être
patché pour faire fonctionner IPv6, sans que le code ne soit intégré en
upstream, et il fait tout en un seul programme (L2TP et PPP).
De plus, il comporte un bug qui fait que le RA n’est envoyé qu’une fois en
début de session et que si le routeur ne l’a pas reçu (mauvais câble,
implémentation qui ne s’attendait pas à le recevoir à ce moment, etc.), la
route par défaut n’est jamais connue. De plus, il commençait à partir en
buffer-overflow quand je l’ai testé chez moi.