Netzwerk-Debugging mit eBPF (RHEL 8 Beta)

    Alle mit den letzten Ferien!

    Nach den Feiertagen haben wir uns entschieden, unseren ersten Artikel für Linux zu widmen, dh für unseren wundervollen Kurs "Linux Administrator" , den wir in die Kohorte der dynamischsten Kurse aufgenommen haben, dh mit den relevantesten Materialien und Praktiken. Nun, und dementsprechend bieten wir interessante Artikel und eine offene Lektion an .

    Autor des Artikels: Matteo Croce
    Originaltitel: Netzwerk-Debugging mit eBPF (RHEL 8 Beta)


    Einführung Das

    Arbeiten mit dem Netzwerk ist eine aufregende Tätigkeit, aber es ist nicht immer möglich, Probleme zu vermeiden. Die Fehlerbehebung kann schwierig sein und versucht, das falsche Verhalten zu reproduzieren, das „im Feld“ auftritt.

    Glücklicherweise gibt es Tools, die dabei helfen können: Netzwerk-Namespaces, virtuelle Maschinen tc und netfilter. Einfache Netzwerkeinstellungen können über den Netzwerk - Namespace und veth-Geräte wiedergegeben werden, während komplexere Einstellungen , um die Verbindung von virtuellen Maschinen einer Bridge - Software und die Verwendung von Standard - Netzwerk - Tool benötigen, beispielsweise iptables oder tczur Simulation von unangemessenem Verhalten. Wenn ein Problem mit ICMP-Antworten auftritt, die generiert werden, wenn der SSH-Server abstürzt, kann iptables -A INPUT -p tcp --dport 22 -j REJECT --reject-with icmp-host-unreachableder richtige Namespace das Problem lösen.

    Dieser Artikel beschreibt, wie komplexe Netzwerkprobleme mit eBPF (Extended BPF) behandelt werden können., eine verbesserte Version des Berkeley Packet Filters. eBPF ist eine relativ neue Technologie, das Projekt befindet sich noch in einem frühen Stadium, sodass die Dokumentation und das SDK noch nicht fertiggestellt sind. Hoffen wir jedoch auf Verbesserungen, insbesondere da XDP (eXpress Data Path) mit Red Hat Enterprise Linux 8 Beta geliefert wird , das Sie jetzt herunterladen und ausführen können.

    eBPF löst nicht alle Probleme, aber es ist immer noch ein leistungsfähiges Werkzeug für das Debuggen im Netzwerk, das Aufmerksamkeit verdient. Ich bin sicher, dass dies eine wirklich wichtige Rolle für die Zukunft der Netzwerke spielen wird.



    Problem

    Ich habe ein Problem mit dem Open vSwitch (OVS) -Netzwerk debugiert , das eine sehr komplexe Installation betraf: Einige TCP-Pakete wurden verstreut und in der falschen Reihenfolge zugestellt , und die Bandbreite der virtuellen Maschinen sank von stabilen 6 GBit / s auf 2-4 GBit / s . 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 ein pro Verbindung.

    Ich habe versucht , diese Konfiguration mit zwei virtuellen Maschinen zu reproduzieren und nach vielen Referenzartikeln und Durchsuchungen, dass keine gefunden iptables, noch nftables kann die Flaggen von TCP nicht manipulieren, während der tc Dose, aber nur Fahnen und Brechen neue Verbindungen und TCP als Ganze zu überschreiben.

    Es war zwar möglich, das Problem mit einer Kombination zu lösen iptables, conntrack und tcich entschied, dass dies eine großartige Aufgabe für eBPF war.

    Was ist eBPF?

    eBPF ist eine verbesserte Version des Berkeley Batch Filters. Sie bringt viele Verbesserungen in BPF. Insbesondere können Sie 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 (klassisch (klassisch) BPF) bezeichnet. Daher kann das Wort "BPF" verwendet werden, um je nach Kontext auf beide Versionen zu verweisen: In diesem Artikel spreche ich immer von der erweiterten Version.

    "Under the hood" von eBPF ist eine sehr einfache virtuelle Maschine, die kleine Bytecodes ausführen und einige Speicherpuffer bearbeiten kann. EBPF hat Einschränkungen, die es vor böswilliger Verwendung schützen:

    • Zyklen sind verboten, damit das Programm immer zu einer bestimmten Zeit endet.
    • Es kann nur über den Stack und den Scratch-Puffer auf den Speicher zugreifen.
    • Nur autorisierte Kernfunktionen können aufgerufen werden.

    Das Programm kann auf verschiedene Arten mit Debugging und Tracing in den Kernel geladen werden . In unserem Fall interessieren wir uns für die Arbeit von eBPF mit Netzwerk-Subsystemen. Es gibt zwei Möglichkeiten, das eBPF-Programm zu verwenden:

    • Verbindung über XDP mit dem Anfang des RX-Pfads einer physischen oder virtuellen Netzwerkkarte;
    • An tc den qdisc-Einlass oder -Auslass angeschlossen.

    Um ein eBPF-Programm für die Verbindung zu erstellen, schreiben Sie einfach den Code in C und konvertieren ihn in Bytecode. Hier ist ein einfaches Beispiel mit XDP:

    SEC("prog")
    intxdp_main(structxdp_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);
        /* sanity check needed by the eBPF verifier */
        if (icmph + 1 > data_end)
            return XDP_PASS;
        /* matched a pong packet */
        if (eth->h_proto != ntohs(ETH_P_IP) ||
            iph->protocol != IPPROTO_ICMP ||
            icmph->type != ICMP_ECHOREPLY)
            return XDP_PASS;
        if (iph->ttl) {
            /* save the old TTL to recalculate the checksum */
            uint16_t *ttlproto = (uint16_t *)&iph->ttl;
            uint16_t old_ttlproto = *ttlproto;
            /* set the TTL to a pseudorandom number 1 < x < TTL */
            iph->ttl = bpf_get_prandom_u32() % iph->ttl + 1;
            /* recalculate the checksum; otherwise, the IP stack will drop it */
            csum_replace2(&iph->check, old_ttlproto, *ttlproto);
        }
        returnXDP_PASS;
    }
    char _license[]SEC("license") = "GPL";

    Das obige Fragment ohne Ausdrücke include, Helfer und optionalem Code ist ein XDP-Programm, das die TTL der empfangenen ICMP-Echoantworten, nämlich pong'ov, in eine Zufallszahl ändert. Die Hauptfunktion empfängt eine Struktur, xdp_mdin der zwei Zeiger auf den Anfang und das Ende des Pakets stehen.

    Um unseren Code in den eBPF-Bytecode zu kompilieren, ist ein Compiler mit entsprechender Unterstützung erforderlich. Clang unterstützt dies und erstellt den eBPF-Bytecode durch Verfeinerung von bpf als Ziel zur Kompilierzeit:

    $ clang -O2 -target bpf -c xdp_manglepong.c -o xdp_manglepong.o

    Der obige Befehl erstellt eine Datei, die auf den ersten Blick als normale Objektdatei erscheint. Bei näherer Betrachtung stellt sich jedoch heraus, dass der angegebene Computertyp Linux eBPF und nicht der native Typ des Betriebssystems 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 Sie den Wrapper einer regulären Objektdatei erhalten haben, kann das eBPF-Programm geladen und über XDP mit dem Gerät verbunden werden. Dies kann mit ipdem Paket iproute2mit der folgenden Syntax durchgeführt werden:

    # ip -force link set dev wlan0 xdp object xdp_manglepong.o verbose

    Dieser Befehl gibt die Zielschnittstelle wlan0 an und überschreibt dank der Option -force vorhandenen eBPF-Code, der bereits geladen wurde. Nach dem Download 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, das eventuell einige Änderungen vornimmt und entscheidet, ob das Paket gelöscht oder übersprungen werden soll.

    Wie eBPF helfen kann: Um

    auf das anfängliche Netzwerkproblem zurückzukommen, erinnern wir uns daran, dass wir mehrere TCP-Flags markieren mussten, eines pro Verbindung, und iptablesbeide tc hätten dies nicht tun können. Das Schreiben von Code für dieses Szenario ist ein Kinderspiel: Sie müssen zwei virtuelle Maschinen einrichten, die über eine OVS-Brücke verbunden sind, und einfach eBPF mit einem der virtuellen VM-Geräte verbinden.

    Das klingt nach einer großartigen Lösung, aber Sie sollten bedenken, dass XDP nur die Verarbeitung empfangener Pakete unterstützt und das Verbinden von eBPF mit dem Pfad der rx empfangenden virtuellen Maschine keine Auswirkungen auf den Switch hat.

    Um dieses Problem zu lösen, muss eBPF mit heruntergeladen werdentc und ist mit dem Ausgabepfad der VM verbunden, da es tc eBPF-Programme herunterladen und mit qdisk verbinden kann. Um Pakete zu markieren, die den Host verlassen, muss eBPF mit der Ausgangs-qdisk verbunden sein.

    Wenn EBPF Programme zwischen Laden XDP und tc API gibt es einige Unterschiede sind: der Standardname verschiedene Abschnitte, andere Art von Struktur ist das Argument der Hauptfunktion, unterschiedliche Rückgabewerte. Das ist aber kein Problem. Unten ist ein Fragment eines Programms, das TCP kennzeichnet, wenn es an eine Tc-Aktion angehängt ist:

    #define RATIO 10
    SEC("action")
    intbpf_main(struct __sk_buff *skb){
        void *data = (void *)(uintptr_t)skb->data;
        void *data_end = (void *)(uintptr_t)skb->data_end;
        structethhdr *eth = data;structiphdr *iph = (structiphdr *)(eth + 1);structtcphdr *tcphdr = (structtcphdr *)(iph + 1);/* sanity check needed by the eBPF verifier */if ((void *)(tcphdr + 1) > data_end)
            return TC_ACT_OK;
        /* skip non-TCP packets */if (eth->h_proto != __constant_htons(ETH_P_IP) || iph->protocol != IPPROTO_TCP)
            return TC_ACT_OK;
        /* incompatible flags, or PSH already set */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 mit folgendem Befehl:

    clang -O2 -target bpf -c tcp_psh.c -o tcp_psh.o

    Der Download ist jedoch anders:

    # tc qdisc add dev eth0 clsact# tc filter add dev eth0 egress matchall action bpf object-file tcp_psh.o

    Die eBPF ist jetzt an der richtigen Stelle geladen und die Pakete, die die VM verlassen, sind markiert. Nach der Überprüfung der in der zweiten VM empfangenen Pakete wird Folgendes



    tcpdump angezeigt : Bestätigt, dass der neue eBPF-Code funktioniert, und bei etwa 1 von 10 TCP-Paketen ist das PSH-Flag gesetzt. Es wurden nur 20 Zeilen C-Code benötigt, um selektiv TCP-Pakete zu markieren, die eine virtuelle Maschine verlassen, den Fehler zu reproduzieren, der im Kampf auftritt, und dies alles ohne erneutes Kompilieren oder gar Neustarten! Dies hat die Überprüfung des Open vSwitch-Updates erheblich vereinfacht , was mit anderen Tools nicht möglich war.

    Schlussfolgerung

    eBPF ist eine relativ neue Technologie, und die Community hat eine klare Meinung zu ihrer Implementierung. Erwähnenswert ist auch, dass auf eBPF basierende Projekte, beispielsweise bpfilter, erstellt werdenwerden immer beliebter, und daher beginnen viele Gerätehersteller damit, die eBPF-Unterstützung direkt in Netzwerkkarten zu implementieren.

    eBPF wird nicht alle Probleme lösen, also nicht missbrauchen, aber es bleibt ein sehr leistungsfähiges Werkzeug für das Debuggen im Netzwerk und verdient Aufmerksamkeit. Ich bin sicher, dass er eine wichtige Rolle in der Zukunft der Netzwerke spielen wird.

    DAS ENDE

    Wir warten hier auf Ihre Kommentare und laden Sie ein, unsere offene Lektion zu besuchen , in der Sie gegebenenfalls Fragen stellen können.

    Jetzt auch beliebt: