Padalintas tuneliavimas Android VpnService: ką iš tikrųjų galima maršrutizuoti
VpnService perduoda tik tuos paketus, kuriuos builder įdeda į savo deskriptorių. Patikrinkite prefiksus, proxy autentifikavimą, DNS ir programų sąrašus prieš kaltindami padalintą tuneliavimą.
Padalintas tuneliavimas VpnService programoje lūžta keliose konkrečiose vietose, ir kiekvienos taisymas skirtingas. Klientas rodo tik vieną nepavykusią paskirties vietą, todėl pirmas darbas yra išsiaiškinti, į kurią ribą atsimušate: builder gauti maršrutai, proxy, kurio negalite autentifikuoti, resolver, kurį tik paskelbėte, ar programų sąrašas, kurį platforma taiko kiekvienam naudotojui. Būtent todėl laikau keturių žingsnių patikrą. Ji eina toliau nurodyta tvarka ir sustoja ties pirmąja klaida.
Patvirtinkite, ką builder perdavė platformai
VpnService.Builder yra deklaracijų sąrašas. establish() grąžina ParcelFileDescriptor virtualiajam sąsajai, o platforma į tą deskriptorių siunčia tik srautą, atitinkantį addAddress ir addRoute prefiksus; addDnsServer nurodo klientams, kurio resolver klausti. Paskirties vieta, esanti už visų jūsų pridėtų prefiksų, niekada nepasiekia jūsų programos, kad ir ką žadėtų sąsaja. Šis vienas faktas paaiškina daugumą pranešimų apie sulaužytą padalintą tuneliavimą dar prieš keičiant kodą.
Užregistruokite builder surinktus adresų, maršrutų ir DNS sąrašus, tada palyginkite juos su nepavykstančiomis paskirties vietomis. Trūkstamas /32 valdymo mazgui yra daug dažnesnis nei sulaužytas tunelis. Jei sąrašas teisingas, eikite toliau; jei ne, likusi kontrolinio sąrašo dalis yra bergždžias darbas.
excludeRoute egzistuoja tik nuo API 33
Išskyrimas pagal paskirties vietą yra prašymas, kuris dažniausiai tampa palaikymo tema. builder.excludeRoute(IpPrefix) atsirado API 33; prieš tai vienintelis palaikomas sprendimas yra pridėti maršrutus viskam, išskyrus išskirtą prefiksą, o tai reiškia, kad papildomus prefiksus turite apskaičiuoti patys. Abu kvietimai nėra veidrodiniai: vienas atima iš sąsajos maršrutų rinkinio, kitas kuria rinkinį nuo nulio, o senesnis įrenginys išmes arba tyliai ignoruos jam nepažįstamą kvietimą. Šakokite pagal Build.VERSION.SDK_INT ir abu kelius testuokite tikroje aparatinėje įrangoje.
Skaičiuodami papildinius, atkreipkite dėmesį į prefiksų lygiavimą. /24 išskyrimas /16 maršruto viduje reikalauja dengiančių prefiksų, kurie tikrai apibūdina likusią dalį, o ne vieno netaisyklingo /8. Užregistruokite galutinį sąrašą ir patikrinkite, kad išskirtas mazgas vis dar sprendžiamas keliu už tunelio.
Proxy kredencialai negali būti išreikšti
setHttpProxy(ProxyInfo) nukreipia sąsają į HTTP arba SOCKS proxy, o PAC URL gali rinktis tarp jų kiekvienai paskirties vietai. Nei ProxyInfo, nei PAC formatas neperduoda naudotojo vardo ir slaptažodžio, todėl autentifikaciją reikalaujantis proxy lieka už platformos API. Programos, kurioms to reikia, paprastai įterpia naudotojo erdvės steką, tun2socks ar panašų variklį ir pačios atsako į 407 iššūkį.
Šis pasirinkimas turi pasekmių. Naudotojo erdvės stekas mato tik srautą, kurį jam nukreipiate, todėl jis turi gyventi tose pačiose deskriptoriaus ribose ir paveldi tą patį protect() reikalavimą savo uplink lizdui. Praleiskite protect() ir tunelis pagaus savo paties transportą, kas pasirodo kaip rankos paspaudimas, sustojantis po pirmo paketo. Testuokite su proxy, kuris reikalauja kredencialų, nes atviras proxy praeina platformos keliu ir nieko neįrodo.
Programų sąrašai ir DNS taip pat yra deklaracijos
Builder turi ir addAllowedApplication, ir addDisallowedApplication, ir jie vienas kitą išskiria: kviečiant abu metama išimtis. Bet kuriuo atveju sąrašas taikomas kiekvienam naudotojui nustatant sąsają, todėl darbo profilio programa valdoma darbo profilio tunelio, o ne jūsų. Ko neduoda jokia API, tai kontrolės kito programos sprendimui: negalite iš išorės įstumti paketo į savo tunelį ir negalite uždaryti ar perkonfigūruoti jau aktyvaus VPN.
DNS laikosi to paties modelio. addDnsServer paskelbia resolver adresą; jis neleidžia jūsų programai atsakyti į užklausas. Jei norite patys spręsti vardus, paleiskite resolver ir nukreipkite jo adresą į tunelį. Programos, naudojančios DoT arba DoH fiksuotam mazgo vardui, gali apeiti paskelbtą adresą, todėl vardų sprendimą laikykite geriausiomis pastangomis, nebent perimtumėte ir užklausos kelią.
Manifestas, always-on ir keturių žingsnių testas
Tinkamumas deklaruojamas manifeste: BIND_VPN_SERVICE, android.net.VpnService intent filtras ir foreground paslauga, jei tunelis turi išgyventi fone. Always-on VPN papildomai reikalauja švaraus onRevoke, nes sistema jį kviečia, kai naudotojas keičia nustatymus arba kita programa perima kontrolę.
Atlikite patikras iš eilės ir sustokite ties pirmąja klaida. Pirma, išveskite builder perduotus adresų, maršrutų ir DNS sąrašus. Antra, patvirtinkite, kad establish() grąžino deskriptorių ir transporto lizdas apsaugotas. Trečia, paprašykite vienos paskirties vietos įtraukto prefikso viduje ir vienos išskirto prefikso viduje. Ketvirta, patikrinkite, kuris resolver iš tikrųjų atsakė, paklausdami mazgo vardo, kurį gali išspręsti tik jūsų resolver.
Užbaigtas rankos paspaudimas neįrodo nė vieno vėlesnio žingsnio. Kiekviena patikra izoliuoja vieną sluoksnį, todėl sėkmė antrame žingsnyje nieko nesako apie ketvirtą.
VpnService.Builder nuoroda išvardija visą builder paviršių. Viskas, ko reikia daugiau, yra kita API, kita programa arba jūsų pačių naudotojo erdvės stekas, ir tai yra projektavimo sprendimas, o ne padalinto tunelio klaida.