Du simptomoj aperas en la sama VpnService-buklo kaj estas kulpigitaj pri la sama afero: leganto kiu neniam revenas, kaj skribo kiu malsukcesas kun ENOBUFS. Ili ne estas variaĵoj de unu cimo. Blokita legado estas malkonsento pri fluokontrolo kun la peer; malsukcesinta skribo estas paketo kiun la interfaco ne akceptos. Se vi traktas ilin kiel unu problemon, vi pasigos tagon agordante bufrojn kiuj neniam estis implikitaj. La suba divido estas kiel mi decidas kiun duonon de la buklo instrumenti unue.

Unu buklo, du sendependaj fiaskopunktoj

Via servo legas IP-paketon el la TUN-dosierdeskriptoro, ekzamenas ĝin kaj skribas respondon reen al la sama deskriptoro. Ĉio, kio rompiĝas sur tiu vojo, montriĝas ĉe unu el tiuj du alvokoj, do ambaŭ estas raportitaj kiel rompita tunelo. La alvokoj havas malsamajn kontraktojn. Legado atendas ke la kerno donu al vi paketon, kaj skribo donas al la kerno paketon kiun ĝi rajtas rifuzi.

Legadoj estas la facila duono por pripensi. Legado kiu redonas nenion aŭ atendas trafikon kiu neniam alvenas aŭ atendas ĉar la peer ĉesis sendi.

Blokita legado estas malkonsento pri fluokontrolo

Legado de TUN-aparato blokiĝas ĝis paketo haveblas. Tio estas desegnita konduto, kaj tial fadeno parkita en read aspektas normala sur neaktiva ligilo. Ĝi fariĝas pendado kiam via propra TCP-stako daŭre akceptas datumojn de la loka aplikaĵo sed neniam anoncas sufiĉan fenestron por ke la fora peer sendu pli. La peer plenigas la fenestron kiun vi anoncis, atendas ACK kaj fenestran ĝisdatigon, kaj haltas. Tiam via leganto havas nenion por redoni, kaj la aplikaĵo vidas konekton kiu estas malfermita kaj silenta.

La kutimaj kaŭzoj estas stako kiu skribas en la ingon sen spuri la anoncitan fenestron, kaj unu kiu forgesas sendi fenestran ĝisdatigon post malplenigi sian bufron. De ekstere ili aspektas identaj: bajtoj eliras, nenio revenas, neniu eraro leviĝas. Blokado de la lega fadeno ĉe analizado, DNS-rezolucio aŭ disk-I/O produktas la saman formon de simptomo pro nerilata kaŭzo, do konfirmu ke la fadeno vere estas ene de read antaŭ ol kulpigi fluokontrolon.

Kontrolu la sendovicon de la peer kaj la fenestron kiun ĝi lastatempe vidis. Se la peer tenas datumojn ĉar via fenestro estas nulo, la korekto estas en via ACK-vojo, ne en la TUN-legado.

ENOBUFS estas grandproblemo, ne bufra manko

Skribo kiu redonas ENOBUFS sur TUN-interfaco kutime rifuzas la paketan longon. La interfaco havas MTU, paketo pli granda ol ĝi ne povas esti vicigita, kaj ripeta provo de la sama paketo malsukcesas same ĉiufoje. Tiu determinismo estas la signo. Vera bufra manko klariĝas kiam la premo falas, dum granda rifuzo ne zorgas kiom longe vi atendas.

TCP havas mekanismon ĝuste por tio, kaj ĝi estas la paŝo kiun homoj preterlasas: la MSS-clamp. Kiam vi sintezas segmentojn en uzantspaco, vi elektas la utilŝarĝan grandecon, kaj elekti ĝin el la MTU de la loka interfaco anstataŭ el la MTU de la tunelo eligas segmentojn kiujn la tunelo ne povas porti. Limigu al la pli malgranda el la du. La clamp kovras nur TCP, do trogranda UDP-datagramo devas esti fragmentita aŭ forĵetita de via kodo.

Registru la longon de ĉiu paketo kiun vi skribas dum vi sencimigas. La longo kiu malsukcesas estas la respondo, kaj ĝi ne ŝanĝiĝos inter provoj.

Tri mezuroj kiuj apartigas la kazojn

Vi ne bezonas sencimigilon por iu ajn el la du fiaskoj. Tri observoj sufiĉas por direkti vin al la ĝusta sekcio, kaj ili daŭras malpli ol rekonstruo.

What you seeWhere to look
Reader parked, peer send queue empty, last advertised window zeroWindow updates in your ACK path
Write fails on one length and succeeds on a smaller oneMSS clamp and tunnel MTU
Both directions look fine, app still sees no dataProtected socket and routing loop

Se la tria vico kongruas, la buklo mem estas en ordo kaj la problemo kuŝas kie la tunelo ne povas vidi ĝin: neprotektita transporta ingo portanta vian propran VPN-trafikon reen en la tunnelon, kio kutime aperas kiel konekto kiu sukcesas kaj poste liveras nenion.

Riparu la tubaron de kiu ambaŭ vojoj dependas

Neniu simptomo indas ĉasi ĝin antaŭ ol la interfaco estas ĝuste konstruita. La builder-alvokoj kiuj gravas estas la adreso, la vojo, la MTU kaj la konsento, en tiu ordo:

Builder()
  .addAddress(10.8.0.2, 32)
  .addRoute(0.0.0.0, 0)
  .setMtu(1400)
  .establish()

Agordu la MTU al la reala valoro de la tunelo anstataŭ lasi ĝin je tio, kion la ekzemplo uzis. Ĉio, kion vi poste skribos, estas limigita de tiu nombro, kaj malkongruo inter la MTU kiun vi agordas kaj la MSS al kiu vi limigas estas la plej ofta maniero reenkonduki la skriberaron post korekto. Poste protektu la ingon kiu portas trafikon al la VPN-servilo mem. Sen protect, tiuj paketoj estas reen direktitaj en vian propran tunnelon kaj la konekto neniam finiĝas, kio aspektas kiel blokita legado sed estas vojiga buklo.

Konsento ne povas esti preterpasita. Servo startigita sen ke prepare redonu null ne ricevas interfacon, kaj establish redonas null anstataŭ ĵeti, do kontrolu la deskriptoron antaŭ ol starti vian buklon. La VpnService-referenco estas la loko por konfirmi la builder-kontrakton anstataŭ kopii ekzemplon.

Kontrolu unu direkton samtempe

Sendu UDP-ekon tra la tunelo kaj observu paketajn longojn antaŭ ol tuŝi TCP. Poste malfermu manpremon kaj konfirmu ke la segmentoj restas sub la MSS kiun vi kalkulis. Kalkulu paketojn anstataŭ konektojn, por ke blokiĝo aperu kiel breĉo en la nombrilo anstataŭ pendanta ingo. Se legadoj haltas post eksplodo de skriboj, la fenestro estas la suspektato; se skriboj malsukcesas tuj, ĝi estas la longo.

Poste restartigu la aparaton kaj ripetu unufoje, ĉar VpnService kiu funkcias nur ĝis la unua retŝanĝo estas vojiga problemo, ne fluokontrola.

Rilataj artikoloj