VpnService i Android har inga utelukkingsrute, og inga mengd API-lesing vil produsere ei. Builder.addRoute() legg alltid berre til destinasjonar i tunnelen, så å halde eit LAN-segment lokalt tyder at segmentet aldri kjem inn i rutelista. Det du legg til i staden er komplementet til segmentet, uttrykt som vanlege prefiks. Arbeidet er aritmetikk, og det feilar på nokre få føreseielege stader.

Gå gjennom sjekkane nedanfor i rekkjefølgje. Kvar føreset at den førre heldt, så stopp ved den første feilen i staden for å lappe symptom lenger nede.

Start med det tunnelen hevdar

Å sende 0.0.0.0/0 til addRoute() fortel Android at kvar IPv4-destinasjon høyrer til tunnelen. Derfor ser så mange klientar ut til å vere ute av stand til delt tunnelering: dei krev heile adresseområdet og prøver så å trekkje frå det. Ingenting i byggjaren trekkjer frå. Ei meir spesifikk rute for nettverket du ville halde lokalt gjer det motsette av det folk ventar, fordi det lengste samsvarande prefikset vinn og nettverket hamnar inne i tunnelen.

Rutelista er heile politikken. Det er inga veto per destinasjon bak henne, og Builder-referansen listar ingen metode som ville gje ei.

Generer komplementet frå prefiksaritmetikk

Komplementet til eit utelukka prefiks er mengda av syskena på vegen mot rota av adresetreet. For kvar lengd frå 1 opp til den utelukka lengda tek du blokka av den lengda som inneheld prefikset ditt, snur biten rett under prefikset og skriv ut resultatet. Det høyrest abstrakt ut til du køyrer det.

Utelukking av 10.0.0.0/8 gjev å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 dei inneheld 10.0.0.0/8, og saman dekkjer dei alt anna.

Åtte linjer genererer den lista for eit kva prefiks som helst:

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

Talet på ruter er lik prefikslengda, og det er talet å fylgje med på. Ei einsleg /21-utelukking kostar 21 ruter og held seg leseleg. Ti utelukkingar kan koste eit par hundre, noko som vanlegvis tyder at nettverksdesignet må endrast, ikkje generatoren.

Sjekk adressefamilien og omfanget av tunnelen

IPv4-ruter gjer ingenting for IPv6. Viss tunnelen hevdar ei IPv6-adresse, treng eit IPv6 LAN-prefiks sitt eige komplement, generert med same aritmetikk over 128 bit. Ei vanleg årsak til at utelukkinga verkar på papiret medan den lokale eininga blir verande utilgjengeleg, er denne: verten vart nådd over IPv6, og berre IPv4 var skoren ut. Sjekk begge familiane før du konkluderer med at matematikken er feil.

Omfang er den andre halvdelen av sjekken. Android sporar ruting per nettverk og per app, så same prefiks kan gå gjennom tunnelen for éin app og direkte for ein annan, avhengig av korleis VPN-en er deklarert.

Plasser DNS- og per-app-reglar der dei høyrer heime

Ruteutelukkingar endrar ikkje resolvaren. Eit lokalt vertsnamn kan framleis bli svara av DNS-tenaren til tunnelen medan adressa blir ruta lokalt, og avviket viser seg som eit namn som løyser seg til noko LAN-et ikkje når. Set resolverpolitikken medvite, med eit søkjedomene eller ei regel for delt DNS. Ikkje gå ut frå at rutene avgjorde det for deg.

addDisallowedApplication() held éin app utanfor tunnelen; det held ikkje ein destinasjon utanfor for appane som blir inne. protect() lèt ein socket du opnar omgå tunnelen, og slik unngår ein tunnel å fange sin eigen transport. Ingen av dei erstattar komplementet.

Stadfest med eit oppslag, ikkje med di eiga liste

Når tunnelen er oppe, spør eininga kvar ei adresse faktisk går i staden for å lese lista du bygde på nytt. På ein build som leverer iproute2, namngjev adb shell ip route get 10.1.2.3 grensesnittet kjernen valde. Køyr same kommando for ei offentleg adresse og stadfest at han namngjev tunnelen.

To sjekkar til lukkar løkka. Slå først opp eit lokalt namn, køyr deretter adb shell ip route get på den oppslåtte adressa og krev eit grensesnitt som ikkje er tunnelen, fordi eit vellukka adb shell ping -c 1 host.lan provar nåbarheit og ikkje vegen. Gjenta adressefamilietesten over IPv6 viss tunnelen krev ein. Test på API-nivået du leverer, fordi korleis VPN-nettverk blir eksponerte for rutetabellen har endra seg mellom utgjevingar. Ei ruteliste som ser riktig ut på papiret og eit oppslag som vel feil grensesnitt er ulike feil, og berre den andre når brukarane.

Viss utelukkingslista held fram med å vekse, slutt å generere ruter og omnummerer fjernnettverket eller snevre inn tunnelen i staden. Generatoren er ein omveg for den manglande API-en, ikkje eit designverktøy.

Relaterte artiklar