Implementierung eines ICMP-Tunnels im Linux-Kernel: Kapselung von TCP/UDP in Echo-Anfragen
Ein ICMP-Tunnel auf Kernel-Ebene unter Linux ermöglicht die Kapselung von TCP/UDP-Traffic in ICMP-Echo-Request-Paketen und umgeht so Standard-Netzwerkfilter. Dieser Ansatz eignet sich hervorragend zur Erforschung des Netzwerk-Stacks, zum Testen der Paketinterception und zum Verständnis des Funktionsweises von Netfilter. Der Artikel zerlegt die Architektur des Kernel-Moduls auf, das die Traffic-Übersetzung über die Hooks NF_INET_POST_ROUTING und NF_INET_LOCAL_IN mittels Tasklets für asynchrones Senden umsetzt.
ICMP-Tunnel-Architektur im Linux-Kernel
Das Modul besteht aus drei zentralen Komponenten: zwei Netfilter-Hooks und einem Tasklet für das Senden von Paketen. Der NF_INET_POST_ROUTING-Hook fängt ausgehende TCP/UDP-Pakete ab, bevor sie ans Netzwerk übergeben werden, wandelt sie in ICMP-Echo-Requests um und übergibt die Steuerung an den Tasklet. Der zweite Hook – NF_INET_LOCAL_IN – erfasst eingehende ICMP-Pakete, extrahiert den ursprünglichen Traffic und leitet ihn an die Loopback-Schnittstelle weiter, damit er an die Anwendungen geliefert werden kann. Der Tasklet übernimmt das verschobene Paket-Senden über dev_queue_xmit und minimiert so Blockierungen im Interrupt-Kontext.
Die Hook-Registrierung erfolgt mit der Priorität NF_IP_PRI_FIRST, um eine frühe Verarbeitung zu gewährleisten. Dies ist entscheidend, da Störungen durch andere Module davor die Integrität der Kapselung oder Dekapselung beeinträchtigen könnten. Die Speicherverwaltung basiert auf sk_buff – der zentralen Struktur des Linux-Netzwerk-Stacks –, die eine sorgfältige Reservierung von Header-Platz und eine ordnungsgemäße Freigabe über kfree_skb bei Fehlern erfordert.
Detaillierte Aufschlüsselung von output_hook und create_packet_output
Die Funktion output_hook wird für jedes ausgehende Paket aufgerufen. Sie delegiert die Erstellung der ICMP-Hülle an die Funktion create_packet_output, die:
- Prüft, ob es sich um TCP oder UDP handelt – andernfalls gibt sie NULL zurück, und das Paket setzt seinen Weg unverändert fort.
- Ermittelt die MAC-Adresse des Empfängers über
__ipv4_neigh_lookup, die für den Aufbau des Ethernet-Headers benötigt wird. - Linearisiert den
sk_buffmitskb_linearize, da nachfolgende Datenkopiervorgänge einen kontinuierlichen Puffer erfordern. - Bestimmt den Transportprotokolltyp (0 für UDP, 1 für TCP), die Header-Länge und die Payload-Größe.
- Allokiert einen neuen
sk_buffunter Berücksichtigung des reservierten Platzes unter den Headers (LL_RESERVED_SPACE) und der benötigten Tailroom (needed_tailroom). - Baut schrittweise die Headers auf: Ethernet, IPv4, ICMP, kopiert den Transport-Header und die Daten in den ICMP-Body.
- Berechnet die Checksums für IP und ICMP mit
ip_fast_csumundip_compute_csum. - Gibt das fertige Paket zum Senden zurück.
Ein wichtiger Aspekt ist die Nutzung des Felds icmp->un.echo.id, um Dienstinformationen über den ursprünglichen Protokolltyp und den Stream-Identifier zu übermitteln. Dadurch kann die empfangende Seite das Originalpaket ohne zusätzliche Metadaten außerhalb der ICMP-Struktur korrekt rekonstruieren.
Umgang mit eingehenden Paketen und Loopback-Routing
Auf der empfangenden Seite analysiert input_hook jedes eingehende ICMP-Paket. Passt es zum Tunnel-Format (Echo Request mit bekanntem Muster im ID-Feld), extrahiert das Modul:
- Den Transport-Header und die Daten aus dem ICMP-Body.
- Einen neuen
sk_buff, der ein eingehendes TCP/UDP-Paket simuliert. - Die Ziel-Schnittstelle auf Loopback (
dev_set_name(skb, "lo")). - Die MAC-Adressen auf Localhost und ruft
netif_rx_ni()auf, um das Paket in den Netzwerk-Stack einzuspritzen.
Dadurch verarbeitet der Kernel das Paket so, als käme es von außen, allerdings über die lo-Schnittstelle. Anwendungen erhalten die Daten unverändert, ohne die Kapselung zu bemerken. Wichtig: NF_STOLEN wird hier nicht verwendet – das Paket wird nicht "gestohlen", sondern ersetzt, daher wird nach erfolgreicher Injektion NF_ACCEPT zurückgegeben.
Kritische technische Details und Einschränkungen
Die Implementierung stößt auf mehrere technische Limitationen:
- Fragmentierung: ICMP-Pakete können unterwegs fragmentiert werden, doch die aktuelle Umsetzung behandelt keine Fragmente – sie geht von einer ausreichenden MTU für die vollständige Kapselung aus.
- Sicherheit: Fehlende Verschlüsselung oder Authentifizierung macht den Tunnel anfällig für Spoofing und MITM-Angriffe.
- Performance: Jedes Paket erfordert die Allokation eines neuen
sk_buff, Datenkopieren und Checksum-Berechnung – das erzeugt CPU-Overhead. - Nur IPv4: Das Modul unterstützt kein IPv6, da es
ip_hdrund__ipv4_neigh_lookupverwendet. - MAC-Abhängigkeit: Es benötigt einen ARP-Eintrag für die Ziel-IP; andernfalls wird das Paket verworfen.
Trotz dieser Einschränkungen ist das Projekt hervorragend zum Lernen: Es deckt Netfilter-Operationen, Kernel-Speicherverwaltung, Netzwerk-Header-Aufbau und Echtzeit-Interaktionen auf OSI-Ebene ab.
Wichtige Erkenntnisse
- Der ICMP-Tunnel kapselt TCP/UDP in Echo-Requests und umgeht so einige Netzwerkfilter.
- Netfilter-Hooks mit höchster Priorität intercepten den Traffic vor/nach dem Routing.
- Tasklets ermöglichen asynchrones Senden ohne Blockierung des Interrupt-Kontexts.
- Auf der Empfängerseite werden Pakete in die Loopback eingespritzt für nahtlose Zustellung an Anwendungen.
- Die Implementierung ist lehrreich – nicht für den produktiven Einsatz gedacht aufgrund fehlender Sicherheit und Fragmentbehandlung.
— Editorial Team
Noch keine Kommentare.