Nic w VpnService nie przechwytuje ruchu samo z siebie. Prosi system o interfejs tun, przekazuje temu interfejsowi zestaw adresów i tras, i od tego momentu jądro kopiuje każdy pakiet pasujący do tych tras do deskryptora pliku, który aplikacja może czytać. Kod aplikacji nigdy nie widzi pakietów, których trasy nie wybrały, więc zasięg tunelu jest pytaniem o tablicę routingu, zanim staje się pytaniem o programowanie. Dlatego ta sama implementacja usługi może połknąć każdy przepływ na jednym telefonie, a na innym tylko jego część, i dlatego dodanie trasy czasem naprawia tunel, który wyglądał na zepsuty. Dostajesz to, co zadeklarowałeś, minus wszystko, czego budowniczy po cichu odmówił.

Trasy decydują, które pakiety docierają

Builder.addRoute(prefix, prefixLength) instaluje prefiks w tablicy routingu sieci VPN. System traktuje tę sieć jako domyślną dla aplikacji w zasięgu, więc jej wpisy są sprawdzane przed domyślną trasą sieci fizycznej, a pasujący prefiks wystarcza, by przekierować przepływ. Dlatego trasa dla 0.0.0.0/0 bierze IPv4 w całości, łącznie z drukarką, celem cast i stroną administracyjną routera.

Klienci, którzy chcą tego pokrycia bez usuwania fizycznego wpisu domyślnego, używają zamiast tego pary 0.0.0.0/1 i 128.0.0.0/1. Dwie bardziej szczegółowe trasy biją trasę domyślną, razem obejmują całą przestrzeń adresową, a własny wpis domyślny telefonu pozostaje w tablicy dla gniazd wciąż związanych z siecią fizyczną.

Adresy i wpisy DNS pochodzą od tego samego budowniczego i odpowiadają na inne pytanie. addAddress daje interfejsowi adres z długością prefiksu, a addDnsServer instaluje resolver, o który aplikacje w zasięgu będą pytać przez interfejs. Obie rodziny mogą siedzieć na jednym interfejsie, więc zestaw tras tylko IPv4 obok adresu IPv6 to konfiguracja, która czyta się poprawnie i mimo to przecieka. Jeśli kierujesz rodzinę, w której interfejs nie ma adresu, jądro nie ma adresu źródłowego dla tych pakietów i nigdy nie pojawiają się one przy deskryptorze.

Co zwraca establish()

establish() zwraca ParcelFileDescriptor albo null, gdy aplikacja nie jest upoważniona. Null to stan zgody, a nie błąd sieci: VpnService.prepare() musiał już zwrócić null albo użytkownik musiał zatwierdzić okno systemowe przed wywołaniem. Zgodę można wycofać w trakcie działania usługi, a interfejs zamyka się pod tobą bez żadnego wywołania zwrotnego do twojej pętli odczytu.

Deskryptor nie blokuje, o ile nie powiesz inaczej, więc odczyt wraca natychmiast bez danych, zamiast parkować wątek. Pętla napisana dla blokującego gniazda pali jeden rdzeń, a pętla, która pusty odczyt uznaje za krytyczny, rozrywa tunel w jednej sekundzie bezczynności. Builder.setBlocking(true) zmienia to zachowanie, a cena to wątek, który czeka wewnątrz odczytu.

Argumenty trasy i adresu są walidowane jako wartości LinkAddress, a cele multicast, takie jak 224.0.0.0/4, są odrzucane od razu. Interfejs tun punkt-punkt nie ma też domeny rozgłaszania, więc wykrywanie multicast przez niego wymaga innego projektu, a nie jeszcze jednej trasy. Dokumentacja VpnService.Builder to lista, którą trzeba sprawdzić, zanim stwierdzisz, że wywołanie zostało zignorowane.

Zasięg na aplikację zwęża zbiór, nie tunel

Od API 21 budowniczy przyjmuje addAllowedApplication i addDisallowedApplication, a obie listy wykluczają się wzajemnie w jednym budowniczym. Lista dozwolonych sprawia, że tunel istnieje dla tych pakietów, podczas gdy wszystko inne zachowuje sieć fizyczną; lista zabronionych przechwytuje wszystko poza nazwanymi pakietami, co klient oferuje jako tunelowanie dzielone. Przed API 21 nie ma żadnego mechanizmu platformy, a zachowanie na aplikację pochodziło od klientów firm trzecich, którzy filtrowali po UID w swoim własnym przekierowniku. Różnica ma znaczenie przy debugowaniu, bo tunel na aplikację z listą dozwolonych z jednego pakietu prawie nie generuje ruchu i to jest poprawne zachowanie, a nie zepsuta trasa.

Jakikolwiek jest zasięg, gniazdo, które twoja aplikacja otwiera do serwera VPN, nie może podróżować przez tunel, który buduje. VpnService.protect(socket) wiąże to gniazdo z siecią bazową, żeby jego pakiety omijały urządzenie tun. Pomiń wywołanie, a uzgadnianie zostanie połknięte przez interfejs, który miał je podnieść.

VpnService.setUnderlyingNetworks zamyka pętlę, mówiąc systemowi, która sieć fizyczna niesie tunel. Ta wartość decyduje o tym, jak VPN raportuje łączność innym aplikacjom, a wartość null lub nieaktualna jest typową przyczyną połączonego tunelu, który reszta telefonu uznaje za offline. Aktualizuj ją, gdy zmienia się sieć bazowa, nie tylko przy starcie.

Sumy kontrolne i MTU: gdzie poprawna trasa i tak zawodzi

Gdy pakiety już dotrą, dwa szczegóły na poziomie pakietu decydują, czy cokolwiek działa od końca do końca. Pierwszy to adres źródłowy: przy kilku adresach interfejsu jądro może wysyłać pakiety niosące pierwszy, więc przekierownik, który przepisuje nagłówki, musi poprawić adres i sumę kontrolną, która go obejmuje.

Drugi to zasięg sumy kontrolnej. Gdy aplikacja sama buduje pakiety TCP lub UDP, suma kontrolna obejmuje pseudo-nagłówek z adresem źródłowym, adresem docelowym, numerem protokołu i długością transportu, a pominięcie któregokolwiek z nich daje pakiety, które docierają i są odrzucane. Ta awaria wygląda jak przekroczenie czasu po odpowiedzi, która nigdy nie przychodzi, co odsyła większość ludzi do tras. setMtu ustala MTU interfejsu, a każdy odczyt zwraca dokładnie jeden kompletny pakiet IP, więc bufor napisany dla strumienia bajtów pociągnie na kawałki twoje parsowanie, a nie pakiet.

Przebieg weryfikacji, który rozdziela warstwy

Aby znaleźć, gdzie zasięg się pomylił, testuj warstwy po kolei. Zapytaj urządzenie, które trasy naprawdę trzyma sieć VPN, potem sprawdź, czy przepływ do znanego adresu pojawia się jako odczyt przy deskryptorze, a potem sprawdź, czy pakiet wysyłany przez twój przekierownik jest dobrze uformowany. Problem trasy nie pokazuje niczego przy deskryptorze, podczas gdy problem sumy kontrolnej pokazuje pakiet, który dociera i znika.

Jeśli nic nie dociera do deskryptora, podejrzanym jest zestaw tras, a nie czytelnik: potwierdź prefiksy, potwierdź, że istnieje adres w tej samej rodzinie, i potwierdź, że aplikacja jest w zasięgu, który ustawiłeś. Jeśli pakiety docierają do deskryptora, ale nic nie wraca, sprawdź protect() i wartość sieci bazowej, zanim znowu dotkniesz trasy. Jeśli druga strona zgłasza uszkodzone pakiety, suma kontrolna i MTU to miejsca, w które trzeba patrzeć. Jedna próbka na warstwę oszczędza popołudnie zgadywania.

Powiązane artykuły