From 3335e0471bc89c8d12dc414c538c97025df1341b Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 24 Nov 2025 23:51:08 +0530 Subject: [PATCH 1/3] Return None instead of KeyError for failure of BpfMap::lookup for missing key --- src/core/bpf_map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/bpf_map.cpp b/src/core/bpf_map.cpp index 213233f..794ad3a 100644 --- a/src/core/bpf_map.cpp +++ b/src/core/bpf_map.cpp @@ -42,7 +42,7 @@ py::object BpfMap::lookup(const py::object &key) const { value_span.data(), value_size_, BPF_ANY); if (ret < 0) { if (ret == -ENOENT) - throw py::key_error("Key not found in map '" + map_name_ + "'"); + return py::none(); throw BpfException("Failed to lookup key in map '" + map_name_ + "': " + std::strerror(-ret)); } From d4202cdc88040c65614f801778c5423074150bff Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Thu, 27 Nov 2025 03:43:24 +0530 Subject: [PATCH 2/3] Add basic userspace support for struct value type for maps --- src/bindings/main.cpp | 4 ++++ src/core/bpf_map.cpp | 24 ++++++++++++++++++++++++ src/core/bpf_map.h | 13 +++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/bindings/main.cpp b/src/bindings/main.cpp index 023215c..37148ac 100644 --- a/src/bindings/main.cpp +++ b/src/bindings/main.cpp @@ -65,6 +65,10 @@ PYBIND11_MODULE(pylibbpf, m) { .def("get_key_size", &BpfMap::get_key_size) .def("get_value_size", &BpfMap::get_value_size) .def("get_max_entries", &BpfMap::get_max_entries) + .def("set_value_struct", &BpfMap::set_value_struct, + py::arg("struct_name")) + .def("get_value_struct_name", &BpfMap::get_value_struct_name) + .def("has_struct_value", &BpfMap::has_struct_value) .def("__getitem__", &BpfMap::lookup, py::arg("key")) .def("__setitem__", &BpfMap::update, py::arg("key"), py::arg("value")) .def("__delitem__", &BpfMap::delete_elem, py::arg("key")); diff --git a/src/core/bpf_map.cpp b/src/core/bpf_map.cpp index 794ad3a..2c00ed4 100644 --- a/src/core/bpf_map.cpp +++ b/src/core/bpf_map.cpp @@ -25,6 +25,24 @@ BpfMap::BpfMap(std::shared_ptr parent, struct bpf_map *raw_map, value_size_ = bpf_map__value_size(map_); } +void BpfMap::set_value_struct(const std::string &struct_name) { + auto parent = parent_obj_.lock(); + if (!parent) { + throw BpfException("Parent BpfObject no longer exists"); + } + + struct_parser_ = parent->get_struct_parser(); + if (!struct_parser_) { + throw BpfException("No struct definitions available in BpfObject"); + } + + if (!struct_parser_->has_struct(struct_name)) { + throw BpfException("Unknown struct: " + struct_name); + } + + value_struct_name_ = struct_name; +} + py::object BpfMap::lookup(const py::object &key) const { if (map_fd_ < 0) throw BpfException("Map '" + map_name_ + "' is not initialized properly"); @@ -216,6 +234,12 @@ void BpfMap::python_to_bytes_inplace(const py::object &obj, } py::object BpfMap::bytes_to_python(std::span data) { + // NOTE: Struct parsing for value type + if (struct_parser_ && !value_struct_name_.empty()) { + py::bytes py_data(reinterpret_cast(data.data()), data.size()); + return struct_parser_->parse(value_struct_name_, py_data); + } + if (data.size() == 4) { uint32_t value; std::memcpy(&value, data.data(), 4); diff --git a/src/core/bpf_map.h b/src/core/bpf_map.h index e3d7af3..94bc551 100644 --- a/src/core/bpf_map.h +++ b/src/core/bpf_map.h @@ -9,6 +9,7 @@ #include class BpfObject; +class StructParser; namespace py = pybind11; @@ -20,6 +21,11 @@ class BpfMap : public std::enable_shared_from_this { std::string map_name_; __u32 key_size_, value_size_; + // TODO: For now, we'll only support struct parsing for value types + // later we can extend this to keys + std::shared_ptr struct_parser_; + std::string value_struct_name_; + template struct BufferManager { std::array stack_buf; std::vector heap_buf; @@ -52,6 +58,7 @@ class BpfMap : public std::enable_shared_from_this { py::dict items() const; py::list keys() const; py::list values() const; + void set_value_struct(const std::string &struct_name); [[nodiscard]] std::string get_name() const { return map_name_; } [[nodiscard]] int get_fd() const { return map_fd_; } @@ -62,6 +69,12 @@ class BpfMap : public std::enable_shared_from_this { [[nodiscard]] std::shared_ptr get_parent() const { return parent_obj_.lock(); } + [[nodiscard]] std::string get_value_struct_name() const { + return value_struct_name_; + } + [[nodiscard]] bool has_struct_value() const { + return !value_struct_name_.empty(); + } private: static void python_to_bytes_inplace(const py::object &obj, From 8ac2d67a76d58f26cce47860e1a1ecc1c82730a7 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Thu, 27 Nov 2025 03:55:58 +0530 Subject: [PATCH 3/3] Make bytes_to_python const non-static --- src/core/bpf_map.cpp | 3 ++- src/core/bpf_map.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/bpf_map.cpp b/src/core/bpf_map.cpp index 2c00ed4..81f4526 100644 --- a/src/core/bpf_map.cpp +++ b/src/core/bpf_map.cpp @@ -1,6 +1,7 @@ #include "core/bpf_map.h" #include "core/bpf_exception.h" #include "core/bpf_object.h" +#include "utils/struct_parser.h" #include #include #include @@ -233,7 +234,7 @@ void BpfMap::python_to_bytes_inplace(const py::object &obj, } } -py::object BpfMap::bytes_to_python(std::span data) { +py::object BpfMap::bytes_to_python(std::span data) const { // NOTE: Struct parsing for value type if (struct_parser_ && !value_struct_name_.empty()) { py::bytes py_data(reinterpret_cast(data.data()), data.size()); diff --git a/src/core/bpf_map.h b/src/core/bpf_map.h index 94bc551..3aa45e3 100644 --- a/src/core/bpf_map.h +++ b/src/core/bpf_map.h @@ -79,7 +79,7 @@ class BpfMap : public std::enable_shared_from_this { private: static void python_to_bytes_inplace(const py::object &obj, std::span buffer); - static py::object bytes_to_python(std::span data); + py::object bytes_to_python(std::span data) const; }; #endif // PYLIBBPF_BPF_MAP_H