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; fn next(&mut self) -> Option { 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; fn next(&mut self) -> Option { 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; } /// Builder for creating an [`OpenObject`]. Typically the entry point into libbpf-rs. #[derive(Debug)] pub struct ObjectBuilder { name: Option, pin_root_path: Option, 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::() 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>(&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>(&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>(&mut self, path: P) -> Result { 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 { 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 { // 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, } 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) -> Self { Self { ptr } } /// Takes underlying `libbpf_sys::bpf_object` pointer. pub fn take_ptr(mut self) -> NonNull { 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> { 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> { 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> { 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> { 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 { 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.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, } 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) -> 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>> { 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> { 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> { 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> { 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> { 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.ptr } } impl Drop for Object { fn drop(&mut self) { unsafe { libbpf_sys::bpf_object__close(self.ptr.as_ptr()); } } }