Lorsqu'un réseau privé virtuel est configuré pour acheminer l'intégralité du trafic réseau (0.0.0.0/0 et ::/0), chaque paquet généré par le système hôte doit emprunter le tunnel chiffré. Cette conception engendre un paradoxe architectural majeur nommé récursion de routage (routing recursion). La charge utile chiffrée par le client VPN doit elle-même être expédiée sur le réseau physique via un socket UDP externe. Si le système d'exploitation dirige sans distinction tout le trafic IP vers l'interface virtuelle du tunnel, le paquet UDP externe est renvoyé vers le pilote du tunnel, générant une boucle d'encapsulation infinie.
Les protocoles conventionnels comme OpenVPN ou IPsec tentaient traditionnellement de résoudre ce conflit en insérant des routes statiques vers l'adresse IP publique de la passerelle VPN dans la table de routage principale. Toutefois, cette approche devient fragile lors des transitions entre Wi-Fi et réseau cellulaire, lors des changements dynamiques d'adresses ou en présence d'architectures Anycast.
WireGuard écarte tout risque de boucle sous Linux grâce à l'association des marques de pare-feu (fwmark), de l'option de socket SO_MARK et de la base de règles de routage (Routing Policy Database, RPDB).
Le dilemme de la récursion de routage dans les tunnels chiffrés
Pour appréhender la nécessité du routage par politiques, observons le cycle de vie d'un paquet réseau sur un poste de travail Linux exécutant l'interface WireGuard wg0.
Lorsqu'une application transmet un segment TCP vers un serveur distant à l'adresse 203.0.113.50, la pile réseau du noyau sélectionne une interface de sortie. Si une commande de routage standard telle que ip route add default dev wg0 est appliquée, le noyau désigne wg0 comme prochain saut. Le pilote WireGuard intercepte le paquet, consulte sa table Cryptokey Routing, chiffre les données à l'aide de ChaCha20-Poly1305 et les encapsule dans un datagramme UDP destiné à 198.51.100.10:51820.
Paquet applicatif (Destination: 203.0.113.50)
│
▼
Table FIB du noyau: default dev wg0
│
▼
Pilote WireGuard (wg0): Encapsulation UDP externe (Destination: 198.51.100.10:51820)
│
▼
Table FIB du noyau: Correspondance default dev wg0 à nouveau ! ──> BOUCLE DÉTECTÉE
Dès que le socket UDP de WireGuard confie ce paquet à la pile réseau Linux, le noyau consulte à nouveau sa base d'informations de transfert (FIB). Comme la route par défaut pointe vers wg0, le datagramme UDP externe est renvoyé vers wg0. L'interface tente de rechiffrer un paquet déjà chiffré, saturant la file d'attente du socket et excédant la taille maximale de transmission (MTU). Le noyau abandonne les trames et la communication s'interrompt brutalement.
La base de règles RPDB et la structure à double table
Linux prend en charge les topologies de réseau complexes grâce à sa base de règles de routage (RPDB), administrée au moyen de la suite d'outils ip rule. Au lieu de dépendre d'une unique table statique, la RPDB permet d'orienter les flux vers plusieurs tables indépendantes selon l'adresse source, l'interface, le type de service ou les métadonnées des paquets.
Une configuration Linux standard comporte trois tables par défaut :
local(table 255) : traite le trafic de boucle locale et de diffusion générale.main(table 254) : contient les routes des interfaces physiques et les attributions DHCP.default(table 253) : table de secours réservée à des traitements spécifiques.
Les utilitaires de gestion WireGuard (comme wg-quick) ne modifient pas la passerelle par défaut dans la table main. Ils créent une table de routage dédiée — souvent indexée sous le numéro 51820 — dans laquelle est insérée la route par défaut vers wg0 :
# Ajout de la route par défaut dans la table dédiée
ip route add default dev wg0 table 51820
ip -6 route add default dev wg0 table 51820
En séparant la route du tunnel de la passerelle physique de la table main, le système maintient deux environnements de routage parallèles. L'enjeu technique consiste à guider le trafic applicatif vers la table 51820 tout en autorisant les sockets de transport WireGuard à consulter la table main (254).
Marquage des sockets via SO_MARK et évaluation fwmark
Le composant reliant le socket du tunnel au réseau physique est la marque de pare-feu (fwmark). Dans le noyau Linux, chaque structure de tampon réseau (struct sk_buff) possède un champ 32 bits désigné par skb->mark. Cette marque réside exclusivement dans la mémoire vive du noyau ; elle ne circule pas sur le support physique et ne modifie aucun en-tête IP.
Lorsque le module noyau WireGuard initialise son socket UDP de transport, il active l'option SO_MARK via l'interface POSIX :
int fwmark = 51820;
setsockopt(sock_fd, SOL_SOCKET, SO_MARK, &fwmark, sizeof(fwmark));
Tous les paquets UDP émis par ce socket portent la marque numérique 51820. Pour orchestrer l'acheminement, le système définit des règles RPDB adaptées :
# Règles de politique pour les flux marqués et non marqués
ip rule add not fwmark 51820 table 51820
ip rule add table main suppress_prefixlength 0
L'arbitrage s'opère avec une rigueur mathématique :
- Trafic des applications : Un navigateur émet un paquet vers
203.0.113.50. Le socket du navigateur n'ayant aucune marque,skb->markest égal à0. Le noyau évalue la règlenot fwmark 51820. Comme0 != 51820, la condition est satisfaite. Le paquet est orienté vers la table 51820 et confié à l'interfacewg0. - Trafic de transport WireGuard : WireGuard chiffre la charge utile et émet le paquet UDP via son socket marqué (
skb->mark = 51820). Le noyau examine la règlenot fwmark 51820. La marque correspondant exactement à51820, la règle négative est invalidée. Le noyau ignore la table 51820 et passe à la règle suivante dans la RPDB, à savoir la tablemain(254). Dans la tablemain, la passerelle physique viaeth0ouwlan0achemine le paquet vers le routeur local.
Ce mécanisme à double voie permet aux paquets chiffrés d'atteindre l'infrastructure physique sans jamais réintégrer wg0.
Filtrage de chemin inverse et le piège du rp_filter
Même si le trafic sortant est correctement aiguillé, un tunnel complet peut être perturbé si les protections d'entrée éliminent les paquets de réponse légitimes. La contrainte principale provient du filtrage de chemin inverse (Reverse Path Filtering, RPF), normalisé dans la RFC 3704 et piloté par la variable noyau rp_filter.
Le RPF protège le système contre l'usurpation d'adresses IP. À l'arrivée d'un paquet sur une interface, le noyau vérifie si cette même interface serait sélectionnée pour renvoyer une réponse vers l'adresse source.
Linux propose trois modes RPF :
0(Désactivé) : aucune validation de l'adresse source.1(Mode strict) : le paquet entrant doit provenir de l'interface précise désignée par la table de routage pour le retour.2(Mode souple) : l'adresse source doit posséder une route valide dans n'importe quelle table, sans distinction d'interface.
En mode strict (rp_filter = 1), la dissymétrie introduite par le routage par politiques provoque le rejet des paquets. Lorsqu'un paquet de réponse arrive depuis Internet sur l'interface physique eth0, le noyau effectue une recherche de route retour vers l'adresse IP distante. Comme cette requête ne porte pas la marque fwmark de WireGuard, la RPDB l'oriente vers la table 51820, qui indique que l'IP externe dépend de wg0. Cette discordance incite le noyau à considérer le paquet comme usurpé et à le rejeter.
Afin d'éviter ces pertes invisibles, WireGuard configure automatiquement le mode souple sur les interfaces concernées :
sysctl -w net.ipv4.conf.all.rp_filter=2
sysctl -w net.ipv4.conf.eth0.rp_filter=2
sysctl -w net.ipv4.conf.wg0.rp_filter=2
De surcroît, la clause suppress_prefixlength 0 rattachée aux règles par défaut garantit que les sous-réseaux locaux de la table main conservent leur priorité face à la route /0 du tunnel.
Environnements mobiles et postes fixes : Android VpnService et espaces de noms réseau
L'appel SO_MARK requiert le privilège système CAP_NET_ADMIN. Sur les distributions Linux de bureau et les serveurs, les privilèges administrateur permettent d'appliquer les marques et de configurer la RPDB directement. En revanche, les systèmes d'exploitation mobiles imposent un bac à sable strict qui interdit aux applications ordinaires de modifier les tables du noyau.
Sous Android, une application WireGuard ne peut pas exécuter setsockopt(SO_MARK). L'architecture Android fournit donc l'API android.net.VpnService. Lors de la création du socket UDP de transport, l'application transmet son descripteur de fichier au système via VpnService.protect(int socketFd). Le service de connectivité d'Android lie directement ce socket au réseau physique sous-jacent, écartant tout passage par l'interface virtuelle tun.
Linux Desktop: Socket -> SO_MARK (51820) -> Règle RPDB (not fwmark) -> Table main
Android Mobile: Socket -> VpnService.protect() -> Liaison directe au réseau physique
Espaces de noms: Interface eth0 dans netns0 <-> wg0 dans init_net (isolation matérielle)
Sur les serveurs exigeant une isolation rigoureuse, les espaces de noms réseau (ip netns) offrent une solution alternative. Placer l'interface physique eth0 dans un espace isolé tout en conservant wg0 dans l'espace principal supprime physiquement tout risque de boucle de routage, sans nécessiter de marquage de paquets.
L'architecture de règles RiceGuard et la prévention des fuites DNS
En conditions opérationnelles réelles, les clients VPN doivent gérer avec fluidité les transitions entre bornes Wi-Fi, les renouvellements de baux DHCP et les résolveurs DNS locaux tels que systemd-resolved actif sur 127.0.0.53.
RiceGuard déploie un moteur de routage par politiques structuré :
- Règles RPDB hiérarchisées : Traitement prioritaire du trafic de boucle locale, des sous-réseaux LAN et des flux multidiffusion avant redirection vers le tunnel virtuel.
- Verrouillage au niveau du pare-feu : Synchronisation des règles de routage avec les chaînes
nftables. Si le tunnel vacille, le pare-feu bloque tout paquet non marqué qui tenterait d'emprunter l'interface physique en clair. - Isolation des requêtes système : Les requêtes destinées aux résolveurs locaux sont capturées et transmises au canal chiffré afin d'éviter toute exposition d'adresses IP.
L'articulation coordonnée du marquage des paquets, des bases de règles et des filtres de chemin inverse assure une connectivité fluide, performante et exempte de récursion.



