Zakaj vaš tunel Android VpnService preneha usmerjati promet
Štiri prepričanja o VpnService, ki v produkciji pokajo: en tunel naenkrat, obseg poti, seznami po aplikacijah in vrstni red izklopa, s preverjanjem za vsako.
Štiri predpostavke o Android VpnService so me enkrat stale večino tedna in nobena ni bila o omrežnem protokolu. Aplikacija je javljala povezano, ikona ključa je sedela v vrstici stanja, promet pa je še vedno odhajal po običajni poti. Še naprej sem predpostavljal, da je tunel moj, dokler ga ne ustavim, da so poti, ki jih dodam, poti, ki jih dobim, in da bo klic stopSelf odnesel vmesnik s seboj. Storitev se je obnašala točno tako, kot je dokumentirano. Pokvarjen je bil moj miselni model.
Mit 1: tunel stoji, dokler ga ne ustavite
Android drži en vmesnik VPN naenkrat. Vzpostavite drugega in platforma ga ne postavi v čakalno vrsto: deaktivira vašega in v vaši storitvi pokliče onRevoke(). Če vaša koda vse razgradi le ob pritisku gumba, prevzem za seboj pusti deskriptor in bralno nit, naslednji zagon pa podeduje oboje.
Referenca VpnService izrecno pravi, da prepare() vrne null, ko ima aplikacija že soglasje, in Intent, ko ga mora uporabnik še odobriti. To soglasje prispe le, če namero dostavite prek startActivityForResult(). Preskočite predajo in establish() vrne null, kar se bere kot tiha napaka in ne kot težava z dovoljenji.
Always-on VPN doda lastnika, ki ga ne nadzorujete. Sistem lahko tunel vrne po zagonu ali po sesutju in prehiti aplikacijo VPN, ki jo je uporabnik zagnal ročno. Gradite onRevoke() tako, da zapre iste vire kot vaš običajni izklop, in ga obravnavajte kot glavno pot zaustavitve, ne kot robni primer. Uporabniki menjajo aplikacije VPN; vaša koda mora to preživeti.
Mit 2: addRoute ujame natanko to, kar ste našteli
Poti odločajo, kateri paketi dosežejo vaš deskriptor, in napaka v obsegu ustvari nor primer, ko gre del prometa skozi tunel, del pa ne. addRoute("0.0.0.0", 0) vzame ves IPv4, kar pomeni, da vsak paket zdaj pride k vam in ga je treba poslati naprej ali zavreči. /32 za enega samega gostitelja ne vzame skoraj ničesar, vse drugo pa se obnaša, kot da vaš VPN nikoli ni bil nameščen.
Družine se štejejo ločeno. Pot samo za IPv4 pusti IPv6 na osnovnem omrežju, zato lahko gostitelj z dvema skladoma še vedno razrešuje in se povezuje zunaj tunela, medtem ko je znotraj videti popolnoma povezan.
DNS sledi isti logiki. Strežniki, dodani z addDnsServer(), gredo aplikacijam, ki uporabljajo razreševalnik tunela, medtem ko jih aplikacija z lastno šifrirano končno točko DNS ali s Private DNS, nastavljenim na ime gostitelja, popolnoma ignorira. Ko imena odpovedo pri eni aplikaciji in delujejo pri drugi, preverite pot razreševalnika, preden se dotaknete ene same poti.
Mit 3: pravila po aplikacijah so stikalo, ki ga lahko obrnete na katero koli stran
addDisallowedApplication() in addAllowedApplication() sta dva načina, ne dva filtra. Graditelj sprejme en model na tunel, zato aplikacija, ki hoče vse razen treh paketov, uporabi prepovedani seznam in pusti dovoljeni seznam prazen. Ta dva se medsebojno izključujeta, ljudje pa to odkrijejo med izvajanjem, potem ko je zaslon z nastavitvami že ponudil obe stikali.
Vidnost paketov tudi ugrizne. Na Androidu 11 in novejšem lahko poimenujete le pakete, ki jih vidite, zato bo seznam izjem, zgrajen iz poizvedbe, ki je vaša aplikacija ne sme izvesti, tiho izgubil vnose. Primerjajte seznam s tem, kar upravitelj paketov v resnici vrne, preden mu zaupate.
Delovni profili znova delijo občinstvo. Tunel, zagnan znotraj profila, pokriva ta profil in njegove aplikacije, medtem ko osebna stran ohrani svojo povezljivost. To je odločitev o obsegu, sprejeta enkrat, in spada v vmesnik, ne v zahtevek za podporo.
Mit 4: stopSelf konča tunel
Ne konča ga. Vmesnik živi v deskriptorju, ki ga vrne establish(), in objekt storitve lahko umre, medtem ko deskriptor ostane odprt. Najprej zaprite ParcelFileDescriptor, spustite referenco, pridružite se bralni in pisalni niti ali ju prekinite, nato pokličite stopForeground(true) in stopSelf() v tem vrstnem redu.
Pozabljen deskriptor je običajen razlog, da je videti, kot da storitev preživi lastno zahtevo za zaustavitev, in pojasnjuje zastarelo pot, ki živi dlje od aplikacije, ki jo je dodala.
Nasprotna napaka je enako pogosta: zapreti deskriptor, medtem ko bralna nit bere, in nato to nit pustiti, da se spopade z izjemo, za katero ni nihče napisal obravnave. Držite zaporedje izklopa na enem mestu in pustite, da vanj kličeta tako uporabnikovo dejanje kot onRevoke().
Kaj preveriti, ko je tunel videti mrtev
Zaščitite dolgožive vtičnice z protect(), preden se tunel dvigne. Brez tega se lastni promet tunela usmeri nazaj v tunel in povezava se ustavi tako, da je videti natanko kot okvara strežnika. Testirajte proti naslovu, ki obstaja le za oddaljenim omrežjem, ne proti javnemu spletnemu mestu, ki odgovarja iz predpomnilnika ali s podlage poti.
Zabeležite rezultat establish(), poti, izključene pakete in razlog izklopa. Te štiri vrstice ločijo težavo s soglasjem od težave z obsegom in od težave z življenjskim ciklom brez zajema paketov.