VpnService na Androidu nema rutu za isključivanje i nijedno čitanje API-ja neće je proizvesti. Builder.addRoute() uvijek dodaje samo odredišta u tunel, pa zadržavanje LAN segmenta lokalnim znači da taj segment nikada ne ulazi u listu ruta. Ono što umjesto toga dodajete jest komplement segmenta, izražen običnim prefiksima. Posao je aritmetika i pada na nekoliko predvidivih mjesta.

Prođite provjere ispod redom. Svaka pretpostavlja da je prethodna prošla, pa stanite na prvom neuspjehu umjesto da krpite simptome niže.

Pođite od onoga što tunel tvrdi

Prosljeđivanje 0.0.0.0/0 u addRoute() govori Androidu da svako IPv4 odredište pripada tunelu. Zato toliko klijenata izgleda nesposobno za podijeljeno tuneliranje: traže cijeli adresni prostor i onda pokušavaju oduzeti od njega. Ništa u graditelju ne oduzima. Specifičnija ruta za mrežu koju ste htjeli zadržati lokalnom čini suprotno od očekivanog, jer najduži prefiks koji se podudara pobjeđuje i ta mreža završava unutar tunela.

Lista ruta je cijela politika. Nema veta po odredištu iza nje, a Builder referenca ne navodi metodu koja bi ga pružila.

Generirajte komplement iz aritmetike prefiksa

Komplement isključenog prefiksa je skup njegove braće na putu do korijena stabla adresa. Za svaku dužinu od 1 do isključene dužine uzmite blok te dužine koji sadrži vaš prefiks, obrnite bit neposredno ispod prefiksa i ispišite rezultat. Zvuči apstraktno dok ga ne pokrenete.

Isključivanje 10.0.0.0/8 daje osam ruta: 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. Nijedna od njih ne sadrži 10.0.0.0/8, a zajedno pokrivaju sve ostalo.

Osam redova generira tu listu za bilo koji prefiks:

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))

Broj ruta jednak je dužini prefiksa i to je broj koji treba pratiti. Jedno isključenje /21 košta 21 rutu i ostaje čitljivo. Deset isključenja može koštati nekoliko stotina, što obično znači da dizajn mreže treba mijenjati, a ne generator.

Provjerite porodicu adresa i obim tunela

IPv4 rute ne čine ništa za IPv6. Ako tunel traži IPv6 adresu, IPv6 LAN prefiksu treba vlastiti komplement, generiran istom aritmetikom nad 128 bita. Čest uzrok da isključenje radi na papiru dok lokalni uređaj ostaje nedostupan jest ovaj: hostu se pristupilo preko IPv6, a samo je IPv4 izrezan. Provjerite obje porodice prije nego zaključite da je matematika pogrešna.

Obim je druga polovina provjere. Android prati rutiranje po mreži i po aplikaciji, pa isti prefiks može ići kroz tunel za jednu aplikaciju, a direktno za drugu, zavisno od toga kako je VPN deklariran.

Smjestite DNS i pravila po aplikaciji tamo gdje pripadaju

Isključenja ruta ne mijenjaju resolver. Lokalno ime hosta još može odgovoriti DNS server tunela dok se njegova adresa rutira lokalno, a nesklad se pokazuje kao ime koje se razrješava u nešto do čega LAN ne može doći. Postavite politiku resolvera namjerno, s domenom pretrage ili pravilom podijeljenog DNS-a. Ne pretpostavljajte da su rute to odlučile umjesto vas.

addDisallowedApplication() drži jednu aplikaciju izvan tunela; ne drži odredište izvan za aplikacije koje ostaju unutra. protect() pušta socket koji otvorite da zaobiđe tunel, i tako tunel izbjegava hvatanje vlastitog transporta. Nijedno od toga ne zamjenjuje komplement.

Provjerite pretragom, ne vlastitom listom

Kad je tunel podignut, pitajte uređaj kuda adresa zapravo ide umjesto da ponovo čitate listu koju ste sastavili. Na buildu koji isporučuje iproute2, adb shell ip route get 10.1.2.3 imenuje interfejs koji je kernel odabrao. Pokrenite istu komandu za javnu adresu i potvrdite da imenuje tunel.

Još dvije provjere zatvaraju krug. Prvo razriješite lokalno ime, zatim pokrenite adb shell ip route get na razriješenoj adresi i zahtijevajte interfejs koji nije tunel, jer uspješan adb shell ping -c 1 host.lan dokazuje dostupnost, a ne putanju. Ponovite test porodice adresa preko IPv6 ako tunel traži jednu. Testirajte na API nivou koji isporučujete, jer se način na koji se VPN mreže izlažu routing tabeli mijenjao kroz izdanja. Lista ruta koja izgleda ispravno na papiru i pretraga koja odabere pogrešan interfejs su različiti bugovi, a samo drugi stiže do korisnika.

Ako lista isključenja nastavi rasti, prestanite generirati rute i prenumerirajte udaljenu mrežu ili suzite tunel umjesto toga. Generator je zaobilazno rješenje za API koji nedostaje, a ne alat za dizajn.

Povezani članci