diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index a7f423a73..aaeff05c3 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -152,6 +152,27 @@ pub enum BtfError { /// unable to get symbol name #[error("Unable to get symbol name")] InvalidSymbolName, + + /// Inner map definition cannot be pinned. + #[error("BTF map `{name}`: inner map definition cannot be pinned")] + InnerMapCannotBePinned { + /// The name of the map with the invalid definition. + name: String, + }, + + /// Multi-level map-in-map is not supported. + #[error("BTF map `{name}`: multi-level map-in-map is not supported")] + MultiLevelMapInMapNotSupported { + /// The name of the map with the invalid definition. + name: String, + }, + + /// The `values` spec must be a zero-sized array. + #[error("BTF map `{name}`: `values` spec is not a zero-sized array")] + InvalidValuesSpec { + /// The name of the map with the invalid definition. + name: String, + }, } /// Available BTF features diff --git a/aya-obj/src/maps.rs b/aya-obj/src/maps.rs index 2e6887593..fea489be5 100644 --- a/aya-obj/src/maps.rs +++ b/aya-obj/src/maps.rs @@ -258,6 +258,60 @@ impl Map { Self::Btf(m) => Some(m.symbol_index), } } + + /// Returns the inner map definition, in case of a map of maps. + /// + /// The inner map is a synthetic object with no ELF presence of its own, + /// so neutral metadata values are used. + pub fn inner(&self) -> Option { + match self { + Self::Legacy(m) => m.inner_def.as_ref().map(|inner_def| { + Self::Legacy(LegacyMap { + def: *inner_def, + inner_def: None, + section_index: 0, + section_kind: EbpfSectionKind::Undefined, + symbol_index: None, + data: Vec::new(), + }) + }), + Self::Btf(m) => m.inner_def.as_ref().map(|inner_def| { + Self::Btf(BtfMap { + def: *inner_def, + inner_def: None, + section_index: 0, + symbol_index: 0, + data: Vec::new(), + }) + }), + } + } + + /// Creates a new map definition from raw parameters. + pub const fn new_from_params( + map_type: u32, + key_size: u32, + value_size: u32, + max_entries: u32, + flags: u32, + ) -> Self { + Self::Legacy(LegacyMap { + def: bpf_map_def { + map_type, + key_size, + value_size, + max_entries, + map_flags: flags, + id: 0, + pinning: PinningType::None, + }, + inner_def: None, + section_index: 0, + section_kind: EbpfSectionKind::Undefined, + symbol_index: None, + data: Vec::new(), + }) + } } /// A map declared with legacy BPF map declaration style, most likely from a `maps` section. @@ -268,6 +322,8 @@ impl Map { pub struct LegacyMap { /// The definition of the map pub def: bpf_map_def, + /// The definition of the inner map, in case of a map of maps. + pub inner_def: Option, /// The section index pub section_index: usize, /// The section kind @@ -287,6 +343,8 @@ pub struct LegacyMap { pub struct BtfMap { /// The definition of the map pub def: BtfMapDef, + /// The definition of the inner map, in case of a map of maps. + pub(crate) inner_def: Option, pub(crate) section_index: usize, pub(crate) symbol_index: usize, pub(crate) data: Vec, diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index 8d7defb81..c94ebb81a 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -20,6 +20,7 @@ use object::{ use crate::{ btf::{ Array, Btf, BtfError, BtfExt, BtfFeatures, BtfType, DataSecEntry, FuncSecInfo, LineSecInfo, + Struct, }, generated::{ BPF_CALL, BPF_F_RDONLY_PROG, BPF_JMP, BPF_K, bpf_func_id, bpf_insn, bpf_map_info, @@ -785,7 +786,7 @@ impl Object { if type_name == section.name { // each btf_var_secinfo contains a map for info in &datasec.entries { - let (map_name, def) = parse_btf_map_def(btf, info)?; + let (map_name, def, inner_def) = parse_btf_map_def(btf, info)?; let symbol_index = maps.get(&map_name) .ok_or_else(|| ParseError::SymbolNotFound { @@ -795,6 +796,7 @@ impl Object { map_name, Map::Btf(BtfMap { def, + inner_def, section_index: section.index.0, symbol_index: *symbol_index, data: Vec::new(), @@ -840,6 +842,7 @@ impl Object { section_kind: section.kind, symbol_index: Some(sym.index), def, + inner_def: None, data: Vec::new(), }), ); @@ -1255,6 +1258,7 @@ fn parse_data_map_section(section: &Section<'_>) -> Map { // Data maps don't require symbols to be relocated symbol_index: None, def, + inner_def: None, data, }) } @@ -1278,7 +1282,10 @@ fn parse_map_def(name: &str, data: &[u8]) -> Result { } } -fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDef), BtfError> { +fn parse_btf_map_def( + btf: &Btf, + info: &DataSecEntry, +) -> Result<(String, BtfMapDef, Option), BtfError> { let ty = match btf.type_by_id(info.btf_type)? { BtfType::Var(var) => var, other => { @@ -1288,7 +1295,6 @@ fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDe } }; let map_name = btf.string_at(ty.name_offset)?; - let mut map_def = BtfMapDef::default(); let root_type = btf.resolve_type(ty.btf_type)?; let s = match btf.type_by_id(root_type)? { @@ -1300,6 +1306,23 @@ fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDe } }; + let (map_def, inner_def) = parse_btf_map_struct(btf, s, &map_name, false)?; + Ok((map_name.to_string(), map_def, inner_def)) +} + +/// Parses BTF struct members into a map definition. +/// +/// When `is_inner` is true, rejects `values` and `pinning` fields, matching +/// libbpf behavior for inner map definitions. +fn parse_btf_map_struct( + btf: &Btf, + s: &Struct, + map_name: &str, + is_inner: bool, +) -> Result<(BtfMapDef, Option), BtfError> { + let mut map_def = BtfMapDef::default(); + let mut inner_map_def = None; + for m in &s.members { match btf.string_at(m.name_offset)?.as_ref() { "type" => { @@ -1343,18 +1366,67 @@ fn parse_btf_map_def(btf: &Btf, info: &DataSecEntry) -> Result<(String, BtfMapDe map_def.map_extra = get_map_field(btf, m.btf_type)?.into(); } "pinning" => { + if is_inner { + return Err(BtfError::InnerMapCannotBePinned { + name: map_name.to_owned(), + }); + } let pinning = get_map_field(btf, m.btf_type)?; map_def.pinning = PinningType::try_from(pinning).unwrap_or_else(|_| { debug!("{pinning} is not a valid pin type. using PIN_NONE"); PinningType::None }); } + "values" => { + if is_inner { + return Err(BtfError::MultiLevelMapInMapNotSupported { + name: map_name.to_owned(), + }); + } + // The inner map type is encoded as a zero-length array of pointers + // to the inner struct, matching libbpf's parse_btf_map_def(). + let arr = match btf.type_by_id(m.btf_type)? { + BtfType::Array(Array { array, .. }) => array, + other => { + return Err(BtfError::UnexpectedBtfType { + type_id: other.btf_type().unwrap_or(0), + }); + } + }; + if arr.len != 0 { + return Err(BtfError::InvalidValuesSpec { + name: map_name.to_owned(), + }); + } + let elem_type_id = btf.resolve_type(arr.element_type)?; + let ptr = match btf.type_by_id(elem_type_id)? { + BtfType::Ptr(ptr) => ptr, + other => { + return Err(BtfError::UnexpectedBtfType { + type_id: other.btf_type().unwrap_or(0), + }); + } + }; + let inner_struct_id = btf.resolve_type(ptr.btf_type)?; + let inner_s = match btf.type_by_id(inner_struct_id)? { + BtfType::Struct(s) => s, + other => { + return Err(BtfError::UnexpectedBtfType { + type_id: other.btf_type().unwrap_or(0), + }); + } + }; + let (inner_def, _) = parse_btf_map_struct(btf, inner_s, map_name, true)?; + inner_map_def = Some(inner_def); + // Map-of-maps value is always an fd (u32). + map_def.value_size = size_of::() as u32; + } other => { debug!("skipping unknown map section: {other}"); } } } - Ok((map_name.to_string(), map_def)) + Ok((map_def, inner_map_def)) } /// Parses a [`bpf_map_info`] into a [`Map`]. @@ -1375,6 +1447,7 @@ pub const fn parse_map_info(info: bpf_map_info, pinned: PinningType) -> Map { btf_key_type_id: info.btf_key_type_id, btf_value_type_id: info.btf_value_type_id, }, + inner_def: None, section_index: 0, symbol_index: 0, data: Vec::new(), @@ -1390,6 +1463,7 @@ pub const fn parse_map_info(info: bpf_map_info, pinned: PinningType) -> Map { pinning: pinned, id: info.id, }, + inner_def: None, section_index: 0, symbol_index: None, section_kind: EbpfSectionKind::Undefined, @@ -1673,6 +1747,7 @@ mod tests { pinning: PinningType::None, }, data, + .. }) if data == map_data && value_size == map_data.len() as u32 ) } @@ -2728,6 +2803,7 @@ mod tests { id: 1, pinning: PinningType::None, }, + inner_def: None, section_index: 1, section_kind: EbpfSectionKind::Rodata, symbol_index: Some(1), diff --git a/aya-obj/src/relocation.rs b/aya-obj/src/relocation.rs index 1931b5fd2..a2ae5fda4 100644 --- a/aya-obj/src/relocation.rs +++ b/aya-obj/src/relocation.rs @@ -516,6 +516,7 @@ mod test { fn fake_legacy_map(symbol_index: usize) -> Map { Map::Legacy(LegacyMap { def: Default::default(), + inner_def: None, section_index: 0, section_kind: EbpfSectionKind::Undefined, symbol_index: Some(symbol_index), @@ -526,6 +527,7 @@ mod test { fn fake_btf_map(symbol_index: usize) -> Map { Map::Btf(BtfMap { def: Default::default(), + inner_def: None, section_index: 0, symbol_index, data: Vec::new(), diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index eeaccedd8..c3259334d 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -1,7 +1,7 @@ use std::{ borrow::Cow, collections::{HashMap, HashSet}, - fs, io, + fs, io, iter, os::fd::{AsFd as _, AsRawFd as _}, path::{Path, PathBuf}, sync::{Arc, LazyLock}, @@ -495,38 +495,88 @@ impl<'a> EbpfLoader<'a> { if let Some(btf) = &btf { obj.relocate_btf(btf)?; } - let mut maps = HashMap::new(); - for (name, mut obj) in obj.maps.drain() { + + const fn is_map_of_maps(map_type: bpf_map_type) -> bool { + matches!( + map_type, + bpf_map_type::BPF_MAP_TYPE_ARRAY_OF_MAPS | bpf_map_type::BPF_MAP_TYPE_HASH_OF_MAPS + ) + } + + // The kernel requires inner_map_fd when creating map-of-maps, so inner + // maps must be created first. Partition into regular maps and map-of-maps. + let mut regular_maps: Vec<(String, aya_obj::Map)> = Vec::new(); + let mut maps_of_maps: Vec<(String, aya_obj::Map)> = Vec::new(); + + for (name, map_obj) in obj.maps.drain() { if let (false, EbpfSectionKind::Bss | EbpfSectionKind::Data | EbpfSectionKind::Rodata) = - (FEATURES.bpf_global_data(), obj.section_kind()) + (FEATURES.bpf_global_data(), map_obj.section_kind()) { continue; } + let map_type: bpf_map_type = map_obj.map_type().try_into().map_err(MapError::from)?; + if is_map_of_maps(map_type) { + maps_of_maps.push((name, map_obj)); + } else { + regular_maps.push((name, map_obj)); + } + } + + let mut maps: HashMap = HashMap::new(); + + // Regular maps first, so they're available as inner maps below. + for ((name, mut map_obj), is_map_of_maps) in regular_maps + .into_iter() + .zip(iter::repeat(false)) + .chain(maps_of_maps.into_iter().zip(iter::repeat(true))) + { let num_cpus = || { Ok(nr_cpus().map_err(|(path, error)| EbpfError::FileError { path: PathBuf::from(path), error, })? as u32) }; - let map_type: bpf_map_type = obj.map_type().try_into().map_err(MapError::from)?; - if let Some(max_entries) = max_entries_override( + let map_type: bpf_map_type = map_obj.map_type().try_into().map_err(MapError::from)?; + if let Some(max_entries_val) = max_entries_override( map_type, max_entries.get(name.as_str()).copied(), - || obj.max_entries(), + || map_obj.max_entries(), num_cpus, || page_size() as u32, )? { - obj.set_max_entries(max_entries) + map_obj.set_max_entries(max_entries_val) } if let Some(value_size) = value_size_override(map_type) { - obj.set_value_size(value_size) + map_obj.set_value_size(value_size) } + let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd()); + + // Defer inner map creation to avoid a BPF_MAP_CREATE when the outer map is already pinned. + let inner_map_obj = if is_map_of_maps { + Some(map_obj.inner().ok_or_else(|| { + EbpfError::MapError(MapError::MissingInnerMapDefinition { + outer_name: name.clone(), + }) + })?) + } else { + None + }; let mut map = if let Some(pin_path) = map_pin_path_by_name.get(name.as_str()) { - MapData::create_pinned_by_name(pin_path, obj, &name, btf_fd)? + MapData::create_pinned_by_name(pin_path, map_obj, &name, btf_fd, inner_map_obj)? } else { - match obj.pinning() { - PinningType::None => MapData::create(obj, &name, btf_fd)?, + match map_obj.pinning() { + PinningType::None => { + let btf_inner_map; + let inner_map_fd = if let Some(inner) = inner_map_obj { + btf_inner_map = + MapData::create(inner, &format!("{name}.inner"), btf_fd)?; + Some(btf_inner_map.fd().as_fd()) + } else { + None + }; + MapData::create_with_inner_map_fd(map_obj, &name, btf_fd, inner_map_fd)? + } PinningType::ByName => { // pin maps in /sys/fs/bpf by default to align with libbpf // behavior https://github.com/libbpf/libbpf/blob/v1.2.2/src/libbpf.c#L2161. @@ -535,7 +585,7 @@ impl<'a> EbpfLoader<'a> { .unwrap_or_else(|| Path::new("/sys/fs/bpf")); let path = path.join(&name); - MapData::create_pinned_by_name(path, obj, &name, btf_fd)? + MapData::create_pinned_by_name(path, map_obj, &name, btf_fd, inner_map_obj)? } } }; @@ -773,6 +823,8 @@ fn parse_map( bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH => Map::DevMapHash(map), bpf_map_type::BPF_MAP_TYPE_XSKMAP => Map::XskMap(map), bpf_map_type::BPF_MAP_TYPE_SK_STORAGE => Map::SkStorage(map), + bpf_map_type::BPF_MAP_TYPE_ARRAY_OF_MAPS => Map::ArrayOfMaps(map), + bpf_map_type::BPF_MAP_TYPE_HASH_OF_MAPS => Map::HashOfMaps(map), m_type => { if allow_unsupported_maps { Map::Unsupported(map) diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index 9185ec7ae..e8444ac38 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -76,6 +76,7 @@ pub mod bloom_filter; pub mod hash_map; mod info; pub mod lpm_trie; +pub mod of_maps; pub mod perf; pub mod queue; pub mod ring_buf; @@ -90,6 +91,7 @@ pub use bloom_filter::BloomFilter; pub use hash_map::{HashMap, PerCpuHashMap}; pub use info::{MapInfo, MapType, loaded_maps}; pub use lpm_trie::LpmTrie; +pub use of_maps::{ArrayOfMaps, HashOfMaps}; pub use perf::PerfEventArray; pub use queue::Queue; pub use ring_buf::RingBuf; @@ -99,9 +101,49 @@ pub use stack::Stack; pub use stack_trace::StackTraceMap; pub use xdp::{CpuMap, DevMap, DevMapHash, XskMap}; +/// Trait for constructing a typed map from [`MapData`]. +/// +/// This trait is sealed and cannot be implemented outside of this crate. +pub trait FromMapData: sealed::FromMapData {} + +impl FromMapData for T {} + +/// Marker for map types that the kernel supports as inner maps. +/// +/// This trait is sealed and cannot be implemented outside of this crate. +pub trait InnerMap: sealed::InnerMap {} + +impl InnerMap for T {} + +mod sealed { + use super::{MapData, MapError, MapFd}; + + #[expect(unnameable_types, reason = "intentionally unnameable sealed trait")] + pub trait FromMapData: Sized { + /// Constructs a typed map from raw [`MapData`]. + fn from_map_data(map_data: MapData) -> Result; + } + + #[expect(unnameable_types, reason = "intentionally unnameable sealed trait")] + pub trait InnerMap { + /// Returns the map file descriptor. + fn fd(&self) -> &MapFd; + } +} + #[derive(Error, Debug)] /// Errors occuring from working with Maps pub enum MapError { + /// Missing inner map BTF definition for a map-of-maps. + #[error( + "map `{outer_name}` is a map-of-maps but has no inner map definition; \ + use #[btf_map] with a BTF-typed map-of-maps that includes an inner map type" + )] + MissingInnerMapDefinition { + /// Map name + outer_name: String, + }, + /// Invalid map type encontered #[error("invalid map type {map_type}")] InvalidMapType { @@ -254,6 +296,8 @@ impl AsFd for MapFd { pub enum Map { /// An [`Array`] map. Array(MapData), + /// An [`ArrayOfMaps`] map. + ArrayOfMaps(MapData), /// A [`BloomFilter`] map. BloomFilter(MapData), /// A [`CpuMap`] map. @@ -264,6 +308,8 @@ pub enum Map { DevMapHash(MapData), /// A [`HashMap`] map. HashMap(MapData), + /// A [`HashOfMaps`] map. + HashOfMaps(MapData), /// A [`LpmTrie`] map. LpmTrie(MapData), /// A [`HashMap`] map that uses a LRU eviction policy. @@ -305,11 +351,13 @@ impl Map { const fn map_type(&self) -> u32 { match self { Self::Array(map) => map.obj.map_type(), + Self::ArrayOfMaps(map) => map.obj.map_type(), Self::BloomFilter(map) => map.obj.map_type(), Self::CpuMap(map) => map.obj.map_type(), Self::DevMap(map) => map.obj.map_type(), Self::DevMapHash(map) => map.obj.map_type(), Self::HashMap(map) => map.obj.map_type(), + Self::HashOfMaps(map) => map.obj.map_type(), Self::LpmTrie(map) => map.obj.map_type(), Self::LruHashMap(map) => map.obj.map_type(), Self::PerCpuArray(map) => map.obj.map_type(), @@ -337,11 +385,13 @@ impl Map { pub fn pin>(&self, path: P) -> Result<(), PinError> { match self { Self::Array(map) => map.pin(path), + Self::ArrayOfMaps(map) => map.pin(path), Self::BloomFilter(map) => map.pin(path), Self::CpuMap(map) => map.pin(path), Self::DevMap(map) => map.pin(path), Self::DevMapHash(map) => map.pin(path), Self::HashMap(map) => map.pin(path), + Self::HashOfMaps(map) => map.pin(path), Self::LpmTrie(map) => map.pin(path), Self::LruHashMap(map) => map.pin(path), Self::PerCpuArray(map) => map.pin(path), @@ -396,8 +446,8 @@ impl Map { bpf_map_type::BPF_MAP_TYPE_RINGBUF => Self::RingBuf(map_data), bpf_map_type::BPF_MAP_TYPE_BLOOM_FILTER => Self::BloomFilter(map_data), bpf_map_type::BPF_MAP_TYPE_CGROUP_ARRAY => Self::Unsupported(map_data), - bpf_map_type::BPF_MAP_TYPE_ARRAY_OF_MAPS => Self::Unsupported(map_data), - bpf_map_type::BPF_MAP_TYPE_HASH_OF_MAPS => Self::Unsupported(map_data), + bpf_map_type::BPF_MAP_TYPE_ARRAY_OF_MAPS => Self::ArrayOfMaps(map_data), + bpf_map_type::BPF_MAP_TYPE_HASH_OF_MAPS => Self::HashOfMaps(map_data), bpf_map_type::BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED => Self::Unsupported(map_data), bpf_map_type::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY => Self::ReusePortSockArray(map_data), bpf_map_type::BPF_MAP_TYPE_SK_STORAGE => Self::SkStorage(map_data), @@ -472,7 +522,8 @@ impl_map_pin!((K, V) { // Implements TryFrom for different map implementations. Different map implementations can be // constructed from different variants of the map enum. Also, the implementation may have type -// parameters (which we assume all have the bound `Pod` and nothing else). +// parameters. The dispatch arm adds `Pod` bounds explicitly before forwarding to the @impl arm, +// which accepts arbitrary bounds and is also used by impl_try_from_map_of_maps. macro_rules! impl_try_from_map { // At the root the type parameters are marked as a single token tree which will be pasted into // the invocation for each type. Note that the later patterns require that the token tree be @@ -488,23 +539,24 @@ macro_rules! impl_try_from_map { ($(#[$meta:meta])* <$ty_param:tt> $ty:ident) => { impl_try_from_map!($(#[$meta])* <$ty_param> $ty from $ty); }; - // Dispatch for each of the lifetimes. + // Dispatch for each of the lifetimes, adding Pod bounds explicitly. ( $(#[$meta:meta])* <($($ty_param:ident),*)> $ty:ident from $($variant:ident)|+ ) => { - impl_try_from_map!($(#[$meta])* <'a> ($($ty_param),*) $ty from $($variant)|+); - impl_try_from_map!($(#[$meta])* <'a mut> ($($ty_param),*) $ty from $($variant)|+); - impl_try_from_map!($(#[$meta])* <> ($($ty_param),*) $ty from $($variant)|+); + impl_try_from_map!(@impl $(#[$meta])* <'a> ($($ty_param: Pod),*) $ty from $($variant)|+); + impl_try_from_map!(@impl $(#[$meta])* <'a mut> ($($ty_param: Pod),*) $ty from $($variant)|+); + impl_try_from_map!(@impl $(#[$meta])* <> ($($ty_param: Pod),*) $ty from $($variant)|+); }; - // An individual impl. - ( + // An individual impl with explicit bounds. Used by both impl_try_from_map + // and impl_try_from_map_of_maps via the @impl internal rule. + (@impl $(#[$meta:meta])* <$($l:lifetime $($m:ident)?)?> - ($($ty_param:ident),*) + ($($ty_param:ident $(: $bound:path)?),*) $ty:ident from $($variant:ident)|+ ) => { $(#[$meta])* - impl<$($l,)? $($ty_param: Pod),*> TryFrom<$(&$l $($m)?)? Map> + impl<$($l,)? $($ty_param $(: $bound)?),*> TryFrom<$(&$l $($m)?)? Map> for $ty<$(&$l $($m)?)? MapData, $($ty_param),*> { type Error = MapError; @@ -550,6 +602,126 @@ impl_try_from_map!((K, V) { PerCpuHashMap from PerCpuHashMap|PerCpuLruHashMap, }); +// ArrayOfMaps and HashOfMaps require V: InnerMap in TryFrom conversions. +// Delegates to the @impl arm of impl_try_from_map to avoid duplicating the +// match body. +macro_rules! impl_try_from_map_of_maps { + ($ty:ident) => { + impl_try_from_map_of_maps!($ty <>); + }; + ($ty:ident <$($pre:ident : $pre_bound:path),*>) => { + impl_try_from_map!(@impl <'a> ($($pre: $pre_bound,)* V: InnerMap) $ty from $ty); + impl_try_from_map!(@impl <'a mut> ($($pre: $pre_bound,)* V: InnerMap) $ty from $ty); + impl_try_from_map!(@impl <> ($($pre: $pre_bound,)* V: InnerMap) $ty from $ty); + }; +} + +impl_try_from_map_of_maps!(ArrayOfMaps); +impl_try_from_map_of_maps!(HashOfMaps); + +// Implements `sealed::FromMapData` and `sealed::InnerMap` for a map type. +// Types with `inner: T` use the default arm; PerfEventArray and RingBuf +// pass `via map_data` to use their existing accessor method. +macro_rules! impl_from_map_data { + ($ty_param:tt { $($ty:ident),+ $(,)? }) => { + $(impl_from_map_data!(<$ty_param> $ty);)+ + }; + (<($($ty_param:ident),*)> $ty:ident via $accessor:ident) => { + impl<$($ty_param: Pod),*> sealed::FromMapData for $ty { + fn from_map_data(map_data: MapData) -> Result { + Map::from_map_data(map_data)?.try_into() + } + } + impl<$($ty_param: Pod),*> sealed::InnerMap for $ty { + fn fd(&self) -> &MapFd { + self.$accessor().fd() + } + } + }; + (<($($ty_param:ident),*)> $ty:ident) => { + impl<$($ty_param: Pod),*> sealed::FromMapData for $ty { + fn from_map_data(map_data: MapData) -> Result { + Map::from_map_data(map_data)?.try_into() + } + } + impl<$($ty_param: Pod),*> sealed::InnerMap for $ty { + fn fd(&self) -> &MapFd { + self.inner.fd() + } + } + }; +} + +// Map types that the kernel supports as inner maps. +// Excluded: ProgramArray (no map_meta_equal), ArrayOfMaps/HashOfMaps (multi-level nesting +// forbidden). +impl_from_map_data!(() { + CpuMap, DevMap, DevMapHash, + SockMap, StackTraceMap, XskMap, +}); + +// PerfEventArray and RingBuf use map_data() instead of inner field. +impl_from_map_data!(<()> PerfEventArray via map_data); +impl_from_map_data!(<()> RingBuf via map_data); + +impl_from_map_data!((V) { + Array, BloomFilter, PerCpuArray, + Queue, SockHash, SkStorage, Stack, +}); + +impl_from_map_data!((K, V) { + HashMap, LpmTrie, PerCpuHashMap, +}); + +impl sealed::FromMapData for MapData { + fn from_map_data(map_data: MapData) -> Result { + Ok(map_data) + } +} + +impl sealed::InnerMap for MapData { + fn fd(&self) -> &MapFd { + self.fd() + } +} + +impl sealed::InnerMap for MapFd { + fn fd(&self) -> &MapFd { + self + } +} + +macro_rules! impl_creatable_map { + ($ty:ident, $map_type:expr, $key_size:expr, $value_size:expr, $name:expr) => { + impl<$($p: Pod),*> $ty { + /// Creates a standalone map with the given `max_entries` capacity and `flags`. + pub fn create(max_entries: u32, flags: u32) -> Result { + let obj = aya_obj::Map::new_from_params( + $map_type as u32, $key_size, $value_size, max_entries, flags, + ); + Self::new(MapData::create(obj, $name, None)?) + } + } + }; +} + +impl_creatable_map!(Array, + bpf_map_type::BPF_MAP_TYPE_ARRAY, size_of::() as u32, size_of::() as u32, "standalone_array"); +impl_creatable_map!(PerCpuArray, + bpf_map_type::BPF_MAP_TYPE_PERCPU_ARRAY, size_of::() as u32, size_of::() as u32, "standalone_percpu_array"); +impl_creatable_map!(BloomFilter, + bpf_map_type::BPF_MAP_TYPE_BLOOM_FILTER, 0, size_of::() as u32, "standalone_bloom_filter"); +impl_creatable_map!(Queue, + bpf_map_type::BPF_MAP_TYPE_QUEUE, 0, size_of::() as u32, "standalone_queue"); +impl_creatable_map!(Stack, + bpf_map_type::BPF_MAP_TYPE_STACK, 0, size_of::() as u32, "standalone_stack"); +impl_creatable_map!(HashMap, + bpf_map_type::BPF_MAP_TYPE_HASH, size_of::() as u32, size_of::() as u32, "standalone_hash"); +impl_creatable_map!(PerCpuHashMap, + bpf_map_type::BPF_MAP_TYPE_PERCPU_HASH, size_of::() as u32, size_of::() as u32, "standalone_percpu_hash"); +impl_creatable_map!(LpmTrie, + bpf_map_type::BPF_MAP_TYPE_LPM_TRIE, size_of::>() as u32, size_of::() as u32, "standalone_lpm_trie"); + pub(crate) const fn check_bounds(map: &MapData, index: u32) -> Result<(), MapError> { let max_entries = map.obj.max_entries(); if index >= max_entries { @@ -594,9 +766,19 @@ pub struct MapData { impl MapData { /// Creates a new map with the provided `name` pub fn create( + obj: aya_obj::Map, + name: &str, + btf_fd: Option>, + ) -> Result { + Self::create_with_inner_map_fd(obj, name, btf_fd, None) + } + + /// Creates a new map with the provided `name` and optional `inner_map_fd` for map-of-maps types. + pub(crate) fn create_with_inner_map_fd( mut obj: aya_obj::Map, name: &str, btf_fd: Option>, + inner_map_fd: Option>, ) -> Result { let c_name = CString::new(name) .map_err(|std::ffi::NulError { .. }| MapError::InvalidName { name: name.into() })?; @@ -619,11 +801,12 @@ impl MapData { } } - let fd = - bpf_create_map(&c_name, &obj, btf_fd).map_err(|io_error| MapError::CreateError { + let fd = bpf_create_map(&c_name, &obj, btf_fd, inner_map_fd).map_err(|io_error| { + MapError::CreateError { name: name.into(), io_error, - })?; + } + })?; Ok(Self { obj, fd: MapFd::from_fd(fd), @@ -635,6 +818,7 @@ impl MapData { obj: aya_obj::Map, name: &str, btf_fd: Option>, + inner_map_obj: Option, ) -> Result { use std::os::unix::ffi::OsStrExt as _; @@ -658,7 +842,14 @@ impl MapData { fd: MapFd::from_fd(fd), }) } else { - let map = Self::create(obj, name, btf_fd)?; + let inner_map; + let inner_map_fd = if let Some(inner) = inner_map_obj { + inner_map = Self::create(inner, &format!("{name}.inner"), btf_fd)?; + Some(inner_map.fd().as_fd()) + } else { + None + }; + let map = Self::create_with_inner_map_fd(obj, name, btf_fd, inner_map_fd)?; map.pin(path).map_err(|error| MapError::PinError { name: Some(name.into()), error, @@ -1013,6 +1204,7 @@ mod test_utils { max_entries: 1024, ..Default::default() }, + inner_def: None, section_index: 0, section_kind: EbpfSectionKind::Maps, data: Vec::new(), @@ -1032,6 +1224,7 @@ mod test_utils { max_entries, ..Default::default() }, + inner_def: None, section_index: 0, section_kind: EbpfSectionKind::Maps, data: Vec::new(), diff --git a/aya/src/maps/of_maps/array.rs b/aya/src/maps/of_maps/array.rs new file mode 100644 index 000000000..165c72e9a --- /dev/null +++ b/aya/src/maps/of_maps/array.rs @@ -0,0 +1,271 @@ +//! An array of eBPF maps. + +use std::{ + borrow::{Borrow, BorrowMut}, + fmt, + marker::PhantomData, + os::fd::{AsFd as _, AsRawFd as _}, + path::Path, +}; + +use crate::{ + maps::{FromMapData, InnerMap, MapData, MapError, PinError, check_bounds, check_kv_size}, + sys::{SyscallError, bpf_map_lookup_elem, bpf_map_update_elem}, +}; + +/// An array of eBPF maps. +/// +/// An `ArrayOfMaps` stores references to other eBPF maps. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.12. +#[doc(alias = "BPF_MAP_TYPE_ARRAY_OF_MAPS")] +pub struct ArrayOfMaps { + pub(crate) inner: T, + _v: PhantomData, +} + +impl fmt::Debug for ArrayOfMaps { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ArrayOfMaps") + .field("inner", &self.inner) + .finish() + } +} + +impl, V> ArrayOfMaps { + pub(crate) fn new(map: T) -> Result { + let data = map.borrow(); + check_kv_size::(data)?; + Ok(Self { + inner: map, + _v: PhantomData, + }) + } + + /// Returns the number of elements in the array. + /// + /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. + pub fn len(&self) -> u32 { + self.inner.borrow().obj.max_entries() + } + + /// Returns true if the array is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl, V: FromMapData> ArrayOfMaps { + /// Returns the inner map stored at the given index. + /// + /// The inner map type `V` is determined by the type parameter on the + /// `ArrayOfMaps` itself. + /// + /// # File descriptor cost + /// + /// Each call opens a **new file descriptor** to the inner map. The caller + /// owns the returned map and its FD is closed on drop. Avoid calling this + /// in a tight loop without dropping previous results. + /// + /// # Errors + /// + /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] + /// if `bpf_map_lookup_elem` fails. + pub fn get(&self, index: &u32, flags: u64) -> Result { + let data = self.inner.borrow(); + check_bounds(data, *index)?; + let fd = data.fd().as_fd(); + + let value: Option = + bpf_map_lookup_elem(fd, index, flags).map_err(|io_error| SyscallError { + call: "bpf_map_lookup_elem", + io_error, + })?; + match value { + Some(id) => super::map_from_id(id), + None => Err(MapError::KeyNotFound), + } + } +} + +impl, V: InnerMap> ArrayOfMaps { + /// Sets the value of the element at the given index. + /// + /// # Errors + /// + /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] + /// if `bpf_map_update_elem` fails. + pub fn set(&mut self, index: u32, value: &V, flags: u64) -> Result<(), MapError> { + let data = self.inner.borrow_mut(); + check_bounds(data, index)?; + let fd = data.fd().as_fd(); + bpf_map_update_elem(fd, Some(&index), &value.fd().as_fd().as_raw_fd(), flags).map_err( + |io_error| SyscallError { + call: "bpf_map_update_elem", + io_error, + }, + )?; + Ok(()) + } +} + +impl ArrayOfMaps { + /// Pins the map to a BPF filesystem. + /// + /// When a map is pinned it will remain loaded until the corresponding file + /// is deleted. All parent directories in the given `path` must already exist. + pub fn pin>(self, path: P) -> Result<(), PinError> { + self.inner.pin(path) + } +} + +#[cfg(test)] +mod tests { + use std::io; + + use assert_matches::assert_matches; + use aya_obj::generated::{bpf_cmd, bpf_map_type::BPF_MAP_TYPE_ARRAY_OF_MAPS}; + use libc::{EFAULT, ENOENT}; + + use super::*; + use crate::{ + maps::{Map, test_utils}, + sys::{SysResult, Syscall, override_syscall}, + }; + + fn new_obj_map() -> aya_obj::Map { + test_utils::new_obj_map::(BPF_MAP_TYPE_ARRAY_OF_MAPS) + } + + fn new_map(obj: aya_obj::Map) -> MapData { + test_utils::new_map(obj) + } + + fn sys_error(value: i32) -> SysResult { + Err((-1, io::Error::from_raw_os_error(value))) + } + + #[test] + fn test_wrong_key_size() { + let map = new_map(test_utils::new_obj_map::(BPF_MAP_TYPE_ARRAY_OF_MAPS)); + assert_matches!( + ArrayOfMaps::<_>::new(&map), + Err(MapError::InvalidKeySize { + size: 4, + expected: 1 + }) + ); + } + + #[test] + fn test_try_from_wrong_map() { + let map = new_map(test_utils::new_obj_map::( + aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_HASH, + )); + let map = Map::HashMap(map); + assert_matches!( + ArrayOfMaps::<_>::try_from(&map), + Err(MapError::InvalidMapType { .. }) + ); + } + + #[test] + fn test_new_ok() { + let map = new_map(new_obj_map()); + let _: ArrayOfMaps<_> = ArrayOfMaps::new(&map).unwrap(); + } + + #[test] + fn test_set_syscall_error() { + let mut map = new_map(new_obj_map()); + let inner_map = new_map(test_utils::new_obj_map::( + aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_ARRAY, + )); + let mut arr = ArrayOfMaps::new(&mut map).unwrap(); + + override_syscall(|_| sys_error(EFAULT)); + + assert_matches!( + arr.set(0, &inner_map, 0), + Err(MapError::SyscallError(SyscallError { + call: "bpf_map_update_elem", + .. + })) + ); + } + + #[test] + fn test_set_ok() { + let mut map = new_map(new_obj_map()); + let inner_map = new_map(test_utils::new_obj_map::( + aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_ARRAY, + )); + let mut arr = ArrayOfMaps::new(&mut map).unwrap(); + + override_syscall(|call| match call { + Syscall::Ebpf { + cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM, + .. + } => Ok(0), + _ => sys_error(EFAULT), + }); + + arr.set(0, &inner_map, 0).unwrap(); + } + + #[test] + fn test_set_out_of_bounds() { + let mut map = new_map(new_obj_map()); + let inner_map = new_map(test_utils::new_obj_map::( + aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_ARRAY, + )); + let mut arr = ArrayOfMaps::new(&mut map).unwrap(); + + assert_matches!( + arr.set(1024, &inner_map, 0), + Err(MapError::OutOfBounds { .. }) + ); + } + + #[test] + fn test_get_syscall_error() { + let map = new_map(new_obj_map()); + let arr: ArrayOfMaps<_> = ArrayOfMaps::new(&map).unwrap(); + + override_syscall(|_| sys_error(EFAULT)); + + assert_matches!( + arr.get(&0, 0), + Err(MapError::SyscallError(SyscallError { + call: "bpf_map_lookup_elem", + .. + })) + ); + } + + #[test] + fn test_get_not_found() { + let map = new_map(new_obj_map()); + let arr: ArrayOfMaps<_> = ArrayOfMaps::new(&map).unwrap(); + + override_syscall(|call| match call { + Syscall::Ebpf { + cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM, + .. + } => sys_error(ENOENT), + _ => sys_error(EFAULT), + }); + + assert_matches!(arr.get(&0, 0), Err(MapError::KeyNotFound)); + } + + #[test] + fn test_get_out_of_bounds() { + let map = new_map(new_obj_map()); + let arr: ArrayOfMaps<_> = ArrayOfMaps::new(&map).unwrap(); + + assert_matches!(arr.get(&1024, 0), Err(MapError::OutOfBounds { .. })); + } +} diff --git a/aya/src/maps/of_maps/hash_map.rs b/aya/src/maps/of_maps/hash_map.rs new file mode 100644 index 000000000..15fb2d8dc --- /dev/null +++ b/aya/src/maps/of_maps/hash_map.rs @@ -0,0 +1,284 @@ +//! A hash map of eBPF maps. + +use std::{ + borrow::{Borrow, BorrowMut}, + fmt, + marker::PhantomData, + os::fd::{AsFd as _, AsRawFd as _}, + path::Path, +}; + +use crate::{ + Pod, + maps::{FromMapData, InnerMap, MapData, MapError, MapKeys, PinError, check_kv_size, hash_map}, + sys::{SyscallError, bpf_map_lookup_elem}, +}; + +/// A hashmap of eBPF maps. +/// +/// A `HashOfMaps` stores references to other eBPF maps, keyed by an arbitrary key type. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.12. +#[doc(alias = "BPF_MAP_TYPE_HASH_OF_MAPS")] +pub struct HashOfMaps { + pub(crate) inner: T, + _kv: PhantomData<(K, V)>, +} + +impl fmt::Debug for HashOfMaps { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("HashOfMaps") + .field("inner", &self.inner) + .finish() + } +} + +impl, K: Pod, V> HashOfMaps { + pub(crate) fn new(map: T) -> Result { + let data = map.borrow(); + check_kv_size::(data)?; + + Ok(Self { + inner: map, + _kv: PhantomData, + }) + } + + /// An iterator visiting all keys in arbitrary order. The iterator element + /// type is `Result`. + pub fn keys(&self) -> MapKeys<'_, K> { + MapKeys::new(self.inner.borrow()) + } +} + +impl, K: Pod, V: FromMapData> HashOfMaps { + /// Returns the inner map associated with the key. + /// + /// The inner map type `V` is determined by the type parameter on the + /// `HashOfMaps` itself. + /// + /// # File descriptor cost + /// + /// Each call opens a **new file descriptor** to the inner map. The caller + /// owns the returned map and its FD is closed on drop. Avoid calling this + /// in a tight loop without dropping previous results. + pub fn get(&self, key: &K, flags: u64) -> Result { + let fd = self.inner.borrow().fd().as_fd(); + let value: Option = + bpf_map_lookup_elem(fd, key, flags).map_err(|io_error| SyscallError { + call: "bpf_map_lookup_elem", + io_error, + })?; + match value { + Some(id) => super::map_from_id(id), + None => Err(MapError::KeyNotFound), + } + } +} + +impl, K: Pod, V: InnerMap> HashOfMaps { + /// Inserts a key-value pair into the map. + pub fn insert(&mut self, key: impl Borrow, value: &V, flags: u64) -> Result<(), MapError> { + hash_map::insert( + self.inner.borrow_mut(), + key.borrow(), + &value.fd().as_fd().as_raw_fd(), + flags, + ) + } +} + +impl, K: Pod, V> HashOfMaps { + /// Removes a key from the map. + pub fn remove(&mut self, key: &K) -> Result<(), MapError> { + hash_map::remove(self.inner.borrow_mut(), key) + } +} + +impl HashOfMaps { + /// Pins the map to a BPF filesystem. + /// + /// When a map is pinned it will remain loaded until the corresponding file + /// is deleted. All parent directories in the given `path` must already exist. + pub fn pin>(self, path: P) -> Result<(), PinError> { + self.inner.pin(path) + } +} + +#[cfg(test)] +mod tests { + use std::io; + + use assert_matches::assert_matches; + use aya_obj::generated::{bpf_cmd, bpf_map_type::BPF_MAP_TYPE_HASH_OF_MAPS}; + use libc::{EFAULT, ENOENT}; + + use super::*; + use crate::{ + maps::{Map, test_utils}, + sys::{SysResult, Syscall, override_syscall}, + }; + + fn new_obj_map() -> aya_obj::Map { + test_utils::new_obj_map::(BPF_MAP_TYPE_HASH_OF_MAPS) + } + + fn new_map(obj: aya_obj::Map) -> MapData { + test_utils::new_map(obj) + } + + fn sys_error(value: i32) -> SysResult { + Err((-1, io::Error::from_raw_os_error(value))) + } + + #[test] + fn test_wrong_key_size() { + let map = new_map(new_obj_map()); + assert_matches!( + HashOfMaps::<_, u8>::new(&map), + Err(MapError::InvalidKeySize { + size: 1, + expected: 4 + }) + ); + } + + #[test] + fn test_try_from_wrong_map() { + let map = new_map(test_utils::new_obj_map::( + aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_HASH, + )); + let map = Map::HashMap(map); + assert_matches!( + HashOfMaps::<_, u32>::try_from(&map), + Err(MapError::InvalidMapType { .. }) + ); + } + + #[test] + fn test_new_ok() { + let map = new_map(new_obj_map()); + HashOfMaps::<_, u32>::new(&map).unwrap(); + } + + #[test] + fn test_insert_syscall_error() { + let mut map = new_map(new_obj_map()); + let inner_map = new_map(test_utils::new_obj_map::( + aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_HASH, + )); + let mut hm = HashOfMaps::new(&mut map).unwrap(); + + override_syscall(|_| sys_error(EFAULT)); + + assert_matches!( + hm.insert(1u32, &inner_map, 0), + Err(MapError::SyscallError(SyscallError { + call: "bpf_map_update_elem", + .. + })) + ); + } + + #[test] + fn test_insert_ok() { + let mut map = new_map(new_obj_map()); + let inner_map = new_map(test_utils::new_obj_map::( + aya_obj::generated::bpf_map_type::BPF_MAP_TYPE_HASH, + )); + let mut hm = HashOfMaps::new(&mut map).unwrap(); + + override_syscall(|call| match call { + Syscall::Ebpf { + cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM, + .. + } => Ok(0), + _ => sys_error(EFAULT), + }); + + hm.insert(1u32, &inner_map, 0).unwrap(); + } + + #[test] + fn test_remove_syscall_error() { + let mut map = new_map(new_obj_map()); + let mut hm = HashOfMaps::<_, u32>::new(&mut map).unwrap(); + + override_syscall(|_| sys_error(EFAULT)); + + assert_matches!( + hm.remove(&1u32), + Err(MapError::SyscallError(SyscallError { + call: "bpf_map_delete_elem", + .. + })) + ); + } + + #[test] + fn test_remove_ok() { + let mut map = new_map(new_obj_map()); + let mut hm = HashOfMaps::<_, u32>::new(&mut map).unwrap(); + + override_syscall(|call| match call { + Syscall::Ebpf { + cmd: bpf_cmd::BPF_MAP_DELETE_ELEM, + .. + } => Ok(0), + _ => sys_error(EFAULT), + }); + + hm.remove(&1u32).unwrap(); + } + + #[test] + fn test_get_syscall_error() { + let map = new_map(new_obj_map()); + let hm = HashOfMaps::<_, u32>::new(&map).unwrap(); + + override_syscall(|_| sys_error(EFAULT)); + + assert_matches!( + hm.get(&1u32, 0), + Err(MapError::SyscallError(SyscallError { + call: "bpf_map_lookup_elem", + .. + })) + ); + } + + #[test] + fn test_get_not_found() { + let map = new_map(new_obj_map()); + let hm = HashOfMaps::<_, u32>::new(&map).unwrap(); + + override_syscall(|call| match call { + Syscall::Ebpf { + cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM, + .. + } => sys_error(ENOENT), + _ => sys_error(EFAULT), + }); + + assert_matches!(hm.get(&1u32, 0), Err(MapError::KeyNotFound)); + } + + #[test] + fn test_keys_empty() { + let map = new_map(new_obj_map()); + let hm = HashOfMaps::<_, u32>::new(&map).unwrap(); + + override_syscall(|call| match call { + Syscall::Ebpf { + cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY, + .. + } => sys_error(ENOENT), + _ => sys_error(EFAULT), + }); + + let keys: Result, _> = hm.keys().collect(); + assert_matches!(keys, Ok(ks) if ks.is_empty()); + } +} diff --git a/aya/src/maps/of_maps/mod.rs b/aya/src/maps/of_maps/mod.rs new file mode 100644 index 000000000..3da78763b --- /dev/null +++ b/aya/src/maps/of_maps/mod.rs @@ -0,0 +1,18 @@ +//! Support for BPF maps that contain references to other maps. +mod array; +mod hash_map; + +pub use array::ArrayOfMaps; +pub use hash_map::HashOfMaps; + +use super::{FromMapData, MapData, MapError}; + +/// Converts a map ID returned by the kernel into a typed map. +/// +/// The kernel's map-of-maps API is asymmetric: update takes the FD of the inner map, +/// but lookup returns the ID. This helper converts the ID back to an FD and constructs +/// the typed map via [`FromMapData`]. +fn map_from_id(id: u32) -> Result { + let map_data = MapData::from_id(id)?; + M::from_map_data(map_data) +} diff --git a/aya/src/maps/perf/perf_event_array.rs b/aya/src/maps/perf/perf_event_array.rs index 03dc483a3..ba6d10400 100644 --- a/aya/src/maps/perf/perf_event_array.rs +++ b/aya/src/maps/perf/perf_event_array.rs @@ -209,6 +209,10 @@ impl> PerfEventArray { let data: &MapData = self.map.deref().borrow(); data.pin(path) } + + pub(crate) fn map_data(&self) -> &MapData { + self.map.deref().borrow() + } } impl> PerfEventArray { diff --git a/aya/src/maps/ring_buf.rs b/aya/src/maps/ring_buf.rs index 94c8eda8c..c3d877555 100644 --- a/aya/src/maps/ring_buf.rs +++ b/aya/src/maps/ring_buf.rs @@ -109,6 +109,10 @@ impl> RingBuf { producer, }) } + + pub(crate) fn map_data(&self) -> &MapData { + self.map.borrow() + } } impl RingBuf { diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index b265adf97..afb5d4ccb 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -55,6 +55,7 @@ pub(crate) fn bpf_create_map( name: &CStr, def: &aya_obj::Map, btf_fd: Option>, + inner_map_fd: Option>, ) -> io::Result { let mut attr = unsafe { mem::zeroed::() }; @@ -66,6 +67,11 @@ pub(crate) fn bpf_create_map( u.map_flags = def.map_flags(); u.map_extra = def.map_extra(); + // For map-of-maps types, set the inner_map_fd. + if let Some(inner_fd) = inner_map_fd { + u.inner_map_fd = inner_fd.as_raw_fd() as u32; + } + if let aya_obj::Map::Btf(m) = def { // Mimic https://github.com/libbpf/libbpf/issues/355 // Currently a bunch of (usually pretty specialized) BPF maps do not support @@ -1075,6 +1081,7 @@ pub(crate) fn is_bpf_global_data_supported() -> bool { max_entries: 1, ..Default::default() }, + inner_def: None, section_index: 0, section_kind: EbpfSectionKind::Maps, symbol_index: None, @@ -1576,7 +1583,7 @@ bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH`"] let name = CString::new("FILTER").unwrap(); let btf_fd = unsafe { BorrowedFd::borrow_raw(BTF_FD) }; - bpf_create_map(&name, &map, Some(btf_fd)).unwrap(); + bpf_create_map(&name, &map, Some(btf_fd), None).unwrap(); } #[test_case(bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY ; "perf_event_array")] @@ -1621,6 +1628,6 @@ bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH`"] let name = CString::new("TEST").unwrap(); let btf_fd = unsafe { BorrowedFd::borrow_raw(BTF_FD) }; - bpf_create_map(&name, &map, Some(btf_fd)).unwrap(); + bpf_create_map(&name, &map, Some(btf_fd), None).unwrap(); } } diff --git a/ebpf/aya-ebpf/src/btf_maps/bloom_filter.rs b/ebpf/aya-ebpf/src/btf_maps/bloom_filter.rs index f11a74341..d8d4be2fd 100644 --- a/ebpf/aya-ebpf/src/btf_maps/bloom_filter.rs +++ b/ebpf/aya-ebpf/src/btf_maps/bloom_filter.rs @@ -26,7 +26,7 @@ btf_map_def!( map_flags: FLAGS, key_type: (), value_type: T, - map_extra: *const [i32; HASH_FUNCS], + map_extra: *const [i32; HASH_FUNCS] = ::core::ptr::null(), ); impl diff --git a/ebpf/aya-ebpf/src/btf_maps/mod.rs b/ebpf/aya-ebpf/src/btf_maps/mod.rs index eec086527..72ad48b61 100644 --- a/ebpf/aya-ebpf/src/btf_maps/mod.rs +++ b/ebpf/aya-ebpf/src/btf_maps/mod.rs @@ -3,6 +3,7 @@ pub mod bloom_filter; pub mod dev_map; pub mod dev_map_hash; pub mod lpm_trie; +pub mod of_maps; pub mod per_cpu_array; pub mod perf_event_array; pub mod perf_event_byte_array; @@ -21,6 +22,7 @@ pub use bloom_filter::BloomFilter; pub use dev_map::DevMap; pub use dev_map_hash::DevMapHash; pub use lpm_trie::LpmTrie; +pub use of_maps::{ArrayOfMaps, HashOfMaps}; pub use per_cpu_array::PerCpuArray; pub use perf_event_array::PerfEventArray; pub use perf_event_byte_array::PerfEventByteArray; @@ -34,6 +36,56 @@ pub use sock_map::SockMap; pub use stack::Stack; pub use stack_trace::StackTrace; +mod private { + /// Sealed trait exposing the key and value types of a BTF map definition. + #[expect( + unnameable_types, + reason = "sealed trait pattern requires pub trait in private mod" + )] + pub trait MapDef { + /// The key type of this map. + type Key; + /// The value type of this map. + type Value; + } +} + +/// Key and value types of a BTF map definition. +/// +/// Used by map-of-maps types to perform fused lookups that combine the outer +/// and inner `bpf_map_lookup_elem` calls in a single method. +/// +/// This trait is sealed and cannot be implemented outside this crate. +pub trait MapDef: private::MapDef {} + +impl MapDef for T {} + +/// Performs the inner half of a fused map-of-maps lookup, returning a shared reference. +/// +/// # Safety +/// +/// The caller must ensure the returned reference does not alias a mutable +/// pointer obtained from the same map element. +#[inline(always)] +pub(crate) unsafe fn lookup_inner<'a, M: private::MapDef>( + inner_map: core::ptr::NonNull, + key: &M::Key, +) -> Option<&'a M::Value> { + // SAFETY: Both pointers are returned by BPF helpers and are valid for + // the duration of the program. We only produce a shared reference. + unsafe { crate::lookup::(inner_map.as_ptr().cast(), key).map(|p| p.as_ref()) } +} + +/// Same as [`lookup_inner`] but returns a mutable pointer. +#[inline(always)] +pub(crate) fn lookup_inner_ptr_mut( + inner_map: core::ptr::NonNull, + key: &M::Key, +) -> Option<*mut M::Value> { + crate::lookup::(inner_map.as_ptr().cast(), key) + .map(core::ptr::NonNull::as_ptr) +} + /// Defines a BTF-compatible map struct with flat `#[repr(C)]` layout. /// /// This macro generates a map definition struct that produces BTF metadata @@ -46,7 +98,60 @@ pub use stack_trace::StackTrace; /// Generics are an optional list of type parameters (with optional defaults) /// optionally followed by a semicolon and const parameters (with optional /// defaults). Lifetimes and bounds are not supported. +/// +/// # Map-of-maps support +/// +/// For map-of-maps types (`ArrayOfMaps`, `HashOfMaps`), add an `inner_map` clause: +/// +/// ```ignore +/// btf_map_def!( +/// pub struct HashOfMaps, +/// map_type: BPF_MAP_TYPE_HASH_OF_MAPS, +/// max_entries: MAX_ENTRIES, +/// map_flags: FLAGS, +/// key_type: K, +/// value_type: u32, +/// inner_map: V, +/// ); +/// ``` +/// +/// This generates a `values: [*const V; 0]` field for BTF relocation. The inner +/// map type `V` is encoded in BTF so that loaders can resolve the inner map +/// template. macro_rules! btf_map_def { + // Map-of-maps (with inner_map) - rewrites into the regular arm with a + // `values` extra field whose initializer is `[]` (a zero-length array). + ( + $(#[$attr:meta])* + $vis:vis struct $name:ident< + $($ty_gen:ident $(= $ty_default:ty)?),+ + $(; $(const $const_gen:ident : $const_ty:ty $(= $const_default:tt)?),+)? + $(,)? + >, + map_type: $map_type:ident, + max_entries: $max_entries:expr, + map_flags: $map_flags:expr, + key_type: $key_ty:ty, + value_type: $value_ty:ty, + inner_map: $inner_ty:ty + $(,)? + ) => { + $crate::btf_maps::btf_map_def!( + $(#[$attr])* + $vis struct $name< + $($ty_gen $(= $ty_default)?),+ + $(; $(const $const_gen : $const_ty $(= $const_default)?),+)? + >, + map_type: $map_type, + max_entries: $max_entries, + map_flags: $map_flags, + key_type: $key_ty, + value_type: $value_ty, + values: [*const $inner_ty; 0] = [] + ); + }; + + // Regular map (with optional extra fields and initializers) ( $(#[$attr:meta])* $vis:vis struct $name:ident< @@ -59,7 +164,7 @@ macro_rules! btf_map_def { map_flags: $map_flags:expr, key_type: $key_ty:ty, value_type: $value_ty:ty - $(, $extra_field:ident : $extra_ty:ty)* + $(, $extra_field:ident : $extra_ty:ty = $extra_init:expr)* $(,)? ) => { $(#[$attr])* @@ -116,7 +221,7 @@ macro_rules! btf_map_def { max_entries: ::core::ptr::null(), map_flags: ::core::ptr::null(), - $($extra_field: ::core::ptr::null(),)* + $($extra_field: $extra_init,)* } } @@ -125,6 +230,17 @@ macro_rules! btf_map_def { ::core::ptr::from_ref(self).cast_mut().cast() } } + + impl< + $($ty_gen,)* + $($(const $const_gen : $const_ty,)+)? + > $crate::btf_maps::private::MapDef for $name< + $($ty_gen,)* + $($($const_gen,)+)? + > { + type Key = $key_ty; + type Value = $value_ty; + } }; } diff --git a/ebpf/aya-ebpf/src/btf_maps/of_maps/array.rs b/ebpf/aya-ebpf/src/btf_maps/of_maps/array.rs new file mode 100644 index 000000000..6d758ef94 --- /dev/null +++ b/ebpf/aya-ebpf/src/btf_maps/of_maps/array.rs @@ -0,0 +1,72 @@ +use core::ptr::NonNull; + +use crate::{btf_maps::btf_map_def, lookup}; + +btf_map_def!( + /// A BTF-compatible BPF map that stores references to other maps (array of maps). + /// + /// This map type allows you to store file descriptors of other BPF maps, + /// enabling dynamic map selection at runtime. + /// + /// # Minimum kernel version + /// + /// The minimum kernel version required to use this feature is 5.7. + /// + /// # Example + /// + /// ```rust,no_run + /// use aya_ebpf::{btf_maps::{Array, ArrayOfMaps}, macros::btf_map}; + /// + /// // The inner map definition is parsed from BTF at load time. + /// #[btf_map] + /// static OUTER: ArrayOfMaps, 4> = ArrayOfMaps::new(); + /// ``` + pub struct ArrayOfMaps, + map_type: BPF_MAP_TYPE_ARRAY_OF_MAPS, + max_entries: MAX_ENTRIES, + map_flags: FLAGS, + key_type: u32, + value_type: u32, + inner_map: V, +); + +impl ArrayOfMaps { + /// Retrieves a reference to the inner map at the given index. + /// + /// Returns `None` if the index is out of bounds or if no map is stored + /// at that index. + #[inline(always)] + pub fn get(&self, index: u32) -> Option<&V> { + // SAFETY: We only read from the map through BPF helpers. + // The struct fields are never accessed - only the address is used. + unsafe { self.lookup(index).map(|p| p.as_ref()) } + } + + #[inline(always)] + unsafe fn lookup(&self, index: u32) -> Option> { + lookup(self.as_ptr(), &index) + } +} + +impl + ArrayOfMaps +{ + /// Looks up a value directly in the inner map at `outer_index`. + /// + /// Performs both the outer and inner `bpf_map_lookup_elem` calls in a + /// single method, producing fewer BPF instructions between the two + /// helpers. This reduces verifier state explosion in tight loops. + #[inline(always)] + pub fn get_value(&self, outer_index: u32, inner_key: &V::Key) -> Option<&V::Value> { + let inner: NonNull = lookup(self.as_ptr(), &outer_index)?; + // SAFETY: Array lookups are safe (no BPF_F_NO_PREALLOC aliasing concern). + unsafe { crate::btf_maps::lookup_inner(inner, inner_key) } + } + + /// Same as [`get_value`](Self::get_value) but returns a mutable pointer. + #[inline(always)] + pub fn get_value_ptr_mut(&self, outer_index: u32, inner_key: &V::Key) -> Option<*mut V::Value> { + let inner: NonNull = lookup(self.as_ptr(), &outer_index)?; + crate::btf_maps::lookup_inner_ptr_mut(inner, inner_key) + } +} diff --git a/ebpf/aya-ebpf/src/btf_maps/of_maps/hash_map.rs b/ebpf/aya-ebpf/src/btf_maps/of_maps/hash_map.rs new file mode 100644 index 000000000..7fe829f75 --- /dev/null +++ b/ebpf/aya-ebpf/src/btf_maps/of_maps/hash_map.rs @@ -0,0 +1,88 @@ +use core::ptr::NonNull; + +use crate::{btf_maps::btf_map_def, lookup}; + +btf_map_def!( + /// A BTF-compatible BPF hash map that stores references to other maps (hash of maps). + /// + /// This map type allows you to store file descriptors of other BPF maps + /// indexed by arbitrary keys, enabling dynamic map selection at runtime. + /// + /// # Minimum kernel version + /// + /// The minimum kernel version required to use this feature is 5.7. + /// + /// # Example + /// + /// ```rust,no_run + /// use aya_ebpf::{btf_maps::{Array, HashOfMaps}, macros::btf_map}; + /// + /// // The inner map definition is parsed from BTF at load time. + /// #[btf_map] + /// static OUTER: HashOfMaps, 4> = HashOfMaps::new(); + /// ``` + pub struct HashOfMaps, + map_type: BPF_MAP_TYPE_HASH_OF_MAPS, + max_entries: MAX_ENTRIES, + map_flags: FLAGS, + key_type: K, + value_type: u32, + inner_map: V, +); + +impl HashOfMaps { + /// Retrieve the inner map associated with `key` from the map. + /// + /// # Safety + /// + /// Unless the map flag `BPF_F_NO_PREALLOC` is used, the kernel does not guarantee the + /// atomicity of `insert` or `remove`, and any element removed from the map might get + /// aliased by another element in the map, causing garbage to be read, or corruption in + /// case of writes. + #[inline(always)] + pub unsafe fn get(&self, key: &K) -> Option<&V> { + // SAFETY: We only read from the map through BPF helpers. + // The struct fields are never accessed - only the address is used. + unsafe { self.lookup(key).map(|p| p.as_ref()) } + } + + #[inline(always)] + unsafe fn lookup(&self, key: &K) -> Option> { + lookup(self.as_ptr(), key) + } +} + +impl + HashOfMaps +{ + /// Looks up a value directly in the inner map associated with `outer_key`. + /// + /// Performs both the outer and inner `bpf_map_lookup_elem` calls in a + /// single method, producing fewer BPF instructions between the two + /// helpers. This reduces verifier state explosion in tight loops. + /// + /// # Safety + /// + /// See [`get`](Self::get). + #[inline(always)] + pub unsafe fn get_value(&self, outer_key: &K, inner_key: &V::Key) -> Option<&V::Value> { + let inner: NonNull = lookup(self.as_ptr(), outer_key)?; + // SAFETY: The caller upholds the aliasing invariants (see `get`). + unsafe { crate::btf_maps::lookup_inner(inner, inner_key) } + } + + /// Same as [`get_value`](Self::get_value) but returns a mutable pointer. + /// + /// # Safety + /// + /// See [`get_value`](Self::get_value). + #[inline(always)] + pub unsafe fn get_value_ptr_mut( + &self, + outer_key: &K, + inner_key: &V::Key, + ) -> Option<*mut V::Value> { + let inner: NonNull = lookup(self.as_ptr(), outer_key)?; + crate::btf_maps::lookup_inner_ptr_mut(inner, inner_key) + } +} diff --git a/ebpf/aya-ebpf/src/btf_maps/of_maps/mod.rs b/ebpf/aya-ebpf/src/btf_maps/of_maps/mod.rs new file mode 100644 index 000000000..a067bec0a --- /dev/null +++ b/ebpf/aya-ebpf/src/btf_maps/of_maps/mod.rs @@ -0,0 +1,6 @@ +//! BTF-compatible BPF maps that contain references to other maps. +mod array; +mod hash_map; + +pub use array::ArrayOfMaps; +pub use hash_map::HashOfMaps; diff --git a/ebpf/aya-ebpf/src/btf_maps/ring_buf.rs b/ebpf/aya-ebpf/src/btf_maps/ring_buf.rs index 512b7797e..1443e9736 100644 --- a/ebpf/aya-ebpf/src/btf_maps/ring_buf.rs +++ b/ebpf/aya-ebpf/src/btf_maps/ring_buf.rs @@ -18,7 +18,7 @@ btf_map_def!( map_flags: FLAGS, key_type: (), value_type: T, - value_size: *const [i32; 0], + value_size: *const [i32; 0] = ::core::ptr::null(), ); impl RingBuf { diff --git a/ebpf/aya-ebpf/src/maps/array.rs b/ebpf/aya-ebpf/src/maps/array.rs index 05fc608e5..26b999061 100644 --- a/ebpf/aya-ebpf/src/maps/array.rs +++ b/ebpf/aya-ebpf/src/maps/array.rs @@ -12,6 +12,11 @@ pub struct Array { _t: PhantomData, } +impl super::private::Map for Array { + type Key = u32; + type Value = T; +} + impl Array { map_constructors!(u32, T, BPF_MAP_TYPE_ARRAY, phantom _t); diff --git a/ebpf/aya-ebpf/src/maps/bloom_filter.rs b/ebpf/aya-ebpf/src/maps/bloom_filter.rs index 30ed357c7..d7460b85b 100644 --- a/ebpf/aya-ebpf/src/maps/bloom_filter.rs +++ b/ebpf/aya-ebpf/src/maps/bloom_filter.rs @@ -12,6 +12,11 @@ pub struct BloomFilter { _t: PhantomData, } +impl super::private::Map for BloomFilter { + type Key = (); + type Value = T; +} + impl BloomFilter { map_constructors!((), T, BPF_MAP_TYPE_BLOOM_FILTER, phantom _t); diff --git a/ebpf/aya-ebpf/src/maps/hash_map.rs b/ebpf/aya-ebpf/src/maps/hash_map.rs index 705edee58..07fd751c4 100644 --- a/ebpf/aya-ebpf/src/maps/hash_map.rs +++ b/ebpf/aya-ebpf/src/maps/hash_map.rs @@ -16,6 +16,11 @@ macro_rules! define_hash_map { _kv: PhantomData<(K, V)>, } + impl $crate::maps::private::Map for $name { + type Key = K; + type Value = V; + } + impl $name { pub const fn with_max_entries(max_entries: u32, flags: u32) -> Self { Self::new(max_entries, flags, PinningType::None) diff --git a/ebpf/aya-ebpf/src/maps/lpm_trie.rs b/ebpf/aya-ebpf/src/maps/lpm_trie.rs index d76a2bbee..6b61afdee 100644 --- a/ebpf/aya-ebpf/src/maps/lpm_trie.rs +++ b/ebpf/aya-ebpf/src/maps/lpm_trie.rs @@ -15,6 +15,11 @@ pub struct LpmTrie { _kv: PhantomData<(K, V)>, } +impl super::private::Map for LpmTrie { + type Key = Key; + type Value = V; +} + #[repr(C, packed)] pub struct Key { /// Represents the number of bits matched against. diff --git a/ebpf/aya-ebpf/src/maps/mod.rs b/ebpf/aya-ebpf/src/maps/mod.rs index b71af653f..6b6ab329e 100644 --- a/ebpf/aya-ebpf/src/maps/mod.rs +++ b/ebpf/aya-ebpf/src/maps/mod.rs @@ -17,6 +17,7 @@ pub(crate) mod def { unsafe impl Sync for MapDef {} impl MapDef { + /// Creates a new map definition with key type `K` and value type `V`. pub(crate) const fn new( type_: u32, max_entries: u32, @@ -106,3 +107,24 @@ pub use sock_map::SockMap; pub use stack::Stack; pub use stack_trace::StackTrace; pub use xdp::{CpuMap, DevMap, DevMapHash, XskMap}; + +mod private { + /// Sealed trait to prevent external implementations of [`super::Map`]. + #[expect( + unnameable_types, + reason = "sealed trait pattern requires pub trait in private mod" + )] + pub trait Map { + /// The key type declared in this map's definition. + type Key; + /// The value type declared in this map's definition. + type Value; + } +} + +/// Marker trait for all eBPF maps that can be used in a map of maps. +/// +/// This trait is sealed and cannot be implemented outside this crate. +pub trait Map: private::Map {} + +impl Map for T {} diff --git a/ebpf/aya-ebpf/src/maps/per_cpu_array.rs b/ebpf/aya-ebpf/src/maps/per_cpu_array.rs index 8283203dc..8cc93d762 100644 --- a/ebpf/aya-ebpf/src/maps/per_cpu_array.rs +++ b/ebpf/aya-ebpf/src/maps/per_cpu_array.rs @@ -12,6 +12,11 @@ pub struct PerCpuArray { _t: PhantomData, } +impl super::private::Map for PerCpuArray { + type Key = u32; + type Value = T; +} + impl PerCpuArray { map_constructors!(u32, T, BPF_MAP_TYPE_PERCPU_ARRAY, phantom _t); diff --git a/ebpf/aya-ebpf/src/maps/queue.rs b/ebpf/aya-ebpf/src/maps/queue.rs index af51d5b74..240e01d77 100644 --- a/ebpf/aya-ebpf/src/maps/queue.rs +++ b/ebpf/aya-ebpf/src/maps/queue.rs @@ -13,6 +13,11 @@ pub struct Queue { _t: PhantomData, } +impl super::private::Map for Queue { + type Key = (); + type Value = T; +} + impl Queue { map_constructors!((), T, BPF_MAP_TYPE_QUEUE, phantom _t); diff --git a/ebpf/aya-ebpf/src/maps/ring_buf.rs b/ebpf/aya-ebpf/src/maps/ring_buf.rs index f0bf8dc08..dd66cdbbb 100644 --- a/ebpf/aya-ebpf/src/maps/ring_buf.rs +++ b/ebpf/aya-ebpf/src/maps/ring_buf.rs @@ -21,6 +21,11 @@ pub struct RingBuf { def: MapDef, } +impl super::private::Map for RingBuf { + type Key = (); + type Value = (); +} + /// A ring buffer entry, returned from [`RingBuf::reserve_bytes`]. /// /// You must [`submit`] or [`discard`] this entry before it gets dropped. diff --git a/ebpf/aya-ebpf/src/maps/sock_hash.rs b/ebpf/aya-ebpf/src/maps/sock_hash.rs index 3ce23c991..e7f2290d3 100644 --- a/ebpf/aya-ebpf/src/maps/sock_hash.rs +++ b/ebpf/aya-ebpf/src/maps/sock_hash.rs @@ -23,6 +23,11 @@ pub struct SockHash { _k: PhantomData, } +impl super::private::Map for SockHash { + type Key = K; + type Value = u32; +} + impl SockHash { map_constructors!(K, u32, BPF_MAP_TYPE_SOCKHASH, phantom _k); diff --git a/ebpf/aya-ebpf/src/maps/sock_map.rs b/ebpf/aya-ebpf/src/maps/sock_map.rs index 486e51a0e..fee7b6571 100644 --- a/ebpf/aya-ebpf/src/maps/sock_map.rs +++ b/ebpf/aya-ebpf/src/maps/sock_map.rs @@ -16,6 +16,11 @@ pub struct SockMap { def: MapDef, } +impl super::private::Map for SockMap { + type Key = u32; + type Value = u32; +} + impl SockMap { map_constructors!(u32, u32, BPF_MAP_TYPE_SOCKMAP); diff --git a/ebpf/aya-ebpf/src/maps/stack.rs b/ebpf/aya-ebpf/src/maps/stack.rs index 99bc2e366..54106c2c1 100644 --- a/ebpf/aya-ebpf/src/maps/stack.rs +++ b/ebpf/aya-ebpf/src/maps/stack.rs @@ -13,6 +13,11 @@ pub struct Stack { _t: PhantomData, } +impl super::private::Map for Stack { + type Key = (); + type Value = T; +} + impl Stack { map_constructors!((), T, BPF_MAP_TYPE_STACK, phantom _t); diff --git a/ebpf/aya-ebpf/src/maps/stack_trace.rs b/ebpf/aya-ebpf/src/maps/stack_trace.rs index 9604a756d..6afe3b732 100644 --- a/ebpf/aya-ebpf/src/maps/stack_trace.rs +++ b/ebpf/aya-ebpf/src/maps/stack_trace.rs @@ -10,6 +10,11 @@ pub struct StackTrace { def: MapDef, } +impl super::private::Map for StackTrace { + type Key = u32; + type Value = [u64; PERF_MAX_STACK_DEPTH as usize]; +} + impl StackTrace { map_constructors!( u32, diff --git a/ebpf/aya-ebpf/src/maps/xdp/cpu_map.rs b/ebpf/aya-ebpf/src/maps/xdp/cpu_map.rs index df85c42e7..bccf12d6e 100644 --- a/ebpf/aya-ebpf/src/maps/xdp/cpu_map.rs +++ b/ebpf/aya-ebpf/src/maps/xdp/cpu_map.rs @@ -33,6 +33,11 @@ pub struct CpuMap { def: MapDef, } +impl super::super::private::Map for CpuMap { + type Key = u32; + type Value = bpf_cpumap_val; +} + impl CpuMap { map_constructors!( u32, diff --git a/ebpf/aya-ebpf/src/maps/xdp/dev_map.rs b/ebpf/aya-ebpf/src/maps/xdp/dev_map.rs index ffba1ed68..35ee8cadb 100644 --- a/ebpf/aya-ebpf/src/maps/xdp/dev_map.rs +++ b/ebpf/aya-ebpf/src/maps/xdp/dev_map.rs @@ -35,6 +35,11 @@ pub struct DevMap { def: MapDef, } +impl super::super::private::Map for DevMap { + type Key = u32; + type Value = bpf_devmap_val; +} + impl DevMap { map_constructors!( u32, diff --git a/ebpf/aya-ebpf/src/maps/xdp/dev_map_hash.rs b/ebpf/aya-ebpf/src/maps/xdp/dev_map_hash.rs index 781c864fa..7cf91d82b 100644 --- a/ebpf/aya-ebpf/src/maps/xdp/dev_map_hash.rs +++ b/ebpf/aya-ebpf/src/maps/xdp/dev_map_hash.rs @@ -37,6 +37,11 @@ pub struct DevMapHash { def: MapDef, } +impl super::super::private::Map for DevMapHash { + type Key = u32; + type Value = bpf_devmap_val; +} + impl DevMapHash { map_constructors!( u32, diff --git a/ebpf/aya-ebpf/src/maps/xdp/xsk_map.rs b/ebpf/aya-ebpf/src/maps/xdp/xsk_map.rs index 8909d7e39..67b8ddcc5 100644 --- a/ebpf/aya-ebpf/src/maps/xdp/xsk_map.rs +++ b/ebpf/aya-ebpf/src/maps/xdp/xsk_map.rs @@ -54,6 +54,11 @@ pub struct XskMap { def: MapDef, } +impl super::super::private::Map for XskMap { + type Key = u32; + type Value = u32; +} + impl XskMap { map_constructors!( u32, diff --git a/test/integration-common/src/lib.rs b/test/integration-common/src/lib.rs index c3102ed47..86edac9d8 100644 --- a/test/integration-common/src/lib.rs +++ b/test/integration-common/src/lib.rs @@ -119,6 +119,21 @@ pub mod printk { pub const TEST_ISIZE: isize = isize::MIN; } +pub mod btf_map_of_maps { + /// Capacity of each inner array shared between userspace and the eBPF probes. + pub const INNER_MAX_ENTRIES: u32 = 10; + + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + #[repr(C)] + pub struct TestResult { + pub value: u32, + pub ran: u32, + } + + #[cfg(feature = "user")] + unsafe impl aya::Pod for TestResult {} +} + pub mod sk_storage { #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(C)] diff --git a/test/integration-ebpf/Cargo.toml b/test/integration-ebpf/Cargo.toml index e09cb9784..0c00301b0 100644 --- a/test/integration-ebpf/Cargo.toml +++ b/test/integration-ebpf/Cargo.toml @@ -171,3 +171,7 @@ path = "src/stack_trace.rs" [[bin]] name = "stack_trace_lsm" path = "src/stack_trace_lsm.rs" + +[[bin]] +name = "btf_map_of_maps" +path = "src/btf_map_of_maps.rs" diff --git a/test/integration-ebpf/src/btf_map_of_maps.rs b/test/integration-ebpf/src/btf_map_of_maps.rs new file mode 100644 index 000000000..71c38fcb2 --- /dev/null +++ b/test/integration-ebpf/src/btf_map_of_maps.rs @@ -0,0 +1,130 @@ +#![no_std] +#![no_main] +#![expect(unused_crate_dependencies, reason = "used in other bins")] + +//! BTF-compatible map-of-maps tests. +//! +//! Uses BTF map definitions compatible with both aya and libbpf loaders. + +use aya_ebpf::{ + btf_maps::{Array, ArrayOfMaps, HashOfMaps}, + macros::{btf_map, uprobe}, + programs::ProbeContext, +}; +use integration_common::btf_map_of_maps::{INNER_MAX_ENTRIES, TestResult}; + +#[cfg(not(test))] +extern crate ebpf_panic; + +#[btf_map] +static ARRAY_OF_MAPS: ArrayOfMaps, 4> = + ArrayOfMaps::new(); + +#[btf_map] +static HASH_OF_MAPS: HashOfMaps, 4> = + HashOfMaps::new(); + +#[btf_map] +static RESULTS: Array = Array::new(); + +#[unsafe(no_mangle)] +#[inline(never)] +pub const extern "C" fn trigger_btf_array_of_maps() { + core::hint::black_box(()); +} + +#[unsafe(no_mangle)] +#[inline(never)] +pub const extern "C" fn trigger_btf_hash_of_maps() { + core::hint::black_box(()); +} + +#[unsafe(no_mangle)] +#[inline(never)] +pub const extern "C" fn trigger_btf_array_of_maps_get_value() { + core::hint::black_box(()); +} + +#[unsafe(no_mangle)] +#[inline(never)] +pub const extern "C" fn trigger_btf_hash_of_maps_get_value() { + core::hint::black_box(()); +} + +#[uprobe] +pub(crate) fn test_btf_array_of_maps(_ctx: ProbeContext) -> u32 { + if let Some(ptr) = RESULTS.get_ptr_mut(0) { + if let Some(inner) = ARRAY_OF_MAPS.get(0) { + if let Some(val) = inner.get(0) { + unsafe { + (*ptr).value = *val; + } + } + } + unsafe { + (*ptr).ran = 1; + } + } + 0 +} + +#[uprobe] +pub(crate) fn test_btf_hash_of_maps(_ctx: ProbeContext) -> u32 { + if let Some(ptr) = RESULTS.get_ptr_mut(1) { + if let Some(inner) = unsafe { HASH_OF_MAPS.get(&0u32) } { + if let Some(val) = inner.get(0) { + unsafe { + (*ptr).value = *val; + } + } + } + unsafe { + (*ptr).ran = 1; + } + } + 0 +} + +#[uprobe] +pub(crate) fn test_btf_array_of_maps_get_value(_ctx: ProbeContext) -> u32 { + if let Some(ptr) = RESULTS.get_ptr_mut(2) { + if let Some(val) = ARRAY_OF_MAPS.get_value(0, &0u32) { + unsafe { + (*ptr).value = *val; + } + } + unsafe { + (*ptr).ran = 1; + } + } + + if let Some(ptr) = ARRAY_OF_MAPS.get_value_ptr_mut(1, &0u32) { + unsafe { + *ptr = 99; + } + } + + 0 +} + +#[uprobe] +pub(crate) fn test_btf_hash_of_maps_get_value(_ctx: ProbeContext) -> u32 { + if let Some(ptr) = RESULTS.get_ptr_mut(3) { + if let Some(val) = unsafe { HASH_OF_MAPS.get_value(&0u32, &0u32) } { + unsafe { + (*ptr).value = *val; + } + } + unsafe { + (*ptr).ran = 1; + } + } + + if let Some(ptr) = unsafe { HASH_OF_MAPS.get_value_ptr_mut(&1u32, &0u32) } { + unsafe { + *ptr = 88; + } + } + + 0 +} diff --git a/test/integration-test/src/lib.rs b/test/integration-test/src/lib.rs index 2c6e495ef..d742861ef 100644 --- a/test/integration-test/src/lib.rs +++ b/test/integration-test/src/lib.rs @@ -76,6 +76,7 @@ bpf_file!( PROG_ARRAY => "prog_array", STACK_TRACE => "stack_trace", STACK_TRACE_LSM => "stack_trace_lsm", + BTF_MAP_OF_MAPS => "btf_map_of_maps", ); #[cfg(test)] diff --git a/test/integration-test/src/tests.rs b/test/integration-test/src/tests.rs index 809d6261a..622a6d01e 100644 --- a/test/integration-test/src/tests.rs +++ b/test/integration-test/src/tests.rs @@ -11,6 +11,7 @@ mod array; mod bloom_filter; mod bpf_probe_read; +mod btf_map_of_maps; mod btf_maps; mod btf_relocations; mod elf; diff --git a/test/integration-test/src/tests/btf_map_of_maps.rs b/test/integration-test/src/tests/btf_map_of_maps.rs new file mode 100644 index 000000000..db3fbaa23 --- /dev/null +++ b/test/integration-test/src/tests/btf_map_of_maps.rs @@ -0,0 +1,165 @@ +use aya::{ + Ebpf, + maps::{Array, ArrayOfMaps, HashOfMaps, MapData}, + programs::{UProbe, uprobe::UProbeScope}, +}; +use integration_common::btf_map_of_maps::INNER_MAX_ENTRIES; +use test_case::test_case; + +#[derive(Clone, Copy, Debug)] +enum MapKind { + Array, + Hash, +} + +impl MapKind { + fn insert_inner(self, ebpf: &mut Ebpf, key: u32, inner: &Array) { + match self { + Self::Array => { + let mut outer: ArrayOfMaps<&mut MapData, Array> = + ebpf.map_mut("ARRAY_OF_MAPS").unwrap().try_into().unwrap(); + outer.set(key, inner, 0).unwrap(); + } + Self::Hash => { + let mut outer: HashOfMaps<&mut MapData, u32, Array> = + ebpf.map_mut("HASH_OF_MAPS").unwrap().try_into().unwrap(); + outer.insert(key, inner, 0).unwrap(); + } + } + } + + fn trigger_basic(self) { + match self { + Self::Array => trigger_btf_array_of_maps(), + Self::Hash => trigger_btf_hash_of_maps(), + } + } + + fn trigger_get_value(self) { + match self { + Self::Array => trigger_btf_array_of_maps_get_value(), + Self::Hash => trigger_btf_hash_of_maps_get_value(), + } + } +} + +/// Loads and attaches a uprobe from the BTF map-of-maps test binary. +/// +/// The program name is `test_{name}` and the trigger symbol is `trigger_{name}`. +fn load_and_attach(ebpf: &mut Ebpf, name: &str) { + let prog: &mut UProbe = ebpf + .program_mut(&format!("test_{name}")) + .unwrap() + .try_into() + .unwrap(); + prog.load().unwrap(); + prog.attach( + format!("trigger_{name}").as_str(), + "/proc/self/exe", + UProbeScope::AllProcesses, + ) + .unwrap(); +} + +/// Reads `RESULTS[index]` and asserts `value` and `ran == 1`. +fn assert_result(ebpf: &Ebpf, index: u32, expected_value: u32) { + let results: Array<&MapData, integration_common::btf_map_of_maps::TestResult> = + ebpf.map("RESULTS").unwrap().try_into().unwrap(); + let result = results.get(&index, 0).unwrap(); + assert_eq!(result.value, expected_value); + assert_eq!(result.ran, 1); +} + +#[unsafe(no_mangle)] +#[inline(never)] +extern "C" fn trigger_btf_array_of_maps() { + std::hint::black_box(()); +} + +#[unsafe(no_mangle)] +#[inline(never)] +extern "C" fn trigger_btf_hash_of_maps() { + std::hint::black_box(()); +} + +#[unsafe(no_mangle)] +#[inline(never)] +extern "C" fn trigger_btf_array_of_maps_get_value() { + std::hint::black_box(()); +} + +#[unsafe(no_mangle)] +#[inline(never)] +extern "C" fn trigger_btf_hash_of_maps_get_value() { + std::hint::black_box(()); +} + +#[test_case(MapKind::Array, "btf_array_of_maps", 0, 42 ; "array_of_maps")] +#[test_case(MapKind::Hash, "btf_hash_of_maps", 1, 55 ; "hash_of_maps")] +#[test_log::test] +fn btf_map_of_maps(kind: MapKind, name: &str, result_index: u32, expected: u32) { + let mut ebpf = Ebpf::load(crate::BTF_MAP_OF_MAPS).unwrap(); + + let mut inner: Array = Array::create(INNER_MAX_ENTRIES, 0).unwrap(); + inner.set(0, expected, 0).unwrap(); + + kind.insert_inner(&mut ebpf, 0, &inner); + + load_and_attach(&mut ebpf, name); + kind.trigger_basic(); + assert_result(&ebpf, result_index, expected); +} + +#[test_case(MapKind::Array, "btf_array_of_maps_get_value", 2, 77, 99 ; "array_of_maps")] +#[test_case(MapKind::Hash, "btf_hash_of_maps_get_value", 3, 55, 88 ; "hash_of_maps")] +#[test_log::test] +fn btf_map_of_maps_get_value( + kind: MapKind, + name: &str, + result_index: u32, + expected_get: u32, + expected_mut_write: u32, +) { + let mut ebpf = Ebpf::load(crate::BTF_MAP_OF_MAPS).unwrap(); + + let mut inner_1: Array = Array::create(INNER_MAX_ENTRIES, 0).unwrap(); + inner_1.set(0, expected_get, 0).unwrap(); + + let mut inner_2: Array = Array::create(INNER_MAX_ENTRIES, 0).unwrap(); + inner_2.set(0, 0u32, 0).unwrap(); + + kind.insert_inner(&mut ebpf, 0, &inner_1); + kind.insert_inner(&mut ebpf, 1, &inner_2); + + load_and_attach(&mut ebpf, name); + kind.trigger_get_value(); + + assert_result(&ebpf, result_index, expected_get); + assert_eq!(inner_2.get(&0, 0).unwrap(), expected_mut_write); +} + +/// Inserting an inner map into a `HashOfMaps` does not consume the userspace handle: +/// the original `Array` remains readable and writable after the insert. +#[test_log::test] +fn btf_hash_of_maps_dynamic() { + let mut ebpf = Ebpf::load(crate::BTF_MAP_OF_MAPS).unwrap(); + + let mut inner_1: Array = Array::create(INNER_MAX_ENTRIES, 0).unwrap(); + let mut inner_2: Array = Array::create(INNER_MAX_ENTRIES, 0).unwrap(); + + inner_1.set(0, 1000u32, 0).unwrap(); + inner_2.set(0, 2000u32, 0).unwrap(); + + { + let mut outer: HashOfMaps<&mut MapData, u32, Array> = + ebpf.map_mut("HASH_OF_MAPS").unwrap().try_into().unwrap(); + outer.insert(10u32, &inner_1, 0).unwrap(); + outer.insert(11u32, &inner_2, 0).unwrap(); + } + + assert_eq!(inner_1.get(&0, 0).unwrap(), 1000); + assert_eq!(inner_2.get(&0, 0).unwrap(), 2000); + + inner_1.set(1, 3000u32, 0).unwrap(); + assert_eq!(inner_1.get(&1, 0).unwrap(), 3000); +} diff --git a/xtask/public-api/aya-ebpf.txt b/xtask/public-api/aya-ebpf.txt index 3bf7248b6..3da2cf0ba 100644 --- a/xtask/public-api/aya-ebpf.txt +++ b/xtask/public-api/aya-ebpf.txt @@ -102,6 +102,41 @@ impl core::marker::Unpin for impl core::marker::UnsafeUnpin for aya_ebpf::btf_maps::lpm_trie::LpmTrie impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::btf_maps::lpm_trie::LpmTrie where V: core::panic::unwind_safe::RefUnwindSafe, K: core::panic::unwind_safe::RefUnwindSafe impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::btf_maps::lpm_trie::LpmTrie where V: core::panic::unwind_safe::RefUnwindSafe, K: core::panic::unwind_safe::RefUnwindSafe +pub mod aya_ebpf::btf_maps::of_maps +#[repr(C)] pub struct aya_ebpf::btf_maps::of_maps::ArrayOfMaps +impl aya_ebpf::btf_maps::ArrayOfMaps +pub fn aya_ebpf::btf_maps::ArrayOfMaps::get(&self, index: u32) -> core::option::Option<&V> +impl aya_ebpf::btf_maps::ArrayOfMaps +pub const fn aya_ebpf::btf_maps::ArrayOfMaps::new() -> Self +impl aya_ebpf::btf_maps::ArrayOfMaps +pub fn aya_ebpf::btf_maps::ArrayOfMaps::get_value(&self, outer_index: u32, inner_key: &::Key) -> core::option::Option<&::Value> +pub fn aya_ebpf::btf_maps::ArrayOfMaps::get_value_ptr_mut(&self, outer_index: u32, inner_key: &::Key) -> core::option::Option<*mut ::Value> +impl core::default::Default for aya_ebpf::btf_maps::ArrayOfMaps +pub fn aya_ebpf::btf_maps::ArrayOfMaps::default() -> Self +impl core::marker::Sync for aya_ebpf::btf_maps::ArrayOfMaps +impl core::marker::Freeze for aya_ebpf::btf_maps::ArrayOfMaps +impl !core::marker::Send for aya_ebpf::btf_maps::ArrayOfMaps +impl core::marker::Unpin for aya_ebpf::btf_maps::ArrayOfMaps +impl core::marker::UnsafeUnpin for aya_ebpf::btf_maps::ArrayOfMaps +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::btf_maps::ArrayOfMaps where V: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::btf_maps::ArrayOfMaps where V: core::panic::unwind_safe::RefUnwindSafe +#[repr(C)] pub struct aya_ebpf::btf_maps::of_maps::HashOfMaps +impl aya_ebpf::btf_maps::HashOfMaps +pub unsafe fn aya_ebpf::btf_maps::HashOfMaps::get(&self, key: &K) -> core::option::Option<&V> +impl aya_ebpf::btf_maps::HashOfMaps +pub const fn aya_ebpf::btf_maps::HashOfMaps::new() -> Self +impl aya_ebpf::btf_maps::HashOfMaps +pub unsafe fn aya_ebpf::btf_maps::HashOfMaps::get_value(&self, outer_key: &K, inner_key: &::Key) -> core::option::Option<&::Value> +pub unsafe fn aya_ebpf::btf_maps::HashOfMaps::get_value_ptr_mut(&self, outer_key: &K, inner_key: &::Key) -> core::option::Option<*mut ::Value> +impl core::default::Default for aya_ebpf::btf_maps::HashOfMaps +pub fn aya_ebpf::btf_maps::HashOfMaps::default() -> Self +impl core::marker::Sync for aya_ebpf::btf_maps::HashOfMaps +impl core::marker::Freeze for aya_ebpf::btf_maps::HashOfMaps +impl !core::marker::Send for aya_ebpf::btf_maps::HashOfMaps +impl core::marker::Unpin for aya_ebpf::btf_maps::HashOfMaps +impl core::marker::UnsafeUnpin for aya_ebpf::btf_maps::HashOfMaps +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::btf_maps::HashOfMaps where K: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::btf_maps::HashOfMaps where K: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe pub mod aya_ebpf::btf_maps::per_cpu_array #[repr(C)] pub struct aya_ebpf::btf_maps::per_cpu_array::PerCpuArray impl aya_ebpf::btf_maps::per_cpu_array::PerCpuArray @@ -319,6 +354,23 @@ impl core::marker::Unpin for ay impl core::marker::UnsafeUnpin for aya_ebpf::btf_maps::array::Array impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::btf_maps::array::Array where T: core::panic::unwind_safe::RefUnwindSafe impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::btf_maps::array::Array where T: core::panic::unwind_safe::RefUnwindSafe +#[repr(C)] pub struct aya_ebpf::btf_maps::ArrayOfMaps +impl aya_ebpf::btf_maps::ArrayOfMaps +pub fn aya_ebpf::btf_maps::ArrayOfMaps::get(&self, index: u32) -> core::option::Option<&V> +impl aya_ebpf::btf_maps::ArrayOfMaps +pub const fn aya_ebpf::btf_maps::ArrayOfMaps::new() -> Self +impl aya_ebpf::btf_maps::ArrayOfMaps +pub fn aya_ebpf::btf_maps::ArrayOfMaps::get_value(&self, outer_index: u32, inner_key: &::Key) -> core::option::Option<&::Value> +pub fn aya_ebpf::btf_maps::ArrayOfMaps::get_value_ptr_mut(&self, outer_index: u32, inner_key: &::Key) -> core::option::Option<*mut ::Value> +impl core::default::Default for aya_ebpf::btf_maps::ArrayOfMaps +pub fn aya_ebpf::btf_maps::ArrayOfMaps::default() -> Self +impl core::marker::Sync for aya_ebpf::btf_maps::ArrayOfMaps +impl core::marker::Freeze for aya_ebpf::btf_maps::ArrayOfMaps +impl !core::marker::Send for aya_ebpf::btf_maps::ArrayOfMaps +impl core::marker::Unpin for aya_ebpf::btf_maps::ArrayOfMaps +impl core::marker::UnsafeUnpin for aya_ebpf::btf_maps::ArrayOfMaps +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::btf_maps::ArrayOfMaps where V: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::btf_maps::ArrayOfMaps where V: core::panic::unwind_safe::RefUnwindSafe #[repr(C)] pub struct aya_ebpf::btf_maps::BloomFilter impl aya_ebpf::btf_maps::bloom_filter::BloomFilter pub fn aya_ebpf::btf_maps::bloom_filter::BloomFilter::contains(&self, value: impl core::borrow::Borrow) -> core::result::Result<(), i32> @@ -366,6 +418,23 @@ impl core::marker::Unpin for aya_e impl core::marker::UnsafeUnpin for aya_ebpf::btf_maps::dev_map_hash::DevMapHash impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::btf_maps::dev_map_hash::DevMapHash impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::btf_maps::dev_map_hash::DevMapHash +#[repr(C)] pub struct aya_ebpf::btf_maps::HashOfMaps +impl aya_ebpf::btf_maps::HashOfMaps +pub unsafe fn aya_ebpf::btf_maps::HashOfMaps::get(&self, key: &K) -> core::option::Option<&V> +impl aya_ebpf::btf_maps::HashOfMaps +pub const fn aya_ebpf::btf_maps::HashOfMaps::new() -> Self +impl aya_ebpf::btf_maps::HashOfMaps +pub unsafe fn aya_ebpf::btf_maps::HashOfMaps::get_value(&self, outer_key: &K, inner_key: &::Key) -> core::option::Option<&::Value> +pub unsafe fn aya_ebpf::btf_maps::HashOfMaps::get_value_ptr_mut(&self, outer_key: &K, inner_key: &::Key) -> core::option::Option<*mut ::Value> +impl core::default::Default for aya_ebpf::btf_maps::HashOfMaps +pub fn aya_ebpf::btf_maps::HashOfMaps::default() -> Self +impl core::marker::Sync for aya_ebpf::btf_maps::HashOfMaps +impl core::marker::Freeze for aya_ebpf::btf_maps::HashOfMaps +impl !core::marker::Send for aya_ebpf::btf_maps::HashOfMaps +impl core::marker::Unpin for aya_ebpf::btf_maps::HashOfMaps +impl core::marker::UnsafeUnpin for aya_ebpf::btf_maps::HashOfMaps +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::btf_maps::HashOfMaps where K: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::btf_maps::HashOfMaps where K: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe #[repr(C)] pub struct aya_ebpf::btf_maps::LpmTrie impl aya_ebpf::btf_maps::lpm_trie::LpmTrie pub fn aya_ebpf::btf_maps::lpm_trie::LpmTrie::get(&self, key: &aya_ebpf::maps::lpm_trie::Key) -> core::option::Option<&V> @@ -555,6 +624,8 @@ impl core::mar impl core::marker::UnsafeUnpin for aya_ebpf::btf_maps::stack_trace::StackTrace impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::btf_maps::stack_trace::StackTrace impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::btf_maps::stack_trace::StackTrace +pub trait aya_ebpf::btf_maps::MapDef: aya_ebpf::btf_maps::private::MapDef +impl aya_ebpf::btf_maps::MapDef for T pub type aya_ebpf::btf_maps::ReusePortSockArray = aya_ebpf::btf_maps::reuseport_sock_array::ReusePortSockArrayImpl pub mod aya_ebpf::helpers pub use aya_ebpf::helpers::generated @@ -1265,6 +1336,8 @@ impl core::marker::Unpin for aya_ebpf::maps::XskMap impl core::marker::UnsafeUnpin for aya_ebpf::maps::XskMap impl !core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::maps::XskMap impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::maps::XskMap +pub trait aya_ebpf::maps::Map: aya_ebpf::maps::private::Map +impl aya_ebpf::maps::Map for T pub mod aya_ebpf::programs pub mod aya_ebpf::programs::device pub struct aya_ebpf::programs::device::DeviceContext diff --git a/xtask/public-api/aya-obj.txt b/xtask/public-api/aya-obj.txt index 92988819d..fcb5dfa76 100644 --- a/xtask/public-api/aya-obj.txt +++ b/xtask/public-api/aya-obj.txt @@ -4,6 +4,8 @@ pub enum aya_obj::btf::BtfError pub aya_obj::btf::BtfError::FileError pub aya_obj::btf::BtfError::FileError::error: std::io::error::Error pub aya_obj::btf::BtfError::FileError::path: std::path::PathBuf +pub aya_obj::btf::BtfError::InnerMapCannotBePinned +pub aya_obj::btf::BtfError::InnerMapCannotBePinned::name: alloc::string::String pub aya_obj::btf::BtfError::InvalidDatasec pub aya_obj::btf::BtfError::InvalidHeader pub aya_obj::btf::BtfError::InvalidInfo @@ -23,11 +25,15 @@ pub aya_obj::btf::BtfError::InvalidSymbolName pub aya_obj::btf::BtfError::InvalidTypeInfo pub aya_obj::btf::BtfError::InvalidTypeKind pub aya_obj::btf::BtfError::InvalidTypeKind::kind: u32 +pub aya_obj::btf::BtfError::InvalidValuesSpec +pub aya_obj::btf::BtfError::InvalidValuesSpec::name: alloc::string::String pub aya_obj::btf::BtfError::LoadError pub aya_obj::btf::BtfError::LoadError::io_error: std::io::error::Error pub aya_obj::btf::BtfError::LoadError::verifier_log: aya_obj::VerifierLog pub aya_obj::btf::BtfError::MaximumTypeDepthReached pub aya_obj::btf::BtfError::MaximumTypeDepthReached::type_id: u32 +pub aya_obj::btf::BtfError::MultiLevelMapInMapNotSupported +pub aya_obj::btf::BtfError::MultiLevelMapInMapNotSupported::name: alloc::string::String pub aya_obj::btf::BtfError::SymbolOffsetNotFound pub aya_obj::btf::BtfError::SymbolOffsetNotFound::symbol_name: alloc::string::String pub aya_obj::btf::BtfError::UnexpectedBtfType @@ -4475,11 +4481,13 @@ pub aya_obj::maps::Map::Legacy(aya_obj::maps::LegacyMap) impl aya_obj::maps::Map pub fn aya_obj::maps::Map::data(&self) -> &[u8] pub fn aya_obj::maps::Map::data_mut(&mut self) -> &mut alloc::vec::Vec +pub fn aya_obj::maps::Map::inner(&self) -> core::option::Option pub const fn aya_obj::maps::Map::key_size(&self) -> u32 pub const fn aya_obj::maps::Map::map_extra(&self) -> u64 pub const fn aya_obj::maps::Map::map_flags(&self) -> u32 pub const fn aya_obj::maps::Map::map_type(&self) -> u32 pub const fn aya_obj::maps::Map::max_entries(&self) -> u32 +pub const fn aya_obj::maps::Map::new_from_params(map_type: u32, key_size: u32, value_size: u32, max_entries: u32, flags: u32) -> Self pub const fn aya_obj::maps::Map::pinning(&self) -> aya_obj::maps::PinningType pub const fn aya_obj::maps::Map::section_index(&self) -> usize pub const fn aya_obj::maps::Map::section_kind(&self) -> aya_obj::EbpfSectionKind @@ -4574,6 +4582,7 @@ impl core::panic::unwind_safe::UnwindSafe for aya_obj::maps::BtfMapDef pub struct aya_obj::maps::LegacyMap pub aya_obj::maps::LegacyMap::data: alloc::vec::Vec pub aya_obj::maps::LegacyMap::def: aya_obj::maps::bpf_map_def +pub aya_obj::maps::LegacyMap::inner_def: core::option::Option pub aya_obj::maps::LegacyMap::section_index: usize pub aya_obj::maps::LegacyMap::section_kind: aya_obj::EbpfSectionKind pub aya_obj::maps::LegacyMap::symbol_index: core::option::Option @@ -5216,11 +5225,13 @@ pub aya_obj::Map::Legacy(aya_obj::maps::LegacyMap) impl aya_obj::maps::Map pub fn aya_obj::maps::Map::data(&self) -> &[u8] pub fn aya_obj::maps::Map::data_mut(&mut self) -> &mut alloc::vec::Vec +pub fn aya_obj::maps::Map::inner(&self) -> core::option::Option pub const fn aya_obj::maps::Map::key_size(&self) -> u32 pub const fn aya_obj::maps::Map::map_extra(&self) -> u64 pub const fn aya_obj::maps::Map::map_flags(&self) -> u32 pub const fn aya_obj::maps::Map::map_type(&self) -> u32 pub const fn aya_obj::maps::Map::max_entries(&self) -> u32 +pub const fn aya_obj::maps::Map::new_from_params(map_type: u32, key_size: u32, value_size: u32, max_entries: u32, flags: u32) -> Self pub const fn aya_obj::maps::Map::pinning(&self) -> aya_obj::maps::PinningType pub const fn aya_obj::maps::Map::section_index(&self) -> usize pub const fn aya_obj::maps::Map::section_kind(&self) -> aya_obj::EbpfSectionKind diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index 2a3b2e8ae..6f5347d5a 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -15,6 +15,8 @@ impl, V: aya::Pod> aya::maps::array: pub fn aya::maps::array::Array::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> impl, V: aya::Pod> aya::maps::array::Array pub fn aya::maps::array::Array::set(&mut self, index: u32, value: impl core::borrow::Borrow, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::array::Array +pub fn aya::maps::array::Array::create(max_entries: u32, flags: u32) -> core::result::Result impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::array::Array<&'a aya::maps::MapData, V> pub type aya::maps::array::Array<&'a aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::array::Array<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -43,6 +45,8 @@ impl, V: aya::Pod> aya::maps::PerCpu pub fn aya::maps::PerCpuArray::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> impl, V: aya::Pod> aya::maps::PerCpuArray pub fn aya::maps::PerCpuArray::set(&mut self, index: u32, values: aya::maps::PerCpuValues, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::PerCpuArray +pub fn aya::maps::PerCpuArray::create(max_entries: u32, flags: u32) -> core::result::Result impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::PerCpuArray<&'a aya::maps::MapData, V> pub type aya::maps::PerCpuArray<&'a aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::PerCpuArray<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -94,6 +98,8 @@ impl, V: aya::Pod> aya::maps::bloom_ pub fn aya::maps::bloom_filter::BloomFilter::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> impl, V: aya::Pod> aya::maps::bloom_filter::BloomFilter pub fn aya::maps::bloom_filter::BloomFilter::insert(&mut self, value: impl core::borrow::Borrow, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::bloom_filter::BloomFilter +pub fn aya::maps::bloom_filter::BloomFilter::create(max_entries: u32, flags: u32) -> core::result::Result impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V> pub type aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -114,6 +120,8 @@ impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::bloom_filter:: impl core::panic::unwind_safe::UnwindSafe for aya::maps::bloom_filter::BloomFilter where T: core::panic::unwind_safe::UnwindSafe, V: core::panic::unwind_safe::UnwindSafe pub mod aya::maps::hash_map pub struct aya::maps::hash_map::HashMap +impl aya::maps::hash_map::HashMap +pub fn aya::maps::hash_map::HashMap::create(max_entries: u32, flags: u32) -> core::result::Result impl, K: aya::Pod, V: aya::Pod> aya::maps::hash_map::HashMap pub fn aya::maps::hash_map::HashMap::get(&self, key: &K, flags: u64) -> core::result::Result pub fn aya::maps::hash_map::HashMap::iter(&self) -> aya::maps::MapIter<'_, K, V, Self> @@ -149,6 +157,8 @@ impl core::marker::UnsafeUnpin for aya::maps::hash_map::HashMap core::panic::unwind_safe::RefUnwindSafe for aya::maps::hash_map::HashMap where T: core::panic::unwind_safe::RefUnwindSafe, K: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe impl core::panic::unwind_safe::UnwindSafe for aya::maps::hash_map::HashMap where T: core::panic::unwind_safe::UnwindSafe, K: core::panic::unwind_safe::UnwindSafe, V: core::panic::unwind_safe::UnwindSafe pub struct aya::maps::hash_map::PerCpuHashMap +impl aya::maps::hash_map::PerCpuHashMap +pub fn aya::maps::hash_map::PerCpuHashMap::create(max_entries: u32, flags: u32) -> core::result::Result impl, K: aya::Pod, V: aya::Pod> aya::maps::hash_map::PerCpuHashMap pub fn aya::maps::hash_map::PerCpuHashMap::get(&self, key: &K, flags: u64) -> core::result::Result, aya::maps::MapError> pub fn aya::maps::hash_map::PerCpuHashMap::iter(&self) -> aya::maps::MapIter<'_, K, aya::maps::PerCpuValues, Self> @@ -202,6 +212,8 @@ impl core::marker::UnsafeUnpin for aya::maps::lpm_trie::Key where K: core: impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::lpm_trie::Key where K: core::panic::unwind_safe::RefUnwindSafe impl core::panic::unwind_safe::UnwindSafe for aya::maps::lpm_trie::Key where K: core::panic::unwind_safe::UnwindSafe pub struct aya::maps::lpm_trie::LpmTrie +impl aya::maps::lpm_trie::LpmTrie +pub fn aya::maps::lpm_trie::LpmTrie::create(max_entries: u32, flags: u32) -> core::result::Result impl, K: aya::Pod, V: aya::Pod> aya::maps::lpm_trie::LpmTrie pub fn aya::maps::lpm_trie::LpmTrie::get(&self, key: &aya::maps::lpm_trie::Key, flags: u64) -> core::result::Result pub fn aya::maps::lpm_trie::LpmTrie::iter(&self) -> aya::maps::MapIter<'_, aya::maps::lpm_trie::Key, V, Self> @@ -236,6 +248,64 @@ impl core::marker::Unpin for aya::maps::lpm_trie::LpmTrie wher impl core::marker::UnsafeUnpin for aya::maps::lpm_trie::LpmTrie where T: core::marker::UnsafeUnpin impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::lpm_trie::LpmTrie where T: core::panic::unwind_safe::RefUnwindSafe, K: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe impl core::panic::unwind_safe::UnwindSafe for aya::maps::lpm_trie::LpmTrie where T: core::panic::unwind_safe::UnwindSafe, K: core::panic::unwind_safe::UnwindSafe, V: core::panic::unwind_safe::UnwindSafe +pub mod aya::maps::of_maps +pub struct aya::maps::of_maps::ArrayOfMaps +impl, V: aya::maps::FromMapData> aya::maps::ArrayOfMaps +pub fn aya::maps::ArrayOfMaps::get(&self, index: &u32, flags: u64) -> core::result::Result +impl, V> aya::maps::ArrayOfMaps +pub fn aya::maps::ArrayOfMaps::is_empty(&self) -> bool +pub fn aya::maps::ArrayOfMaps::len(&self) -> u32 +impl, V: aya::maps::InnerMap> aya::maps::ArrayOfMaps +pub fn aya::maps::ArrayOfMaps::set(&mut self, index: u32, value: &V, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::ArrayOfMaps +pub fn aya::maps::ArrayOfMaps::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> +impl<'a, V: aya::maps::InnerMap> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::ArrayOfMaps<&'a aya::maps::MapData, V> +pub type aya::maps::ArrayOfMaps<&'a aya::maps::MapData, V>::Error = aya::maps::MapError +pub fn aya::maps::ArrayOfMaps<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a, V: aya::maps::InnerMap> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::ArrayOfMaps<&'a mut aya::maps::MapData, V> +pub type aya::maps::ArrayOfMaps<&'a mut aya::maps::MapData, V>::Error = aya::maps::MapError +pub fn aya::maps::ArrayOfMaps<&'a mut aya::maps::MapData, V>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::fmt::Debug for aya::maps::ArrayOfMaps +pub fn aya::maps::ArrayOfMaps::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::convert::TryFrom for aya::maps::ArrayOfMaps +pub type aya::maps::ArrayOfMaps::Error = aya::maps::MapError +pub fn aya::maps::ArrayOfMaps::try_from(map: aya::maps::Map) -> core::result::Result +impl core::marker::Freeze for aya::maps::ArrayOfMaps where T: core::marker::Freeze +impl core::marker::Send for aya::maps::ArrayOfMaps where T: core::marker::Send, V: core::marker::Send +impl core::marker::Sync for aya::maps::ArrayOfMaps where T: core::marker::Sync, V: core::marker::Sync +impl core::marker::Unpin for aya::maps::ArrayOfMaps where T: core::marker::Unpin, V: core::marker::Unpin +impl core::marker::UnsafeUnpin for aya::maps::ArrayOfMaps where T: core::marker::UnsafeUnpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::ArrayOfMaps where T: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::ArrayOfMaps where T: core::panic::unwind_safe::UnwindSafe, V: core::panic::unwind_safe::UnwindSafe +pub struct aya::maps::of_maps::HashOfMaps +impl aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> +impl, K: aya::Pod, V: aya::maps::FromMapData> aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::get(&self, key: &K, flags: u64) -> core::result::Result +impl, K: aya::Pod, V> aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::keys(&self) -> aya::maps::MapKeys<'_, K> +impl, K: aya::Pod, V: aya::maps::InnerMap> aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::insert(&mut self, key: impl core::borrow::Borrow, value: &V, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl, K: aya::Pod, V> aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::remove(&mut self, key: &K) -> core::result::Result<(), aya::maps::MapError> +impl<'a, K: aya::Pod, V: aya::maps::InnerMap> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::HashOfMaps<&'a aya::maps::MapData, K, V> +pub type aya::maps::HashOfMaps<&'a aya::maps::MapData, K, V>::Error = aya::maps::MapError +pub fn aya::maps::HashOfMaps<&'a aya::maps::MapData, K, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a, K: aya::Pod, V: aya::maps::InnerMap> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::HashOfMaps<&'a mut aya::maps::MapData, K, V> +pub type aya::maps::HashOfMaps<&'a mut aya::maps::MapData, K, V>::Error = aya::maps::MapError +pub fn aya::maps::HashOfMaps<&'a mut aya::maps::MapData, K, V>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::HashOfMaps +pub type aya::maps::HashOfMaps::Error = aya::maps::MapError +pub fn aya::maps::HashOfMaps::try_from(map: aya::maps::Map) -> core::result::Result +impl core::fmt::Debug for aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya::maps::HashOfMaps where T: core::marker::Freeze +impl core::marker::Send for aya::maps::HashOfMaps where T: core::marker::Send, K: core::marker::Send, V: core::marker::Send +impl core::marker::Sync for aya::maps::HashOfMaps where T: core::marker::Sync, K: core::marker::Sync, V: core::marker::Sync +impl core::marker::Unpin for aya::maps::HashOfMaps where T: core::marker::Unpin, K: core::marker::Unpin, V: core::marker::Unpin +impl core::marker::UnsafeUnpin for aya::maps::HashOfMaps where T: core::marker::UnsafeUnpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::HashOfMaps where T: core::panic::unwind_safe::RefUnwindSafe, K: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::HashOfMaps where T: core::panic::unwind_safe::UnwindSafe, K: core::panic::unwind_safe::UnwindSafe, V: core::panic::unwind_safe::UnwindSafe pub mod aya::maps::perf pub enum aya::maps::perf::PerfBufferError pub aya::maps::perf::PerfBufferError::IOError(std::io::error::Error) @@ -324,6 +394,8 @@ pub fn aya::maps::queue::Queue::pin, V: aya::Pod> aya::maps::queue::Queue pub fn aya::maps::queue::Queue::pop(&mut self, flags: u64) -> core::result::Result pub fn aya::maps::queue::Queue::push(&mut self, value: impl core::borrow::Borrow, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::queue::Queue +pub fn aya::maps::queue::Queue::create(max_entries: u32, flags: u32) -> core::result::Result impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::queue::Queue<&'a aya::maps::MapData, V> pub type aya::maps::queue::Queue<&'a aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::queue::Queue<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -511,6 +583,8 @@ pub fn aya::maps::stack::Stack::pin, V: aya::Pod> aya::maps::stack::Stack pub fn aya::maps::stack::Stack::pop(&mut self, flags: u64) -> core::result::Result pub fn aya::maps::stack::Stack::push(&mut self, value: impl core::borrow::Borrow, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::stack::Stack +pub fn aya::maps::stack::Stack::create(max_entries: u32, flags: u32) -> core::result::Result impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::stack::Stack<&'a aya::maps::MapData, V> pub type aya::maps::stack::Stack<&'a aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::stack::Stack<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -710,11 +784,13 @@ impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::XskMap where T impl core::panic::unwind_safe::UnwindSafe for aya::maps::XskMap where T: core::panic::unwind_safe::UnwindSafe pub enum aya::maps::Map pub aya::maps::Map::Array(aya::maps::MapData) +pub aya::maps::Map::ArrayOfMaps(aya::maps::MapData) pub aya::maps::Map::BloomFilter(aya::maps::MapData) pub aya::maps::Map::CpuMap(aya::maps::MapData) pub aya::maps::Map::DevMap(aya::maps::MapData) pub aya::maps::Map::DevMapHash(aya::maps::MapData) pub aya::maps::Map::HashMap(aya::maps::MapData) +pub aya::maps::Map::HashOfMaps(aya::maps::MapData) pub aya::maps::Map::LpmTrie(aya::maps::MapData) pub aya::maps::Map::LruHashMap(aya::maps::MapData) pub aya::maps::Map::PerCpuArray(aya::maps::MapData) @@ -785,6 +861,12 @@ pub fn aya::maps::hash_map::PerCpuHashMap<&'a mut aya::maps::MapData, K, V>::try impl<'a, K: aya::Pod, V: aya::Pod> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::lpm_trie::LpmTrie<&'a mut aya::maps::MapData, K, V> pub type aya::maps::lpm_trie::LpmTrie<&'a mut aya::maps::MapData, K, V>::Error = aya::maps::MapError pub fn aya::maps::lpm_trie::LpmTrie<&'a mut aya::maps::MapData, K, V>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl<'a, K: aya::Pod, V: aya::maps::InnerMap> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::HashOfMaps<&'a aya::maps::MapData, K, V> +pub type aya::maps::HashOfMaps<&'a aya::maps::MapData, K, V>::Error = aya::maps::MapError +pub fn aya::maps::HashOfMaps<&'a aya::maps::MapData, K, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a, K: aya::Pod, V: aya::maps::InnerMap> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::HashOfMaps<&'a mut aya::maps::MapData, K, V> +pub type aya::maps::HashOfMaps<&'a mut aya::maps::MapData, K, V>::Error = aya::maps::MapError +pub fn aya::maps::HashOfMaps<&'a mut aya::maps::MapData, K, V>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::PerCpuArray<&'a aya::maps::MapData, V> pub type aya::maps::PerCpuArray<&'a aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::PerCpuArray<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -827,6 +909,12 @@ pub fn aya::maps::sk_storage::SkStorage<&'a mut aya::maps::MapData, V>::try_from impl<'a, V: aya::Pod> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::stack::Stack<&'a mut aya::maps::MapData, V> pub type aya::maps::stack::Stack<&'a mut aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::stack::Stack<&'a mut aya::maps::MapData, V>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl<'a, V: aya::maps::InnerMap> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::ArrayOfMaps<&'a aya::maps::MapData, V> +pub type aya::maps::ArrayOfMaps<&'a aya::maps::MapData, V>::Error = aya::maps::MapError +pub fn aya::maps::ArrayOfMaps<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a, V: aya::maps::InnerMap> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::ArrayOfMaps<&'a mut aya::maps::MapData, V> +pub type aya::maps::ArrayOfMaps<&'a mut aya::maps::MapData, V>::Error = aya::maps::MapError +pub fn aya::maps::ArrayOfMaps<&'a mut aya::maps::MapData, V>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::CpuMap<&'a aya::maps::MapData> pub type aya::maps::CpuMap<&'a aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::CpuMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -896,6 +984,9 @@ pub fn aya::maps::hash_map::PerCpuHashMap::try_from(ma impl core::convert::TryFrom for aya::maps::lpm_trie::LpmTrie pub type aya::maps::lpm_trie::LpmTrie::Error = aya::maps::MapError pub fn aya::maps::lpm_trie::LpmTrie::try_from(map: aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::HashOfMaps +pub type aya::maps::HashOfMaps::Error = aya::maps::MapError +pub fn aya::maps::HashOfMaps::try_from(map: aya::maps::Map) -> core::result::Result impl core::convert::TryFrom for aya::maps::PerCpuArray pub type aya::maps::PerCpuArray::Error = aya::maps::MapError pub fn aya::maps::PerCpuArray::try_from(map: aya::maps::Map) -> core::result::Result @@ -917,6 +1008,9 @@ pub fn aya::maps::sk_storage::SkStorage::try_from(map: ay impl core::convert::TryFrom for aya::maps::stack::Stack pub type aya::maps::stack::Stack::Error = aya::maps::MapError pub fn aya::maps::stack::Stack::try_from(map: aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::ArrayOfMaps +pub type aya::maps::ArrayOfMaps::Error = aya::maps::MapError +pub fn aya::maps::ArrayOfMaps::try_from(map: aya::maps::Map) -> core::result::Result impl core::marker::Freeze for aya::maps::Map impl core::marker::Send for aya::maps::Map impl core::marker::Sync for aya::maps::Map @@ -944,6 +1038,8 @@ pub aya::maps::MapError::InvalidValueStride::size: usize pub aya::maps::MapError::InvalidValueStride::stride: usize pub aya::maps::MapError::IoError(std::io::error::Error) pub aya::maps::MapError::KeyNotFound +pub aya::maps::MapError::MissingInnerMapDefinition +pub aya::maps::MapError::MissingInnerMapDefinition::outer_name: alloc::string::String pub aya::maps::MapError::OutOfBounds pub aya::maps::MapError::OutOfBounds::index: u32 pub aya::maps::MapError::OutOfBounds::max_entries: u32 @@ -1046,6 +1142,8 @@ impl, V: aya::Pod> aya::maps::array: pub fn aya::maps::array::Array::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> impl, V: aya::Pod> aya::maps::array::Array pub fn aya::maps::array::Array::set(&mut self, index: u32, value: impl core::borrow::Borrow, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::array::Array +pub fn aya::maps::array::Array::create(max_entries: u32, flags: u32) -> core::result::Result impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::array::Array<&'a aya::maps::MapData, V> pub type aya::maps::array::Array<&'a aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::array::Array<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -1065,6 +1163,34 @@ impl core::marker::Unpin for aya::maps::array::Array where T: core:: impl core::marker::UnsafeUnpin for aya::maps::array::Array where T: core::marker::UnsafeUnpin impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::array::Array where T: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe impl core::panic::unwind_safe::UnwindSafe for aya::maps::array::Array where T: core::panic::unwind_safe::UnwindSafe, V: core::panic::unwind_safe::UnwindSafe +pub struct aya::maps::ArrayOfMaps +impl, V: aya::maps::FromMapData> aya::maps::ArrayOfMaps +pub fn aya::maps::ArrayOfMaps::get(&self, index: &u32, flags: u64) -> core::result::Result +impl, V> aya::maps::ArrayOfMaps +pub fn aya::maps::ArrayOfMaps::is_empty(&self) -> bool +pub fn aya::maps::ArrayOfMaps::len(&self) -> u32 +impl, V: aya::maps::InnerMap> aya::maps::ArrayOfMaps +pub fn aya::maps::ArrayOfMaps::set(&mut self, index: u32, value: &V, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::ArrayOfMaps +pub fn aya::maps::ArrayOfMaps::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> +impl<'a, V: aya::maps::InnerMap> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::ArrayOfMaps<&'a aya::maps::MapData, V> +pub type aya::maps::ArrayOfMaps<&'a aya::maps::MapData, V>::Error = aya::maps::MapError +pub fn aya::maps::ArrayOfMaps<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a, V: aya::maps::InnerMap> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::ArrayOfMaps<&'a mut aya::maps::MapData, V> +pub type aya::maps::ArrayOfMaps<&'a mut aya::maps::MapData, V>::Error = aya::maps::MapError +pub fn aya::maps::ArrayOfMaps<&'a mut aya::maps::MapData, V>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::fmt::Debug for aya::maps::ArrayOfMaps +pub fn aya::maps::ArrayOfMaps::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::convert::TryFrom for aya::maps::ArrayOfMaps +pub type aya::maps::ArrayOfMaps::Error = aya::maps::MapError +pub fn aya::maps::ArrayOfMaps::try_from(map: aya::maps::Map) -> core::result::Result +impl core::marker::Freeze for aya::maps::ArrayOfMaps where T: core::marker::Freeze +impl core::marker::Send for aya::maps::ArrayOfMaps where T: core::marker::Send, V: core::marker::Send +impl core::marker::Sync for aya::maps::ArrayOfMaps where T: core::marker::Sync, V: core::marker::Sync +impl core::marker::Unpin for aya::maps::ArrayOfMaps where T: core::marker::Unpin, V: core::marker::Unpin +impl core::marker::UnsafeUnpin for aya::maps::ArrayOfMaps where T: core::marker::UnsafeUnpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::ArrayOfMaps where T: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::ArrayOfMaps where T: core::panic::unwind_safe::UnwindSafe, V: core::panic::unwind_safe::UnwindSafe pub struct aya::maps::BloomFilter impl, V: aya::Pod> aya::maps::bloom_filter::BloomFilter pub fn aya::maps::bloom_filter::BloomFilter::contains(&self, value: impl core::borrow::Borrow, flags: u64) -> core::result::Result<(), aya::maps::MapError> @@ -1072,6 +1198,8 @@ impl, V: aya::Pod> aya::maps::bloom_ pub fn aya::maps::bloom_filter::BloomFilter::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> impl, V: aya::Pod> aya::maps::bloom_filter::BloomFilter pub fn aya::maps::bloom_filter::BloomFilter::insert(&mut self, value: impl core::borrow::Borrow, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::bloom_filter::BloomFilter +pub fn aya::maps::bloom_filter::BloomFilter::create(max_entries: u32, flags: u32) -> core::result::Result impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V> pub type aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::bloom_filter::BloomFilter<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -1171,6 +1299,8 @@ impl core::marker::UnsafeUnpin for aya::maps::DevMapHash where T: core::ma impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::DevMapHash where T: core::panic::unwind_safe::RefUnwindSafe impl core::panic::unwind_safe::UnwindSafe for aya::maps::DevMapHash where T: core::panic::unwind_safe::UnwindSafe pub struct aya::maps::HashMap +impl aya::maps::hash_map::HashMap +pub fn aya::maps::hash_map::HashMap::create(max_entries: u32, flags: u32) -> core::result::Result impl, K: aya::Pod, V: aya::Pod> aya::maps::hash_map::HashMap pub fn aya::maps::hash_map::HashMap::get(&self, key: &K, flags: u64) -> core::result::Result pub fn aya::maps::hash_map::HashMap::iter(&self) -> aya::maps::MapIter<'_, K, V, Self> @@ -1205,7 +1335,38 @@ impl core::marker::Unpin for aya::maps::hash_map::HashMap wher impl core::marker::UnsafeUnpin for aya::maps::hash_map::HashMap where T: core::marker::UnsafeUnpin impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::hash_map::HashMap where T: core::panic::unwind_safe::RefUnwindSafe, K: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe impl core::panic::unwind_safe::UnwindSafe for aya::maps::hash_map::HashMap where T: core::panic::unwind_safe::UnwindSafe, K: core::panic::unwind_safe::UnwindSafe, V: core::panic::unwind_safe::UnwindSafe +pub struct aya::maps::HashOfMaps +impl aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> +impl, K: aya::Pod, V: aya::maps::FromMapData> aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::get(&self, key: &K, flags: u64) -> core::result::Result +impl, K: aya::Pod, V> aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::keys(&self) -> aya::maps::MapKeys<'_, K> +impl, K: aya::Pod, V: aya::maps::InnerMap> aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::insert(&mut self, key: impl core::borrow::Borrow, value: &V, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl, K: aya::Pod, V> aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::remove(&mut self, key: &K) -> core::result::Result<(), aya::maps::MapError> +impl<'a, K: aya::Pod, V: aya::maps::InnerMap> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::HashOfMaps<&'a aya::maps::MapData, K, V> +pub type aya::maps::HashOfMaps<&'a aya::maps::MapData, K, V>::Error = aya::maps::MapError +pub fn aya::maps::HashOfMaps<&'a aya::maps::MapData, K, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a, K: aya::Pod, V: aya::maps::InnerMap> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::HashOfMaps<&'a mut aya::maps::MapData, K, V> +pub type aya::maps::HashOfMaps<&'a mut aya::maps::MapData, K, V>::Error = aya::maps::MapError +pub fn aya::maps::HashOfMaps<&'a mut aya::maps::MapData, K, V>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::HashOfMaps +pub type aya::maps::HashOfMaps::Error = aya::maps::MapError +pub fn aya::maps::HashOfMaps::try_from(map: aya::maps::Map) -> core::result::Result +impl core::fmt::Debug for aya::maps::HashOfMaps +pub fn aya::maps::HashOfMaps::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya::maps::HashOfMaps where T: core::marker::Freeze +impl core::marker::Send for aya::maps::HashOfMaps where T: core::marker::Send, K: core::marker::Send, V: core::marker::Send +impl core::marker::Sync for aya::maps::HashOfMaps where T: core::marker::Sync, K: core::marker::Sync, V: core::marker::Sync +impl core::marker::Unpin for aya::maps::HashOfMaps where T: core::marker::Unpin, K: core::marker::Unpin, V: core::marker::Unpin +impl core::marker::UnsafeUnpin for aya::maps::HashOfMaps where T: core::marker::UnsafeUnpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::HashOfMaps where T: core::panic::unwind_safe::RefUnwindSafe, K: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::HashOfMaps where T: core::panic::unwind_safe::UnwindSafe, K: core::panic::unwind_safe::UnwindSafe, V: core::panic::unwind_safe::UnwindSafe pub struct aya::maps::LpmTrie +impl aya::maps::lpm_trie::LpmTrie +pub fn aya::maps::lpm_trie::LpmTrie::create(max_entries: u32, flags: u32) -> core::result::Result impl, K: aya::Pod, V: aya::Pod> aya::maps::lpm_trie::LpmTrie pub fn aya::maps::lpm_trie::LpmTrie::get(&self, key: &aya::maps::lpm_trie::Key, flags: u64) -> core::result::Result pub fn aya::maps::lpm_trie::LpmTrie::iter(&self) -> aya::maps::MapIter<'_, aya::maps::lpm_trie::Key, V, Self> @@ -1323,6 +1484,8 @@ impl, V: aya::Pod> aya::maps::PerCpu pub fn aya::maps::PerCpuArray::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> impl, V: aya::Pod> aya::maps::PerCpuArray pub fn aya::maps::PerCpuArray::set(&mut self, index: u32, values: aya::maps::PerCpuValues, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::PerCpuArray +pub fn aya::maps::PerCpuArray::create(max_entries: u32, flags: u32) -> core::result::Result impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::PerCpuArray<&'a aya::maps::MapData, V> pub type aya::maps::PerCpuArray<&'a aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::PerCpuArray<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -1343,6 +1506,8 @@ impl core::marker::UnsafeUnpin for aya::maps::PerCpuArray where T: c impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::PerCpuArray where T: core::panic::unwind_safe::RefUnwindSafe, V: core::panic::unwind_safe::RefUnwindSafe impl core::panic::unwind_safe::UnwindSafe for aya::maps::PerCpuArray where T: core::panic::unwind_safe::UnwindSafe, V: core::panic::unwind_safe::UnwindSafe pub struct aya::maps::PerCpuHashMap +impl aya::maps::hash_map::PerCpuHashMap +pub fn aya::maps::hash_map::PerCpuHashMap::create(max_entries: u32, flags: u32) -> core::result::Result impl, K: aya::Pod, V: aya::Pod> aya::maps::hash_map::PerCpuHashMap pub fn aya::maps::hash_map::PerCpuHashMap::get(&self, key: &K, flags: u64) -> core::result::Result, aya::maps::MapError> pub fn aya::maps::hash_map::PerCpuHashMap::iter(&self) -> aya::maps::MapIter<'_, K, aya::maps::PerCpuValues, Self> @@ -1450,6 +1615,8 @@ pub fn aya::maps::queue::Queue::pin, V: aya::Pod> aya::maps::queue::Queue pub fn aya::maps::queue::Queue::pop(&mut self, flags: u64) -> core::result::Result pub fn aya::maps::queue::Queue::push(&mut self, value: impl core::borrow::Borrow, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::queue::Queue +pub fn aya::maps::queue::Queue::create(max_entries: u32, flags: u32) -> core::result::Result impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::queue::Queue<&'a aya::maps::MapData, V> pub type aya::maps::queue::Queue<&'a aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::queue::Queue<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -1606,6 +1773,8 @@ pub fn aya::maps::stack::Stack::pin, V: aya::Pod> aya::maps::stack::Stack pub fn aya::maps::stack::Stack::pop(&mut self, flags: u64) -> core::result::Result pub fn aya::maps::stack::Stack::push(&mut self, value: impl core::borrow::Borrow, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl aya::maps::stack::Stack +pub fn aya::maps::stack::Stack::create(max_entries: u32, flags: u32) -> core::result::Result impl<'a, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::stack::Stack<&'a aya::maps::MapData, V> pub type aya::maps::stack::Stack<&'a aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::stack::Stack<&'a aya::maps::MapData, V>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -1680,6 +1849,10 @@ impl core::marker::Unpin for aya::maps::XskMap where T: core::marker::Unpi impl core::marker::UnsafeUnpin for aya::maps::XskMap where T: core::marker::UnsafeUnpin impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::XskMap where T: core::panic::unwind_safe::RefUnwindSafe impl core::panic::unwind_safe::UnwindSafe for aya::maps::XskMap where T: core::panic::unwind_safe::UnwindSafe +pub trait aya::maps::FromMapData: aya::maps::sealed::FromMapData +impl aya::maps::FromMapData for T +pub trait aya::maps::InnerMap: aya::maps::sealed::InnerMap +impl aya::maps::InnerMap for T pub trait aya::maps::IterableMap pub fn aya::maps::IterableMap::get(&self, key: &K) -> core::result::Result pub fn aya::maps::IterableMap::map(&self) -> &aya::maps::MapData