Een interactieve shell over de tunnel bevriest enkele seconden terwijl een bestandskopie loopt, en gaat dan verder alsof er niets is gebeurd. De download is het enige met een probleem, toch wacht de shell ook, en beide herstellen op hetzelfde moment. Dat patroon is de signatuur van één betrouwbare stroom die veel gesprekken draagt, en het overleeft bijna elke afstemmingswijziging die mensen eerst proberen.

Het symptoom is het zorgvuldig lezen waard, omdat het op een mechanisme wijst en niet op een instelling: de pauze is gedeeld, dus de oorzaak is gedeeld, en het gedeelde object is de buitenste verbinding. Al het andere in het logboek ziet er normaal uit terwijl het gebeurt.

Het symptoom: alles pauzeert tegelijk

Twee interne verbindingen die samen vastlopen zijn de aanwijzing. Een trage download en een trage shell zijn afzonderlijke TCP-gesprekken met afzonderlijke sequentienummers en afzonderlijke congestietoestand, dus een storing in een van beide zou de andere niet moeten raken. Wanneer ze in de pas pauzeren, ordent iets boven hen hun bytes.

Dat iets is het transport van de tunnel. In TCP-modus schrijft OpenVPN de ingekapselde pakketten in één TCP-verbinding naar de peer, en het contract van TCP is levering in volgorde van één bytestroom. Latere bytes worden niet aan de toepassing gegeven vóór de ontbrekende, ongeacht bij welk intern gesprek ze horen.

Let op wat het symptoom niet zegt. Het zegt niet dat het pad kapot is, dat de server overbelast is of dat de versleuteling traag is. Die geven andere vormen: gelijkmatig lage doorvoer, time-outs of fouten die één richting raken. Een gedeelde pauze die eindigt wanneer één verloren segment opnieuw wordt verzonden, is specifiek een ordeningseffect.

Eén externe bytestroom, veel interne gesprekken

Stel je de tunnel voor die tegelijk een download en een shell draagt. Een segment van de buitenste verbinding gaat verloren. De volgende segmenten kunnen bij de ontvangende host aankomen, en de ontvangende TCP-stack zal ze bevestigen, maar hij kan ze niet naar boven afleveren tot de opening is gevuld. OpenVPN heeft dus niets te lezen voor de shell, ook al zijn de bytes van de shell zelf lang geleden aangekomen.

Dit is blokkering aan het hoofd van de wachtrij binnen één verbinding, en het vereist niet dat de twee toepassingen een interne verbinding delen. Ze delen de buitenste, en dat is genoeg.

Het tweede-ordeeffect maakt het erger. Een interne zender die geen bevestiging ziet, kan gegevens opnieuw verzenden die de buitenste laag al probeert af te leveren. Die herverzending wordt extra tunnelverkeer op een pad dat net heeft bewezen segmenten te kunnen verliezen, dus het herstelwerk concurreert met het herstel dat ertoe doet.

Niets hiervan garandeert een instorting. Op een schoon pad met korte rondetijden en ruime wachtrijen kan de opzet jarenlang prima bruikbaar zijn. De storing verschijnt wanneer verlies, vertraging en wachtrijdiepte samenvallen, en daarom clusteren de meldingen rond overbelaste uplinks en mobiele verbindingen en niet rond goed uitgeruste kantoren.

Waarom een langere interne timer niet kan helpen

Het verhogen van de minimale herverzendingstime-out op een route, met rto_min, stelt op timers gebaseerde herverzending uit voor lokaal gegenereerd TCP-verkeer dat die route gebruikt. Het is een echte knop met een echt effect, en hij pakt het tweede-ordeprobleem aan in plaats van het eerste.

ip route show
ip route show cache 2>/dev/null | head
ss -tin dst 10.70.0.1

Wat hij niet kan, is buitenste bytes buiten volgorde vrijgeven, het ontbrekende segment sneller herstellen, of de externe toepassingsserver dezelfde timer laten overnemen. De ontvangende stack levert nog steeds in volgorde, en de zender aan de andere kant gebruikt nog steeds zijn eigen rondetijdmetingen.

Hij schakelt ook niet elk ander mechanisme voor verliesherstel uit. Waar echt verlies herstel op basis van timers vereist, laat een groter minimum de toepassing langer wachten voordat hij handelt, wat het tegenovergestelde is van wat een latentiegevoelige sessie nodig heeft.

Een vaste ondergrens van twaalf seconden duikt op in advies over dit onderwerp alsof het bestaan van een TCP-tunnel die zou impliceren. De in RFC 6298 beschreven herverzendingstimer is afgeleid van gemeten rondetijden met exponentiële backoff, dus een constante ondergrens is een beleidskeuze voor een specifiek pad en geen protocolvereiste. Breed toegepast verbergt hij het symptoom terwijl hij het herstel verandert voor elke stroom die die route dekt.

Wat tcp-nodelay doet en niet doet

De tunneloptie die Nagle-achtige buffering op de transportsocket uitschakelt, heeft een smal doel: hij stopt kleine schrijfacties die op meer gegevens wachten voordat ze worden verzonden. Dat helpt interactief verkeer een extra vertraging te vermijden op een verder gezond pad.

Hij maakt het transport niet ongeordend en verkort het herstel na een verloren segment niet. Een shell die pauzeert voor een herverzendingstime-out zal met die optie aan nog steeds pauzeren, omdat de optie de ordeningsregel nooit heeft aangeraakt.

De twee verwarren is makkelijk, omdat beide als latentiefixes worden beschreven. De ene haalt een kunstmatige wachttijd weg die door buffering is geïntroduceerd; de andere zou vereisen dat verandert wat het transport garandeert, en dat doet geen enkele socketoptie.

Hoe je transporten eerlijk vergelijkt

Als de operator het toestaat en beide diensten bestaan, vergelijk dan een UDP-dienst en een TCP-dienst met dezelfde werklast over hetzelfde toegangspad naar dezelfde server. Land, provider of servergeneratie wisselen tussen de twee tests voert een variabele in die het resultaat zal domineren.

Let op de gedeelde pauze in plaats van op een doorvoergetal. Verschillende interne toepassingen die samen vastlopen tijdens extern verlies is de observatie die blokkering aan het hoofd van de wachtrij identificeert; één lager snelheidstestcijfer doet dat niet.

Geautoriseerde opnames op de tunnelinterface en op de fysieke interface scheiden de twee lagen netjes, omdat interne en externe herverzendingen bij verschillende verbindingen met verschillende sequentieruimten horen. Interne herverzendingen zien terwijl de buitenste stroom een opening toont, is het directe bewijs.

Overschakelen naar UDP verwijdert de extra betrouwbare stroom en laat interne TCP zijn eigen herstel doen. Het belooft geen nul verlies of lage latentie, en een verzadigde uplink, te grote wachtrijen of een MTU-probleem moeten nog steeds worden opgelost. Verkeer buiten de tunnel wordt nooit achter de sequentieopening van de tunnel gehouden, al kan het nog steeds lijden wanneer beide paden één overbelaste link delen, en dat gedeelde knelpunt verdient zijn eigen onderzoek in plaats van hierbij te worden ondergebracht.

Gerelateerde artikelen