Les MTU et vous

Les MTU et vous

Voici un problème que j’ai négligé durant assez longtemps, mais qui m’aura posé problème lors de mon déplacement en Irlande: les MTU. On entend peu ou pas parler des MTU, on sait vite fait que c’est une valeur plus ou moins fixe autour des 1500, que les utilisateurs de PPPoE doivent le régler à 1492, etc… Peu de monde en savent plus, et cette problématique est souvent négligée. Cependant, personne ne réalise l’importance que joue le rôle des MTU dans l’efficacité des transmissions. D’autant plus qu’un mauvais MTU peut être la cause de bien de mystérieux problèmes.

Avez-vous déjà eu des problèmes assez étranges où des pages internet mettent trop de temps à charger (ou ne chargent pas du tout), et ce malgré une réponse expéditive du serveur web d’après Wireshark ? J’ai déjà rencontré ce problème avec un site web en particulier qui ne voulait pas charger en IPv6. Et pour cause, mon opérateur droppait les fragments IP contenant autre chose que de l’UDP/TCP/ICMP (pourquoi cette politique d’ailleurs?). En l’occurrence je fais tourner un tunnel protocole 41 entre chez moi et un de mes serveurs chez Online afin d’avoir une connectivité IPv6 vu que mon opérateur n’en déploie toujours pas. Vu que j’ai mal configuré mon MTU, les paquets fragmentaient et partaient au /dev/null. Conséquence: mon PC attend indéfiniment des paquets qui n’arriveront jamais, donc le navigateur bloque au chargement de la page.

Je vais donc aujourd’hui vous parler de MTU, expliquer son fonctionnement, la fragmentation IP, overhead d’un VPN et la configuration d’un tunnel OpenVPN et tinc prenant en compte le MTU du transport. Encore une fois, si quelque chose ne vous parait pas très clair, libre à vous de me laisser un commentaire pour me poser des questions. J’explique les choses comme je les ai moi-même compris.

Introduction: Qu’est-ce qu’un MTU exactement ?

Il s’agit de la première question à aborder. Qu’est-ce que le MTU et qu’est-ce que cela implique concrètement ?

Paquet, trame, datagramme et segment

Avant de commencer il faut déjà faire la différence entre ces 4 termes pourtant très proches. Wikipédia et le tableau du modèle OSI explicite très bien les différences:

Le modèle OSI
Le modèle OSI
Source: https://en.wikipedia.org/wiki/OSI_model#Description_of_OSI_layers

Qu’est-ce que cela veut dire ? C’est au final assez simple: si vous recevez des données comprenant une en-tête L3 à L7, vous avez un paquet. Si vous avez en plus une en-tête L2 (Ethernet le plus souvent), vous avez une trame. Si vous avez juste une en-tête TCP, vous avez seulement un segment, etc. On dit par exemple que Wireshark est un analyseur de trames, car il sort effectivement les en-têtes Ethernet ainsi que tout ce qui se trouve au-dessus.

A noter que les tags VLAN (802.1q) sont toujours considérés comme faisant partie du L2, et ne sont donc pas compris dans le MTU L3. Les tags MPLS sont cependant compris dans le MTU L3. De ce fait le MTU des interfaces devra être soit réduit en fonction de l’overhead des tags, soit le réseau doit supporter des MTU > 1500 (jumboframes).

Une définition simple du MTU

Le MTU indique la taille maximale d’un paquet (L3 donc) en octets. Cela veut donc dire qu’un paquet de données qui comprend une en-tête IP, TCP/UDP, etc ne doit pas dépasser les 1500 octets au total afin de transiter sur le réseau.

Un MTU inégal

Dans un monde parfait tous les chemins d’internet auraient le même MTU et l’utilisation des mécanismes de fragmentation serait marginale, voire inexistante. Ce n’est malheureusement jamais le cas. Les différents chemins et réseaux que les paquets empruntent afin d’arriver à leur destination peuvent avoir différents MTU.

Un exemple concret serait entre le LAN d’un particulier où le MTU du réseau serait de 1500 (ou bien 9000 avec les jumboframes) et internet. Supposons que ce monsieur dispose d’un accès internet Orange. Jusqu’à présent Orange était très fan de la PPPoE (c’est cependant en train de changer), protocole qui rajoute une en-tête de 8 octets, réduisant le MTU de la ligne à 1492. Un paquet gonflé à bloc (1500 octets) pourra donc circuler tranquillement si sa destination se trouve sur le réseau local, mais ce dernier ne pourra être routé par le réseau d’Orange car il sera trop gros pour passe dans la ligne PPPoE. Il faudra donc le fragmenter, ou bien utiliser des mécanismes alternatifs.

Le problème des MTU différents

Palier aux problèmes des paquets trop gros: fragmentation IP, MSS, PMTUD et IPv4/v6

Qu’est-ce que la fragmentation IP ?

Lorsqu’un paquet est trop gros, il doit être découpé en plusieurs morceaux afin de pouvoir passer sur un lien réduit. En IPv4 la fragmentation est prévue de base dans son en-tête. Des données de fragmentation se trouvent donc dans toutes les en-têtes IPv4, même s’il n’en est pas fait usage. En IPv6 la fragmentation se fait par le biais d’une extension d’en-tête IP. Les données de fragmentation ne se retrouveront dans l’en-tête que si nécessaire.

Fragmentation IPv4 sur Wireshark
Exemple d’une fragmentation IPv4 sur Wireshark

A noter que sur la capture ci-dessus, un paquet rempli fera 1514 octets. Ceci est dû au fait de l’inclusion des en-têtes Ethernet qui font 14 octets.

Il faut également savoir que la fragmentation en IPv4 ne fonctionne pas de la même manière qu’en IPv6. Voici les principales différences:

  • En IPv4 la fragmentation des paquets peut se faire par n’importe quel équipement L3 se situant sur le chemin. Un paquet peut être donc découpé par l’expéditeur ou n’importe quel routeur en chemin. La reconstitution des fragments se fait par l’hôte de destination. Il n’y a donc à priori pas besoin de connaître le MTU du chemin (PMTU) puisque chaque routeur adapte les paquets en fonction du lien. Ce comportement peut être désactivé par le bit « Don’t Fragment » (DF). Ceci a pour effet d’interdire la fragment par le réseau intermédiaire. Si le bit DF est utilisé et qu’un paquet ne peut passer car il est trop gros, un message ICMP « Fragmentation Needed » est généré par le routeur ayant posé problème et est renvoyé à l’expéditeur du paquet.
  • En IPv6 la fragmentation et la reconstitution des paquets ne se font que par l’hôte expéditrice et destinataire. Si un lien avec MTU réduit devait se trouver au milieu du réseau, le paquet ne sera pas fragmenté et un message ICMP « Packet Too Big » sera généré par le dernier routeur et sera renvoyé à l’expéditeur du paquet.

Les différentes erreurs pouvant provoquer des problèmes de MTU

Une mauvaise configuration des routeurs peut engendrer divers problèmes. Ces problèmes sont en général perte d’efficacité dans la transmission, ou bien perte pure et simple de trames. On trouve différents problèmes, tels que:

  • Un opérateur qui a pour politique de dropper les fragments IP: il y a donc perte de données (très dur à débugger),
  • Un firewall configuré pour dropper l’ICMP: un problème de fragmentation rencontré sur le réseau ne sera pas remonté à l’expéditeur car l’ICMP est bloqué. Ce dernier ne pourra donc pas se rendre compte du problème et continuera alors à envoyer des paquets qui iront à la poubelle, ou bien attendra un acquittement TCP qui n’arrivera jamais. De plus bloquer l’ICMP empêche le PMTUD de fonctionner correctement,
  • Le MTU d’un tunnel (GRE, OpenVPN, L2TP, etc) mal configuré.

Le cas de TCP

Petit point concernant la fragmentation IP et TCP. Tout d’abord, le protocole TCP a été conçu afin de remplir différentes tâches, dont:

  • Assurer la fiabilité des transmissions: pallier aux pertes de trames, gérer l’ordre d’arrivée des trames,
  • Gérer le débit de la transmission: fenêtre TCP,
  • Ajuster la taille des trames en fonction du MTU du chemin.

TCP doit acquitter chaque paquet échangé et doit gérer les éventuelles retransmissions. Fragmenter des paquets contenant du TCP est donc contre-productif car les différents fragments d’un segment ne seront pas acquittés (seul le segment entier l’est). Si par exemple l’un des 2 fragments est perdu, TCP fera renvoyer le paquet entier, donc les 2 fragments initiaux. Mis à l’échelle de l’internet et débits actuels, ce n’est pas forcément la meilleure chose à faire. C’est pour cela que TCP a toujours le bit DF en IPv4 actif afin d’éviter la fragmentation des paquets sur le réseau. A noter qu’un hôte ne fragmentera jamais du TCP, que ce soit en IPv4 ou IPv6.

Le protocole TCP a été conçu de manière à pouvoir gérer la fragmentation des données à envoyer de lui-même. La fragmentation ne se fait donc plus au niveau IP, mais exclusivement au niveau TCP. Les données sont fragmentés en segments TCP et la taille maximum d’un segment est appelé le MSS (Maximum Segment Size). De ce fait chaque segment est individuellement acquitté et il n’y a pas lieu à renvoyer ce qui a déjà été acquitté.

Le protocole TCP n’est donc pas concerné par la fragmentation IP, mais peut toujours poser problème lors de l’utilisation d’un tunnel. Voir plus loin.

Découverte du MTU d’un chemin avec PMTUD et l’importance d’ICMP

Le PMTUD, ou Path Maximum Transmission Unit Discovery est une technique permettant de connaître le MTU d’un chemin entre 2 hôtes. La découverte fonctionne en envoyant successivement des paquets de taille de plus en petite (partant du MTU de l’interface) jusqu’à qu’il n’y ait plus de messages ICMP « Fragmentation Needed » ou « Packet Too Big ». Le PMTUD est défini par les RFC 1191 et 1981.

Si un réseau bloque le passages des paquets ICMP, il n’est donc plus directement possible de connaître le MTU du chemin. Du moins il n’est plus possible de le connaître en utilisant les méthodes standardisées. Le passage des données se fera donc à l’aveugle. Si le MTU reste le même sur tout le chemin, tout va bien. Cependant si le chemin comprend effectivement un lien avec un MTU réduit, cela aura pour conséquence de bloquer la transmission des gros paquets. Cela se caractérise généralement en TCP par une connexion établie normalement, mais qui bloque dès qu’il s’agit de transmettre beaucoup de données. TCP ne pourra alors pas s’adapter car il n’aura pas connaissance du problème et pensera juste qu’un bout du chemin a été down.

ICMP est le protocole de signalisation d’IP. Ce dernier s’occupe de la remontée des problèmes du réseau et permet même la résolution de certaines de celles-ci de manière autonome par les hôtes. Filtrer ICMP de manière globale empêche les hôtes de connaître la situation du réseau. Cela pose aux administrateurs des problèmes auxquels ils ne sont généralement pas confrontés, car les hôtes sont censés pouvoir gérer ces problèmes d’eux-mêmes. Filtrer ICMP peut cependant avoir l’avantage de bloquer certaines attaques ou tentatives de découverte. Cela peut toutefois engendrer plus de problèmes qu’il ne résout. Chaque administrateur devrait peser le pour et le contre avant de bloquer un tel protocole sur leur réseau.

Prendre en compte les overhead dans le calcul des MTU

Différentes en-têtes que celles habituellement présentes peuvent être rajoutées avant le champ IP. Ce qui a pour conséquence de réduire le MTU du lien. On rencontre ce problème essentiellement lors de l’utilisation de:

  • Labels MPLS: +4 octets,
  • PPPoE: +8 octets,
  • Tunnel GRE: +24 octets au minimum,
  • Tunnels VPN/IPSec: +28 octets au minimum,
  • etc.

La solution facile pour contourner ces problèmes serait d’utiliser un MTU > 1500 au niveau transport. Cependant cette option n’est pas implémentée de manière globale sur internet. Voici donc une analyse pratique du problème, ainsi que des solutions pour certains logiciels VPN.

Découvrir les liens à MTU réduit

Le plus simple reste d’envoyer un ping le proche à l’autre bout du lien en y interdisant la fragmentation via le bit DF. Voici un exemple:

$ sudo ping -c 1 -M do -s 1472 alpha.host.pingex.net
PING alpha.host.pingex.net (163.172.62.9) 1472(1500) bytes of data.
1480 bytes from alpha.host.pingex.net (163.172.62.9): icmp_seq=1 ttl=60 time=0.487 ms

--- alpha.host.pingex.net ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.487/0.487/0.487/0.000 ms

Ce qui donne sur Wireshark (cliquez pour agrandir):

Ping rempli

Nous avons ici tenté un ping en remplissant le paquet au maximum (1500 octets) et en désactivant la fragmentation IP. Voici une rapide explication des options:

  • -c: Nombre de ping à envoyer,
  • -M: gère la fragmentation IPv4, 3 valeurs sont possibles:
    • do: absolument aucune fragmentation,
    • want: fragmentation locale uniquement, fait un PMTUD,
    • dont: fragmentation possible partout,
  • -s: quantité de données à envoyer.

La 2ème ligne indique la quantité de données envoyée 1472(1500). Le premier nombre reprend l’option -s tandis que le 2ème nombre indique la taille du paquet sur le réseau. La différence entre les deux est de 28 octets, soit 20 pour IPv4 et 8 pour ICMP.

Il existe 3 cas de figure pour le retour:

  • Le ping passe,
  • Un retour ICMP ping: local error: Message too long, mtu=XXXX,
  • Aucun retour.

Dans le second cas le véritable MTU du chemin est tout donné, il suffit ensuite d’ajuster en conséquence. Dans le troisième cas, la perte signifie que soit l’ICMP est bloqué (pas de retour de « Frag Needed »), soit qu’une erreur au niveau d’un tunnel (par exemple) et liée à la taille du paquet se soit produit.

Voici maintenant quelques cas de figure (essentiellement des tunnels) où il faut correctement ajuster le MTU.

Les tunnels simples (GRE, etc.)

Les tunnels tels que GRE ont en général un MTU d’interface très prévisible. Il suffit simplement de faire le MTU du chemin niveau transport entre les 2 points, et d’y soustraire 24 (IPv4) ou 44 (IPv6) octets. Il faut cependant garder à l’esprit que tous les paquets ne passeront pas forcément par les mêmes chemins sur internet. Les PMTU peuvent donc différer dans le temps. Cependant s’il s’agit d’un tunnel entre 2 machines du même AS (par exemple) ou bien un réseau privé, il est en général assez safe de considérer que le PMTU reste constant dans le temps.

tinc

Tinc, en tant que VPN mesh, gère le problème des MTU à sa façon. Par conséquent, le PMTU entre les différents hôtes sera automatiquement adapté afin d’éviter une fragmentation au niveau du transport. Le MTU de l’interface tun/tap reste fixe à 1500, mais une erreur ICMP pourra toujours être renvoyée par tinc si le paquet dépasse la taille limite d’un des liens du mesh. De plus, tinc fait du MSS clamping par défaut.

Le cas d’OpenVPN

OpenVPN est un peu spécial lorsqu’il s’agit de gérer les MTU. A noter que ce problème ne se pose pas pour les tunnels TCP.

Par défaut le MTU de l’interface tun/tap est fixé à 1500. Tout paquet transitant par ce lien sera fragmenté au niveau transport (si besoin est), y compris si du TCP ou paquets avec le bit DF se trouve en-dessous. Cela peut provoquer des pertes de paquet (sans génération d’erreur ICMP à l’intérieur du tunnel) dans le cas où la fragmentation au niveau transport passe mal. Mais surtout, cela engendre une importante perte d’efficacité à cause des en-têtes supplémentaires des fragments IP. Cela se ressentira surtout lors de l’utilisation de protocoles type NFS.

$ sudo ping -c 1 -M do -s 1472 10.192.30.1
PING 10.192.30.1 (10.192.30.1) 1472(1500) bytes of data.
1480 bytes from 10.192.30.1: icmp_seq=1 ttl=63 time=26.5 ms

--- 10.192.30.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 26.587/26.587/26.587/0.000 ms
Fragmentation au niveau transport
Fragmentation au niveau transport, malgré le bit DF du paquet ICMP contenu à l’intérieur

Wireshark montre ici un paquet de 2 fragments réassemblés. On retrouve au sein des data UDP les 1500 octets du paquet ICMP, plus un overhead propre à OpenVPN. Ici, OpenVPN a laissé passer un paquet de 1500 octets dans le tunnel, la fragmentation était alors inévitable. Malgré le bit DF du paquet ICMP à l’intérieur, le paquet encapsulé s’est fait fragmenter, ce qui peut potentiellement poser problème par la suite si le transport n’est pas fiable.

La documentation d’OpenVPN n’est pas suffisamment explicite sur les problème de MTU. Elle ne donne malheureusement pas de vraie solution hormis des options « magiques » pour résoudre ce problème. Je vais donc tenter de documenter un peu mieux la résolution de ce problème.

Les options disponibles

Petit récapitulatif des options disponibles:

  • –link-mtu: Donne le « MSS UDP » pour le lien VPN. Il s’agit entres autres de lui dire combien de data utile (paquets encapsulés + overhead OpenVPN) il peut placer au maximum par paquet. En pratique pour un MTU transport de 1500, le link-mtu serait fixée à 1472 (20 IPv4 + 8 UDP) ou 1452 (40 IPv6 + 8 UDP). OpenVPN dérive ensuite le MTU de l’interface tun/tap par rapport à cette valeur.

    L'effet de link-mtu
    Un link-mtu à 1460 donnera 1460 octets de liberté par paquet pour OpenVPN (quitte à devoir fragmenter, comme ici).
  • –tun-mtu: L’inverse de link-mtu. Il s’agit de donner le MTU du tunnel, et OpenVPN s’occupe ensuite de déterminer le MTU du lien.
  • –mtu-disc: Effectue un PMTUD sur le lien. En pratique, cette option ne semble pas avoir de grands effets, hormis transformer le tunnel en trou noir à gros paquets.
  • –mtu-test: Méthode bourrin pour calculer le MTU du lien.
  • –fragment: Permet de remplacer la fragmentation au niveau IP par une fragmentation interne à OpenVPN. La valeur donnée correspond à la valeur max du paquet et est interprétée de la même manière que link-mtu. Utile si le réseau du lien ne supporte pas les fragments correctement. Cette option est encore moins efficace que la fragmentation IP normale.
  • –mssfix: L’option magique du « MSS clamping » pour éviter que les paquets TCP ne soient fragmentés. N’agit que pour TCP cependant, comme son nom l’indique.

Que faire donc face à tout ça ? Voici 2 cas de figures qui se sont posés à moi.

Le cas d’un tunnel fixe

Lors de l’utilisation d’un tunnel fixe, le PMTU reste en général assez stable. Il est alors facile d’ajuster manuellement le MTU du lien avec simplement l’option link-mtu. Le calcul de cette valeur est expliquée ci-dessus. Pour un lien à 1500, il faut alors un link-mtu à 1472/1452.

$ sudo ping -c 1 -M do -s 1472 10.192.30.1
PING 10.192.30.1 (10.192.30.1) 1472(1500) bytes of data.
ping: local error: Message too long, mtu=1391

--- 10.192.30.1 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms

$ sudo ping -c 1 -M do -s 1363 10.192.30.1
PING 10.192.30.1 (10.192.30.1) 1363(1391) bytes of data.
1371 bytes from 10.192.30.1: icmp_seq=1 ttl=63 time=20.6 ms

--- 10.192.30.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 20.667/20.667/20.667/0.000 ms

$ ip link show cor01
66: cor01: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1391 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 100
    link/none
Paquet avec link-mtu
Paquet OpenVPN à 1492 octets et MTU de tun/tap à 1391. Plus de fragmentation par OpenVPN !

Cet article de blog m’a permis de mieux comprendre link-mtu (par soucis de donner mes sources).

Le cas d’un tunnel « road-warriors » (clients mobiles)

Dans ce dernier cas, les choses sont un peu plus compliquées. Plusieurs clients se connectent à un même serveur. Ces clients, selon leur connexion, peuvent avoir des PMTU sur le lien très différents. Le problème de link-mtu, c’est que la même option avec la même valeur doit être appliquée à tous les nœuds du tunnels. Si un client avec un PMTU réduit doit se connecter au VPN, il faut que tous les autres clients ajustent également leur link-mtu. Ce n’est pas en pratique quelque chose d’envisageable, sauf si on fixe ce dernier à 900, ou n’importe quelle valeur excessivement basse.

A partir de ce moment-là, la fragmentation est inévitable. On a alors 2 choix:

  • Soit on laisse la fragmentation se faire au niveau IP,
  • Soit on demande à OpenVPN de s’en occuper par l’option fragment.

Afin de minimiser l’impact, il convient de laisser la fragmentation se faire au niveau IP. Cependant, certains réseaux n’aiment pas les fragments pour quelque raison que ce soit, il faut alors faire appel à la fragmentation interne à OpenVPN.

Mais dans tous les cas il faut faire du MSS clamping pour éviter que les paquets TCP se fassent fragmenter. OpenVPN fixe une valeur par défaut à 1450… qui ne fonctionne que pour les réseaux avec liens à 1500. Il faut alors donc jouer sur les valeurs jusqu’à trouver un point avec le meilleur compromis efficacité/occupation du paquet/PMTU. Ce point se trouve en général aux alentours de 1400 ou 1300.

Ce problème est également expliqué dans la documentation d’OpenVPN, au niveau de l’option mssfix.

En bref

J’espère avoir été suffisamment clair et précis dans mes propos. J’ai expliqué les choses comme je les ai comprises. Si quelque chose ne va pas, ne pas hésiter à me le dire que je puisse corriger. Cette histoire de MTU m’a pourrie les quelques premiers jours en Irlande, ainsi que plusieurs heures à comprendre et débugger le soucis. Mais au moins j’aurai appris quelque chose qui semble pourtant très important et apparemment assez négligé des administrateurs par soucis d’intuitivité.

TL;DR

  • Trame = L2, Paquet = L3, Segment = L4,
  • Les MTU sont inégaux sur les liens composant internet,
  • En IPv4, la fragmentation peut se faire partout sur le réseau, sauf si le bit DF est set,
  • En IPv6, la fragmentation ne se fait que par les hôtes,
  • Une mauvaise configuration des liens peut engendrer des problèmes assez obscurs,
  • TCP ne devrait jamais se faire fragmenter,
  • PMTUD permet de découvrir le MTU d’un chemin (PMTU),
  • Bloquer ICMP peut conduire à des catastrophes,
  • Le MTU d’un tunnel ne sera jamais effectivement de 1500 (sauf si le réseau supporte les jumboframes de bout-en-bout),
  • ping -M do -s X IP pour tester si un paquet de taille X+28 (IPv4) ou X+48 (IPv6) arrive à IP sans fragmentation,
  • link-mtu sur OpenVPN pour gérer manuellement le MTU du lien (pour un tunnel fixe),
  • La gestion des MTU sur OpenVPN pour des liens non fixes est fucked up.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *