Alles mit den vergangenen Ferien!
Wir haben uns entschlossen, unseren ersten Artikel nach den Ferien Linux zu widmen, dh unserem wunderbaren
Linux-Administratorkurs , den wir in der Kohorte der dynamischsten Kurse haben, dh mit den relevantesten Materialien und Praktiken. Gut und dementsprechend bieten wir interessante Artikel und
eine offene Lektion .
Gepostet von Matteo Croce
Originaltitel: Netzwerk-Debugging mit eBPF (RHEL 8 Beta)EinführungNetworking ist eine aufregende Erfahrung, aber Probleme werden nicht immer vermieden. Die Fehlerbehebung kann schwierig sein, ebenso wie der Versuch, das falsche Verhalten zu reproduzieren, das „vor Ort“ auftritt.
Glücklicherweise gibt es Tools, die dabei helfen können: Netzwerk-Namespaces, virtuelle Maschinen,
tc
und
netfilter
. Einfache Netzwerkeinstellungen können mithilfe von Netzwerk-Namespaces und Veth-Geräten reproduziert werden. Komplexere Einstellungen erfordern die Verbindung virtueller Maschinen mit einer Software-Bridge und die Verwendung von Standard-Netzwerk-Tools wie
iptables
oder
tc
, um falsches Verhalten zu simulieren. Wenn beim
iptables -A INPUT -p tcp --dport 22 -j REJECT --reject-with icmp-host-unreachable
des SSH-Servers ein Problem mit ICMP-Antworten
iptables -A INPUT -p tcp --dport 22 -j REJECT --reject-with icmp-host-unreachable
, kann
iptables -A INPUT -p tcp --dport 22 -j REJECT --reject-with icmp-host-unreachable
im richtigen Namespace zur Lösung des Problems beitragen.
Dieser Artikel beschreibt die
Behebung komplexer Netzwerkprobleme mit
eBPF (Extended BPF) , einer erweiterten Version des Berkeley Packet Filters. eBPF ist eine relativ neue Technologie, das Projekt befindet sich in einem frühen Stadium, sodass die Dokumentation und das SDK noch nicht fertig sind. Hoffen wir jedoch auf Verbesserungen, zumal der XDP (eXpress Data Path) mit
Red Hat Enterprise Linux 8 Beta ausgeliefert wird , das Sie jetzt herunterladen und ausführen können.
eBPF wird nicht alle Probleme lösen, aber es ist immer noch ein leistungsstarkes Netzwerk-Debugging-Tool, das Aufmerksamkeit verdient. Ich bin sicher, dass dies in der Zukunft der Netzwerke eine wirklich wichtige Rolle spielen wird.
Das ProblemIch habe ein
Open vSwitch (OVS) -Netzwerkproblem
debuggt , das eine sehr komplizierte Installation beinhaltete: Einige TCP-Pakete wurden verstreut und in der falschen Reihenfolge
zugestellt , und die Bandbreite der virtuellen Maschinen ging von stabilen 6 Gbit / s auf schwankende 2 bis 4 Gbit / s zurück. Die Analyse ergab, dass das erste TCP-Paket jeder Verbindung mit dem PSH-Flag in der falschen Reihenfolge gesendet wurde: nur das erste und nur eines pro Verbindung.
Ich habe versucht, diese Einstellung mit zwei virtuellen Maschinen zu reproduzieren, und nach vielen
nftables
und Suchanfragen stellte ich fest, dass weder
iptables
noch
nftables
TCP-Flags manipulieren können, während
tc
kann, sondern nur durch Überschreiben der Flags und Unterbrechen neuer Verbindungen und TCP im Allgemeinen.
Es könnte möglich sein, das Problem mit einer Kombination aus
iptables
,
conntrack
und
tc
zu lösen, aber ich entschied, dass dies ein großartiger Job für eBPF ist.
Was ist eBPF?eBPF ist eine erweiterte Version des Berkeley Packet Filters. Sie bringt viele Verbesserungen zu BPF. Insbesondere können Sie damit in den Speicher schreiben und nicht nur lesen, sodass Pakete nicht nur gefiltert, sondern auch bearbeitet werden können.
Oft wird eBPF einfach als BPF bezeichnet, und BPF selbst wird als cBPF (klassischer (klassischer) BPF) bezeichnet, sodass das Wort „BPF“ je nach Kontext für beide Versionen verwendet werden kann: In diesem Artikel spreche ich immer über die erweiterte Version.
"Under the Hood" eBPF verfügt über eine sehr einfache virtuelle Maschine, die kleine Fragmente von Bytecode ausführen und einige Speicherpuffer bearbeiten kann. Es gibt Einschränkungen in eBPF, die es vor böswilliger Verwendung schützen:
- Zyklen sind verboten, damit das Programm immer zu einer bestimmten Zeit beendet wird.
- Es kann nur über den Stapel und den Arbeitspuffer auf den Speicher zugreifen.
- Es können nur zulässige Kernelfunktionen aufgerufen werden.
Ein Programm kann auf verschiedene Arten mithilfe von
Debugging und Tracing in den Kernel geladen werden. In unserem Fall ist eBPF an der Arbeit mit Netzwerksubsystemen interessiert. Es gibt zwei Möglichkeiten, das eBPF-Programm zu verwenden:
- Verbunden über XDP mit dem Anfang des Empfangspfads einer physischen oder virtuellen Netzwerkkarte;
- Verbunden über
tc
mit qdisc in Eingabe oder Ausgabe.
Um ein eBPF-Programm zum Verbinden zu erstellen, schreiben Sie einfach C-Code und konvertieren Sie ihn in Bytecode. Das Folgende ist ein einfaches Beispiel für die Verwendung von XDP:
SEC("prog") int xdp_main(struct xdp_md *ctx) { void *data_end = (void *)(uintptr_t)ctx->data_end; void *data = (void *)(uintptr_t)ctx->data; struct ethhdr *eth = data; struct iphdr *iph = (struct iphdr *)(eth + 1); struct icmphdr *icmph = (struct icmphdr *)(iph + 1); if (icmph + 1 > data_end) return XDP_PASS; if (eth->h_proto != ntohs(ETH_P_IP) || iph->protocol != IPPROTO_ICMP || icmph->type != ICMP_ECHOREPLY) return XDP_PASS; if (iph->ttl) { uint16_t *ttlproto = (uint16_t *)&iph->ttl; uint16_t old_ttlproto = *ttlproto; iph->ttl = bpf_get_prandom_u32() % iph->ttl + 1; csum_replace2(&iph->check, old_ttlproto, *ttlproto); } return XDP_PASS; } char _license[] SEC("license") = "GPL";
Das obige Snippet ohne
include
Ausdrücke, Helfer und optionalen Code ist ein XDP-Programm, das die TTL der empfangenen ICMP-Echoantworten, nämlich Pongs, durch eine Zufallszahl ändert. Die Hauptfunktion erhält die Struktur
xdp_md
, die zwei Zeiger auf den Anfang und das Ende des Pakets enthält.
Um unseren Code in eBPF-Bytecode zu kompilieren, ist ein Compiler mit entsprechender Unterstützung erforderlich. Clang unterstützt dies und erstellt einen eBPF-Bytecode, indem bpf zur Kompilierungszeit als Ziel angegeben wird:
$ clang -O2 -target bpf -c xdp_manglepong.c -o xdp_manglepong.o
Mit dem obigen Befehl wird eine Datei erstellt, die auf den ersten Blick wie eine normale Objektdatei erscheint. Bei näherer Betrachtung stellt sich jedoch heraus, dass der angegebene Computertyp Linux eBPF und nicht der native Betriebssystemtyp ist:
$ readelf -h xdp_manglepong.o ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL (Relocatable file) Machine: Linux BPF <--- HERE [...]
Nachdem das eBPF-Programm den Wrapper einer regulären Objektdatei erhalten hat, kann es heruntergeladen und über XDP mit dem Gerät verbunden werden. Dies kann mit
ip
aus dem
iproute2
Paket mit der folgenden Syntax erfolgen:
Dieser Befehl gibt die Ziel-wlan0-Schnittstelle an und überschreibt dank der Option -force den vorhandenen eBPF-Code, der bereits geladen wurde. Nach dem Laden des eBPF-Bytecodes verhält sich das System wie folgt:
$ ping -c10 192.168.85.1 PING 192.168.85.1 (192.168.85.1) 56(84) bytes of data. 64 bytes from 192.168.85.1: icmp_seq=1 ttl=41 time=0.929 ms 64 bytes from 192.168.85.1: icmp_seq=2 ttl=7 time=0.954 ms 64 bytes from 192.168.85.1: icmp_seq=3 ttl=17 time=0.944 ms 64 bytes from 192.168.85.1: icmp_seq=4 ttl=64 time=0.948 ms 64 bytes from 192.168.85.1: icmp_seq=5 ttl=9 time=0.803 ms 64 bytes from 192.168.85.1: icmp_seq=6 ttl=22 time=0.780 ms 64 bytes from 192.168.85.1: icmp_seq=7 ttl=32 time=0.847 ms 64 bytes from 192.168.85.1: icmp_seq=8 ttl=50 time=0.750 ms 64 bytes from 192.168.85.1: icmp_seq=9 ttl=24 time=0.744 ms 64 bytes from 192.168.85.1: icmp_seq=10 ttl=42 time=0.791 ms --- 192.168.85.1 ping statistics --- 10 packets transmitted, 10 received, 0% packet loss, time 125ms rtt min/avg/max/mdev = 0.744/0.849/0.954/0.082 ms
Jedes Paket durchläuft eBPF, wodurch letztendlich einige Änderungen vorgenommen werden und entschieden wird, ob das Paket verworfen oder übersprungen werden soll.
Wie eBPF helfen kannZurück zum ursprünglichen Netzwerkproblem, erinnern wir uns, dass es notwendig war, mehrere TCP-Flags zu markieren, eines pro Verbindung, und weder
iptables
noch
tc
konnten dies tun. Das Schreiben von Code für dieses Szenario ist überhaupt nicht schwierig: Konfigurieren Sie zwei virtuelle Maschinen, die über eine OVS-Bridge verbunden sind, und verbinden Sie den eBPF einfach mit einem der virtuellen VM-Geräte.
Es klingt nach einer großartigen Lösung, aber denken Sie daran, dass XDP nur die Verarbeitung empfangener Pakete unterstützt und das Verbinden des eBPF mit dem Empfangspfad der empfangenden virtuellen Maschine keine Auswirkungen auf den Switch hat.
Um dieses Problem zu lösen, muss eBPF mit
tc
geladen und mit dem Ausgabepfad der VM verbunden werden, da
tc
eBPF-Programme laden und mit qdisk verbinden kann. Um Pakete zu markieren, die den Host verlassen, muss eBPF mit der Ausgabe-qdisk verbunden sein.
Beim Laden des eBPF-Programms gibt es einige Unterschiede zwischen der
XDP
und der
tc
API: Standardmäßig unterschiedliche Abschnittsnamen, der Typ der Struktur des Arguments der Hauptfunktion, unterschiedliche Rückgabewerte. Das ist aber kein Problem. Unten finden Sie einen Ausschnitt eines Programms, das TCP beim Beitritt zu einer tc-Aktion markiert:
#define RATIO 10 SEC("action") int bpf_main(struct __sk_buff *skb) { void *data = (void *)(uintptr_t)skb->data; void *data_end = (void *)(uintptr_t)skb->data_end; struct ethhdr *eth = data; struct iphdr *iph = (struct iphdr *)(eth + 1); struct tcphdr *tcphdr = (struct tcphdr *)(iph + 1); if ((void *)(tcphdr + 1) > data_end) return TC_ACT_OK; if (eth->h_proto != __constant_htons(ETH_P_IP) || iph->protocol != IPPROTO_TCP) return TC_ACT_OK; if (tcphdr->syn || tcphdr->fin || tcphdr->rst || tcphdr->psh) return TC_ACT_OK; if (bpf_get_prandom_u32() % RATIO == 0) tcphdr->psh = 1; return TC_ACT_OK; } char _license[] SEC("license") = "GPL";
Die Kompilierung in Bytecode erfolgt wie im obigen XDP-Beispiel gezeigt wie folgt:
clang -O2 -target bpf -c tcp_psh.c -o tcp_psh.o
Aber der Download ist anders:
Jetzt wird der eBPF an der richtigen Stelle geladen und die Pakete, die die VM verlassen, werden markiert. Nach dem Überprüfen der in der zweiten VM empfangenen Pakete wird Folgendes angezeigt:

tcpdump
bestätigt, dass der neue eBPF-Code funktioniert und bei ungefähr 1 von 10 TCP-Paketen das PSH-Flag gesetzt ist. Es wurden nur 20 Zeilen C-Code benötigt, um TCP-Pakete, die die virtuelle Maschine verlassen, selektiv zu markieren, den Fehler zu reproduzieren, der "im Kampf" auftritt, und alles ohne Neukompilieren oder sogar Neustarten! Dies vereinfachte die Überprüfung des
Open vSwitch-Fixes erheblich , was mit anderen Tools nicht möglich war.
FaziteBPF ist eine ziemlich neue Technologie, und die Community hat eine klare Meinung zu ihrer Implementierung. Es ist auch erwähnenswert, dass Projekte, die auf eBPF basieren, beispielsweise
bpfilter , immer beliebter werden. Infolgedessen beginnen viele Ausrüstungslieferanten, die eBPF-Unterstützung direkt in Netzwerkkarten zu implementieren.
eBPF wird nicht alle Probleme lösen, also missbrauchen Sie es nicht, aber es bleibt ein sehr leistungsfähiges Tool für das Netzwerk-Debugging und verdient Aufmerksamkeit. Ich bin sicher, dass dies in der Zukunft der Netzwerke eine wichtige Rolle spielen wird.
DAS ENDEWir warten hier auf Ihre Kommentare und laden Sie ein, unsere
offene Lektion zu besuchen, in der Sie auch Fragen stellen können.