Apat na palagay tungkol sa Android VpnService ang kumain sa akin ng halos isang linggo minsan, at wala sa mga ito ang tungkol sa protocol ng network. Sinabi ng app na konektado, nakaupo ang icon ng susi sa status bar, at lumalabas pa rin ang trapiko sa normal na ruta. Paulit-ulit kong inaakala na akin ang tunnel hanggang ihinto ko ito, na ang mga rutang idinagdag ko ang mga rutang nakukuha ko, at na ang pagtawag sa stopSelf ay magbababa ng interface kasama nito. Kumikilos ang serbisyo nang eksakto ayon sa dokumentado. Ang sirang bahagi ay ang aking mental na modelo.

Mito 1: nananatili ang tunnel hanggang ihinto mo ito

Isang interface ng VPN lang ang hawak ng Android sa isang pagkakataon. Magtatag ng pangalawa at hindi ito ipipila ng platform: ide-deactivate nito ang sa iyo at tatawag ng onRevoke() sa serbisyo mo. Kung ang code mo ay nagbabagsak lang kapag pinindot ang button, may naiiwang descriptor at reader thread ang isang pag-agaw, at minamana ng susunod na pagsisimula ang dalawa.

Ang sanggunian ng VpnService ay malinaw na nagsasabi na ang prepare() ay nagbabalik ng null kapag may pahintulot na ang app, at Intent kapag kailangan pa itong aprubahan ng user. Dumarating lang ang pahintulot na iyon kung ipapadala mo ang intensyon sa pamamagitan ng startActivityForResult(). Laktawan ang paghahatid at ang establish() ay nagbabalik ng null, na binabasa bilang tahimik na pagkabigo at hindi bilang problema sa permiso.

Nagdadagdag ang Always-on VPN ng may-ari na hindi mo kontrolado. Maaaring ibalik ng system ang tunnel pagkatapos ng boot o pagkatapos ng crash, at nauunahan nito ang isang VPN app na mano-manong sinimulan ng user. Buuin ang onRevoke() para isara ang parehong resources tulad ng normal mong pagsasara, at ituring itong pangunahing daan ng paghinto at hindi bilang sulok na kaso. Nagpapalit ng VPN app ang mga user; kailangang mabuhay ng code mo doon.

Mito 2: hinuhuli ng addRoute ang eksaktong inilista mo

Ang mga ruta ang nagpapasya kung aling packet ang umabot sa descriptor mo, at ang error sa saklaw ay lumilikha ng nakakainis na kaso kung saan may trapikong dumadaan sa tunnel at may hindi. Kinukuha ng addRoute("0.0.0.0", 0) ang buong IPv4, ibig sabihin bawat packet ay dumarating na sa iyo at kailangang ipasa o itapon. Ang /32 para sa isang host ay halos walang kinukuha, at ang lahat ng iba ay kumikilos na parang hindi kailanman na-install ang VPN mo.

Hiwalay na binibilang ang mga pamilya. Ang rutang IPv4 lang ay nag-iiwan ng IPv6 sa underlying network, kaya ang dual-stack host ay makakaresolba at makakakonekta pa rin sa labas ng tunnel habang mukhang ganap na konektado sa loob.

Sumusunod ang DNS sa parehong lohika. Ang mga server na idinagdag gamit ang addDnsServer() ay napupunta sa mga app na gumagamit ng resolver ng tunnel, samantalang ang app na may sariling naka-encrypt na DNS endpoint o Private DNS na nakatakda sa isang hostname ay ganap silang binabalewala. Kapag nabigo ang mga pangalan sa isang app at gumagana sa iba, tingnan ang landas ng resolver bago galawin ang kahit isang ruta.

Mito 3: ang mga patakaran kada app ay switch na mababaligtad sa alinmang direksyon

Ang addDisallowedApplication() at addAllowedApplication() ay dalawang mode, hindi dalawang filter. Isang modelo lang kada tunnel ang tinatanggap ng builder, kaya ang app na gusto ang lahat maliban sa tatlong pakete ay gumagamit ng ipinagbabawal na listahan at iniiwang walang laman ang pinapayagang listahan. Nagbabawal sa isa't isa ang dalawa, at natutuklasan ito ng mga tao habang tumatakbo matapos nang ialok ng settings screen ang dalawang toggle.

Kumakagat din ang visibility ng pakete. Mula sa Android 11, maaari mong pangalanan lang ang mga paketeng nakikita mo, kaya ang listahan ng pagbubukod na binuo mula sa query na hindi pinapayagang patakbuhin ng app mo ay tahimik na makakaligtaan ng mga entry. Ihambing ang listahan sa aktwal na ibinabalik ng package manager bago ito paniwalaan.

Hinahati muli ng mga work profile ang audience. Ang tunnel na sinimulan sa loob ng profile ay sumasaklaw sa profile na iyon at sa mga app nito, habang pinapanatili ng personal na panig ang sarili nitong konektibidad. Iyon ay desisyon sa saklaw na ginawa nang isang beses, at nababagay ito sa interface at hindi sa isang support ticket.

Mito 4: tinatapos ng stopSelf ang tunnel

Hindi. Nabubuhay ang interface sa descriptor na ibinabalik ng establish(), at maaaring mamatay ang service object habang bukas pa ang descriptor na iyon. Isara muna ang ParcelFileDescriptor, bitawan ang reference, pagsamahin o abalahin ang reader at writer thread, pagkatapos ay tawagin ang stopForeground(true) at stopSelf() sa ganoong pagkakasunod-sunod.

Ang nakalimutang descriptor ang karaniwang dahilan kung bakit mukhang nakakaligtas ang isang serbisyo sa sarili nitong kahilingang huminto, at ipinapaliwanag nito ang lumang rutang mas matagal pang nabubuhay kaysa sa app na nagdagdag nito.

Kasingkaraniwan din ang kabaligtarang mali: isara ang descriptor habang nasa gitna ng pagbasa ang reader thread, pagkatapos ay hayaang harapin ng thread na iyon ang isang exception na walang nakasulat na code. Panatilihin ang pagkakasunod-sunod ng pagsasara sa isang lugar at hayaang tumawag doon ang aksyon ng user at ang onRevoke().

Ano ang dapat tsekan kapag mukhang patay ang tunnel

Protektahan ang mga long-lived socket gamit ang protect() bago umangat ang tunnel. Kung wala iyon, ang sariling transport ng tunnel ay nairuruta pabalik sa tunnel at nag-stall ang koneksyon sa paraang eksaktong mukhang sira ng server. Subukan laban sa isang address na umiiral lang sa likod ng remote network, hindi sa isang pampublikong site na sumasagot mula sa cache o mula sa underlying path.

I-log ang resulta ng establish(), ang mga ruta, ang mga ibinukod na pakete at ang dahilan ng pagsasara. Ang apat na linyang iyon ang naghihiwalay ng problema sa pahintulot mula sa problema sa saklaw at sa problema sa lifecycle nang walang packet capture.

Kaugnay na mga artikulo