SwordArMor

De l’IPv6 dans du PPP

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

  1. L’abonné entre ses identifiants PPP dans son modem,
  2. 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,
  3. Le LNS de l’opérateur vérifie les identifiants depuis une base RADIUS,
  4. Et s’ils sont corrects, envoie la configuration au client en allant également la chercher dans une base RADIUS.
J’ai volontairement omis certains détails, mais le plus gros y est.

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.
  • .
Maintenant je comprends donc pourquoi personne ne fait d’IPv6 dans PPP. Ça demande beaucoup trop de bidouille pour que ça soit exploitable. Je comprends également pourquoi Orange attend de passer en collecte ethernet pour proposer de l’IPv6 et pourquoi Free utilise 6rd.
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.