<?xml version="1.0" encoding="utf-8"?>
<entry title="Régler les problèmes de MTU et de MSS">
<date>2007-06-27</date>
<update_date>2007-09-03</update_date>
<summary><p>Les liens Internet dont la <wikipedia name="Maximum Transmission Unit">MTU</wikipedia> est plus
faible que les traditionnels 1500 <wikipedia name="Octet">octets</wikipedia>
rencontrent souvent de nombreux problèmes, qui frappent
particulièrement les transferts <wikipedia name="Transmission Control Protocol">TCP</wikipedia>. Quelles
sont les solutions pratiques ?</p></summary>
<content>
<p>Les liens Internet dont la <wikipedia name="Maximum Transmission Unit">MTU</wikipedia> est plus
faible que les traditionnels 1500 <wikipedia name="Octet">octets</wikipedia>
rencontrent souvent de nombreux problèmes, qui frappent
particulièrement les transferts <wikipedia name="Transmission Control Protocol">TCP</wikipedia>. Quelles
sont les solutions pratiques ?</p>
<p>Le lien « standard » sur <wikipedia>Internet</wikipedia> a une
<wikipedia name="Maximum Transmission Unit">MTU</wikipedia> de 1500 <wikipedia>octets</wikipedia>,
héritée d'<wikipedia>Ethernet</wikipedia>. Si, de bout en bout, tous
les liens ont cette MTU, la machine émettrice peut fabriquer des
paquets de 1500 octets et ils arriveront intacts. Mais il y a souvent
sur le trajet un lien dont la MTU est plus faible, par exemple un
<wikipedia name="Tunnel (réseau informatique)">tunnel</wikipedia> d'<wikipedia>IPv6</wikipedia> dans
<wikipedia>IPv4</wikipedia> ou bien un tunnel
<wikipedia name="Generic Routing Encapsulation">GRE</wikipedia> ou encore une connexion
<wikipedia>PPPoE</wikipedia> sur de l'<wikipedia name="Asymmetric Digital Subscriber Line">ADSL</wikipedia>. On
observe alors souvent un phénomène désagréable : les paquets de petite
taille, tels que ceux fabriqués par <wikipedia name="ping (logiciel)">ping</wikipedia> ou
<wikipedia>traceroute</wikipedia> passent mais les gros paquets, par
exemple les transferts de fichier avec <wikipedia name="Hypertext Transfer Protocol">HTTP</wikipedia>
bloquent mystérieusement. Le débogage est donc difficile. Une bonne
technique est de tester la connectivité, non pas avec <computer>ping AUTRE-MACHINE</computer>
tout court mais avec <computer>ping -s 1480 AUTRE-MACHINE</computer>
pour forcer l'usage de paquets de 1480 octets. Si le premier ping
fonctionne mais pas le second, vous avez un problème de MTU.</p>
<p>Si tous les ping fonctionnent, ainsi que des essais avec UDP, mais
que TCP n'y arrive pas, cela peut être parce que les implémentations
d'IP ne mettent souvent le bit DF (<foreign>Don't Fragment</foreign>,
un bit dans l'en-tête <wikipedia name="Internet
Protocol">IP</wikipedia> qui indique aux routeurs de ne pas fragmenter
le paquet) que pour TCP. Sur <wikipedia>Linux</wikipedia>, mettre
l'option <computer>net.ipv4.ip_no_pmtu_disc</computer> du noyau à 1
pourrait permettre de tester cette hypothèse (je n'ai pas essayé).</p>
<p>En théorie, ces problèmes de MTU ne devraient jamais arriver. En
<wikipedia>IPv4</wikipedia>, le routeur qui connecte au lien « trop étroit »
devrait <wikipedia name="Fragmentation">fragmenter</wikipedia> les paquets, en
<wikipedia>IPv6</wikipedia>, la <wikipedia name="Path MTU discovery">découverte de la MTU du
chemin</wikipedia>, décrite dans le <rfc num="1981" local="true"/>, devrait empêcher la machine émettrice d'envoyer des
paquets trop gros. (Cette découverte de la MTU du chemin peut aussi se
faire en IPv4, cf. le <rfc num="1191" local="true"/>, pour éviter la chute de performance provoquée par la
fragmentation.)</p>
<p>Mais, en pratique, cela marche mal (le <rfc num="2923"
local="true"/> a été le premier à expliquer ce problème, le <rfc
num="4459" local="true"/> le détaillant ensuite et le <rfc num="4821"
local="true"/> finissant par proposer d'abandonner l'ancien mécanisme
et d'en utiliser un tout nouvau). En effet, la découverte de la MTU du
chemin dépend de la bonne réception des messages
<wikipedia name="Internet Control Message Protocol">ICMP</wikipedia> <foreign>Packet too big</foreign>. Ceux-ci
sont malheureusement souvent filtrés par des <wikipedia
name="Pare-feu">coupe-feux</wikipedia> mal configurés par des
administrateurs ignorants qui croient améliorer la sécurité en
bloquant tout ICMP. On constate alors qu'on peut transférer des
fichiers avec certains sites et pas d'autres, selon la configuration
des coupe-feux situés sur le chemin. (Un excellent article très
complet sur la question est <foreign><link
url="http://www.potaroo.net/ispcol/2009-01/mtu6.html">A Tale of Two
Protocols: IPv4, IPv6, MTUs and Fragmentation</link></foreign>.)</p>
<p>Il existe alors plusieurs solutions. On peut se passer de tunnel,
on peut tricher un petit peu pour remonter la MTU de PPPoE (le <rfc
num="4638" local="true"/> explique comment), on peut attendre le
déploiement de la nouvelle technique de découverte de MTU du chemin,
normalisée dans le <rfc local="true" num="4821"/> mais il existe aussi des
solutions plus raisonnables.</p>
<p>Une classique est est d'abaisser manuellement la MTU sur toutes les
machines du réseau local (sur <wikipedia>Unix</wikipedia>, cela se
fait avec la commande <unix>ifconfig</unix>). C'est pénible car il ne
faut pas oublier de machine et cela abaisse la MTU (donc les
performances) même pour les transferts purement locaux.</p>
<p>Cela peut se faire spécifiquement pour IPv6 si on utilise le RA (<foreign>Router
Advertisement</foreign>, autoconfiguration IPv6 sans état). Une option permet en effet de fixer la MTU du
lien. Plus besoin alors de courir sur toutes les machines. Si on
utilise le démon <computer>radvd</computer>, c'est l'option
<computer>AdvLinkMTU</computer>. Par exemple :
<code>
interface eth0
{
...
   AdvLinkMTU 1460;
...
};  
</code></p>
<p>Une autre approche est de partir du fait que le problème touche
surtout <wikipedia name="Transmission Control Protocol">TCP</wikipedia>, plus souvent impliqué dans les
gros transferts et elle est donc spécifique à TCP (le test avec
<wikipedia name="ping (logiciel)">ping</wikipedia> échouera donc toujours puisque ping
utilise <wikipedia name="Internet Control Message Protocol">ICMP</wikipedia>). Elle consiste à
modifier la <wikipedia name="Maximum Segment Size">MSS</wikipedia>, la taille maximale des paquets
qu'annonce une machine qui fait du TCP à son partenaire. On voit cette
annonce avec <wikipedia>tcpdump</wikipedia> :
<code>
13:35:32.703105 2001:7a8:7509:0:216:3eff:fe78:b525.65513 &gt; 2001:660:3003:2::4:20.80: \
         S 4227314701:4227314701(0) win 32768 \
         &lt;mss 1400,nop,wscale 0,sackOK,nop,nop,nop,nop,timestamp 0[|tcp]&gt; \
         [flowlabel 0xbbb6b]
</code>On voit ici la machine <computer>2001:7a8:7509:0:216:3eff:fe78:b525</computer> qui annonce, dans le paquet d'établissement de connexion TCP (paquet S pour <computer>SYN</computer>) une MSS de 1400 octets.</p>
<p>Pour modifier cette valeur, on peut bien sûr intervenir sur toutes
les machines du réseau local mais c'est un travail pénible pour
l'administrateur. Mieux vaut modifier de force ce réglage sur le
routeur, ce qui se nomme le <foreign>MSS clamping</foreign>. Cela peut
se faire dans le démon <computer>pppoe</computer> avec l'option -m,
par exemple, dans <computer>/etc/ppp/peers/MON-FAI</computer>, on met
<computer>pty "pppoe -I eth1 -T 80 -m 1412"</computer>. À noter que,
sur <wikipedia>Linux</wikipedia>, ce <foreign>clamping</foreign> ne
fonctionne apparemment que pour IPv4, obligeant à trouver une autre
solution (comme l'annonce d'une MTU par RA) pour IPv6. Le
<foreign>clamping</foreign> existe aussi sur d'autres systèmes ; par
exemple, sur <wikipedia name="Cisco IOS">IOS</wikipedia>, c'est
l'option <computer>ip tcp adjust-mss</computer> (profitons-en pour
signaler que <wikipedia name="Cisco Systems">Cisco</wikipedia> a une <link
url="http://www.cisco.com/en/US/tech/tk827/tk369/technologies_tech_note09186a0080093f1f.shtml">excellente
documentation</link> sur le sujet).</p>
<p>On peut aussi créer une règle <computer>iptables</computer> ou <computer>ip6tables</computer> avec la cible TCPMSS, mais celle-ci n'est
disponible en IPv6 que depuis la version 2.6.21 du noyau Linux, et n'est
acceptée que par des versions très récentes d'iptables. Le principe (je n'ai pas testé moi-même, je copie Pascal Hambourg) :
<code>
iptables -t mangle -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN \
  -j TCPMSS --clamp-mss-to-pmtu
</code>
On peut aussi remplacer <computer>--clamp-mss-to-pmtu</computer>, qui
force (<foreign>to clamp</foreign>) la MSS à la valeur de la MTU par
<computer>--set-mss XXX</computer> qui permet d'indiquer une valeur explicite.</p>
<p>Notons enfin que le fait de fixer la MTU, quel que soit le moyen
utilisé, ne va pas forcément influer directement sur le MSS. Par
exemple, avec <wikipedia>NetBSD</wikipedia>, par défaut, la MSS est
la MTU de l'interface réseau non-locale ayant la plus grande MTU, ce
qui peut être déroutant si on a une interface virtuelle ayant une MTU
de plusieurs dizaines de milliers d'octets. Il peut donc être prudent
de mettre les paramètres <unix>sysctl</unix>
<computer>net.inet6.tcp6.mss_ifmtu</computer> et
<computer>net.inet.tcp.mss_ifmtu</computer> à 1, pour que la MSS soit
celle de la MTU de l'interface utilisée.</p>
<p>Un grand merci à Pascal Hambourg pour son aide pour déboguer ces
problèmes et les documenter. Et merci aussi à Christophe Wolfhugel
pour des détails érudits.</p>
</content>
</entry>


