blob: 7279128512c4838be5f61543add4baa59dd14723 [file] [log] [blame] [edit]
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());
}
}
}