Kāpēc jūsu Android VpnService tunelis pārstāj maršrutēt datplūsmu
Četri VpnService uzskati, kas ražošanā salūzt: viens tunelis vienlaikus, maršrutu apjoms, saraksti pa lietotnēm un izslēgšanas secība, ar pārbaudi katram.
Četri pieņēmumi par Android VpnService man reiz izmaksāja lielāko daļu nedēļas, un neviens no tiem nebija par tīkla protokolu. Lietotne teica savienots, atslēgas ikona sēdēja statusjoslā, un datplūsma joprojām izgāja pa parasto maršrutu. Es turpināju pieņemt, ka tunelis ir mans, līdz to apstādināšu, ka maršruti, ko pievienoju, ir maršruti, ko saņemu, un ka stopSelf izsaukums paņems līdzi arī saskarni. Pakalpojums uzvedās tieši tā, kā dokumentēts. Salūzusi bija mana mentālā modele.
Mīts 1: tunelis turas, līdz jūs to apstādinat
Android vienlaikus tur vienu VPN saskarni. Izveidojiet otru, un platforma to neieliek rindā: tā atslēdz jūsējo un izsauc onRevoke() jūsu pakalpojumā. Ja jūsu kods visu nojauc tikai ar pogas nospiešanu, pārņemšana atstāj aiz sevis deskriptoru un lasīšanas pavedienu, un nākamā palaišana manto abus.
VpnService atsauce skaidri saka, ka prepare() atgriež null, ja lietotnei jau ir piekrišana, un Intent, ja lietotājam tā vēl jāapstiprina. Šī piekrišana nonāk tikai tad, ja nolūku piegādājat caur startActivityForResult(). Izlaidiet piegādi, un establish() atgriezīs null, kas tiek lasīts kā klusa neveiksme, nevis atļauju problēma.
Always-on VPN pievieno īpašnieku, ko jūs nekontrolējat. Sistēma var atgriezt tuneli pēc palaišanas vai pēc avārijas, un tā apsteidz VPN lietotni, ko lietotājs palaida ar roku. Veidojiet onRevoke() tā, lai tas aizvērtu tos pašus resursus kā jūsu parastā izslēgšana, un uzskatiet to par galveno apturēšanas ceļu, nevis malas gadījumu. Lietotāji maina VPN lietotnes; jūsu kodam tas jāpārdzīvo.
Mīts 2: addRoute uztver tieši to, ko uzskaitījāt
Maršruti nosaka, kuri paketes sasniedz jūsu deskriptoru, un apjoma kļūda rada kaitinošo gadījumu, kad daļa datplūsmas iet caur tuneli, bet daļa ne. addRoute("0.0.0.0", 0) paņem visu IPv4, kas nozīmē, ka tagad katra pakete nonāk pie jums un tā ir jāpārsūta vai jāizmet. /32 vienam resursdatoram paņem gandrīz neko, un viss pārējais uzvedas tā, it kā jūsu VPN nekad nebūtu instalēts.
Ģimenes skaita atsevišķi. Tikai IPv4 maršruts atstāj IPv6 pamata tīklā, tāpēc divkāršā steks resursdators joprojām var atrisināt vārdu un savienoties ārpus tuneļa, kamēr iekšpusē tas izskatās pilnīgi savienots.
DNS ievēro to pašu loģiku. Serveri, kas pievienoti ar addDnsServer(), nonāk lietotnēs, kuras izmanto tuneļa atrisinātāju, bet lietotne ar savu šifrēto DNS galapunktu vai Private DNS, kas iestatīts uz resursdatora nosaukumu, tos pilnībā ignorē. Kad vārdi neizdodas vienai lietotnei un izdodas citai, pārbaudiet atrisinātāja ceļu, pirms aiztiekat kaut vienu maršrutu.
Mīts 3: noteikumi pa lietotnēm ir slēdzis, ko var pārslēgt abos virzienos
addDisallowedApplication() un addAllowedApplication() ir divi režīmi, nevis divi filtri. Veidotājs pieņem vienu modeli vienam tunelim, tāpēc lietotne, kas grib visu, izņemot trīs pakotnes, izmanto aizliegto sarakstu un atstāj atļauto sarakstu tukšu. Abi ir savstarpēji izslēdzoši, un cilvēki to uzzina izpildlaikā pēc tam, kad iestatījumu ekrāns jau ir piedāvājis abus slēdžus.
Pakešu redzamība arī kodina. Kopš Android 11 jūs varat nosaukt tikai tās pakotnes, kuras redzat, tāpēc izslēgšanas saraksts, kas veidots no vaicājuma, kuru jūsu lietotnei nav atļauts izpildīt, klusi palaidīs ierakstus garām. Salīdziniet sarakstu ar to, ko pakotņu pārvaldnieks patiešām atgriež, pirms tam uzticaties.
Darba profili atkal sadala auditoriju. Profila iekšpusē palaists tunelis aptver šo profilu un tā lietotnes, bet personīgā puse saglabā savu savienojumu. Tas ir vienreiz pieņemts apjoma lēmums, un tam vieta saskarnē, nevis atbalsta pieteikumā.
Mīts 4: stopSelf pabeidz tuneli
Nē. Saskarne dzīvo deskriptorā, ko atgriež establish(), un pakalpojuma objekts var nomirt, kamēr šis deskriptors paliek atvērts. Vispirms aizveriet ParcelFileDescriptor, atlaidiet atsauci, pievienojieties lasīšanas un rakstīšanas pavedieniem vai pārtrauciet tos, tad izsauciet stopForeground(true) un stopSelf() šajā secībā.
Aizmirsts deskriptors ir parastais iemesls, kāpēc pakalpojums šķietami pārdzīvo savu paša apturēšanas pieprasījumu, un tas izskaidro novecojušu maršrutu, kas dzīvo ilgāk par lietotni, kura to pievienoja.
Pretējā kļūda ir tikpat izplatīta: aizvērt deskriptoru, kamēr lasīšanas pavediens ir lasīšanas vidū, un pēc tam atstāt šo pavedienu apstrādāt izņēmumu, kuram neviens nav uzrakstījis apstrādi. Turiet izslēgšanas secību vienā vietā un ļaujiet gan lietotāja darbībai, gan onRevoke() tajā izsaukties.
Kas jāpārbauda, kad tunelis izskatās miris
Aizsargājiet ilgmūžīgus ligzdas ar protect() pirms tuneļa pacelšanas. Bez tā tuneļa paša transports tiek maršrutēts atpakaļ tunelī un savienojums iestrēgst tā, ka izskatās tieši kā servera kļūme. Pārbaudiet ar adresi, kas pastāv tikai aiz attālā tīkla, nevis ar publisku vietni, kas atbild no kešatmiņas vai no pamata ceļa.
Reģistrējiet establish() rezultātu, maršrutus, izslēgtās pakotnes un izslēgšanas iemeslu. Šīs četras rindas atdala piekrišanas problēmu no apjoma problēmas un no dzīves cikla problēmas bez pakešu tveršanas.