Mot-clé - IPv4

Fil des billets

Linux et le cas étrange des trous noirs TCP/IP

Sur Internet, certains sites peuvent être mystérieusement inaccessibles. Quelle est la cause de ces étranges trous noirs ?

Lors de l'établissement d'une connexion TCP, les 2 machines négocient la taille maximale des paquets échangés (le MTU). Cependant, elles ignorent les contraintes propres au chemin suivi.

Normalement, si un paquet envoyé est trop gros, le routeur concerné le signale, via un message ICMP, et la machine émettrice ajuste sa taille de paquets[1]

Cependant, ces messages ICMP sont parfois bloqués par des pare-feu paranoïaques. C'est là que les ennuis commencent. Le routeur jette le paquet et signale le problème avec un message ICMP. Ce message est bloqué par le pare-feu. C'est le trou noir. Les paquets continuent à être envoyées, mais sont perdus à chaque fois.

Une solution à ce problème est décrite par le RFC 4821. Afin de déterminer la taille optimale des paquets, l'émetteur va envoyer des sondes, c'est-à-dire des paquets un peu plus gros que les autres, afin de rechercher la taille idéale. Si la sonde ne passe pas, mais que le reste du trafic passe, c'est que la taille de la sonde est trop grosse. Si elle passe, sa taille servira de base aux paquets suivants.

Linux dispose de ce mécanisme depuis le noyau 2.6.17, activable via l'option tcp_mtu_probing. Cependant, cette première mise en œuvre n'est pas très subtile.

Lorsque la détection démarre, on bascule à une taille de segment par défaut (tcp_base_mss[2]). Si rien ne passe, on divise cette taille par 2. Si les paquets passent, on essaie d'envoyer des sondes en doublant la taille des paquets.

Avec l'option tcp_mtu_probing=1, on ne déclenche ce mécanisme que si l'on détecte un trou noir (un blocage du trafic).

Par exemple, avec une taille de paquet négociée de 1460, on détecte un trou noir. La taille des paquets est passée à 512 (ça passe), puis on envoie une sonde à 1024 (ça passe encore), le trafic passe à 1024. On devrait envoyer une sonde à 2048, mais cela dépasse la capacité de la carte Ethernet (sans Jumbo Frames). Fin de la recherche. Le trafic reste sur une taille de paquet de 1024. Même si la taille optimale était de 1300.

Cela dit, c'est un compromis acceptable pour éviter les blocages. Mieux vaut un débit un peu dégradé qu'un blocage du trafic.

On peut paramétrer ce mécanisme pour être utilisé en permanence avec l'option tcp_mtu_probing=2.

Inutile de le dire, c'est une très très mauvaise idée.

Dans ce second cas, dès l'établissement du flux, on bascule en mode détection de MTU. Donc, on commence à la taille définie par tcp_base_mss, puis on envoie des sondes en doublant la taille à chaque fois. Pour un réseau Ethernet, on va se retrouver systématiquement avec des paquets dont la taille sera limitée à 1024 (au lieu de 1460).

Avec Linux 4.1, ce mécanisme a été sérieusement amélioré. La taille n'est plus doublée, mais les sondes sont utilisée pour réaliser une recherche par dichotomie de la taille idéale, ce qui permet de se rapprocher assez rapidement de la taille idéale. De plus, la taille de segment de base (tcp_base_mss) passe à 1024.

Quel paramétrage adopter ?

  • Activez systématiquement ce mécanisme en mode détection de trou noir (tcp_mtu_probing=1).
  • Pour les noyaux < 4.1, passez la taille de base (tcp_base_mss) à 1024.

Avec les noyau 4.1 et plus, le mode tcp_mtu_probing=2 est beaucoup moins pénalisant, mais pas forcément recommandé (il y a une dégradation de la taille des paquets en début de connexion). Dans tous les cas, le mode tcp_mtu_probing=1 permet de se protéger des trous noirs sans prendre de risque.

Notes

[1] C'est le mécanisme de découverte automatique du MTU du chemin (Path MTU discovery), décrit par le RFC 1191. Ce mécanisme est activé par défaut sous Linux.

[2] La taille de segment (MSS) est la taille des données du paquet TCP. La valeur de tcp_base_mss est 512 par défaut. Cette valeur passe à 1024 avec le noyau 4.1. Une valeur de tcp_base_mss de 1024 devrait vous donner un MTU initial de 1064 (1024 + 20 pour l'en-tête IP + 20 pour l'en-tête TCP). Comme rien n'est simple, sur les anciens noyaux Linux, il faut également y ajouter la taille des options TCP.