Android VpnService: TUN-i lugemise tõrge versus ENOBUFS-i kirjutamisvead
Lugemine, mis ei naase kunagi, ja kirjutamine, mis ebaõnnestub ENOBUFS-iga, on vastandlike põhjustega. Mõõtke väljakuulutatud aken ja paketi pikkus, et valida parandus.
Samas VpnService-tsüklis ilmneb kaks sümptomit ja süüdistatakse sama asja: lugeja, mis ei naase kunagi, ja kirjutamine, mis ebaõnnestub ENOBUFS-iga. Need ei ole ühe vea variatsioonid. Takerdunud lugemine on vastuolu voo juhtimises partneriga; ebaõnnestunud kirjutamine on pakett, mida liides ei võta vastu. Kui käsitleda neid ühe probleemina, kulub päev puhvrite häälestamisele, mis ei olnud kunagi asjasse kaasatud. Alljärgnev jaotus on see, kuidas ma otsustan, kumba tsükli poolt esimesena mõõta.
Üks tsükkel, kaks sõltumatut tõrkepunkti
Teie teenus loeb TUN-i failideskriptorist IP-paketi, uurib seda ja kirjutab vastuse samasse deskriptorisse. Kõik, mis sellel teel katki läheb, ilmneb ühes neist kahest kutsest, mistõttu teatatakse mõlemast kui katkisest tunnelist. Kutsetel on erinevad lepingud. Lugemine ootab, et kernel annaks teile paketi, ja kirjutamine annab kernelile paketi, mille see võib tagasi lükata.
Lugemised on see lihtsam pool, mille üle mõelda. Lugemine, mis ei tagasta midagi, ootab kas liiklust, mis kunagi ei saabu, või ootab seetõttu, et partner lõpetas saatmise.
Takerdunud lugemine on vastuolu voo juhtimises
TUN-seadme lugemine blokeerib, kuni pakett on saadaval. See on kavandatud käitumine ja seetõttu näeb read-i sees pargitud lõim tühjal lingil normaalsena. Sellest saab hangumine, kui teie enda TCP-pinu jätkab kohalikult rakenduselt andmete vastuvõtmist, kuid ei kuuluta kunagi piisavat akent, et kaugpartner saaks rohkem saata. Partner täidab teie kuulutatud akna, ootab ACK-i ja akna värskendust ning peatub. Siis ei ole teie lugejal midagi tagastada ja rakendus näeb avatud ning vaikivat ühendust.
Levinud põhjused on pinu, mis kirjutab soklisse kuulutatud akent jälgimata, ja teine, mis unustab pärast puhvri tühjendamist akna värskenduse saata. Väljast näevad need identsed välja: baidid lähevad välja, midagi ei tule tagasi, viga ei tõsteta. Lugeja lõime blokeerimine parsimise, DNS-i lahendamise või kettale kirjutamise tõttu tekitab sama kujuga sümptomi mitteseotud põhjusel, seega kinnitage, et lõim on tõesti read-i sees, enne kui süüdistate voo juhtimist.
Kontrollige partneri saatmisjärjekorda ja akent, mida see viimati nägi. Kui partner hoiab andmeid, sest teie aken on null, on parandus teie ACK-i teel, mitte TUN-i lugemises.
ENOBUFS on suuruse probleem, mitte puhvri puudus
TUN-liidesel ENOBUFS-i tagastav kirjutamine lükkab tavaliselt tagasi paketi pikkuse. Liidesel on MTU, sellest suuremat paketti ei saa järjekorda panna ja sama paketi kordamine ebaõnnestub iga kord ühtemoodi. See determinism on vihje. Tõeline puhvri puudus kaob rõhu langedes, suuruse tagasilükkamine aga ei hooli, kui kaua ootate.
TCP-l on just selleks mehhanism ja see on samm, mille inimesed vahele jätavad: MSS-i piiramine. Kui te kasutajaruumis segmente sünteesite, valite kasuliku koormuse suuruse, ja selle võtmine kohaliku liidese MTU-st tunnel MTU asemel väljastab segmente, mida tunnel kanda ei suuda. Piirake kahest väiksemale. Piirang katab ainult TCP-d, seega liiga suur UDP-datagramm tuleb teie koodil fragmenteerida või kõrvale jätta.
Logige iga paketi pikkust, mille kirjutate, samal ajal silumisel. Pikkus, mis ebaõnnestub, on vastus ja see ei muutu katsete vahel.
Kolm mõõtmist, mis juhtumid eraldavad
Te ei vaja kummagi tõrke jaoks silurit. Kolmest vaatlusest piisab, et end õigesse jaotisse suunata, ja need võtavad vähem aega kui ümberehitus.
| What you see | Where to look |
|---|---|
| Reader parked, peer send queue empty, last advertised window zero | Window updates in your ACK path |
| Write fails on one length and succeeds on a smaller one | MSS clamp and tunnel MTU |
| Both directions look fine, app still sees no data | Protected socket and routing loop |
Kui kolmas rida klapib, on tsükkel ise korras ja probleem on seal, kus tunnel seda näha ei saa: kaitsmata transpordisokkel, mis viib teie enda VPN-liikluse tunnelisse tagasi, mis tavaliselt ilmneb ühendusena, mis õnnestub ja siis ei too midagi kohale.
Parandage torustik, millest mõlemad teed sõltuvad
Ükski sümptom ei ole jälitamist väärt enne, kui liides on korralikult ehitatud. Olulised builder-kutsed on aadress, marsruut, MTU ja nõusolek, selles järjekorras:
Builder()
.addAddress(10.8.0.2, 32)
.addRoute(0.0.0.0, 0)
.setMtu(1400)
.establish()Seadke MTU tunnelile tegelikule väärtusele, selle asemel et jätta see selleks, mida näidis kasutas. Kõik, mida hiljem kirjutate, on selle arvuga piiratud, ja teie seatud MTU ning piiramise MSS-i lahknevus on kõige tavalisem viis kirjutustõrke pärast parandust uuesti sisse tuua. Seejärel kaitske soklit, mis kannab liiklust VPN-serverini. Ilma protect-ita suunatakse need paketid tagasi teie enda tunnelisse ja ühendus ei lõpe kunagi, mis näeb välja nagu takerdunud lugemine, kuid on marsruutimistsükkel.
Nõusolekut ei saa mööda minna. Teenus, mis käivitati ilma, et prepare tagastaks null, ei saa liidest, ja establish tagastab null, selle asemel et visata, seega kontrollige deskriptorit enne tsükli käivitamist. VpnService-i viide on koht, kus builder-lepingut kinnitada, selle asemel et näidist kopeerida.
Kontrollige ühte suunda korraga
Saatke tunnelist läbi UDP-kaja ja jälgige pakettide pikkusi enne, kui TCP-d puutute. Seejärel avage kätlus ja kinnitage, et segmendid jäävad teie arvutatud MSS-i alla. Loendage pakette, mitte ühendusi, et tõrge ilmneks loenduri lüngana, mitte hangunud soklina. Kui lugemised lakkavad pärast kirjutuste purset, on kahtlusalune aken; kui kirjutused ebaõnnestuvad kohe, siis pikkus.
Seejärel taaskäivitage seade ja korrake üks kord, sest VpnService, mis töötab ainult kuni esimese võrgu muutuseni, on marsruutimisprobleem, mitte voo juhtimise oma.