From fbb09304a2de0d8baf7ea20c9727fcd2e4fb7f41 Mon Sep 17 00:00:00 2001 From: tyrone-wu Date: Fri, 2 Aug 2024 22:25:04 +0000 Subject: [PATCH] aya,int-test: revamp MapInfo be more friendly with older kernels Adds detection for whether a field is available in `MapInfo`: - For `map_type()`, we treturn new enum `MapType` instead of the integer representation. - For fields that can't be zero, we return `Option` type. - For `name_as_str()`, it now uses the feature probe `bpf_name()` to detect if field is available. Although the feature probe checks for program name, it can also be used for map name since they were both introduced in the same commit. --- aya-log/src/lib.rs | 2 +- aya-obj/src/obj.rs | 5 +- aya/src/maps/info.rs | 413 ++++++++++++++++++++++++ aya/src/maps/mod.rs | 133 +------- test/integration-test/src/tests/info.rs | 113 ++++--- xtask/public-api/aya.txt | 84 ++++- 6 files changed, 577 insertions(+), 173 deletions(-) create mode 100644 aya/src/maps/info.rs diff --git a/aya-log/src/lib.rs b/aya-log/src/lib.rs index 6a2ca27a..421ce2a0 100644 --- a/aya-log/src/lib.rs +++ b/aya-log/src/lib.rs @@ -150,7 +150,7 @@ impl EbpfLogger { None => false, }) .ok_or(Error::MapNotFound)?; - let map = MapData::from_id(map.id()).map_err(Error::MapError)?; + let map = MapData::from_id(map.id().unwrap().get()).map_err(Error::MapError)?; Self::read_logs_async(Map::PerfEventArray(map), logger)?; diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index 4e203fe1..e4be87da 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -81,7 +81,10 @@ impl Features { } } - /// Returns whether BPF program names are supported. + /// Returns whether BPF program names and map names are supported. + /// + /// Although the feature probe performs the check for program name, we can use this to also + /// detect if map name is supported since they were both introduced in the same commit. pub fn bpf_name(&self) -> bool { self.bpf_name } diff --git a/aya/src/maps/info.rs b/aya/src/maps/info.rs new file mode 100644 index 00000000..1d15dc6f --- /dev/null +++ b/aya/src/maps/info.rs @@ -0,0 +1,413 @@ +//! Metadata information about an eBPF map. + +use std::{ + ffi::CString, + num::NonZeroU32, + os::fd::{AsFd as _, BorrowedFd}, + path::Path, +}; + +use aya_obj::generated::{bpf_map_info, bpf_map_type}; + +use super::{MapError, MapFd}; +use crate::{ + sys::{ + bpf_get_object, bpf_map_get_fd_by_id, bpf_map_get_info_by_fd, iter_map_ids, SyscallError, + }, + util::bytes_of_bpf_name, + FEATURES, +}; + +/// Provides Provides metadata information about a loaded eBPF map. +#[doc(alias = "bpf_map_info")] +#[derive(Debug)] +pub struct MapInfo(pub(crate) bpf_map_info); + +impl MapInfo { + pub(crate) fn new_from_fd(fd: BorrowedFd<'_>) -> Result { + let info = bpf_map_get_info_by_fd(fd.as_fd())?; + Ok(Self(info)) + } + + /// Loads map info from a map ID. + /// + /// Uses kernel v4.13 features. + pub fn from_id(id: u32) -> Result { + bpf_map_get_fd_by_id(id) + .map_err(MapError::from) + .and_then(|fd| Self::new_from_fd(fd.as_fd())) + } + + /// The type of map. + /// + /// Introduced in kernel v4.13. + pub fn map_type(&self) -> Result { + bpf_map_type::try_from(self.0.type_) + .unwrap_or(bpf_map_type::__MAX_BPF_MAP_TYPE) + .try_into() + } + + /// The unique ID for this map. + /// + /// `None` is returned if the field is not available. + /// + /// Introduced in kernel v4.13. + pub fn id(&self) -> Option { + NonZeroU32::new(self.0.id) + } + + /// The key size for this map in bytes. + /// + /// `None` is returned if the field is not available. + /// + /// Introduced in kernel v4.13. + pub fn key_size(&self) -> Option { + NonZeroU32::new(self.0.key_size) + } + + /// The value size for this map in bytes. + /// + /// `None` is returned if the field is not available. + /// + /// Introduced in kernel v4.13. + pub fn value_size(&self) -> Option { + NonZeroU32::new(self.0.value_size) + } + + /// The maximum number of entries in this map. + /// + /// `None` is returned if the field is not available. + /// + /// Introduced in kernel v4.13. + pub fn max_entries(&self) -> Option { + NonZeroU32::new(self.0.max_entries) + } + + /// The flags used in loading this map. + /// + /// Introduced in kernel v4.13. + pub fn map_flags(&self) -> u32 { + self.0.map_flags + } + + /// The name of the map, limited to 16 bytes. + /// + /// Introduced in kernel v4.15. + pub fn name(&self) -> &[u8] { + bytes_of_bpf_name(&self.0.name) + } + + /// The name of the map as a &str. + /// + /// `None` is returned if the name was not valid unicode or if field is not available. + /// + /// Introduced in kernel v4.15. + pub fn name_as_str(&self) -> Option<&str> { + let name = std::str::from_utf8(self.name()).ok(); + if let Some(name_str) = name { + // Char in program name was introduced in the same commit as map name + if FEATURES.bpf_name() || !name_str.is_empty() { + return name; + } + } + None + } + + /// 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. + /// + /// Uses kernel v4.13 features. + pub fn fd(&self) -> Result { + let Self(info) = self; + let fd = bpf_map_get_fd_by_id(info.id)?; + Ok(MapFd::from_fd(fd)) + } + + /// Loads a map from a pinned path in bpffs. + /// + /// Uses kernel v4.4 and v4.13 features. + pub fn from_pin>(path: P) -> Result { + 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 of [`MapInfo`] over all eBPF maps on the host. +/// +/// Unlike [`Ebpf::maps`](crate::Ebpf::maps), this includes all maps on the host system, not +/// just those tied to a specific [`crate::Ebpf`] instance. +/// +/// Uses kernel v4.13 features. +/// +/// # 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> { + iter_map_ids().map(|id| { + let id = id?; + MapInfo::from_id(id) + }) +} + +/// The type of eBPF map. +#[non_exhaustive] +#[doc(alias = "bpf_map_type")] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum MapType { + /// An unspecified program type. + Unspecified = bpf_map_type::BPF_MAP_TYPE_UNSPEC as isize, + /// A Hash map type. See [`HashMap`](super::hash_map::HashMap) for the map implementation. + /// + /// Introduced in kernel v3.19. + #[doc(alias = "BPF_MAP_TYPE_HASH")] + Hash = bpf_map_type::BPF_MAP_TYPE_HASH as isize, + /// An Array map type. See [`Array`](super::array::Array) for the map implementation. + /// + /// Introduced in kernel v3.19. + #[doc(alias = "BPF_MAP_TYPE_ARRAY")] + Array = bpf_map_type::BPF_MAP_TYPE_ARRAY as isize, + /// A Program Array map type. See [`ProgramArray`](super::array::ProgramArray) for the map + /// implementation. + /// + /// Introduced in kernel v4.2. + #[doc(alias = "BPF_MAP_TYPE_PROG_ARRAY")] + ProgramArray = bpf_map_type::BPF_MAP_TYPE_PROG_ARRAY as isize, + /// A Perf Event Array map type. See [`PerfEventArray`](super::perf::PerfEventArray) and + /// [`AsyncPerfEventArray`](super::perf::AsyncPerfEventArray) for the map implementations. + /// + /// Introduced in kernel v4.3. + #[doc(alias = "BPF_MAP_TYPE_PERF_EVENT_ARRAY")] + PerfEventArray = bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY as isize, + /// A per-CPU Hash map type. See [`PerCpuHashMap`](super::hash_map::PerCpuHashMap) for the map + /// implementation. + /// + /// Introduced in kernel v4.6. + #[doc(alias = "BPF_MAP_TYPE_PERCPU_HASH")] + PerCpuHash = bpf_map_type::BPF_MAP_TYPE_PERCPU_HASH as isize, + /// A per-CPU Array map type. See [`PerCpuArray`](super::array::PerCpuArray) for the map + /// implementation. + /// + /// Introduced in kernel v4.6. + #[doc(alias = "BPF_MAP_TYPE_PERCPU_ARRAY")] + PerCpuArray = bpf_map_type::BPF_MAP_TYPE_PERCPU_ARRAY as isize, + /// A Stack Trace map type. See [`StackTraceMap`](super::stack_trace::StackTraceMap) for the map + /// implementation. + /// + /// Introduced in kernel v4.6. + #[doc(alias = "BPF_MAP_TYPE_STACK_TRACE")] + StackTrace = bpf_map_type::BPF_MAP_TYPE_STACK_TRACE as isize, + /// A cGroup Array map type. + /// + /// Introduced in kernel v4.8. + #[doc(alias = "BPF_MAP_TYPE_CGROUP_ARRAY")] + CgroupArray = bpf_map_type::BPF_MAP_TYPE_CGROUP_ARRAY as isize, + /// A Least Recently Used (LRU) Hash map type. See [`HashMap`](super::hash_map::HashMap) for + /// the map implementation. + /// + /// Introduced in kernel v4.10. + #[doc(alias = "BPF_MAP_TYPE_LRU_HASH")] + LruHash = bpf_map_type::BPF_MAP_TYPE_LRU_HASH as isize, + /// A Least Recently Used (LRU) per-CPU Hash map type. See + /// [`PerCpuHashMap`](super::hash_map::PerCpuHashMap) for the map implementation. + /// + /// Introduced in kernel v4.10. + #[doc(alias = "BPF_MAP_TYPE_LRU_PERCPU_HASH")] + LruPerCpuHash = bpf_map_type::BPF_MAP_TYPE_LRU_PERCPU_HASH as isize, + /// A Longest Prefix Match (LPM) Trie map type. See [`LpmTrie`](super::lpm_trie::LpmTrie) for + /// the map implementation. + /// + /// Introduced in kernel v4.11. + #[doc(alias = "BPF_MAP_TYPE_LPM_TRIE")] + LpmTrie = bpf_map_type::BPF_MAP_TYPE_LPM_TRIE as isize, + /// An Array of Maps map type. + /// + /// Introduced in kernel v4.12. + #[doc(alias = "BPF_MAP_TYPE_ARRAY_OF_MAPS")] + ArrayOfMaps = bpf_map_type::BPF_MAP_TYPE_ARRAY_OF_MAPS as isize, + /// A Hash of Maps map type. + /// + /// Introduced in kernel v4.12. + #[doc(alias = "BPF_MAP_TYPE_HASH_OF_MAPS")] + HashOfMaps = bpf_map_type::BPF_MAP_TYPE_HASH_OF_MAPS as isize, + /// A Device Map type. See [`DevMap`](super::xdp::DevMap) for the map implementation. + /// + /// Introduced in kernel v4.14. + #[doc(alias = "BPF_MAP_TYPE_DEVMAP")] + DevMap = bpf_map_type::BPF_MAP_TYPE_DEVMAP as isize, + /// A Socket Map type. See [`SockMap`](super::sock::SockMap) for the map implementation. + /// + /// Introduced in kernel v4.14. + #[doc(alias = "BPF_MAP_TYPE_SOCKMAP")] + SockMap = bpf_map_type::BPF_MAP_TYPE_SOCKMAP as isize, + /// A CPU Map type. See [`CpuMap`](super::xdp::CpuMap) for the map implementation. + /// + /// Introduced in kernel v4.15. + #[doc(alias = "BPF_MAP_TYPE_CPUMAP")] + CpuMap = bpf_map_type::BPF_MAP_TYPE_CPUMAP as isize, + /// An XDP Socket Map type. See [`XskMap`](super::xdp::XskMap) for the map implementation. + /// + /// Introduced in kernel v4.18. + #[doc(alias = "BPF_MAP_TYPE_XSKMAP")] + XskMap = bpf_map_type::BPF_MAP_TYPE_XSKMAP as isize, + /// A Socket Hash map type. See [`SockHash`](super::sock::SockHash) for the map implementation. + /// + /// Introduced in kernel v4.18. + #[doc(alias = "BPF_MAP_TYPE_SOCKHASH")] + SockHash = bpf_map_type::BPF_MAP_TYPE_SOCKHASH as isize, + /// A cGroup Storage map type. + /// + /// Introduced in kernel v4.19. + // #[deprecated] + #[doc(alias = "BPF_MAP_TYPE_CGROUP_STORAGE")] + #[doc(alias = "BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED")] + CgroupStorage = bpf_map_type::BPF_MAP_TYPE_CGROUP_STORAGE as isize, + /// A Reuseport Socket Array map type. + /// + /// Introduced in kernel v4.19. + #[doc(alias = "BPF_MAP_TYPE_REUSEPORT_SOCKARRAY")] + ReuseportSockArray = bpf_map_type::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY as isize, + /// A per-CPU cGroup Storage map type. + /// + /// Introduced in kernel v4.20. + #[doc(alias = "BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE")] + #[doc(alias = "BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED")] + PerCpuCgroupStorage = bpf_map_type::BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE as isize, + /// A Queue map type. See [`Queue`](super::queue::Queue) for the map implementation. + /// + /// Introduced in kernel v4.20. + #[doc(alias = "BPF_MAP_TYPE_QUEUE")] + Queue = bpf_map_type::BPF_MAP_TYPE_QUEUE as isize, + /// A Stack map type. See [`Stack`](super::stack::Stack) for the map implementation. + /// + /// Introduced in kernel v4.20. + #[doc(alias = "BPF_MAP_TYPE_STACK")] + Stack = bpf_map_type::BPF_MAP_TYPE_STACK as isize, + /// A Socket-local Storage map type. + /// + /// Introduced in kernel v5.2. + #[doc(alias = "BPF_MAP_TYPE_SK_STORAGE")] + SkStorage = bpf_map_type::BPF_MAP_TYPE_SK_STORAGE as isize, + /// A Device Hash Map type. See [`DevMapHash`](super::xdp::DevMapHash) for the map + /// implementation. + /// + /// Introduced in kernel v5.4. + #[doc(alias = "BPF_MAP_TYPE_DEVMAP_HASH")] + DevMapHash = bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH as isize, + /// A Struct Ops map type. + /// + /// Introduced in kernel v5.6. + #[doc(alias = "BPF_MAP_TYPE_STRUCT_OPS")] + StructOps = bpf_map_type::BPF_MAP_TYPE_STRUCT_OPS as isize, + /// A Ring Buffer map type. See [`RingBuf`](super::ring_buf::RingBuf) for the map + /// implementation. + /// + /// Introduced in kernel v5.8. + #[doc(alias = "BPF_MAP_TYPE_RINGBUF")] + RingBuf = bpf_map_type::BPF_MAP_TYPE_RINGBUF as isize, + /// An Inode Storage map type. + /// + /// Introduced in kernel v5.10. + #[doc(alias = "BPF_MAP_TYPE_INODE_STORAGE")] + InodeStorage = bpf_map_type::BPF_MAP_TYPE_INODE_STORAGE as isize, + /// A Task Storage map type. + /// + /// Introduced in kernel v5.11. + #[doc(alias = "BPF_MAP_TYPE_TASK_STORAGE")] + TaskStorage = bpf_map_type::BPF_MAP_TYPE_TASK_STORAGE as isize, + /// A Bloom Filter map type. See [`BloomFilter`](super::bloom_filter::BloomFilter) for the map + /// implementation. + /// + /// Introduced in kernel v5.16. + #[doc(alias = "BPF_MAP_TYPE_BLOOM_FILTER")] + BloomFilter = bpf_map_type::BPF_MAP_TYPE_BLOOM_FILTER as isize, + /// A User Ring Buffer map type. + /// + /// Introduced in kernel v6.1. + #[doc(alias = "BPF_MAP_TYPE_USER_RINGBUF")] + UserRingBuf = bpf_map_type::BPF_MAP_TYPE_USER_RINGBUF as isize, + /// A cGroup Storage map type. + /// + /// Introduced in kernel v6.2. + #[doc(alias = "BPF_MAP_TYPE_CGRP_STORAGE")] + CgrpStorage = bpf_map_type::BPF_MAP_TYPE_CGRP_STORAGE as isize, + /// An Arena map type. + /// + /// Introduced in kernel v6.9. + #[doc(alias = "BPF_MAP_TYPE_ARENA")] + Arena = bpf_map_type::BPF_MAP_TYPE_ARENA as isize, +} + +impl TryFrom for MapType { + type Error = MapError; + + fn try_from(map_type: bpf_map_type) -> Result { + use bpf_map_type::*; + Ok(match map_type { + BPF_MAP_TYPE_UNSPEC => Self::Unspecified, + BPF_MAP_TYPE_HASH => Self::Hash, + BPF_MAP_TYPE_ARRAY => Self::Array, + BPF_MAP_TYPE_PROG_ARRAY => Self::ProgramArray, + BPF_MAP_TYPE_PERF_EVENT_ARRAY => Self::PerfEventArray, + BPF_MAP_TYPE_PERCPU_HASH => Self::PerCpuHash, + BPF_MAP_TYPE_PERCPU_ARRAY => Self::PerCpuArray, + BPF_MAP_TYPE_STACK_TRACE => Self::StackTrace, + BPF_MAP_TYPE_CGROUP_ARRAY => Self::CgroupArray, + BPF_MAP_TYPE_LRU_HASH => Self::LruHash, + BPF_MAP_TYPE_LRU_PERCPU_HASH => Self::LruPerCpuHash, + BPF_MAP_TYPE_LPM_TRIE => Self::LpmTrie, + BPF_MAP_TYPE_ARRAY_OF_MAPS => Self::ArrayOfMaps, + BPF_MAP_TYPE_HASH_OF_MAPS => Self::HashOfMaps, + BPF_MAP_TYPE_DEVMAP => Self::DevMap, + BPF_MAP_TYPE_SOCKMAP => Self::SockMap, + BPF_MAP_TYPE_CPUMAP => Self::CpuMap, + BPF_MAP_TYPE_XSKMAP => Self::XskMap, + BPF_MAP_TYPE_SOCKHASH => Self::SockHash, + BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED => Self::CgroupStorage, + BPF_MAP_TYPE_REUSEPORT_SOCKARRAY => Self::ReuseportSockArray, + BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED => Self::PerCpuCgroupStorage, + BPF_MAP_TYPE_QUEUE => Self::Queue, + BPF_MAP_TYPE_STACK => Self::Stack, + BPF_MAP_TYPE_SK_STORAGE => Self::SkStorage, + BPF_MAP_TYPE_DEVMAP_HASH => Self::DevMapHash, + BPF_MAP_TYPE_STRUCT_OPS => Self::StructOps, + BPF_MAP_TYPE_RINGBUF => Self::RingBuf, + BPF_MAP_TYPE_INODE_STORAGE => Self::InodeStorage, + BPF_MAP_TYPE_TASK_STORAGE => Self::TaskStorage, + BPF_MAP_TYPE_BLOOM_FILTER => Self::BloomFilter, + BPF_MAP_TYPE_USER_RINGBUF => Self::UserRingBuf, + BPF_MAP_TYPE_CGRP_STORAGE => Self::CgrpStorage, + BPF_MAP_TYPE_ARENA => Self::Arena, + __MAX_BPF_MAP_TYPE => { + return Err(MapError::InvalidMapType { + map_type: map_type as u32, + }) + } + }) + } +} diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index e914d052..752bd9ff 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -65,21 +65,20 @@ use log::warn; use thiserror::Error; use crate::{ - generated::bpf_map_info, obj::{self, parse_map_info, EbpfSectionKind}, 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, - iter_map_ids, SyscallError, + bpf_create_map, bpf_get_object, bpf_map_freeze, bpf_map_get_fd_by_id, bpf_map_get_next_key, + bpf_map_update_elem_ptr, bpf_pin_object, SyscallError, }, - util::{bytes_of_bpf_name, nr_cpus, KernelVersion}, + util::{nr_cpus, KernelVersion}, PinningType, Pod, }; pub mod array; pub mod bloom_filter; pub mod hash_map; +mod info; pub mod lpm_trie; pub mod perf; pub mod queue; @@ -92,6 +91,7 @@ pub mod xdp; pub use array::{Array, PerCpuArray, ProgramArray}; pub use bloom_filter::BloomFilter; pub use hash_map::{HashMap, PerCpuHashMap}; +pub use info::{loaded_maps, MapInfo, MapType}; pub use lpm_trie::LpmTrie; #[cfg(any(feature = "async_tokio", feature = "async_std"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "async_tokio", feature = "async_std"))))] @@ -947,121 +947,6 @@ impl Deref for PerCpuValues { } } -/// 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 { - 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 { - 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] { - bytes_of_bpf_name(&self.0.name) - } - - /// 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 { - let Self(info) = self; - let fd = bpf_map_get_fd_by_id(info.id)?; - Ok(MapFd::from_fd(fd)) - } - - /// Loads a map from a pinned path in bpffs. - pub fn from_pin>(path: P) -> Result { - 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::Ebpf::maps`] since it will return all maps -/// listed on the host system and not only maps for a specific [`crate::Ebpf`] instance. -/// -/// Uses kernel v4.13 features. -/// -/// # 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> { - iter_map_ids().map(|id| { - let id = id?; - MapInfo::from_id(id) - }) -} - #[cfg(test)] mod test_utils { use crate::{ @@ -1332,11 +1217,11 @@ mod tests { .map(|map_info| { let map_info = map_info.unwrap(); ( - map_info.id(), - map_info.key_size(), - map_info.value_size(), + map_info.id().unwrap().get(), + map_info.key_size().unwrap().get(), + map_info.value_size().unwrap().get(), map_info.map_flags(), - map_info.max_entries(), + map_info.max_entries().unwrap().get(), map_info.fd().unwrap().as_fd().as_raw_fd(), ) }) diff --git a/test/integration-test/src/tests/info.rs b/test/integration-test/src/tests/info.rs index 16770136..e90b31b1 100644 --- a/test/integration-test/src/tests/info.rs +++ b/test/integration-test/src/tests/info.rs @@ -3,13 +3,12 @@ use std::{fs, panic, path::Path, time::SystemTime}; use aya::{ - maps::{loaded_maps, MapError}, + maps::{loaded_maps, Array, HashMap, IterableMap as _, MapError, MapType}, programs::{loaded_programs, ProgramError, ProgramType, SocketFilter, TracePoint}, sys::enable_stats, util::KernelVersion, Ebpf, }; -use aya_obj::generated::bpf_map_type; use libc::EINVAL; use crate::utils::{kernel_assert, kernel_assert_eq}; @@ -233,7 +232,7 @@ fn list_loaded_maps() { let prog: &mut SocketFilter = bpf.program_mut("simple_prog").unwrap().try_into().unwrap(); prog.load().unwrap(); - // Ensure the loaded_maps() api doesn't panic and retrieve loaded maps. + // Ensure the loaded_maps() api doesn't panic let mut maps = loaded_maps().peekable(); if let Err(err) = maps.peek().unwrap() { if let MapError::SyscallError(err) = &err { @@ -250,64 +249,94 @@ fn list_loaded_maps() { } panic!("{err}"); } - let mut maps: Vec<_> = maps.filter_map(|m| m.ok()).collect(); - // There's not a good way to extract our maps of interest with load order being - // non-deterministic. Since we are trying to be more considerate of older kernels, we should - // only rely on v4.13 feats. - // Expected sort order should be: `BAR`, `aya_global` (if avail), `FOO` - maps.sort_unstable_by_key(|m| (m.map_type(), m.id())); - - // Ensure program has the 2 maps. - if let Ok(info) = prog.info() { - let map_ids = info.map_ids().unwrap(); - kernel_assert!(map_ids.is_some(), KernelVersion::new(4, 15, 0)); - - if let Some(map_ids) = map_ids { + // Loaded maps should contain our test maps + let maps: Vec<_> = maps.filter_map(|m| m.ok()).collect(); + if let Ok(info) = &prog.info() { + if let Some(map_ids) = info.map_ids().unwrap() { assert_eq!(2, map_ids.len()); for id in map_ids.iter() { assert!( - maps.iter().any(|m| m.id() == id.get()), - "expected `loaded_maps()` to have `map_ids` from program" + maps.iter().any(|m| &m.id().unwrap() == id), + "expected `loaded_maps()` to have `map_ids` from program", ); } } } + let hash: HashMap<_, u32, u8> = HashMap::try_from(bpf.map("BAR").unwrap()).unwrap(); + let hash_id = hash.map().info().unwrap().id(); + kernel_assert!( + maps.iter().any(|map| map.id() == hash_id), + KernelVersion::new(4, 13, 0), + ); + + let array: Array<_, u32> = Array::try_from(bpf.map("FOO").unwrap()).unwrap(); + let array_id = array.map().info().unwrap().id(); + kernel_assert!( + maps.iter().any(|map| map.id() == array_id), + KernelVersion::new(4, 13, 0), + ); +} + +#[test] +fn test_map_info() { + let mut bpf: Ebpf = Ebpf::load(crate::MAP_TEST).unwrap(); + let prog: &mut SocketFilter = bpf.program_mut("simple_prog").unwrap().try_into().unwrap(); + prog.load().unwrap(); + // Test `bpf_map_info` fields. - let hash = maps.first().unwrap(); + let hash: HashMap<_, u32, u8> = HashMap::try_from(bpf.map("BAR").unwrap()).unwrap(); + let hash = hash.map().info().unwrap(); kernel_assert_eq!( - bpf_map_type::BPF_MAP_TYPE_HASH as u32, - hash.map_type(), - KernelVersion::new(4, 13, 0) + MapType::Hash, + hash.map_type().unwrap_or(MapType::Unspecified), + KernelVersion::new(4, 13, 0), ); - kernel_assert!(hash.id() > 0, KernelVersion::new(4, 13, 0)); - kernel_assert_eq!(4, hash.key_size(), KernelVersion::new(4, 13, 0)); - kernel_assert_eq!(1, hash.value_size(), KernelVersion::new(4, 13, 0)); - kernel_assert_eq!(8, hash.max_entries(), KernelVersion::new(4, 13, 0)); - kernel_assert_eq!( - "BAR", - hash.name_as_str().unwrap(), - KernelVersion::new(4, 15, 0) + kernel_assert!(hash.id().is_some(), KernelVersion::new(4, 13, 0)); + kernel_assert!( + hash.key_size().is_some_and(|size| size.get() == 4), + KernelVersion::new(4, 13, 0), + ); + kernel_assert!( + hash.value_size().is_some_and(|size| size.get() == 1), + KernelVersion::new(4, 13, 0), + ); + kernel_assert!( + hash.max_entries().is_some_and(|size| size.get() == 8), + KernelVersion::new(4, 13, 0), + ); + kernel_assert!( + hash.name_as_str().is_some_and(|name| name == "BAR"), + KernelVersion::new(4, 15, 0), ); hash.map_flags(); hash.fd().unwrap(); - let array = maps.last().unwrap(); + let array: Array<_, u32> = Array::try_from(bpf.map("FOO").unwrap()).unwrap(); + let array = array.map().info().unwrap(); kernel_assert_eq!( - bpf_map_type::BPF_MAP_TYPE_ARRAY as u32, - array.map_type(), - KernelVersion::new(4, 13, 0) + MapType::Array, + array.map_type().unwrap_or(MapType::Unspecified), + KernelVersion::new(4, 13, 0), ); - kernel_assert!(array.id() > 0, KernelVersion::new(4, 13, 0)); - kernel_assert_eq!(4, array.key_size(), KernelVersion::new(4, 13, 0)); - kernel_assert_eq!(4, array.value_size(), KernelVersion::new(4, 13, 0)); - kernel_assert_eq!(10, array.max_entries(), KernelVersion::new(4, 13, 0)); - kernel_assert_eq!( - "FOO", - array.name_as_str().unwrap(), - KernelVersion::new(4, 15, 0) + kernel_assert!(array.id().is_some(), KernelVersion::new(4, 13, 0)); + kernel_assert!( + array.key_size().is_some_and(|size| size.get() == 4), + KernelVersion::new(4, 13, 0), + ); + kernel_assert!( + array.value_size().is_some_and(|size| size.get() == 4), + KernelVersion::new(4, 13, 0), + ); + kernel_assert!( + array.max_entries().is_some_and(|size| size.get() == 10), + KernelVersion::new(4, 13, 0), + ); + kernel_assert!( + array.name_as_str().is_some_and(|name| name == "FOO"), + KernelVersion::new(4, 15, 0), ); array.map_flags(); diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index b26f09d0..2b0c37fd 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -1405,6 +1405,80 @@ impl core::borrow::BorrowMut for aya::maps::MapError where T: core::marker pub fn aya::maps::MapError::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::maps::MapError pub fn aya::maps::MapError::from(t: T) -> T +#[non_exhaustive] pub enum aya::maps::MapType +pub aya::maps::MapType::Arena = 33 +pub aya::maps::MapType::Array = 2 +pub aya::maps::MapType::ArrayOfMaps = 12 +pub aya::maps::MapType::BloomFilter = 30 +pub aya::maps::MapType::CgroupArray = 8 +pub aya::maps::MapType::CgroupStorage = 19 +pub aya::maps::MapType::CgrpStorage = 32 +pub aya::maps::MapType::CpuMap = 16 +pub aya::maps::MapType::DevMap = 14 +pub aya::maps::MapType::DevMapHash = 25 +pub aya::maps::MapType::Hash = 1 +pub aya::maps::MapType::HashOfMaps = 13 +pub aya::maps::MapType::InodeStorage = 28 +pub aya::maps::MapType::LpmTrie = 11 +pub aya::maps::MapType::LruHash = 9 +pub aya::maps::MapType::LruPerCpuHash = 10 +pub aya::maps::MapType::PerCpuArray = 6 +pub aya::maps::MapType::PerCpuCgroupStorage = 21 +pub aya::maps::MapType::PerCpuHash = 5 +pub aya::maps::MapType::PerfEventArray = 4 +pub aya::maps::MapType::ProgramArray = 3 +pub aya::maps::MapType::Queue = 22 +pub aya::maps::MapType::ReuseportSockArray = 20 +pub aya::maps::MapType::RingBuf = 27 +pub aya::maps::MapType::SkStorage = 24 +pub aya::maps::MapType::SockHash = 18 +pub aya::maps::MapType::SockMap = 15 +pub aya::maps::MapType::Stack = 23 +pub aya::maps::MapType::StackTrace = 7 +pub aya::maps::MapType::StructOps = 26 +pub aya::maps::MapType::TaskStorage = 29 +pub aya::maps::MapType::Unspecified = 0 +pub aya::maps::MapType::UserRingBuf = 31 +pub aya::maps::MapType::XskMap = 17 +impl core::clone::Clone for aya::maps::MapType +pub fn aya::maps::MapType::clone(&self) -> aya::maps::MapType +impl core::cmp::PartialEq for aya::maps::MapType +pub fn aya::maps::MapType::eq(&self, other: &aya::maps::MapType) -> bool +impl core::convert::TryFrom for aya::maps::MapType +pub type aya::maps::MapType::Error = aya::maps::MapError +pub fn aya::maps::MapType::try_from(map_type: aya_obj::generated::linux_bindings_x86_64::bpf_map_type) -> core::result::Result +impl core::fmt::Debug for aya::maps::MapType +pub fn aya::maps::MapType::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for aya::maps::MapType +impl core::marker::StructuralPartialEq for aya::maps::MapType +impl core::marker::Freeze for aya::maps::MapType +impl core::marker::Send for aya::maps::MapType +impl core::marker::Sync for aya::maps::MapType +impl core::marker::Unpin for aya::maps::MapType +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::MapType +impl core::panic::unwind_safe::UnwindSafe for aya::maps::MapType +impl core::convert::Into for aya::maps::MapType where U: core::convert::From +pub fn aya::maps::MapType::into(self) -> U +impl core::convert::TryFrom for aya::maps::MapType where U: core::convert::Into +pub type aya::maps::MapType::Error = core::convert::Infallible +pub fn aya::maps::MapType::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::MapType where U: core::convert::TryFrom +pub type aya::maps::MapType::Error = >::Error +pub fn aya::maps::MapType::try_into(self) -> core::result::Result>::Error> +impl alloc::borrow::ToOwned for aya::maps::MapType where T: core::clone::Clone +pub type aya::maps::MapType::Owned = T +pub fn aya::maps::MapType::clone_into(&self, target: &mut T) +pub fn aya::maps::MapType::to_owned(&self) -> T +impl core::any::Any for aya::maps::MapType where T: 'static + core::marker::Sized +pub fn aya::maps::MapType::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::MapType where T: core::marker::Sized +pub fn aya::maps::MapType::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::MapType where T: core::marker::Sized +pub fn aya::maps::MapType::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for aya::maps::MapType where T: core::clone::Clone +pub unsafe fn aya::maps::MapType::clone_to_uninit(&self, dst: *mut T) +impl core::convert::From for aya::maps::MapType +pub fn aya::maps::MapType::from(t: T) -> T pub struct aya::maps::Array impl, V: aya::Pod> aya::maps::array::Array pub fn aya::maps::array::Array::get(&self, index: &u32, flags: u64) -> core::result::Result @@ -1801,14 +1875,14 @@ impl aya::maps::MapInfo pub fn aya::maps::MapInfo::fd(&self) -> core::result::Result pub fn aya::maps::MapInfo::from_id(id: u32) -> core::result::Result pub fn aya::maps::MapInfo::from_pin>(path: P) -> core::result::Result -pub fn aya::maps::MapInfo::id(&self) -> u32 -pub fn aya::maps::MapInfo::key_size(&self) -> u32 +pub fn aya::maps::MapInfo::id(&self) -> core::option::Option +pub fn aya::maps::MapInfo::key_size(&self) -> core::option::Option 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::map_type(&self) -> core::result::Result +pub fn aya::maps::MapInfo::max_entries(&self) -> core::option::Option 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 +pub fn aya::maps::MapInfo::value_size(&self) -> core::option::Option 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::Freeze for aya::maps::MapInfo