1use 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
106pub trait FromMapData: sealed::FromMapData {}
110
111impl<T: sealed::FromMapData> FromMapData for T {}
112
113pub 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 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 fn fd(&self) -> &MapFd;
133 }
134}
135
136#[derive(Error, Debug)]
137pub enum MapError {
139 #[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 outer_name: String,
147 },
148
149 #[error("invalid map type {map_type}")]
151 InvalidMapType {
152 map_type: u32,
154 },
155
156 #[error("invalid map name `{name}`")]
158 InvalidName {
159 name: String,
161 },
162
163 #[error("failed to create map `{name}`")]
165 CreateError {
166 name: String,
168 #[source]
169 io_error: io::Error,
171 },
172
173 #[error("invalid key size {size}, expected {expected}")]
175 InvalidKeySize {
176 size: usize,
178 expected: usize,
180 },
181
182 #[error("invalid value size {size}, expected {expected}")]
184 InvalidValueSize {
185 size: usize,
187 expected: usize,
189 },
190
191 #[error("invalid value size {size}, expected a non-zero multiple of {stride}")]
193 InvalidValueStride {
194 size: usize,
196 stride: usize,
198 },
199
200 #[error("the index is {index} but `max_entries` is {max_entries}")]
202 OutOfBounds {
203 index: u32,
205 max_entries: u32,
207 },
208
209 #[error("key not found")]
211 KeyNotFound,
212
213 #[error("element not found")]
215 ElementNotFound,
216
217 #[error("the program is not loaded")]
219 ProgramNotLoaded,
220
221 #[error(transparent)]
223 IoError(#[from] io::Error),
224
225 #[error(transparent)]
227 SyscallError(#[from] SyscallError),
228
229 #[error("map `{name:?}` requested pinning. pinning failed")]
231 PinError {
232 name: Option<String>,
234 #[source]
236 error: PinError,
237 },
238
239 #[error("program ids are not supported by the current kernel")]
241 ProgIdNotSupported,
242
243 #[error(
245 "type of {name} ({map_type:?}) is unsupported; see `EbpfLoader::allow_unsupported_maps`"
246 )]
247 Unsupported {
248 name: String,
250 map_type: bpf_map_type,
252 },
253
254 #[error("unsupported map flags {flags:#x}: {reason}")]
256 UnsupportedMapFlags {
257 flags: u32,
259 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#[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#[derive(Debug)]
298pub enum Map {
299 Array(MapData),
301 ArrayOfMaps(MapData),
303 BloomFilter(MapData),
305 CgroupArray(MapData),
307 CpuMap(MapData),
309 DevMap(MapData),
311 DevMapHash(MapData),
313 HashMap(MapData),
315 HashOfMaps(MapData),
317 InodeStorage(MapData),
319 LpmTrie(MapData),
321 LruHashMap(MapData),
323 PerCpuArray(MapData),
325 PerCpuHashMap(MapData),
327 PerCpuLruHashMap(MapData),
329 PerfEventArray(MapData),
331 ProgramArray(MapData),
333 Queue(MapData),
335 ReusePortSockArray(MapData),
337 RingBuf(MapData),
339 SockHash(MapData),
341 SockMap(MapData),
343 SkStorage(MapData),
345 Stack(MapData),
347 StackTraceMap(MapData),
349 Unsupported(MapData),
351 XskMap(MapData),
353}
354
355impl Map {
356 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 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 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
480macro_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 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
535macro_rules! impl_try_from_map {
540 ($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 ($(#[$meta:meta])* <$ty_param:tt> $ty:ident) => {
552 impl_try_from_map!($(#[$meta])* <$ty_param> $ty from $ty);
553 };
554 (
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 (@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
619macro_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
636macro_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
669impl_from_map_data!(() {
673 CpuMap, DevMap, DevMapHash,
674 SockMap, StackTraceMap, XskMap,
675});
676
677impl_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 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#[derive(Debug)]
775pub struct MapData {
776 obj: aya_obj::Map,
777 fd: MapFd,
778}
779
780impl MapData {
781 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 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 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 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 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 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 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 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 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 pub fn info(&self) -> Result<MapInfo, MapError> {
997 MapInfo::new_from_fd(self.fd.as_fd())
998 }
999}
1000
1001pub trait IterableMap<K: Pod, V> {
1003 fn map(&self) -> &MapData;
1005
1006 fn get(&self, key: &K) -> Result<V, MapError>;
1008}
1009
1010pub 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
1053pub 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#[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 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 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 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}