Kein Teil von VpnService fängt von sich aus Datenverkehr ab. Er bittet das System um eine tun-Schnittstelle, übergibt dieser Schnittstelle eine Menge von Adressen und Routen, und von diesem Moment an kopiert der Kernel jedes Paket, das diesen Routen entspricht, in einen Dateideskriptor, den die App lesen kann. Der Code der App sieht nie die Pakete, die die Routen nicht ausgewählt haben, also ist der Umfang eines Tunnels eine Frage der Routingtabelle, bevor sie eine Programmierfrage ist. Deshalb kann dieselbe Dienstimplementierung auf einem Telefon jeden Fluss schlucken und auf einem anderen nur einen Teil, und deshalb repariert eine zusätzliche Route manchmal einen Tunnel, der kaputt aussah. Was Sie bekommen, ist das, was Sie erklärt haben, abzüglich allem, was der Erbauer stillschweigend abgelehnt hat.

Routen entscheiden, welche Pakete ankommen

Builder.addRoute(prefix, prefixLength) trägt ein Präfix in die Routingtabelle des VPN-Netzes ein. Das System behandelt dieses Netz als Standard für die Apps im Geltungsbereich, daher werden seine Einträge vor der Standardroute des physischen Netzes herangezogen, und ein passendes Präfix genügt, um einen Fluss umzuleiten. Eine Route für 0.0.0.0/0 nimmt daher IPv4 vollständig, einschließlich Drucker, Cast-Ziel und Administrationsseite des Routers.

Clients, die diese Abdeckung ohne Löschen des physischen Standardeintrags wollen, verwenden stattdessen das Paar 0.0.0.0/1 und 128.0.0.0/1. Zwei spezifischere Routen schlagen eine Standardroute, zusammen spannen sie den ganzen Adressraum auf, und der eigene Standardeintrag des Telefons bleibt in der Tabelle für die Sockets, die noch an das physische Netz gebunden sind.

Adressen und DNS-Einträge kommen vom selben Erbauer und beantworten eine andere Frage. addAddress gibt der Schnittstelle eine Adresse mit Präfixlänge, und addDnsServer installiert einen Resolver, den Apps im Geltungsbereich über die Schnittstelle abfragen. Beide Familien können auf einer Schnittstelle liegen, also ist eine reine IPv4-Routenmenge neben einer IPv6-Adresse eine Konfiguration, die sich korrekt liest und trotzdem leckt. Wenn Sie eine Familie routen, in der die Schnittstelle keine Adresse hat, hat der Kernel keine Quelladresse für diese Pakete, und sie erscheinen nie am Deskriptor.

Was establish() zurückgibt

establish() gibt einen ParcelFileDescriptor zurück, oder null, wenn die App nicht autorisiert ist. Null ist ein Zustimmungszustand und kein Netzfehler: VpnService.prepare() muss bereits null zurückgegeben haben, oder der Nutzer muss den Systemdialog vor dem Aufruf bestätigt haben. Die Zustimmung kann widerrufen werden, während der Dienst läuft, und die Schnittstelle schließt sich unter Ihnen, ohne Rückruf in Ihre Leseschleife.

Der Deskriptor ist nicht blockierend, sofern Sie nichts anderes sagen, also kehrt ein Lesen sofort ohne Daten zurück, statt den Thread zu parken. Eine Schleife für einen blockierenden Socket verbrennt einen Kern, und eine Schleife, die ein leeres Lesen als fatal behandelt, reißt den Tunnel in einer Leerlaufsekunde ab. Builder.setBlocking(true) ändert dieses Verhalten, und der Preis ist ein Thread, der im Lesen wartet.

Routen- und Adressargumente werden als LinkAddress-Werte validiert, und Multicast-Ziele wie 224.0.0.0/4 werden rundweg abgelehnt. Eine Punkt-zu-Punkt-tun-Schnittstelle hat auch keine Broadcast-Domäne, also braucht Multicast-Erkennung darüber ein anderes Design statt einer weiteren Route. Die VpnService.Builder-Referenz ist die Liste, die Sie prüfen sollten, bevor Sie schließen, dass ein Aufruf ignoriert wurde.

Der App-Bereich verengt die Menge, nicht den Tunnel

Ab API 21 akzeptiert der Erbauer addAllowedApplication und addDisallowedApplication, und die beiden Listen schließen sich innerhalb eines Erbauers gegenseitig aus. Eine Erlaubnisliste lässt den Tunnel für diese Pakete existieren, während alles andere das physische Netz behält; eine Sperrliste fängt alles außer den genannten Paketen, was ein Client als Split-Tunneling anbietet. Vor API 21 gibt es überhaupt keinen Plattformmechanismus, und App-Verhalten kam von Drittanbieter-Clients, die nach UID in ihrem eigenen Weiterleiter filterten. Der Unterschied zählt beim Debuggen, denn ein App-Tunnel mit einer Erlaubnisliste aus einem Paket erzeugt fast keinen Verkehr, und das ist korrektes Verhalten, keine kaputte Route.

Wie der Geltungsbereich auch aussieht, der Socket, den Ihre App zum VPN-Server öffnet, darf nicht durch den Tunnel laufen, den sie aufbaut. VpnService.protect(socket) bindet diesen Socket an das darunterliegende Netz, damit seine Pakete die tun-Schnittstelle überspringen. Lassen Sie den Aufruf weg, und der Handshake wird von der Schnittstelle geschluckt, die er hochbringen soll.

VpnService.setUnderlyingNetworks schließt den Kreis, indem es dem System sagt, welches physische Netz den Tunnel trägt. Dieser Wert bestimmt, wie das VPN Konnektivität an andere Apps meldet, und ein null- oder veralteter Wert ist eine Routineursache für einen verbundenen Tunnel, den der Rest des Telefons als offline behandelt. Aktualisieren Sie ihn, wenn sich das darunterliegende Netz ändert, nicht nur beim Start.

Prüfsummen und MTU: wo eine korrekte Route trotzdem scheitert

Sobald Pakete ankommen, entscheiden zwei Details auf Paketebene, ob etwas Ende zu Ende funktioniert. Das erste ist die Quelladresse: bei mehreren Schnittstellenadressen kann der Kernel Pakete mit der ersten senden, also muss ein Weiterleiter, der Header umschreibt, die Adresse und die sie abdeckende Prüfsumme korrigieren.

Das zweite ist der Umfang der Prüfsumme. Wenn die App TCP- oder UDP-Pakete selbst baut, deckt die Prüfsumme einen Pseudo-Header mit Quelladresse, Zieladresse, Protokollnummer und Transportlänge ab, und eines davon wegzulassen ergibt Pakete, die ankommen und verworfen werden. Dieser Fehler sieht wie ein Timeout nach einer Antwort aus, die nie kommt, was die meisten Leute zurück zu den Routen schickt. setMtu setzt die MTU der Schnittstelle, und jedes Lesen gibt genau ein vollständiges IP-Paket zurück, also fragmentiert ein Puffer, der für einen Bytestrom geschrieben wurde, Ihr Parsen statt das Paket.

Ein Prüfdurchgang, der die Schichten trennt

Um zu finden, wo der Geltungsbereich schiefging, prüfen Sie die Schichten der Reihe nach. Fragen Sie das Gerät, welche Routen das VPN-Netz tatsächlich hält, prüfen Sie dann, ob ein Fluss zu einer bekannten Adresse als Lesen am Deskriptor erscheint, und prüfen Sie dann, ob das Paket, das Ihr Weiterleiter sendet, wohlgeformt ist. Ein Routenproblem zeigt nichts am Deskriptor, während ein Prüfsummenproblem ein Paket zeigt, das ankommt und verschwindet.

Wenn nichts den Deskriptor erreicht, ist die Routenmenge verdächtig und nicht der Leser: bestätigen Sie die Präfixe, bestätigen Sie, dass es eine Adresse in derselben Familie gibt, und bestätigen Sie, dass die App im gesetzten Geltungsbereich liegt. Wenn Pakete den Deskriptor erreichen, aber nichts zurückkommt, prüfen Sie protect() und den Wert des darunterliegenden Netzes, bevor Sie wieder eine Route anfassen. Wenn die Gegenseite fehlerhafte Pakete meldet, sind Prüfsumme und MTU die Stellen zum Nachsehen. Eine Sonde pro Schicht spart einen Nachmittag Raten.

Ähnliche Artikel