Kaksi oiretta ilmaantuu samaan VpnService-silmukkaan ja molempia syytetään samasta asiasta: lukija, joka ei koskaan palaa, ja kirjoitus, joka epäonnistuu ENOBUFSilla. Ne eivät ole yhden bugin muunnelmia. Jumissa oleva luku on erimielisyyttä vuonhallinnasta vertaisen kanssa; epäonnistunut kirjoitus on paketti, jota rajapinta ei hyväksy. Jos käsittelet niitä yhtenä ongelmana, vietät päivän säätäen puskureita, jotka eivät koskaan olleet mukana. Alla oleva jako on tapani päättää, kumpi silmukan puolisko instrumentoidaan ensin.

Yksi silmukka, kaksi riippumatonta vikapistettä

Palvelusi lukee IP-paketin TUN-tiedostokuvauksesta, tutkii sen ja kirjoittaa vastauksen takaisin samaan kuvaukseen. Kaikki, mikä tuolla polulla hajoaa, ilmenee toisessa näistä kahdesta kutsusta, minkä vuoksi molemmat raportoidaan rikkinäisenä tunnelina. Kutsuilla on erilaiset sopimukset. Luku odottaa, että ydin antaa sinulle paketin, ja kirjoitus antaa ytimelle paketin, jonka se saa hylätä.

Luvut ovat se helppo puolisko ajatella. Luku, joka ei palauta mitään, joko odottaa liikennettä, joka ei koskaan saavu, tai odottaa koska vertainen lopetti lähettämisen.

Jumissa oleva luku on erimielisyyttä vuonhallinnasta

TUN-laitteen lukeminen estyy, kunnes paketti on saatavilla. Se on suunniteltu käyttäytyminen, ja siksi readin sisään pysäköity säie näyttää normaalilta tyhjällä linkillä. Siitä tulee jumitus, kun oma TCP-pinosi jatkaa datan vastaanottamista paikalliselta sovellukselta mutta ei koskaan ilmoita riittävää ikkunaa, jotta etävertainen voisi lähettää lisää. Vertainen täyttää ilmoittamasi ikkunan, odottaa ACKia ja ikkunan päivitystä ja pysähtyy. Silloin lukijallasi ei ole mitään palautettavaa, ja sovellus näkee yhteyden, joka on auki ja hiljainen.

Yleiset syyt ovat pino, joka kirjoittaa socketiin seuraamatta ilmoitettua ikkunaa, ja toinen, joka unohtaa lähettää ikkunan päivityksen tyhjennettyään puskurinsa. Ulkopuolelta ne näyttävät identtisiltä: tavuja lähtee, mitään ei tule takaisin, virhettä ei nosteta. Lukusäikeen estäminen jäsentämisessä, DNS-selvityksessä tai levy-I/Ossa tuottaa saman muotoisen oireen eri syystä, joten varmista, että säie on todella readin sisällä ennen kuin syytät vuonhallintaa.

Tarkista vertaisen lähetysjono ja ikkuna, jonka se viimeksi näki. Jos vertainen pitää dataa, koska ikkunasi on nolla, korjaus on ACK-polullasi, ei TUN-luennassa.

ENOBUFS on kokoprobleema, ei puskuripula

TUN-rajapinnalla ENOBUFSin palauttava kirjoitus hylkää yleensä paketin pituuden. Rajapinnalla on MTU, sitä suurempaa pakettia ei voi jonottaa, ja saman paketin uudelleenyritys epäonnistuu joka kerta samalla tavalla. Se deterministisyys on vihje. Todellinen puskuripula väistyy paineen laskiessa, kun taas kokohylkäys ei välitä, kuinka kauan odotat.

TCP:llä on mekanismi juuri tähän, ja se on vaihe, jonka ihmiset ohittavat: MSS-rajoitus. Kun muodostat segmenttejä käyttäjätilassa, valitset hyötykuorman koon, ja sen ottaminen paikallisen rajapinnan MTU:sta tunnelin MTU:n sijaan tuottaa segmenttejä, joita tunneli ei pysty kuljettamaan. Rajoita kahdesta pienempään. Rajoitus kattaa vain TCP:n, joten liian suuri UDP-datagrammi on koodisi pilkottava tai hylättävä.

Kirjaa jokaisen kirjoittamasi paketin pituus virheenjäljityksen aikana. Pituus, joka epäonnistuu, on vastaus, eikä se muutu yritysten välillä.

Kolme mittausta, jotka erottavat tapaukset

Et tarvitse debuggeria kumpaankaan vikaan. Kolme havaintoa riittää ohjaamaan sinut oikeaan osioon, ja ne vievät vähemmän aikaa kuin uudelleenrakennus.

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

Jos kolmas rivi täsmää, silmukka itsessään on kunnossa ja ongelma on siellä, missä tunneli ei näe sitä: suojaamaton kuljetussocket, joka kuljettaa oman VPN-liikenteesi takaisin tunneliin, mikä näkyy yleensä yhteytenä, joka onnistuu ja sitten ei toimita mitään.

Korjaa putkisto, josta molemmat polut riippuvat

Mikään oire ei ansaitse jahtaamista ennen kuin rajapinta on rakennettu oikein. Merkitsevät builder-kutsut ovat osoite, reitti, MTU ja suostumus, tässä järjestyksessä:

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

Aseta MTU tunnelin todelliseen arvoon sen sijaan, että jättäisit sen siihen, mitä esimerkki käytti. Kaikki myöhemmin kirjoittamasi rajoittuu tuohon numeroon, ja asettamasi MTU:n ja rajoittamasi MSS:n välinen ristiriita on yleisin tapa tuoda kirjoitusvirhe takaisin korjauksen jälkeen. Suojaa sitten socket, joka kuljettaa liikennettä itse VPN-palvelimelle. Ilman protectia ne paketit reititetään takaisin omaan tunneliisi eikä yhteys koskaan valmistu, mikä näyttää jumittuneelta luvulta mutta on reitityssilmukka.

Suostumusta ei voi ohittaa. Palvelu, joka käynnistetään ilman että prepare palauttaa null, ei saa rajapintaa, ja establish palauttaa null heittämisen sijaan, joten tarkista kuvaus ennen silmukan käynnistämistä. VpnService-viite on paikka vahvistaa builder-sopimus sen sijaan, että kopioisit esimerkin.

Vahvista yksi suunta kerrallaan

Lähetä UDP-kaiku tunnelin läpi ja seuraa pakettien pituuksia ennen kuin kosket TCP:hen. Avaa sitten kättely ja vahvista, että segmentit pysyvät laskemasi MSS:n alla. Laske paketteja älä yhteyksiä, jotta jumitus näkyy laskurin aukkona eikä jumittuneena socketina. Jos luvut pysähtyvät kirjoitusten ryöpyn jälkeen, ikkuna on epäilty; jos kirjoitukset epäonnistuvat heti, se on pituus.

Käynnistä sitten laite uudelleen ja toista kerran, koska VpnService, joka toimii vain ensimmäiseen verkkomuutokseen asti, on reititysongelma eikä vuonhallintaongelma.

Aiheeseen liittyvät artikkelit