Routage par politique Debian 13 : déboguer ip rule et ip route sans douleur

Cet article vous a aidé ?

Vous n’avez rien changé de « important », juste ajouté une seconde liaison montante, un VPN ou un pont de conteneur. Soudain, la moitié de votre trafic sortant disparaît,
ou les réponses reviennent par la mauvaise interface et meurent en silence. Les journaux ne disent rien. La supervision indique « perte de paquets ».
Vos collègues disent « le routage Linux, c’est de la magie noire. »

Ce n’est pas de la magie. C’est du routage par politique. C’est déterministe. Et c’est aussi impitoyable comme seuls les sous-systèmes matures peuvent l’être : il fera
exactement ce que vous avez demandé, pas ce que vous vouliez dire. Ceci est un guide de terrain production pour Debian 13 sur le débogage de ip rule et ip route,
avec des commandes que vous pouvez lancer sous pression et des conclusions que vous pouvez expliquer dans un postmortem.

Le modèle mental : ce qui arrive réellement à un paquet

Quand Debian 13 route un paquet, il ne choisit pas « la route par défaut » dans une unique table globale et s’arrête là.
Il exécute un moteur de politique. Ce moteur évalue une liste de règles (ip rule) de haut en bas.
Chaque règle peut correspondre aux métadonnées du paquet (adresse source, destination, fwmark, interface entrante, UID, et plus) et pointer la recherche vers une table de routage.
La table choisie est ensuite recherchée pour la meilleure route (ip route), et ce n’est qu’ensuite que le noyau détermine l’interface de sortie, la passerelle et l’adresse source.

Le point clé est que les tables de routage ne sont pas seulement « les routes ». Elles représentent « un univers possible de routes »,
et les règles décident quel univers s’applique à ce paquet.
Si vous déboguez, ne demandez pas « quelle est ma route ? » Demandez : « quelle règle gagne, quelle table est consultée et quelle route est sélectionnée dans cette table ? »

Ce que la “magie noire” ressemble en pratique

Les défaillances de routage par politique ont une signature : ça fonctionne depuis une IP mais pas une autre ; ping fonctionne mais le TCP non ; l’outbound marche mais les réponses non ;
un sous-réseau est OK tandis qu’un autre est mort. Les pannes de routage standard sont généralement plus uniformes. Les pannes de politique sont sélectives.

Une autre signature : vous lancez ip route, vous voyez une route par défaut parfaitement saine, et pourtant le trafic sort par la mauvaise interface.
C’est parce que le paquet pertinent n’a jamais consulté la table main. Il a consulté une table différente à cause d’une règle dont vous avez oublié l’existence.
Ou une règle insérée par un gestionnaire réseau. Ou un client VPN. Ou votre propre contournement « temporaire » d’il y a trois quarts d’année.

Le routage par politique n’est pas optionnel quand la topologie est complexe

Si vous avez l’un de ces éléments, vous appliquez du routage par politique que vous l’admettiez ou non :

  • Deux liaisons montantes (dual‑WAN, secours LTE, migration d’ISP)
  • Split tunneling VPN
  • Plusieurs adresses source sur un même hôte
  • Kubernetes/CNI avec particularités de routage sur l’hôte
  • Routage piloté par application (UID/cgroup marks)
  • Toute politique de sécurité utilisant des fwmarks pour orienter le trafic

L’astuce est de rendre cela observable et ennuyeux. L’ennui, c’est ce qui préserve vos week‑ends.

Une citation à garder sur votre bureau

L’espoir n’est pas une stratégie. — General Gordon R. Sullivan

L’espoir en routage ressemble souvent à « ça marchait en staging » ou « Linux va s’en sortir tout seul ». Il s’en sortira. Juste pas comme vous le vouliez.

Faits et historique qui expliquent les bizarreries actuelles

  • Le routage par politique sous Linux n’est pas nouveau. Il existe dans le noyau principal depuis l’ère 2.2, et iproute2 s’est développé autour de lui.
  • La table « main » n’est pas spéciale par conception—seulement par convention. Les règles décident quand elle est utilisée ; de nombreux systèmes routent la plupart du trafic via main seulement parce que les règles sont par défaut.
  • ip rule est évalué par priorité, pas par ordre d’insertion. Les priorités peuvent se chevaucher, et « plus petit nombre gagne » est l’ordre réel.
  • Il existe plusieurs règles intégrées même sur un hôte « simple ». Typiquement : lookup table local, puis main, puis default. Ce lookup local explique pourquoi votre hôte peut atteindre ses propres IP même si vous avez tout cassé.
  • Le filtrage par chemin inversé (rp_filter) a été conçu pour la cohérence, pas le multi‑homing. Le mode strict rejette les paquets qui arrivent sur une interface que le noyau n’utiliserait pas pour atteindre la source.
  • Le routage basé sur fwmark est plus ancien que la plupart des carrières. Le marquage Netfilter et le routage par politique sont associés depuis longtemps parce que c’est une séparation propre : classifier avec le pare‑feu, router avec les règles.
  • La « route par défaut » n’est pas unique dans le noyau. Vous pouvez avoir plusieurs routes par défaut, les metrics décident de la préférence, et les règles décident si une route par défaut est même prise en compte.
  • Les gestionnaires réseau aiment ajouter des règles. systemd‑networkd, ifupdown2, les clients VPN et les piles de conteneurs installent souvent leurs propres règles et tables pour l’isolation.
  • Conntrack peut préserver des décisions que vous ne voulez plus. Un flux routé d’une manière peut rester sur cette voie jusqu’à expiration, même après que vous ayez « corrigé » le routage.

Ce ne sont pas des anecdotes. Ce sont les raisons pour lesquelles le routage par politique semble avoir des humeurs.
Il n’en a pas. Vous avez juste changé les règles, et les règles ont changé le monde.

Mode d’emploi pour un diagnostic rapide (premier/deuxième/troisième)

Quand le trafic « choisit mystérieusement » la mauvaise interface, ne commencez pas par éditer des fichiers de configuration. Demandez d’abord au noyau ce qu’il ferait.
Ces vérifications sont ordonnées pour réduire rapidement l’espace du problème.

Premier : confirmer la décision de routage pour un paquet spécifique

  • Utilisez ip route get pour la destination, avec une IP source si pertinent.
  • Si des fwmarks sont impliqués, testez avec la marque via ip route get ... mark (quand pris en charge) ou émulez via l’inspection des règles et la consultation des tables.
  • Confirmez la table choisie via ip rule et la route sélectionnée via ip route show table X.

Deuxième : identifier quelle règle gagne et pourquoi

  • Dump des règles avec priorités. Cherchez les correspondances : from, to, iif, fwmark, uidrange.
  • Vérifiez les règles que vous n’avez pas créées : clients VPN, cloud‑init, networkd, piles de conteneurs.
  • Confirmez que la route existe dans la table consultée ; des routes « blackhole » ou « unreachable » peuvent être volontaires.

Troisième : valider le chemin de retour et le filtrage

  • Vérifiez la sélection de l’adresse source et les réglages de rp_filter.
  • Cherchez le routage asymétrique : sortie par une interface, retours par une autre.
  • Validez le comportement NAT/conntrack ; purgez avec précaution si nécessaire.

Si vous suivez cet ordre, vous évitez généralement le rituel à 2 heures du matin de « redémarrer le réseau jusqu’à ce que ça paraisse réparé ».
Ce rituel transforme un problème de routage en incident.

Tâches pratiques : 12+ commandes, sortie attendue et actions suivantes

Chaque tâche ci‑dessous a trois parties : une commande à exécuter, ce que signifie la sortie, et la décision à prendre à partir de celle‑ci.
C’est la différence entre déboguer et faire une danse interprétative au terminal.

Task 1: Snapshot the rules (with priorities) and spot surprises

cr0x@server:~$ ip -details -statistics rule show
0:	from all lookup local
32764:	from all fwmark 0x1 lookup vpn
32765:	from 192.0.2.10 lookup isp2
32766:	from all lookup main
32767:	from all lookup default

Ce que cela signifie : Le noyau essaiera d’abord la table local. Ensuite, tout paquet marqué 0x1 utilisera la table vpn.
Tout paquet provenant de 192.0.2.10 utilisera isp2. Tout le reste ira vers main.

Décision : Si le trafic « va aléatoirement » vers le VPN ou le mauvais ISP, vous connaissez maintenant les conditions. Si vous ne reconnaissez pas une règle, trouvez quel outil l’a installée avant de la supprimer.

Task 2: List routing tables names and ensure you’re not guessing table IDs

cr0x@server:~$ cat /etc/iproute2/rt_tables
#
# reserved values
#
255	local
254	main
253	default
0	unspec
#
# local
#
100	isp2
200	vpn

Ce que cela signifie : Les noms de table correspondent à des ID numériques. Si des scripts réfèrent à « table 200 » et que quelqu’un la renomme, vous créez une énigme pour le Vous du futur.

Décision : Utilisez des noms dans l’automatisation quand possible (lookup vpn), gardez ce fichier sous gestion de configuration, et traitez les IDs de table comme une partie de l’API stable.

Task 3: Ask the kernel “how would you route this?”

cr0x@server:~$ ip route get 1.1.1.1
1.1.1.1 via 203.0.113.1 dev eth0 src 203.0.113.10 uid 0
    cache

Ce que cela signifie : Pour cette destination, le noyau a choisi la passerelle 203.0.113.1 sur eth0 et utiliserait la source 203.0.113.10.

Décision : Si vous attendiez « utiliser eth1 » ou « source depuis 192.0.2.10 », vous avez un décalage règles/tables, pas un problème applicatif.

Task 4: Test source-based policy explicitly (the common gotcha)

cr0x@server:~$ ip route get 1.1.1.1 from 192.0.2.10
1.1.1.1 via 192.0.2.1 dev eth1 src 192.0.2.10 uid 0
    cache

Ce que cela signifie : Avec la source 192.0.2.10, le noyau utilise maintenant eth1. Cela implique une règle correspondante from 192.0.2.10.

Décision : Si les réponses échouent, vérifiez que le trafic de retour utilise aussi la même source et que rp_filter ne rejette pas des chemins asymétriques.

Task 5: Dump the main table and check the default route and metrics

cr0x@server:~$ ip -statistics route show table main
default via 203.0.113.1 dev eth0 metric 100
default via 192.0.2.1 dev eth1 metric 200
203.0.113.0/24 dev eth0 proto kernel scope link src 203.0.113.10
192.0.2.0/24 dev eth1 proto kernel scope link src 192.0.2.10

Ce que cela signifie : Deux routes par défaut existent ; la metric la plus basse (100) l’emporte quand la table main est utilisée.

Décision : Si vous vouliez une bascule, assurez‑vous d’avoir une vérification d’état (pas seulement des metrics). Si vous vouliez du routage split, les metrics seules ne suffiront pas ; vous avez besoin de règles et de tables séparées.

Task 6: Inspect a non-main table (where the “magic” usually hides)

cr0x@server:~$ ip route show table isp2
default via 192.0.2.1 dev eth1
192.0.2.0/24 dev eth1 scope link src 192.0.2.10

Ce que cela signifie : La table isp2 a sa propre route par défaut et une route connectée. C’est la table minimale sensée pour le routage par politique : elle peut atteindre la passerelle et l’internet.

Décision : Si une table n’a qu’une route par défaut mais manque la route du sous‑réseau connecté, la résolution ARP/voisine peut devenir étrange. Ajoutez la route connectée ou utilisez proto kernel via l’assignation d’adresse.

Task 7: Check for blackhole/unreachable routes that silently “work as designed”

cr0x@server:~$ ip route show table vpn
blackhole 10.0.0.0/8
default via 10.8.0.1 dev tun0
10.8.0.0/24 dev tun0 scope link src 10.8.0.2

Ce que cela signifie : Quelqu’un a intentionnellement blackholé l’espace RFC1918 dans la table VPN. Cela peut empêcher un hairpinning accidentel vers des réseaux d’entreprise.

Décision : Si votre appli doit atteindre des services 10.x et que cela échoue, ce n’est pas un « bug de routage », c’est une décision de politique. Corrigez la politique : retirez ou resserrez le blackhole, ou ajustez les règles pour que seul le trafic spécifique atteigne cette table.

Task 8: Confirm whether rp_filter is dropping your packets

cr0x@server:~$ sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.eth0.rp_filter net.ipv4.conf.eth1.rp_filter
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.eth1.rp_filter = 1

Ce que cela signifie : Le filtrage inverse strict est activé. Dans des configurations multi‑homing/routage par politique, le mode strict rejette souvent du trafic asymétrique légitime.

Décision : Si vous avez une asymétrie intentionnelle ou plusieurs uplinks, passez en mode loose (2) sur les interfaces pertinentes ou globalement—après avoir compris les implications de sécurité.

Task 9: Verify the rules are actually matching (source, iif, fwmark)

cr0x@server:~$ ip rule show
0:	from all lookup local
32764:	from all fwmark 0x1 lookup vpn
32765:	from 192.0.2.10 lookup isp2
32766:	from all lookup main
32767:	from all lookup default

Ce que cela signifie : Les règles sont larges. La règle fwmark correspond à tout paquet marqué de n’importe quelle source. La règle source correspond exactement à une IP.

Décision : Si trop de trafic va vers le VPN, arrêtez de marquer tout le monde. Si trop peu de trafic va vers isp2, élargissez le from à un sous‑réseau ou ajoutez des règles from pour d’autres adresses.

Task 10: Inspect nftables/iptables marking (the hidden steering wheel)

cr0x@server:~$ nft list ruleset | sed -n '1,120p'
table inet filter {
  chain output {
    type filter hook output priority 0; policy accept;
    meta skuid 1001 meta mark set 0x1
  }
}

Ce que cela signifie : Le trafic généré par l’UID 1001 se voit appliquer la marque 0x1 dans le hook OUTPUT, ce qui déclenche la règle de routage fwmark.

Décision : Si la mauvaise appli est redirigée, corrigez la sélection UID, utilisez des marques cgroup, ou marquez uniquement des destinations/ports spécifiques. Évitez des marques trop larges capturant par erreur les mises à jour, la supervision ou le DNS.

Task 11: Use tcpdump with interface focus to prove asymmetry

cr0x@server:~$ sudo tcpdump -ni eth0 'host 1.1.1.1 and (tcp or icmp)'
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:10:55.120001 IP 203.0.113.10 > 1.1.1.1: ICMP echo request, id 511, seq 1, length 64

Ce que cela signifie : L’ICMP sort bien sur eth0. Si les réponses reviennent sur eth1, vous avez trouvé l’asymétrie.

Décision : Si vous voyez la sortie sur une interface et l’entrée sur une autre, vérifiez rp_filter et assurez‑vous que le chemin de retour est fixé avec des règles basées sur la source et des routes par table.

Task 12: Validate neighbor/ARP on the intended egress interface

cr0x@server:~$ ip neigh show dev eth1
192.0.2.1 lladdr 52:54:00:12:34:56 REACHABLE

Ce que cela signifie : Le MAC de la passerelle est connu et joignable. S’il était FAILED ou manquant, le routage pourrait être correct mais la connectivité L2 absente.

Décision : Si la résolution voisine échoue, cessez d’accuser ip rule. Vérifiez le tag VLAN, les ports de switch, les groupes de sécurité ou les réglages d’ARP filtering.

Task 13: Check the local table and rule 0 (because localhost lies)

cr0x@server:~$ ip route show table local | head
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 203.0.113.10 dev eth0 proto kernel scope host src 203.0.113.10
local 192.0.2.10 dev eth1 proto kernel scope host src 192.0.2.10
broadcast 203.0.113.0 dev eth0 proto kernel scope link src 203.0.113.10

Ce que cela signifie : La table local explique pourquoi vous pouvez toujours atteindre vos propres adresses même si votre routage principal est cassé.
Elle peut masquer des problèmes lors des tests (« curl vers mon IP marche ! »).

Décision : Lors du débogage, testez depuis un autre hôte, ou utilisez ip route get pour voir les véritables décisions d’egress. Le succès via la table local n’est pas une preuve d’atteignabilité externe.

Task 14: Inspect conntrack for flows that keep taking the old path

cr0x@server:~$ sudo conntrack -L -p tcp 2>/dev/null | head
tcp      6 431999 ESTABLISHED src=203.0.113.10 dst=93.184.216.34 sport=42412 dport=443 src=93.184.216.34 dst=203.0.113.10 sport=443 dport=42412 [ASSURED] mark=1 use=1

Ce que cela signifie : Ce flux est établi et porte mark=1. Même si vous changez les règles de marquage, les connexions existantes peuvent persister sur leur route d’origine.

Décision : Si vous testez des changements de routage, utilisez de nouvelles connexions (port source différent) ou supprimez chirurgicalement les entrées conntrack pour le flux de test. Évitez de vider conntrack en production sauf si vous aimez devoir tout expliquer.

Task 15: Validate that the right source address is chosen (and fix with ip route if needed)

cr0x@server:~$ ip route show table isp2 default
default via 192.0.2.1 dev eth1

Ce que cela signifie : La route par défaut ne spécifie pas src. Le noyau peut choisir une adresse source que vous n’aviez pas prévue si plusieurs adresses existent.

Décision : Dans les environnements multi‑adresses, définissez explicitement src sur les routes clés des tables de politique pour rendre le comportement stable et débogable.

Blague #1 : Le routage par politique, c’est comme la politique de bureau : l’organigramme officiel existe, mais les vraies décisions se prennent dans les règles annexes.

Trois mini-histoires d’entreprise (comment les adultes cassent le routage)

Incident #1 : La mauvaise hypothèse (« main table signifie comportement par défaut »)

Une société SaaS de taille moyenne est passée d’un ISP unique à deux uplinks : une fibre primaire et un routeur LTE de secours.
La flotte d’hôtes avait plusieurs IP par machine à cause d’allowlists héritées. Tout semblait correct après un test rapide :
ip route montrait la route fibre par défaut avec une metric inférieure. Parfait. Déployez.

Le lendemain matin, un sous‑ensemble d’appels API sortants a commencé à avoir des timeouts. Pas tous. Pas même la plupart.
Seulement les appels d’un service spécifique, s’exécutant sous un utilisateur système dédié, et seulement vers quelques réseaux partenaires.
L’équipe a cherché du côté DNS. Elles ont cherché le MTU. Elles ont cherché le pare‑feu du partenaire. Le partenaire les a fait revenir.

Le vrai problème était une seule ip rule insérée des mois plus tôt par un client VPN « temporaire » :
from all fwmark 0x1 lookup vpn. Le trafic de l’utilisateur du service était marqué par une vieille règle nftables que personne n’avait retenue.
Ce trafic ne consultait jamais la table main. Il allait dans la table VPN, qui avait une route blackhole pour l’un des espaces RFC1918 du partenaire utilisé derrière du NAT.

La mauvaise hypothèse était subtile : « si ip route a l’air correct, le routage est correct. » Ce n’est pas vrai.
ip route sans table ou sans requête get affiche seulement l’univers par défaut.
La correction fut ennuyeuse : retirer la règle de marquage obsolète, restreindre l’orientation VPN aux seules destinations voulues, et ajouter un test de régression de routage en CI exécutant ip route get avec les sources des services.

Action postmortem : « Aucune règle de politique ne va en production sans propriétaire et commentaire dans la gestion de configuration. » Ce n’est pas de la bureaucratie ; c’est de la mémoire.

Incident #2 : L’optimisation qui se retourne contre vous (« marquons tout pour la performance »)

Une grande entreprise avait une couche de proxy Debian avec deux sorties : un lien internet bon marché pour le trafic volumineux et un lien premium pour les appels API sensibles à la latence.
Quelqu’un a eu l’idée intelligente : marquer le trafic par UID de processus et laisser le routage par politique faire le reste. Plus besoin d’ACLs complexes côté proxy.
Ça fonctionnait. Et ça a encouragé davantage de « brillances ».

L’optimisation consistait à marquer tout le trafic sortant d’un groupe de services pour utiliser le lien premium, sous l’argument que cela réduisait la latence tail.
La règle nftables était large : « si UID dans cette plage, set mark 0x1. »
Cela incluait non seulement le service, mais l’agent de mise à jour, un expéditeur de métriques et un renouvelleur TLS exécutés sous le même compte.

Pendant un moment, rien ne casse. Puis une instabilité de routage survient sur le lien premium lors d’une fenêtre de maintenance.
Le design de basculement s’appuyait sur des metrics dans la table main, mais le trafic marqué contourna complètement main et allait dans une table de politique avec une unique route par défaut.
Quand la passerelle premium est devenue injoignable, le trafic marqué n’a pas basculé. Il s’est simplement arrêté.
La supervision a affiché des symptômes étranges parce que le trafic non marqué allait bien et le trafic marqué mourait en silence.

La correction n’était pas d’abandonner le routage par politique. La correction fut de respecter les modes de défaillance :
fournir une route par défaut de secours dans la table de politique avec une metric supérieure, ou utiliser une règle qui retombe sur main lorsque la route premium est indisponible.
Et cesser d’utiliser le marquage par UID comme instrument grossier ; ciblez les préfixes de destination ou les ports quand c’est possible.

Blague #2 : Si vous « optimisez » le routage sans plan de défaillance, vous avez inventé une nouvelle façon pour les paquets d’aller déjeuner longtemps.

Incident #3 : La pratique ennuyeuse qui a sauvé la mise (« route get comme test standard »)

Une fintech exploitait des hôtes Debian avec des exigences strictes de conformité : tout le trafic de base de données devait transiter sur un réseau privé, le reste sur l’internet.
Ils avaient plusieurs NIC, plusieurs VLAN et un VPN pour l’accès admin. Beaucoup d’éléments mobiles.
L’équipe réseau ne s’en remettait pas au savoir tribal ; elle a codifié le comportement de routage sous forme de tests.

Lors d’un cycle de mise à jour OS, un changement dans la gestion de configuration réseau a involontairement permuté un ID de table : vpn est passé de 200 à 201 sur certains hôtes.
Les règles faisaient toujours référence à lookup 200 dans un script legacy, donc ces machines avaient une règle pointant vers une table vide.
Le noyau a chuté sur main pour le trafic admin, ce qui était « acceptable » jusqu’à ce que l’équipe sécurité remarque des connexions admin provenant de la mauvaise IP d’egress et le signale.

Le salut fut ennuyeux : leur pipeline de déploiement exécutait une suite d’assertions de route sur chaque hôte après les changements réseau.
Il exécutait des vérifications ip route get pour des destinations représentatives avec des sources représentatives, incluant les sous‑réseaux admin, les API partenaires et les plages privées de base de données.
L’assertion pour le trafic admin a échoué immédiatement sur les hôtes affectés. Pas d’incident, pas de mystère, juste un voyant rouge.

Ils ont rollbacké, corrigé les scripts pour utiliser des noms de table au lieu d’IDs numériques, et gardé les tests.
Le système est resté compliqué. Le comportement est resté prédictible. C’est la vraie victoire.

Erreurs courantes : symptôme → cause racine → correction

1) « Le trafic sort par la mauvaise interface, mais ip route semble correct »

Symptôme : ip route montre la valeur par défaut attendue, mais les paquets partent par un autre NIC.

Cause racine : Une règle de politique envoie ce trafic vers une table différente (fwmark, source‑based, iif‑based).

Correction : Utilisez ip rule show et ip route get DEST from SRC. Corrigez la règle gagnante ou la table consultée. Ne faites plus confiance à la vue de la table main seule.

2) « L’outbound marche, les réponses sont rejetées (ou vice versa) »

Symptôme : Les SYN partent, les SYN‑ACK ne complètent jamais, ou les réponses ICMP disparaissent.

Cause racine : Routage asymétrique plus rp_filter=1 (strict) ou anti‑spoofing en amont.

Correction : Fixez le chemin de retour avec des règles basées sur la source et des routes connectées par table ; mettez rp_filter=2 où approprié ; assurez‑vous du bon src sur les routes de politique.

3) « Le split tunnel VPN fuit du trafic ou tout détourne »

Symptôme : Du trafic passe inopinément par le VPN, ou le trafic censé aller par le VPN va direct.

Cause racine : Règle de marquage trop large, mauvaise priorité de règle, ou routes spécifiques manquantes dans la table VPN.

Correction : Restreignez le marquage aux destinations/ports ; vérifiez la priorité de la règle VPN au‑dessus de main ; assurez‑vous que la table VPN contient des routes explicites pour les préfixes tunnelés et une stratégie par défaut délibérée.

4) « Une table de politique a une default mais rien ne passe »

Symptôme : Les paquets sélectionnent la bonne table mais n’atteignent jamais la passerelle.

Cause racine : Route connectée manquante dans cette table, passerelle injoignable sur l’interface, ou problèmes ARP/voisin.

Correction : Ajoutez la route link dans cette table ; vérifiez ip neigh ; confirmez l’IP d’interface et la connectivité L2.

5) « Après avoir corrigé les règles, le comportement ne change pas pour les connexions actives »

Symptôme : Les nouveaux flux se comportent correctement, les anciens flux continuent d’échouer ou d’utiliser l’ancien chemin.

Cause racine : Les entrées conntrack préservent l’état et les marques pour les flux établis.

Correction : Testez avec de nouvelles connexions ; supprimez des entrées conntrack spécifiques ; envisagez de réduire les timeouts pour les flux impactés pendant la réponse à l’incident plutôt que de tout purger.

6) « Les paquets sont routés correctement, mais l’IP source est mauvaise »

Symptôme : Le trafic sort par l’interface prévue mais utilise une adresse source inattendue, provoquant des rejets en amont.

Cause racine : La sélection d’adresse source choisit une autre adresse sur cette interface ou depuis une autre interface à cause du scope de route et des labels d’adresse.

Correction : Spécifiez src sur les routes critiques dans les tables de politique ; assurez‑vous que chaque interface a la bonne adresse primaire ; vérifiez avec ip route get ... from ....

7) « Tout marche jusqu’à une panne de lien, puis seul une partie du trafic bascule »

Symptôme : Le trafic non marqué bascule bien ; le trafic marqué ou source‑routé reste bloqué.

Cause racine : Les tables de politique n’ont pas de default secondaire ou n’ont pas de basculement basé sur l’état ; les metrics de la table main ne s’appliquent pas aux recherches de table de politique.

Correction : Construisez le basculement à l’intérieur des tables de politique (default secondaire avec metric plus haute) ou implémentez un basculement sensible à l’état du lien avec des changements de règle explicites.

8) « Le routage marche en interactif, échoue dans le service en production »

Symptôme : Votre curl manuel marche ; le daemon plante.

Cause racine : Marquage basé sur UID, différences de namespace réseau, ou service lié à une IP source spécifique.

Correction : Vérifiez les règles nftables qui marquent dans OUTPUT ; vérifiez l’utilisateur de l’unité service et son namespace réseau ; validez les paramètres de bind du service.

Listes de contrôle / plans pas à pas à confier à l’astreinte

Checklist A: « Le trafic prend la mauvaise sortie » (15 minutes, sans modifications)

  1. Exécutez un snapshot des règles.

    cr0x@server:~$ ip rule show
    0:	from all lookup local
    32764:	from all fwmark 0x1 lookup vpn
    32765:	from 192.0.2.10 lookup isp2
    32766:	from all lookup main
    32767:	from all lookup default
    

    Décision : Identifiez la règle vraisemblablement correspondante pour la classe de trafic (IP source ? fwmark ?).

  2. Lancez une requête de décision de route pour la destination exacte, puis avec la source suspectée.

    cr0x@server:~$ ip route get 93.184.216.34
    93.184.216.34 via 203.0.113.1 dev eth0 src 203.0.113.10 uid 0
        cache
    
    cr0x@server:~$ ip route get 93.184.216.34 from 192.0.2.10
    93.184.216.34 via 192.0.2.1 dev eth1 src 192.0.2.10 uid 0
        cache
    

    Décision : Confirmez si le mauvais comportement dépend de la source. Si oui, concentrez‑vous sur les règles sources et les routes par table.

  3. Dump de la table consultée.

    cr0x@server:~$ ip route show table isp2
    default via 192.0.2.1 dev eth1
    192.0.2.0/24 dev eth1 scope link src 192.0.2.10
    

    Décision : Si la table manque d’éléments essentiels (route connectée, passerelle correcte), corrigez la table ; ne « corrigez » pas en changeant main.

  4. Prouvez le chemin des paquets avec tcpdump sur les deux interfaces (petre capture).

    cr0x@server:~$ sudo tcpdump -ni eth0 'host 93.184.216.34 and tcp'
    tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
    
    cr0x@server:~$ sudo tcpdump -ni eth1 'host 93.184.216.34 and tcp'
    tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
    listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
    

    Décision : Si vous observez de l’asymétrie, passez à rp_filter et aux règles de chemin de retour.

Checklist B: « Le split tunnel VPN se comporte mal »

  1. Identifiez la table VPN et la règle.

    cr0x@server:~$ ip rule show | grep -i vpn
    32764:	from all fwmark 0x1 lookup vpn
    

    Décision : Si la sélection VPN est basée sur une marque, auditez le marquage. Si elle est basée sur la source, auditez le bind/source de l’application.

  2. Inspectez les règles de marquage.

    cr0x@server:~$ nft list ruleset | grep -n 'mark set' | head
    12:    meta skuid 1001 meta mark set 0x1
    

    Décision : Si le match est trop large, restreignez‑le. Marquez seulement ce que vous pouvez justifier par écrit.

  3. Confirmez que la table VPN contient les routes que vous souhaitez tunneliser.

    cr0x@server:~$ ip route show table vpn
    blackhole 10.0.0.0/8
    default via 10.8.0.1 dev tun0
    10.8.0.0/24 dev tun0 scope link src 10.8.0.2
    

    Décision : Si vous devez atteindre certains préfixes privés, retirez ou resserrez les blackholes et ajoutez des routes explicites.

Checklist C: « Le basculement ne fonctionne pas pour une partie du trafic »

  1. Déterminez quelle table ce trafic utilise (règle correspondant).

    cr0x@server:~$ ip -details rule show
    0:	from all lookup local
    32764:	from all fwmark 0x1 lookup vpn
    32765:	from 192.0.2.10 lookup isp2
    32766:	from all lookup main
    32767:	from all lookup default
    

    Décision : Si ce n’est pas main, les metrics de la table main ne vous sauveront pas.

  2. Assurez‑vous que la table de politique possède une route par défaut secondaire (si votre conception l’exige).

    cr0x@server:~$ ip route show table isp2 | grep '^default'
    default via 192.0.2.1 dev eth1
    

    Décision : S’il n’y a qu’une seule default et que la passerelle tombe, ce trafic restera bloqué. Ajoutez un chemin de secours dans cette table ou implémentez des changements de règle à la détection d’échec de lien.

FAQ

1) Pourquoi ip route me trompe‑t‑il ?

Il ne vous trompe pas ; il affiche la table main par défaut. Le routage par politique peut envoyer votre paquet vers une table différente.
Utilisez ip rule show et ip route get DEST from SRC pour voir la décision réelle.

2) Quelle est la différence entre ip route show et ip route get ?

show liste les routes dans une table. get demande au noyau de résoudre une destination spécifique (et optionnellement une source),
retournant la passerelle choisie, l’interface et l’IP source. En incident, get est votre sérum de vérité.

3) Puis‑je avoir deux routes par défaut sous Debian 13 ?

Oui. Le noyau choisit en fonction des metrics dans une table. Mais si des règles de politique orientent le trafic vers une autre table, ce seront les defaults de cette table qui s’appliquent.
Les doubles defaults sont normales ; les doubles defaults non gérées sont le chaos.

4) Pourquoi le routage par politique casse‑t‑il seulement pour une IP source ?

Parce que beaucoup de politiques sont basées sur la source : ip rule add from 192.0.2.10 lookup isp2.
Les applis qui se lient à une source spécifique, ou le noyau qui choisit une source différente, peuvent changer la règle qui correspond.

5) Dois‑je désactiver rp_filter pour le routage par politique ?

Pas toujours, mais le mode strict (1) entre souvent en conflit avec l’asymétrie intentionnelle dans les configurations multi‑homing.
Le mode loose (2) est un compromis courant. Gardez la sécurité en tête : rp_filter aide contre le spoofing, ne le modifiez pas à la légère.

6) Pourquoi ma correction a‑t‑elle fonctionné pour les nouvelles connexions mais pas pour les anciennes ?

Conntrack conserve l’état des flux établis, y compris les marques dans de nombreuses configurations. Les anciens flux peuvent rester liés aux anciennes décisions de routage.
Testez avec de nouvelles connexions ou supprimez chirurgicalement les entrées conntrack pour les flux spécifiques.

7) Quelle est la manière la plus propre d’implémenter le split tunneling ?

Placez uniquement les préfixes tunnelisés dans la table VPN et gardez une politique par défaut délibérée (soit pas de défaut, soit une default seulement pour le trafic marqué).
Ensuite, orientez seulement le trafic voulu avec des règles spécifiques (basées sur la destination quand possible ; marques quand nécessaire).

8) Comment savoir quel service provoque le routage basé sur fwmark ?

Regardez les règles nftables qui définissent des marks (souvent en s’appuyant sur UID/cgroup). Puis mappez l’UID à un service.
Inspectez aussi les entrées conntrack pour mark= afin de voir quels flux sont marqués.

9) Dois‑je utiliser des IDs numériques de table directement dans les scripts ?

Évitez‑le quand vous le pouvez. Utilisez des tables nommées et gardez /etc/iproute2/rt_tables stable.
Les IDs numériques sont acceptables, mais sujets à dérive sur une flotte et difficiles à auditer dans un postmortem.

10) Est‑ce acceptable de « tout flush » (règles et routes) pendant un incident ?

Sur une machine de production qui transporte du trafic réel : non, pas comme première action.
Vous pouvez couper SSH, casser des bindings de service et effacer les preuves dont vous aviez besoin pour diagnostiquer. Faites un snapshot d’abord, puis changez chirurgicalement.

Conclusion : étapes suivantes pour réduire la douleur future

Le routage par politique sur Debian 13 n’est « magie noire » que si vous le traitez comme du folklore.
Traitez‑le comme un programme : les règles sont le flux de contrôle, les tables sont les données, et ip route get est votre débogueur.
Le noyau est cohérent. Votre configuration peut ne pas l’être.

Étapes pratiques qui rapportent :

  • Standardisez sur des tables de routage nommées et gardez /etc/iproute2/rt_tables sous gestion de configuration.
  • Ajoutez un test de régression de routage minimal : une poignée de vérifications ip route get pour destinations et sources critiques.
  • Auditez ip rule et les règles de marquage nftables trimestriellement ; supprimez les règles « temporaires » avant qu’elles ne deviennent de la politique.
  • Décidez explicitement comment fonctionne le basculement dans chaque table de politique. Si vous n’avez pas conçu la défaillance, vous avez conçu la surprise.
  • Documentez le propriétaire et l’objet de chaque règle non‑par défaut. Si elle n’a pas de propriétaire, elle n’a pas le droit d’exister.

Faites cela, et la « magie noire du routage » redeviendra ce qu’elle a toujours été : une série de recherches déterministes que vous pouvez raisonner,
même quand le pager hurle.

← Précédent
Ubuntu 24.04 — « Stale file handle » sur NFS : pourquoi cela arrive et comment l’empêcher
Suivant →
Sauvegardes ZFS immuables : readonly et politiques de snapshots qui tiennent la route

Laisser un commentaire