OpenVPN en TCP : rto_min ne corrige pas le blocage en tête de file
Un flux d’octets externe livre dans l’ordre, donc un seul segment manquant peut suspendre chaque flux interne. Un minuteur interne plus long change une horloge, pas cette contrainte.
Un shell interactif via le tunnel se fige pendant plusieurs secondes pendant qu’une copie de fichier s’exécute, puis reprend comme si rien ne s’était produit. Le téléchargement est la seule chose qui a un problème, pourtant le shell attend aussi, et les deux se rétablissent au même moment. Ce schéma est la signature d’un flux fiable transportant de nombreuses conversations, et il survit à presque tous les réglages que les gens essaient en premier.
Le symptôme mérite une lecture attentive parce qu’il désigne un mécanisme plutôt qu’un réglage : la pause est partagée, donc la cause est partagée, et l’objet partagé est la connexion externe. Tout le reste du journal semble normal pendant ce temps.
Le symptôme : tout se suspend en même temps
Deux connexions internes qui se bloquent ensemble sont l’indice. Un téléchargement lent et un shell lent sont des conversations TCP distinctes avec des numéros de séquence et un état de congestion distincts, donc une panne dans l’une ne devrait pas toucher l’autre. Quand elles se suspendent en cadence, quelque chose au-dessus d’elles ordonne leurs octets.
Ce quelque chose est le transport du tunnel. En mode TCP, OpenVPN écrit les paquets encapsulés dans une seule connexion TCP vers le pair, et le contrat de TCP est la livraison dans l’ordre d’un unique flux d’octets. Les octets ultérieurs ne sont pas remis à l’application avant les manquants, quel que soit le conversation interne à laquelle ils appartiennent.
Notez ce que le symptôme ne dit pas. Il ne dit pas que le chemin est cassé, que le serveur est surchargé ou que le chiffrement est lent. Ceux-ci produisent des formes différentes : un débit uniformément faible, des délais d’attente ou des défaillances qui touchent une direction. Une pause partagée qui se termine quand un segment perdu est retransmis est spécifiquement un effet d’ordre.
Un flux d’octets externe, de nombreuses conversations internes
Imaginez le tunnel transportant un téléchargement et un shell en même temps. Un segment de la connexion externe est perdu. Les segments suivants peuvent arriver à l’hôte récepteur, et la pile TCP réceptrice les acquittera, mais elle ne peut pas les livrer vers le haut tant que le trou n’est pas comblé. OpenVPN n’a donc rien à lire pour le shell, même si les octets du shell lui-même sont arrivés depuis longtemps.
C’est le blocage en tête de file à l’intérieur d’une connexion, et il n’exige pas que les deux applications partagent une connexion interne. Elles partagent l’externe, ce qui suffit.
L’effet de second ordre l’aggrave. Un expéditeur interne qui ne voit aucun acquittement peut retransmettre des données que la couche externe essaie déjà de livrer. Cette retransmission devient du trafic de tunnel supplémentaire sur un chemin qui vient de prouver qu’il peut perdre des segments, donc le travail de récupération entre en concurrence avec la récupération qui compte.
Rien de tout cela ne garantit un effondrement. Sur un chemin propre avec des allers-retours courts et des files généreuses, l’arrangement peut être parfaitement utilisable pendant des années. La défaillance apparaît quand perte, délai et profondeur de file s’alignent, ce qui explique pourquoi les rapports se concentrent autour des liaisons montantes congestionnées et des liens mobiles plutôt que des bureaux bien provisionnés.
Pourquoi un minuteur interne plus long ne peut pas aider
Relever le délai minimal de retransmission sur une route, avec rto_min, reporte la retransmission pilotée par minuterie pour le trafic TCP généré localement qui emprunte cette route. C’est un vrai bouton avec un vrai effet, et il traite le problème de second ordre plutôt que le premier.
ip route show
ip route show cache 2>/dev/null | head
ss -tin dst 10.70.0.1
Ce qu’il ne peut pas faire : libérer des octets externes hors ordre, récupérer le segment manquant plus vite, ou amener le serveur d’application distant à adopter la même minuterie. La pile réceptrice livre toujours dans l’ordre, et l’expéditeur à l’autre bout utilise toujours ses propres mesures d’aller-retour.
Il ne désactive pas non plus tous les autres mécanismes de récupération de perte. Là où une perte réelle exige une récupération pilotée par minuterie, un minimum plus grand fait attendre l’application plus longtemps avant d’agir, ce qui est l’inverse de ce dont une session sensible à la latence a besoin.
Un plancher fixe de douze secondes apparaît dans les conseils sur ce sujet comme si l’existence d’un tunnel TCP l’impliquait. Le minuteur de retransmission décrit dans la RFC 6298 dérive du temps aller-retour mesuré avec un retrait exponentiel, donc un plancher constant est un choix de politique pour un chemin particulier plutôt qu’une exigence du protocole. Appliqué largement, il masque le symptôme tout en modifiant la récupération de chaque flux que cette route couvre.
Ce que tcp-nodelay fait et ne fait pas
L’option de tunnel qui désactive la mise en tampon de style Nagle sur la socket de transport a un but étroit : elle empêche les petites écritures d’attendre davantage de données avant la transmission. Cela aide le trafic interactif à éviter un délai supplémentaire sur un chemin par ailleurs sain.
Elle ne rend pas le transport désordonné, et elle ne raccourcit pas la récupération après un segment perdu. Un shell qui se suspend pour un délai de retransmission se suspendra encore avec cette option activée, parce que l’option n’a jamais touché la règle d’ordre.
Confondre les deux est facile car les deux sont décrits comme des correctifs de latence. L’un supprime une attente artificielle introduite par la mise en tampon ; l’autre exigerait de changer ce que le transport garantit, ce qu’aucune option de socket ne fait.
Comment comparer les transports équitablement
Si l’opérateur le permet et que les deux services existent, comparez un service UDP et un service TCP transportant la même charge de travail sur le même chemin d’accès vers le même serveur. Changer de pays, de fournisseur ou de génération de serveur entre les deux tests introduit une variable qui dominera le résultat.
Guettez la pause partagée plutôt qu’un chiffre de débit. Plusieurs applications internes se bloquant ensemble pendant une perte externe est l’observation qui identifie le blocage en tête de file ; un unique chiffre de test de vitesse plus bas ne le fait pas.
Des captures autorisées sur l’interface du tunnel et sur l’interface physique séparent proprement les deux couches, car les retransmissions internes et externes appartiennent à des connexions différentes avec des espaces de séquence différents. Voir des retransmissions internes alors que le flux externe montre un trou est la preuve directe.
Passer à UDP supprime le flux fiable supplémentaire et laisse le TCP interne faire sa propre récupération. Il ne promet ni perte nulle ni faible latence, et une liaison montante saturée, des files surdimensionnées ou un problème de MTU devront encore être corrigés. Le trafic hors du tunnel n’est jamais retenu derrière le trou de séquence du tunnel, bien qu’il puisse encore souffrir quand les deux chemins partagent un lien congestionné, et ce goulot d’étranglement partagé mérite sa propre enquête plutôt que d’être replié dans celle-ci.