Das Projekt bpftime wurde vorgestellt, das ein Runtime und eine virtuelle Maschine zur Ausführung von eBPF-Handlern im Benutzerspeicher entwickelt. Bpftime ermöglicht die vollständige Ausführung von eBPF-Programmen zur Trace- und Interventionsbehandlung in Benutzerspeicher und nutzt Funktionen wie uprobe und Software-Intercepts von Systemaufrufen. Es wird hervorgehoben, dass bpftime durch den Ausschluss unnötiger Kontextwechsel eine zehnfache Reduzierung der Overheadkosten im Vergleich zur Verwendung der uprobe- und uretprobe-Funktionalität des Linux-Kernels ermöglicht. Darüber hinaus vereinfacht bpftime die Debugging erheblich, kann potenziell auf Systemen ohne Linux-Kernel verwendet werden und erfordert keine erweiterten Berechtigungen, die zum Laden von eBPF-Anwendungen in den Kernel erforderlich sind. Der Code des Projekts ist in C/C++ geschrieben und wird unter der MIT-Lizenz verbreitet.
Das Abfangen von Systemaufrufen und die Integration von uprobe-Prüfungen werden durch die Technik der Umprogrammierung des ausführbaren Codes (binary rewriting) umgesetzt, bei der Aufrufe von Systemaufrufen, Einstiegspunkten und lokalen Funktionen durch den Übergang zu Debugging-Handlern durch Modifikation des Maschinencodes der ausführenden Anwendung ersetzt werden, was erheblich effizienter ist als das Abfangen mit uprobe auf der Ebene des Linux-Kernels.
Es werden Operationen zur Ersetzung oder Modifizierung von Funktionen, das Anbringen von Hooks und Filtern, Umleitungen, Blockierungen oder Ersetzungen von Parametern von Systemaufrufen, das Abfangen von Ein- und Ausstiegspunkten von Funktionen sowie die Substitution von Handlern an beliebigen Offsets im Code unterstützt. Bpftime kann an jeden arbeitenden Prozess im System angehängt werden, ohne dass diese neu gestartet oder neu kompiliert werden müssen. Die Substitution von bpftime in Prozessen kann für laufende Prozesse über ptrace und für ladbare Prozesse über LD_PRELOAD erfolgen.
Im Rahmen von bpftime wird ein Runtime entwickelt, das das Anhängen von eBPF-Programmen an die Trace-Punkte von Systemaufrufen und uprobe ermöglicht. eine virtuelle Maschine eBPF mit JIT für die isolierte Ausführung von eBPF-Programmen auf der Ebene des Benutzerprozesses (zusätzlich wird AOT-Compilation unterstützt); ein Hintergrundprozess für die Interaktion mit dem Kernel und die Gewährleistung der Kompatibilität mit dem uprobe-Subsystem des Kernels (bpftime unterstützt den Modus für das Laden von eBPF in den Adressraum des Benutzers aus dem Kernel, um eine Zusammenarbeit mit eBPF-Programmen im Kernel zu ermöglichen, die beispielsweise für die Verarbeitung von kprobe oder die Anwendung von Netzwerkfiltern verwendet werden).
Die eBPF-VM ist als dynamische Bibliothek implementiert und bietet eine API, die der von ubpf ähnelt, was die Integration von eBPF-Funktionalitäten in andere Projekte ermöglicht. Für die aggregierte Datensammlung aus mehreren Prozessen wird die Erstellung gemeinsamer eBPF-Maps unterstützt, die im Shared Memory abgelegt werden. Zusammen mit bpftime können gängige eBPF-Handler verwendet werden, die für den Einsatz im Kernel geschrieben wurden, und für das Kompilieren können Standardwerkzeuge auf Basis von clang und libbpf angewendet werden.
Mit bpftime im Benutzerraum können solche Tracing-Systeme wie BCC, bpftrace und Deepflow ausgeführt werden. Beispielsweise wurde die Anwendung des Skripts sslsniff aus dem BCC-Framework demonstriert, um verschlüsselten Traffic in nginx zu analysieren und zu speichern. In durchgeführten Tests sank die Leistung von nginx bei der Ausführung von sslsniff im Kernel-Modus um 58 % und bei der Auslagerung des Handlers in den Benutzerraum um 12,3 %.
Architektur des Prozess-Traceings unter Verwendung des originalen eBPF im Kernel:

Architektur des Traceings im Benutzerraum unter Verwendung von bpftime:

Hybrider Modus, in dem bpftime zusammen mit eBPF im Kernel arbeitet, beispielsweise zur Einrichtung von Netzwerkfiltern oder zur Auslagerung bestimmter Handler in den Benutzerraum:

Zukünftige Pläne umfassen: die Möglichkeit der Fehlerinjektion (Fault Injection); Hot Patching zur Änderung der Logik oder zur Fehlerbehebung in Binärversionen; die Erstellung eines Moduls für Nginx, das die Entwicklung von Erweiterungen mithilfe von eBPF-Programmen ermöglicht (z. B. für dynamische Routenauswahl, Caching, Anwendung von Sicherheitsrichtlinien und Lastverteilung); Erweiterung der Möglichkeiten des FUSE-Subsystems (z. B. die Schaffung von eBPF-Programmen zur Erweiterung von Dateisystemen für Caching oder Zugriffsmanagement).
Quelle: opennet.ru
