Nic ve VpnService samo o sobě provoz nezachytává. Požádá systém o rozhraní tun, předá mu sadu adres a tras a od té chvíle jádro kopíruje každý paket odpovídající těmto trasám do deskriptoru souboru, který aplikace může číst. Kód aplikace nikdy nevidí pakety, které trasy nevybraly, takže rozsah tunelu je otázka směrovací tabulky dřív než otázka programování. Proto může stejná implementace služby spolknout každý tok na jednom telefonu a jen část na jiném, a proto přidání trasy někdy opraví tunel, který vypadal rozbitý. Dostanete to, co jste deklarovali, minus vše, co tvůrce tiše odmítl.

Trasy rozhodují, které pakety dorazí

Builder.addRoute(prefix, prefixLength) nainstaluje prefix do směrovací tabulky sítě VPN. Systém tuto síť považuje za výchozí pro aplikace v rozsahu, takže se její záznamy procházejí před výchozí trasou fyzické sítě a shodný prefix stačí k odklonění toku. Trasa pro 0.0.0.0/0 tedy převezme IPv4 celý, včetně tiskárny, cíle cast a administrační stránky routeru.

Klienti, kteří chtějí toto pokrytí bez smazání fyzického výchozího záznamu, používají místo toho dvojici 0.0.0.0/1 a 128.0.0.0/1. Dvě konkrétnější trasy porazí výchozí trasu, společně pokrývají celý adresní prostor a vlastní výchozí záznam telefonu zůstává v tabulce pro sockety, které jsou stále vázány na fyzickou síť.

Adresy a záznamy DNS pocházejí od stejného tvůrce a odpovídají na jinou otázku. addAddress dá rozhraní adresu s délkou prefixu a addDnsServer nainstaluje překladač, který budou aplikace v rozsahu dotazovat přes rozhraní. Obě rodiny mohou sedět na jednom rozhraní, takže sada tras jen pro IPv4 vedle adresy IPv6 je konfigurace, která se čte správně a přesto uniká. Pokud směrujete rodinu, ve které rozhraní nemá adresu, jádro nemá zdrojovou adresu pro tyto pakety a nikdy se u deskriptoru neobjeví.

Co establish() vrací

establish() vrací ParcelFileDescriptor, nebo null, když aplikace není autorizovaná. Null je stav souhlasu, ne síťová chyba: VpnService.prepare() už musel vrátit null, nebo uživatel musel před voláním schválit systémový dialog. Souhlas lze odvolat za běhu služby a rozhraní se pod vámi zavře bez jakéhokoli zpětného volání do vaší čtecí smyčky.

Deskriptor je neblokující, pokud neřeknete jinak, takže čtení se vrátí okamžitě bez dat, místo aby vlákno zaparkovalo. Smyčka napsaná pro blokující socket spálí jedno jádro a smyčka, která prázdné čtení považuje za fatální, roztrhne tunel během nečinné sekundy. Builder.setBlocking(true) toto chování mění a cenou je vlákno, které čeká uvnitř čtení.

Argumenty trasy a adresy se ověřují jako hodnoty LinkAddress a multicastová cíle jako 224.0.0.0/4 se odmítají rovnou. Rozhraní tun bod-bod také nemá broadcastovou doménu, takže multicastové objevování přes něj potřebuje jiný návrh, ne další trasu. Referenční příručka VpnService.Builder je seznam, který je třeba zkontrolovat, než usoudíte, že volání bylo ignorováno.

Rozsah podle aplikací zužuje sadu, ne tunel

Od API 21 tvůrce přijímá addAllowedApplication a addDisallowedApplication a oba seznamy se v jednom tvůrci vzájemně vylučují. Seznam povolených vytvoří tunel pro tyto pakety, zatímco vše ostatní si ponechá fyzickou síť; seznam zakázaných zachytí vše kromě uvedených paketů, což je to, co klient nabízí jako rozdělené tunelování. Před API 21 neexistuje žádný platformní mechanismus a chování podle aplikací pocházelo od klientů třetích stran, kteří filtrovali podle UID uvnitř svého přesměrovávače. Rozdíl je důležitý při ladění, protože tunel podle aplikací se seznamem povolených z jedné aplikace produkuje téměř žádný provoz a to je správné chování, ne rozbitá trasa.

Ať je rozsah jakýkoli, socket, který vaše aplikace otevírá k serveru VPN, nesmí cestovat tunelem, který staví. VpnService.protect(socket) připoutá tento socket k podkladové síti, takže jeho pakety přeskakují zařízení tun. Vynechejte volání a handshake pohltí rozhraní, které ho mělo zvednout.

VpnService.setUnderlyingNetworks uzavírá smyčku tím, že systému řekne, která fyzická síť tunel nese. Tato hodnota určuje, jak VPN hlásí připojení ostatním aplikacím, a null nebo zastaralá hodnota je běžnou příčinou připojeného tunelu, který zbytek telefonu považuje za offline. Aktualizujte ji při změně podkladové sítě, ne jen při startu.

Kontrolní součty a MTU: kde správná trasa přesto selže

Jakmile pakety dorazí, dvě podrobnosti na úrovni paketu rozhodují, zda něco funguje od konce do konce. První je zdrojová adresa: při několika adresách rozhraní může jádro vysílat pakety s první z nich, takže přesměrovávač, který přepisuje hlavičky, musí opravit adresu i kontrolní součet, který ji pokrývá.

Druhý je rozsah kontrolního součtu. Když aplikace staví pakety TCP nebo UDP sama, kontrolní součet pokrývá pseudo-hlavičku se zdrojovou adresou, cílovou adresou, číslem protokolu a délkou transportu, a vynechání kteréhokoli z nich dá pakety, které dorazí a zahodí se. Tato porucha vypadá jako časový limit po odpovědi, která nikdy nepřijde, což většinu lidí pošle zpět k trasám. setMtu fixuje MTU rozhraní a každé čtení vrací právě jeden úplný paket IP, takže buffer napsaný pro proud bajtů fragmentuje vaše parsování, ne paket.

Ověřovací průchod, který odděluje vrstvy

Chcete-li najít, kde se rozsah pokazil, testujte vrstvy popořadě. Zeptejte se zařízení, které trasy síť VPN skutečně drží, pak zkontrolujte, zda se tok na známou adresu objeví jako čtení u deskriptoru, pak zkontrolujte, zda je paket, který váš přesměrovávač vysílá, správně utvořený. Problém trasy neukáže u deskriptoru nic, zatímco problém kontrolního součtu ukáže paket, který dorazí a zmizí.

Pokud se k deskriptoru nic nedostane, podezřelým je sada tras, ne čtecí smyčka: ověřte prefixy, ověřte, že existuje adresa ve stejné rodině, a ověřte, že aplikace je v rozsahu, který jste nastavili. Pokud pakety k deskriptoru dorazí, ale nic se nevrací, prohlédněte protect() a hodnotu podkladové sítě, než znovu sáhnete na trasu. Pokud vzdálená strana hlásí poškozené pakety, kontrolní součet a MTU jsou místa, kam se dívat. Jedna sonda na vrstvu ušetří odpoledne hádání.

Související články