use core::ffi::c_void;
use std::ffi::CStr;
use std::ffi::CString;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fmt::Debug;
use std::fs::remove_file;
use std::io;
use std::marker::PhantomData;
use std::mem;
use std::mem::transmute;
use std::ops::Deref;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::AsFd;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::BorrowedFd;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::OwnedFd;
use std::os::unix::io::RawFd;
use std::path::Path;
use std::ptr;
use std::ptr::NonNull;
use std::slice;
use std::slice::from_raw_parts;
use bitflags::bitflags;
use libbpf_sys::bpf_map_info;
use libbpf_sys::bpf_obj_get_info_by_fd;
use crate::util;
use crate::util::parse_ret_i32;
use crate::util::validate_bpf_ret;
use crate::AsRawLibbpf;
use crate::Error;
use crate::ErrorExt as _;
use crate::Link;
use crate::Mut;
use crate::Result;
pub type OpenMap<'obj> = OpenMapImpl<'obj>;
pub type OpenMapMut<'obj> = OpenMapImpl<'obj, Mut>;
#[derive(Debug)]
#[repr(transparent)]
pub struct OpenMapImpl<'obj, T = ()> {
ptr: NonNull<libbpf_sys::bpf_map>,
_phantom: PhantomData<&'obj T>,
}
#[allow(missing_docs)]
impl<'obj> OpenMap<'obj> {
pub fn new(object: &'obj libbpf_sys::bpf_map) -> Self {
Self {
ptr: unsafe { NonNull::new_unchecked(object as *const _ as *mut _) },
_phantom: PhantomData,
}
}
pub fn name(&self) -> &OsStr {
let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
OsStr::from_bytes(name_c_str.to_bytes())
}
pub fn map_type(&self) -> MapType {
let ty = unsafe { libbpf_sys::bpf_map__type(self.ptr.as_ptr()) };
MapType::from(ty)
}
fn initial_value_raw(&self) -> (*mut u8, usize) {
let mut size = 0u64;
let ptr = unsafe {
libbpf_sys::bpf_map__initial_value(self.ptr.as_ptr(), &mut size as *mut _ as _)
};
(ptr.cast(), size as _)
}
pub fn initial_value(&self) -> Option<&[u8]> {
let (ptr, size) = self.initial_value_raw();
if ptr.is_null() {
None
} else {
let data = unsafe { slice::from_raw_parts(ptr.cast::<u8>(), size) };
Some(data)
}
}
}
impl<'obj> OpenMapMut<'obj> {
pub fn new_mut(object: &'obj mut libbpf_sys::bpf_map) -> Self {
Self {
ptr: unsafe { NonNull::new_unchecked(object as *mut _) },
_phantom: PhantomData,
}
}
pub fn initial_value_mut(&mut self) -> Option<&mut [u8]> {
let (ptr, size) = self.initial_value_raw();
if ptr.is_null() {
None
} else {
let data = unsafe { slice::from_raw_parts_mut(ptr.cast::<u8>(), size) };
Some(data)
}
}
pub fn set_map_ifindex(&mut self, idx: u32) {
unsafe { libbpf_sys::bpf_map__set_ifindex(self.ptr.as_ptr(), idx) };
}
pub fn set_initial_value(&mut self, data: &[u8]) -> Result<()> {
let ret = unsafe {
libbpf_sys::bpf_map__set_initial_value(
self.ptr.as_ptr(),
data.as_ptr() as *const c_void,
data.len() as libbpf_sys::size_t,
)
};
util::parse_ret(ret)
}
pub fn set_type(&mut self, ty: MapType) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_map__set_type(self.ptr.as_ptr(), ty as u32) };
util::parse_ret(ret)
}
pub fn set_key_size(&mut self, size: u32) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_map__set_key_size(self.ptr.as_ptr(), size) };
util::parse_ret(ret)
}
pub fn set_value_size(&mut self, size: u32) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_map__set_value_size(self.ptr.as_ptr(), size) };
util::parse_ret(ret)
}
pub fn set_max_entries(&mut self, count: u32) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_map__set_max_entries(self.ptr.as_ptr(), count) };
util::parse_ret(ret)
}
pub fn set_map_flags(&mut self, flags: u32) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_map__set_map_flags(self.ptr.as_ptr(), flags) };
util::parse_ret(ret)
}
#[allow(missing_docs)]
pub fn set_numa_node(&mut self, numa_node: u32) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_map__set_numa_node(self.ptr.as_ptr(), numa_node) };
util::parse_ret(ret)
}
#[allow(missing_docs)]
pub fn set_inner_map_fd(&mut self, inner_map_fd: BorrowedFd<'_>) -> Result<()> {
let ret = unsafe {
libbpf_sys::bpf_map__set_inner_map_fd(self.ptr.as_ptr(), inner_map_fd.as_raw_fd())
};
util::parse_ret(ret)
}
#[allow(missing_docs)]
pub fn set_map_extra(&mut self, map_extra: u64) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_map__set_map_extra(self.ptr.as_ptr(), map_extra) };
util::parse_ret(ret)
}
pub fn set_autocreate(&mut self, autocreate: bool) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_map__set_autocreate(self.ptr.as_ptr(), autocreate) };
util::parse_ret(ret)
}
pub fn set_pin_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let path_c = util::path_to_cstring(path)?;
let path_ptr = path_c.as_ptr();
let ret = unsafe { libbpf_sys::bpf_map__set_pin_path(self.ptr.as_ptr(), path_ptr) };
util::parse_ret(ret)
}
pub fn reuse_fd(&mut self, fd: BorrowedFd<'_>) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_map__reuse_fd(self.ptr.as_ptr(), fd.as_raw_fd()) };
util::parse_ret(ret)
}
pub fn reuse_pinned_map<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let cstring = util::path_to_cstring(path)?;
let fd = unsafe { libbpf_sys::bpf_obj_get(cstring.as_ptr()) };
if fd < 0 {
return Err(Error::from(io::Error::last_os_error()));
}
let fd = unsafe { OwnedFd::from_raw_fd(fd) };
let reuse_result = self.reuse_fd(fd.as_fd());
reuse_result
}
}
impl<'obj> Deref for OpenMapMut<'obj> {
type Target = OpenMap<'obj>;
fn deref(&self) -> &Self::Target {
unsafe { transmute::<&OpenMapMut<'obj>, &OpenMap<'obj>>(self) }
}
}
impl<T> AsRawLibbpf for OpenMapImpl<'_, T> {
type LibbpfType = libbpf_sys::bpf_map;
fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
self.ptr
}
}
pub(crate) fn map_fd(map: NonNull<libbpf_sys::bpf_map>) -> Option<RawFd> {
let fd = unsafe { libbpf_sys::bpf_map__fd(map.as_ptr()) };
let fd = util::parse_ret_i32(fd).ok().map(|fd| fd as RawFd);
fd
}
fn percpu_aligned_value_size<M>(map: &M) -> usize
where
M: MapCore + ?Sized,
{
let val_size = map.value_size() as usize;
util::roundup(val_size, 8)
}
fn percpu_buffer_size<M>(map: &M) -> Result<usize>
where
M: MapCore + ?Sized,
{
let aligned_val_size = percpu_aligned_value_size(map);
let ncpu = crate::num_possible_cpus()?;
Ok(ncpu * aligned_val_size)
}
fn map_key<M>(map: &M, key: &[u8]) -> *const c_void
where
M: MapCore + ?Sized,
{
if map.key_size() == 0 && map.map_type().is_keyless() {
return ptr::null();
}
key.as_ptr() as *const c_void
}
fn lookup_raw<M>(map: &M, key: &[u8], flags: MapFlags, out_size: usize) -> Result<Option<Vec<u8>>>
where
M: MapCore + ?Sized,
{
if key.len() != map.key_size() as usize {
return Err(Error::with_invalid_data(format!(
"key_size {} != {}",
key.len(),
map.key_size()
)));
};
let mut out: Vec<u8> = Vec::with_capacity(out_size);
let ret = unsafe {
libbpf_sys::bpf_map_lookup_elem_flags(
map.as_fd().as_raw_fd(),
map_key(map, key),
out.as_mut_ptr() as *mut c_void,
flags.bits(),
)
};
if ret == 0 {
unsafe {
out.set_len(out_size);
}
Ok(Some(out))
} else {
let err = io::Error::last_os_error();
if err.kind() == io::ErrorKind::NotFound {
Ok(None)
} else {
Err(Error::from(err))
}
}
}
fn update_raw<M>(map: &M, key: &[u8], value: &[u8], flags: MapFlags) -> Result<()>
where
M: MapCore + ?Sized,
{
if key.len() != map.key_size() as usize {
return Err(Error::with_invalid_data(format!(
"key_size {} != {}",
key.len(),
map.key_size()
)));
};
let ret = unsafe {
libbpf_sys::bpf_map_update_elem(
map.as_fd().as_raw_fd(),
map_key(map, key),
value.as_ptr() as *const c_void,
flags.bits(),
)
};
util::parse_ret(ret)
}
#[allow(clippy::wildcard_imports)]
mod private {
use super::*;
pub trait Sealed {}
impl<T> Sealed for MapImpl<'_, T> {}
impl Sealed for MapHandle {}
}
pub trait MapCore: Debug + AsFd + private::Sealed {
fn name(&self) -> &OsStr;
fn map_type(&self) -> MapType;
fn key_size(&self) -> u32;
fn value_size(&self) -> u32;
#[inline]
fn info(&self) -> Result<MapInfo> {
MapInfo::new(self.as_fd())
}
fn keys(&self) -> MapKeyIter<'_> {
MapKeyIter::new(self.as_fd(), self.key_size())
}
fn lookup(&self, key: &[u8], flags: MapFlags) -> Result<Option<Vec<u8>>> {
if self.map_type().is_bloom_filter() {
return Err(Error::with_invalid_data(
"lookup_bloom_filter() must be used for bloom filter maps",
));
}
if self.map_type().is_percpu() {
return Err(Error::with_invalid_data(format!(
"lookup_percpu() must be used for per-cpu maps (type of the map is {:?})",
self.map_type(),
)));
}
let out_size = self.value_size() as usize;
lookup_raw(self, key, flags, out_size)
}
fn lookup_bloom_filter(&self, value: &[u8]) -> Result<bool> {
let ret = unsafe {
libbpf_sys::bpf_map_lookup_elem(
self.as_fd().as_raw_fd(),
ptr::null(),
value.to_vec().as_mut_ptr() as *mut c_void,
)
};
if ret == 0 {
Ok(true)
} else {
let err = io::Error::last_os_error();
if err.kind() == io::ErrorKind::NotFound {
Ok(false)
} else {
Err(Error::from(err))
}
}
}
fn lookup_percpu(&self, key: &[u8], flags: MapFlags) -> Result<Option<Vec<Vec<u8>>>> {
if !self.map_type().is_percpu() && self.map_type() != MapType::Unknown {
return Err(Error::with_invalid_data(format!(
"lookup() must be used for maps that are not per-cpu (type of the map is {:?})",
self.map_type(),
)));
}
let val_size = self.value_size() as usize;
let aligned_val_size = percpu_aligned_value_size(self);
let out_size = percpu_buffer_size(self)?;
let raw_res = lookup_raw(self, key, flags, out_size)?;
if let Some(raw_vals) = raw_res {
let mut out = Vec::new();
for chunk in raw_vals.chunks_exact(aligned_val_size) {
out.push(chunk[..val_size].to_vec());
}
Ok(Some(out))
} else {
Ok(None)
}
}
fn delete(&self, key: &[u8]) -> Result<()> {
if key.len() != self.key_size() as usize {
return Err(Error::with_invalid_data(format!(
"key_size {} != {}",
key.len(),
self.key_size()
)));
};
let ret = unsafe {
libbpf_sys::bpf_map_delete_elem(self.as_fd().as_raw_fd(), key.as_ptr() as *const c_void)
};
util::parse_ret(ret)
}
fn delete_batch(
&self,
keys: &[u8],
count: u32,
elem_flags: MapFlags,
flags: MapFlags,
) -> Result<()> {
if keys.len() as u32 / count != self.key_size() || (keys.len() as u32) % count != 0 {
return Err(Error::with_invalid_data(format!(
"batch key_size {} != {} * {}",
keys.len(),
self.key_size(),
count
)));
};
#[allow(clippy::needless_update)]
let opts = libbpf_sys::bpf_map_batch_opts {
sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
elem_flags: elem_flags.bits(),
flags: flags.bits(),
..Default::default()
};
let mut count = count;
let ret = unsafe {
libbpf_sys::bpf_map_delete_batch(
self.as_fd().as_raw_fd(),
keys.as_ptr() as *const c_void,
&mut count,
&opts as *const libbpf_sys::bpf_map_batch_opts,
)
};
util::parse_ret(ret)
}
fn lookup_and_delete(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
if key.len() != self.key_size() as usize {
return Err(Error::with_invalid_data(format!(
"key_size {} != {}",
key.len(),
self.key_size()
)));
};
let mut out: Vec<u8> = Vec::with_capacity(self.value_size() as usize);
let ret = unsafe {
libbpf_sys::bpf_map_lookup_and_delete_elem(
self.as_fd().as_raw_fd(),
map_key(self, key),
out.as_mut_ptr() as *mut c_void,
)
};
if ret == 0 {
unsafe {
out.set_len(self.value_size() as usize);
}
Ok(Some(out))
} else {
let err = io::Error::last_os_error();
if err.kind() == io::ErrorKind::NotFound {
Ok(None)
} else {
Err(Error::from(err))
}
}
}
fn update(&self, key: &[u8], value: &[u8], flags: MapFlags) -> Result<()> {
if self.map_type().is_percpu() {
return Err(Error::with_invalid_data(format!(
"update_percpu() must be used for per-cpu maps (type of the map is {:?})",
self.map_type(),
)));
}
if value.len() != self.value_size() as usize {
return Err(Error::with_invalid_data(format!(
"value_size {} != {}",
value.len(),
self.value_size()
)));
};
update_raw(self, key, value, flags)
}
fn update_batch(
&self,
keys: &[u8],
values: &[u8],
count: u32,
elem_flags: MapFlags,
flags: MapFlags,
) -> Result<()> {
if keys.len() as u32 / count != self.key_size() || (keys.len() as u32) % count != 0 {
return Err(Error::with_invalid_data(format!(
"batch key_size {} != {} * {}",
keys.len(),
self.key_size(),
count
)));
};
if values.len() as u32 / count != self.value_size() || (values.len() as u32) % count != 0 {
return Err(Error::with_invalid_data(format!(
"batch value_size {} != {} * {}",
values.len(),
self.value_size(),
count
)));
}
#[allow(clippy::needless_update)]
let opts = libbpf_sys::bpf_map_batch_opts {
sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
elem_flags: elem_flags.bits(),
flags: flags.bits(),
..Default::default()
};
let mut count = count;
let ret = unsafe {
libbpf_sys::bpf_map_update_batch(
self.as_fd().as_raw_fd(),
keys.as_ptr() as *const c_void,
values.as_ptr() as *const c_void,
&mut count,
&opts as *const libbpf_sys::bpf_map_batch_opts,
)
};
util::parse_ret(ret)
}
fn update_percpu(&self, key: &[u8], values: &[Vec<u8>], flags: MapFlags) -> Result<()> {
if !self.map_type().is_percpu() && self.map_type() != MapType::Unknown {
return Err(Error::with_invalid_data(format!(
"update() must be used for maps that are not per-cpu (type of the map is {:?})",
self.map_type(),
)));
}
if values.len() != crate::num_possible_cpus()? {
return Err(Error::with_invalid_data(format!(
"number of values {} != number of cpus {}",
values.len(),
crate::num_possible_cpus()?
)));
};
let val_size = self.value_size() as usize;
let aligned_val_size = percpu_aligned_value_size(self);
let buf_size = percpu_buffer_size(self)?;
let mut value_buf = vec![0; buf_size];
for (i, val) in values.iter().enumerate() {
if val.len() != val_size {
return Err(Error::with_invalid_data(format!(
"value size for cpu {} is {} != {}",
i,
val.len(),
val_size
)));
}
value_buf[(i * aligned_val_size)..(i * aligned_val_size + val_size)]
.copy_from_slice(val);
}
update_raw(self, key, &value_buf, flags)
}
}
pub type Map<'obj> = MapImpl<'obj>;
pub type MapMut<'obj> = MapImpl<'obj, Mut>;
#[derive(Debug)]
pub struct MapImpl<'obj, T = ()> {
ptr: NonNull<libbpf_sys::bpf_map>,
_phantom: PhantomData<&'obj T>,
}
impl<'obj> Map<'obj> {
pub fn new(map: &'obj libbpf_sys::bpf_map) -> Self {
let ptr = unsafe { NonNull::new_unchecked(map as *const _ as *mut _) };
assert!(
map_fd(ptr).is_some(),
"provided BPF map does not have file descriptor"
);
Self {
ptr,
_phantom: PhantomData,
}
}
#[doc(hidden)]
pub unsafe fn from_map_without_fd(ptr: NonNull<libbpf_sys::bpf_map>) -> Self {
Self {
ptr,
_phantom: PhantomData,
}
}
pub fn is_pinned(&self) -> bool {
unsafe { libbpf_sys::bpf_map__is_pinned(self.ptr.as_ptr()) }
}
pub fn get_pin_path(&self) -> Option<&OsStr> {
let path_ptr = unsafe { libbpf_sys::bpf_map__pin_path(self.ptr.as_ptr()) };
if path_ptr.is_null() {
return None;
}
let path_c_str = unsafe { CStr::from_ptr(path_ptr) };
Some(OsStr::from_bytes(path_c_str.to_bytes()))
}
}
impl<'obj> MapMut<'obj> {
pub fn new_mut(map: &'obj mut libbpf_sys::bpf_map) -> Self {
let ptr = unsafe { NonNull::new_unchecked(map as *mut _) };
assert!(
map_fd(ptr).is_some(),
"provided BPF map does not have file descriptor"
);
Self {
ptr,
_phantom: PhantomData,
}
}
pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let path_c = util::path_to_cstring(path)?;
let path_ptr = path_c.as_ptr();
let ret = unsafe { libbpf_sys::bpf_map__pin(self.ptr.as_ptr(), path_ptr) };
util::parse_ret(ret)
}
pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let path_c = util::path_to_cstring(path)?;
let path_ptr = path_c.as_ptr();
let ret = unsafe { libbpf_sys::bpf_map__unpin(self.ptr.as_ptr(), path_ptr) };
util::parse_ret(ret)
}
pub fn attach_struct_ops(&mut self) -> Result<Link> {
if self.map_type() != MapType::StructOps {
return Err(Error::with_invalid_data(format!(
"Invalid map type ({:?}) for attach_struct_ops()",
self.map_type(),
)));
}
let ptr = unsafe { libbpf_sys::bpf_map__attach_struct_ops(self.ptr.as_ptr()) };
let ptr = validate_bpf_ret(ptr).context("failed to attach struct_ops")?;
let link = unsafe { Link::new(ptr) };
Ok(link)
}
}
impl<'obj> Deref for MapMut<'obj> {
type Target = Map<'obj>;
fn deref(&self) -> &Self::Target {
unsafe { transmute::<&MapMut<'obj>, &Map<'obj>>(self) }
}
}
impl<T> AsFd for MapImpl<'_, T> {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
let fd = map_fd(self.ptr).unwrap();
let fd = unsafe { BorrowedFd::borrow_raw(fd as _) };
fd
}
}
impl<T> MapCore for MapImpl<'_, T>
where
T: Debug,
{
fn name(&self) -> &OsStr {
let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
OsStr::from_bytes(name_c_str.to_bytes())
}
#[inline]
fn map_type(&self) -> MapType {
let ty = unsafe { libbpf_sys::bpf_map__type(self.ptr.as_ptr()) };
MapType::from(ty)
}
#[inline]
fn key_size(&self) -> u32 {
unsafe { libbpf_sys::bpf_map__key_size(self.ptr.as_ptr()) }
}
#[inline]
fn value_size(&self) -> u32 {
unsafe { libbpf_sys::bpf_map__value_size(self.ptr.as_ptr()) }
}
}
impl AsRawLibbpf for Map<'_> {
type LibbpfType = libbpf_sys::bpf_map;
#[inline]
fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
self.ptr
}
}
#[derive(Debug)]
pub struct MapHandle {
fd: OwnedFd,
name: OsString,
ty: MapType,
key_size: u32,
value_size: u32,
}
impl MapHandle {
pub fn create<T: AsRef<OsStr>>(
map_type: MapType,
name: Option<T>,
key_size: u32,
value_size: u32,
max_entries: u32,
opts: &libbpf_sys::bpf_map_create_opts,
) -> Result<Self> {
let name = match name {
Some(name) => name.as_ref().to_os_string(),
None => OsString::new(),
};
let name_c_str = CString::new(name.as_bytes()).map_err(|_| {
Error::with_invalid_data(format!("invalid name `{name:?}`: has NUL bytes"))
})?;
let name_c_ptr = if name.is_empty() {
ptr::null()
} else {
name_c_str.as_bytes_with_nul().as_ptr()
};
let fd = unsafe {
libbpf_sys::bpf_map_create(
map_type.into(),
name_c_ptr.cast(),
key_size,
value_size,
max_entries,
opts,
)
};
let () = util::parse_ret(fd)?;
Ok(Self {
fd: unsafe { OwnedFd::from_raw_fd(fd) },
name,
ty: map_type,
key_size,
value_size,
})
}
pub fn from_pinned_path<P: AsRef<Path>>(path: P) -> Result<Self> {
fn inner(path: &Path) -> Result<MapHandle> {
let p = CString::new(path.as_os_str().as_bytes()).expect("path contained null bytes");
let fd = parse_ret_i32(unsafe {
libbpf_sys::bpf_obj_get(p.as_ptr())
})?;
MapHandle::from_fd(unsafe {
OwnedFd::from_raw_fd(fd)
})
}
inner(path.as_ref())
}
pub fn from_map_id(id: u32) -> Result<Self> {
parse_ret_i32(unsafe {
libbpf_sys::bpf_map_get_fd_by_id(id)
})
.map(|fd| unsafe {
OwnedFd::from_raw_fd(fd)
})
.and_then(Self::from_fd)
}
fn from_fd(fd: OwnedFd) -> Result<Self> {
let info = MapInfo::new(fd.as_fd())?;
Ok(Self {
fd,
name: info.name()?.into(),
ty: info.map_type(),
key_size: info.info.key_size,
value_size: info.info.value_size,
})
}
pub fn freeze(&self) -> Result<()> {
let ret = unsafe { libbpf_sys::bpf_map_freeze(self.fd.as_raw_fd()) };
util::parse_ret(ret)
}
pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let path_c = util::path_to_cstring(path)?;
let path_ptr = path_c.as_ptr();
let ret = unsafe { libbpf_sys::bpf_obj_pin(self.fd.as_raw_fd(), path_ptr) };
util::parse_ret(ret)
}
pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
remove_file(path).context("failed to remove pin map")
}
}
impl MapCore for MapHandle {
#[inline]
fn name(&self) -> &OsStr {
&self.name
}
#[inline]
fn map_type(&self) -> MapType {
self.ty
}
#[inline]
fn key_size(&self) -> u32 {
self.key_size
}
#[inline]
fn value_size(&self) -> u32 {
self.value_size
}
}
impl AsFd for MapHandle {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl<T> TryFrom<&MapImpl<'_, T>> for MapHandle
where
T: Debug,
{
type Error = Error;
fn try_from(other: &MapImpl<'_, T>) -> Result<Self> {
Ok(Self {
fd: other
.as_fd()
.try_clone_to_owned()
.context("failed to duplicate map file descriptor")?,
name: other.name().to_os_string(),
ty: other.map_type(),
key_size: other.key_size(),
value_size: other.value_size(),
})
}
}
impl TryFrom<&MapHandle> for MapHandle {
type Error = Error;
fn try_from(other: &MapHandle) -> Result<Self> {
Ok(Self {
fd: other
.as_fd()
.try_clone_to_owned()
.context("failed to duplicate map file descriptor")?,
name: other.name().to_os_string(),
ty: other.map_type(),
key_size: other.key_size(),
value_size: other.value_size(),
})
}
}
bitflags! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct MapFlags: u64 {
const ANY = libbpf_sys::BPF_ANY as _;
const NO_EXIST = libbpf_sys::BPF_NOEXIST as _;
const EXIST = libbpf_sys::BPF_EXIST as _;
const LOCK = libbpf_sys::BPF_F_LOCK as _;
}
}
#[non_exhaustive]
#[repr(u32)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[allow(missing_docs)]
pub enum MapType {
Unspec = libbpf_sys::BPF_MAP_TYPE_UNSPEC,
Hash = libbpf_sys::BPF_MAP_TYPE_HASH,
Array = libbpf_sys::BPF_MAP_TYPE_ARRAY,
ProgArray = libbpf_sys::BPF_MAP_TYPE_PROG_ARRAY,
PerfEventArray = libbpf_sys::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
PercpuHash = libbpf_sys::BPF_MAP_TYPE_PERCPU_HASH,
PercpuArray = libbpf_sys::BPF_MAP_TYPE_PERCPU_ARRAY,
StackTrace = libbpf_sys::BPF_MAP_TYPE_STACK_TRACE,
CgroupArray = libbpf_sys::BPF_MAP_TYPE_CGROUP_ARRAY,
LruHash = libbpf_sys::BPF_MAP_TYPE_LRU_HASH,
LruPercpuHash = libbpf_sys::BPF_MAP_TYPE_LRU_PERCPU_HASH,
LpmTrie = libbpf_sys::BPF_MAP_TYPE_LPM_TRIE,
ArrayOfMaps = libbpf_sys::BPF_MAP_TYPE_ARRAY_OF_MAPS,
HashOfMaps = libbpf_sys::BPF_MAP_TYPE_HASH_OF_MAPS,
Devmap = libbpf_sys::BPF_MAP_TYPE_DEVMAP,
Sockmap = libbpf_sys::BPF_MAP_TYPE_SOCKMAP,
Cpumap = libbpf_sys::BPF_MAP_TYPE_CPUMAP,
Xskmap = libbpf_sys::BPF_MAP_TYPE_XSKMAP,
Sockhash = libbpf_sys::BPF_MAP_TYPE_SOCKHASH,
CgroupStorage = libbpf_sys::BPF_MAP_TYPE_CGROUP_STORAGE,
ReuseportSockarray = libbpf_sys::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
PercpuCgroupStorage = libbpf_sys::BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
Queue = libbpf_sys::BPF_MAP_TYPE_QUEUE,
Stack = libbpf_sys::BPF_MAP_TYPE_STACK,
SkStorage = libbpf_sys::BPF_MAP_TYPE_SK_STORAGE,
DevmapHash = libbpf_sys::BPF_MAP_TYPE_DEVMAP_HASH,
StructOps = libbpf_sys::BPF_MAP_TYPE_STRUCT_OPS,
RingBuf = libbpf_sys::BPF_MAP_TYPE_RINGBUF,
InodeStorage = libbpf_sys::BPF_MAP_TYPE_INODE_STORAGE,
TaskStorage = libbpf_sys::BPF_MAP_TYPE_TASK_STORAGE,
BloomFilter = libbpf_sys::BPF_MAP_TYPE_BLOOM_FILTER,
UserRingBuf = libbpf_sys::BPF_MAP_TYPE_USER_RINGBUF,
Unknown = u32::MAX,
}
impl MapType {
pub fn is_percpu(&self) -> bool {
matches!(
self,
MapType::PercpuArray
| MapType::PercpuHash
| MapType::LruPercpuHash
| MapType::PercpuCgroupStorage
)
}
fn is_keyless(&self) -> bool {
matches!(self, MapType::Queue | MapType::Stack | MapType::BloomFilter)
}
pub fn is_bloom_filter(&self) -> bool {
MapType::BloomFilter.eq(self)
}
pub fn is_supported(&self) -> Result<bool> {
let ret = unsafe { libbpf_sys::libbpf_probe_bpf_map_type(*self as u32, ptr::null()) };
match ret {
0 => Ok(false),
1 => Ok(true),
_ => Err(Error::from_raw_os_error(-ret)),
}
}
}
impl From<u32> for MapType {
fn from(value: u32) -> Self {
use MapType::*;
match value {
x if x == Unspec as u32 => Unspec,
x if x == Hash as u32 => Hash,
x if x == Array as u32 => Array,
x if x == ProgArray as u32 => ProgArray,
x if x == PerfEventArray as u32 => PerfEventArray,
x if x == PercpuHash as u32 => PercpuHash,
x if x == PercpuArray as u32 => PercpuArray,
x if x == StackTrace as u32 => StackTrace,
x if x == CgroupArray as u32 => CgroupArray,
x if x == LruHash as u32 => LruHash,
x if x == LruPercpuHash as u32 => LruPercpuHash,
x if x == LpmTrie as u32 => LpmTrie,
x if x == ArrayOfMaps as u32 => ArrayOfMaps,
x if x == HashOfMaps as u32 => HashOfMaps,
x if x == Devmap as u32 => Devmap,
x if x == Sockmap as u32 => Sockmap,
x if x == Cpumap as u32 => Cpumap,
x if x == Xskmap as u32 => Xskmap,
x if x == Sockhash as u32 => Sockhash,
x if x == CgroupStorage as u32 => CgroupStorage,
x if x == ReuseportSockarray as u32 => ReuseportSockarray,
x if x == PercpuCgroupStorage as u32 => PercpuCgroupStorage,
x if x == Queue as u32 => Queue,
x if x == Stack as u32 => Stack,
x if x == SkStorage as u32 => SkStorage,
x if x == DevmapHash as u32 => DevmapHash,
x if x == StructOps as u32 => StructOps,
x if x == RingBuf as u32 => RingBuf,
x if x == InodeStorage as u32 => InodeStorage,
x if x == TaskStorage as u32 => TaskStorage,
x if x == BloomFilter as u32 => BloomFilter,
x if x == UserRingBuf as u32 => UserRingBuf,
_ => Unknown,
}
}
}
impl From<MapType> for u32 {
fn from(value: MapType) -> Self {
value as u32
}
}
#[derive(Debug)]
pub struct MapKeyIter<'map> {
map_fd: BorrowedFd<'map>,
prev: Option<Vec<u8>>,
next: Vec<u8>,
}
impl<'map> MapKeyIter<'map> {
fn new(map_fd: BorrowedFd<'map>, key_size: u32) -> Self {
Self {
map_fd,
prev: None,
next: vec![0; key_size as usize],
}
}
}
impl Iterator for MapKeyIter<'_> {
type Item = Vec<u8>;
fn next(&mut self) -> Option<Self::Item> {
let prev = self.prev.as_ref().map_or(ptr::null(), |p| p.as_ptr());
let ret = unsafe {
libbpf_sys::bpf_map_get_next_key(
self.map_fd.as_raw_fd(),
prev as _,
self.next.as_mut_ptr() as _,
)
};
if ret != 0 {
None
} else {
self.prev = Some(self.next.clone());
Some(self.next.clone())
}
}
}
#[derive(Debug)]
pub struct MapInfo {
pub info: bpf_map_info,
}
impl MapInfo {
pub fn new(fd: BorrowedFd<'_>) -> Result<Self> {
let mut map_info = bpf_map_info::default();
let mut size = mem::size_of_val(&map_info) as u32;
let () = util::parse_ret(unsafe {
bpf_obj_get_info_by_fd(
fd.as_raw_fd(),
&mut map_info as *mut bpf_map_info as *mut c_void,
&mut size as *mut u32,
)
})?;
Ok(Self { info: map_info })
}
#[inline]
pub fn map_type(&self) -> MapType {
MapType::from(self.info.type_)
}
pub fn name<'a>(&self) -> Result<&'a str> {
let char_slice =
unsafe { from_raw_parts(self.info.name[..].as_ptr().cast(), self.info.name.len()) };
util::c_char_slice_to_cstr(char_slice)
.ok_or_else(|| Error::with_invalid_data("no nul byte found"))?
.to_str()
.map_err(Error::with_invalid_data)
}
#[inline]
pub fn flags(&self) -> MapFlags {
MapFlags::from_bits_truncate(self.info.map_flags as u64)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem::discriminant;
#[test]
fn map_type() {
use MapType::*;
for t in [
Unspec,
Hash,
Array,
ProgArray,
PerfEventArray,
PercpuHash,
PercpuArray,
StackTrace,
CgroupArray,
LruHash,
LruPercpuHash,
LpmTrie,
ArrayOfMaps,
HashOfMaps,
Devmap,
Sockmap,
Cpumap,
Xskmap,
Sockhash,
CgroupStorage,
ReuseportSockarray,
PercpuCgroupStorage,
Queue,
Stack,
SkStorage,
DevmapHash,
StructOps,
RingBuf,
InodeStorage,
TaskStorage,
BloomFilter,
UserRingBuf,
Unknown,
] {
assert_eq!(discriminant(&t), discriminant(&MapType::from(t as u32)));
}
}
}