Androids VpnService har ingen utelukkelsesrute, og ingen mengde API-lesing vil produsere en. Builder.addRoute() legger alltid bare til destinasjoner i tunnelen, så å holde et LAN-segment lokalt betyr at segmentet aldri kommer inn i rutelisten. Det du legger til i stedet er komplementet til segmentet, uttrykt som vanlige prefikser. Arbeidet er aritmetikk, og det feiler på noen få forutsigbare steder.

Gå gjennom sjekkene nedenfor i rekkefølge. Hver forutsetter at den forrige holdt, så stopp ved den første feilen i stedet for å lappe symptomer lenger ned.

Start med det tunnelen hevder

Å sende 0.0.0.0/0 til addRoute() forteller Android at hver IPv4-destinasjon tilhører tunnelen. Derfor virker så mange klienter ute av stand til delt tunnelering: de krever hele adresseområdet og prøver så å trekke fra det. Ingenting i byggeren trekker fra. En mer spesifikk rute for nettverket du ville holde lokalt gjør det motsatte av det folk forventer, fordi det lengste samsvarende prefikset vinner og nettverket ender inne i tunnelen.

Rutelisten er hele politikken. Det er ingen veto per destinasjon bak den, og Builder-referansen lister ingen metode som ville gi en.

Generer komplementet fra prefiksaritmetikk

Komplementet til et utelukket prefiks er mengden av søsknene på veien mot roten av adresetreet. For hver lengde fra 1 opp til den utelukkede lengden tar du blokken av den lengden som inneholder prefikset ditt, snur biten rett under prefikset og skriver ut resultatet. Det høres abstrakt ut til du kjører det.

Utelukkelse av 10.0.0.0/8 gir åtte ruter: 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. Ingen av dem inneholder 10.0.0.0/8, og sammen dekker de alt annet.

Åtte linjer genererer den listen for ethvert 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))

Antall ruter er lik prefikslengden, og det er tallet å følge med på. En enkelt /21-utelukkelse koster 21 ruter og forblir lesbar. Ti utelukkelser kan koste et par hundre, noe som vanligvis betyr at nettverksdesignet må endres, ikke generatoren.

Sjekk adressefamilien og tunnelens omfang

IPv4-ruter gjør ingenting for IPv6. Hvis tunnelen krever en IPv6-adresse, trenger et IPv6 LAN-prefiks sitt eget komplement, generert med samme aritmetikk over 128 bit. En vanlig årsak til at utelukkelsen virker på papiret mens den lokale enheten forblir utilgjengelig, er denne: verten ble nådd over IPv6, og bare IPv4 var skåret ut. Sjekk begge familiene før du konkluderer med at matematikken er feil.

Omfang er den andre halvdelen av sjekken. Android sporer ruting per nettverk og per app, så samme prefiks kan gå gjennom tunnelen for én app og direkte for en annen avhengig av hvordan VPN-en er deklarert.

Plasser DNS- og per-app-regler der de hører hjemme

Ruteutelukkelser endrer ikke resolveren. Et lokalt vertsnavn kan fortsatt besvares av tunnelens DNS-server mens adressen rutes lokalt, og avviket viser seg som et navn som løses til noe LAN-et ikke når. Sett resolverpolitikken bevisst, med et søkedomene eller en regel for delt DNS. Ikke anta at rutene avgjorde det for deg.

addDisallowedApplication() holder én app utenfor tunnelen; det holder ikke en destinasjon utenfor for appene som blir inne. protect() lar en socket du åpner omgå tunnelen, og slik unngår en tunnel å fange sin egen transport. Ingen av dem erstatter komplementet.

Verifiser med et oppslag, ikke med din egen liste

Når tunnelen er oppe, spør enheten hvor en adresse faktisk går i stedet for å lese listen du bygde på nytt. På en build som leverer iproute2, navngir adb shell ip route get 10.1.2.3 grensesnittet kjernen valgte. Kjør samme kommando for en offentlig adresse og bekreft at den navngir tunnelen.

To sjekker til lukker løkken. Slå først opp et lokalt navn, kjør deretter adb shell ip route get på den oppslåtte adressen og krev et grensesnitt som ikke er tunnelen, fordi et vellykket adb shell ping -c 1 host.lan beviser nåbarhet og ikke veien. Gjenta adressefamilietesten over IPv6 hvis tunnelen krever en. Test på API-nivået du leverer, fordi hvordan VPN-nettverk eksponeres for rutetabellen har endret seg mellom utgivelser. En ruteliste som ser riktig ut på papiret og et oppslag som velger feil grensesnitt er forskjellige feil, og bare den andre når brukerne.

Hvis utelukkelseslisten fortsetter å vokse, slutt å generere ruter og omnummerer fjernnettverket eller innsnevre tunnelen i stedet. Generatoren er en omvei for den manglende API-en, ikke et designverktøy.

Relaterte artikler