WireGuard lent : trouver la vraie cause
WireGuard lent ? Une MTU incorrecte peut bloquer les gros transferts tout en laissant SSH fonctionner. Testez le path MTU, ajustez le TCP MSS et vérifiez le steal time.
D’où viennent réellement les faibles débits de WireGuard
Les faibles débits de WireGuard ont généralement l’une de quatre causes, qui ne sont pas toutes aussi probables. La première est la MTU (unité de transmission maximale) : le tunnel construit des paquets trop volumineux pour l’un des liens du chemin réseau, ce qui bloque les transferts importants alors que les petits transferts fonctionnent correctement. La deuxième est le chemin réseau lui-même, qui était déjà la limite avant la mise en place du tunnel. La troisième est le CPU d’un petit VPS partagé, où le chiffrement entre en concurrence avec tous les autres invités du même hôte. La quatrième est la connexion du pair lui-même.
Vérifiez ces causes dans cet ordre. La MTU vient en premier, car c’est la seule cause de la liste introduite par WireGuard lui-même et parce que ses symptômes ne ressemblent pas du tout à un ralentissement. Une MTU incorrecte se manifeste généralement par un tunnel qui se connecte instantanément, répond à ping, accepte une connexion SSH, puis se bloque dès que vous copiez un fichier.
Il faut écarter un symptôme avant tout cela. Si chaque nouveau site met plusieurs secondes à commencer son chargement, puis se transfère à pleine vitesse, le problème vient de la résolution de noms et non du débit. Le DNS avec WireGuard a ses propres modes de défaillance ; aucune modification de la MTU ne les corrige.
Pourquoi la MTU de WireGuard est-elle de 1420 ?
Chaque paquet que vous envoyez dans le tunnel est chiffré et encapsulé dans un nouveau paquet. Cette encapsulation consomme des octets, qui sont retirés de la charge utile.
L’en-tête de données WireGuard fait 32 octets : un champ de type de 4 octets, un index de récepteur de 4 octets, un compteur de 8 octets et un tag d’authentification Poly1305 de 16 octets. Il est entouré d’un en-tête UDP de 8 octets. Celui-ci est entouré de l’en-tête IP externe, qui fait 20 octets en IPv4 et 40 octets en IPv6. L’encapsulation totale consomme donc 60 octets lorsque votre Endpoint est une adresse IPv4, et 80 octets lorsqu’il s’agit d’une adresse IPv6. La page du protocole WireGuard documente la structure des messages dont proviennent ces valeurs.
wg-quick ne devine pas cette valeur. Il lit la MTU de l’interface qui achemine le trafic vers votre Endpoint, puis soustrait 80. Sur un chemin Ethernet ordinaire de 1500 octets, cela donne 1420, qui est la valeur affichée par ip link show wg0. La soustraction de 80 au lieu de 60 garantit que cette valeur reste sûre si ce point d’accès est un jour atteint en IPv6, où l’en-tête externe est supérieur de 20 octets.
The data behind this chart
[
{
"label": "Ethernet, IPv4 endpoint",
"path_mtu": 1500,
"encap_overhead": 60,
"usable_wg0_mtu": 1440
},
{
"label": "Ethernet, IPv6 endpoint",
"path_mtu": 1500,
"encap_overhead": 80,
"usable_wg0_mtu": 1420
},
{
"label": "PPPoE DSL, IPv4 endpoint",
"path_mtu": 1492,
"encap_overhead": 60,
"usable_wg0_mtu": 1432
},
{
"label": "Extra tunnel in the path",
"path_mtu": 1400,
"encap_overhead": 80,
"usable_wg0_mtu": 1320
}
]Ces 4 lignes correspondent à des calculs, pas à des mesures. Sur un chemin propre de 1500 octets avec un endpoint IPv4, 1440 pourrait être utilisé, et la valeur par défaut de 1420 laisse donc 20 octets inutilisés. Cette marge est intentionnelle. Elle ne constitue pas un problème.
La dernière ligne est la plus importante. Lorsqu’un maillon du chemin ne transporte que 1400 octets, un tunnel configuré avec 1420 produit un paquet externe de 1500 octets pour chaque segment de taille maximale, soit 100 octets de plus que ce que ce maillon accepte. La valeur adaptée est 1320.
Ne copiez pas non plus 1320. La MTU de votre chemin dépend de ce chemin. Le seul moyen de la connaître consiste à la mesurer.
À quoi ressemble un MTU incorrect
La panne n’est pas progressive. Elle sépare nettement les petits paquets des gros paquets.
pingdans le tunnel fonctionne avec n’importe quelle taille normale.- La connexion SSH s’établit et la saisie reste réactive.
curl -I https://example.comrenvoie immédiatement les en-têtes.curl https://example.comsur une page volumineuse se bloque après les premiers kilooctets.scpd’un gros fichier démarre, puis s’arrête à un certain pourcentage.- Une session SSH se fige dès que vous exécutez une commande qui génère beaucoup de sortie.
Tout cela se produit parce qu’une connexion TCP ne construit des segments de taille maximale que lorsqu’elle doit transférer beaucoup de données. La négociation et la première requête tiennent sous toutes les valeurs de MTU du chemin. Le blocage commence avec le premier segment de taille maximale. C’est pourquoi la connexion semble fonctionner jusqu’au moment où elle devient inutilisable.
Un paquet externe plus grand que le MTU du lien suivant connaît l’un de ces deux traitements.
Il est fragmenté. Un routeur le découpe et l’extrémité distante réassemble les fragments. Le transfert fonctionne, mais il est plus lent, car un paquet nécessite désormais deux paquets et le récepteur doit conserver un état jusqu’à la réception des deux fragments. La perte d’un fragment entraîne la perte du paquet d’origine entier. Un chemin avec 1 % de pertes se comporte donc comme un chemin bien plus dégradé. De nombreux firewalls bloquent également les fragments IP selon leur politique. Ce traitement devient alors le suivant.
Il est supprimé, et vous pouvez en être informé ou non. Un routeur qui ne veut pas fragmenter renvoie à l’émetteur un message ICMP (internet control message protocol) « fragmentation needed », contenant le MTU qu’il peut accepter. Si ce message arrive, la découverte du MTU du chemin fonctionne et l’émetteur réduit automatiquement la taille de ses segments. De nombreux réseaux filtrent ICMP, qui n’arrive donc souvent pas à destination. Rien d’autre ne signale la perte. C’est le black hole : le paquet part, rien ne revient, aucune erreur n’apparaît dans les journaux des deux extrémités et le transfert se bloque jusqu’à l’expiration d’un délai.
Comment trouver la bonne MTU ?
Mesurez le chemin, puis faites la soustraction. Testez le réseau sous-jacent plutôt que le tunnel. Envoyez donc un ping vers l’adresse publique du serveur depuis le client et interdisez la fragmentation.
ping -M do -s 1472 -c 3 203.0.113.10-M do définit le bit DF (« don't fragment »). Aucun routeur traversé ne peut donc fragmenter le paquet. -s correspond à la taille de la charge utile ICMP. Un paquet IPv4 complet contient cette charge utile, plus 8 octets d’en-tête ICMP et 20 octets d’en-tête IP. La valeur -s 1472 place donc exactement 1500 octets sur le réseau.
Trois résultats sont importants. Des réponses normales signifient que 1500 octets passent et que la MTU n’est pas en cause. Une erreur locale signifie que votre propre interface est déjà plus petite que la valeur demandée :
ping: local error: message too long, mtu=1500Une réponse provenant d’un routeur intermédiaire vous donne directement la valeur. Vous pouvez vous arrêter :
From 203.0.113.1 icmp_seq=1 Frag needed and DF set (mtu = 1492)Une perte de 100 % des paquets à 1472, avec des réponses normales à une taille inférieure, indique un black hole. Aucun routeur ne vous informe du problème. Trouvez donc la limite par dichotomie. Conservez une taille dont vous savez qu’elle fonctionne et une autre dont vous savez qu’elle échoue. Testez le milieu, puis déplacez la borne correspondant au résultat. Chaque étape divise la plage restante par deux. Cinq ou six étapes suffisent.
Exemple de dichotomie, étape par étape
Chaque ligne est une commande exécutée sur le client vers l’adresse publique du serveur. Le commentaire indique le résultat obtenu.
ping -M do -s 1472 -c 3 203.0.113.10 # 100% loss, so 1500 is too big
ping -M do -s 1272 -c 3 203.0.113.10 # replies, so 1300 fits
ping -M do -s 1372 -c 3 203.0.113.10 # replies, so 1400 fits
ping -M do -s 1422 -c 3 203.0.113.10 # 100% loss, so 1450 is too big
ping -M do -s 1397 -c 3 203.0.113.10 # 100% loss, so 1425 is too big
ping -M do -s 1384 -c 3 203.0.113.10 # 100% loss, so 1412 is too bigLa plus grande charge utile qui a fonctionné est 1372. Ce chemin transporte donc au moins 1400 octets et moins de 1412 octets. Retenez la valeur sûre. Une MTU de chemin de 1400, moins 80 octets d’encapsulation, donne une MTU de 1320 pour wg0.
tracepath effectue cette recherche automatiquement. Il est utile de l’exécuter une fois avant de commencer la dichotomie :
tracepath -n 203.0.113.10Sa dernière ligne indique le résultat :
Resume: pmtu 1492 hops 12 back 12Considérez ces deux outils comme un point de départ, et non comme une preuve. Certains hôtes limitent le débit d’ICMP ou suppriment complètement les paquets ICMP. Une dichotomie peut donc indiquer une MTU inférieure à celle réellement supportée par le chemin. Le véritable test est le transfert qui échouait.
Appliquez d’abord la valeur à chaud. En cas d’erreur, une seule commande permet ainsi de revenir en arrière :
sudo ip link set mtu 1320 dev wg0Relancez le transfert qui restait bloqué. S’il se termine, rendez la valeur permanente dans le bloc [Interface] du client :
[Interface]
PrivateKey = <client private key>
Address = 10.8.0.2/32
MTU = 1320sudo wg-quick down wg0 && sudo wg-quick up wg0
ip link show wg0ip link show wg0 doit maintenant afficher mtu 1320. S’il affiche encore l’ancienne valeur, wg-quick n’a pas lu le fichier que vous avez modifié. Vérifiez que vous avez modifié /etc/wireguard/wg0.conf et que MTU se trouve sous [Interface], et non sous [Peer], où il est ignoré.
La MTU est une propriété d’une interface. Elle n’est jamais négociée entre les pairs. La définir sur le client réduit uniquement la taille des paquets envoyés par le client. Le serveur continue de construire ses paquets avec la MTU configurée pour son interface wg0. Les téléchargements peuvent donc encore être bloqués par un black hole même si les envois fonctionnent de nouveau. Définissez la valeur aux deux extrémités ou limitez la MSS sur le serveur.
Pourquoi le clamping MSS corrige TCP et rien d’autre
Si le serveur relaie le trafic de ses pairs, comme c’est le cas dans toute configuration WireGuard VPS standard utilisant le NAT (network address translation), une seule règle corrige TCP pour tous les pairs. Vous n’avez donc pas à rechercher une valeur sur chaque client que vous ne contrôlez pas.
La MSS (maximum segment size) est une option TCP que chaque côté place dans son paquet SYN pour indiquer la taille maximale du segment qu’il accepte de recevoir. Le clamping réécrit cette option en transit afin de l’adapter à la MTU réelle du chemin. Les deux extrémités utilisent ainsi une taille de segment inférieure avant le transfert des données. Cette méthode fonctionne parce qu’elle intervient pendant le handshake et ne dépend pas d’un message ICMP que le chemin filtre probablement.
Avec nftables, ajoutez cette table dans /etc/nftables.conf, sous vos tables existantes :
table inet mangle {
chain forward {
type filter hook forward priority mangle; policy accept;
tcp flags syn tcp option maxseg size set rt mtu
}
}Rechargez la configuration avec sudo systemctl reload nftables. Sur un serveur utilisant iptables, l’équivalent tient en une ligne :
sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtuVérifiez que la règle se trouve bien sur le chemin emprunté par les paquets. Exécutez sudo nft list table inet mangle ou sudo iptables -t mangle -L FORWARD -n -v pendant qu’un client ouvre de nouvelles connexions, puis surveillez l’augmentation du compteur. Un compteur qui reste à zéro signifie que les paquets ne passent pas par ce hook. La règle ne fait donc rien.
Voici maintenant les limites réelles. Le clamping couvre TCP et rien d’autre. Il ne couvre que le trafic transféré. Un service exécuté directement sur le serveur WireGuard ne passe jamais par le hook forward et n’est donc jamais soumis au clamping. Cette règle ne s’applique pas non plus aux connexions ouvertes avant son chargement : les sessions existantes conservent la MSS négociée initialement.
UDP n’est pas modifié, car UDP n’a pas de handshake à réécrire. La plupart du trafic UDP fonctionne malgré tout. QUIC, le transport utilisé par HTTP/3, sonde sa propre taille de paquet utilisable et commence volontairement avec une petite taille. En revanche, UDP ne fonctionne pas lorsqu’il envoie un datagramme volumineux en espérant qu’il arrive sans fragmentation, comme une réponse DNSSEC (DNS security extensions) de plus de 1400 octets. Ces requêtes expirent, puis sont relancées via TCP. Pour l’utilisateur, le site semble alors lent plutôt que hors service.
La limite vient-elle du CPU de mon VPS ?
WireGuard chiffre avec ChaCha20-Poly1305 et échange les clés avec Curve25519. AES n’intervient jamais dans le chemin des données. Cela entraîne une conséquence souvent mal comprise : les instructions AES-NI de votre CPU ne servent à rien pour WireGuard. Un hôte qui annonce la prise en charge d’AES-NI ne vous offre donc aucun avantage de performances pour WireGuard. ChaCha20 a été choisi parce qu’il est rapide en logiciel, y compris sur les CPU qui ne disposent d’aucune accélération cryptographique.
Cela ne signifie pas que WireGuard ne consomme aucune ressource. Sur un VPS doté d’1 vCPU, un seul cœur gère le chiffrement et les interruptions réseau, en plus du travail effectué par vos applications.
Mesurez l’utilisation pendant le transfert :
sudo apt install -y sysstat
mpstat -P ALL 1Lisez les trois colonnes. %soft correspond au temps passé dans les softirqs, où s’effectue le traitement des paquets par le noyau. %steal correspond au temps que l’hyperviseur a attribué à un autre utilisateur. %idle correspond au temps restant.
%soft proche de 100 sur votre seul cœur signifie que le serveur a atteint sa limite de traitement des paquets. Il s’agit d’une limite réelle, que l’ajout de cœurs permettra d’augmenter. top affiche ksoftirqd/0 en tête de la liste des processus au même moment. C’est le même constat, observé sous un autre angle.
%steal supérieur à quelques pour cent signifie que vous ne pouvez pas corriger cette limite vous-même. L’hôte est alors surchargé et votre vCPU attend qu’un cœur physique se libère. Cette situation est fréquente sur les offres mutualisées les moins chères et varie au cours de la journée. Le temps volé par un voisin bruyant nécessite une analyse distincte. Aucune valeur de MTU ne permettra de le résoudre.
Un autre facteur est l’implémentation utilisée par le client. Le module du noyau Linux constitue le chemin rapide et répartit le chiffrement d’un pair entre plusieurs cœurs. wireguard-go, l’implémentation en espace utilisateur, est plus lente. C’est celle qu’utilisent les clients macOS et iOS, car ces plateformes n’autorisent pas une application à charger un module du noyau.
Le problème vient-il du chemin réseau ou de la liaison du pair ?
Avant de modifier quoi que ce soit, relevez deux mesures depuis le même client, à quelques minutes d’intervalle : le débit sans le tunnel, puis le débit avec le tunnel. Sans ces deux mesures, vous ne faites que supposer.
Lancez iperf3 -s sur le serveur. Le test direct nécessite que TCP 5201 soit accessible sur l’adresse publique. Ouvrez donc ce port pendant la durée du test, puis supprimez la règle. Vérifiez que le port est de nouveau fermé au lieu de le supposer.
# on the client, outside the tunnel
iperf3 -c 203.0.113.10
# then through the tunnel
iperf3 -c 10.8.0.1Si les deux mesures sont proches, WireGuard a un coût très faible et la limite vient du chemin réseau. Si le débit avec le tunnel est nettement inférieur au débit direct alors que %soft est resté faible, revenez au réglage de la MTU. La fragmentation réduit le débit sans provoquer de panne. Elle apparaît donc ici sous la forme d’une perte en pourcentage, et non d’un blocage.
Testez les deux directions, car les connexions résidentielles sont généralement asymétriques. iperf3 -c 10.8.0.1 -R inverse le flux afin que le serveur émette. Un client disposant d’une ligne à 500/20 ne pourra jamais envoyer plus de 20 Mbit/s dans le tunnel. Aucun changement côté serveur ne peut modifier cette limite.
Testez ensuite avec plusieurs flux parallèles :
iperf3 -c 10.8.0.1 -P 4Si quatre flux transfèrent ensemble beaucoup plus de données qu’un seul, une connexion TCP ne parvient pas à remplir le chemin réseau. Le débit d’un flux est limité par la fenêtre de réception divisée par le temps aller-retour. Un chemin avec 150 ms de latence nécessite donc une fenêtre importante pour transporter beaucoup de données. Consultez vos propres limites :
sysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmemLa troisième valeur de chaque commande correspond à la valeur maximale que Linux peut définir automatiquement. La perte de paquets limite également fortement un flux unique, car le contrôle de congestion TCP réagit aux pertes. Sur un chemin long, la récupération coûte davantage. Lancez mtr -rwc 100 203.0.113.10 depuis le client pendant cent cycles pour déterminer où les pertes apparaissent sur le trajet. Une perte qui commence à un saut et continue jusqu’au saut final est réelle. Une perte présente sur un seul routeur intermédiaire, puis absente des sauts suivants, indique que ce routeur donne une priorité moindre à ICMP. Elle n’a aucune signification.
Pour obtenir une mesure reproductible du serveur lui-même, indépendamment du réseau, évaluez les performances de la VPS avec une méthode documentée afin de pouvoir relancer le même test après une modification et comparer des résultats comparables.
Ce que WireGuard ne peut pas résoudre
WireGuard est un tunnel. Il ne peut pas être plus rapide que le maillon le plus lent du chemin qu’il emprunte, et son ajout ralentit toujours légèrement ce chemin.
Il ne compresse pas les données. Il n’existe pas d’équivalent à comp-lzo d’OpenVPN, et ce n’est pas prévu, car compresser les données avant de les chiffrer divulgue des informations sur le texte en clair. La plupart des données volumineuses sont déjà compressées, donc cela ne vous coûte rien en pratique. C’est l’une des véritables différences à prendre en compte lorsque vous comparez WireGuard à OpenVPN, et il s’agit d’un choix de conception délibéré.
Un full tunnel modifie la route empruntée par chaque paquet. Le trafic qui circulait auparavant entre vous et un CDN proche (content delivery network) circule désormais entre vous, votre VPS, puis le CDN. Si le VPS se trouve sur un autre continent, chaque requête subit ce détour et le temps d’aller-retour augmente en conséquence. Aucune valeur de configuration ne peut le réduire. Rapprochez le VPS, ou utilisez un split tunnel afin que seul le trafic qui a besoin du VPN emprunte le chemin le plus long. Le routage du trafic est entièrement déterminé par AllowedIPs, et le routage par clé cryptographique explique comment cette décision est prise.
Les limites du fournisseur se trouvent en dehors du tunnel et sont faciles à oublier. Une offre avec un quota mensuel de bande passante limite souvent le port à un débit bien inférieur une fois le quota épuisé, et le tunnel semble alors fonctionner incorrectement. Vérifiez votre panneau de contrôle avant de passer la soirée à modifier la MTU.
PersistentKeepalive n’a aucune incidence sur le débit. Il sert à maintenir une correspondance NAT ouverte afin que le serveur puisse toujours joindre un client situé derrière un routeur domestique. Le réduire à moins de 25 secondes ajoute des paquets et ne résout rien.
Mesurez dans cet ordre
- Reproduisez le problème et indiquez s’il s’agit d’un blocage ou d’un ralentissement régulier. Un blocage indique un problème de MTU. Un ralentissement régulier ne l’indique pas.
- Effectuez une recherche dichotomique avec
ping -M do, du client vers l’adresse publique du serveur, et notez la MTU du chemin. - Soustrayez 80, définissez cette MTU sur wg0 aux deux extrémités, puis testez de nouveau le transfert qui échouait.
- Ajoutez un réglage MSS sur le serveur s’il transmet le trafic des pairs.
- Exécutez
mpstat -P ALL 1pendant un transfert, puis consultez%softet%steal. - Exécutez
iperf3en dehors du tunnel et à l’intérieur de celui-ci, dans les deux directions, avec un seul flux puis avec-P 4. - Exécutez
mtr -rwc 100vers le serveur et recherchez une perte qui persiste jusqu’au dernier saut.
Ne changez de formule serveur qu’après que l’étape 5 a montré que le CPU constitue la limite. Les étapes 1 à 4 ne coûtent rien et résolvent la plupart des signalements de tunnel lent.
FAQ
Pourquoi mon tunnel WireGuard est-il rapide avec ping, mais lent pour les téléchargements ?
Cette différence indique généralement un problème de MTU. Les petits paquets passent sur toutes les liaisons du chemin, donc ping et une connexion SSH fonctionnent. Un transfert important envoie des segments de taille maximale. Leur version encapsulée dépasse alors la taille acceptée par certaines liaisons. Si le routeur les abandonne sans qu’un message ICMP ne revienne, rien ne signale la perte et le transfert reste bloqué. Déterminez la MTU du chemin par dichotomie avec ping -M do vers l’adresse publique du serveur, soustrayez 80 octets pour l’encapsulation, puis définissez le résultat comme MTU de wg0 aux deux extrémités.
Quelle MTU dois-je définir pour WireGuard ?
Il n’existe pas de valeur universelle. C’est précisément pourquoi la valeur par défaut de 1420 échoue dans certains cas. 1420 correspond à 1500 moins 80 octets pour l’en-tête WireGuard, l’en-tête UDP et l’en-tête IPv6 externe. Si le chemin transporte moins de 1500 octets, ce qui est courant avec le DSL PPPoE et lorsque le trafic traverse un autre tunnel, vous devez utiliser une valeur plus faible. Mesurez d’abord la MTU du chemin, puis soustrayez 80.
Le MSS clamping remplace-t-il la configuration de la MTU ?
Non. Le clamping réécrit l’option MSS pendant le handshake TCP afin que les deux extrémités envoient des segments plus petits. Cela corrige TCP sans modifier l’interface. UDP n’a pas de handshake à réécrire et n’est donc pas concerné. Le clamping s’applique aussi uniquement au trafic que le serveur route. Un service exécuté directement sur le serveur WireGuard n’en bénéficie donc pas. Utilisez les deux mécanismes : une MTU correcte sur l’interface et le clamping pour les peers dont vous ne contrôlez pas la configuration.
Un forfait VPS plus rapide rendra-t-il WireGuard plus rapide ?
Uniquement si le CPU est le facteur limitant, et une commande permet de le vérifier. Exécutez mpstat -P ALL 1 pendant un transfert. %soft proche de 100 sur votre seul cœur signifie que le traitement des paquets est le facteur limitant. Un plus grand nombre de cœurs augmentera alors le débit. Un %steal élevé signifie que l’hôte est surchargé. La solution consiste alors à choisir un autre forfait ou un autre hôte. Si les deux valeurs sont faibles alors que le tunnel reste lent, le CPU est inactif et un forfait plus puissant ne changera rien.
Pourquoi mon Mac est-il plus lent que mon client Linux sur le même réseau ?
Le client Linux utilise le module WireGuard intégré au noyau. Il traite les paquets dans l’espace noyau et répartit le chiffrement d’un peer entre plusieurs cœurs CPU. Les applications macOS et iOS utilisent wireguard-go, une implémentation en espace utilisateur, car ces plateformes ne permettent pas à une application de charger un module noyau. L’espace utilisateur copie chaque paquet entre le noyau et l’application, ce qui réduit le débit. Cette différence est normale et aucun réglage du client ne permet de la supprimer.