Der Berkeley Packet Filter (BPF) oder Berkeley-Filter ist für alle unixoiden Be­triebs­sys­te­me wie bei­spiels­wei­se Linux in­ter­es­sant. Die Haupt­auf­ga­be der 1992 ent­wi­ckel­ten „vir­tu­el­len Maschine für besondere Aufgaben“ (engl. Special Purpose Virtual Machine) besteht darin, Da­ten­pa­ke­te aus Netz­wer­ken zu filtern und in den Be­triebs­sys­tem­kern ein­zu­bet­ten. Der BPF bildet dabei eine Schnitt­stel­le zu Si­che­rungs­schich­ten von Da­ten­ein­heit bzw. Pro­gram­men. Die Si­che­rungs­schich­ten haben die Aufgabe, die zu­ver­läs­si­ge Über­tra­gung von Da­ten­pa­ke­ten zu ge­währ­leis­ten und den Zugriff auf diese Pakete zu regeln.

Kommt ein solches Da­ten­pa­ket beim Empfänger an, liest der BPF die Daten der Si­che­rungs­schich­ten aus dem Paket und sucht dabei bei­spiels­wei­se nach Fehlern. Dem Empfänger ist es so möglich, diese zu beheben. Zudem kann er die Daten mit Fil­ter­de­fi­ni­tio­nen ver­glei­chen und so ein Paket ak­zep­tie­ren oder verwerfen, das nicht als relevant ein­ge­stuft wird. Dies kann viel Re­chen­ka­pa­zi­tät sparen.

Wie funk­tio­niert der Berkeley Packet Filter?

Für die Aus­füh­rung seiner Funk­tio­nen wurde der Berkeley Packet Filter als In­ter­pre­ter in Ma­schi­nen­spra­che im Rahmen einer vir­tu­el­len Maschine ein­ge­bun­den. Das hat zur Folge, dass der BPF ein vor­ge­ge­be­nes Format von An­wei­sun­gen ausführt. Als In­ter­pre­ter liest der Berkeley-Filter dabei die Quell­da­tei­en, ana­ly­siert diese und führt Anweisung für Anweisung aus. Die An­wei­sun­gen übersetzt er wiederum in Ma­schi­nen­codes, um so eine direkte Aus­füh­rung zu er­mög­li­chen.

Mithilfe der SysCalls – also Aufrufen spe­zi­el­ler, ein­satz­be­rei­ter Sys­tem­funk­tio­nen – stellt der Berkeley-Filter Anfragen an den Be­triebs­sys­tem­kern, der auch als Kernel be­zeich­net wird. Dieser prüft die Zu­griffs­rech­te, bevor er die Anfrage bestätigt oder ablehnt. Zu den rund 330 Linux SysCalls zählen u. a. folgende:

  • read – Le­se­be­rech­ti­gung, mit der eine Datei gelesen werden kann
  • write – Schreib­be­rech­ti­gung, damit eine Datei ge­schrie­ben 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 per­ma­nen­te Wei­ter­ent­wick­lung arbeitet BPF heute als uni­ver­sel­le, virtuelle Maschine direkt im Be­triebs­sys­tem­kern, in dem die gesamte Prozess- und Da­ten­or­ga­ni­sa­ti­on statt­fin­det. Mit vielen neuen Features ist der Filter als Extended BPF oder kurz als eBPF bekannt. Er kann damit jeden ver­wen­de­ten Zwi­schen­code (Bytecode) sicher und während der Laufzeit (Just-in-time-Kom­pi­lie­rung) direkt im Be­triebs­kern ausführen. Der Extended BPF läuft im Be­triebs­kern innerhalb einer iso­lier­ten Umgebung und wird damit geschützt aus­ge­führt. Das als Sandbox bekannte Um­ge­bungs­mus­ter sorgt dafür, das Risiko zu mi­ni­mie­ren, dass das System schäd­li­chen Einfluss auf die Logik des Be­triebs­kerns nimmt.

Hinweis

Der Berkeley-Filter kann sowohl im Kernel-Modus (maximaler Zugriff auf Res­sour­cen des Rechners) als auch im Benutzer-Modus (ein­ge­schränk­ter Zugriff auf Re­chen­res­sour­cen) laufen.

Vorteile des Berkeley-Filter

Mit dem eBPF können Sie Da­ten­pa­ke­te filtern und so ver­hin­dern, dass ir­rele­van­te Daten Ihre PC-Per­for­mance ver­lang­sa­men. Un­brauch­ba­re oder feh­ler­haf­te Da­ten­sät­ze lassen sich so von vorn­her­ein ablehnen oder re­pa­rie­ren. Zudem sorgt der Extended BPF für erhöhte Si­cher­heit durch die SysCalls – Sie können mit den Sys­tem­auf­ru­fen pro­blem­los Ihre Per­for­mance messen oder eine Ab­lauf­ver­fol­gung vornehmen.

Bereits 2007 wurde die BPF-Im­ple­men­tie­rung um die „Zero copy buffer ex­ten­si­ons“ erweitert. Ge­rä­te­trei­ber können dank dieser Er­wei­te­run­gen erfasste Da­ten­pa­ke­te direkt im Programm speichern, ohne die Daten zunächst kopieren zu müssen.

Filter mit BPF pro­gram­mie­ren

Im Benutzer-Modus können Sie jederzeit in­di­vi­du­el­le Filter für die Berkeley-Filter-Schnitt­stel­le de­fi­nie­ren. Früher wurden die ent­spre­chen­den Codes noch manuell ge­schrie­ben und in einen BPF-Bytecode übersetzt. Heute ist es dank dem LLVM Clang Compiler möglich, die Bytecodes direkt zu über­set­zen.

In den Bi­blio­the­ken des Be­triebs­kerns sind außerdem Bei­spiel­pro­gram­me hin­ter­legt, die die De­fi­ni­ti­on von eBPF-Pro­gram­men ver­ein­fa­chen. Ver­schie­de­ne Hilfs­funk­tio­nen ver­ein­fa­chen Ihnen dabei die Arbeit.

Die eBPF-Ve­ri­fi­zie­rer für Si­cher­heit

Die Aus­füh­rung von Sys­tem­auf­ru­fen im Kernel ist immer mit gewissen Si­cher­heits- und Sta­bi­li­täts­ri­si­ken verbunden. Bevor ein eBPF-SysCall lädt, muss er eine Reihe von Über­prü­fun­gen durch­lau­fen:

  1. Zunächst wird geprüft, ob der Sys­tem­auf­ruf beendet wurde und keine Schleifen enthält. Diese könnten zu einem Absturz des Kernels führen. Dabei wird der Kon­troll­fluss­graph (KFG) des Programms überprüft, um nicht er­reich­ba­re An­wei­sun­gen ausfindig zu machen, die in der Folge nicht geladen werden.
  2. Bevor und nachdem eine Anweisung aus­ge­führt wird, wird der Status des eBPF-Sys­tem­auf­rufs geprüft. So soll si­cher­ge­stellt werden, dass das Extended BPF nur in zu­läs­si­gen 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 aus­rei­chend.
  3. Schließ­lich wird auch der SysCall-Typ ein­ge­stellt. Dieser Schritt ist wichtig, um ein­zu­schrän­ken, welche Ker­nel­funk­tio­nen aus dem SysCall auf­ge­ru­fen werden können und auf welche Da­ten­struk­tu­ren zu­ge­grif­fen werden kann. So können Sie z. B. Sys­tem­auf­ru­fe nutzen, die direkt auf Netz­werk­pa­ket­da­ten zugreifen.

Die SysCall-Typen be­schäf­ti­gen sich grob mit vier Funk­tio­nen: Wo das Programm angehängt werden kann, welche Kernel-Hilfs­funk­tio­nen auf­ge­ru­fen werden können, ob auf Netz­werk­pa­ket­da­ten direkt oder nicht direkt zu­ge­grif­fen werden kann und welcher Objekttyp bei einem Sys­tem­auf­ruf prio­ri­siert übergeben wird.

Aktuell gibt es folgende eBPF-SysCall-Typen, die vom Kernel un­ter­stützt werden:

  • BPF_PROG_TYPE_SOCKET_FILTER
  • BPF_PROG_TYPE_KPROBE
  • BPF_PROG_TYPE_SCHED_CLS
  • BPF_PROG_TYPE_SCHED_ACT
  • BPF_PROG_TYPE_TRACE­POINT
  • 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
Zum Hauptmenü