Android VPN: deskriptor VpnService verzus root tun zariadenie
Aplikácie pre Android nemôžu samy vytvoriť tun0: čo vracia establish(), kde končí Binder a začína JNI a kedy vyhrá zariadenie vytvorené rootom.
Dve cesty môžu postaviť IP rozhranie pred aplikáciu Android a obe končia pri rovnakom druhu deskriptora súboru s veľmi odlišnými vlastníkmi. Jedna vedie cez VpnService a jeho builder, druhá cez root shell a /dev/net/tun. Každé čítanie z tohto deskriptora je odchádzajúci paket a každý zápis je prichádzajúci. Cesty oddeľuje to, kto drží politiku, kto prežije upgrade operačného systému a koľko zo smerovacej tabuľky smieš zmeniť.
Choď cestou platformy, ak nedokážeš pomenovať konkrétnu vec, ktorú odmieta urobiť. Toto odmietnutie býva TAP, zachytávanie mimo tvojej množiny aplikácií alebo smerovacie pravidlo, ktoré framework nikdy nevystaví.
Na ceste platformy deklaruješ zámer namiesto príkazov. Dodáš aspoň jednu adresu, trasy, ktoré chceš, servery DNS a voliteľný zoznam aplikácií, a potom systémová služba Androidu vykoná privilegované nastavenie pod vlastnou identitou. Názov rozhrania sa nikdy nedozvieš vopred a nikdy nevydáš ioctl, ktoré ho vytvára, a o to ide.
Dva spôsoby, ako získať IP rozhranie
Cesta root je starší recept. Otvoríš /dev/net/tun, zavoláš TUNSETIFF s IFF_TUN, zdvihneš rozhranie pomocou ip link a adresy aj trasy pridáš ručne. Každý krok musíš urobiť správne sám, vrátane tých, ktoré by framework vybavil potichu. Jedna chýbajúca trasa vyvolá u používateľa rovnakú správu ako mŕtvy server.
Ladenie sa líši rovnako ako nastavenie. Na ceste root rozhranie buď existuje, alebo ti ioctl vrátil chybu, ktorú môžeš prečítať priamo. Na ceste platformy rozhranie existuje len po úspechu establish(), takže zlyhanie príde ako výnimka alebo null deskriptor, nie ako uzol zariadenia, do ktorého by si mohol vŕtať.
Čo establish() skutočne vracia
Builder.establish() má tri výsledky a každý potrebuje inú reakciu. Deskriptor znamená, že rozhranie je nakonfigurované a je tvoje na použitie, null znamená, že požiadavka neprešla, a vyhodená výnimka znamená, že ju systémová služba odmietla. Zlúčenie null a úspechu do jedného booleanu je najbežnejší spôsob, akým aplikácia hlási funkčný tunel, ktorý nikdy nenesie paket. Referenčná príručka k Builderu uvádza požiadavku na adresu a stojí za prečítanie, než obviníš server.
Smery určuje deskriptor, nie tvoj protokol. Čítania ti dávajú odchádzajúce pakety už vybrané pre VPN a zápisy vstrekujú prichádzajúce pakety späť do rozhrania. Pakety šifruje, overuje a rámuje tvoj vlastný transport, pretože platforma končí na hranici paketu.
Binder je jeden krok, JNI je iná os
Požiadavka builderu prechádza do systému cez Binder a niekde pod tým platforma volá natívny kód, aby zariadenie nakonfigurovala. To sú dva rôzne mechanizmy naskladané v jednom volaní. Binder je medziprocesová komunikácia medzi tvojou aplikáciou a systémovou službou, zatiaľ čo JNI je most z Javy do natívneho kódu vnútri procesu. Čítanie stromu AOSP pre Android 9 robí zásobník doslova viditeľným a stojí za to pamätať, že interné názvy tried a cesty k súborom sa medzi verziami menia.
Čo sa nemení, je zmluva, na ktorú sa môžeš spoľahnúť: builder, deskriptor, ktorý vracia, a životný cyklus služby okolo neho. Žiadna aplikácia by nemala volať internú transakciu služby ani znovu vytvárať uzol zariadenia, aby získala rozhranie. Ak tvoj kód závisí od názvu triedy z AOSP, rozbije sa vo verzii, ktorú si netestoval.
TUN nesie IP, TAP nesie Ethernet
TUN je IP rozhranie. TAP je Ethernet rozhranie a tento rozdiel rozhoduje, či je prepojenie vôbec možné. Zariadenie TUN nemá MAC adresu ani Ethernetovú hlavičku na preposielanie, takže klient TUN sa nemôže pripojiť k mostu vrstvy 2, nech sú trasy napísané akokoľvek.
VpnService ti dáva variant IP, ktorý VPN na úrovni aplikácie potrebuje a ktorý zvládne bez surového Ethernet zásobníka. Ak tvoj návrh vyžaduje prepojenie, cesta platformy ťa tam nedovedie a cesta root len vtedy, keď jadro a oprávnenia zariadenia spolupracujú.
Kde cesta root stále víťazí
Tri úlohy tlačia ľudí z cesty platformy. Prepojenie vrstvy 2 potrebuje TAP. Zachytávanie prevádzky aplikácií mimo tvojho rozsahu VPN potrebuje viditeľnosť, ktorú framework nedáva. Politické smerovanie, ktoré builder nikdy nevystaví, napríklad samostatnú tabuľku pre jeden cieľ, potrebuje pravidlá, ktoré nainštaluješ sám.
Náklady prídu neskôr: odomknutý bootloader alebo obraz s rootom, politika SELinux, ktorá sa pod tebou mení, a zariadenie, ktoré sa po OTA prestane správať správne. Recept s rootom tiež nemožno doručiť bežnou distribúciou aplikácií, takže väčšina tímov ho drží ako internú diagnostickú cestu, nie ako produkt.
Voľba medzi nimi
Začni od rozhrania, ktoré potrebuješ, nie od kódu, ktorý máš. Ak je odpoveďou zariadenie TUN s trasami obmedzenými na aplikáciu, cesta platformy je kratšia a prežije upgrady. Ak odpoveď zahŕňa Ethernetové rámce, zachytávanie iných aplikácií alebo smerovacie pravidlá, ktoré builder odmieta, si na území rootu a mal by si na to vyhradiť rozpočet ako prevádzkový náklad.
Kontrola, ktorá vyrieši väčšinu sporov: najprv postav cestu platformy, zaznamenaj výsledok establish() oddelene od stavu vzdialenej relácie a potvrď jeden paket v každom smere. Až potom rozhodni, či chýbajúca schopnosť ospravedlňuje opustenie podporovanej cesty.