BPF – der Berkeley Packet Filter vorgestellt
Der Berkeley Packet Filter (BPF) oder Berkeley-Filter ist für alle unixoiden Betriebssysteme wie beispielsweise Linux interessant. Die Hauptaufgabe der 1992 entwickelten „virtuellen Maschine für besondere Aufgaben“ (engl. Special Purpose Virtual Machine) besteht darin, Datenpakete aus Netzwerken zu filtern und in den Betriebssystemkern einzubetten. Der BPF bildet dabei eine Schnittstelle zu Sicherungsschichten von Dateneinheit bzw. Programmen. Die Sicherungsschichten haben die Aufgabe, die zuverlässige Übertragung von Datenpaketen zu gewährleisten und den Zugriff auf diese Pakete zu regeln.
Kommt ein solches Datenpaket beim Empfänger an, liest der BPF die Daten der Sicherungsschichten aus dem Paket und sucht dabei beispielsweise nach Fehlern. Dem Empfänger ist es so möglich, diese zu beheben. Zudem kann er die Daten mit Filterdefinitionen vergleichen und so ein Paket akzeptieren oder verwerfen, das nicht als relevant eingestuft wird. Dies kann viel Rechenkapazität sparen.
Wie funktioniert der Berkeley Packet Filter?
Für die Ausführung seiner Funktionen wurde der Berkeley Packet Filter als Interpreter in Maschinensprache im Rahmen einer virtuellen Maschine eingebunden. Das hat zur Folge, dass der BPF ein vorgegebenes Format von Anweisungen ausführt. Als Interpreter liest der Berkeley-Filter dabei die Quelldateien, analysiert diese und führt Anweisung für Anweisung aus. Die Anweisungen übersetzt er wiederum in Maschinencodes, um so eine direkte Ausführung zu ermöglichen.
Mithilfe der SysCalls – also Aufrufen spezieller, einsatzbereiter Systemfunktionen – stellt der Berkeley-Filter Anfragen an den Betriebssystemkern, der auch als Kernel bezeichnet wird. Dieser prüft die Zugriffsrechte, bevor er die Anfrage bestätigt oder ablehnt. Zu den rund 330 Linux SysCalls zählen u. a. folgende:
- read – Leseberechtigung, mit der eine Datei gelesen werden kann
- write – Schreibberechtigung, damit eine Datei geschrieben werden kann
- open – Dateien oder Geräte lassen sich öffnen
- close – Dateien oder Geräte lassen sich schließen
- stat – der Status einer Datei wird abgerufen
Durch die permanente Weiterentwicklung arbeitet BPF heute als universelle, virtuelle Maschine direkt im Betriebssystemkern, in dem die gesamte Prozess- und Datenorganisation stattfindet. Mit vielen neuen Features ist der Filter als Extended BPF oder kurz als eBPF bekannt. Er kann damit jeden verwendeten Zwischencode (Bytecode) sicher und während der Laufzeit (Just-in-time-Kompilierung) direkt im Betriebskern ausführen. Der Extended BPF läuft im Betriebskern innerhalb einer isolierten Umgebung und wird damit geschützt ausgeführt. Das als Sandbox bekannte Umgebungsmuster sorgt dafür, das Risiko zu minimieren, dass das System schädlichen Einfluss auf die Logik des Betriebskerns nimmt.
Der Berkeley-Filter kann sowohl im Kernel-Modus (maximaler Zugriff auf Ressourcen des Rechners) als auch im Benutzer-Modus (eingeschränkter Zugriff auf Rechenressourcen) laufen.
Vorteile des Berkeley-Filter
Mit dem eBPF können Sie Datenpakete filtern und so verhindern, dass irrelevante Daten Ihre PC-Performance verlangsamen. Unbrauchbare oder fehlerhafte Datensätze lassen sich so von vornherein ablehnen oder reparieren. Zudem sorgt der Extended BPF für erhöhte Sicherheit durch die SysCalls – Sie können mit den Systemaufrufen problemlos Ihre Performance messen oder eine Ablaufverfolgung vornehmen.
Bereits 2007 wurde die BPF-Implementierung um die „Zero copy buffer extensions“ erweitert. Gerätetreiber können dank dieser Erweiterungen erfasste Datenpakete direkt im Programm speichern, ohne die Daten zunächst kopieren zu müssen.
Filter mit BPF programmieren
Im Benutzer-Modus können Sie jederzeit individuelle Filter für die Berkeley-Filter-Schnittstelle definieren. Früher wurden die entsprechenden Codes noch manuell geschrieben und in einen BPF-Bytecode übersetzt. Heute ist es dank dem LLVM Clang Compiler möglich, die Bytecodes direkt zu übersetzen.
In den Bibliotheken des Betriebskerns sind außerdem Beispielprogramme hinterlegt, die die Definition von eBPF-Programmen vereinfachen. Verschiedene Hilfsfunktionen vereinfachen Ihnen dabei die Arbeit.
Die eBPF-Verifizierer für Sicherheit
Die Ausführung von Systemaufrufen im Kernel ist immer mit gewissen Sicherheits- und Stabilitätsrisiken verbunden. Bevor ein eBPF-SysCall lädt, muss er eine Reihe von Überprüfungen durchlaufen:
- Zunächst wird geprüft, ob der Systemaufruf beendet wurde und keine Schleifen enthält. Diese könnten zu einem Absturz des Kernels führen. Dabei wird der Kontrollflussgraph (KFG) des Programms überprüft, um nicht erreichbare Anweisungen ausfindig zu machen, die in der Folge nicht geladen werden.
- Bevor und nachdem eine Anweisung ausgeführt wird, wird der Status des eBPF-Systemaufrufs geprüft. So soll sichergestellt werden, dass das Extended BPF nur in zulässigen Bereichen agiert und nicht außerhalb der Sandbox auf Daten zugreift. Dabei muss nicht jeder Pfad einzeln geprüft werden. Eine Teilmenge ist dazu meist ausreichend.
- Schließlich wird auch der SysCall-Typ eingestellt. Dieser Schritt ist wichtig, um einzuschränken, welche Kernelfunktionen aus dem SysCall aufgerufen werden können und auf welche Datenstrukturen zugegriffen werden kann. So können Sie z. B. Systemaufrufe nutzen, die direkt auf Netzwerkpaketdaten zugreifen.
Die SysCall-Typen beschäftigen sich grob mit vier Funktionen: Wo das Programm angehängt werden kann, welche Kernel-Hilfsfunktionen aufgerufen werden können, ob auf Netzwerkpaketdaten direkt oder nicht direkt zugegriffen werden kann und welcher Objekttyp bei einem Systemaufruf priorisiert übergeben wird.
Aktuell gibt es folgende eBPF-SysCall-Typen, die vom Kernel unterstützt werden:
- BPF_PROG_TYPE_SOCKET_FILTER
- BPF_PROG_TYPE_KPROBE
- BPF_PROG_TYPE_SCHED_CLS
- BPF_PROG_TYPE_SCHED_ACT
- BPF_PROG_TYPE_TRACEPOINT
- BPF_PROG_TYPE_XDP
- BPF_PROG_TYPE_PERF_EVENT
- BPF_PROG_TYPE_CGROUP_SKB
- BPF_PROG_TYPE_CGROUP_SOCK
- BPF_PROG_TYPE_LWT_ *
- BPF_PROG_TYPE_SOCK_OPS
- BPF_PROG_TYPE_SK_SKB
- BPF_PROG_CGROUP_DEVICE