aya: add MapInfo struct following the same pattern as ProgramInfo

This makes the APIs for loading maps and programs more similar.
pull/676/merge
Adam Preuss 1 year ago committed by Alessandro Decina
parent 36420d9297
commit 4d24d1cfe8

@ -65,12 +65,13 @@ use obj::maps::InvalidMapTypeError;
use thiserror::Error;
use crate::{
generated::bpf_map_info,
obj::{self, parse_map_info, BpfSectionKind},
pin::PinError,
sys::{
bpf_create_map, bpf_get_object, bpf_map_freeze, bpf_map_get_fd_by_id,
bpf_map_get_info_by_fd, bpf_map_get_next_key, bpf_map_update_elem_ptr, bpf_pin_object,
SyscallError,
iter_map_ids, SyscallError,
},
util::{nr_cpus, KernelVersion},
PinningType, Pod,
@ -639,21 +640,14 @@ impl MapData {
call: "BPF_OBJ_GET",
io_error,
})?;
let fd = MapFd(fd);
let info = bpf_map_get_info_by_fd(fd.as_fd())?;
Ok(Self {
obj: parse_map_info(info, PinningType::ByName),
fd,
})
Self::from_fd(fd)
}
/// Loads a map from a map id.
pub fn from_id(id: u32) -> Result<Self, MapError> {
bpf_map_get_fd_by_id(id)
.map_err(MapError::from)
.and_then(Self::from_fd)
let fd = bpf_map_get_fd_by_id(id)?;
Self::from_fd(fd)
}
/// Loads a map from a file descriptor.
@ -662,12 +656,10 @@ impl MapData {
/// This API is intended for cases where you have received a valid BPF FD from some other means.
/// For example, you received an FD over Unix Domain Socket.
pub fn from_fd(fd: OwnedFd) -> Result<Self, MapError> {
let info = bpf_map_get_info_by_fd(fd.as_fd())?;
let fd = MapFd(fd);
let MapInfo(info) = MapInfo::new_from_fd(fd.as_fd())?;
Ok(Self {
obj: parse_map_info(info, PinningType::None),
fd,
fd: MapFd(fd),
})
}
@ -723,6 +715,11 @@ impl MapData {
let Self { obj, fd: _ } = self;
obj
}
/// Returns the kernel's information about the loaded map.
pub fn info(&self) -> Result<MapInfo, MapError> {
MapInfo::new_from_fd(self.fd.as_fd())
}
}
/// An iterable map
@ -911,6 +908,129 @@ impl<T: Pod> Deref for PerCpuValues<T> {
}
}
/// Provides information about a loaded map, like name, id and size.
#[derive(Debug)]
pub struct MapInfo(bpf_map_info);
impl MapInfo {
fn new_from_fd(fd: BorrowedFd<'_>) -> Result<Self, MapError> {
let info = bpf_map_get_info_by_fd(fd.as_fd())?;
Ok(Self(info))
}
/// Loads map info from a map id.
pub fn from_id(id: u32) -> Result<Self, MapError> {
bpf_map_get_fd_by_id(id)
.map_err(MapError::from)
.and_then(|fd| Self::new_from_fd(fd.as_fd()))
}
/// The name of the map, limited to 16 bytes.
pub fn name(&self) -> &[u8] {
let length = self
.0
.name
.iter()
.rposition(|ch| *ch != 0)
.map(|pos| pos + 1)
.unwrap_or(0);
// The name field is defined as [std::os::raw::c_char; 16]. c_char may be signed or
// unsigned depending on the platform; that's why we're using from_raw_parts here.
unsafe { std::slice::from_raw_parts(self.0.name.as_ptr() as *const _, length) }
}
/// The name of the map as a &str. If the name is not valid unicode, None is returned.
pub fn name_as_str(&self) -> Option<&str> {
std::str::from_utf8(self.name()).ok()
}
/// The id for this map. Each map has a unique id.
pub fn id(&self) -> u32 {
self.0.id
}
/// The map type as defined by the linux kernel enum
/// [`bpf_map_type`](https://elixir.bootlin.com/linux/v6.4.4/source/include/uapi/linux/bpf.h#L905).
pub fn map_type(&self) -> u32 {
self.0.type_
}
/// The key size for this map.
pub fn key_size(&self) -> u32 {
self.0.key_size
}
/// The value size for this map.
pub fn value_size(&self) -> u32 {
self.0.value_size
}
/// The maximum number of entries in this map.
pub fn max_entries(&self) -> u32 {
self.0.max_entries
}
/// The flags for this map.
pub fn map_flags(&self) -> u32 {
self.0.map_flags
}
/// Returns a file descriptor referencing the map.
///
/// The returned file descriptor can be closed at any time and doing so does
/// not influence the life cycle of the map.
pub fn fd(&self) -> Result<MapFd, MapError> {
let Self(info) = self;
let fd = bpf_map_get_fd_by_id(info.id)?;
Ok(MapFd(fd))
}
/// Loads a map from a pinned path in bpffs.
pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, MapError> {
use std::os::unix::ffi::OsStrExt as _;
// TODO: avoid this unwrap by adding a new error variant.
let path_string = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| SyscallError {
call: "BPF_OBJ_GET",
io_error,
})?;
Self::new_from_fd(fd.as_fd())
}
}
/// Returns an iterator over all loaded bpf maps.
///
/// This differs from [`crate::Bpf::maps`] since it will return all maps
/// listed on the host system and not only maps for a specific [`crate::Bpf`] instance.
///
/// # Example
/// ```
/// # use aya::maps::loaded_maps;
///
/// for m in loaded_maps() {
/// match m {
/// Ok(map) => println!("{:?}", map.name_as_str()),
/// Err(e) => println!("Error iterating maps: {:?}", e),
/// }
/// }
/// ```
///
/// # Errors
///
/// Returns [`MapError::SyscallError`] if any of the syscalls required to either get
/// next map id, get the map fd, or the [`MapInfo`] fail. In cases where
/// iteration can't be performed, for example the caller does not have the necessary privileges,
/// a single item will be yielded containing the error that occurred.
pub fn loaded_maps() -> impl Iterator<Item = Result<MapInfo, MapError>> {
iter_map_ids().map(|id| {
let id = id?;
MapInfo::from_id(id)
})
}
#[cfg(test)]
mod tests {
use std::os::fd::AsRawFd as _;
@ -994,6 +1114,96 @@ mod tests {
);
}
#[test]
// Syscall overrides are performing integer-to-pointer conversions, which
// should be done with `ptr::from_exposed_addr` in Rust nightly, but we have
// to support stable as well.
#[cfg_attr(miri, ignore)]
fn test_name() {
use crate::generated::bpf_map_info;
const TEST_NAME: &str = "foo";
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_CREATE,
..
} => Ok(42),
Syscall::Bpf {
cmd: bpf_cmd::BPF_OBJ_GET_INFO_BY_FD,
attr,
} => {
assert_eq!(
unsafe { attr.info.info_len },
mem::size_of::<bpf_map_info>() as u32
);
let map_info = unsafe { &mut *(attr.info.info as *mut bpf_map_info) };
map_info.name[..TEST_NAME.len()]
.copy_from_slice(unsafe { std::mem::transmute(TEST_NAME) });
Ok(0)
}
_ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
});
let map_data = MapData::create(new_obj_map(), TEST_NAME, None).unwrap();
assert_eq!(TEST_NAME, map_data.info().unwrap().name_as_str().unwrap());
}
#[test]
// Syscall overrides are performing integer-to-pointer conversions, which
// should be done with `ptr::from_exposed_addr` in Rust nightly, but we have
// to support stable as well.
#[cfg_attr(miri, ignore)]
fn test_loaded_maps() {
use crate::generated::bpf_map_info;
override_syscall(|call| match call {
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_ID,
attr,
} => unsafe {
let id = attr.__bindgen_anon_6.__bindgen_anon_1.start_id;
if id < 5 {
attr.__bindgen_anon_6.next_id = id + 1;
Ok(0)
} else {
Err((-1, io::Error::from_raw_os_error(libc::ENOENT)))
}
},
Syscall::Bpf {
cmd: bpf_cmd::BPF_MAP_GET_FD_BY_ID,
attr,
} => Ok((1000 + unsafe { attr.__bindgen_anon_6.__bindgen_anon_1.map_id }) as c_long),
Syscall::Bpf {
cmd: bpf_cmd::BPF_OBJ_GET_INFO_BY_FD,
attr,
} => {
let map_info = unsafe { &mut *(attr.info.info as *mut bpf_map_info) };
map_info.id = unsafe { attr.info.bpf_fd } - 1000;
map_info.key_size = 32;
map_info.value_size = 64;
map_info.map_flags = 1234;
map_info.max_entries = 99;
Ok(0)
}
_ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
});
let loaded_maps: Vec<_> = loaded_maps().collect();
assert_eq!(loaded_maps.len(), 5);
for (i, map_info) in loaded_maps.into_iter().enumerate() {
let i = i + 1;
let map_info = map_info.unwrap();
assert_eq!(map_info.id(), i as u32);
assert_eq!(map_info.key_size(), 32);
assert_eq!(map_info.value_size(), 64);
assert_eq!(map_info.map_flags(), 1234);
assert_eq!(map_info.max_entries(), 99);
assert_eq!(map_info.fd().unwrap().as_fd().as_raw_fd(), 1000 + i as i32);
}
}
#[test]
fn test_create_failed() {
override_syscall(|_| Err((-42, io::Error::from_raw_os_error(EFAULT))));

@ -1049,6 +1049,10 @@ pub(crate) fn iter_link_ids() -> impl Iterator<Item = Result<u32, SyscallError>>
iter_obj_ids(bpf_cmd::BPF_LINK_GET_NEXT_ID, "bpf_link_get_next_id")
}
pub(crate) fn iter_map_ids() -> impl Iterator<Item = Result<u32, SyscallError>> {
iter_obj_ids(bpf_cmd::BPF_MAP_GET_NEXT_ID, "bpf_map_get_next_id")
}
pub(crate) fn retry_with_verifier_logs<T>(
max_retries: usize,
f: impl Fn(&mut [u8]) -> SysResult<T>,

@ -1,4 +1,5 @@
use aya::{
maps::loaded_maps,
programs::{loaded_programs, Extension, TracePoint, Xdp, XdpFlags},
util::KernelVersion,
Bpf, BpfLoader,
@ -98,3 +99,28 @@ fn list_loaded_programs() {
prog.loaded_at();
prog.fd().unwrap();
}
#[test]
fn list_loaded_maps() {
// Load a program with maps.
let mut bpf = Bpf::load(crate::MAP_TEST).unwrap();
let dispatcher: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
dispatcher.load().unwrap();
dispatcher.attach("lo", XdpFlags::default()).unwrap();
// Ensure the loaded_maps() api doesn't panic and retrieve a map.
let map = loaded_maps()
.map(|m| m.unwrap())
.find(|m| m.name_as_str().unwrap() == "FOO")
.unwrap();
// Ensure all relevant helper functions don't panic.
map.name();
map.id();
map.map_type();
map.key_size();
map.value_size();
map.max_entries();
map.map_flags();
map.fd().unwrap();
}

@ -1688,6 +1688,7 @@ pub fn aya::maps::MapData::fd(&self) -> &aya::maps::MapFd
pub fn aya::maps::MapData::from_fd(fd: std::os::fd::owned::OwnedFd) -> core::result::Result<Self, aya::maps::MapError>
pub fn aya::maps::MapData::from_id(id: u32) -> core::result::Result<Self, aya::maps::MapError>
pub fn aya::maps::MapData::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::maps::MapError>
pub fn aya::maps::MapData::info(&self) -> core::result::Result<aya::maps::MapInfo, aya::maps::MapError>
pub fn aya::maps::MapData::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
impl core::fmt::Debug for aya::maps::MapData
pub fn aya::maps::MapData::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
@ -1738,6 +1739,42 @@ impl<T> core::borrow::BorrowMut<T> for aya::maps::MapFd where T: core::marker::S
pub fn aya::maps::MapFd::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::maps::MapFd
pub fn aya::maps::MapFd::from(t: T) -> T
pub struct aya::maps::MapInfo(_)
impl aya::maps::MapInfo
pub fn aya::maps::MapInfo::fd(&self) -> core::result::Result<aya::maps::MapFd, aya::maps::MapError>
pub fn aya::maps::MapInfo::from_id(id: u32) -> core::result::Result<Self, aya::maps::MapError>
pub fn aya::maps::MapInfo::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::maps::MapError>
pub fn aya::maps::MapInfo::id(&self) -> u32
pub fn aya::maps::MapInfo::key_size(&self) -> u32
pub fn aya::maps::MapInfo::map_flags(&self) -> u32
pub fn aya::maps::MapInfo::map_type(&self) -> u32
pub fn aya::maps::MapInfo::max_entries(&self) -> u32
pub fn aya::maps::MapInfo::name(&self) -> &[u8]
pub fn aya::maps::MapInfo::name_as_str(&self) -> core::option::Option<&str>
pub fn aya::maps::MapInfo::value_size(&self) -> u32
impl core::fmt::Debug for aya::maps::MapInfo
pub fn aya::maps::MapInfo::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Send for aya::maps::MapInfo
impl core::marker::Sync for aya::maps::MapInfo
impl core::marker::Unpin for aya::maps::MapInfo
impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::MapInfo
impl core::panic::unwind_safe::UnwindSafe for aya::maps::MapInfo
impl<T, U> core::convert::Into<U> for aya::maps::MapInfo where U: core::convert::From<T>
pub fn aya::maps::MapInfo::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::maps::MapInfo where U: core::convert::Into<T>
pub type aya::maps::MapInfo::Error = core::convert::Infallible
pub fn aya::maps::MapInfo::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
impl<T, U> core::convert::TryInto<U> for aya::maps::MapInfo where U: core::convert::TryFrom<T>
pub type aya::maps::MapInfo::Error = <U as core::convert::TryFrom<T>>::Error
pub fn aya::maps::MapInfo::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
impl<T> core::any::Any for aya::maps::MapInfo where T: 'static + core::marker::Sized
pub fn aya::maps::MapInfo::type_id(&self) -> core::any::TypeId
impl<T> core::borrow::Borrow<T> for aya::maps::MapInfo where T: core::marker::Sized
pub fn aya::maps::MapInfo::borrow(&self) -> &T
impl<T> core::borrow::BorrowMut<T> for aya::maps::MapInfo where T: core::marker::Sized
pub fn aya::maps::MapInfo::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::maps::MapInfo
pub fn aya::maps::MapInfo::from(t: T) -> T
pub struct aya::maps::MapIter<'coll, K: aya::Pod, V, I: aya::maps::IterableMap<K, V>>
impl<K: aya::Pod, V, I: aya::maps::IterableMap<K, V>> core::iter::traits::iterator::Iterator for aya::maps::MapIter<'_, K, V, I>
pub type aya::maps::MapIter<'_, K, V, I>::Item = core::result::Result<(K, V), aya::maps::MapError>
@ -2289,6 +2326,7 @@ pub fn aya::maps::PerCpuArray<T, V>::map(&self) -> &aya::maps::MapData
impl<T: core::borrow::Borrow<aya::maps::MapData>> aya::maps::IterableMap<u32, aya::maps::stack_trace::StackTrace> for aya::maps::stack_trace::StackTraceMap<T>
pub fn aya::maps::stack_trace::StackTraceMap<T>::get(&self, index: &u32) -> core::result::Result<aya::maps::stack_trace::StackTrace, aya::maps::MapError>
pub fn aya::maps::stack_trace::StackTraceMap<T>::map(&self) -> &aya::maps::MapData
pub fn aya::maps::loaded_maps() -> impl core::iter::traits::iterator::Iterator<Item = core::result::Result<aya::maps::MapInfo, aya::maps::MapError>>
pub mod aya::pin
pub enum aya::pin::PinError
pub aya::pin::PinError::InvalidPinPath

Loading…
Cancel
Save