From ec003a2c0a12ed1b7b32a0040f28568ed2896046 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Sun, 21 Sep 2025 18:02:49 +0530 Subject: [PATCH] addd example and support for load and attach --- .gitignore | 1 + README.md | 14 ++++++++++++-- examples/execve.py | 41 ++++++++++++++++++++++++++++++++++++++++ src/bindings/main.cpp | 1 + src/core/bpf_program.cpp | 36 +++++++++++++++++++++++------------ src/core/bpf_program.h | 9 +++++---- 6 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 examples/execve.py diff --git a/.gitignore b/.gitignore index 16e0c73..1aec23a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ _generate/ build/ *venv/ .idea/ +sudo-python.sh diff --git a/README.md b/README.md index 9f6d7cc..b5ff398 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # Py-libbpf +

+ + +

This library provides Python bindings for libbpf on Linux to make loading of eBPF object files easier. This is meant to be used along with `pythonbpf`, the eBPF Python DSL compiler. This library makes it possible to attach these programs to events in the kernel right from inside Python. -# Warning -IN DEVELOPMENT. DO NOT USE. +# IN DEVELOPMENT. DO NOT USE. ## Prerequisites @@ -19,6 +22,7 @@ Just clone this repository and pip install. Note the `--recursive` option which needed for the pybind11 submodule: ```bash +sudo apt install libelf-dev git clone --recursive https://github.com/varun-r-mallya/pylibbpf.git pip install . ``` @@ -26,5 +30,11 @@ pip install . With the `setup.py` file included in this example, the `pip install` command will invoke CMake and build the pybind11 module as specified in `CMakeLists.txt`. +## Development +Do this before running to make sure Python can manipulate bpf programs without sudo +```bash +sudo setcap cap_bpf,cap_sys_admin+ep /usr/bin/python3.12 +``` + ## Building the documentation The documentation here is still boilerplate. diff --git a/examples/execve.py b/examples/execve.py new file mode 100644 index 0000000..a0d5c68 --- /dev/null +++ b/examples/execve.py @@ -0,0 +1,41 @@ +from pythonbpf import bpf, map, section, bpfglobal, BPF +from ctypes import c_void_p, c_int64, c_int32, c_uint64 +from pythonbpf.helpers import ktime +from pythonbpf.maps import HashMap +import time + +@bpf +@map +def last() -> HashMap: + return HashMap(key=c_uint64, value=c_uint64, max_entries=1) + + +@bpf +@section("tracepoint/syscalls/sys_enter_execve") +def hello(ctx: c_void_p) -> c_int32: + print("entered") + print("multi constant support") + return c_int32(0) + + +@bpf +@section("tracepoint/syscalls/sys_exit_execve") +def hello_again(ctx: c_void_p) -> c_int64: + print("exited") + key = 0 + tsp = last().lookup(key) + print(tsp) + ts = ktime() + return c_int64(0) + + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + +b = BPF() +b.load_and_attach() +while True: + print("running") + time.sleep(1) diff --git a/src/bindings/main.cpp b/src/bindings/main.cpp index 4989446..ffbd296 100644 --- a/src/bindings/main.cpp +++ b/src/bindings/main.cpp @@ -33,6 +33,7 @@ PYBIND11_MODULE(pylibbpf, m) { .def("load", &BpfProgram::load) .def("attach", &BpfProgram::attach) // .def("detach", &BpfProgram::detach) + .def("load_and_attach", &BpfProgram::load_and_attach) .def("is_loaded", &BpfProgram::is_loaded) .def("is_attached", &BpfProgram::is_attached); diff --git a/src/core/bpf_program.cpp b/src/core/bpf_program.cpp index 0b59e60..5b46fab 100644 --- a/src/core/bpf_program.cpp +++ b/src/core/bpf_program.cpp @@ -1,10 +1,11 @@ #include "bpf_program.h" #include "bpf_exception.h" #include +#include -BpfProgram::BpfProgram(const std::string& object_path, const std::string& program_name) +BpfProgram::BpfProgram(std::string object_path, std::string program_name) : obj_(nullptr), prog_(nullptr), link_(nullptr), - object_path_(object_path), program_name_(program_name) { + object_path_(std::move(object_path)), program_name_(std::move(program_name)) { } BpfProgram::~BpfProgram() { @@ -28,9 +29,12 @@ bool BpfProgram::load() { throw BpfException("Program '" + program_name_ + "' not found in object"); } } else { - // Use the first program if no name specified - prog_ = bpf_object__next_program(obj_, nullptr); - if (!prog_) { + while ((prog_ = bpf_object__next_program(obj_, prog_)) != nullptr) { + programs.emplace_back(prog_, nullptr); + } + + // throw if no programs found + if (programs.empty()) { throw BpfException("No programs found in object file"); } } @@ -44,15 +48,23 @@ bool BpfProgram::load() { } bool BpfProgram::attach() { - if (!prog_) { - throw BpfException("Program not loaded"); - } + for (auto [prog, link] : programs) + { + if (!prog) { + throw BpfException("Program not loaded"); + } - link_ = bpf_program__attach(prog_); - if (libbpf_get_error(link_)) { - link_ = nullptr; - throw BpfException("Failed to attach BPF program"); + link = bpf_program__attach(prog); + if (libbpf_get_error(link)) { + link = nullptr; + throw BpfException("Failed to attach BPF program"); + } } return true; } + +void BpfProgram::load_and_attach() { + load(); + attach(); +} diff --git a/src/core/bpf_program.h b/src/core/bpf_program.h index c603f89..1cfe0ed 100644 --- a/src/core/bpf_program.h +++ b/src/core/bpf_program.h @@ -14,16 +14,17 @@ private: struct bpf_link* link_; std::string object_path_; std::string program_name_; - + std::vector> programs; public: - explicit BpfProgram(const std::string& object_path, const std::string& program_name = ""); + explicit BpfProgram(std::string object_path, std::string program_name = ""); ~BpfProgram(); bool load(); bool attach(); + void load_and_attach(); - bool is_loaded() const { return obj_ != nullptr; } - bool is_attached() const { return link_ != nullptr; } + [[nodiscard]] bool is_loaded() const { return obj_ != nullptr; } + [[nodiscard]] bool is_attached() const { return link_ != nullptr; } }; #endif //PYLIBBPF_BPF_PROGRAM_H