Kial via Android VpnService-tunelo ĉesas enkursigi trafikon
Kvar konvinkoj pri VpnService, kiuj rompiĝas en produkto: unu tunelo samtempe, atingosfero de itineroj, listoj laŭ aplikaĵo kaj malŝalta ordo, kun kontrolo por ĉiu.
Kvar supozoj pri Android VpnService iam kostis al mi la plej grandan parton de semajno, kaj neniu el ili temis pri la reta protokolo. La aplikaĵo diris konektita, la ŝlosila ikono sidis en la stata breto, kaj la trafiko tamen eliris laŭ la normala itinero. Mi daŭre supozis, ke la tunelo estas mia ĝis mi haltigos ĝin, ke la itineroj, kiujn mi aldonis, estas la itineroj, kiujn mi ricevas, kaj ke voko al stopSelf kunportos la interfacon. La servo kondutis ĝuste kiel dokumentite. Mia mensa modelo estis la rompita parto.
Mito 1: la tunelo restas supren ĝis vi haltigas ĝin
Android tenas unu VPN-interfacon samtempe. Starigu duan kaj la platformo ne metas ĝin en vicon: ĝi malaktivigas la vian kaj vokas onRevoke() ĉe via servo. Se via kodo nur malkonstruas ĉe butonpremo, transpreno lasas malantaŭe priskribilon kaj lega fadeno, kaj la sekva starto heredas ambaŭ.
La referenco de VpnService eksplicite diras, ke prepare() redonas null kiam la aplikaĵo jam havas konsenton, kaj Intent kiam la uzanto ankoraŭ devas aprobi. Tiu aprobo alvenas nur se vi transdonas la intencon per startActivityForResult(). Preterlasu la transdonon kaj establish() redonas null, kio legeblas kiel silenta malsukceso anstataŭ permesa problemo.
Always-on VPN aldonas posedanton, kiun vi ne regas. La sistemo povas revenigi la tunelon post lanĉo aŭ post kraŝo, kaj ĝi antaŭas VPN-aplikaĵon, kiun la uzanto startis mane. Konstruu onRevoke() tiel, ke ĝi fermas la samajn rimedojn kiel via normala malŝalto, kaj traktu ĝin kiel ĉefan haltan vojon anstataŭ randa kazo. Uzantoj ŝanĝas VPN-aplikaĵojn; via kodo devas postvivi tion.
Mito 2: addRoute kaptas ĝuste tion, kion vi listigis
La itineroj decidas, kiuj pakaĵoj atingas vian priskribilon, kaj eraro pri atingosfero kreas la frenezan kazon, en kiu parto de la trafiko iras tra la tunelo kaj parto ne. addRoute("0.0.0.0", 0) prenas la tutan IPv4, kio signifas, ke ĉiu pakaĵo nun alvenas al vi kaj devas esti plusendita aŭ forĵetita. /32 por unu sola gasto prenas preskaŭ nenion, kaj ĉio alia kondutas kvazaŭ via VPN neniam estis instalita.
Familioj estas kalkulataj aparte. Itinero nur por IPv4 lasas IPv6 sur la suba reto, do duobla-staka gasto ankoraŭ povas solvi kaj konekti ekster la tunelo dum ĝi aspektas perfekte konektita interne.
DNS sekvas la saman logikon. Serviloj aldonitaj per addDnsServer() iras al aplikaĵoj, kiuj uzas la solvilon de la tunelo, dum aplikaĵo kun propra ĉifrita DNS-fina punkto aŭ Private DNS agordita al gastnomo tute ignoras ilin. Kiam nomoj malsukcesas por unu aplikaĵo kaj funkcias por alia, kontrolu la solvilan vojon antaŭ ol tuŝi eĉ unu itinero.
Mito 3: reguloj laŭ aplikaĵo estas ŝaltilo, kiun vi povas turni al ambaŭ flankoj
addDisallowedApplication() kaj addAllowedApplication() estas du reĝimoj, ne du filtriloj. La konstruilo akceptas unu modelon por tunelo, do aplikaĵo, kiu volas ĉion krom tri pakaĵoj, uzas la malpermesitan liston kaj lasas la permesitan liston malplena. La du sin reciproke ekskludas, kaj homoj malkovras tion dum rulado post kiam la agorda ekrano jam proponis ambaŭ ŝaltilojn.
Pakaĵa videbleco ankaŭ mordas. En Android 11 kaj poste vi povas nomi nur pakaĵojn, kiujn vi vidas, do ekskluda listo konstruita el peto, kiun via aplikaĵo ne rajtas ruli, silentos pri mankantaj eroj. Komparu la liston kun tio, kion la pakaĵadministrilo efektive redonas, antaŭ ol fidi ĝin.
Laborprofiloj denove dividas la publikon. Tunelo startita ene de la profilo kovras tiun profilon kaj ĝiajn aplikaĵojn, dum la persona flanko konservas sian propran konektecon. Tio estas decido pri atingosfero farita unufoje, kaj ĝi apartenas al la interfaco, ne al subtena bileto.
Mito 4: stopSelf finas la tunelon
Ne finas. La interfaco vivas en la priskribilo redonita de establish(), kaj la serva objekto povas morti dum tiu priskribilo restas malfermita. Fermu unue la ParcelFileDescriptor, forlasu la referencon, kunigu aŭ interrompu la legajn kaj skribajn fadenojn, poste voku stopForeground(true) kaj stopSelf() en tiu ordo.
Forgesita priskribilo estas la kutima kialo, ke servo ŝajnas postvivi sian propran haltpeton, kaj ĝi klarigas malnoviĝintan itineron, kiu vivas pli longe ol la aplikaĵo, kiu aldonis ĝin.
La mala eraro same oftas: fermi la priskribilon dum lega fadeno estas meze de legado, kaj poste lasi tiun fadenon trakti escepton, por kiu neniu skribis prilaboron. Tenadu la malŝaltan sekvencon en unu loko kaj lasu kaj la uzantan agon kaj onRevoke() voki en ĝin.
Kion kontroli, kiam la tunelo aspektas morta
Protektu longdaŭrajn ingojn per protect() antaŭ ol la tunelo leviĝas. Sen tio la propra transporto de la tunelo estas enkursigita reen en la tunelon kaj la konekto ŝtopiĝas tiel, ke ĝi aspektas ĝuste kiel servila paneo. Testu kontraŭ adreso, kiu ekzistas nur malantaŭ la fora reto, ne kontraŭ publika retejo, kiu respondas el kaŝmemoro aŭ el la suba vojo.
Registru la rezulton de establish(), la itinerojn, la ekskluditajn pakaĵojn kaj la kialon de la malŝalto. Tiuj kvar linioj apartigas konsentan problemon de atingosfera problemo kaj de vivcikla problemo sen pakaĵkapto.