L'optimisation des retransmissions sur les réseaux étendus (WAN) a longtemps constitué un défi majeur pour les concepteurs d'infrastructures. Pendant des décennies, le protocole standard TCP s'est appuyé exclusivement sur des acquittements cumulatifs (RFC 793). Dès qu'un unique paquet d'une fenêtre de transmission était perdu, l'émetteur devait réexpédier ce segment ainsi que tous les suivants, gaspillant inutilement de la bande passante.
Pour surmonter cette inefficacité, l'IETF a standardisé l'option TCP Selective Acknowledgment (SACK) dans la RFC 2018. SACK permet au récepteur de signaler précisément les plages disjointes de données reçues avec succès, autorisant une retransmission ciblée des seuls octets manquants.
Cependant, la transcription de spécifications théoriques en code de bas niveau dans le noyau d'un système d'exploitation peut introduire des vulnérabilités critiques. En 2019, des experts en sécurité ont découvert une série de failles dans la pile réseau du noyau Linux, menée par CVE-2019-11477 (désignée sous le nom de SACK Panic). En manipulant la taille maximale de segment (MSS) et en envoyant des blocs SACK forgés, un attaquant distant pouvait déclencher un dépassement d'entier 16 bits dans la file des tampons de socket (sk_buff), provoquant un arrêt immédiat du système.
L'analyse de SACK Panic met en lumière les interactions subtiles entre les optimisations de transport et les mécanismes d'allocation mémoire, tout en démontrant l'efficacité de l'encapsulation de niveau 3 avec RiceGuard pour neutraliser les attaques par fragmentation réseau.
+-------------------------------------------------------------------------+
| ACK CUMULATIF CONTRE SELECTIVE ACK (RFC 2018) |
+-------------------------------------------------------------------------+
| SCÉNARIO : Paquets 1, 2, 4, 5 reçus ; Paquet 3 perdu en transit. |
| 1. RFC 793 ACK CUMULATIF : Le récepteur envoie ACK=3. L'émetteur doit |
| réexpédier les paquets 3, 4 et 5. Débit gaspillé. |
| 2. RFC 2018 OPTION SACK : Le récepteur envoie ACK=3 + SACK [4-6]. |
| L'émetteur réexpédie UNIQUEMENT le paquet 3. Zéro surcoût réseau. |
+-------------------------------------------------------------------------+
L'évolution des acquittements TCP : ACK cumulatif contre SACK (RFC 2018)
Dans le modèle cumulatif traditionnel, le champ de numéro d'acquittement de l'en-tête TCP de 20 octets indique simplement le prochain octet attendu par le récepteur, certifiant la réception de tous les octets antérieurs. En cas de pertes multiples non contiguës, l'émetteur se trouve contraint de réexpédier un seul paquet et d'attendre un RTT complet, ou de renvoyer l'intégralité de la fenêtre de transmission.
La RFC 2018 a levé ce goulet d'étranglement en intégrant l'option SACK dans l'en-tête TCP (Option Kind 5). Le récepteur peut transmettre jusqu'à quatre blocs décrivant des segments non ordonnés reçus. Chaque bloc est défini par deux entiers non signés de 32 bits :
- Bordure gauche (Left Edge) : Premier numéro de séquence du bloc isolé reçu.
- Bordure droite (Right Edge) : Numéro de séquence suivant immédiatement le dernier octet valide du bloc.
L'émetteur identifie ainsi précisément les segments manquants, transformant la récupération de perte en une procédure chirurgicale adaptée aux réseaux à haut débit.
Anatomie de l'option SACK : Formats de blocs et descripteurs de bordures
L'en-tête TCP réserve au maximum 40 octets pour les options. L'option SACK de base consomme 2 octets pour son identification : Type (Kind 5) et Longueur (Length). Chaque bloc SACK additionnel requiert ensuite 8 octets (deux repères de 32 bits).
Comme la plupart des liaisons modernes activent les horodatages TCP Timestamps (RFC 7323, 10 octets), l'espace utile pour les blocs SACK se trouve restreint :
- Sans horodatage : 40 octets - 2 octets = 38 octets disponibles, autorisant au maximum 4 blocs SACK (32 octets).
- Avec horodatage activé : 40 octets - 10 octets - 2 octets = 28 octets disponibles, autorisant au maximum 3 blocs SACK (24 octets).
Associé au Duplicate SACK (D-SACK, RFC 2883), le premier bloc signale la réception de segments en double, offrant une visibilité cruciale sur le réordonnancement des paquets par le réseau.
Gestion des segments désordonnés par le noyau Linux : La file sk_buff
Dans l'architecture du noyau Linux, chaque paquet réseau est encapsulé au sein d'une structure nommée tampon de socket, ou struct sk_buff (skb). Pour chaque socket TCP active, le noyau maintient une file d'émission et de retransmission organisant ces instances sk_buff sous forme de liste doublement chaînée.
Sur les interfaces modernes exploitant le Generic Segmentation Offload (GSO), le noyau ne fabrique pas de paquets unitaires de 1 500 octets. Il assemble des super-paquets pouvant atteindre 64 Ko, dont le découpage en trames MTU est confié au contrôleur matériel avant l'injection sur la liaison physique.
Mécanismes de fragmentation du tampon socket (skb) et empreinte mémoire
Pour suivre le nombre de segments filaires représentés par un tampon GSO, le noyau utilise un compteur interne stocké dans sa structure partagée (skb_shared_info), désigné par gso_segs :
struct skb_shared_info {
__u16 gso_size;
__u16 gso_segs;
struct sk_buff *frag_list;
};
Cette structure comporte une contrainte décisive : gso_segs est un entier non signé de 16 bits (__u16), avec un plafond fixé à 65 535.
Quand un paquet SACK acquitte uniquement une portion centrale d'un super-tampon GSO en transit, le noyau ne peut libérer l'ensemble de la mémoire. Il fait appel à tcp_fragment() pour scinder le sk_buff existant en deux entités : le segment acquitté et le reliquat restant en attente.
+-------------------------------------------------------------------------+
| FRAGMENTATION DU SK_BUFF DANS LE NOYAU LINUX |
+-------------------------------------------------------------------------+
| 1. Tampon GSO (32 Ko) : Données : 32 Ko | gso_segs : 23 segments |
| 2. Arrivée d'un SACK : Appel de tcp_fragment() pour diviser le tampon |
| 3. Schéma d'attaque : Micro-MSS (48 octets) fragmente la file en |
| milliers de skb -> DÉPASSEMENT de gso_segs |
+-------------------------------------------------------------------------+
Le mécanisme de SACK Panic (CVE-2019-11477) : Dépassement d'entier 16 bits
L'origine de la vulnérabilité CVE-2019-11477 réside dans le regroupement des tampons morcelés lors de la préparation des retransmissions.
Afin d'optimiser le traitement de la file d'attente, le noyau tente de fusionner les petits segments consécutifs via tcp_shift_skb_data(). Cette fonction regroupe les octets dans un tampon de tête et incrémente son compteur gso_segs du nombre de segments absorbés :
/* net/ipv4/tcp_input.c */
tcp_skb_pcount_add(prev, pcount);
Comme le champ gso_segs ne dispose que de 16 bits, tout cumul excédant 65 535 provoque un dépassement arithmétique et réinitialise le compteur à 0.
Peu après, la pile réseau exécute un contrôle d'intégrité strict dans tcp_fragment() :
BUG_ON(tcp_skb_pcount(skb) == 0);
La macro BUG_ON() représente l'ultime garde-fou du noyau : dès que cette assertion se vérifie, le système s'arrête instantanément pour éviter toute altération mémoire, déclenchant un Kernel Panic. Un assaillant distant pouvait ainsi faire tomber un serveur en quelques secondes sans aucune authentification préalable.
Le rôle des micro-MSS dans l'amplification de l'épuisement des tampons
Comment un assaillant parvenait-il à saturer gso_segs au-delà de 65 535 sans saturer le réseau avec des gigaoctets de données ? L'élément déclencheur résidait dans l'altération de la valeur Maximum Segment Size (MSS) négociée lors de la poignée de main TCP.
Si une valeur nominale de MSS oscille entre 1 400 et 1 460 octets, la RFC 793 autorise des valeurs infimes. La limite minimale admise par le noyau Linux était alors de 48 octets (l'en-tête IPv4 combiné aux options TCP élémentaires).
Avec un MSS bridé à 48 octets, chaque segment ne véhicule que d'infimes fractions de données applicatives. Un unique tampon de 32 Ko engendre :
$$ ext{gso_segs} = rac{32,768 ext{ octets}}{48 ext{ octets/segment}} pprox 682 ext{ segments}$$
En expédiant une séquence orchestrée de blocs SACK acquittant des fragments alternés de 48 octets, l'assaillant forçait le noyau à morceler sa file d'attente en une myriade de micro-structures sk_buff. Lors de leur coalescence, le compteur franchissait immédiatement le seuil de 65 535.
Dégradation de performance SACK et épuisement de ressources (CVE-2019-11478 et 11479)
L'enquête sur SACK Panic a mis au jour deux failles connexes d'épuisement des ressources système :
- CVE-2019-11478 (SACK Sluggishness) : L'assaillant fragmente intentionnellement la file d'envoi. Lors des expirations de temporisateurs de retransmission, le parcours de listes contenant des dizaines de milliers d'éléments
sk_buffsaturait le processeur à 100 %. - CVE-2019-11479 (Épuisement par micro-MSS) : En imposant un MSS de 48 octets, le volume de paquets émis était multiplié par plus de 30, asséchant la mémoire allouée aux sockets.
Pénalités asymétriques de processeur lors du parcours de listes non corrigées
L'asymétrie de coût était saisissante. L'assaillant n'émettait que quelques kilooctets de trames SYN et ACK. À l'inverse, le serveur cible devait allouer des mégaoctets de mémoire noyau (kmalloc), exécuter des boucles de parcours de listes sans fin et saturer ses cœurs sous l'afflux d'interruptions logicielles (ksoftirqd).
Pour les passerelles de cloud et les serveurs VPN traitant des milliers de flux simultanés, ce trafic provoquait un déni de service complet sans franchir les seuils d'alerte volumétrique.
Comment le tunnel de niveau 3 et RiceGuard neutralisent les failles SACK
L'exposition directe d'équipements sur des réseaux publics rend leur pile de transport vulnérable aux sondages et aux paquets malveillants.
L'architecture de tunnel de niveau 3 déployée par VPNRice et son protocole RiceGuard élimine cette exposition grâce à un cloisonnement rigoureux :
- Découplage par encapsulation UDP : RiceGuard encapsule l'intégralité du trafic client au sein de datagrammes UDP réguliers sécurisés par ChaCha20-Poly1305. Les tiers extérieurs ne perçoivent qu'un flux chiffré uniforme, dans lequel il est impossible d'injecter des options TCP forgées.
- Filtrage MSS sur les passerelles : Les passerelles RiceGuard appliquent un contrôle strict sur le MSS. Tout échange interne tentant d'établir un MSS anormalement bas (< 536 octets) est corrigé ou rejeté, stoppant net les attaques de fragmentation.
- Isolement d'état : Le tunnel s'interrompt sur des passerelles renforcées, préservant les appareils mobiles et les postes distants connectés à des réseaux Wi-Fi publics non fiables.
Les utilisateurs peuvent configurer leur environnement sécurisé depuis la page de téléchargement VPN, protégeant leur couche réseau contre les exploits de bas niveau.
+-------------------------------------------------------------------------+
| PROTECTION PAR TUNNEL L3 RICEGUARD |
+-------------------------------------------------------------------------+
| NON SÉCURISÉ : [Attaquant] ── MSS=48 + SACK malveillant ──> [Crash noyau]
| SÉCURISÉ : [Attaquant] ── Trames micro-MSS ──> [Passerelle RiceGuard]
| ├─ Normalisation MSS |
| ├─ Filtrage options |
| └─ Tunnel UDP Noise |
| └─> [Hôte protégé] |
+-------------------------------------------------------------------------+
Durcissement du noyau et inspection des paquets : Sysctl et filtrage MSS
Les administrateurs d'infrastructures Linux peuvent appliquer plusieurs mesures immédiates via sysctl et leur pare-feu.
1. Désactivation temporaire de SACK (Mesure d'urgence)
En attendant l'application des correctifs système, le traitement SACK peut être suspendu :
# Vérifier l'état actuel de SACK (1 = activé, 0 = désactivé)
sysctl net.ipv4.tcp_sack
# Désactiver globalement SACK
sudo sysctl -w net.ipv4.tcp_sack=0
Arbitrage : Cette action élimine le risque de panique du noyau mais dégrade le débit sur les liaisons sujettes à des pertes en raison du recours aux acquittements cumulatifs.
2. Imposition d'une limite minimale pour le MSS
Les versions récentes de Linux intègrent tcp_min_snd_mss pour refuser les valeurs anormales :
# Fixer le MSS minimal d'émission à 536 octets (norme RFC 879)
sudo sysctl -w net.ipv4.tcp_min_snd_mss=536
3. Filtrage du MSS sur pare-feu Iptables
Les passerelles doivent filtrer les requêtes de négociation abusives :
# Rejeter les paquets SYN imposant un MSS inférieur à 536 octets
sudo iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1:535 -j DROP
# Ajuster le MSS sortant en fonction du MTU de liaison
sudo iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
4. Surveillance du trafic avec Tcpdump
Les équipes de sécurité peuvent auditer les tentatives suspectes :
# Détecter les paquets SYN annonçant un MSS inférieur à 500 octets
sudo tcpdump -nn -i eth0 'tcp[tcpflags] & tcp-syn != 0 and tcp[20:2] < 500'
Comparaison architecturale : TCP standard, SACK vulnérable et tunnel sécurisé
Le tableau suivant met en regard les propriétés opérationnelles des approches :
| Indicateur clé | TCP standard RFC 793 | SACK non corrigé (RFC 2018) | Tunnel sécurisé RiceGuard |
|---|---|---|---|
| Vitesse de récupération | Lente (Renvoi complet) | Rapide (Renvoi sélectif) | Optimale (Reprise L3 UDP) |
| Risque d'arrêt noyau | Aucun (Pas de SACK) | Critique (Panique CVE-2019-11477) | Nul (Filtrage MSS et isolation) |
| Charge CPU sous attaque | Faible | Critique (Fragmentation de file) | Négligeable (Chiffrement matériel) |
| Consommation mémoire | Tampon fixe prévisible | Critique (Myriade de micro-skb) | Parfaitement bornée |
| Vulnérabilité aux tiers | Élevée (En-têtes en clair) | Moyenne (Options altérables) | Nulle (Chiffrement de bout en bout) |
| Débit sur liaison dégradée | Chute jusqu'à 60 % | Efficacité préservée | Débit maximal résilient |
Correction moderne du noyau Linux : Files en arbres rouge et noir (RB-Tree)
La crise de SACK Panic a conduit à une refonte de la pile réseau Linux menée par Eric Dumazet :
- Vérification stricte des compteurs : La fonction
tcp_shifted_skb()intègre désormais des garde-fous empêchant tout débordement degso_segsvers zéro. - Adoption des arbres rouge et noir : Les segments désordonnés sont désormais gérés par un arbre rouge et noir (
struct rb_node) au lieu d'une liste chaînée, réduisant la complexité de recherche de $O(N)$ à $O(\log N)$ et éradiquant l'épuisement de CVE-2019-11478. - Plancher MSS imposé dans le code : Le noyau interdit formellement à
tcp_fragment()de générer des micro-fragments sous la barre des 48 octets, quelles que soient les requêtes de l'émetteur.
Ces améliorations pérennisent les gains de performance de SACK tout en garantissant la stabilité de la mémoire noyau.
Liste de contrôle de l'ingénieur réseau pour sécuriser l'infrastructure SACK
Pour prémunir les parcs de serveurs contre les attaques de saturation mémoire, appliquez les recommandations suivantes :
- Vérifier les versions de noyau : Assurez-vous que l'ensemble des serveurs Linux de production exécutent des noyaux corrigés (Linux 4.19.52+, 5.1.11+ ou versions LTS avec correctifs rétroportés).
- Appliquer le filtrage MSS en périphérie : Configurez les répartiteurs de charge et pare-feu pour rejeter les connexions réclamant un MSS inférieur à 536 octets.
- Contrôler les variables Sysctl : Validez la présence de
net.ipv4.tcp_min_snd_mss = 536sur tous les serveurs exposés à Internet. - Isoler les accès distants : Imposez l'usage de tunnels de niveau 3 RiceGuard pour les collaborateurs en mobilité afin de clore les sessions TCP sur des passerelles éprouvées.
- Surveiller la charge SoftIRQ : Configurez une alerte sur l'activité processeur de
ksoftirqdpour détecter les tentatives de saturation algorithmique. - Activer la protection SYN-Cookies : Combinez le durcissement de SACK avec
net.ipv4.tcp_syncookies = 1pour éviter l'épuisement des tables de connexion.
L'articulation entre des réglages système rigoureux et une encapsulation de niveau 3 garantit des débits optimaux tout en immunisant les serveurs contre les vulnérabilités de la couche transport.



