La VpnService de Android havas neniun ekskludan itineron, kaj neniu kvanto da API-legado produktos unu. Builder.addRoute() ĉiam nur aldonas celojn al la tunelo, do teni LAN-segmenton loka signifas, ke tiu segmento neniam eniras la itinerliston. Kion vi anstataŭe aldonas, estas la komplemento de la segmento, esprimita per ordinaraj prefiksoj. La laboro estas aritmetiko, kaj ĝi malsukcesas en kelkaj antaŭvideblaj lokoj.

Trairu la subajn kontrolojn laŭ ordo. Ĉiu supozas, ke la antaŭa validis, do haltu ĉe la unua malsukceso anstataŭ fliki simptomojn pli malsupre.

Komencu de tio, kion la tunelo postulas

Transdoni 0.0.0.0/0 al addRoute() diras al Android, ke ĉiu IPv4-celo apartenas al la tunelo. Tial tiom da klientoj ŝajnas nekapablaj je dividita tunelado: ili postulas la tutan adresspacon kaj poste provas subtrahi el ĝi. Nenio en la konstruilo subtrahas. Pli specifa itinero por la reto, kiun vi volis teni loka, faras la malon de tio, kion homoj atendas, ĉar la plej longa kongrua prefikso venkas kaj tiu reto finiĝas ene de la tunelo.

La itinerlisto estas la tuta politiko. Malantaŭ ĝi ne estas vetoo por celo, kaj la Builder-referenco listigas neniun metodon, kiu provizus ĝin.

Generu la komplementon el prefiksa aritmetiko

La komplemento de ekskludita prefikso estas la aro de ĝiaj gefratoj sur la vojo al la radiko de la adresarbo. Por ĉiu longo de 1 ĝis la ekskludita longo, prenu la blokon de tiu longo enhavantan vian prefikson, renversu la biton tuj sub la prefikso kaj eligi la rezulton. Ĝi sonas abstrakta ĝis vi rulas ĝin.

Ekskludi 10.0.0.0/8 donas ok itinerojn: 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. Neniu el ili enhavas 10.0.0.0/8, kaj kune ili kovras ĉion alian.

Ok linioj generas tiun liston por ajna prefikso:

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

La nombro da itineroj egalas la prefikslongon, kaj tio estas la nombro gardenda. Unu ekskludo /21 kostas 21 itinerojn kaj restaslegebla. Dek ekskludoj povas kosti kelkcent, kio kutime signifas, ke la retdezajno bezonas ŝanĝon, ne la generatoro.

Kontrolu la adresfamilion kaj la tunelan amplekson

IPv4-itineroj faras nenion por IPv6. Se la tunelo postulas IPv6-adreson, IPv6-LAN-prefikso bezonas sian propran komplementon, generitan per la sama aritmetiko super 128 bitoj. Ofta kaŭzo, ke la ekskludo funkcias surpapera dum la loka aparato restas neatingebla, estas ĉi tio: la gastiganto estis atingita per IPv6, kaj nur IPv4 estis eltranĉita. Kontrolu ambaŭ familiojn antaŭ ol konkludi, ke la matematiko malpravas.

Amplekso estas la alia duono de la kontrolo. Android spuras enkursigon laŭ reto kaj laŭ aplikaĵo, do la sama prefikso povas iri tra la tunelo por unu aplikaĵo kaj rekta por alia, depende de kiel la VPN estis deklarita.

Metu DNS- kaj po-aplikaĵajn regulojn tie, kie ili apartenas

Itinerekskludoj ne ŝanĝas la solvilon. Loka gastigantnomo ankoraŭ povas esti respondata de la DNS-servilo de la tunelo dum ĝia adreso estas enkursigata loke, kaj la malkongruo montriĝas kiel nomo, kiu solviĝas al io, kion la LAN ne povas atingi. Starigu la solvilan politikon intence, per serĉdomajno aŭ regulo de dividita DNS. Ne supozu, ke la itineroj decidis tion por vi.

addDisallowedApplication() tenas unu aplikaĵon ekster la tunelo; ĝi ne tenas celon ekster por la aplikaĵoj, kiuj restas ene. protect() lasas ingon, kiun vi malfermas, preteriri la tunelon, kaj tiel tunelo evitas kapti sian propran transporton. Neniu el la du anstataŭas la komplementon.

Kontrolu per serĉo, ne per via propra listo

Kiam la tunelo estas supren, demandu la aparaton, kien adreso efektive iras, anstataŭ relegi la liston, kiun vi konstruis. Sur konstruo, kiu liveras iproute2, adb shell ip route get 10.1.2.3 nomas la interfacon, kiun la kerno elektis. Rulu la saman komandon por publika adreso kaj konfirmu, ke ĝi nomas la tunelon.

Du pliaj kontroloj fermas la ciklon. Unue solvu lokan nomon, poste rulu adb shell ip route get sur la solvitan adreson kaj postulu interfacon, kiu ne estas la tunelo, ĉar sukcesa adb shell ping -c 1 host.lan pruvas atingeblon kaj ne la vojon. Ripetu la adresfamilian teston super IPv6, se la tunelo postulas unu. Testu ĉe la API-nivelo, kiun vi liveras, ĉar la maniero, kiel VPN-retaj retoj estas elmetitaj al la enkursiga tabelo, ŝanĝiĝis tra eldonoj. Itinerlisto, kiu aspektas ĝusta surpapera, kaj serĉo, kiu elektas la malĝustan interfacon, estas malsamaj cimoj, kaj nur la dua atingas uzantojn.

Se la ekskluda listo daŭre kreskas, ĉesu generi itinerojn kaj renumero la malproksiman reton aŭ mallarĝigu la tunelon anstataŭe. La generatoro estas provizora solvo por la mankanta API, ne dezajna ilo.

Rilataj artikoloj