Skip to main content

aya_friday/maps/
mod.rs

1//! Data structures used to setup and share data with eBPF programs.
2//!
3//! The eBPF platform provides data structures - maps in eBPF speak - that are
4//! used to setup and share data with eBPF programs. When you call
5//! [`Ebpf::load_file`](crate::Ebpf::load_file) or
6//! [`Ebpf::load`](crate::Ebpf::load), all the maps defined in the eBPF code get
7//! initialized and can then be accessed using [`Ebpf::map`](crate::Ebpf::map),
8//! [`Ebpf::map_mut`](crate::Ebpf::map_mut), or
9//! [`Ebpf::take_map`](crate::Ebpf::take_map).
10//!
11//! # Typed maps
12//!
13//! The eBPF API includes many map types each supporting different operations.
14//! [`Ebpf::map`](crate::Ebpf::map), [`Ebpf::map_mut`](crate::Ebpf::map_mut), and
15//! [`Ebpf::take_map`](crate::Ebpf::take_map) always return the opaque
16//! [`&Map`](crate::maps::Map), [`&mut Map`](crate::maps::Map), and [`Map`]
17//! types respectively. Those three types can be converted to *typed maps* using
18//! the [`TryFrom`] or [`TryInto`] trait. For example:
19//!
20//! ```no_run
21//! # #[derive(Debug, thiserror::Error)]
22//! # enum Error {
23//! #     #[error(transparent)]
24//! #     IO(#[from] std::io::Error),
25//! #     #[error(transparent)]
26//! #     Map(#[from] aya::maps::MapError),
27//! #     #[error(transparent)]
28//! #     Program(#[from] aya::programs::ProgramError),
29//! #     #[error(transparent)]
30//! #     Ebpf(#[from] aya::EbpfError)
31//! # }
32//! # let mut bpf = aya::Ebpf::load(&[])?;
33//! use aya::maps::SockMap;
34//! use aya::programs::SkMsg;
35//!
36//! let intercept_egress = SockMap::try_from(bpf.map_mut("INTERCEPT_EGRESS").unwrap())?;
37//! let map_fd = intercept_egress.fd().try_clone()?;
38//! let prog: &mut SkMsg = bpf.program_mut("intercept_egress_packet").unwrap().try_into()?;
39//! prog.load()?;
40//! prog.attach(&map_fd)?;
41//!
42//! # Ok::<(), Error>(())
43//! ```
44//!
45//! # Maps and `Pod` values
46//!
47//! Many map operations copy data from kernel space to user space and vice
48//! versa. Because of that, all map values must be plain old data and therefore
49//! implement the [Pod] trait.
50use std::{
51    borrow::Borrow,
52    ffi::CString,
53    io,
54    marker::PhantomData,
55    ops::Deref,
56    os::fd::{AsFd, BorrowedFd, OwnedFd},
57    path::Path,
58    ptr,
59};
60
61use aya_obj::{EbpfSectionKind, InvalidTypeBinding, generated::bpf_map_type, parse_map_info};
62use thiserror::Error;
63
64use crate::{
65    PinningType, Pod,
66    pin::PinError,
67    sys::{
68        SyscallError, bpf_create_map, bpf_get_object, bpf_map_freeze, bpf_map_get_fd_by_id,
69        bpf_map_get_next_key, bpf_map_update_elem_ptr, bpf_pin_object,
70    },
71    util::nr_cpus,
72};
73
74pub mod array;
75pub mod bloom_filter;
76pub mod hash_map;
77mod info;
78pub mod inode_storage;
79pub mod lpm_trie;
80pub mod of_maps;
81pub mod perf;
82pub mod queue;
83pub mod ring_buf;
84pub mod sk_storage;
85pub mod sock;
86pub mod stack;
87pub mod stack_trace;
88pub mod xdp;
89
90pub use array::{Array, CgroupArray, PerCpuArray, ProgramArray};
91pub use bloom_filter::BloomFilter;
92pub use hash_map::{HashMap, PerCpuHashMap};
93pub use info::{MapInfo, MapType, loaded_maps};
94pub use inode_storage::InodeStorage;
95pub use lpm_trie::LpmTrie;
96pub use of_maps::{ArrayOfMaps, HashOfMaps};
97pub use perf::PerfEventArray;
98pub use queue::Queue;
99pub use ring_buf::RingBuf;
100pub use sk_storage::SkStorage;
101pub use sock::{ReusePortSockArray, SockHash, SockMap};
102pub use stack::Stack;
103pub use stack_trace::StackTraceMap;
104pub use xdp::{CpuMap, DevMap, DevMapHash, XskMap};
105
106/// Trait for constructing a typed map from [`MapData`].
107///
108/// This trait is sealed and cannot be implemented outside of this crate.
109pub trait FromMapData: sealed::FromMapData {}
110
111impl<T: sealed::FromMapData> FromMapData for T {}
112
113/// Marker for map types that the kernel supports as inner maps.
114///
115/// This trait is sealed and cannot be implemented outside of this crate.
116pub trait InnerMap: sealed::InnerMap {}
117
118impl<T: sealed::InnerMap> InnerMap for T {}
119
120mod sealed {
121    use super::{MapData, MapError, MapFd};
122
123    #[expect(unnameable_types, reason = "intentionally unnameable sealed trait")]
124    pub trait FromMapData: Sized {
125        /// Constructs a typed map from raw [`MapData`].
126        fn from_map_data(map_data: MapData) -> Result<Self, MapError>;
127    }
128
129    #[expect(unnameable_types, reason = "intentionally unnameable sealed trait")]
130    pub trait InnerMap {
131        /// Returns the map file descriptor.
132        fn fd(&self) -> &MapFd;
133    }
134}
135
136#[derive(Error, Debug)]
137/// Errors occurring from working with Maps
138pub enum MapError {
139    /// Missing inner map BTF definition for a map-of-maps.
140    #[error(
141        "map `{outer_name}` is a map-of-maps but has no inner map definition; \
142             use #[btf_map] with a BTF-typed map-of-maps that includes an inner map type"
143    )]
144    MissingInnerMapDefinition {
145        /// Map name
146        outer_name: String,
147    },
148
149    /// Invalid map type encontered
150    #[error("invalid map type {map_type}")]
151    InvalidMapType {
152        /// The map type
153        map_type: u32,
154    },
155
156    /// Invalid map name encountered
157    #[error("invalid map name `{name}`")]
158    InvalidName {
159        /// The map name
160        name: String,
161    },
162
163    /// Failed to create map
164    #[error("failed to create map `{name}`")]
165    CreateError {
166        /// Map name
167        name: String,
168        #[source]
169        /// Original [`io::Error`]
170        io_error: io::Error,
171    },
172
173    /// Invalid key size
174    #[error("invalid key size {size}, expected {expected}")]
175    InvalidKeySize {
176        /// Size encountered
177        size: usize,
178        /// Size expected
179        expected: usize,
180    },
181
182    /// Invalid value size
183    #[error("invalid value size {size}, expected {expected}")]
184    InvalidValueSize {
185        /// Size encountered
186        size: usize,
187        /// Size expected
188        expected: usize,
189    },
190
191    /// Invalid value stride
192    #[error("invalid value size {size}, expected a non-zero multiple of {stride}")]
193    InvalidValueStride {
194        /// Size encountered
195        size: usize,
196        /// Required stride
197        stride: usize,
198    },
199
200    /// Index is out of bounds
201    #[error("the index is {index} but `max_entries` is {max_entries}")]
202    OutOfBounds {
203        /// Index accessed
204        index: u32,
205        /// Map size
206        max_entries: u32,
207    },
208
209    /// Key not found
210    #[error("key not found")]
211    KeyNotFound,
212
213    /// Element not found
214    #[error("element not found")]
215    ElementNotFound,
216
217    /// Program Not Loaded
218    #[error("the program is not loaded")]
219    ProgramNotLoaded,
220
221    /// An IO error occurred
222    #[error(transparent)]
223    IoError(#[from] io::Error),
224
225    /// Syscall failed
226    #[error(transparent)]
227    SyscallError(#[from] SyscallError),
228
229    /// Could not pin map
230    #[error("map `{name:?}` requested pinning. pinning failed")]
231    PinError {
232        /// The map name
233        name: Option<String>,
234        /// The reason for the failure
235        #[source]
236        error: PinError,
237    },
238
239    /// Program IDs are not supported
240    #[error("program ids are not supported by the current kernel")]
241    ProgIdNotSupported,
242
243    /// Unsupported Map type
244    #[error(
245        "type of {name} ({map_type:?}) is unsupported; see `EbpfLoader::allow_unsupported_maps`"
246    )]
247    Unsupported {
248        /// Map name
249        name: String,
250        /// The map type
251        map_type: bpf_map_type,
252    },
253
254    /// Unsupported map flags
255    #[error("unsupported map flags {flags:#x}: {reason}")]
256    UnsupportedMapFlags {
257        /// The map flags
258        flags: u32,
259        /// The reason
260        reason: &'static str,
261    },
262}
263
264impl From<InvalidTypeBinding<u32>> for MapError {
265    fn from(e: InvalidTypeBinding<u32>) -> Self {
266        let InvalidTypeBinding { value } = e;
267        Self::InvalidMapType { map_type: value }
268    }
269}
270
271/// A map file descriptor.
272#[derive(Debug)]
273pub struct MapFd {
274    fd: crate::MockableFd,
275}
276
277impl MapFd {
278    const fn from_fd(fd: crate::MockableFd) -> Self {
279        Self { fd }
280    }
281
282    fn try_clone(&self) -> io::Result<Self> {
283        let Self { fd } = self;
284        let fd = fd.try_clone()?;
285        Ok(Self { fd })
286    }
287}
288
289impl AsFd for MapFd {
290    fn as_fd(&self) -> BorrowedFd<'_> {
291        let Self { fd } = self;
292        fd.as_fd()
293    }
294}
295
296/// eBPF map types.
297#[derive(Debug)]
298pub enum Map {
299    /// An [`Array`] map.
300    Array(MapData),
301    /// An [`ArrayOfMaps`] map.
302    ArrayOfMaps(MapData),
303    /// A [`BloomFilter`] map.
304    BloomFilter(MapData),
305    /// A [`CgroupArray`] map.
306    CgroupArray(MapData),
307    /// A [`CpuMap`] map.
308    CpuMap(MapData),
309    /// A [`DevMap`] map.
310    DevMap(MapData),
311    /// A [`DevMapHash`] map.
312    DevMapHash(MapData),
313    /// A [`HashMap`] map.
314    HashMap(MapData),
315    /// A [`HashOfMaps`] map.
316    HashOfMaps(MapData),
317    /// An [`InodeStorage`] map.
318    InodeStorage(MapData),
319    /// A [`LpmTrie`] map.
320    LpmTrie(MapData),
321    /// A [`HashMap`] map that uses a LRU eviction policy.
322    LruHashMap(MapData),
323    /// A [`PerCpuArray`] map.
324    PerCpuArray(MapData),
325    /// A [`PerCpuHashMap`] map.
326    PerCpuHashMap(MapData),
327    /// A [`PerCpuHashMap`] map that uses a LRU eviction policy.
328    PerCpuLruHashMap(MapData),
329    /// A [`PerfEventArray`] map.
330    PerfEventArray(MapData),
331    /// A [`ProgramArray`] map.
332    ProgramArray(MapData),
333    /// A [`Queue`] map.
334    Queue(MapData),
335    /// A [`ReusePortSockArray`] map.
336    ReusePortSockArray(MapData),
337    /// A [`RingBuf`] map.
338    RingBuf(MapData),
339    /// A [`SockHash`] map
340    SockHash(MapData),
341    /// A [`SockMap`] map.
342    SockMap(MapData),
343    /// A [`SkStorage`] map.
344    SkStorage(MapData),
345    /// A [`Stack`] map.
346    Stack(MapData),
347    /// A [`StackTraceMap`] map.
348    StackTraceMap(MapData),
349    /// An unsupported map type.
350    Unsupported(MapData),
351    /// A [`XskMap`] map.
352    XskMap(MapData),
353}
354
355impl Map {
356    /// Returns the low level map type.
357    const fn map_type(&self) -> u32 {
358        match self {
359            Self::Array(map) => map.obj.map_type(),
360            Self::ArrayOfMaps(map) => map.obj.map_type(),
361            Self::BloomFilter(map) => map.obj.map_type(),
362            Self::CgroupArray(map) => map.obj.map_type(),
363            Self::CpuMap(map) => map.obj.map_type(),
364            Self::DevMap(map) => map.obj.map_type(),
365            Self::DevMapHash(map) => map.obj.map_type(),
366            Self::HashMap(map) => map.obj.map_type(),
367            Self::HashOfMaps(map) => map.obj.map_type(),
368            Self::InodeStorage(map) => map.obj.map_type(),
369            Self::LpmTrie(map) => map.obj.map_type(),
370            Self::LruHashMap(map) => map.obj.map_type(),
371            Self::PerCpuArray(map) => map.obj.map_type(),
372            Self::PerCpuHashMap(map) => map.obj.map_type(),
373            Self::PerCpuLruHashMap(map) => map.obj.map_type(),
374            Self::PerfEventArray(map) => map.obj.map_type(),
375            Self::ProgramArray(map) => map.obj.map_type(),
376            Self::Queue(map) => map.obj.map_type(),
377            Self::ReusePortSockArray(map) => map.obj.map_type(),
378            Self::RingBuf(map) => map.obj.map_type(),
379            Self::SockHash(map) => map.obj.map_type(),
380            Self::SockMap(map) => map.obj.map_type(),
381            Self::SkStorage(map) => map.obj.map_type(),
382            Self::Stack(map) => map.obj.map_type(),
383            Self::StackTraceMap(map) => map.obj.map_type(),
384            Self::Unsupported(map) => map.obj.map_type(),
385            Self::XskMap(map) => map.obj.map_type(),
386        }
387    }
388
389    /// Pins the map to a BPF filesystem.
390    ///
391    /// When a map is pinned it will remain loaded until the corresponding file
392    /// is deleted. All parent directories in the given `path` must already exist.
393    pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), PinError> {
394        match self {
395            Self::Array(map) => map.pin(path),
396            Self::ArrayOfMaps(map) => map.pin(path),
397            Self::BloomFilter(map) => map.pin(path),
398            Self::CgroupArray(map) => map.pin(path),
399            Self::CpuMap(map) => map.pin(path),
400            Self::DevMap(map) => map.pin(path),
401            Self::DevMapHash(map) => map.pin(path),
402            Self::HashMap(map) => map.pin(path),
403            Self::HashOfMaps(map) => map.pin(path),
404            Self::InodeStorage(map) => map.pin(path),
405            Self::LpmTrie(map) => map.pin(path),
406            Self::LruHashMap(map) => map.pin(path),
407            Self::PerCpuArray(map) => map.pin(path),
408            Self::PerCpuHashMap(map) => map.pin(path),
409            Self::PerCpuLruHashMap(map) => map.pin(path),
410            Self::PerfEventArray(map) => map.pin(path),
411            Self::ProgramArray(map) => map.pin(path),
412            Self::Queue(map) => map.pin(path),
413            Self::RingBuf(map) => map.pin(path),
414            Self::ReusePortSockArray(map) => map.pin(path),
415            Self::SockHash(map) => map.pin(path),
416            Self::SockMap(map) => map.pin(path),
417            Self::SkStorage(map) => map.pin(path),
418            Self::Stack(map) => map.pin(path),
419            Self::StackTraceMap(map) => map.pin(path),
420            Self::Unsupported(map) => map.pin(path),
421            Self::XskMap(map) => map.pin(path),
422        }
423    }
424
425    /// Constructs a [`Map`] enum variant directly from a [`MapData`] instance. This allows creating
426    /// a user-space handle to a pinned BPF map.
427    ///
428    /// # Arguments
429    ///
430    /// * `map_data` - The map data obtained from [`MapData::from_pin`].
431    ///
432    /// # Errors
433    ///
434    /// Returns an error if the map type is not supported.
435    pub fn from_map_data(map_data: MapData) -> Result<Self, MapError> {
436        let map_type = map_data.obj.map_type();
437        let map = match bpf_map_type::try_from(map_type)? {
438            bpf_map_type::BPF_MAP_TYPE_HASH => Self::HashMap(map_data),
439            bpf_map_type::BPF_MAP_TYPE_ARRAY => Self::Array(map_data),
440            bpf_map_type::BPF_MAP_TYPE_PROG_ARRAY => Self::ProgramArray(map_data),
441            bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY => Self::PerfEventArray(map_data),
442            bpf_map_type::BPF_MAP_TYPE_PERCPU_HASH => Self::PerCpuHashMap(map_data),
443            bpf_map_type::BPF_MAP_TYPE_PERCPU_ARRAY => Self::PerCpuArray(map_data),
444            bpf_map_type::BPF_MAP_TYPE_STACK_TRACE => Self::StackTraceMap(map_data),
445            bpf_map_type::BPF_MAP_TYPE_LRU_HASH => Self::LruHashMap(map_data),
446            bpf_map_type::BPF_MAP_TYPE_LRU_PERCPU_HASH => Self::PerCpuLruHashMap(map_data),
447            bpf_map_type::BPF_MAP_TYPE_LPM_TRIE => Self::LpmTrie(map_data),
448            bpf_map_type::BPF_MAP_TYPE_DEVMAP => Self::DevMap(map_data),
449            bpf_map_type::BPF_MAP_TYPE_SOCKMAP => Self::SockMap(map_data),
450            bpf_map_type::BPF_MAP_TYPE_CPUMAP => Self::CpuMap(map_data),
451            bpf_map_type::BPF_MAP_TYPE_XSKMAP => Self::XskMap(map_data),
452            bpf_map_type::BPF_MAP_TYPE_SOCKHASH => Self::SockHash(map_data),
453            bpf_map_type::BPF_MAP_TYPE_QUEUE => Self::Queue(map_data),
454            bpf_map_type::BPF_MAP_TYPE_STACK => Self::Stack(map_data),
455            bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH => Self::DevMapHash(map_data),
456            bpf_map_type::BPF_MAP_TYPE_RINGBUF => Self::RingBuf(map_data),
457            bpf_map_type::BPF_MAP_TYPE_BLOOM_FILTER => Self::BloomFilter(map_data),
458            bpf_map_type::BPF_MAP_TYPE_CGROUP_ARRAY => Self::CgroupArray(map_data),
459            bpf_map_type::BPF_MAP_TYPE_ARRAY_OF_MAPS => Self::ArrayOfMaps(map_data),
460            bpf_map_type::BPF_MAP_TYPE_HASH_OF_MAPS => Self::HashOfMaps(map_data),
461            bpf_map_type::BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED => Self::Unsupported(map_data),
462            bpf_map_type::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY => Self::ReusePortSockArray(map_data),
463            bpf_map_type::BPF_MAP_TYPE_SK_STORAGE => Self::SkStorage(map_data),
464            bpf_map_type::BPF_MAP_TYPE_STRUCT_OPS => Self::Unsupported(map_data),
465            bpf_map_type::BPF_MAP_TYPE_INODE_STORAGE => Self::InodeStorage(map_data),
466            bpf_map_type::BPF_MAP_TYPE_TASK_STORAGE => Self::Unsupported(map_data),
467            bpf_map_type::BPF_MAP_TYPE_USER_RINGBUF => Self::Unsupported(map_data),
468            bpf_map_type::BPF_MAP_TYPE_CGRP_STORAGE => Self::Unsupported(map_data),
469            bpf_map_type::BPF_MAP_TYPE_ARENA => Self::Unsupported(map_data),
470            bpf_map_type::BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE_DEPRECATED => {
471                Self::Unsupported(map_data)
472            }
473            bpf_map_type::BPF_MAP_TYPE_UNSPEC => return Err(MapError::InvalidMapType { map_type }),
474            bpf_map_type::__MAX_BPF_MAP_TYPE => return Err(MapError::InvalidMapType { map_type }),
475        };
476        Ok(map)
477    }
478}
479
480// Implements map pinning for different map implementations
481macro_rules! impl_map_pin {
482    ($ty_param:tt {
483        $($ty:ident),+ $(,)?
484    }) => {
485        $(impl_map_pin!(<$ty_param> $ty);)+
486    };
487    (
488      <($($ty_param:ident),*)>
489      $ty:ident
490    ) => {
491            impl<T: Borrow<MapData>, $($ty_param: Pod),*> $ty<T, $($ty_param),*>
492            {
493                    /// Pins the map to a BPF filesystem.
494                    ///
495                    /// When a map is pinned it will remain loaded until the corresponding file
496                    /// is deleted. All parent directories in the given `path` must already exist.
497                    pub fn pin<P: AsRef<Path>>(self, path: P) -> Result<(), PinError> {
498                        let data = self.inner.borrow();
499                        data.pin(path)
500                    }
501            }
502
503    };
504}
505
506impl_map_pin!(() {
507    CgroupArray,
508    ProgramArray,
509    ReusePortSockArray,
510    SockMap,
511    StackTraceMap,
512    CpuMap,
513    DevMap,
514    DevMapHash,
515    XskMap,
516});
517
518impl_map_pin!((V) {
519    Array,
520    PerCpuArray,
521    SockHash,
522    BloomFilter,
523    InodeStorage,
524    Queue,
525    SkStorage,
526    Stack,
527});
528
529impl_map_pin!((K, V) {
530    HashMap,
531    PerCpuHashMap,
532    LpmTrie,
533});
534
535// Implements TryFrom<Map> for different map implementations. Different map implementations can be
536// constructed from different variants of the map enum. Also, the implementation may have type
537// parameters. The dispatch arm adds `Pod` bounds explicitly before forwarding to the @impl arm,
538// which accepts arbitrary bounds and is also used by impl_try_from_map_of_maps.
539macro_rules! impl_try_from_map {
540    // At the root the type parameters are marked as a single token tree which will be pasted into
541    // the invocation for each type. Note that the later patterns require that the token tree be
542    // zero or more comma separated idents wrapped in parens. Note that the tt metavar is used here
543    // rather than the repeated idents used later because the macro language does not allow one
544    // repetition to be pasted inside another.
545    ($ty_param:tt {
546        $($(#[$meta:meta])* $ty:ident $(from $($variant:ident)|+)?),+ $(,)?
547    }) => {
548        $(impl_try_from_map!($(#[$meta])* <$ty_param> $ty $(from $($variant)|+)?);)+
549    };
550    // Add the "from $variant" using $ty as the default if it is missing.
551    ($(#[$meta:meta])* <$ty_param:tt> $ty:ident) => {
552        impl_try_from_map!($(#[$meta])* <$ty_param> $ty from $ty);
553    };
554    // Dispatch for each of the lifetimes, adding Pod bounds explicitly.
555    (
556        $(#[$meta:meta])* <($($ty_param:ident),*)> $ty:ident from $($variant:ident)|+
557    ) => {
558        impl_try_from_map!(@impl $(#[$meta])* <'a> ($($ty_param: Pod),*) $ty from $($variant)|+);
559        impl_try_from_map!(@impl $(#[$meta])* <'a mut> ($($ty_param: Pod),*) $ty from $($variant)|+);
560        impl_try_from_map!(@impl $(#[$meta])* <> ($($ty_param: Pod),*) $ty from $($variant)|+);
561    };
562    // An individual impl with explicit bounds. Used by both impl_try_from_map
563    // and impl_try_from_map_of_maps via the @impl internal rule.
564    (@impl
565        $(#[$meta:meta])*
566        <$($l:lifetime $($m:ident)?)?>
567        ($($ty_param:ident $(: $bound:path)?),*)
568        $ty:ident from $($variant:ident)|+
569    ) => {
570        $(#[$meta])*
571        impl<$($l,)? $($ty_param $(: $bound)?),*> TryFrom<$(&$l $($m)?)? Map>
572            for $ty<$(&$l $($m)?)? MapData, $($ty_param),*>
573        {
574            type Error = MapError;
575
576            fn try_from(map: $(&$l $($m)?)? Map) -> Result<Self, Self::Error> {
577                match map {
578                    $(Map::$variant(map_data) => Self::new(map_data),)+
579                    map => Err(MapError::InvalidMapType {
580                        map_type: map.map_type()
581                    }),
582                }
583            }
584        }
585    };
586}
587
588impl_try_from_map!(() {
589    CgroupArray,
590    CpuMap,
591    DevMap,
592    DevMapHash,
593    PerfEventArray,
594    ProgramArray,
595    ReusePortSockArray,
596    RingBuf,
597    SockMap,
598    StackTraceMap,
599    XskMap,
600});
601
602impl_try_from_map!((V) {
603    Array,
604    BloomFilter,
605    InodeStorage,
606    PerCpuArray,
607    Queue,
608    SockHash,
609    SkStorage,
610    Stack,
611});
612
613impl_try_from_map!((K, V) {
614    HashMap from HashMap|LruHashMap,
615    LpmTrie,
616    PerCpuHashMap from PerCpuHashMap|PerCpuLruHashMap,
617});
618
619// ArrayOfMaps and HashOfMaps require V: InnerMap in TryFrom conversions.
620// Delegates to the @impl arm of impl_try_from_map to avoid duplicating the
621// match body.
622macro_rules! impl_try_from_map_of_maps {
623    ($ty:ident) => {
624        impl_try_from_map_of_maps!($ty <>);
625    };
626    ($ty:ident <$($pre:ident : $pre_bound:path),*>) => {
627        impl_try_from_map!(@impl <'a> ($($pre: $pre_bound,)* V: InnerMap) $ty from $ty);
628        impl_try_from_map!(@impl <'a mut> ($($pre: $pre_bound,)* V: InnerMap) $ty from $ty);
629        impl_try_from_map!(@impl <> ($($pre: $pre_bound,)* V: InnerMap) $ty from $ty);
630    };
631}
632
633impl_try_from_map_of_maps!(ArrayOfMaps);
634impl_try_from_map_of_maps!(HashOfMaps<K: Pod>);
635
636// Implements `sealed::FromMapData` and `sealed::InnerMap` for a map type.
637// Types with `inner: T` use the default arm; PerfEventArray and RingBuf
638// pass `via map_data` to use their existing accessor method.
639macro_rules! impl_from_map_data {
640    ($ty_param:tt { $($ty:ident),+ $(,)? }) => {
641        $(impl_from_map_data!(<$ty_param> $ty);)+
642    };
643    (<($($ty_param:ident),*)> $ty:ident via $accessor:ident) => {
644        impl<$($ty_param: Pod),*> sealed::FromMapData for $ty<MapData, $($ty_param),*> {
645            fn from_map_data(map_data: MapData) -> Result<Self, MapError> {
646                Map::from_map_data(map_data)?.try_into()
647            }
648        }
649        impl<$($ty_param: Pod),*> sealed::InnerMap for $ty<MapData, $($ty_param),*> {
650            fn fd(&self) -> &MapFd {
651                self.$accessor().fd()
652            }
653        }
654    };
655    (<($($ty_param:ident),*)> $ty:ident) => {
656        impl<$($ty_param: Pod),*> sealed::FromMapData for $ty<MapData, $($ty_param),*> {
657            fn from_map_data(map_data: MapData) -> Result<Self, MapError> {
658                Map::from_map_data(map_data)?.try_into()
659            }
660        }
661        impl<$($ty_param: Pod),*> sealed::InnerMap for $ty<MapData, $($ty_param),*> {
662            fn fd(&self) -> &MapFd {
663                self.inner.fd()
664            }
665        }
666    };
667}
668
669// Map types that the kernel supports as inner maps.
670// Excluded: ProgramArray (no map_meta_equal), ArrayOfMaps/HashOfMaps (multi-level nesting
671// forbidden).
672impl_from_map_data!(() {
673    CpuMap, DevMap, DevMapHash,
674    SockMap, StackTraceMap, XskMap,
675});
676
677// PerfEventArray and RingBuf use map_data() instead of inner field.
678impl_from_map_data!(<()> PerfEventArray via map_data);
679impl_from_map_data!(<()> RingBuf via map_data);
680
681impl_from_map_data!((V) {
682    Array, BloomFilter, InodeStorage, PerCpuArray,
683    Queue, SockHash, SkStorage, Stack,
684});
685
686impl_from_map_data!((K, V) {
687    HashMap, LpmTrie, PerCpuHashMap,
688});
689
690impl sealed::FromMapData for MapData {
691    fn from_map_data(map_data: MapData) -> Result<Self, MapError> {
692        Ok(map_data)
693    }
694}
695
696impl sealed::InnerMap for MapData {
697    fn fd(&self) -> &MapFd {
698        self.fd()
699    }
700}
701
702impl sealed::InnerMap for MapFd {
703    fn fd(&self) -> &MapFd {
704        self
705    }
706}
707
708macro_rules! impl_creatable_map {
709    ($ty:ident<MapData $(, $p:ident: Pod)*>, $map_type:expr, $key_size:expr, $value_size:expr, $name:expr) => {
710        impl<$($p: Pod),*> $ty<MapData, $($p),*> {
711            /// Creates a standalone map with the given `max_entries` capacity and `flags`.
712            pub fn create(max_entries: u32, flags: u32) -> Result<Self, MapError> {
713                let obj = aya_obj::Map::new_from_params(
714                    $map_type as u32, $key_size, $value_size, max_entries, flags,
715                );
716                Self::new(MapData::create(obj, $name, None)?)
717            }
718        }
719    };
720}
721
722impl_creatable_map!(Array<MapData, V: Pod>,
723    bpf_map_type::BPF_MAP_TYPE_ARRAY, size_of::<u32>() as u32, size_of::<V>() as u32, "standalone_array");
724impl_creatable_map!(PerCpuArray<MapData, V: Pod>,
725    bpf_map_type::BPF_MAP_TYPE_PERCPU_ARRAY, size_of::<u32>() as u32, size_of::<V>() as u32, "standalone_percpu_array");
726impl_creatable_map!(BloomFilter<MapData, V: Pod>,
727    bpf_map_type::BPF_MAP_TYPE_BLOOM_FILTER, 0, size_of::<V>() as u32, "standalone_bloom_filter");
728impl_creatable_map!(Queue<MapData, V: Pod>,
729    bpf_map_type::BPF_MAP_TYPE_QUEUE, 0, size_of::<V>() as u32, "standalone_queue");
730impl_creatable_map!(Stack<MapData, V: Pod>,
731    bpf_map_type::BPF_MAP_TYPE_STACK, 0, size_of::<V>() as u32, "standalone_stack");
732impl_creatable_map!(HashMap<MapData, K: Pod, V: Pod>,
733    bpf_map_type::BPF_MAP_TYPE_HASH, size_of::<K>() as u32, size_of::<V>() as u32, "standalone_hash");
734impl_creatable_map!(PerCpuHashMap<MapData, K: Pod, V: Pod>,
735    bpf_map_type::BPF_MAP_TYPE_PERCPU_HASH, size_of::<K>() as u32, size_of::<V>() as u32, "standalone_percpu_hash");
736impl_creatable_map!(LpmTrie<MapData, K: Pod, V: Pod>,
737    bpf_map_type::BPF_MAP_TYPE_LPM_TRIE, size_of::<lpm_trie::Key<K>>() as u32, size_of::<V>() as u32, "standalone_lpm_trie");
738
739pub(crate) const fn check_bounds(map: &MapData, index: u32) -> Result<(), MapError> {
740    let max_entries = map.obj.max_entries();
741    if index >= max_entries {
742        Err(MapError::OutOfBounds { index, max_entries })
743    } else {
744        Ok(())
745    }
746}
747
748pub(crate) const fn check_kv_size<K, V>(map: &MapData) -> Result<(), MapError> {
749    let size = size_of::<K>();
750    let expected = map.obj.key_size() as usize;
751    if size != expected {
752        return Err(MapError::InvalidKeySize { size, expected });
753    }
754    let size = size_of::<V>();
755    let expected = map.obj.value_size() as usize;
756    if size != expected {
757        return Err(MapError::InvalidValueSize { size, expected });
758    }
759    Ok(())
760}
761
762pub(crate) const fn check_v_size<V>(map: &MapData) -> Result<(), MapError> {
763    let size = size_of::<V>();
764    let expected = map.obj.value_size() as usize;
765    if size != expected {
766        return Err(MapError::InvalidValueSize { size, expected });
767    }
768    Ok(())
769}
770
771/// A generic handle to a BPF map.
772///
773/// You should never need to use this unless you're implementing a new map type.
774#[derive(Debug)]
775pub struct MapData {
776    obj: aya_obj::Map,
777    fd: MapFd,
778}
779
780impl MapData {
781    /// Creates a new map with the provided `name`
782    pub fn create(
783        obj: aya_obj::Map,
784        name: &str,
785        btf_fd: Option<BorrowedFd<'_>>,
786    ) -> Result<Self, MapError> {
787        Self::create_with_inner_map_fd(obj, name, btf_fd, None)
788    }
789
790    /// Creates a new map with the provided `name` and optional `inner_map_fd` for map-of-maps types.
791    pub(crate) fn create_with_inner_map_fd(
792        mut obj: aya_obj::Map,
793        name: &str,
794        btf_fd: Option<BorrowedFd<'_>>,
795        inner_map_fd: Option<BorrowedFd<'_>>,
796    ) -> Result<Self, MapError> {
797        let c_name = CString::new(name)
798            .map_err(|std::ffi::NulError { .. }| MapError::InvalidName { name: name.into() })?;
799
800        // BPF_MAP_TYPE_PERF_EVENT_ARRAY's max_entries should not exceed the number of
801        // CPUs.
802        //
803        // By default, the newest versions of Aya, libbpf and cilium/ebpf define `max_entries` of
804        // `PerfEventArray` as `0`, with an intention to get it replaced with a correct value
805        // by the loader.
806        //
807        // We allow custom values (potentially coming either from older versions of aya-ebpf or
808        // programs written in C) as long as they don't exceed the number of CPUs.
809        //
810        // Otherwise, when the value is `0` or too large, we set it to the number of CPUs.
811        if obj.map_type() == bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32 {
812            let nr_cpus = nr_cpus().map_err(|(_, error)| MapError::IoError(error))? as u32;
813            if obj.max_entries() == 0 || obj.max_entries() > nr_cpus {
814                obj.set_max_entries(nr_cpus);
815            }
816        }
817
818        let fd = bpf_create_map(&c_name, &obj, btf_fd, inner_map_fd).map_err(|io_error| {
819            MapError::CreateError {
820                name: name.into(),
821                io_error,
822            }
823        })?;
824        Ok(Self {
825            obj,
826            fd: MapFd::from_fd(fd),
827        })
828    }
829
830    pub(crate) fn create_pinned_by_name<P: AsRef<Path>>(
831        path: P,
832        obj: aya_obj::Map,
833        name: &str,
834        btf_fd: Option<BorrowedFd<'_>>,
835        inner_map_obj: Option<aya_obj::Map>,
836    ) -> Result<Self, MapError> {
837        use std::os::unix::ffi::OsStrExt as _;
838
839        // try to open map in case it's already pinned
840        let path = path.as_ref();
841        let path_string = match CString::new(path.as_os_str().as_bytes()) {
842            Ok(path) => path,
843            Err(error) => {
844                return Err(MapError::PinError {
845                    name: Some(name.into()),
846                    error: PinError::InvalidPinPath {
847                        path: path.to_path_buf(),
848                        error,
849                    },
850                });
851            }
852        };
853        if let Ok(fd) = bpf_get_object(&path_string) {
854            Ok(Self {
855                obj,
856                fd: MapFd::from_fd(fd),
857            })
858        } else {
859            let inner_map;
860            let inner_map_fd = if let Some(inner) = inner_map_obj {
861                inner_map = Self::create(inner, &format!("{name}.inner"), btf_fd)?;
862                Some(inner_map.fd().as_fd())
863            } else {
864                None
865            };
866            let map = Self::create_with_inner_map_fd(obj, name, btf_fd, inner_map_fd)?;
867            map.pin(path).map_err(|error| MapError::PinError {
868                name: Some(name.into()),
869                error,
870            })?;
871            Ok(map)
872        }
873    }
874
875    pub(crate) fn finalize(&mut self) -> Result<(), MapError> {
876        let Self { obj, fd } = self;
877        if !obj.data().is_empty() {
878            bpf_map_update_elem_ptr(fd.as_fd(), &0, obj.data_mut().as_mut_ptr(), 0)
879                .map_err(|io_error| SyscallError {
880                    call: "bpf_map_update_elem",
881                    io_error,
882                })
883                .map_err(MapError::from)?;
884        }
885        if obj.section_kind() == EbpfSectionKind::Rodata {
886            bpf_map_freeze(fd.as_fd())
887                .map_err(|io_error| SyscallError {
888                    call: "bpf_map_freeze",
889                    io_error,
890                })
891                .map_err(MapError::from)?;
892        }
893        Ok(())
894    }
895
896    /// Loads a map from a pinned path in bpffs.
897    pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, MapError> {
898        use std::os::unix::ffi::OsStrExt as _;
899
900        let path = path.as_ref();
901        let path_string =
902            CString::new(path.as_os_str().as_bytes()).map_err(|error| MapError::PinError {
903                name: None,
904                error: PinError::InvalidPinPath {
905                    path: path.into(),
906                    error,
907                },
908            })?;
909
910        let fd = bpf_get_object(&path_string).map_err(|io_error| SyscallError {
911            call: "BPF_OBJ_GET",
912            io_error,
913        })?;
914
915        Self::from_fd_inner(fd)
916    }
917
918    /// Loads a map from a map id.
919    pub fn from_id(id: u32) -> Result<Self, MapError> {
920        let fd = bpf_map_get_fd_by_id(id)?;
921        Self::from_fd_inner(fd)
922    }
923
924    fn from_fd_inner(fd: crate::MockableFd) -> Result<Self, MapError> {
925        let MapInfo(info) = MapInfo::new_from_fd(fd.as_fd())?;
926        Ok(Self {
927            obj: parse_map_info(info, PinningType::None),
928            fd: MapFd::from_fd(fd),
929        })
930    }
931
932    /// Loads a map from a file descriptor.
933    ///
934    /// If loading from a BPF Filesystem (bpffs) you should use [`Map::from_pin`](crate::maps::MapData::from_pin).
935    /// This API is intended for cases where you have received a valid BPF FD from some other means.
936    /// For example, you received an FD over Unix Domain Socket.
937    pub fn from_fd(fd: OwnedFd) -> Result<Self, MapError> {
938        let fd = crate::MockableFd::from_fd(fd);
939        Self::from_fd_inner(fd)
940    }
941
942    /// Allows the map to be pinned to the provided path.
943    ///
944    /// Any directories in the the path provided should have been created by the caller.
945    /// The path must be on a BPF filesystem.
946    ///
947    /// # Errors
948    ///
949    /// Returns a [`PinError::SyscallError`] if the underlying syscall fails.
950    /// This may also happen if the path already exists, in which case the wrapped
951    /// [`std::io::Error`] kind will be [`std::io::ErrorKind::AlreadyExists`].
952    /// Returns a [`PinError::InvalidPinPath`] if the path provided cannot be
953    /// converted to a [`CString`].
954    ///
955    /// # Example
956    ///
957    /// ```no_run
958    /// # let mut bpf = aya::Ebpf::load(&[])?;
959    /// # use aya::maps::MapData;
960    ///
961    /// let mut map = MapData::from_pin("/sys/fs/bpf/my_map")?;
962    /// map.pin("/sys/fs/bpf/my_map2")?;
963    ///
964    /// # Ok::<(), Box<dyn std::error::Error>>(())
965    /// ```
966    pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), PinError> {
967        use std::os::unix::ffi::OsStrExt as _;
968
969        let Self { fd, obj: _ } = self;
970        let path = path.as_ref();
971        let path_string = CString::new(path.as_os_str().as_bytes()).map_err(|error| {
972            PinError::InvalidPinPath {
973                path: path.to_path_buf(),
974                error,
975            }
976        })?;
977        bpf_pin_object(fd.as_fd(), &path_string).map_err(|io_error| SyscallError {
978            call: "BPF_OBJ_PIN",
979            io_error,
980        })?;
981        Ok(())
982    }
983
984    /// Returns the file descriptor of the map.
985    pub const fn fd(&self) -> &MapFd {
986        let Self { obj: _, fd } = self;
987        fd
988    }
989
990    pub(crate) const fn obj(&self) -> &aya_obj::Map {
991        let Self { obj, fd: _ } = self;
992        obj
993    }
994
995    /// Returns the kernel's information about the loaded map.
996    pub fn info(&self) -> Result<MapInfo, MapError> {
997        MapInfo::new_from_fd(self.fd.as_fd())
998    }
999}
1000
1001/// An iterable map
1002pub trait IterableMap<K: Pod, V> {
1003    /// Get a generic map handle
1004    fn map(&self) -> &MapData;
1005
1006    /// Get the value for the provided `key`
1007    fn get(&self, key: &K) -> Result<V, MapError>;
1008}
1009
1010/// Iterator returned by `map.keys()`.
1011pub struct MapKeys<'coll, K: Pod> {
1012    map: &'coll MapData,
1013    err: bool,
1014    key: Option<K>,
1015}
1016
1017impl<'coll, K: Pod> MapKeys<'coll, K> {
1018    const fn new(map: &'coll MapData) -> Self {
1019        Self {
1020            map,
1021            err: false,
1022            key: None,
1023        }
1024    }
1025}
1026
1027impl<K: Pod> Iterator for MapKeys<'_, K> {
1028    type Item = Result<K, MapError>;
1029
1030    fn next(&mut self) -> Option<Result<K, MapError>> {
1031        if self.err {
1032            return None;
1033        }
1034
1035        let fd = self.map.fd().as_fd();
1036        let key = bpf_map_get_next_key(fd, self.key.as_ref()).map_err(|io_error| SyscallError {
1037            call: "bpf_map_get_next_key",
1038            io_error,
1039        });
1040        match key {
1041            Err(err) => {
1042                self.err = true;
1043                Some(Err(err.into()))
1044            }
1045            Ok(key) => {
1046                self.key = key;
1047                key.map(Ok)
1048            }
1049        }
1050    }
1051}
1052
1053/// Iterator returned by `map.iter()`.
1054pub struct MapIter<'coll, K: Pod, V, I: IterableMap<K, V>> {
1055    keys: MapKeys<'coll, K>,
1056    map: &'coll I,
1057    _v: PhantomData<V>,
1058}
1059
1060impl<'coll, K: Pod, V, I: IterableMap<K, V>> MapIter<'coll, K, V, I> {
1061    fn new(map: &'coll I) -> Self {
1062        Self {
1063            keys: MapKeys::new(map.map()),
1064            map,
1065            _v: PhantomData,
1066        }
1067    }
1068}
1069
1070impl<K: Pod, V, I: IterableMap<K, V>> Iterator for MapIter<'_, K, V, I> {
1071    type Item = Result<(K, V), MapError>;
1072
1073    fn next(&mut self) -> Option<Self::Item> {
1074        loop {
1075            match self.keys.next() {
1076                Some(Ok(key)) => match self.map.get(&key) {
1077                    Ok(value) => return Some(Ok((key, value))),
1078                    Err(MapError::KeyNotFound) => {}
1079                    Err(e) => return Some(Err(e)),
1080                },
1081                Some(Err(e)) => return Some(Err(e)),
1082                None => return None,
1083            }
1084        }
1085    }
1086}
1087
1088pub(crate) struct PerCpuKernelMem {
1089    bytes: Vec<u8>,
1090}
1091
1092impl PerCpuKernelMem {
1093    pub(crate) const fn as_mut_ptr(&mut self) -> *mut u8 {
1094        self.bytes.as_mut_ptr()
1095    }
1096}
1097
1098/// A slice of per-CPU values.
1099///
1100/// Used by maps that implement per-CPU storage like [`PerCpuHashMap`].
1101///
1102/// # Examples
1103///
1104/// ```no_run
1105/// # #[derive(thiserror::Error, Debug)]
1106/// # enum Error {
1107/// #     #[error(transparent)]
1108/// #     IO(#[from] std::io::Error),
1109/// #     #[error(transparent)]
1110/// #     Map(#[from] aya::maps::MapError),
1111/// #     #[error(transparent)]
1112/// #     Ebpf(#[from] aya::EbpfError)
1113/// # }
1114/// # let bpf = aya::Ebpf::load(&[])?;
1115/// use aya::maps::PerCpuValues;
1116/// use aya::util::nr_cpus;
1117///
1118/// let nr_cpus = nr_cpus().map_err(|(_, error)| error)?;
1119/// let values = PerCpuValues::try_from(vec![42u32; nr_cpus])?;
1120/// # Ok::<(), Error>(())
1121/// ```
1122#[derive(Debug)]
1123pub struct PerCpuValues<T: Pod> {
1124    values: Box<[T]>,
1125}
1126
1127impl<T: Pod> TryFrom<Vec<T>> for PerCpuValues<T> {
1128    type Error = io::Error;
1129
1130    fn try_from(values: Vec<T>) -> Result<Self, Self::Error> {
1131        let nr_cpus = nr_cpus().map_err(|(_, error)| error)?;
1132        if values.len() != nr_cpus {
1133            return Err(io::Error::new(
1134                io::ErrorKind::InvalidInput,
1135                format!("not enough values ({}), nr_cpus: {}", values.len(), nr_cpus),
1136            ));
1137        }
1138        Ok(Self {
1139            values: values.into_boxed_slice(),
1140        })
1141    }
1142}
1143
1144impl<T: Pod> PerCpuValues<T> {
1145    pub(crate) fn alloc_kernel_mem() -> Result<PerCpuKernelMem, io::Error> {
1146        let value_size = size_of::<T>().next_multiple_of(8);
1147        let nr_cpus = nr_cpus().map_err(|(_, error)| error)?;
1148        Ok(PerCpuKernelMem {
1149            bytes: vec![0u8; nr_cpus * value_size],
1150        })
1151    }
1152
1153    pub(crate) unsafe fn from_kernel_mem(mem: PerCpuKernelMem) -> Self {
1154        let stride = size_of::<T>().next_multiple_of(8);
1155        let mut values = Vec::new();
1156        let mut offset = 0;
1157        while offset < mem.bytes.len() {
1158            values.push(unsafe { ptr::read_unaligned(mem.bytes.as_ptr().add(offset).cast()) });
1159            offset += stride;
1160        }
1161
1162        Self {
1163            values: values.into_boxed_slice(),
1164        }
1165    }
1166
1167    pub(crate) fn build_kernel_mem(&self) -> Result<PerCpuKernelMem, io::Error> {
1168        let mut mem = Self::alloc_kernel_mem()?;
1169        let mem_ptr = mem.as_mut_ptr();
1170        let value_size = size_of::<T>().next_multiple_of(8);
1171        for (i, value) in self.values.iter().enumerate() {
1172            unsafe { ptr::write_unaligned(mem_ptr.byte_add(i * value_size).cast(), *value) }
1173        }
1174
1175        Ok(mem)
1176    }
1177}
1178
1179impl<T: Pod> Deref for PerCpuValues<T> {
1180    type Target = Box<[T]>;
1181
1182    fn deref(&self) -> &Self::Target {
1183        &self.values
1184    }
1185}
1186
1187#[cfg(test)]
1188mod test_utils {
1189    use aya_obj::{
1190        EbpfSectionKind,
1191        generated::{bpf_cmd, bpf_map_type},
1192        maps::LegacyMap,
1193    };
1194
1195    use crate::{
1196        bpf_map_def,
1197        maps::MapData,
1198        sys::{Syscall, override_syscall},
1199    };
1200
1201    pub(super) fn new_map(obj: aya_obj::Map) -> MapData {
1202        override_syscall(|call| match call {
1203            Syscall::Ebpf {
1204                cmd: bpf_cmd::BPF_MAP_CREATE,
1205                ..
1206            } => Ok(crate::MockableFd::mock_signed_fd().into()),
1207            call => panic!("unexpected syscall {call:?}"),
1208        });
1209        MapData::create(obj, "foo", None).unwrap()
1210    }
1211
1212    pub(super) fn new_obj_map<K>(map_type: bpf_map_type) -> aya_obj::Map {
1213        aya_obj::Map::Legacy(LegacyMap {
1214            def: bpf_map_def {
1215                map_type: map_type as u32,
1216                key_size: size_of::<K>() as u32,
1217                value_size: 4,
1218                max_entries: 1024,
1219                ..Default::default()
1220            },
1221            inner_def: None,
1222            section_index: 0,
1223            section_kind: EbpfSectionKind::Maps,
1224            data: Vec::new(),
1225            symbol_index: None,
1226        })
1227    }
1228
1229    pub(super) fn new_obj_map_with_max_entries<K>(
1230        map_type: bpf_map_type,
1231        max_entries: u32,
1232    ) -> aya_obj::Map {
1233        aya_obj::Map::Legacy(LegacyMap {
1234            def: bpf_map_def {
1235                map_type: map_type as u32,
1236                key_size: size_of::<K>() as u32,
1237                value_size: 4,
1238                max_entries,
1239                ..Default::default()
1240            },
1241            inner_def: None,
1242            section_index: 0,
1243            section_kind: EbpfSectionKind::Maps,
1244            data: Vec::new(),
1245            symbol_index: None,
1246        })
1247    }
1248}
1249
1250#[cfg(test)]
1251mod tests {
1252    use std::{ffi::c_char, os::fd::AsRawFd as _};
1253
1254    use assert_matches::assert_matches;
1255    use aya_obj::generated::{bpf_cmd, bpf_map_info};
1256    use libc::EFAULT;
1257
1258    use super::*;
1259    use crate::sys::{Syscall, override_syscall};
1260
1261    fn new_obj_map() -> aya_obj::Map {
1262        test_utils::new_obj_map::<u32>(bpf_map_type::BPF_MAP_TYPE_HASH)
1263    }
1264
1265    #[test]
1266    fn test_from_map_id() {
1267        override_syscall(|call| match call {
1268            Syscall::Ebpf {
1269                cmd: bpf_cmd::BPF_MAP_GET_FD_BY_ID,
1270                attr,
1271            } => {
1272                assert_eq!(
1273                    unsafe { attr.__bindgen_anon_6.__bindgen_anon_1.map_id },
1274                    1234
1275                );
1276                Ok(crate::MockableFd::mock_signed_fd().into())
1277            }
1278            Syscall::Ebpf {
1279                cmd: bpf_cmd::BPF_OBJ_GET_INFO_BY_FD,
1280                attr,
1281            } => {
1282                assert_eq!(
1283                    unsafe { attr.info.bpf_fd },
1284                    crate::MockableFd::mock_unsigned_fd(),
1285                );
1286                Ok(0)
1287            }
1288            _ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
1289        });
1290
1291        assert_matches!(
1292            MapData::from_id(1234),
1293            Ok(MapData {
1294                obj: _,
1295                fd,
1296            }) => assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd())
1297        );
1298    }
1299
1300    #[test]
1301    fn test_create() {
1302        override_syscall(|call| match call {
1303            Syscall::Ebpf {
1304                cmd: bpf_cmd::BPF_MAP_CREATE,
1305                ..
1306            } => Ok(crate::MockableFd::mock_signed_fd().into()),
1307            _ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
1308        });
1309
1310        assert_matches!(
1311            MapData::create(new_obj_map(), "foo", None),
1312            Ok(MapData {
1313                obj: _,
1314                fd,
1315            }) => assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd())
1316        );
1317    }
1318
1319    #[test]
1320    fn test_create_perf_event_array() {
1321        override_syscall(|call| match call {
1322            Syscall::Ebpf {
1323                cmd: bpf_cmd::BPF_MAP_CREATE,
1324                ..
1325            } => Ok(crate::MockableFd::mock_signed_fd().into()),
1326            _ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
1327        });
1328
1329        let nr_cpus = nr_cpus().unwrap();
1330
1331        // Create with max_entries > nr_cpus is clamped to nr_cpus
1332        assert_matches!(
1333            MapData::create(test_utils::new_obj_map_with_max_entries::<u32>(
1334                bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
1335                65535,
1336            ), "foo", None),
1337            Ok(MapData {
1338                obj,
1339                fd,
1340            }) => {
1341                assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd());
1342                assert_eq!(obj.max_entries(), nr_cpus as u32)
1343            }
1344        );
1345
1346        // Create with max_entries = 0 is set to nr_cpus
1347        assert_matches!(
1348            MapData::create(test_utils::new_obj_map_with_max_entries::<u32>(
1349                bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
1350                0,
1351            ), "foo", None),
1352            Ok(MapData {
1353                obj,
1354                fd,
1355            }) => {
1356                assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd());
1357                assert_eq!(obj.max_entries(), nr_cpus as u32)
1358            }
1359        );
1360
1361        // Create with max_entries < nr_cpus is unchanged
1362        assert_matches!(
1363            MapData::create(test_utils::new_obj_map_with_max_entries::<u32>(
1364                bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
1365                1,
1366            ), "foo", None),
1367            Ok(MapData {
1368                obj,
1369                fd,
1370            }) => {
1371                assert_eq!(fd.as_fd().as_raw_fd(), crate::MockableFd::mock_signed_fd());
1372                assert_eq!(obj.max_entries(), 1)
1373            }
1374        );
1375    }
1376
1377    #[test]
1378    fn test_name() {
1379        const TEST_NAME: &str = "foo";
1380
1381        override_syscall(|call| match call {
1382            Syscall::Ebpf {
1383                cmd: bpf_cmd::BPF_MAP_CREATE,
1384                ..
1385            } => Ok(crate::MockableFd::mock_signed_fd().into()),
1386            Syscall::Ebpf {
1387                cmd: bpf_cmd::BPF_OBJ_GET_INFO_BY_FD,
1388                attr,
1389            } => {
1390                assert_eq!(
1391                    unsafe { attr.info.info_len },
1392                    size_of::<bpf_map_info>() as u32
1393                );
1394                unsafe {
1395                    let name_bytes = std::mem::transmute::<&[u8], &[c_char]>(TEST_NAME.as_bytes());
1396                    let map_info = attr.info.info as *mut bpf_map_info;
1397                    map_info.write({
1398                        let mut map_info = map_info.read();
1399                        map_info.name[..name_bytes.len()].copy_from_slice(name_bytes);
1400                        map_info
1401                    })
1402                }
1403                Ok(0)
1404            }
1405            _ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
1406        });
1407
1408        let map_data = MapData::create(new_obj_map(), TEST_NAME, None).unwrap();
1409        assert_eq!(TEST_NAME, map_data.info().unwrap().name_as_str().unwrap());
1410    }
1411
1412    #[test]
1413    fn test_loaded_maps() {
1414        override_syscall(|call| match call {
1415            Syscall::Ebpf {
1416                cmd: bpf_cmd::BPF_MAP_GET_NEXT_ID,
1417                attr,
1418            } => unsafe {
1419                let id = attr.__bindgen_anon_6.__bindgen_anon_1.start_id;
1420                if id < 5 {
1421                    attr.__bindgen_anon_6.next_id = id + 1;
1422                    Ok(0)
1423                } else {
1424                    Err((-1, io::Error::from_raw_os_error(libc::ENOENT)))
1425                }
1426            },
1427            Syscall::Ebpf {
1428                cmd: bpf_cmd::BPF_MAP_GET_FD_BY_ID,
1429                attr,
1430            } => Ok((unsafe { attr.__bindgen_anon_6.__bindgen_anon_1.map_id }
1431                + crate::MockableFd::mock_unsigned_fd())
1432            .into()),
1433            Syscall::Ebpf {
1434                cmd: bpf_cmd::BPF_OBJ_GET_INFO_BY_FD,
1435                attr,
1436            } => {
1437                unsafe {
1438                    let info = attr.info;
1439                    let map_info = info.info as *mut bpf_map_info;
1440                    map_info.write({
1441                        let mut map_info = map_info.read();
1442                        map_info.id = info.bpf_fd - crate::MockableFd::mock_unsigned_fd();
1443                        map_info.key_size = 32;
1444                        map_info.value_size = 64;
1445                        map_info.map_flags = 1234;
1446                        map_info.max_entries = 99;
1447                        map_info
1448                    });
1449                }
1450                Ok(0)
1451            }
1452            _ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
1453        });
1454
1455        assert_eq!(
1456            loaded_maps()
1457                .map(|map_info| {
1458                    let map_info = map_info.unwrap();
1459                    (
1460                        map_info.id(),
1461                        map_info.key_size(),
1462                        map_info.value_size(),
1463                        map_info.map_flags(),
1464                        map_info.max_entries(),
1465                        map_info.fd().unwrap().as_fd().as_raw_fd(),
1466                    )
1467                })
1468                .collect::<Vec<_>>(),
1469            (1..6)
1470                .map(|i: u8| (
1471                    i.into(),
1472                    32,
1473                    64,
1474                    1234,
1475                    99,
1476                    crate::MockableFd::mock_signed_fd() + i32::from(i)
1477                ))
1478                .collect::<Vec<_>>(),
1479        );
1480    }
1481
1482    #[test]
1483    fn test_create_failed() {
1484        override_syscall(|_| Err((-1, io::Error::from_raw_os_error(EFAULT))));
1485
1486        assert_matches!(
1487            MapData::create(new_obj_map(), "foo", None),
1488            Err(MapError::CreateError { name, io_error }) => {
1489                assert_eq!(name, "foo");
1490                assert_eq!(io_error.raw_os_error(), Some(EFAULT));
1491            }
1492        );
1493    }
1494
1495    #[test]
1496    fn test_from_map_data_reuseport_sock_array() {
1497        let map_data = test_utils::new_map(test_utils::new_obj_map::<u32>(
1498            bpf_map_type::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
1499        ));
1500
1501        let map = Map::from_map_data(map_data).unwrap();
1502        let _typed: ReusePortSockArray<_> = map.try_into().unwrap();
1503    }
1504
1505    #[test]
1506    fn test_reuseport_sock_array_pin() {
1507        let map = ReusePortSockArray::new(test_utils::new_map(test_utils::new_obj_map::<u32>(
1508            bpf_map_type::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
1509        )))
1510        .unwrap();
1511
1512        override_syscall(|call| match call {
1513            Syscall::Ebpf {
1514                cmd: bpf_cmd::BPF_OBJ_PIN,
1515                ..
1516            } => Ok(0),
1517            _ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
1518        });
1519
1520        assert_matches!(map.pin("/sys/fs/bpf/socket_map"), Ok(()));
1521    }
1522}