Android VpnService neturi pašalinimo maršruto, ir joks API skaitymo kiekis jo nesukurs. Builder.addRoute() visada prideda tik paskirties vietas į tunelį, todėl LAN segmento palaikymas vietiniu reiškia, kad tas segmentas niekada nepatenka į maršrutų sąrašą. Vietoj to pridedate segmento papildinį, išreikštą įprastais prefiksais. Darbas yra aritmetika, ir ji nepavyksta keliose nuspėjamose vietose.

Atlikite žemiau esančias patikras iš eilės. Kiekviena daro prielaidą, kad ankstesnė pasitvirtino, todėl sustokite ties pirmuoju nepavykimu, o ne lopydami simptomus žemiau.

Pradėkite nuo to, ką tunelis teigia

0.0.0.0/0 perdavimas į addRoute() pasako Android, kad kiekviena IPv4 paskirties vieta priklauso tuneliui. Štai kodėl tiek daug klientų atrodo nepajėgūs skaidyto tunelio: jie reikalauja visos adresų erdvės ir tada bando iš jos atimti. Kurtuve niekas neatima. Konkretesnis maršrutas tinklui, kurį norėjote palikti vietinį, daro priešingai, nei tikimasi, nes laimi ilgiausias sutampantis prefiksas ir tas tinklas atsiduria tunelio viduje.

Maršrutų sąrašas yra visa politika. Už jo nėra veto kiekvienai paskirties vietai, o Builder nuoroda neišvardija jokio metodo, kuris jį suteiktų.

Sugeneruokite papildinį iš prefiksų aritmetikos

Pašalinto prefikso papildinys yra jo brolių ir seserų rinkinys kelyje į adresų medžio šaknį. Kiekvienam ilgiui nuo 1 iki pašalinto ilgio paimkite to ilgio bloką, kuriame yra jūsų prefiksas, apverskite bitą iškart po prefiksu ir išveskite rezultatą. Tai skamba abstrakčiai, kol nepaleidžiate.

Pašalinus 10.0.0.0/8 gaunami aštuoni maršrutai: 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. Nė vienas jų neturi 10.0.0.0/8, o kartu jie apima visa kita.

Aštuonios eilutės generuoja tą sąrašą bet kuriam prefiksui:

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

Maršrutų skaičius lygus prefikso ilgiui, ir tai yra skaičius, kurį reikia stebėti. Vienas /21 pašalinimas kainuoja 21 maršrutą ir lieka įskaitomas. Dešimt pašalinimų gali kainuoti kelis šimtus, o tai paprastai reiškia, kad keisti reikia tinklo dizainą, o ne generatorių.

Patikrinkite adresų šeimą ir tunelio apimtį

IPv4 maršrutai nieko nedaro IPv6. Jei tunelis reikalauja IPv6 adreso, IPv6 LAN prefiksui reikia savo papildinio, sugeneruoto ta pačia aritmetika per 128 bitus. Dažna priežastis, kodėl pašalinimas veikia popieriuje, o vietinis įrenginys lieka nepasiekiamas, yra ši: prie hosto prieita per IPv6, o iškirptas buvo tik IPv4. Patikrinkite abi šeimas prieš darydami išvadą, kad matematika neteisinga.

Apimtis yra kita patikros pusė. Android seka maršrutizavimą pagal tinklą ir pagal programą, todėl tas pats prefiksas gali eiti per tunelį vienai programai ir tiesiogiai kitai, priklausomai nuo to, kaip VPN buvo deklaruotas.

Padėkite DNS ir pagal programą taisykles ten, kur jos priklauso

Maršrutų pašalinimai nekeičia resolverio. Vietinis hosto vardas vis dar gali būti atsakytas tunelio DNS serverio, kol jo adresas maršrutizuojamas vietiškai, ir neatitikimas pasireiškia kaip vardas, kuris sprendžiamas į kažką, ko LAN nepasiekia. Nustatykite resolverio politiką sąmoningai, su paieškos domenu arba skaidyto DNS taisykle. Nepamanykite, kad maršrutai tai nusprendė už jus.

addDisallowedApplication() laiko vieną programą už tunelio ribų; jis nelaiko paskirties vietos lauke toms programoms, kurios lieka viduje. protect() leidžia jūsų atidarytam lizdui apeiti tunelį, ir taip tunelis išvengia savo paties transporto užfiksavimo. Nė vienas iš jų nepakeičia papildinio.

Tikrinkite paieška, o ne savo sąrašu

Kai tunelis pakeltas, paklauskite įrenginio, kur adresas iš tikrųjų eina, o ne skaitykite savo sudarytą sąrašą iš naujo. Kūrime, kuriame yra iproute2, adb shell ip route get 10.1.2.3 įvardija sąsają, kurią pasirinko branduolys. Paleiskite tą pačią komandą viešajam adresui ir patvirtinkite, kad ji įvardija tunelį.

Dar dvi patikros uždaro ciklą. Pirmiausia išspręskite vietinį vardą, tada paleiskite adb shell ip route get išspręstam adresui ir reikalaukite sąsajos, kuri nėra tunelis, nes sėkmingas adb shell ping -c 1 host.lan įrodo pasiekiamumą, o ne kelią. Pakartokite adresų šeimos testą per IPv6, jei tunelis jos reikalauja. Testuokite tame API lygyje, kurį išleidžiate, nes tai, kaip VPN tinklai atskleidžiami maršrutizavimo lentelei, keitėsi tarp versijų. Maršrutų sąrašas, kuris popieriuje atrodo teisingai, ir paieška, pasirenkanti netinkamą sąsają, yra skirtingos klaidos, ir tik antroji pasiekia naudotojus.

Jei pašalinimų sąrašas vis auga, nustokite generuoti maršrutus ir pernumeruokite nuotolinį tinklą arba vietoj to susiaurinkite tunelį. Generatorius yra trūkstamo API apėjimas, o ne dizaino įrankis.

Susiję straipsniai