In der Welt der Netzwerktechnologien gibt es immer wieder spannende Phänomene, die auf den ersten Blick verwirrend wirken. Ein solches Beispiel ist der Zustand, wenn IPv4-Adressen eigentlich keine klassischen IPv4-Adressen mehr sind, sondern als IPv6-Adressen getarnt daherkommen. Dieses Verhalten ist keineswegs ein Fehler oder ein Bug, sondern eine bewusste Technik, die insbesondere im modernen Netzwerk-Stack und in Verbindung mit sogenannten Dual-Mode-Sockets zum Tragen kommt. Insbesondere in Umgebungen, in denen Ergänzungen durch eBPF (extended Berkeley Packet Filter) vorgenommen werden, treten hier interessante Herausforderungen und Erkenntnisse zutage. Doch warum und wie kann IPv4 überhaupt „IPv6 spielen“? Dieser Artikel nimmt Sie mit auf eine spannende Reise durch die Tiefen der Netzwerkprogrammierung und erklärt anhand eines realen Fallbeispiels, wie diese Technik funktioniert und welche Auswirkungen sie auf die Netzwerkanalyse und die Paketfilterung hat.
Der Ursprung des Rätsels liegt in einem praktischen Anwendungsfall: Eine eBPF-Programmierung, die den Netzwerkverkehr eines Systems beeinflussen und gezielt bestimmte Verbindungen umleiten möchte. Konkret bestand das Ziel darin, DNS-Anfragen (Port 53) für einzelne Programme oder Container transparent umzuschreiben, um sie an einen sogenannten Man-in-the-Middle (MITM) DNS-Proxy umzuleiten. Hierfür wurde das eBPF-Hook BPF_CGROUP_INET4_CONNECT verwendet, welches Verbindungsversuche über IPv4 beim Verbindungsaufbau abfangen kann. Diese Herangehensweise schien in einer Umgebung ohne IPv6-Unterstützung vollständig ausreichend zu sein, denn eigentlich wurde nur IPv4-Traffic erwartet. Doch schon bald traten unerwartete Probleme auf, insbesondere beim Einsatz der dotnet CLI.
Bestimmte Befehle, wie das Hinzufügen von Paketen, blieben hängen und im Log tauchten immer wieder Meldungen über abgelehnte IPv6-Pakete auf. Diese überraschende Beobachtung führte zu intensiven Untersuchungen, denn auf den ersten Blick schien das System keine IPv6-Verbindungen zu initiieren. Der eBPF-Programmteil, der für IPv4-Verbindungen zuständig war, wurde nicht ausgelöst, das Monitoring zeigte jedoch eingehende IPv6-Events. Netzwerkanalysetools wie Wireshark bestätigten zudem, dass der tatsächliche Netzwerkverkehr nur über IPv4 lief. Es entstand somit eine unerwartete Diskrepanz zwischen der Sicht auf Kernel-/eBPF-Ebene und der tatsächlichen Netzwerklage.
Die Lösung des Rätsels führt tief in die Dual-Stack-Technologie und insbesondere in die Funktionsweise von Dual-Mode-Sockets. Seit .NET 5 nutzt der SocketsHttpHandler sogenannte DualMode-Sockets. Das bedeutet, dass ein einzelner IPv6-Socket in der Lage ist, sowohl IPv6- als auch IPv4-Verbindungen zu handhaben. Dabei wird IPv4-Traffic über speziell kodierte IPv6-Adressen abgewickelt, die als IPv4-mapped IPv6-Adressen bezeichnet werden.
Das ist eine Technik, die es erlaubt, eine IPv4-Adresse innerhalb eines IPv6-Adressraums darzustellen. Bei einer IPv4-mapped IPv6-Adresse handelt es sich konkret um eine IPv6-Adresse, deren letztes 32-Bit-Feld die eigentliche IPv4-Adresse enthält. Die Bytes davor füllen einen festgelegten Präfix, der aus null Byteabschnitten und zwei Bytes mit dem Wert 0xffff besteht. Ein klassisches Beispiel ist ::ffff:192.168.
1.1 – hierbei repräsentieren die letzten 32 Bits die IPv4-Adresse 192.168.1.1.
Dieses Konzept ist standardisiert und wird in modernen Betriebssystem-Stacks unterstützt, um Interoperabilität zwischen IPv4 und IPv6 zu gewährleisten. Für eBPF-Programme, die ursprünglich getrennt auf IPv4- und IPv6-Verkehr ausgelegt sind, ergibt sich hierdurch eine komplexe Herausforderung. Wenn ein Prozess, wie die dotnet CLI, eine IPv4-Verbindung über einen DualMode-Socket initiiert, erscheint dies auf Systemebene zunächst als ein IPv6-Verbindungsversuch – der Socket ist schließlich ein IPv6-Socket. Das eBPF-Programm, das im cgroup-Protokoll den connect-Event abfängt, empfängt einen IPv6-Hook, auch wenn die tatsächlich verwendete Zieladresse eine IPv4-mapped IPv6-Adresse ist. Dies kann dazu führen, dass rein auf IPv4 programmierte Filter und Umleitungen versagen oder nicht ausgelöst werden, obwohl der dahinterliegende Netzwerkverkehr tatsächlich IPv4-basiert ist.
Zudem werden eBPF-basierte Paketfilterungen, die IPv6-Verkehr strikt blockieren, durch diese Konstruktion ebenfalls getäuscht und blockieren unbeabsichtigt legitimen IPv4-Traffic, da er scheinbar IPv6 ist. Die Lösung dieses Problems erfordert eine Anpassung der eBPF-Programme. Anstatt sich ausschließlich auf IPv4 oder IPv6 zu konzentrieren, muss die IPv6-Variante nun in der Lage sein, IPv4-mapped Adressen zu erkennen und entsprechend zu behandeln – etwa indem die korrekte Extrahierung der echten IPv4-Adresse aus dem IPv6-Feld erfolgt. Dafür werden in eBPF Socketspezifische Felder verwendet, die das IPv6-Bitfeld als vier 32-Bit-Werte verwalten. Hierbei entspricht das letzte Feld (Index 3) der integrierten IPv4-Adresse.
Die erste dreiviertel Adresse hat dabei den Präfix mit Null-Bytes und den 0xffff-Werten. Der eBPF-Code muss nun überprüfen, ob die Adresse diesem Muster entspricht und gegebenenfalls die IPv4-Adresse extrahieren und die Umleitungen entsprechend durchführen. Zusätzlich zum Erkennen und Umleiten wird als eine weitere wichtige Maßnahme die differenziertere Prüfung des Netzwerkpackets im egress-Socket-Programm notwendig. Die einfache Abfrage des Socket-Familientyps reicht hier nicht aus, da der Socket zwar als IPv6 deklariert ist (AF_INET6), das angewendete Protokoll jedoch tatsächlich IPv4 ist. Eine Überprüfung des Ethernet-Protokolltyps (ETH_P_IPV6) auf dem skb-Buffer kann hier entscheidend sein.
Nur echte IPv6-Pakete werden auf diese Weise erkannt und weiter blockiert, während IPv4-mapped IPv6 Traffic durchgelassen wird und korrekt behandelt werden kann. Das beschriebene Phänomen zeigt eindrucksvoll die Komplexität moderner Netzwerkarchitekturen und die Bedeutung, die eBPF als leistungsfähiges Instrument zur Netzwerküberwachung und -manipulation im Kernel hat. Es demonstriert auch, wie wichtige Details wie die DualMode-Funktionalität moderner Bibliotheken und Frameworks Einfluss auf die Netzwerkinfrastruktur und deren Kontrolle haben können. Für Entwickler und Administratoren, die eBPF-Programme schreiben oder betreiben, ist es essenziell, diese Feinheiten zu verstehen, um Fehlfunktionen oder unerwartetes Verhalten zu vermeiden. Im Zweifel empfiehlt sich eine eingehende Analyse der verwendeten Netzwerk-Stacks und Implementierungen, insbesondere bei der Unterstützung von IPv6 und Dual-Mode-Sockets.
Eventlog-Mechanismen, wie das bpf_ringbuf_output, sind dabei unverzichtbare Hilfsmittel, um den Paketfluss auf Kernel-Ebene sichtbar zu machen und Fehlersituationen nachvollziehen zu können. Abschließend zeigt die Geschichte, dass IPv4 in der heutigen vernetzten Welt viel mehr als nur eine einfache IP-Adresse ist. Durch Mechanismen wie IPv4-mapped IPv6 Adressen verschiebt sich die Bedeutung von IP-Protokollen und ihrer Implementierung. eBPF bietet hier die Flexibilität, diese komplexen Verbindungen zu erkennen und zu steuern, erfordert aber auch ein tiefgehendes Verständnis der zugrundeliegenden Protokolle und Implementierungen. Wenn es also heißt „Wann ist IPv4 nicht IPv4?“, dann lautet die Antwort schlicht: Wenn es sich temporär als IPv6 tarnt, um von DualMode-Sockets verwaltet und letztlich vom Kernel über eine IPv4-Verbindung abgewickelt zu werden.
Dies zeigt auf überzeugende Weise, wie sich noch vermeintlich bekannte Technologien durch innovative Lösungen weiterentwickeln und anpassen. Die genannte Erfahrung liefert eine wichtige Lehre für alle, die in der Netzwerkprogrammierung und Systementwicklung tätig sind – flexible und umfassende Lösungen sind der Schlüssel zur Bewältigung moderner Netzwerk-Herausforderungen.