| use core::ffi::c_void; |
| use std::ffi::CStr; |
| use std::ffi::CString; |
| use std::ffi::OsStr; |
| use std::mem; |
| use std::os::unix::ffi::OsStrExt as _; |
| use std::path::Path; |
| use std::ptr; |
| use std::ptr::addr_of; |
| use std::ptr::NonNull; |
| |
| use crate::map::map_fd; |
| use crate::set_print; |
| use crate::util; |
| use crate::util::validate_bpf_ret; |
| use crate::Btf; |
| use crate::ErrorExt as _; |
| use crate::Map; |
| use crate::MapMut; |
| use crate::OpenMap; |
| use crate::OpenMapMut; |
| use crate::OpenProgram; |
| use crate::OpenProgramMut; |
| use crate::PrintLevel; |
| use crate::Program; |
| use crate::ProgramMut; |
| use crate::Result; |
| |
| |
| /// An iterator over the maps in a BPF object. |
| #[derive(Debug)] |
| pub struct MapIter<'obj> { |
| obj: &'obj libbpf_sys::bpf_object, |
| last: *mut libbpf_sys::bpf_map, |
| } |
| |
| impl<'obj> MapIter<'obj> { |
| /// Create a new iterator over the maps of the given BPF object. |
| pub fn new(obj: &'obj libbpf_sys::bpf_object) -> Self { |
| Self { |
| obj, |
| last: ptr::null_mut(), |
| } |
| } |
| } |
| |
| impl Iterator for MapIter<'_> { |
| type Item = NonNull<libbpf_sys::bpf_map>; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| self.last = unsafe { libbpf_sys::bpf_object__next_map(self.obj, self.last) }; |
| NonNull::new(self.last) |
| } |
| } |
| |
| |
| /// An iterator over the programs in a BPF object. |
| #[derive(Debug)] |
| pub struct ProgIter<'obj> { |
| obj: &'obj libbpf_sys::bpf_object, |
| last: *mut libbpf_sys::bpf_program, |
| } |
| |
| impl<'obj> ProgIter<'obj> { |
| /// Create a new iterator over the programs of the given BPF object. |
| pub fn new(obj: &'obj libbpf_sys::bpf_object) -> Self { |
| Self { |
| obj, |
| last: ptr::null_mut(), |
| } |
| } |
| } |
| |
| impl Iterator for ProgIter<'_> { |
| type Item = NonNull<libbpf_sys::bpf_program>; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| self.last = unsafe { libbpf_sys::bpf_object__next_program(self.obj, self.last) }; |
| NonNull::new(self.last) |
| } |
| } |
| |
| |
| /// A trait implemented for types that are thin wrappers around `libbpf` types. |
| /// |
| /// The trait provides access to the underlying `libbpf` (or `libbpf-sys`) |
| /// object. In many cases, this enables direct usage of `libbpf-sys` |
| /// functionality when higher-level bindings are not yet provided by this crate. |
| pub trait AsRawLibbpf { |
| /// The underlying `libbpf` type. |
| type LibbpfType; |
| |
| /// Retrieve the underlying `libbpf` object. |
| /// |
| /// # Warning |
| /// By virtue of working with a mutable raw pointer this method effectively |
| /// circumvents mutability and liveness checks. While by-design, usage is |
| /// meant as an escape-hatch more than anything else. If you find yourself |
| /// making use of it, please consider discussing your workflow with crate |
| /// maintainers to see if it would make sense to provide safer wrappers. |
| fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType>; |
| } |
| |
| /// Builder for creating an [`OpenObject`]. Typically the entry point into libbpf-rs. |
| #[derive(Debug)] |
| pub struct ObjectBuilder { |
| name: Option<CString>, |
| pin_root_path: Option<CString>, |
| |
| opts: libbpf_sys::bpf_object_open_opts, |
| } |
| |
| impl Default for ObjectBuilder { |
| fn default() -> Self { |
| let opts = libbpf_sys::bpf_object_open_opts { |
| sz: mem::size_of::<libbpf_sys::bpf_object_open_opts>() as libbpf_sys::size_t, |
| object_name: ptr::null(), |
| relaxed_maps: false, |
| pin_root_path: ptr::null(), |
| kconfig: ptr::null(), |
| btf_custom_path: ptr::null(), |
| kernel_log_buf: ptr::null_mut(), |
| kernel_log_size: 0, |
| kernel_log_level: 0, |
| ..Default::default() |
| }; |
| Self { |
| name: None, |
| pin_root_path: None, |
| opts, |
| } |
| } |
| } |
| |
| impl ObjectBuilder { |
| /// Override the generated name that would have been inferred from the constructor. |
| pub fn name<T: AsRef<str>>(&mut self, name: T) -> Result<&mut Self> { |
| self.name = Some(util::str_to_cstring(name.as_ref())?); |
| self.opts.object_name = self.name.as_ref().map_or(ptr::null(), |p| p.as_ptr()); |
| Ok(self) |
| } |
| |
| /// Set the pin_root_path for maps that are pinned by name. |
| /// |
| /// By default, this is NULL which bpf translates to /sys/fs/bpf |
| pub fn pin_root_path<T: AsRef<Path>>(&mut self, path: T) -> Result<&mut Self> { |
| self.pin_root_path = Some(util::path_to_cstring(path)?); |
| self.opts.pin_root_path = self |
| .pin_root_path |
| .as_ref() |
| .map_or(ptr::null(), |p| p.as_ptr()); |
| Ok(self) |
| } |
| |
| /// Option to parse map definitions non-strictly, allowing extra attributes/data |
| pub fn relaxed_maps(&mut self, relaxed_maps: bool) -> &mut Self { |
| self.opts.relaxed_maps = relaxed_maps; |
| self |
| } |
| |
| /// Option to print debug output to stderr. |
| /// |
| /// Note: This function uses [`set_print`] internally and will overwrite any callbacks |
| /// currently in use. |
| pub fn debug(&mut self, dbg: bool) -> &mut Self { |
| if dbg { |
| set_print(Some((PrintLevel::Debug, |_, s| print!("{s}")))); |
| } else { |
| set_print(None); |
| } |
| self |
| } |
| |
| /// Open an object using the provided path on the file system. |
| pub fn open_file<P: AsRef<Path>>(&mut self, path: P) -> Result<OpenObject> { |
| let path = path.as_ref(); |
| let path_c = util::path_to_cstring(path)?; |
| let path_ptr = path_c.as_ptr(); |
| let opts_ptr = self.as_libbpf_object().as_ptr(); |
| |
| let ptr = unsafe { libbpf_sys::bpf_object__open_file(path_ptr, opts_ptr) }; |
| let ptr = validate_bpf_ret(ptr) |
| .with_context(|| format!("failed to open object from `{}`", path.display()))?; |
| |
| let obj = unsafe { OpenObject::from_ptr(ptr) }; |
| Ok(obj) |
| } |
| |
| /// Open an object from memory. |
| pub fn open_memory(&mut self, mem: &[u8]) -> Result<OpenObject> { |
| let opts_ptr = self.as_libbpf_object().as_ptr(); |
| let ptr = unsafe { |
| libbpf_sys::bpf_object__open_mem( |
| mem.as_ptr() as *const c_void, |
| mem.len() as libbpf_sys::size_t, |
| opts_ptr, |
| ) |
| }; |
| let ptr = validate_bpf_ret(ptr).context("failed to open object from memory")?; |
| let obj = unsafe { OpenObject::from_ptr(ptr) }; |
| Ok(obj) |
| } |
| } |
| |
| impl AsRawLibbpf for ObjectBuilder { |
| type LibbpfType = libbpf_sys::bpf_object_open_opts; |
| |
| /// Retrieve the underlying [`libbpf_sys::bpf_object_open_opts`]. |
| fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> { |
| // SAFETY: A reference is always a valid pointer. |
| unsafe { NonNull::new_unchecked(addr_of!(self.opts).cast_mut()) } |
| } |
| } |
| |
| |
| /// Represents an opened (but not yet loaded) BPF object file. |
| /// |
| /// Use this object to access [`OpenMap`]s and [`OpenProgram`]s. |
| #[derive(Debug)] |
| #[repr(transparent)] |
| pub struct OpenObject { |
| ptr: NonNull<libbpf_sys::bpf_object>, |
| } |
| |
| impl OpenObject { |
| /// Takes ownership from pointer. |
| /// |
| /// # Safety |
| /// |
| /// Operations on the returned object are undefined if `ptr` is any one of: |
| /// - null |
| /// - points to an unopened `bpf_object` |
| /// - points to a loaded `bpf_object` |
| /// |
| /// It is not safe to manipulate `ptr` after this operation. |
| pub unsafe fn from_ptr(ptr: NonNull<libbpf_sys::bpf_object>) -> Self { |
| Self { ptr } |
| } |
| |
| /// Takes underlying `libbpf_sys::bpf_object` pointer. |
| pub fn take_ptr(mut self) -> NonNull<libbpf_sys::bpf_object> { |
| let ptr = { |
| let Self { ptr } = &mut self; |
| *ptr |
| }; |
| // avoid double free of self.ptr |
| mem::forget(self); |
| ptr |
| } |
| |
| /// Retrieve the object's name. |
| pub fn name(&self) -> Option<&OsStr> { |
| // SAFETY: We ensured `ptr` is valid during construction. |
| let name_ptr = unsafe { libbpf_sys::bpf_object__name(self.ptr.as_ptr()) }; |
| // SAFETY: `libbpf_get_error` is always safe to call. |
| let err = unsafe { libbpf_sys::libbpf_get_error(name_ptr as *const _) }; |
| if err != 0 { |
| return None |
| } |
| let name_c_str = unsafe { CStr::from_ptr(name_ptr) }; |
| let str = OsStr::from_bytes(name_c_str.to_bytes()); |
| Some(str) |
| } |
| |
| /// Retrieve an iterator over all BPF maps in the object. |
| pub fn maps(&self) -> impl Iterator<Item = OpenMap<'_>> { |
| MapIter::new(unsafe { self.ptr.as_ref() }).map(|ptr| unsafe { OpenMap::new(ptr.as_ref()) }) |
| } |
| |
| /// Retrieve an iterator over all BPF maps in the object. |
| pub fn maps_mut(&mut self) -> impl Iterator<Item = OpenMapMut<'_>> { |
| MapIter::new(unsafe { self.ptr.as_ref() }) |
| .map(|mut ptr| unsafe { OpenMapMut::new_mut(ptr.as_mut()) }) |
| } |
| |
| /// Retrieve an iterator over all BPF programs in the object. |
| pub fn progs(&self) -> impl Iterator<Item = OpenProgram<'_>> { |
| ProgIter::new(unsafe { self.ptr.as_ref() }) |
| .map(|ptr| unsafe { OpenProgram::new(ptr.as_ref()) }) |
| } |
| |
| /// Retrieve an iterator over all BPF programs in the object. |
| pub fn progs_mut(&mut self) -> impl Iterator<Item = OpenProgramMut<'_>> { |
| ProgIter::new(unsafe { self.ptr.as_ref() }) |
| .map(|mut ptr| unsafe { OpenProgramMut::new_mut(ptr.as_mut()) }) |
| } |
| |
| /// Load the maps and programs contained in this BPF object into the system. |
| pub fn load(self) -> Result<Object> { |
| let ret = unsafe { libbpf_sys::bpf_object__load(self.ptr.as_ptr()) }; |
| let () = util::parse_ret(ret)?; |
| |
| let obj = unsafe { Object::from_ptr(self.take_ptr()) }; |
| |
| Ok(obj) |
| } |
| } |
| |
| impl AsRawLibbpf for OpenObject { |
| type LibbpfType = libbpf_sys::bpf_object; |
| |
| /// Retrieve the underlying [`libbpf_sys::bpf_object`]. |
| fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> { |
| self.ptr |
| } |
| } |
| |
| impl Drop for OpenObject { |
| fn drop(&mut self) { |
| // `self.ptr` may be null if `load()` was called. This is ok: libbpf noops |
| unsafe { |
| libbpf_sys::bpf_object__close(self.ptr.as_ptr()); |
| } |
| } |
| } |
| |
| /// Represents a loaded BPF object file. |
| /// |
| /// An `Object` is logically in charge of all the contained [`Program`]s and [`Map`]s as well as |
| /// the associated metadata and runtime state that underpins the userspace portions of BPF program |
| /// execution. As a libbpf-rs user, you must keep the `Object` alive during the entire lifetime |
| /// of your interaction with anything inside the `Object`. |
| /// |
| /// Note that this is an explanation of the motivation -- Rust's lifetime system should already be |
| /// enforcing this invariant. |
| #[derive(Debug)] |
| #[repr(transparent)] |
| pub struct Object { |
| ptr: NonNull<libbpf_sys::bpf_object>, |
| } |
| |
| impl Object { |
| /// Takes ownership from pointer. |
| /// |
| /// # Safety |
| /// |
| /// If `ptr` is not already loaded then further operations on the returned object are |
| /// undefined. |
| /// |
| /// It is not safe to manipulate `ptr` after this operation. |
| pub unsafe fn from_ptr(ptr: NonNull<libbpf_sys::bpf_object>) -> Self { |
| Self { ptr } |
| } |
| |
| /// Retrieve the object's name. |
| pub fn name(&self) -> Option<&OsStr> { |
| // SAFETY: We ensured `ptr` is valid during construction. |
| let name_ptr = unsafe { libbpf_sys::bpf_object__name(self.ptr.as_ptr()) }; |
| // SAFETY: `libbpf_get_error` is always safe to call. |
| let err = unsafe { libbpf_sys::libbpf_get_error(name_ptr as *const _) }; |
| if err != 0 { |
| return None |
| } |
| let name_c_str = unsafe { CStr::from_ptr(name_ptr) }; |
| let str = OsStr::from_bytes(name_c_str.to_bytes()); |
| Some(str) |
| } |
| |
| /// Parse the btf information associated with this bpf object. |
| pub fn btf(&self) -> Result<Option<Btf<'_>>> { |
| Btf::from_bpf_object(unsafe { &*self.ptr.as_ptr() }) |
| } |
| |
| /// Retrieve an iterator over all BPF maps in the object. |
| pub fn maps(&self) -> impl Iterator<Item = Map<'_>> { |
| MapIter::new(unsafe { self.ptr.as_ref() }) |
| .filter(|ptr| map_fd(*ptr).is_some()) |
| .map(|ptr| unsafe { Map::new(ptr.as_ref()) }) |
| } |
| |
| /// Retrieve an iterator over all BPF maps in the object. |
| pub fn maps_mut(&mut self) -> impl Iterator<Item = MapMut<'_>> { |
| MapIter::new(unsafe { self.ptr.as_ref() }) |
| .filter(|ptr| map_fd(*ptr).is_some()) |
| .map(|mut ptr| unsafe { MapMut::new_mut(ptr.as_mut()) }) |
| } |
| |
| /// Retrieve an iterator over all BPF programs in the object. |
| pub fn progs(&self) -> impl Iterator<Item = Program<'_>> { |
| ProgIter::new(unsafe { self.ptr.as_ref() }).map(|ptr| unsafe { Program::new(ptr.as_ref()) }) |
| } |
| |
| /// Retrieve an iterator over all BPF programs in the object. |
| pub fn progs_mut(&self) -> impl Iterator<Item = ProgramMut<'_>> { |
| ProgIter::new(unsafe { self.ptr.as_ref() }) |
| .map(|mut ptr| unsafe { ProgramMut::new_mut(ptr.as_mut()) }) |
| } |
| } |
| |
| impl AsRawLibbpf for Object { |
| type LibbpfType = libbpf_sys::bpf_object; |
| |
| /// Retrieve the underlying [`libbpf_sys::bpf_object`]. |
| fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> { |
| self.ptr |
| } |
| } |
| |
| impl Drop for Object { |
| fn drop(&mut self) { |
| unsafe { |
| libbpf_sys::bpf_object__close(self.ptr.as_ptr()); |
| } |
| } |
| } |