Hvorfor Android VpnService opfanger noget trafik og ikke alt
Ruter, DNS-poster og omfanget pr. app afgør, hvilke pakker der når tun-enheden, hvorfor lokale enheder forsvinder, og hvad byggeren afviser.
Intet i VpnService opsnapper trafik af sig selv. Det beder systemet om en tun-grænseflade, giver den grænseflade et sæt adresser og ruter, og fra det øjeblik kopierer kernen hver pakke, der matcher disse ruter, til en fildeskriptor, som appen kan læse. Appens kode ser aldrig de pakker, som ruterne ikke valgte, så omfanget af en tunnel er et spørgsmål om routingtabellen, før det er et programmeringsspørgsmål. Derfor kan den samme serviceimplementering sluge hver strøm på én telefon og kun en bid på en anden, og derfor reparerer en ekstra rute nogle gange en tunnel, der så brudt ud. Det, du får, er det, du erklærede, minus alt hvad byggeren stiltiende afviste.
Ruter afgør, hvilke pakker der ankommer
Builder.addRoute(prefix, prefixLength) installerer et præfiks i VPN-netværkets routingtabel. Systemet behandler det netværk som standard for appene i omfanget, så dets poster slås op før det fysiske netværks standardrute, og et matchende præfiks er nok til at omdirigere en strøm. Derfor tager en rute til 0.0.0.0/0 IPv4 fuldt ud, inklusive printeren, cast-målet og routerens admin-side.
Klienter, der vil have den dækning uden at slette den fysiske standardpost, bruger i stedet parret 0.0.0.0/1 og 128.0.0.0/1. To mere specifikke ruter slår en standardrute, sammen spænder de over hele adresseområdet, og telefonens egen standardpost bliver i tabellen for de sockets, der stadig er bundet til det fysiske netværk.
Adresser og DNS-poster kommer fra den samme bygger og besvarer et andet spørgsmål. addAddress giver grænsefladen en adresse med en præfikslængde, og addDnsServer installerer en resolver, som apper i omfanget vil spørge gennem grænsefladen. Begge familier kan ligge på én grænseflade, så et rent IPv4-rutesæt ved siden af en IPv6-adresse er en konfiguration, der læses korrekt og stadig lækker. Hvis du router en familie, hvor grænsefladen ikke har en adresse, har kernen ingen kildeadresse til de pakker, og de vises aldrig ved deskriptoren.
Hvad establish() giver tilbage
establish() returnerer en ParcelFileDescriptor, eller null når appen ikke er autoriseret. Null er en samtykketilstand og ikke en netværksfejl: VpnService.prepare() skal allerede have returneret null, eller brugeren skal have godkendt systemdialogen før kaldet. Samtykke kan trækkes tilbage, mens tjenesten kører, og grænsefladen lukker under dig uden et callback ind i din læseløkke.
Deskriptoren er ikke-blokerende, medmindre du siger andet, så en læsning vender straks tilbage uden data i stedet for at parkere tråden. En løkke skrevet til en blokerende socket brænder en kerne, og en løkke, der behandler en tom læsning som fatal, river tunnelen ned i et ledigt sekund. Builder.setBlocking(true) ændrer den adfærd, og prisen er en tråd, der venter inde i læsningen.
Rute- og adresseargumenter valideres som LinkAddress-værdier, og multicast-destinationer som 224.0.0.0/4 afvises blankt. En punkt-til-punkt tun-grænseflade har heller ikke et broadcast-domæne, så multicast-opdagelse over den kræver et andet design i stedet for endnu en rute. VpnService.Builder-reference er listen, du skal tjekke, før du konkluderer, at et kald blev ignoreret.
App-omfanget indsnævrer sættet, ikke tunnelen
Fra API 21 accepterer byggeren addAllowedApplication og addDisallowedApplication, og de to lister udelukker hinanden inden for én bygger. En tilladelsesliste lader tunnelen findes for de pakker, mens alt andet beholder det fysiske netværk; en blokeringsliste fanger alt undtagen de navngivne pakker, hvilket er hvad en klient tilbyder som delt tunnelling. Før API 21 findes ingen platformsmekanisme overhovedet, og adfærd for hver app kom fra tredjepartsklienter, der filtrerede efter UID inde i deres egen videresender. Forskellen betyder noget, når du fejlretter, fordi en app-tunnel med en tilladelsesliste på én pakke næsten ikke giver trafik, og det er korrekt adfærd, ikke en brudt rute.
Uanset omfanget må den socket, din app åbner til VPN-serveren, ikke rejse gennem den tunnel, den bygger. VpnService.protect(socket) binder den socket til det underliggende netværk, så dens pakker springer tun-enheden over. Springer du kaldet over, bliver håndtrykket slugt af den grænseflade, som håndtrykket skulle rejse.
VpnService.setUnderlyingNetworks lukker løkken ved at fortælle systemet, hvilket fysisk netværk der bærer tunnelen. Den værdi styrer, hvordan VPN rapporterer forbindelse til andre apper, og en null- eller forældet værdi er en rutinemæssig årsag til en tilsluttet tunnel, som resten af telefonen behandler som offline. Opdater den, når det underliggende netværk ændrer sig, ikke kun ved opstart.
Kontrolsummer og MTU: hvor en korrekt rute alligevel fejler
Når pakkerne ankommer, afgør to detaljer på pakkeniveau, om noget virker ende til ende. Den første er kildeadressen: med flere grænsefladeadresser kan kernen sende pakker, der bærer den første, så en videresender, der omskriver headere, skal rette adressen og den kontrolsum, der dækker den.
Den anden er kontrolsummens omfang. Når appen selv bygger TCP- eller UDP-pakker, dækker kontrolsummen en pseudo-header med kildeadresse, destinationsadresse, protokollnummer og transportlængde, og at udelade en af dem giver pakker, der ankommer og kasseres. Den fejl ser ud som en timeout efter et svar, der aldrig kommer, hvilket sender de fleste tilbage til ruterne. setMtu fastsætter grænsefladens MTU, og hver læsning returnerer præcis én komplet IP-pakke, så en buffer skrevet til en bytestrøm fragmenterer din parsing i stedet for pakken.
En verifikationsrunde, der adskiller lagene
For at finde, hvor omfanget gik galt, test lagene i rækkefølge. Spørg enheden, hvilke ruter VPN-netværket faktisk holder, og tjek derefter, om en strøm til en kendt adresse dukker op som en læsning på deskriptoren, og tjek derefter, om den pakke, din videresender sender, er velformet. Et ruteproblem viser intet ved deskriptoren, mens et kontrolsumproblem viser en pakke, der ankommer og forsvinder.
Hvis intet når deskriptoren, er rutesættet den mistænkte snarere end læseren: bekræft præfikserne, bekræft at der er en adresse i samme familie, og bekræft at appen er inden for det omfang, du satte. Hvis pakker når deskriptoren, men intet kommer tilbage, undersøg protect() og værdien for det underliggende netværk, før du rører en rute igen. Hvis den fjerne side rapporterer fejlbehæftede pakker, er kontrolsummen og MTUen stedet at kigge. Én sondering for hvert lag sparer en eftermiddag med gætteri.