Split tunneling i en VpnService-app bryder sammen på nogle få konkrete steder, og reparationen er forskellig for hvert. Klienten viser kun én destination, der fejler, så den første opgave er at finde ud af, hvilken grænse du rammer: de ruter, builderen fik, den proxy du ikke kan godkende, den resolver du kun annoncerede, eller den app-liste platformen anvender pr. bruger. Netop derfor holder jeg en kontrol i fire trin. Den kører i rækkefølgen nedenfor og stopper ved den første fejl.

Bekræft, hvad builderen gav platformen

VpnService.Builder er en deklarationsliste. establish() returnerer en ParcelFileDescriptor til et virtuelt interface, og platformen sender kun trafik, der matcher præfikserne fra addAddress og addRoute, ind i den descriptor; addDnsServer fortæller klienterne, hvilken resolver de skal spørge. En destination uden for alle de præfikser, du tilføjede, når aldrig din app, uanset hvad interfacet lover. Den ene kendsgerning forklarer de fleste rapporter om brudt split tunneling, før nogen kode ændres.

Log de adresse-, rute- og DNS-lister, din builder samlede, og sammenlign dem med de destinationer, der fejler. Et manglende /32 til en administrationshost er langt mere almindeligt end en brudt tunnel. Er listen rigtig, så gå videre; er den forkert, er resten af tjeklisten spildt arbejde.

excludeRoute findes først fra API 33

Destinationsbaseret udelukkelse er den anmodning, der oftest bliver en supporttråd. builder.excludeRoute(IpPrefix) kom i API 33; før det er den eneste understøttede tilnærmelse at tilføje ruter for alt undtagen det udelukkede præfiks, hvilket betyder, at du selv skal beregne de komplementære præfikser. De to kald er ikke spejlbilleder: det ene trækker fra interfacets rutesæt, det andet bygger sættet fra bunden, og en ældre enhed vil kaste eller stiltiende ignorere et kald, den ikke kender. Forgren på Build.VERSION.SDK_INT, og test begge veje på rigtigt hardware.

Når du beregner komplementer, så hold øje med præfiksjustering. En /24-udelukkelse inde i en /16-rute kræver de dækkende præfikser, der faktisk beskriver resten, ikke et enkelt forkert /8. Log den endelige liste, og bekræft at den udelukkede host stadig opløses ad en vej uden for tunnelen.

Proxy-legitimationsoplysninger kan ikke udtrykkes

setHttpProxy(ProxyInfo) peger interfacet mod en HTTP- eller SOCKS-proxy, og en PAC-URL kan vælge mellem dem pr. destination. Hverken ProxyInfo eller PAC-formatet bærer brugernavn og adgangskode, så en godkendelseskrævende proxy ligger uden for platformens API. Apps, der har brug for en, indlejrer normalt en userspace-stak, tun2socks eller en lignende motor og svarer selv på 407-udfordringen.

Det valg har konsekvenser. En userspace-stak ser kun den trafik, du ruter til den, så den skal leve inden for samme descriptorgrænse, og den arver samme protect()-krav for sin egen uplink-socket. Spring protect() over, og tunnelen fanger sin egen transport, hvilket viser sig som et handshake, der går i stå efter den første pakke. Test mod en proxy, der kræver legitimationsoplysninger, for en åben proxy består på platformens vej og beviser intet.

App-lister og DNS er også deklarationer

Builder har både addAllowedApplication og addDisallowedApplication, og de udelukker hinanden: at kalde begge kaster en fejl. Under alle omstændigheder anvendes listen pr. bruger ved interfaceopsætning, så en app i arbejdsprofilen styres af arbejdsprofilens tunnel og ikke din. Det, som ingen API giver dig, er kontrol over en anden apps beslutning: du kan ikke tvinge en pakke ind i din tunnel udefra, og du kan ikke lukke eller omkonfigurere en VPN, der allerede er aktiv.

DNS følger samme mønster. addDnsServer annoncerer en resolveradresse; det lader ikke din app besvare forespørgsler. Vil du selv opløse navne, så kør en resolver og rut dens adresse ind i tunnelen. Apps, der bruger DoT eller DoH mod et fast hostnavn, kan omgå den adresse, du annoncerede, så behandl navneopløsning som bedste forsøg, medmindre du også opfanger forespørgslens vej.

Manifest, always-on og firetrinstesten

Berettigelse deklareres i manifestet: BIND_VPN_SERVICE, intent-filteret android.net.VpnService og en foreground-tjeneste, hvis tunnelen skal overleve i baggrunden. Always-on VPN kræver desuden en ren onRevoke, fordi systemet kalder den, når brugeren ændrer indstillinger, eller en anden app overtager kontrollen.

Kør kontrollerne i rækkefølge, og stop ved den første fejl. Først: dump de adresse-, rute- og DNS-lister, der blev givet til builderen. Dernæst: bekræft, at establish() returnerede en descriptor, og at transportsocket er beskyttet. Tredje: anmod om én destination inde i et inkluderet præfiks og én inde i et udelukket præfiks. Fjerde: undersøg, hvilken resolver der faktisk svarede, ved at spørge på et hostnavn, som kun din resolver kan opløse.

Et fuldført handshake beviser ingen af de senere trin. Hver kontrol isolerer ét lag, så et gennemløb ved trin to siger intet om trin fire.

VpnService.Builder-referencen oplister hele builderens overflade. Alt, du har brug for ud over det, er en anden API, en anden app eller din egen userspace-stak, og det er en designbeslutning snarere end en split-tunnel-fejl.

Relaterede artikler