Ako vylúčiť podsiete z tunela VPN v Androide
VpnService nevie vylúčiť trasu, preto pridáte doplnok: osem vygenerovaných prefixov pre /8, plus kontroly IPv6, DNS a vyhľadania, ktoré zachytia zvyšok.
VpnService v Androide nemá vylučovaciu trasu a žiadne čítanie API ju nevytvorí. Builder.addRoute() vždy iba pridáva ciele do tunela, takže ponechanie segmentu LAN lokálne znamená, že sa tento segment vôbec nedostane do zoznamu trás. Namiesto toho pridáte doplnok segmentu vyjadrený bežnými prefixmi. Práca je aritmetika a zlyháva na niekoľkých predvídateľných miestach.
Prejdite nižšie uvedené kontroly v poradí. Každá predpokladá, že predchádzajúca prešla, takže zastavte sa pri prvom zlyhaní namiesto záplatovania príznakov nižšie.
Začnite tým, čo tunel tvrdí
Odovzdanie 0.0.0.0/0 do addRoute() hovorí Androidu, že každý cieľ IPv4 patrí tunelu. Preto toľko klientov pôsobí neschopne pre rozdelené tunelovanie: nárokujú si celý adresný priestor a potom sa z neho snažia odčítať. Nič v konštruktéri neodčíta. Konkrétnejšia trasa pre sieť, ktorú ste chceli ponechať lokálnu, robí opak očakávania, pretože najdlhší zodpovedajúci prefix vyhráva a táto sieť skončí vnútri tunela.
Zoznam trás je celá politika. Nie je za ním žiadne veto na cieľ a referencia Builder neuvádza metódu, ktorá by ho poskytla.
Vygenerujte doplnok z aritmetiky prefixov
Doplnok vylúčeného prefixu je množina jeho súrodencov na ceste ku koreňu stromu adries. Pre každú dĺžku od 1 do vylúčenej dĺžky vezmite blok tejto dĺžky obsahujúci váš prefix, otočte bit bezprostredne pod prefixom a vypíšte výsledok. Znie to abstraktne, kým to nespustíte.
Vylúčenie 10.0.0.0/8 dáva osem trás: 128.0.0.0/1, 64.0.0.0/2, 32.0.0.0/3, 16.0.0.0/4, 0.0.0.0/5, 12.0.0.0/6, 8.0.0.0/7, 11.0.0.0/8. Žiadna z nich neobsahuje 10.0.0.0/8 a spolu pokrývajú všetko ostatné.
Osem riadkov generuje tento zoznam pre akýkoľvek prefix:
import ipaddress
def complement(addr, length):
base = int(ipaddress.IPv4Address(addr))
out = []
for k in range(1, length + 1):
block = base & (0xFFFFFFFF << (32 - k))
sibling = block ^ (1 << (32 - k))
out.append(f"{ipaddress.IPv4Address(sibling)}/{k}")
return out
print(complement("10.0.0.0", 8))
Počet trás sa rovná dĺžke prefixu a to je číslo, ktoré treba sledovať. Jedno vylúčenie /21 stojí 21 trás a zostáva čitateľné. Desať vylúčení môže stáť niekoľko stoviek, čo zvyčajne znamená, že zmenu potrebuje návrh siete, nie generátor.
Skontrolujte rodinu adries a rozsah tunela
Trasy IPv4 nerobia pre IPv6 nič. Ak tunel nárokuje adresu IPv6, lokálny prefix IPv6 potrebuje vlastný doplnok vygenerovaný tou istou aritmetikou nad 128 bitmi. Častou príčinou, že vylúčenie funguje na papieri, zatiaľ čo lokálne zariadenie zostáva nedostupné, je toto: hostiteľ bol dosiahnutý cez IPv6 a vyrezané bolo iba IPv4. Skontrolujte obe rodiny, kým uzavriete, že matematika je nesprávna.
Rozsah je druhá polovica kontroly. Android sleduje smerovanie po sieťach a po aplikáciách, takže ten istý prefix môže ísť tunelom pre jednu aplikáciu a priamo pre inú podľa toho, ako je VPN deklarovaná.
Umiestnite pravidlá DNS a pre aplikácie tam, kam patria
Vylúčenia trás nemenia resolver. Lokálny názov hostiteľa môže stále odpovedať DNS server tunela, zatiaľ čo jeho adresa sa smeruje lokálne, a nesúlad sa prejaví ako názov, ktorý sa prekladá na niečo, čo LAN nedosiahne. Nastavte politiku resolvera zámerne, s vyhľadávacou doménou alebo pravidlom rozdeleného DNS. Nepredpokladajte, že to za vás rozhodli trasy.
addDisallowedApplication() drží jednu aplikáciu mimo tunela; nedrží cieľ mimo pre aplikácie, ktoré zostávajú vnútri. protect() umožňuje soketu, ktorý otvoríte, obísť tunel, a tak sa tunel vyhýba zachyteniu vlastnej dopravy. Ani jedno nenahrádza doplnok.
Overujte vyhľadaním, nie vlastným zoznamom
Keď tunel beží, opýtajte sa zariadenia, kam adresa skutočne ide, namiesto opätovného čítania zoznamu, ktorý ste zostavili. Na builde, ktorý dodáva iproute2, adb shell ip route get 10.1.2.3 pomenúva rozhranie, ktoré vybralo jadro. Spustite ten istý príkaz pre verejnú adresu a potvrďte, že pomenúva tunel.
Dve ďalšie kontroly uzatvárajú slučku. Najprv preložte lokálny názov, potom spustite adb shell ip route get na preloženej adrese a požadujte rozhranie, ktoré nie je tunel, pretože úspešný adb shell ping -c 1 host.lan dokazuje dosiahnuteľnosť, nie cestu. Zopakujte test rodiny adries cez IPv6, ak tunel nejakú nárokuje. Testujte na úrovni API, ktorú dodávate, pretože spôsob, akým sa siete VPN vystavujú smerovacej tabuľke, sa medzi verziami menil. Zoznam trás, ktorý na papieri vyzerá správne, a vyhľadanie, ktoré vyberie nesprávne rozhranie, sú rôzne chyby a iba tá druhá sa dostane k používateľom.
Ak zoznam vylúčení ďalej rastie, prestaňte generovať trasy a prečíslujte vzdialenú sieť alebo zúžte tunel. Generátor je obchádzka chýbajúceho API, nie nástroj návrhu.