Zurück zur Startseite

ICMP-Tunnel im Linux-Kernel: TCP/UDP-Kapselung

Der Artikel beschreibt detailliert die Implementierung eines ICMP-Tunnels im Linux-Kernel, der TCP/UDP-Traffic in ICMP-Echo-Anfragen kapselt. Behandelt Netfilter-Hooks, Umgang mit sk_buff, Tasklets und Paket-Injektion in den Loopback.

Eintauchen in den Kernel: Wie TCP/UDP in ICMP-Echo versteckt wird
Advertisement 728x90

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.

Google AdInline article slot

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_buff mit skb_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_buff unter 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_csum und ip_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:

Google AdInline article slot
  • 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_hdr und __ipv4_neigh_lookup verwendet.
  • 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.

Google AdInline article slot

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

Advertisement 728x90

Weiterlesen