Les serveurs Debian à deux interfaces échouent d’une manière très précise et particulièrement frustrante : tout « fonctionne » jusqu’à ce que ça ne fonctionne plus.
SSH se fige 20 secondes, les contrôles de santé oscillent, la réplication de stockage s’interrompt, et les flux TCP se réinitialisent sans autre motif que « c’est pire aux heures de pointe ».
Le responsable est souvent le routage asymétrique : les paquets arrivent sur la NIC A et repartent par la NIC B, si bien que des équipements à état (ou votre noyau) concluent que vous mentez.
Ceci est le cas n°53 : deux interfaces, deux passerelles, un serveur, et une montagne de pertes aléatoires que vous ne parvenez pas à reproduire à la demande.
La forme de la défaillance : à quoi ressemble l’asymétrie en production
Les configurations à deux NIC sont censées être ennuyeuses. Une NIC pour le « front », une pour le « back », ou peut‑être une pour la gestion et une pour le stockage.
Dans la réalité, la frontière s’estompe. Quelqu’un ajoute une seconde route par défaut « au cas où ». Ou un démon de routage apprend quelque chose que vous ne vouliez pas annoncer. Ou vous branchez les deux NIC sur des réseaux qui ont chacun leur opinion sur vos paquets.
Le routage asymétrique n’est pas « mauvais » en soi. L’internet fonctionne en asymétrie toute la journée. Le problème survient quand l’asymétrie traverse
des points d’étranglement à état : pare-feux, NAT, équilibreurs, conntrack, appareils DSR, et parfois le noyau Linux lui‑même quand le reverse-path filtering est activé trop strictement.
Symptômes typiques :
- Réeset TCP intermittents, surtout sur des flux longue durée (réplication, base de données, iSCSI/NFS, streaming d’API).
- SSH parfois bloqué juste après la connexion ou pendant un scp ; les nouvelles tentatives « règlent » le problème.
- La supervision montre des pertes de paquets, mais seulement depuis certains sous‑réseaux sources.
- Le trafic entrant arrive sur une interface, mais les réponses sortent par l’autre (vu avec tcpdump).
- Journaux du noyau comme
martian sourceou réponses silencieusement abandonnées quandrp_filterest strict.
Si vous avez deux passerelles par défaut sur la même machine et aucun routage par politique, vous n’avez pas de redondance.
Vous avez un pile ou face avec des conséquences.
Faits et contexte intéressants (oui, le réseautage a sa tradition)
- Le policy routing sous Linux est plus ancien que beaucoup de pratiques « modernes » du cloud. Le framework
ip ruleest arrivé à la fin des années 1990 avec Linux 2.2 et a mûri dans 2.4/2.6. - Le reverse path filtering (rp_filter) a été popularisé pour contrer l’usurpation d’adresses. C’est une fonctionnalité de sécurité qui peut devenir un piège sur les hôtes multi‑homés.
- L’asymétrie de routage n’est pas intrinsèquement mauvaise. Elle devient problématique lorsqu’un intermédiaire à état s’attend à voir les deux directions d’un flux sur le même chemin.
- Linux dispose de plusieurs mécanismes de sélection de route. Le meilleur préfixe l’emporte au sein d’une table, puis les règles de routage décident quelle table est consultée et quand.
- L’ARP flux existe réellement. Si Linux répond à ARP pour la mauvaise interface, des pairs envoient du trafic vers la mauvaise MAC et vous poursuivez des fantômes.
- ECMP (equal-cost multipath) peut ressembler à des « pertes aléatoires ». C’est déterministe par hash de flux, mais l’application le perçoit comme du chaos quand les intermédiaires ne sont pas d’accord.
- Conntrack n’est pas qu’un détail de pare‑feu. Même des règles « laisser tout passer » dépendant de l’état sont tributaires de conntrack ; les retours hors chemin sont marqués INVALID et rejetés.
- Systemd‑networkd a changé l’expérience par défaut pour beaucoup d’administrateurs. Debian 13 facilite la production de multiples routes par défaut à moins d’être délibéré.
Playbook de diagnostic rapide
Quand les paquets « tombent au hasard », vous n’avez pas le temps pour de longues discussions théoriques. Vous voulez un entonnoir rapide :
confirmer l’asymétrie, identifier qui abandonne, puis rendre le routage déterministe.
1) Prouvez (ou infirmez) l’asymétrie en 5 minutes
- Vérifiez les tables de routage et les règles :
ip route,ip rule. - Capturez l’entrée/la sortie sur les deux NIC pendant un flux défaillant :
tcpdumpsur les deux interfaces. - Confirmez la sélection d’adresse source :
ip route get <dest> from <src>.
2) Trouvez le « dropper » : noyau, pare‑feu ou réseau
- Cherchez rp_filter/martians :
journalctl -k,sysctl net.ipv4.conf.*.rp_filter. - Cherchez des drops conntrack INVALID :
nft list rulesetet compteurs, ouiptables -L -vsi vous êtes encore téméraire. - Vérifiez les erreurs/drops sur les NIC :
ip -s link,ethtool -S.
3) Rendre le routage déterministe (ne « tunez » pas d’abord)
- Choisissez une sortie par sous‑réseau source avec le policy routing.
- Réglez rp_filter en mode
loose(2) sur les hôtes multi‑homés sauf si vous savez exactement ce que vous appliquez. - Arrêtez d’annoncer/utiliser deux routes par défaut sans metrics et règles.
Blague #1 : Si votre routage dépend de « la passerelle qui se sent chanceuse aujourd’hui », félicitations — vous avez inventé le load balancing, mais sans ses bénéfices.
Modèle mental : routage Linux, règles et pourquoi Debian 13 vous surprend
Vous déboguez le routage à deux NIC en comprenant trois couches de décision :
sélection d’adresse, recherche dans une table de routage, et règles de policy routing.
La plupart des pannes proviennent de l’hypothèse que Linux « répondra par la même interface par laquelle c’est arrivé ».
Cette hypothèse est attendrissante. Elle est aussi fausse.
Sélection de route : les tables
Linux maintient des tables de routage. La plupart des systèmes utilisent la table main par défaut. Quand vous lancez ip route,
vous regardez typiquement la table main. Le meilleur préfixe l’emporte, et si plusieurs routes sont égales, les metrics et l’ECMP s’appliquent.
Les problèmes à deux NIC commencent quand les deux NIC ajoutent une route par défaut dans main. Linux choisit alors une sortie
selon les metrics (si différentes) ou ECMP (si identiques). Cela peut être stable par flux, mais ne s’alignera pas sur les attentes de votre réseau.
Policy routing : les règles
ip rule vous permet de choisir quelle table consulter en fonction des propriétés du paquet : adresse source, fwmark, interface entrante,
TOS, plages d’UID, et plus. En pratique, pour les serveurs à deux NIC, le routage basé sur la source est l’outil de travail :
« Le trafic issu de 192.0.2.10 utilise la table 100, qui a une passerelle par défaut A. »
Reverse path filtering : rp_filter
rp_filter vérifie si l’adresse source d’un paquet entrant est joignable via l’interface par laquelle il est arrivé.
Avec le mode strict (1), le multi‑homing peut casser parce que la « meilleure route de retour » peut passer par l’autre NIC.
Le mode loose (2) est typiquement ce que vous voulez pour les hôtes multi‑homés : il vérifie la joignabilité, mais pas nécessairement via la même interface.
Conntrack et filtrage à état
Si vous utilisez nftables/iptables avec des règles à état (la plupart des gens le font, parfois sans s’en rendre compte), l’asymétrie peut amener les paquets de retour à être vus comme
INVALID parce que conntrack n’a pas observé la direction initiale sur ce chemin. Alors un paquet parfaitement valide est rejeté.
Le paquet n’est pas « mauvais ». C’est votre topologie qui l’est.
ARP et sélection des voisins
Un autre méchant discret est le comportement ARP sur des hôtes avec plusieurs interfaces sur le même domaine L2 ou des préfixes qui se chevauchent.
Linux peut répondre aux requêtes ARP sur une interface avec la MAC d’une autre, ou choisir une adresse source qui embrouille les pairs.
Le résultat : le trafic arrive où vous ne l’attendiez pas, et votre « correction de routage » devient une session de tape‑sur‑le‑mole.
Tâches pratiques (commandes, sorties, décisions)
Les tâches suivantes sont écrites comme si vous étiez de garde et que le pager est encore chaud. Chacune inclut :
une commande, un extrait réaliste de sortie, ce que ça signifie, et la décision à prendre.
Task 1 — Inventaire des interfaces et adresses
cr0x@server:~$ ip -br addr
lo UNKNOWN 127.0.0.1/8 ::1/128
enp1s0 UP 192.0.2.10/24 fe80::a00:27ff:fe12:3456/64
enp2s0 UP 198.51.100.10/24 fe80::a00:27ff:fe65:4321/64
Signification : Deux NIC, deux sous‑réseaux IPv4. C’est OK. Ça cesse d’être OK quand les deux revendiquent d’être « l’internet » (deux routes par défaut).
Décision : Confirmez à quoi sert chaque sous‑réseau (frontend/backend/gestion). Notez‑le. Si personne ne le sait, vous êtes déjà en difficulté.
Task 2 — Vérifier la table de routage principale pour plusieurs défauts
cr0x@server:~$ ip route show table main
default via 192.0.2.1 dev enp1s0 proto dhcp metric 100
default via 198.51.100.1 dev enp2s0 proto dhcp metric 100
192.0.2.0/24 dev enp1s0 proto kernel scope link src 192.0.2.10 metric 100
198.51.100.0/24 dev enp2s0 proto kernel scope link src 198.51.100.10 metric 100
Signification : Deux défauts à métrique égale. Linux peut ECMP cela. Votre pare‑feu probablement pas.
Décision : Vous avez besoin de policy routing ou d’un seul défaut. Ne « changez les metrics » que si le comportement de bascule vous convient et que vous le testez.
Task 3 — Inspecter les règles de policy routing
cr0x@server:~$ ip rule show
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
Signification : Pas de règles de policy routing. Tout consulte main, donc l’asymétrie est possible.
Décision : Ajoutez des règles basées sur la source et des tables par interface (ou supprimez la route par défaut supplémentaire).
Task 4 — Demandez à Linux « comment routerez‑tu ceci ? » pour chaque source
cr0x@server:~$ ip route get 203.0.113.50 from 192.0.2.10
203.0.113.50 via 198.51.100.1 dev enp2s0 src 192.0.2.10 uid 0
cache
Signification : Le trafic sourcé depuis 192.0.2.10 sortirait via enp2s0. C’est de l’asymétrie classique.
Décision : Implémentez « source 192.0.2.10 utilise la passerelle 192.0.2.1 » via policy routing.
Task 5 — Vérifier rp_filter (le mode strict est un tueur silencieux fréquent)
cr0x@server:~$ sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.enp1s0.rp_filter net.ipv4.conf.enp2s0.rp_filter
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.enp1s0.rp_filter = 1
net.ipv4.conf.enp2s0.rp_filter = 1
Signification : Le reverse‑path filtering strict est activé. Avec le multi‑homing, il abandonne souvent des paquets légitimes.
Décision : Passez en mode loose (2) ou désactivez (0) selon votre modèle de menace et topologie. Loose est le choix sensé pour le routage à deux NIC.
Task 6 — Chercher des indices noyau : martians, drops rp_filter, bizarreries de voisin
cr0x@server:~$ journalctl -k --since "2 hours ago" | tail -n 8
Dec 30 09:11:04 server kernel: IPv4: martian source 203.0.113.50 from 203.0.113.50, on dev enp1s0
Dec 30 09:11:04 server kernel: ll header: 00000000: 00 1b 21 22 33 44 00 1b 21 aa bb cc 08 00
Dec 30 09:12:18 server kernel: nf_conntrack: table full, dropping packet
Signification : « Martian source » se corrèle souvent avec rp_filter ou une incohérence de routage. Aussi : la table conntrack est pleine, ce qui engendre des drops qui paraissent « aléatoires ».
Décision : Corrigez le routage d’abord. Ensuite ajustez la dimension de conntrack si nécessaire. Si conntrack est plein, vous ne déboguez pas le routage — vous déboguez une surcharge aussi.
Task 7 — Vérifier l’utilisation de conntrack
cr0x@server:~$ sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_count = 262144
net.netfilter.nf_conntrack_max = 262144
Signification : Vous êtes au plafond. Les nouveaux flux sont abandonnés ou contournés selon les règles. Dans tous les cas : douleur.
Décision : Si cet hôte gère beaucoup de connexions (proxies, NAT, API très chargée), augmentez le max et confirmez la marge mémoire. Réduisez aussi les timeouts d’inactivité si approprié.
Task 8 — Inspecter les règles nftables et compteurs pour les drops INVALID
cr0x@server:~$ nft list ruleset | sed -n '1,120p'
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
ct state invalid counter packets 1843 bytes 110580 drop
iif "lo" accept
tcp dport 22 accept
}
}
Signification : INVALID est en train d’être rejeté, et le compteur augmente. L’asymétrie est une cause majeure.
Décision : N’acceptez pas les INVALID comme pansement. Corrigez le routage pour que les paquets soient correctement trackés.
Task 9 — Vérifier les statistiques d’interface pour drops réels vs drops de routage
cr0x@server:~$ ip -s link show dev enp1s0
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 00:1b:21:aa:bb:cc brd ff:ff:ff:ff:ff:ff
RX: bytes packets errors dropped missed mcast
91433921 812334 0 0 0 1290
TX: bytes packets errors dropped carrier collsns
88122301 799221 0 0 0 0
Signification : Pas de drops au niveau NIC. Les « pertes aléatoires » ont donc probablement lieu au niveau des règles/policy/conntrack, ou en amont.
Décision : Restez concentré : règles de routage, rp_filter, conntrack, état du pare‑feu.
Task 10 — Capturer le trafic sur les deux NIC pour prouver l’asymétrie
cr0x@server:~$ sudo tcpdump -ni enp1s0 host 203.0.113.50 and tcp port 443 -c 6
09:14:22.110001 IP 203.0.113.50.443 > 192.0.2.10.53122: Flags [S.], seq 1200, ack 900, win 65160, options [mss 1460], length 0
09:14:22.110050 IP 203.0.113.50.443 > 192.0.2.10.53122: Flags [.], ack 901, win 65160, length 0
cr0x@server:~$ sudo tcpdump -ni enp2s0 host 203.0.113.50 and tcp port 443 -c 6
09:14:22.110120 IP 192.0.2.10.53122 > 203.0.113.50.443: Flags [.], ack 1201, win 501, length 0
09:14:22.110200 IP 192.0.2.10.53122 > 203.0.113.50.443: Flags [P.], seq 901:1041, ack 1201, win 501, length 140
Signification : SYN/ACK est arrivé sur enp1s0, mais ACK/données sont partis sur enp2s0. C’est de l’asymétrie, prouvée par reçu.
Décision : Mettez en place du policy routing pour que les réponses au trafic destiné à 192.0.2.10 sortent par enp1s0 (ou assurez la symétrie en amont, ce qui est plus difficile).
Task 11 — Vérifier le comportement neighbor/ARP pour flux
cr0x@server:~$ ip neigh show nud reachable,stale | head
192.0.2.1 dev enp1s0 lladdr 00:11:22:33:44:55 REACHABLE
198.51.100.1 dev enp2s0 lladdr 00:11:22:33:44:66 STALE
Signification : Rien d’évidemment cassé ici. Mais si vous voyez la même IP de voisin reachable via deux interfaces, ou beaucoup d’entrées FAILED, suspectez l’ARP flux ou un problème de conception L2.
Décision : Si les réseaux se chevauchent ou partagent un VLAN, appliquez des contrôles ARP (section ultérieure) et corrigez le plan d’adressage.
Task 12 — Vérifier le routage par source une fois le policy routing ajouté
cr0x@server:~$ ip route get 203.0.113.50 from 192.0.2.10
203.0.113.50 via 192.0.2.1 dev enp1s0 src 192.0.2.10 uid 0
cache
Signification : Maintenant la sortie correspond au sous‑réseau source. C’est le comportement déterministe souhaité.
Décision : Relancez la validation tcpdump et surveillez les compteurs nft. Si les drops INVALID cessent d’augmenter, vous venez d’acheter de la stabilité.
Task 13 — Confirmer qu’aucun défaut ECMP accidentel ne subsiste
cr0x@server:~$ ip route show default
default via 192.0.2.1 dev enp1s0 proto static metric 100
default via 198.51.100.1 dev enp2s0 proto static metric 200
Signification : Vous pouvez garder deux défauts avec des metrics différentes pour la bascule, mais vos règles de policy doivent être cohérentes.
Décision : Préférez un seul défaut dans main, et mettez l’autre défaut seulement dans une table dédiée. Les modèles mixtes embrouillent le vous du futur.
Task 14 — Vérifier l’état de systemd-networkd (réalité Debian 13)
cr0x@server:~$ networkctl status enp1s0 | sed -n '1,40p'
● 2: enp1s0
Link File: /usr/lib/systemd/network/99-default.link
Network File: /etc/systemd/network/10-enp1s0.network
State: routable (configured)
Online state: online
Address: 192.0.2.10/24
Gateway: 192.0.2.1
Signification : networkd gère votre routage. C’est bien si vous le configurez intentionnellement, et chaotique si vous laissez le DHCP répandre des routes par défaut.
Décision : Mettez la policy routing dans la configuration networkd pour qu’elle survive au reboot et ne dépende pas d’un collage héroïque en cas d’alerte.
Schémas de correction qui tiennent la route
Pattern A : Routage basé sur la source (recommandé pour la plupart des serveurs à deux NIC)
Objectif : le trafic issu de l’IP de l’interface A utilise la passerelle de l’interface A ; le trafic issu de l’IP de l’interface B utilise la passerelle de l’interface B.
Cela empêche les réponses asymétriques sans nécessiter de changements en amont.
Vous implémentez cela avec :
- Deux tables de routage (une par NIC)
- Deux entrées
ip rulecorrespondant aux sous‑réseaux sources - Des routes dans chaque table : sous‑réseau connecté + défaut via sa passerelle
Configuration immédiate (runtime) avec iproute2
cr0x@server:~$ sudo ip route add 192.0.2.0/24 dev enp1s0 src 192.0.2.10 table 100
cr0x@server:~$ sudo ip route add default via 192.0.2.1 dev enp1s0 table 100
cr0x@server:~$ sudo ip route add 198.51.100.0/24 dev enp2s0 src 198.51.100.10 table 200
cr0x@server:~$ sudo ip route add default via 198.51.100.1 dev enp2s0 table 200
cr0x@server:~$ sudo ip rule add from 192.0.2.10/32 table 100 priority 1000
cr0x@server:~$ sudo ip rule add from 198.51.100.10/32 table 200 priority 1001
Cela fonctionne, mais disparaît au reboot à moins d’être persisté. Ne soyez pas cette personne.
Configuration persistante avec systemd-networkd (compatible Debian 13)
Exemple /etc/systemd/network/10-enp1s0.network :
cr0x@server:~$ sudo sed -n '1,200p' /etc/systemd/network/10-enp1s0.network
[Match]
Name=enp1s0
[Network]
Address=192.0.2.10/24
Gateway=192.0.2.1
DNS=192.0.2.53
[RoutingPolicyRule]
From=192.0.2.10/32
Table=100
Priority=1000
[Route]
Destination=0.0.0.0/0
Gateway=192.0.2.1
Table=100
Et /etc/systemd/network/20-enp2s0.network :
cr0x@server:~$ sudo sed -n '1,200p' /etc/systemd/network/20-enp2s0.network
[Match]
Name=enp2s0
[Network]
Address=198.51.100.10/24
Gateway=198.51.100.1
DNS=198.51.100.53
[RoutingPolicyRule]
From=198.51.100.10/32
Table=200
Priority=1001
[Route]
Destination=0.0.0.0/0
Gateway=198.51.100.1
Table=200
Puis redémarrez networkd :
cr0x@server:~$ sudo systemctl restart systemd-networkd
cr0x@server:~$ ip rule show | sed -n '1,10p'
0: from all lookup local
1000: from 192.0.2.10 lookup 100
1001: from 198.51.100.10 lookup 200
32766: from all lookup main
32767: from all lookup default
Pattern B : Une route par défaut, un réseau « spécial » (meilleur quand une NIC est vraiment privée)
Si enp2s0 est strictement un réseau de stockage et ne doit jamais être utilisé pour l’internet ou les réponses clients, ne lui donnez aucune route par défaut.
Donnez‑lui seulement la route du sous‑réseau connecté, et peut‑être quelques routes explicites vers les pairs de stockage.
Cela élimine une classe entière de pannes. L’interface devient un « tuyau bête vers le sous‑réseau X ».
La meilleure politique de routage est celle dont vous n’avez pas besoin.
Pattern C : Routage basé sur fwmark (pour apps complexes et VIP)
Si vous avez plusieurs adresses sources sur une interface (VIPs), des conteneurs, ou vous devez orienter seulement certains trafics,
vous pouvez marquer les paquets dans nftables et router en fonction du fwmark.
C’est plus puissant et plus sujet aux erreurs. Utilisez‑le quand les règles basées sur la source ne suffisent pas.
rp_filter : réglez‑le délibérément, pas par superstition
Sur les hôtes multi‑homés, rp_filter strict est souvent incompatible avec le policy routing et l’asymétrie légitime.
Le mode loose est le compromis habituel : il exige toujours une route de retour vers la source, mais pas nécessairement via la même interface.
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.all.rp_filter=2
net.ipv4.conf.all.rp_filter = 2
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.default.rp_filter=2
net.ipv4.conf.default.rp_filter = 2
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.enp1s0.rp_filter=2
net.ipv4.conf.enp1s0.rp_filter = 2
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.enp2s0.rp_filter=2
net.ipv4.conf.enp2s0.rp_filter = 2
Persistez via /etc/sysctl.d/99-multihome.conf :
cr0x@server:~$ sudo tee /etc/sysctl.d/99-multihome.conf >/dev/null <<'EOF'
net.ipv4.conf.all.rp_filter=2
net.ipv4.conf.default.rp_filter=2
EOF
cr0x@server:~$ sudo sysctl --system | tail -n 4
* Applying /etc/sysctl.d/99-multihome.conf ...
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
Contrôles ARP : empêcher de « répondre sur la mauvaise NIC »
Si les deux NIC sont dans le même L2 ou que vous avez des routes qui se chevauchent, ajustez le comportement ARP pour réduire le flux :
arp_ignore et arp_announce.
Ce n’est pas toujours nécessaire, mais quand c’est le cas, c’est la différence entre la raison et une danse d’interprétation.
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.all.arp_ignore=1
net.ipv4.conf.all.arp_ignore = 1
cr0x@server:~$ sudo sysctl -w net.ipv4.conf.all.arp_announce=2
net.ipv4.conf.all.arp_announce = 2
Une maxime fiabilité (idée paraphrasée)
Idée paraphrasée (attribuée à Richard Cook) : « Le succès n’est pas l’absence d’échec ; c’est la présence d’une capacité d’adaptation. »
Trois mini‑récits du monde de l’entreprise
Mini‑récit 1 : L’incident causé par une mauvaise hypothèse
Une entreprise SaaS de taille moyenne exploitait des serveurs Debian avec deux NIC : un VLAN public pour le trafic client, un VLAN privé pour base de données et sauvegarde.
L’équipe supposait que les réponses sortiraient par la même NIC qui avait reçu la requête. Ils l’avaient « vu marcher » en labo.
Un lundi, les connexions clients ont commencé à vaciller. Pas une panne complète ; juste lent, en pics, et étrange. L’équilibreur montrait des SYN/ACK revenant en retard,
et la moitié des handshakes TLS échouaient. Les ingénieurs ont chassé le CPU, puis les certificats, puis l’équilibreur. Les graphiques étaient tous légèrement faux de différentes façons.
Le vrai problème était simple : un renouvellement DHCP sur la NIC privée a réintroduit une route par défaut avec la même métrique que la NIC publique.
Soudain, certaines réponses au trafic client sortaient par la gateway privée. Le pare‑feu périmétrique les a rejetées car il n’avait pas d’état pour cette direction.
Du point de vue du serveur, il « avait envoyé » le paquet. Du point de vue du client, le serveur s’était évaporé.
Le correctif n’a rien d’héroïque : épingler une route par défaut dans main, utiliser le policy routing basé sur la source pour l’interface secondaire, et arrêter d’accepter des routes par défaut via DHCP sur le VLAN backend.
Le postmortem avait une ligne digne d’encadrement : « Nous avons supposé la symétrie ; nous avons configuré le hasard. »
Mini‑récit 2 : L’optimisation qui a mal tourné
Une autre organisation a tenté « d’optimiser la latence » en activant strict rp_filter partout.
Leur baseline de sécurité considérait cela comme une protection gratuite contre l’usurpation. C’était déployé automatiquement sur une flotte incluant des hôtes multi‑homés.
Quelques semaines plus tard, la réplication de stockage a commencé à tomber sous charge, mais seulement entre certains racks. Le TCP s’établissait, transférait des données,
puis se bloquait. Les nouvelles tentatives réussissaient. Tout le monde a d’abord blâmé le fournisseur de stockage. Puis le MTU. Puis quelqu’un a blâmé l’ASIC du switch, parce que c’est toujours l’ASIC du switch quand on est fatigué.
Il s’est avéré que le policy routing était déjà en place, mais le rp_filter strict n’aimait pas la recherche de route de retour dans certains cas où la « meilleure route » différait de l’ingress.
Le noyau rejetait silencieusement des paquets légitimes comme martians. La réplication est retombée sur des retries et des timeouts, transformant un mismatch de politique en effondrement du débit.
L’« optimisation » a rendu le système fragile. Le mode loose a rétabli la stabilité, et ils ont gardé la protection anti‑usurpation là où elle a du sens : en bordure réseau,
avec des ACL explicites, pas des sysctls par souhait.
Mini‑récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la journée
Une grande équipe plateforme interne avait une règle : chaque hôte multi‑homé doit avoir une note d’« intention de routage » d’une page dans le repo.
Elle décrivait quelle interface possède quelles adresses sources, quelle est la route par défaut, et quelles règles policy existent. Pas de poésie. Juste la vérité.
Lors d’une migration de datacenter, un nouveau cluster de pare‑feux a été introduit en amont. Un petit sous‑ensemble de services a commencé à voir des 502 intermittents.
Les équipes applicatives ont escaladé ; l’équipe réseau jurait que rien n’avait changé « pour ces VLANs ». Ça sentait un problème L7, mais la perte de paquets sentait L3.
L’SRE de garde a ouvert la note d’intention de routage et a immédiatement remarqué que l’hôte était conçu pour répondre via une NIC spécifique en utilisant la table 200.
Un rapide ip rule a montré que ces règles manquaient sur la nouvelle variante d’image. Le système a démarré correctement. Il a aussi démarré faux.
Ils ont restauré la config networkd prévue, redéployé, et le problème a disparu. Pas de drama. Pas de war room. Pas de blâme sur les sentiments du pare‑feu.
La documentation ennuyeuse plus une config déterministe n’est pas glamour, mais c’est la différence entre un incident et un message Slack.
Blague #2 : La seule chose plus aléatoire que le routage asymétrique est la réunion où tout le monde affirme que c’est « définitivement le DNS ».
Erreurs fréquentes : symptômes → cause racine → correctif
1) SSH se fige ou se bloque de façon intermittente
Symptômes : SSH se connecte, puis marque une pause ; scp se bloque ; les nouvelles tentatives aident.
Cause racine : Les réponses sortent par la mauvaise NIC ; un pare‑feu à état ou un ACL amont rejette le trafic de retour.
Fix : Routage basé sur la source par interface ; assurez‑vous d’un seul défaut dans main ; validez avec tcpdump sur les deux NIC.
2) nftables montre des drops INVALID en hausse
Symptômes : Le compteur ct state invalid drop augmente ; les utilisateurs voient des échecs aléatoires.
Cause racine : Conntrack ne voit qu’une direction à cause de l’asymétrie (ou la table conntrack est pleine).
Fix : Corrigez d’abord la symétrie du routage ; puis dimensionnez correctement conntrack et assurez‑vous de ne pas tracker inutilement du trafic volumineux.
3) Des logs « martian source » apparaissent
Symptômes : Le noyau journalise des martians ; des paquets apparaissent sur la « mauvaise » interface.
Cause racine : rp_filter strict sur un hôte multi‑homé, ou routes/règles incorrectes.
Fix : Réglez rp_filter en loose (2) et implémentez du policy routing pour aligner la recherche de route avec la réalité.
4) Ça marche jusqu’à un renouvellement DHCP, puis ça casse
Symptômes : Toutes les quelques heures/jours ça part en vrille ; un reboot « répare » temporairement.
Cause racine : DHCP installe ou modifie des routes par défaut/metrics ; le routage redevient non déterministe.
Fix : Désactivez la route par défaut via DHCP sur la NIC secondaire, ou isolez le DHCP à une interface ; persistez les routes via networkd.
5) Seuls certains sous‑réseaux distants échouent
Symptômes : La plupart des clients fonctionnent ; une région/fournisseur oscille.
Cause racine : Le chemin de retour de ces clients emprunte un amont différent (règles de pare‑feu/NAT différentes), rendant l’asymétrie visible.
Fix : Imposer une sortie déterministe par source ; assurer que l’amont voit des chemins cohérents.
6) « On a mis des metrics, donc c’est bon »
Symptômes : Majoritairement stable, mais la bascule ou des échecs partiels produisent des trous noirs.
Cause racine : Les metrics décident de la préférence, mais ne garantissent pas que les réponses suivent l’interface d’ingress, surtout avec plusieurs sources.
Fix : Utilisez le policy routing ; les metrics servent la préférence, pas la correction.
7) Le trafic de stockage ou de réplication fond sous charge
Symptômes : Le débit s’effondre, les retransmissions montent, timeouts.
Cause racine : Épuisement de la table conntrack, ou filtrage à état plus asymétrie, ou les deux.
Fix : Évitez de tracker le trafic qui n’en a pas besoin ; ajustez la taille de conntrack ; corrigez le routage pour que les flux restent cohérents.
8) Bizarrerie ARP : le trafic arrive sur la mauvaise NIC même si le routage est correct
Symptômes : Les pairs envoient des paquets à la « mauvaise » MAC ; le basculement se comporte étrangement.
Cause racine : ARP flux : l’hôte répond à l’ARP depuis la mauvaise interface ; ou domaines L2 qui se chevauchent.
Fix : Séparez les domaines L2 ; ajustez arp_ignore/arp_announce ; assurez des sous‑réseaux uniques et un design VLAN propre.
Listes de contrôle / plan étape par étape
Étape par étape : arrêter le routage asymétrique sur Debian 13 (ordre sûr pour la production)
-
Capturer l’état courant (avant de toucher quoi que ce soit) :
ip -br addrip route show table mainip rule showsysctl net.ipv4.conf.all.rp_filter
Décision : confirmez que vous êtes vraiment multi‑homé (pas juste des alias) et si deux défauts existent.
-
Prouvez l’asymétrie avec tcpdump sur les deux NIC pendant un flux défaillant.
Décision : si ingress et egress diffèrent pour le même flux, passez au policy routing. Sinon, vous avez peut‑être un MTU, une congestion, ou un filtrage amont.
-
Décidez votre intention :
- Une NIC est le défaut pour tout, l’autre est privée uniquement (meilleur).
- Les deux NIC servent de vrais clients/peers et doivent router correctement (policy routing requis).
Décision : écrivez l’intention dans un commentaire de config. « On s’en souviendra » n’est pas un plan.
-
Implémentez le policy routing (tables + règles) via networkd ou ifupdown.
Décision : préférez la persistance networkd sur Debian 13 si c’est déjà lui qui gère les interfaces.
-
Réglez rp_filter en loose (2) pour les hôtes multi‑homés.
Décision : si la sécurité exige strict, demandez qu’ils signent une revue d’incident quand ça casse. Loose est le compromis réaliste.
-
Validez avec ip route get pour des destinations représentatives depuis chaque adresse source.
Décision : si une source choisit la « mauvaise » passerelle, les tables/règles sont incomplètes.
-
Re‑vérifiez les compteurs du pare‑feu (nft INVALID drops, etc.).
Décision : si les INVALID augmentent encore, vérifiez que conntrack n’est pas plein et que le trafic ne contourne pas le chemin attendu.
-
Testez en charge ou rejouez un trafic similaire à la production.
Décision : si les échecs n’apparaissent qu’en charge, contrôlez la taille de conntrack, l’IRQ saturation, le qdisc, et la police en amont — pas seulement le routage.
-
Rendez‑le durable :
- Commettre les fichiers networkd et la config sysctl dans votre gestion de configuration.
- Documenter l’intention de routage et une sortie « known‑good » de
ip ruleetip route show table 100/200.
Décision : si le correctif peut être annulé par un renouvellement DHCP, vous ne l’avez pas fixé.
Checklist opérationnelle : vérification après changement (10 minutes)
- Confirmer les règles :
ip rule show - Confirmer les tables :
ip route show table 100,ip route show table 200 - Confirmer rp_filter :
sysctl net.ipv4.conf.all.rp_filter - Confirmer la stabilisation des compteurs nft :
nft list chain inet filter input(ou équivalent) - Confirmer la symétrie du trafic avec tcpdump lors d’une transaction de test
- Confirmer qu’aucun défaut surprise via DHCP n’apparaît après renouvellement (ou attendre la fenêtre de renouvellement)
FAQ
1) Puis‑je simplement régler des metrics et considérer le problème réglé ?
Les metrics décident la préférence, pas la correction. Elles ne garantissent pas que les réponses utilisent la même interface que l’adresse source,
et elles ne corrigent pas les attentes des dispositifs stateful. Utilisez le policy routing pour la correction ; utilisez les metrics pour la préférence/la bascule.
2) Ai‑je vraiment besoin de policy routing si les sous‑réseaux sont différents ?
Si il n’y a qu’une seule route par défaut et que l’autre interface n’a pas de défaut, vous pouvez souvent éviter le policy routing.
Si les deux interfaces ont des passerelles ou si vous sourcez du trafic des deux adresses, le policy routing est la conception sûre.
3) Quel réglage rp_filter devrais‑je utiliser sur des hôtes dual‑NIC ?
Typiquement 2 (loose). Le mode strict (1) rejette fréquemment du trafic légitime en multi‑homing.
Désactivez (0) seulement si vous comprenez les implications anti‑usurpation et avez des contrôles compensatoires.
4) Pourquoi les pertes semblent‑elles aléatoires ?
Parce que les décisions de routage peuvent varier par flux (ECMP hash), par entrée du cache, ou après des renouvellements DHCP.
Ajoutez l’état conntrack et les attentes d’un pare‑feu amont, et vous obtenez des échecs qui dépendent du timing et de la forme du trafic.
5) Cela s’applique‑t‑il aussi à IPv6 ?
Oui, mais les mécanismes diffèrent (rp_filter IPv6 n’est pas identique, et les règles de sélection d’adresse source sont plus riches).
Le policy routing existe pour IPv6 avec ip -6 rule et des routes par table ; testez soigneusement car IPv6 permet plusieurs adresses sources valides par conception.
6) Je vois « nf_conntrack: table full. » Est‑ce du routage ?
Pas directement, mais cela crée des pertes qui s’y ressemblent. Corrigez d’abord l’asymétrie du routage, puis dimensionnez conntrack.
Si vous suivez des millions de flux courts, vous aurez besoin à la fois de plus de capacité et d’une meilleure stratégie de filtrage.
7) Dois‑je accepter les paquets INVALID pour arrêter les drops ?
Non. C’est comme désactiver un détecteur de fumée parce qu’il est bruyant. INVALID signifie souvent asymétrie, inadéquation de timeout, ou trafic attaquant.
Corrigez le chemin pour que les paquets redeviennent valides.
8) Comment gérer la bascule entre passerelles ?
Si vous avez vraiment besoin de bascule, conservez un routage déterministe par source et utilisez des mécanismes contrôlés :
routage dynamique (BFD/FRR), routes suivies, ou changements de metrics pilotés par de l’automatisation. Évitez « deux défauts égaux » à moins de vouloir ECMP et d’en comprendre l’effet de bout en bout.
9) Le bonding (LACP) ne résoudrait‑il pas le problème ?
Le bonding résout un autre problème : redondance/agrégation de lien au niveau L2. Il peut aider si les deux liens sont sur le même domaine L2 et que vous voulez une interface logique unique.
Il ne remplace pas un routage L3 correct quand les réseaux/passerelles sont différents.
10) Pourquoi ça casse seulement quand on ajoute un pare‑feu ?
Parce que les dispositifs à état imposent la symétrie pour les flux qu’ils suivent. Sans eux, l’internet peut tolérer l’asymétrie.
Une fois que vous introduisez de l’état, le chemin fait partie du contrat.
Prochaines étapes à entreprendre cette semaine
Corriger le cas n°53 ne consiste pas seulement à faire cesser les pertes aujourd’hui. Il s’agit d’éviter que le prochain changement ne les ressuscite.
Voici la liste pratique à faire qui survit au turnover du personnel et aux expérimentations réseau « temporaires ».
- Décidez et documentez l’intention de routage pour chaque hôte dual‑NIC : quelle NIC gère quel trafic, et pourquoi.
- Éliminez les défauts doubles accidentels : une seule route par défaut dans main, ou policy routing explicite avec tables séparées.
- Réglez rp_filter consciemment : mode loose pour le multi‑homing, persistez via sysctl.d.
- Validez avec trois outils :
ip route get,tcpdumpsur les deux NIC, et compteurs du pare‑feu (nft/conntrack). - Rendez la config persistante dans systemd‑networkd (ou votre gestionnaire réseau choisi) et engagez‑la dans l’automatisation.
- Surveillez conntrack si vous êtes stateful : capacité, timeouts, et si vous trackez du trafic inutile.
- Faites une répétition après changement : simulez une bascule de gateway si vous prétendez avoir de la haute disponibilité, et observez ce qui se passe réellement.
Le routage à deux NIC n’est pas difficile. Ce qui est difficile, c’est prétendre qu’il n’existe pas jusqu’à ce qu’il choisisse une journée chargée pour vous le rappeler.
Rendez‑le déterministe et revenez résoudre des problèmes qui, au moins, ont un peu d’intérêt.