Hvorfor Android VpnService fanger noe trafikk og ikke alt
Ruter, DNS-oppføringer og omfanget per app avgjør hvilke pakker som når tun-enheten, hvorfor lokale enheter forsvinner og hva byggeren nekter.
Ingenting i VpnService fanger trafikk av seg selv. Det ber systemet om et tun-grensesnitt, gir det grensesnittet et sett adresser og ruter, og fra det øyeblikket kopierer kjernen hver pakke som samsvarer med disse rutene, inn i en fildeskriptor appen kan lese. Appens kode ser aldri pakkene rutene ikke valgte, så omfanget av en tunnel er et spørsmål om rutetabellen før det er et programmeringsspørsmål. Derfor kan samme tjenesteimplementasjon sluke hver strøm på én telefon og bare en del på en annen, og derfor reparerer en ekstra rute noen ganger en tunnel som så ødelagt ut. Det du får er det du erklærte, minus alt byggeren stille avviste.
Ruter avgjør hvilke pakker som kommer
Builder.addRoute(prefix, prefixLength) installerer et prefiks i rutetabellen til VPN-nettverket. Systemet behandler det nettverket som standard for appene i omfanget, så oppføringene leses før standardruten til det fysiske nettverket, og et samsvarende prefiks er nok til å avlede en strøm. Derfor tar en rute for 0.0.0.0/0 IPv4 fullt ut, inkludert skriveren, cast-målet og admin-siden til ruteren.
Klienter som vil ha den dekningen uten å slette den fysiske standardoppføringen, bruker i stedet paret 0.0.0.0/1 og 128.0.0.0/1. To mer spesifikke ruter slår en standardrute, sammen dekker de hele adresserommet, og telefonens egen standardoppføring blir i tabellen for socketene som fortsatt er bundet til det fysiske nettverket.
Adresser og DNS-oppføringer kommer fra samme bygger og svarer på et annet spørsmål. addAddress gir grensesnittet en adresse med prefikslengde, og addDnsServer installerer en resolver som appene i omfanget vil spørre gjennom grensesnittet. Begge familiene kan sitte på ett grensesnitt, så et rent IPv4-rutesett ved siden av en IPv6-adresse er en konfigurasjon som leses riktig og likevel lekker. Hvis du ruter en familie der grensesnittet ikke har adresse, har kjernen ingen kildeadresse for de pakkene, og de vises aldri ved deskriptoren.
Hva establish() gir tilbake
establish() returnerer en ParcelFileDescriptor, eller null når appen ikke er autorisert. Null er en samtykketilstand, ikke en nettverksfeil: VpnService.prepare() må allerede ha returnert null, eller brukeren må ha godkjent systemdialogen før kallet. Samtykket kan trekkes tilbake mens tjenesten kjører, og grensesnittet lukkes under føttene dine uten noe tilbakekall i leseløkken.
Deskriptoren er ikke-blokkerende med mindre du sier noe annet, så en lesing kommer straks tilbake uten data i stedet for å parkere tråden. En løkke skrevet for en blokkerende socket brenner en kjerne, og en løkke som behandler en tom lesing som fatal, river tunnelen ned i et ledig sekund. Builder.setBlocking(true) endrer denne oppførselen, og prisen er en tråd som venter inne i lesingen.
Rute- og adresseargumenter valideres som LinkAddress-verdier, og multicast-mål som 224.0.0.0/4 avvises blankt. Et punkt-til-punkt tun-grensesnitt har heller ikke et broadcast-domene, så multicast-oppdagelse over det krever et annet design i stedet for enda en rute. Referansen VpnService.Builder er listen du bør sjekke før du konkluderer med at et kall ble ignorert.
Omfanget per app snevrer inn settet, ikke tunnelen
Fra API 21 godtar byggeren addAllowedApplication og addDisallowedApplication, og de to listene utelukker hverandre innenfor én bygger. En tillatelsesliste gjør tunnelen eksisterende for de pakkene, mens alt annet beholder det fysiske nettverket; en blokkeringsliste fanger alt unntatt de navngitte pakkene, som er det en klient tilbyr som delt tunnellering. Før API 21 finnes ingen plattformmekanisme i det hele tatt, og oppførsel per app kom fra tredjepartsklienter som filtrerte på UID i sin egen videresender. Forskjellen betyr noe når du feilsøker, fordi en app-tunnel med en tillatelsesliste på én pakke nesten ikke gir trafikk, og det er korrekt oppførsel, ikke en ødelagt rute.
Uansett omfang må socketen appen din åpner mot VPN-serveren, ikke reise gjennom tunnelen den bygger. VpnService.protect(socket) binder den socketen til det underliggende nettverket slik at pakkene hopper over tun-enheten. Hvis du hopper over kallet, blir håndtrykket slukt av grensesnittet som skulle heve det.
VpnService.setUnderlyingNetworks lukker løkken ved å fortelle systemet hvilket fysisk nettverk som bærer tunnelen. Den verdien avgjør hvordan VPN rapporterer tilkobling til andre apper, og en null- eller utdatert verdi er en vanlig årsak til en tilkoblet tunnel som resten av telefonen behandler som frakoblet. Oppdater den når det underliggende nettverket endres, ikke bare ved oppstart.
Kontrollsummer og MTU: der en korrekt rute likevel feiler
Når pakkene først kommer, avgjør to detaljer på pakkenivå om noe fungerer ende til ende. Den første er kildeadressen: med flere grensesnittadresser kan kjernen sende pakker som bærer den første, så en videresender som skriver om hoder, må rette adressen og kontrollsummen som dekker den.
Den andre er kontrollsummens omfang. Når appen bygger TCP- eller UDP-pakker selv, dekker kontrollsummen en pseudo-header med kildeadresse, måladdresse, protokollnummer og transportlengde, og å utelate noen av dem gir pakker som kommer og blir forkastet. Den feilen ser ut som en tidsavbrudd etter et svar som aldri kommer, noe som sender de fleste tilbake til rutene. setMtu fastsetter grensesnittets MTU, og hver lesing returnerer nøyaktig én komplett IP-pakke, så en buffer skrevet for en bytestrøm vil fragmentere parsingen din, ikke pakken.
En verifiseringsrunde som skiller lagene
For å finne hvor omfanget gikk galt, test lagene i rekkefølge. Spør enheten hvilke ruter VPN-nettverket faktisk holder, sjekk deretter om en strøm til en kjent adresse dukker opp som en lesing på deskriptoren, og sjekk deretter om pakken videresenderen din sender ut, er velformet. Et ruteproblem viser ingenting ved deskriptoren, mens et kontrollsumproblem viser en pakke som kommer og forsvinner.
Hvis ingenting når deskriptoren, er det rutesettet som er mistenkt og ikke leseren: bekreft prefiksene, bekreft at det finnes en adresse i samme familie, og bekreft at appen er innenfor omfanget du satte. Hvis pakkene når deskriptoren, men ingenting kommer tilbake, undersøk protect() og verdien for det underliggende nettverket før du rører en rute igjen. Hvis den fjerne siden rapporterer misdannede pakker, er kontrollsummen og MTUen stedet å se. Én sondering per lag sparer en ettermiddag med gjetting.