Twee paaie kan 'n IP-koppelvlak voor 'n Android-toepassing plaas, en albei eindig by dieselfde soort lêerdeskriptor met baie verskillende eienaars. Die een loop deur VpnService en sy bouer, die ander deur 'n root-dop en /dev/net/tun. Elke lesing op daardie deskriptor is 'n uitgaande pakket en elke skryf is 'n inkomende een. Wat die paaie skei, is wie die beleid hou, wie 'n opgradering van die bedryfstelsel oorleef, en hoeveel van die roetetabel jy mag aanraak.

Neem die platformpad tensy jy die spesifieke ding kan noem wat dit weier om te doen. Daardie weiering is gewoonlik TAP, vasvang buite jou eie stel toepassings, of 'n roetereël wat die raamwerk nooit blootlê nie.

Op die platformpad verklaar jy bedoeling eerder as opdragte. Jy voorsien ten minste een adres, die roetes wat jy wil, DNS-bedienerre en 'n opsionele lys toepassings, en dan doen Android se stelseldiens die bevoorregte opstelling onder sy eie identiteit. Jy leer nooit die koppelvlaknaam vooraf nie, en jy stuur nooit die ioctl uit wat dit skep nie — dit is juis die punt.

Twee maniere om 'n IP-koppelvlak te kry

Die root-pad is die ouer resep. Jy maak /dev/net/tun oop, roep TUNSETIFF met IFF_TUN, bring die koppelvlak op met ip link, en voeg adresse en roetes met die hand by. Elke stap is joune om reg te kry, insluitend dié wat die raamwerk stilswyend sou hanteer. Een ontbrekende roete lewer dieselfde gebruikersterugvoer as 'n dooie bediener.

Ontfouting verskil net soveel as opstelling. Op die root-pad bestaan die koppelvlak óf jou ioctl het 'n fout teruggegee wat jy direk kan lees. Op die platformpad bestaan die koppelvlak eers nadat establish() geslaag het, so 'n mislukking kom as 'n uitsondering of 'n null-deskriptor eerder as 'n toestelknoop waaraan jy kan peuter.

Wat establish() werklik teruggee

Builder.establish() het drie uitkomste en hulle verg drie verskillende reaksies. 'n Deskriptor beteken die koppelvlak is opgestel en joune om te gebruik, null beteken die versoek het nie deurgegaan nie, en 'n gegooide uitsondering beteken die stelseldiens het dit verwerp. Om null en sukses in een boolean saam te vou is die mees algemene manier waarop 'n app 'n werkende tonnel aanmeld wat nooit 'n pakket dra nie. Die Builder-verwysing noem die adresvereiste, wat die moeite werd is om te lees voordat jy die bediener die skuld gee.

Rigtings word deur die deskriptor vasgestel, nie deur jou protokol nie. Lesings gee jou uitgaande pakkette wat reeds vir die VPN gekies is, en skrywes spuit inkomende pakkette terug in die koppelvlak in. Jou eie transport versleutel, verifieer en raam daardie pakkette, want die platform stop by die pakketgrens.

Binder is een sprong, JNI is 'n ander as

'n Bouer-versoek gaan die stelsel binne deur Binder, en ergens daaronder roep die platform inheemse kode om die toestel op te stel. Dit is twee verskillende meganismes wat in een oproep gestapel is. Binder is interproseskommunikasie tussen jou app en 'n stelseldiens, terwyl JNI die Java-na-inheemse brug binne 'n proses is. Om 'n Android 9 AOSP-boom te lees laat die stapel letterlik lyk, en dit is die moeite werd om te onthou dat interne klasname en lêerpaaie tussen weergawes skuif.

Wat nie skuif nie, is die kontrak waarop jy mag staatmaak: die bouer, die deskriptor wat dit teruggee, en die dienslewensiklus daaromheen. Geen app behoort 'n interne dienstransaksie te roep of 'n toestelknoop te herskep om 'n koppelvlak te kry nie. As jou kode van 'n AOSP-klasnaam afhang, sal dit breek op 'n weergawe wat jy nie getoets het nie.

TUN dra IP, TAP dra Ethernet

TUN is 'n IP-koppelvlak. TAP is 'n Ethernet-koppelvlak, en daardie verskil bepaal of oorbrugging hoegenaamd moontlik is. 'n TUN-toestel het geen MAC-adres en geen Ethernet-opskrif om aan te stuur nie, so 'n TUN-kliënt kan nie by 'n laag-2-brug aansluit nie, hoe die roetes ook al geskryf is.

VpnService gee jou die IP-variant, wat 'n VPN op app-vlak nodig het en kan ondersteun sonder 'n rou Ethernet-stapel. As jou ontwerp oorbrugging vereis, sal die platformpad jou nie daar kry nie, en die root-pad sal dit net doen as die kern en die toesteltoestemmings saamwerk.

Waar die root-pad steeds wen

Drie take stoot mense van die platformpad af. Laag-2-oorbrugging het TAP nodig. Om verkeer van toepassings buite jou eie VPN-omvang vas te vang, het sigbaarheid nodig wat die raamwerk nie gee nie. Beleidsroetering wat die bouer nooit blootlê nie, soos 'n aparte tabel vir een bestemming, het reëls nodig wat jy self installeer.

Die koste kom later: 'n oopgemaakte bootloader of 'n geroote beeld, SELinux-beleid wat onder jou verander, en 'n toestel wat ná 'n OTA ophou werk soos dit moet. 'n Geroote resep kan ook nie deur normale app-verspreiding gestuur word nie, so die meeste spanne hou dit as 'n interne diagnostiese pad eerder as 'n produk.

Om tussen hulle te kies

Begin by die koppelvlak wat jy nodig het, nie by die kode wat jy het nie. As die antwoord 'n TUN-toestel met app-omvattende roetes is, is die platformpad korter en oorleef dit opgraderings. As die antwoord Ethernet-rame, vasvang van ander toepassings of roetereëls wat die bouer verwerp behels, is jy in root-gebied en moet jy daarvoor begroot as 'n bedryfskoste.

Een toets wat die meeste argumente besleg: bou eers die platformpad, teken die establish()-uitkoms apart van die buitenste sessietoestand aan, en bevestig een pakket in elke rigting. Besluit eers daarna of die ontbrekende vermoë dit regverdig om die ondersteunde pad te verlaat.

Verwante artikels