A VpnService-ben semmi sem fogja meg magától a forgalmat. Egy tun-interfészt kér a rendszertől, átad annak az interfésznek egy sor címet és útvonalat, és attól a pillanattól a kernel minden, az útvonalaknak megfelelő csomagot átmásol egy fájlleíróba, amelyet az alkalmazás olvashat. Az alkalmazás kódja soha nem látja azokat a csomagokat, amelyeket az útvonalak nem választottak ki, ezért az alagút hatóköre útválasztási táblázat kérdése, mielőtt programozási kérdés lenne. Ezért tudja ugyanaz a szolgáltatásmegvalósítás egy telefonon az összes folyamot elnyelni, egy másikon pedig csak egy szeletet, és ezért javít meg egy útvonal hozzáadása néha egy töröttnek látszó alagutat. Azt kapod, amit deklaráltál, mínusz mindaz, amit az építő csendben megtagadott.

Az útvonalak döntik el, mely csomagok érkeznek

Builder.addRoute(prefix, prefixLength) egy előtagot telepít a VPN-hálózat útválasztási táblázatába. A rendszer ezt a hálózatot a hatókörön belüli alkalmazások alapértelmezettjeként kezeli, ezért bejegyzéseit a fizikai hálózat alapértelmezett útvonala előtt nézi, és egy illeszkedő előtag elég egy folyam eltereléséhez. Ezért a 0.0.0.0/0 útvonala a teljes IPv4-et elveszi, beleértve a nyomtatót, a cast-célt és az útválasztó adminisztrációs oldalát.

Azok az ügyfelek, amelyek ezt a lefedettséget a fizikai alapértelmezett bejegyzés törlése nélkül akarják, helyette a 0.0.0.0/1 és 128.0.0.0/1 párost használják. Két specifikusabb útvonal legyőzi az alapértelmezett útvonalat, együtt a teljes címteret lefedik, és a telefon saját alapértelmezett bejegyzése a táblázatban marad azoknak a socketeknek, amelyek még a fizikai hálózathoz vannak kötve.

A címek és a DNS-bejegyzések ugyanattól az építőtől származnak, és más kérdésre válaszolnak. addAddress címet ad az interfésznek egy előtaghosszal, és addDnsServer olyan feloldót telepít, amelyet a hatókörön belüli alkalmazások az interfészen keresztül kérdeznek. Mindkét család ülhet egy interfészen, ezért a csak IPv4-es útvonalkészlet egy IPv6-cím mellett olyan konfiguráció, amely helyesen olvasható, és mégis szivárog. Ha olyan családot irányítasz, amelyben az interfésznek nincs címe, a kernelnek nincs forrás címe ezekhez a csomagokhoz, és soha nem jelennek meg a leírónál.

Mit ad vissza az establish()

establish() egy ParcelFileDescriptor-t ad vissza, vagy null-t, amikor az alkalmazás nincs engedélyezve. A null hozzájárulási állapot, nem hálózati hiba: a VpnService.prepare()-nak már null-t kellett visszaadnia, vagy a felhasználónak a hívás előtt jóvá kellett hagynia a rendszerpárbeszédet. A hozzájárulás visszavonható a szolgáltatás futása közben, és az interfész a lábad alatt zárul be anélkül, hogy bármilyen visszahívás érkezne az olvasóciklusodba.

A leíró nem blokkoló, hacsak nem mondasz mást, ezért az olvasás azonnal visszatér adat nélkül, ahelyett hogy parkolná a szálat. Egy blokkoló sockethez írt ciklus eléget egy magot, és egy ciklus, amely az üres olvasást végzetesnek tekinti, szétszakítja az alagutat egy üres másodperc alatt. Builder.setBlocking(true) megváltoztatja ezt a viselkedést, és az ára egy szál, amely az olvasáson belül vár.

Az útvonal- és címargumentumok LinkAddress-értékként ellenőrzöttek, és a multicast-célpontok, például a 224.0.0.0/4, azonnal elutasítottak. Egy pont-pont tun-interfésznek sincs broadcast-tartománya, ezért a rajta keresztüli multicast-felderítés más tervezést igényel, nem még egy útvonalat. A VpnService.Builder-referencia az a lista, amelyet ellenőrizni kell, mielőtt arra jutnál, hogy egy hívást figyelmen kívül hagytak.

Az alkalmazásonkénti hatókör szűkíti a halmazt, nem az alagutat

Az API 21-től az építő elfogadja a addAllowedApplication-t és a addDisallowedApplication-t, és a két lista egy építőn belül kölcsönösen kizárja egymást. Az engedélyezési lista létezővé teszi az alagutat ezekhez a csomagokhoz, miközben minden más megtartja a fizikai hálózatot; a tiltási lista mindent elkap a megnevezett csomagok kivételével, amit egy ügyfél osztott alagútként kínál. Az API 21 előtt egyáltalán nincs platformmechanizmus, és az alkalmazásonkénti viselkedés harmadik féltől származó ügyfelektől jött, amelyek UID szerint szűrtek a saját továbbítójukon belül. A különbség hibakereséskor számít, mert az egycsomagos engedélyezési listával rendelkező alkalmazásonkénti alagút szinte semmilyen forgalmat nem termel, és ez helyes viselkedés, nem törött útvonal.

Bármekkora is a hatókör, az a socket, amelyet az alkalmazásod a VPN-kiszolgáló felé nyit, nem haladhat át azon az alagúton, amelyet épít. VpnService.protect(socket) ehhez a sockethez köti a mögöttes hálózatot, hogy a csomagjai kihagyják a tun-eszközt. Ha kihagyod a hívást, a kézfogást az az interfész nyeli el, amelynek fel kellett volna hoznia.

VpnService.setUnderlyingNetworks bezárja a kört azzal, hogy megmondja a rendszernek, melyik fizikai hálózat viszi az alagutat. Ez az érték határozza meg, hogyan jelenti a VPN a kapcsolatot a többi alkalmazásnak, és egy null vagy elavult érték rutinszerű oka egy csatlakoztatott alagútnak, amelyet a telefon többi része offline-nak tekint. Frissítsd, amikor a mögöttes hálózat változik, ne csak indításkor.

Ellenőrzőösszegek és MTU: ahol egy helyes útvonal mégis elbukik

Amint a csomagok megérkeznek, két csomagszintű részlet dönti el, működik-e valami végponttól végpontig. Az első a forráscím: több interfészcímmel a kernel olyan csomagokat bocsáthat ki, amelyek az elsőt hordozzák, ezért a fejléceket újraíró továbbítónak ki kell javítania a címet és az azt lefedő ellenőrzőösszeget.

A második az ellenőrzőösszeg hatóköre. Amikor az alkalmazás maga épít TCP- vagy UDP-csomagokat, az ellenőrzőösszeg egy pszeudo-fejlécet fed le, amely a forráscímet, a célcímet, a protokollszámot és a szállítási hosszt tartalmazza, és ezek bármelyikének kihagyása olyan csomagokat ad, amelyek megérkeznek és eldobódnak. Ez a hiba egy soha meg nem érkező válasz utáni időtúllépésnek látszik, ami a legtöbb embert visszaküldi az útvonalakhoz. setMtu rögzíti az interfész MTU-ját, és minden olvasás pontosan egy teljes IP-csomagot ad vissza, ezért a bájtfolyamra írt puffer a feldolgozásodat töredezi, nem a csomagot.

Egy ellenőrzési menet, amely szétválasztja a rétegeket

Ahhoz, hogy megtaláld, hol csúszott el a hatókör, sorrendben teszteld a rétegeket. Kérdezd meg az eszközt, mely útvonalakat tart valójában a VPN-hálózat, aztán ellenőrizd, hogy egy ismert cím felé menő folyam megjelenik-e olvasásként a leírónál, aztán ellenőrizd, hogy a továbbítód által kibocsátott csomag jól formált-e. Az útvonalprobléma semmit sem mutat a leírónál, míg az ellenőrzőösszeg-probléma egy megérkező és eltűnő csomagot mutat.

Ha semmi sem ér el a leíróig, a gyanúsított az útvonalkészlet, nem az olvasó: erősítsd meg az előtagokat, erősítsd meg, hogy van cím ugyanabban a családban, és erősítsd meg, hogy az alkalmazás a beállított hatókörön belül van. Ha a csomagok elérik a leírót, de semmi sem jön vissza, vizsgáld meg a protect()-ot és a mögöttes hálózat értékét, mielőtt újra hozzányúlsz egy útvonalhoz. Ha a távoli oldal hibás csomagokat jelent, az ellenőrzőösszeg és az MTU a néznivaló helyek. Egy próba rétegenként egy délutáni találgatást takarít meg.

Kapcsolódó cikkek