Két útvonal tehet IP-interfészt egy Android-alkalmazás elé, és mindkettő ugyanolyan fájlleíróra fut ki, nagyon különböző tulajdonosokkal. Az egyik a VpnService-en és annak builderén át vezet, a másik root shellen és a /dev/net/tunon át. Minden olvasás ezen a leírón egy kimenő csomag, minden írás pedig egy bejövő. Az utakat az választja el, kinél van a házirend, mi éli túl az OS-frissítést, és a routingtáblából mennyihez nyúlhatsz.

A platform útvonalát válaszd, kivéve ha meg tudod nevezni a konkrét dolgot, amit megtagad. Ez a megtagadás általában a TAP, a saját alkalmazáskészletedem kívüli rögzítés, vagy egy routing-szabály, amelyet a keretrendszer soha nem tesz elérhetővé.

A platform útvonalán szándékot nyilatkozol meg parancsok helyett. Adsz legalább egy címet, a kívánt útvonalakat, DNS-kiszolgálókat és egy opcionális alkalmazáslistát, majd az Android rendszerszolgáltatása saját identitása alatt elvégzi a jogosultságos beállítást. Soha nem tudod meg előre az interfész nevét, és soha nem adod ki az ioctl-t, amely létrehozza, és éppen ez a lényeg.

Két módja az IP-interfész megszerzésének

A root útvonal a régebbi recept. Megnyitod a /dev/net/tunt, meghívod a TUNSETIFF-et IFF_TUN-nal, felhozod az interfészt az ip linkkel, és kézzel adod hozzá a címeket és útvonalakat. Minden lépést neked kell jól elvégezni, azokat is, amelyeket a keretrendszer csendben elintézett volna. Egy hiányzó útvonal ugyanazt a felhasználói jelentést adja, mint egy halott kiszolgáló.

A hibakeresés ugyanannyira különbözik, mint a beállítás. A root útvonalon az interfész vagy létezik, vagy az ioctl olyan hibát adott vissza, amelyet közvetlenül olvashatsz. A platform útvonalán az interfész csak azután létezik, hogy az establish() sikeres, ezért a hiba kivételként vagy null leíróként érkezik, nem pedig olyan eszközcsomópontként, amelyben turkálhatsz.

Mit ad vissza valójában az establish()

A Builder.establish() három kimenetelű, és mindhárom más választ kíván. A leíró azt jelenti, hogy az interfész be van állítva és a tiéd használatra, a null azt, hogy a kérés nem ment át, a dobott kivétel pedig azt, hogy a rendszerszolgáltatás elutasította. A null és a siker egyetlen booleanba való összevonása a leggyakoribb módja annak, hogy egy alkalmazás működő alagutat jelentsen, amely soha nem visz csomagot. A Builder-referencia megadja a címkövetelményt, és érdemes elolvasni, mielőtt a kiszolgálót hibáztatod.

Az irányokat a leíró rögzíti, nem a protokollod. Az olvasások olyan kimenő csomagokat adnak, amelyek már ki vannak választva a VPN-hez, az írások pedig visszafecskendezik a bejövő csomagokat az interfészbe. A saját átviteled titkosítja, hitelesíti és keretezi ezeket a csomagokat, mert a platform a csomaghatáron megáll.

A Binder egy ugrás, a JNI egy másik tengely

A builder kérése a Binderen át lép be a rendszerbe, és valahol ez alatt a platform natív kódot hív meg az eszköz beállításához. Ez két különböző mechanizmus, egyetlen hívásba rétegezve. A Binder az alkalmazásod és egy rendszerszolgáltatás közötti folyamatok közti kommunikáció, míg a JNI a Java és a natív kód közötti híd egy folyamaton belül. Egy Android 9 AOSP-fa olvasása szó szerintivé teszi a vermet, és érdemes megjegyezni, hogy a belső osztálynevek és fájlútvonalak kiadások között mozognak.

Ami nem mozdul, az a szerződés, amelyre támaszkodhatsz: a builder, az általa visszaadott leíró és a körülötte lévő szolgáltatás életciklusa. Egyetlen alkalmazásnak sem szabad belső szolgáltatástranszakciót hívni vagy eszközcsomópontot újra létrehozni interfészhez. Ha a kódod egy AOSP-osztálynévtől függ, el fog törni egy olyan kiadáson, amelyet nem teszteltél.

A TUN IP-t visz, a TAP Ethernetet

A TUN IP-interfész. A TAP Ethernet-interfész, és ez a különbség dönti el, hogy az áthidalás egyáltalán lehetséges-e. A TUN-eszköznek nincs MAC-címe és nincs továbbítandó Ethernet-fejléce, ezért a TUN-kliens nem csatlakozhat layer-2 hídhoz, akárhogy vannak is megírva az útvonalak.

A VpnService az IP-változatot adja, amelyre egy alkalmazásszintű VPN-nek szüksége van, és amelyet nyers Ethernet-verem nélkül is támogatni tud. Ha a tervezésed áthidalást igényel, a platform útvonala nem visz oda, és a root útvonal csak akkor, ha a kernel és az eszköz engedélyei együttműködnek.

Ahol a root útvonal még nyer

Három munka szorítja ki az embereket a platform útvonaláról. A layer-2 áthidalás TAP-ot igényel. A saját VPN-hatókörödön kívüli alkalmazások forgalmának rögzítése olyan láthatóságot igényel, amelyet a keretrendszer nem ad meg. A builder által soha nem feltárt házirend-alapú routing, például külön tábla egy célállomáshoz, olyan szabályokat igényel, amelyeket magad telepítesz.

A költségek később jönnek: feloldott bootloader vagy rootolt lemezkép, alattad elmozduló SELinux-házirend, és egy eszköz, amely egy OTA után már nem viselkedik megfelelően. A rootolt receptet sem lehet a szokásos alkalmazásterjesztéssel kiszolgálni, ezért a legtöbb csapat belső diagnosztikai útvonalként tartja, nem termékként.

Választás a kettő között

A szükséges interfészből indulj ki, ne a meglévő kódból. Ha a válasz egy alkalmazásra korlátozott útvonalú TUN-eszköz, a platform útvonala rövidebb és túléli a frissítéseket. Ha a válasz Ethernet-kereteket, más alkalmazások rögzítését vagy a builder által elutasított routing-szabályokat foglal magában, root-területen vagy, és üzemeltetési költségként kell tervezned vele.

Egy ellenőrzés, amely a legtöbb vitát eldönti: először építsd fel a platform útvonalát, jegyezd fel az establish() eredményét a távoli munkamenet állapotától külön, és igazolj egy csomagot mindkét irányban. Csak ezután döntsd el, hogy a hiányzó képesség indokolja-e a támogatott útvonal elhagyását.

Kapcsolódó cikkek