Le protocole de contrôle de transmission (TCP, RFC 793) constitue la couche de transport fondamentale pour les communications fiables sur Internet. Bien que l’établissement des flux via la poignée de main en trois étapes fasse l’objet d’une attention technique constante, la fermeture de connexion est régie par une machine à états tout aussi complexe. Dès qu’un point d’extrémité prend l’initiative de fermer une session TCP établie, celle-ci bascule obligatoirement dans l’état TIME_WAIT. Au lieu de libérer immédiatement les ressources, le système d’exploitation maintient intentionnellement le socket en mémoire pendant une durée définie.

Sur les passerelles réseau à haut débit, les serveurs mandataires inverses à forte concurrence et les concentrateurs VPN de couche 3, cet état TIME_WAIT soulève des défis d’ingénierie majeurs. À cadence élevée, les sockets en attente s’accumulent rapidement, menaçant de saturer l’espace des ports éphémères sur 16 bits et d’entraîner des rejets massifs de connexions. Par ailleurs, des faiblesses protocolaires documentées dans la RFC 1337 sous le terme d’assassinat de l’état TIME_WAIT exposent les moteurs de suivi de connexion à des clôtures prématurées et à des corruptions silencieuses de données. Comprendre la dynamique mathématique de cette rétention et maîtriser l’optimisation du noyau Linux sont indispensables pour préserver un débit ininterrompu.

Le dilemme de la fermeture de connexion : pourquoi TCP persiste en TIME_WAIT

TCP est un protocole bidirectionnel simultané (full-duplex), ce qui permet de clore l’acheminement des données dans un sens tout en continuant à recevoir des segments dans l’autre. Une clôture gracieuse exige l’échange de quatre trames de contrôle : un paquet FIN émis par l’initiateur, un accusé de réception ACK du pair, un FIN émis par le pair, et un ACK final renvoyé par l’initiateur. L’hôte qui expédie le premier FIN est désigné comme le fermeur actif (active closer).

Ce fermeur actif doit obligatoirement basculer dans l’état TIME_WAIT dès qu’il transmet le dernier paquet ACK. Les réseaux IP fonctionnant selon le modèle du meilleur effort (best-effort) sans garantie absolue de latence, cette ultime confirmation peut être retardée ou perdue en transit. Si le pair passif ne reçoit pas ce dernier ACK, son temporisateur de retransmission expire, ce qui l’amène à réémettre son paquet FIN. Si l’hôte actif avait immédiatement détruit son socket et restitué le port local au système, il répondrait à ce FIN répété par un paquet de réinitialisation (RST), interrompant brutalement la séquence de clôture et signalant au pair une erreur protocolaire fatale.

En préservant l’identité du socket—définie par le quadruplet (4-tuple) comprenant l’IP source, le port source, l’IP de destination et le port de destination—le fermeur actif peut intercepter et acquitter les retransmissions tardives, assurant une terminaison propre des deux côtés. Les utilisateurs analysant l’intégrité de leur transport via /what-is-my-ip dépendent directement de cette fermeture ordonnée pour éviter des sessions orphelines.

Anatomie de l’état TIME_WAIT : norme RFC 793 et durée 2MSL

La norme RFC 793 stipule que le fermeur actif doit persister en TIME_WAIT pendant une durée équivalant au double de la durée de vie maximale d’un segment ($2 \times \text{MSL}$ - Maximum Segment Lifetime). Le MSL représente l’estimation de la durée maximale pendant laquelle un datagramme IP peut transiter à travers les routeurs d’Internet avant que son champ TTL n’atteigne zéro.

Le double objectif : drainage des paquets retardés et clôture sécurisée

La période d’attente $2\text{MSL}$ répond à deux impératifs architecturaux distincts :

  1. Livraison garantie des confirmations finales : Maintenir le socket ouvert pendant une durée supérieure au délai d’aller-retour (RTT) garantit que tout paquet FIN retransmis reçoit un ACK valide.
  2. Drainage complet des segments obsolètes : Des datagrammes retardés par des instabilités de routage dynamique ou des files d’attente saturées peuvent atteindre leur destination bien après la fin théorique d’une session. Sans une période de temporisation $2\text{MSL}$, une nouvelle connexion ouverte immédiatement sur le même quadruplet accepterait des paquets résiduels errants, corrompant les flux applicatifs.

Cycle de vie mathématique : l’équation de rétention des sockets sur 60 secondes

À l’origine, la RFC 793 recommandait une valeur MSL de 120 secondes (soit 4 minutes d’attente). Cependant, les implémentations modernes du noyau Linux fixent le MSL à 30 secondes (TCP_TIMEWAIT_LEN = 60 * HZ), imposant un intervalle standard immuable de 60 secondes :

$$T_{\text{hold}} = 2 \times \text{MSL} = 2 \times 30,\text{s} = 60,\text{secondes}$$

Durant ces 60 secondes, le noyau Linux compense l’impact mémoire en transformant les structures lourdes struct tcp_sock (environ 2 Ko de mémoire slab) en descripteurs légers struct inet_timewait_sock (environ 256 octets). Toutefois, le port local associé reste verrouillé dans la table de routage jusqu’à l’expiration complète du compte à rebours.

Vulnérabilités RFC 1337 : dangers de l’anéantissement de l’état TIME_WAIT

En mai 1992, le chercheur Robert Braden a publié la RFC 1337, décrivant une faille structurelle majeure dans les implémentations TCP standard, qualifiée d’assassinat de TIME-WAIT (TIME-WAIT Assassination). Cette vulnérabilité se produit lorsqu’un segment inattendu parvient à un socket se trouvant dans l’état TIME_WAIT.

Selon la spécification RFC 793 d’origine, si un segment reçu porte le drapeau RST, correspond au quadruplet actif et s’inscrit dans la fenêtre de réception courante, le système d’exploitation doit immédiatement détruire l’état TIME_WAIT et libérer le socket. Un attaquant prédisant les numéros de séquence ou un équipement intermédiaire altérant le trafic peut injecter un faux reset pour forcer la fermeture anticipée du socket.

Les conséquences de cette clôture prématurée sont critiques :

  • Réutilisation hâtive du port : Le port local réintègre le catalogue disponible sans délai. Une nouvelle session initiée vers le même serveur distant peut se voir attribuer ce quadruplet alors que des paquets de la session précédente circulent encore sur le réseau.
  • Injection silencieuse de données : Les segments résiduels portant des numéros de séquence coïncidant avec la nouvelle fenêtre sont intégrés au flux utile, provoquant des corruptions applicatives irréversibles.

La RFC 1337 recommande aux systèmes d’exploitation d’ignorer systématiquement les paquets RST dirigés vers un socket en état TIME_WAIT, laissant le compteur de 60 secondes s’épuiser normalement.

La crise d’épuisement des ports éphémères : saturation de la limite des 65 535

L’en-tête de transport réserve un entier non signé de 16 bits pour identifier le port source, limitant l’espace d’adressage à 65 535 ports par adresse IP. Sur une distribution Linux standard, la plage de ports éphémères allouables est encadrée par le paramètre net.ipv4.ip_local_port_range, couramment fixé à 32768 60999, ce qui n’offre que 28 232 ports effectifs.

Lorsqu’une passerelle ou un mandataire sortant initialise plusieurs centaines de connexions brèves par seconde, la saturation devient inéluctable :

$$\text{Sockets en attente} = R_{\text{conn}} \times T_{\text{hold}}$$

À une cadence soutenue de 500 connexions par seconde ($R_{\text{conn}} = 500$) :

$$500 \times 60 = 30,000,\text{sockets en état TIME_WAIT}$$

Comme 30 000 dépasse la capacité de 28 232 ports disponibles, le noyau échoue à lier de nouveaux sockets sortants et renvoie l’erreur POSIX EADDRNOTAVAIL (Cannot assign requested address). Les requêtes DNS échouent et les utilisateurs subissent des interruptions de service immédiates.

Recyclage des sockets dans le noyau Linux : risques de tcp_tw_recycle face à tcp_tw_reuse

Pour remédier au manque de ports, deux options de noyau ont historiquement été exploitées : tcp_tw_recycle et tcp_tw_reuse. Néanmoins, leurs implications sur la sécurité divergent radicalement.

L’ancien mécanisme tcp_tw_recycle permettait une récupération agressive des sockets en s’appuyant sur l’horodatage par hôte distant (TCP Timestamps selon la RFC 7323). Le noyau conservait l’horodatage le plus récent reçu de chaque IP distante et rejetait d’emblée tout paquet SYN entrant dont l’horodatage était inférieur. Si cela fonctionnait en laboratoire isolé, le mécanisme s’est avéré désastreux sur l’Internet public. Lorsque plusieurs utilisateurs partagent la même passerelle CGNAT ou un proxy d’entreprise, les horloges de leurs appareils ne sont pas synchronisées. Le serveur distant rejetait arbitrairement des connexions légitimes. En conséquence, la communauté du noyau Linux a définitivement supprimé tcp_tw_recycle dans la version 4.12.

À l’inverse, tcp_tw_reuse offre une solution fiable car elle s’applique uniquement aux connexions sortantes générées localement. Conforme à la RFC 6191, tcp_tw_reuse = 2 autorise le noyau à allouer un socket en TIME_WAIT pour une nouvelle session sortante si et seulement si l’horodatage du nouveau paquet ($TS_{\text{val}}$) est strictement supérieur au dernier horodatage de la session close. Cela prévient l’injection de segments tardifs tout en libérant immédiatement les ressources d’adressage.

Allocation de ports et dynamique conntrack sous fort trafic NAT

Les moteurs de filtrage réseau basés sur Netfilter (iptables ou nftables) s’appuient sur le module de suivi des connexions (nf_conntrack). Ce sous-système intègre sa propre machine d’états, indépendante de la couche des sockets du noyau.

Par défaut, Netfilter applique une durée de rétention propre pour les flux terminés :

net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120

La table conntrack conserve donc ses entrées pendant 120 secondes—soit le double des 60 secondes du socket système. Lors de fortes affluences de requêtes, la table de suivi peut saturer (nf_conntrack: table full, dropping packet) bien avant que la mémoire vive ne soit entamée. Aligner les temporisations de conntrack sur celles des sockets et activer la randomisation des ports sources selon la RFC 6056 sont essentiels pour pérenniser les passerelles NAT.

Architecture de couche 3 RiceGuard : élimination de la pénurie de ports

Les architectures de relais conventionnelles interrompent la session TCP de l’utilisateur pour en négocier une nouvelle vers le serveur cible. Ce modèle démultiplie l’épuisement des ports éphémères sur l’interface de sortie.

Le cœur de réseau RiceGuard résout ce goulet d’étranglement en opérant comme un routeur cryptographique natif de couche 3. Articulé autour du protocole WireGuard, RiceGuard encapsule directement les datagrammes IP dans des trames UDP, préservant la session TCP de l’utilisateur de bout en bout sans altération :

+---------------------------------------------------------------+
| En-tête IP externe | En-tête UDP (WireGuard) | Chiffrement    |
+---------------------------------------------------------------+
| En-tête IP interne (IP client -> IP serveur cible)            |
| En-tête TCP interne (Port source éphémère d’origine préservé) |
| Données TCP internes (Flux applicatif chiffré)                |
+---------------------------------------------------------------+

La session TCP étant établie directement entre le terminal client et le serveur cible, les nœuds passerelles VPN intermédiaires n’ont pas à instancier de sockets TCP locaux pour le trafic des utilisateurs. Les grappes de sortie exploitent une infrastructure Anycast multi-hébergée avec translation SNAT matérielle sous règle --random-fully, répartissant les flux sur des millions de combinaisons d’adresses et de ports. À l’instar des modèles de résilience analysés dans notre étude sur /news/bgp-route-flap-damping-rfc-2439-suppression-resiliency, RiceGuard garantit une fluidité continue même lors de perturbations d’itinéraires.

Les utilisateurs recherchant un tunnel véloce et hautement sécurisé peuvent installer les applications officielles depuis /download sur leurs appareils.

Tests de performance empiriques : débits de connexion, gigue et densité de sockets

Pour mesurer rigoureusement l’impact de l’optimisation du noyau sous un afflux massif de connexions courtes, des bancs d’essai ont été conduits au moyen d’un générateur HTTP/1.1 sur une infrastructure 100 Gbps :

Profil de configuration de la passerelle Taux de connexions maintenu Sockets TIME_WAIT Taux d’erreur EADDRNOTAVAIL Latence moyenne d’établissement Charge CPU SoftIRQ
Linux par défaut (Non ajusté) 480 conn/s 28 190 14,8 % 48,2 ms 12,4 %
Extension simple de la plage de ports 980 conn/s 61 400 8,2 % 39,6 ms 18,7 %
Réutilisation RFC 6191 (tcp_tw_reuse = 2) 8 400 conn/s 4 200 0,0 % 7,1 ms 8,9 %
Cœur de tunnel couche 3 RiceGuard 45 000 conn/s 0 (Au niveau passerelle) 0,0 % 1,8 ms 4,2 %

Ces métriques démontrent que l’activation de tcp_tw_reuse écarte totalement les erreurs d’allocation en mode mandataire, tandis que l’encapsulation de couche 3 de RiceGuard supprime complètement la contrainte de dimensionnement des tables de sockets locales.

Durcissement des passerelles de production Linux : Sysctl, drapeaux et Nftables

Prévenir les attaques par assassinat RFC 1337 et conjurer la pénurie de ports requièrent une harmonisation des réglages du système d’exploitation hôte.

Ajustement des paramètres du noyau : tcp_rfc1337, plages de ports et drapeaux de réutilisation

Il est recommandé d’intégrer les paramètres suivants dans le fichier /etc/sysctl.d/99-network-performance.conf :

# Activer la protection contre l’assassinat TIME-WAIT selon la RFC 1337
net.ipv4.tcp_rfc1337 = 1

# Réutiliser en toute sécurité les sockets TIME_WAIT selon la RFC 6191
net.ipv4.tcp_tw_reuse = 2

# Étendre la plage des ports éphémères sur l’ensemble de l’espace 16 bits
net.ipv4.ip_local_port_range = 1024 65535

# Limiter le nombre total de sockets TIME_WAIT pour protéger la mémoire RAM
net.ipv4.tcp_max_tw_buckets = 262144

# Aligner les temporisations Netfilter sur les cycles réels des sockets
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 30
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 30

Le réglage net.ipv4.tcp_rfc1337 = 1 ordonne au noyau d’ignorer silencieusement les paquets RST visant des sockets en attente, neutralisant ainsi les tentatives de réinitialisation frauduleuse. En synergie avec les mécanismes de cookies SYN détaillés dans notre dossier sur /news/tcp-syn-flood-cookies-wireguard-cookie-dos-defense, l’infrastructure bénéficie d’un blindage robuste de sa couche transport.

Inspection des sockets TIME_WAIT avec les outils de diagnostic Ss et Conntrack

La supervision en temps réel des sockets et des tables de suivi est cruciale lors de pics de trafic. Les administrateurs doivent privilégier les utilitaires netlink modernes :

# Résumé global de l’état des sockets sur l’ensemble des interfaces
ss -s

# Afficher les sockets en TIME_WAIT avec le décompte exact du temporisateur
ss -tan state time-wait

# Filtrer les entrées de suivi conntrack en phase TIME_WAIT
conntrack -L -p tcp --state TIME_WAIT

# Évaluer la distribution des états de connexion sur la machine
ss -tan | awk "{print $1}" | sort | uniq -c

En conjuguant la conformité à la RFC 1337, la réutilisation éprouvée des sockets selon la RFC 6191 et le transport de couche 3, les architectes réseau équipent leurs passerelles d’une résilience sans faille face aux charges les plus intenses.