Black hole PMTU : Gitea SSH inaccessible depuis l'extérieur (gros paquets perdus)
Date : juillet 2026
Symptômes
- Gitea SSH (port 8329) inaccessible depuis un réseau mobile 4G, web OK (le web passe par Caddy qui termine sa propre connexion TCP ; le SSH va en direct vers le serveur).
- La connexion TCP s'établit (SYN/SYN-ACK OK), le handshake SSH démarre, puis blocage.
- « Ça fonctionnait hier » : rien n'a changé dans l'infra — c'est le chemin opérateur mobile du client qui a changé (autre antenne, autre nœud CGNAT).
Diagnostic
1. tcpdump sur le serveur (.9) :
sudo tcpdump -ni any port 8329
Signature caractéristique : les petits paquets passent, mais un trou de séquence apparaît — le serveur acquitte ack 43 et signale en SACK avoir reçu 1447:1611. Le segment 43:1447 (~1404 octets de données, 1456 octets sur le fil) n'arrive jamais, même réémis.
... Flags [.], ack 43, ... sack 1 {1447:1611} ...
2. tcpdump sur le routeur (apk add tcpdump — OpenWrt récent utilise apk, plus opkg) : le gros segment n'apparaît jamais sur wan In → perdu en amont du routeur, sur le chemin opérateur. L'infra locale est hors de cause.
3. Test MTU depuis le client (réflexe à retenir) :
ping -M do -s 1428 -c 2 git.beafrancois.fr # 1456 octets sur le fil
- Réponse (même une erreur ICMP de la Freebox) → le paquet passe.
- Silence total → droppé sans ICMP « Frag needed » = black hole PMTU.
Résultat mesuré : 1428 (fil) passe, 1456 silence — alors que la gateway 4G annonçait MTU 1456. Équipement opérateur défaillant.
Cause
Un équipement du chemin opérateur mobile a une MTU réelle < 1456 et droppe les paquets trop gros sans envoyer l'ICMP « fragmentation needed ». Le TCP du client ne peut donc pas détecter le problème (PMTU discovery cassée) et réémet indéfiniment le même segment trop gros.
Le mtu_fix d'OpenWrt (MSS clamping de la zone wan) était actif mais inopérant : il clampe selon la MTU du WAN du routeur (1500), alors que le goulot est ailleurs.
Solution : MSS clamp forcé sur le routeur
Forcer un MSS bas dans tous les SYN forwardés : le client négocie des segments plus petits qui passent partout. Protège tous les clients externes quel que soit leur réseau.
Sur le routeur (aiguilleur, .90) :
cat > /etc/nftables.d/10-mss-clamp.nft << 'EOF' chain mssfix { type filter hook forward priority mangle; policy accept; tcp flags syn tcp option maxseg size > 1388 tcp option maxseg size set 1388 } EOF /etc/init.d/firewall restart
MSS 1388 = paquets de 1428 octets max (la valeur validée par le test). Impact débit : ~4 % de payload en moins par segment, négligeable.
Pérennité après sysupgrade :
echo '/etc/nftables.d/10-mss-clamp.nft' >> /etc/sysupgrade.conf
Notes
/etc/nftables.d/survit aux reboots et auxfirewall restart; seul le sysupgrade nécessite l'entrée danssysupgrade.conf.- Si un jour un chemin a une MTU < 1428, ça recassera : descendre le MSS (1240 couvre même l'IPv6 minimal à 1280).
- Ne pas confondre avec le problème du 14 juillet (mêmes symptômes apparents) : routes parasites ConnMan sur le serveur — vérifier d'abord
ip route show(voir doc dédiée).
