Obtenir une connectivité IPv6 dans la misère d’IPv4

Avertissement

Ce document s’adresse aux professionnels des réseaux à des fins uniquement pédagogiques.

Pour reproduire les exemples de cet article, il est nécessaire d’avoir :

  • Un routeur OpenWRT physique.
  • Un ordinateur dans le nuage avec une adresse IPv4 publique.

Introduction

Comment obtenir une connectivité IPv6 alors que nos fournisseurs d’accès Internet tardent encore à nous offrir nativement et avec qualité le standard de l’Internet, en version 6 (RFC8200) ? L’IETF recommande depuis longtemps l’implémentation des deux protocoles (Dual Stack), IPv4 et IPv6. Toutefois, la plupart d’entre nous reste dans la misère d’IPv4 et l’ignorance d’IPv6. Passer par un fournisseur de tunnel IPv6 semble la solution la moins mauvaise en attendant mais à condition d’avoir la main sur le routeur qui connecte l’IPv4 public … ce qui est n’est probablement pas ou plus le cas.

Cet article explique comment redistribuer sa connectivité IPv6 acquise grâce à un fournisseur de tunnel dans un réseau local caché par du NAT multiple. On mettra en oeuvre ici du routage de trafic IPv6 transporté dans un tunnel OpenVPN (UDP en IPv4) sécurisé et puis dans un tunnel 6in4 offert par Hurricane Electric.

Si vous ne maîtrisez pas IPv6 dans votre réseau, d’autres pourraient s’en charger à votre place …

À partir du moment où IPv6 sera le protocole préféré dans tous les cas par nos hôtes terminaux (tout périphérique capable en IPv6, RFC 6434), cette démonstration dans un réseau qui n’est pas le nôtre pourrait être interprété comme une étape d’une attaque de redirection et d’homme du milieu.

Il s’agit juste ici de proposer une solution élégante et fonctionnelle qui fournit des tunnels IPv6 supportés en IPv4 grâce à OpenVPN et au “tunnel broker” Hurricane Electric.

Connectivité native Dual Stack (double pile)

Comme le suggère la figure suivante, le réseau IPv6 est un Internet distinct de celui de la version 4. Pour accéder à IPv6 en tant qu’hôte terminal, votre fournisseur de services Internet doit aussi le mettre en oeuvre dans son infrastructure et c’est ici que le bât blesse.

Connectivité IPv6 native en Dual Stack

Pour remédier au manque d’adresses IPv4, la meilleure solution est d’implémenter le protocole standard IPv6 en double pile. Si une ressource à joindre l’est uniquement en IPv4, l’ordinateur utilisera sa connexion IPv4 ; dans tous les autres cas, il utilisera sa connexion IPv6.

Fournisseurs de tunnels IPv6

En attendant une connectivité native, il est toujours possible de prendre les services d’un fournisseur de tunnel comme Hurricane Electric Tunnel Broker. Entretemps, d’autres ont disparu comme Gogo6 (✝ 2016) ou Sixxs (✝ 2017).

Peut-on raisonnablement imaginer du trafic de production dans des tunnels publics ? Je ne le pense pas.

Tunnel 6in4

Hurricane Electric propose des tunnels 6in4 (RFC 4213 et RFC 3053) : le trafic IPv6 est transporté dans l’Internet IPv4 public. Chaque utilisateur a la possibilité de créer jusqu’à cinq tunnels avec un bloc /64 et un bloc /48 routés dans le tunnel.

Rappellons juste qu’ici qu’un bloc /48 offre encore 16 bits pour un découpage en 65536 sous-réseaux /64, soit l’équivalent d’un réseau IPv4 de la classe A découpé en /24 (255.255.255.0).

                                  +-------------+
                                  |    IPv4     |
                                  |   Header    |
  +-------------+                 +-------------+
  |    IPv6     |                 |    IPv6     |
  |   Header    |                 |   Header    |
  +-------------+                 +-------------+
  |  Transport  |                 |  Transport  |
  |   Layer     |      ===>       |   Layer     |
  |   Header    |                 |   Header    |
  +-------------+                 +-------------+
  |             |                 |             |
  ~    Data     ~                 ~    Data     ~
  |             |                 |             |
  +-------------+                 +-------------+

Encapsulation d’IPv6 dans IPv4, RFC 4213, page 5

                                 +------+
                                /|tunnel|
                               / |server|
                              /  |      |
                             /   +------+
   +----------+     +------+/    +------+
   |dual-stack|     |tunnel|     |tunnel|
   |   node   |<--->|broker|<--->|server|
   |  (user)  |     |      |     |      |
   +----------+     +------+\    +------+
                       |     \   +------+
 tunnel end-point      v      \  |tunnel|
       /\            +---+     \ |server|
       ||            |DNS|      \|      |
       ||            +---+       +------+
       ||
       ||                    tunnel end-point
       ||                           /\
       ||                           ||
       |+---------------------------+|
       +-----------------------------+
            IPv6 over IPv4 tunnel

Modèle Fournisseur de tunnel, RFC 3053, p.3

On trouvera d’autres considérations sur les méthodes de transition IPv6 dans un autre document : Méthodes de transition IPv6

Carrier-Grade NAT

Mais ce type de tunnel exige des adresses IPv4 publiques qui se voient directement sans NAT, c’est-à-dire sans que les champs d’adresses IPv4 de son en-tête ne soient traduits et transformés lors de leur transport. On parle alors de respect d’unicité des adresses IP et de la connectivité de bout-en-bout.

Or beaucoup de nos connexions domestiques ou mobiles sont fournies avec des adresses privées dans le nuage opérateur qui traduit ces adresses en adresses publiques au sortir de leur réseau (Carrier-Grade NAT). Le CG-NAT est de la traduction d’adresses IPv4 dans le nuage et à l’échelle de l’opérateur. Le CG-NAT est déployé chez les fournisseurs d’accès Internet pour faire face au manque d’adresses IPv4.

NAT dans le nuage opérateur

Voici un exemple de connexion Orange en Belgique :

curl ipinfo.io
{
  "ip": "94.109.49.28",
  "city": "",
  "region": "",
  "country": "BE",
  "loc": "50.8500,4.3500",
  "hostname": "cust-28-49-109-94.dyn.as47377.net",
  "org": "AS47377 Orange Belgium SA"
}

Je sors dans le réseau IPv4 public au septième saut.

traceroute -n 1.1.1.1
traceroute to 1.1.1.1 (1.1.1.1), 64 hops max, 52 byte packets
 1  192.168.8.1  2.219 ms *  1.450 ms
 2  192.168.2.1  4.304 ms  4.829 ms  2.864 ms
 3  192.168.225.1  14.242 ms  8.124 ms  10.168 ms
 4  10.50.22.1  38.118 ms  43.988 ms  32.121 ms
 5  10.50.28.6  53.154 ms  24.486 ms  35.778 ms
 6  10.50.31.250  23.446 ms  24.165 ms  27.696 ms
 7  * 212.65.33.233  87.518 ms  55.441 ms
 8  194.53.172.66  26.132 ms  36.018 ms  32.540 ms
 9  1.1.1.1  22.466 ms  28.251 ms  27.798 ms

Aussi, les adresses IPv4 attribuées aux connexions domestiques peuvent changer : ce n’est pas insurmontable grâce à des mécanismes de mise-à-jour (comme du “Dynamic DNS”). Quoi qu’il en soit, le trafic sera traduit au moins deux fois : au sortir de la passerelle locale et dans le nuage opérateur. S’il y a souvent moyen de contourner un simple NAT entre le réseau local et l’Internet, dans ce cas-ci, ce n’est plus vraiment jouable.

Difficile de monter un tunnel 6in4 à travers un nuage CG-NAT

Dans ce contexte, il n’est pas étonnant qu’une simple encapsulation d’IPv6 dans IPv4 ne fonctionne pas.

Transporter de l’IPv6 dans un tunnel UDP IPv4

Une solution consisterait à utiliser un relai dans l’Internet IPv4 public qui monterait le tunnel IPv6 vers Hurricane Electric. Mais une portion du trajet serait toujours en IPv4 seulement : comment transporter ce trafic IPv6 jusqu’à notre réseau local depuis ce relai en IPv4 ? Grâce à un second tunnel entre le relai qui connecte le fournisseur et un point terminal du réseau local.

J’ai d’abord pensé aux tunnels de type GRE et IPSEC mais ceux-ci exigent aussi idéalement une vue directe des interfaces en IPv4. Les solutions propriétaires et open source ne manquent pas en la matière.

Il m’a semblé plus simple d’utiliser OpenVPN pour sa souplesse et sa disponibilité sur les PCs et sur les plateformes embarquées. Le principe est d’encapsuler le trafic IPv6 entre le client local et le relai dans l’Internet dans un tunnel UDP (port 1194 par défaut) supporté par IPv4 avec du chiffrement. Ensuite, le trafic IPv6 est routé jusque chez Hurricane Electric dans un tunnel 6in4.

Connectivité IPv6 dans un tunnel OpenVPN en remote access

La figure illustre une connexion d’un PC vers le relai pour obtenir une connectivité IPv6 (en remote access selon le jargon).

Passerelle de service IPv6

L’objectif final serait de construire une passerelle IPv6 portable qui s’occuperait d’établir la connectivité en bordure d’un réseau IPv4, quel qu’il soit, et qui s’occuperait aussi de configurer et transférer IPv6 dans le réseau local.

Pour distribuer IPv6 dans le réseau à partir d’un client OpenVPN, je conseille d’utiliser un routeur qui fonctionne avec OpenWRT. OpenWRT est une distribution Linux spécialisée pour le matériel embarqué qui supporte un grand nombre de plateformes.

Voici deux modèles de routeurs qui fonctionnent avec OpenWRT.

ModèleImage
Le Convexa-B (GL-B1300) de gl.inet à 89$. Atheros IPQ4028, Quad-core ARM, 717MHz, 400Mbps(2.4G) + 867Mbps(5G) high speed, DDR3L 256MB RAM / 32MB FLASH ROM, 1WAN, 2LAN, 1USB3.0, 10/100/1000M Gigabit Ethernet type, 802.11a/b/g/n/ac
<figcaption>Convexa-B (GL-B1300)</figcaption>
Le GL-AR300M Mini Smart Router de gl.inet à 40 $. Qualcomm QCA9531 SoC, 650MHz CPU, 300Mbps high speed, 128MB RAM, 16MB Nor flash + 128MB Nand flash(optional), UART, PCIe interfaces
<figcaption>GL-AR300M Mini Smart Router</figcaption>

La figure suivante représente une configuration site-à-site (site-to-site) où la passerelle domestique est un de ces routeurs avec OpenWRT et OpenVPN installés.

Connectivité IPv6 dans un tunnel OpenVPN en _remote access_

Paramètres du tunnel Hurricane Electric

Au préalable, j’ai pris un compte et j’ai créé un tunnel 6in4 sur https://www.tunnelbroker.net/.

Le serveur Openvpn (client HE) est situé en France et le point d’accès HE (serveur HE) est situé aux Pays-Bas.

Voici les paramètres de la configuration du tunnel. Les adresses IP ont été adaptées pour la documentation.

  • Points terminaux du tunnel IPv6 :
    • Server IPv4 Address: “216.66.84.46
    • Server IPv6 Address: “2001:db8:1f54:67::1/64
    • Client IPv4 Address: “51.77.222.56
    • Client IPv6 Address: “2001:db8:1f54:67::2/64
  • Préfixes IPv6 routés dans le tunnel: “2001:db8:78c9::/48
  • Résolveurs DNS :
    • Résolveurs DNS Anycast IPv6: “2001:470:20::2
    • Résolveurs DNS Anycast IPv4: “74.82.42.42

Pour rappel, voici la topologie à monter :

Topologie IPv6 Tunnel Broker

Si nous avions un routeur Cisco en bordure du réseau public IPv4, la configuration proposée aurait ressemblé à ceci :

configure terminal
interface Tunnel0
 description Hurricane Electric IPv6 Tunnel Broker
 no ip address
 ipv6 address 2001:db8:1f54:67::2/64
 tunnel source 51.77.222.56
 tunnel destination 216.66.84.46
 tunnel mode ipv6ip
ipv6 route ::/0 Tunnel0
end
write

Préparation de la connectivité du serveur Openvpn

Notre relai n’est pas un routeur Cisco, c’est un serveur (VPS) hébergé chez OVH : j’ai pris une offre OVH VPS SSD (2Go RAM, 1 vcpu, 20Go SSD) en Ubuntu 18.04 LTS à 3 EUR/mois

Après s’être assuré de la joignabilité de l’adresse IPv4 du serveur OpenVPN, il est nécessaire de monter le tunnel 6in4 en modifiant trois fichiers sur le serveur :

  • /etc/netplan/50-cloud-init.yaml
  • /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
  • /etc/resolv.conf et avec l’arrêt du service systemd-resolved

Dans les dernières version d’Ubuntu, c’est netplan qui gère la configuration du réseau.

Modification du fichier /etc/netplan/50-cloud-init.yaml.

# This file is generated from information provided by
# the datasource.  Changes to it will not persist across an instance.
# To disable cloud-init's network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    version: 2
    ethernets:
        ens3:
            dhcp4: true
            match:
                macaddress: fa:16:3e:14:1e:fd
            set-name: ens3
    tunnels:
      he-ipv6:
        mode: sit
        remote: 216.66.84.46
        local: 51.77.222.56
        addresses:
          - "2001:db8:1f54:67::2/64"
        gateway6: "2001:db8:1f54:67::1"

Modification du fichier /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg :

echo "network: {config: disabled}" > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg

Arrêt du service local de résolution de noms :

systemctl disable systemd-resolved
systemctl stop systemd-resolved

Modification du fichier /etc/resolv.conf :

rm /etc/resolv.conf
cat << EOF > /etc/resolv.conf
nameserver 2606:4700:4700::1111
nameserver 2606:4700:4700::1001
nameserver 1.1.1.1
nameserver 1.0.0.1
EOF

Les commandes netplan try (test de la configuration) et netplan apply permettent de recharger la configuration du réseau. Toutefois, le redémarrage de l’ordinateur serait judicieux.

Des vérifications d’usage sont à faire sur les adresses :

ping6 -c1 2001:db8:1f54:67::1
ping6 -c1 2606:4700:4700::1111
ping6 -c1 www.google.com

Configuration du serveur OpenVPN

Installation de :

  • openvpn : serveur de tunnel udp chiffré
  • easy-rsa : un outil de gestion d’une PKI
apt update
apt -y install openvpn easy-rsa

Création des clés du serveur : le Common Name (CN) doit exactement correspondre.

mkdir /etc/openvpn/easy-rsa/
cp -r /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/
vi /etc/openvpn/easy-rsa/vars
cd /etc/openvpn/easy-rsa/
source vars
./clean-all
ln -s openssl-1.0.0.cnf openssl.cnf
./build-ca
./build-key-server tun0
./build-dh
cp tun0.crt tun0.key ca.crt dh2048.pem /etc/openvpn/

Création du fichier du serveur :

cat << EOF > /etc/openvpn/server.conf
# general settings for openvpn server
local 51.77.222.56
proto udp
dev tun
ca ca.crt
cert tun0.crt
key tun0.key
dh dh2048.pem
server 10.11.12.0 255.255.255.0
# adittional settings for ipv6:
server-ipv6 2001:db8:78c9:ffff::/64
push "route-ipv6 ::/0"
push "route-metric 2000"
route-ipv6 2001:db8:78c9:1::/64
client-config-dir ccd
EOF
  • server-ipv6 2001:db8:78c9:ffff::/64 indique le bloc utilisé pour le tunnel.
  • push "route-ipv6 ::/0" et push "route-metric 2000" poussent une route par défaut avec une métrique de 2000 sur le client.
  • route-ipv6 2001:db8:78c9:1::/64 indique au routeur de transfèrer du trafic pour le bloc du client.
  • client-config-dir ccd indique le dossier des configurations propres aux clients.

Un redémarrage du serveur OpenVPN est probablement conseillé.

systemctl restart openvpn

Clés du client

Création des clés du client client01 :

cd /etc/openvpn/easy-rsa/
source vars
./build-key --batch client01
mkdir -p /etc/openvpn/ccd/
cat << EOF > /etc/openvpn/ccd/client01
iroute-ipv6 2001:db8:78c9:1::/64
EOF

L’instruction iroute-ipv6 2001:db8:78c9:1::/64 indique au serveur OpenVPN de “router” le trafic de ce bloc dans la connexion du client01.

Il est possible de tester la connectivité avec un client OpenVPN sur un PC avec le logiciel client approprié.

Le fichier de configuration .ovpn peut être généré à partir du serveur OpenVPN avec cette procédure :

client="client01"
cat << EOF > /root/${client}.ovpn
client
dev tun-ipv6
remote `dig @ns1.google.com -t txt o-o.myaddr.l.google.com +short -4 | sed 's/"//g'` 1194 udp
<key>
`cat /etc/openvpn/easy-rsa/keys/${client}.key`
</key>
<cert>
`cat /etc/openvpn/easy-rsa/keys/${client}.crt`
</cert>
<ca>
`cat /etc/openvpn/easy-rsa/keys/ca.crt`
</ca>
EOF

Installation d’un client OpenVPN pour OpenWRT

Pour distribuer IPv6 dans le réseau à partir d’un client OpenVPN, je conseille d’utiliser un routeur qui fonctionne avec OpenWRT. Comme déjà dit, OpenWRT est une distribution Linux spécialisée pour le matériel embarqué qui supporte un grand nombre de plateformes (voir plus haut). On pourrait aussi utiliser l’appliance OpenWRT dans GNS3.

Installation d’OpenVPN :

opkg update
opkg install openvpn-openssl luci-app-openvpn

Copie des clés du client : on aura pris la peine de copier les clés à partir du serveur OpenVPN sur le client OpenVPN.

mkdir /etc/keys/
cp ca.crt /etc/keys/
cp client01.key /etc/keys/
cp client01.crt /etc/keys/

Création du fichier de configuration /etc/config/openvpn :

mv /etc/config/openvpn /etc/config/openvpn.old
cat << EOF > /etc/config/openvpn
config openvpn 'client01'
	option dev 'tun'
	option verb '3'
	option client '1'
	option pull '1'
	list remote '51.77.222.56'
	option remote_random '0'
	option tun_ipv6 '1'
	option ca '/etc/keys/ca.crt'
	option key '/etc/keys/client01.key'
	option cert '/etc/keys/client01.crt'
	option enabled '1'
EOF

Configuration des interfaces et du pare-feu OpenWRT en IPv6

Adaptation du fichier /etc/config/network : On ajoute une adresse IPv6 du bloc routé à l’interface WAN. L’interface WAN6 à associée à l’interface physique tun0 en mode “non géré”.

config interface 'lan'
	option ip6addr '2001:db8:78c9:1::1/64'

config interface 'wan6'
	option proto 'none'
	option ifname 'tun0'

Adaptation du fichier /etc/network/dhcp : Pour distribuer IPv6 dans le réseau local en autoconfiguration et en DHCPv6 Stateful, on active ces options.

config dhcp 'lan'
	option ra 'server'
	option dhcpv6 'server'
	option ndp 'hybrid'
	option ra_management '1'

Un peu de diagnostic

À partir d’un client Mac OS X du réseau local connecté à la passerelle OpenWRT.

Paramètres d’interface :

ifconfig en0
en0: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
	ether ac:bc:32:95:7f:5b
	inet6 fe80::18de:3188:5c8f:92a%en0 prefixlen 64 secured scopeid 0x5
	inet6 2001:db8:78c9:1:c8f:6453:fe97:af4 prefixlen 64 autoconf secured
	inet6 2001:db8:78c9:1:b5ce:7019:2ec1:f7b1 prefixlen 64 autoconf temporary
	inet 192.168.9.119 netmask 0xffffff00 broadcast 192.168.9.255
	inet6 2001:db8:78c9:1::2b5 prefixlen 64 dynamic
	nd6 options=201<PERFORMNUD,DAD>
	media: autoselect
	status: active

Connectivité IPv6 :

ping6 -c5 www.google.com
PING6(56=40+8+8 bytes) 2001:db8:78c9:1:61a6:fec9:9510:b688 --> 2a00:1450:400e:80b::2004
16 bytes from 2a00:1450:400e:80b::2004, icmp_seq=0 hlim=56 time=65.868 ms
16 bytes from 2a00:1450:400e:80b::2004, icmp_seq=1 hlim=56 time=109.260 ms
16 bytes from 2a00:1450:400e:80b::2004, icmp_seq=2 hlim=56 time=39.896 ms
16 bytes from 2a00:1450:400e:80b::2004, icmp_seq=3 hlim=56 time=38.733 ms
16 bytes from 2a00:1450:400e:80b::2004, icmp_seq=4 hlim=56 time=76.402 ms

--- www.google.com ping6 statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 38.733/66.032/109.260/26.094 ms

Trafic HTTP sur IPv6 :

curl -v -6 http://test.tf/status
*   Trying 2001:41d0:305:1000::1d8a...
* TCP_NODELAY set
* Connected to test.tf (2001:41d0:305:1000::1d8a) port 80 (#0)
> GET /status HTTP/1.1
> Host: test.tf
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 01 Jul 2019 20:50:50 GMT
< Server: Apache/2.4.7 (Ubuntu)
< Last-Modified: Sat, 29 Jun 2019 21:20:13 GMT
< ETag: "15-58c7cf5ed13b9"
< Accept-Ranges: bytes
< Content-Length: 21
<
{
  "status": "OK"
}
* Connection #0 to host test.tf left intact

Interroger le site http://ipv6-test.com :

curl -6 http://v4v6.ipv6-test.com/api/myip.php ; echo ''
2001:db8:78c9:1:61a6:fec9:9510:b688

Conclusion

Maintenant, désactivez IPv4 et tentez de naviguer sur Internet … Bonne chance ! Le NAT64/DNS64 est la prochaine étape.

Inspirations

Je me suis inspiré des deux articles de blog (Wouter Hanegraaff) suivants :