Einen Moment ist der Laptop im Büro-WLAN, im nächsten hängt er an einem Telefon, und der Tunnelindikator ist wieder grün, bevor du mit dem Fensterwechsel fertig bist. Ein grünes Licht meldet nur, dass der Tunnel wieder verbunden ist; was die Routing- und Sperrrichtlinie während der Lücke getan hat, ist eine andere Frage. Diese Richtlinie ist eine Menge von Übereinstimmungsbedingungen, die bei der letzten Installation kompiliert wurden und eine Schnittstelle, eine Adresse und eine Adressfamilie benennen, die die Übergabe gerade entfernt hat. Derselbe Wechsel erzeugt daher drei Ergebnisse, die gleich aussehen: Die Sperre läuft weiter, die Richtlinie blockiert ihre eigene Wiederherstellung, oder es öffnet sich ein Pfad, den nichts abdeckt.

Nichts auf dem Bildschirm unterscheidet diese drei Zustände, frag also, an welche Bezeichner die Regeln gebunden waren, statt welche Farbe der Indikator zeigt. Unter Linux kann eine Regel eine Schnittstelle benennen oder ein ipset, das aus einer gefüllt wird; unter Android kann man einem VpnService über setUnderlyingNetworks sagen, welchen zugrunde liegenden Netzen er folgen soll. Jede Bindung zeigt auf eine Verbindung, die gerade ersetzt wird, und jede muss danach neu bewertet werden.

Was die Übergabe tatsächlich umschreibt

Wenn die alte Verbindung verschwindet, entfernt der Kernel ihre Adressen, zieht die Routen zurück, die durch sie hindurchführten, und baut die Quelladressauswahl aus dem Verbliebenen neu auf. Die Standardroute wandert zum neuen Gateway, und der Schnittstellenindex, auf den die alten Regeln verwiesen, ist weg oder für ein anderes Gerät recycelt. Resolver pro Verbindung werden ebenfalls erneuert, allerdings nicht im selben Augenblick wie die Routen. All das geschieht unterhalb des VPN-Clients, ein Client, der nur seinen eigenen Tunnelzustand beobachtet, sieht nichts davon.

Das Problem ist, dass eine Regel einen bestimmten Bezeichner kodiert statt die Absicht dahinter. Eine Regel, die alles verwirft, was nicht durch die Tunnelschnittstelle hinausgeht, kodiert einen Schnittstellennamen; eine Regel, die den Endpunkt erlaubt, kodiert eine Adresse, die das neue Netz woanders hinführen kann. Wenn der Bezeichner verschwindet, wird die Regel nicht sichtbar falsch, sie trifft entweder nichts mehr oder das Falsche.

Den äußeren Tunnel neu aufbauen, ohne ein Loch zu schlagen

Der äußere Transport war an die alte Quelladresse gebunden, also muss er einen neuen Socket von der neuen aus öffnen. Ist der Endpunkt ein Hostname statt einer wörtlichen Adresse, muss eine Auflösung gelingen, während die Sperre noch gilt. Das ist die Reihenfolgefalle: Eine Richtlinie, die streng genug ist, alles außer dem Endpunkt zu verwerfen, verwirft auch die Auflösung, die nötig ist, um ihn zu finden, es sei denn, die Namensauflösung geschieht, bevor die Sperre angehoben wird, oder wird durch einen noch lebenden Tunnel getragen. Beide Reihenfolgen können funktionieren, aber nur, wenn der Client weiß, in welcher er steckt.

Clients unterscheiden sich hier auf eine Weise, die bei der Diagnose zählt. Manche lösen zuerst auf und installieren die Regeln danach, ein Wechsel kostet dann einen sichtbaren Ausfall und erholt sich anschließend von selbst; andere halten die Sperre aufrecht und verlassen sich darauf, dass der überlebende Tunnel die Auflösung trägt, was völlig scheitert, wenn der Tunnel mit der Verbindung gestorben ist. Eine breite direkte Standardroute hinzuzufügen, um Konnektivität wiederherzustellen, lässt den Test immer bestehen und lässt immer das Leck zurück, das du verhindern wolltest.

Eine Route, die noch auf das alte Gateway zeigt, liefert dieselbe festgefahrene Erholung aus anderer Ursache, also lies die Tabelle, bevor du den Client beschuldigst. Die Befehle unten benennen die Schnittstelle und die Quelladresse, die der Kernel wirklich verwenden würde, und das klärt in einer Zeile, ob der alte Pfad noch installiert ist.

ip route get 1.1.1.1
ip -6 route get 2606:4700:4700::1111

Wenn die neue Verbindung IPv6 bringt und die Regeln nicht

Mobilfunk- und Gastnetze vergeben oft ein globales IPv6-Präfix, selbst wenn das verkabelte LAN im Büro das nie getan hat. Eine Richtlinie, die nur als IPv4-Regeln geschrieben ist, die Standardform der meisten handgebauten iptables- und nftables-Rezepte, deckt davon nichts ab. Der Tunnel bleibt verbunden, Anwendungen mit IPv6-Erreichbarkeit verlassen ihn nach außen, und der Indikator kann den Unterschied nicht zeigen, weil der Tunnel wirklich steht. Der Spiegelfall ist genauso häufig: Ein Regelsatz nur für IPv6 tut auf einem Netz, das kein v6 anbietet, überhaupt nichts.

Vergleiche, was die Verbindung bietet, mit dem, was die Richtlinie erwähnt, auf beiden Seiten der Übergabe. Diese Befehle drucken dieselben Fakten, mit denen der Client arbeitet:

ip -4 route show default
ip -6 route show default
sudo nft list ruleset | grep -c 'ip6 '

Wenn der zweite Befehl eine Standardroute druckt, während der dritte null zurückgibt, kann die neue Verbindung die Maschine über IPv6 verlassen, ohne dass eine Regel darauf achtet. Führe alle drei nach dem Wechsel erneut aus, denn die interessante Zahl ist die Differenz zwischen den beiden Läufen. Druckt der zweite nichts, gilt der umgekehrte Fall, und jede v6-lastige Richtlinie ist dort bloße Dekoration.

Der Resolver wechselt mitten in der Sitzung die Hände

Auf modernen Systemen sind Resolver an Verbindungen gebunden, eine neue Verbindung kommt also mit einem neuen Resolver-Satz und womöglich einer anderen Split-DNS-Sicht. systemd-resolved hält einen Scope pro Verbindung und wählt nach Route zwischen ihnen, während macOS gescopte Resolver aus dem dynamischen Speicher liest und die Schnittstellenreihenfolge beachtet. Ein Client, der seinen eigenen Resolver installiert, konkurriert mit dem der Verbindung, und Prioritätsregeln, die niemand beobachtet, entscheiden den Gewinner. Dann kann die Namensauflösung brechen, während die Paketfilterung genau das tut, wofür sie geschrieben wurde.

Lies den Resolver, der aktiv ist, statt den konfigurierten. Auf einem systemd-Host zeigt der Unterbefehl status, von welcher Verbindung jeder Scope stammt und ob ein private-DNS-Name gilt:

resolvectl status --no-pager
resolvectl query intranet.example.com

Wenn die Abfrage über die physische Verbindung statt über den Tunnel hinausgeht, hat der Client den Prioritätswettstreit verloren, und keine Firewall-Anpassung repariert die Namensgebung. Vergleiche den Abfragepfad auf beiden Seiten der Übergabe, um zu sehen, welcher sich bewegt hat. Ein Name, der auf eine private Adresse auflöst, die das neue Netz nicht routen kann, ist ein dritter Fall, und er zeigt sich als Zeitüberschreitung statt als Fehler.

Übe die Übergabe und führe Protokoll

Erzeuge die Änderung absichtlich, statt auf sie zu warten. Bringe den Tunnel auf der ersten Verbindung hoch, starte eine langlaufende Übertragung und eine wiederholte Auflösung, wechsle dann die Verbindung und beobachte, welche überlebt. Notiere das Intervall zwischen dem Verschwinden der alten Verbindung und der Meldung des Tunnels, dass er verbunden ist, denn in diesem Fenster hat die Richtlinie entweder gehalten oder nicht. Führe die Sequenz auch in der anderen Richtung aus, damit das Ergebnis nicht einseitig ist.

Ausnahmen brauchen ihren eigenen Durchgang, denn eine im alten Netz begründete Umgehung ist im neuen nicht automatisch begründet. Ein Drucker, ein NAS oder ein Captive Portal im Heim-LAN hat keine Entsprechung im Mobilfunk, und die Ausnahme, die ihn erreichte, wird zu einer Regel ohne Entsprechung.

Schreibe die Schnittstellennamen, die angebotenen Familien, den geltenden Resolver und die Länge des Ausfalls für jede Richtung auf. Eine Wiederverbindung beweist, dass der Transport sich erholt hat, nicht dass die Sperre während der Lücke hielt, und das sind getrennte Behauptungen für ein Ticket. Mit diesem Protokoll wird die nächste Übergabe gegen einen bekannten Zustand verglichen statt gegen die Erinnerung.

Ähnliche Artikel