Skip to main content

libbpf_rs/
map.rs

1use core::ffi::c_void;
2use std::ffi::CStr;
3use std::ffi::CString;
4use std::ffi::OsStr;
5use std::ffi::OsString;
6use std::fmt::Debug;
7use std::fs::remove_file;
8use std::fs::File;
9use std::io;
10use std::io::BufRead as _;
11use std::io::BufReader;
12use std::marker::PhantomData;
13use std::mem;
14use std::mem::transmute;
15use std::ops::Deref;
16use std::os::unix::ffi::OsStrExt;
17use std::os::unix::io::AsFd;
18use std::os::unix::io::AsRawFd;
19use std::os::unix::io::BorrowedFd;
20use std::os::unix::io::FromRawFd;
21use std::os::unix::io::OwnedFd;
22use std::os::unix::io::RawFd;
23use std::path::Path;
24use std::ptr;
25use std::ptr::NonNull;
26use std::slice;
27use std::slice::from_raw_parts;
28
29use bitflags::bitflags;
30use libbpf_sys::bpf_map_info;
31use libbpf_sys::bpf_obj_get_info_by_fd;
32
33use crate::error;
34use crate::util;
35use crate::util::parse_ret_i32;
36use crate::util::validate_bpf_ret;
37use crate::AsRawLibbpf;
38use crate::Error;
39use crate::ErrorExt as _;
40use crate::Link;
41use crate::Mut;
42use crate::ProgramType;
43use crate::Result;
44
45/// An immutable parsed but not yet loaded BPF map.
46pub type OpenMap<'obj> = OpenMapImpl<'obj>;
47/// A mutable parsed but not yet loaded BPF map.
48pub type OpenMapMut<'obj> = OpenMapImpl<'obj, Mut>;
49
50/// Represents a parsed but not yet loaded BPF map.
51///
52/// This object exposes operations that need to happen before the map is created.
53///
54/// Some methods require working with raw bytes. You may find libraries such as
55/// [`plain`](https://crates.io/crates/plain) helpful.
56#[derive(Debug)]
57#[repr(transparent)]
58#[doc(alias = "bpf_map")]
59pub struct OpenMapImpl<'obj, T = ()> {
60    ptr: NonNull<libbpf_sys::bpf_map>,
61    _phantom: PhantomData<&'obj T>,
62}
63
64impl<'obj> OpenMap<'obj> {
65    /// Create a new [`OpenMap`] from a ptr to a `libbpf_sys::bpf_map`.
66    pub fn new(object: &'obj libbpf_sys::bpf_map) -> Self {
67        // SAFETY: We inferred the address from a reference, which is always
68        //         valid.
69        Self {
70            ptr: unsafe { NonNull::new_unchecked(object as *const _ as *mut _) },
71            _phantom: PhantomData,
72        }
73    }
74
75    /// Retrieve the [`OpenMap`]'s name.
76    #[doc(alias = "bpf_map__name")]
77    pub fn name(&self) -> &'obj OsStr {
78        // SAFETY: We ensured `ptr` is valid during construction.
79        let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
80        // SAFETY: `bpf_map__name` can return NULL but only if it's passed
81        //          NULL. We know `ptr` is not NULL.
82        let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
83        OsStr::from_bytes(name_c_str.to_bytes())
84    }
85
86    /// Retrieve type of the map.
87    #[doc(alias = "bpf_map__type")]
88    pub fn map_type(&self) -> MapType {
89        let ty = unsafe { libbpf_sys::bpf_map__type(self.ptr.as_ptr()) };
90        MapType::from(ty)
91    }
92
93    fn initial_value_raw(&self) -> (*mut u8, usize) {
94        let mut size = 0u64;
95        let ptr = unsafe {
96            libbpf_sys::bpf_map__initial_value(self.ptr.as_ptr(), (&raw mut size).cast())
97        };
98        (ptr.cast(), size as _)
99    }
100
101    /// Retrieve the initial value of the map.
102    #[doc(alias = "bpf_map__initial_value")]
103    pub fn initial_value(&self) -> Option<&[u8]> {
104        let (ptr, size) = self.initial_value_raw();
105        if ptr.is_null() {
106            None
107        } else {
108            let data = unsafe { slice::from_raw_parts(ptr.cast::<u8>(), size) };
109            Some(data)
110        }
111    }
112
113    /// Retrieve the maximum number of entries of the map.
114    #[doc(alias = "bpf_map__max_entries")]
115    pub fn max_entries(&self) -> u32 {
116        unsafe { libbpf_sys::bpf_map__max_entries(self.ptr.as_ptr()) }
117    }
118
119    /// Return `true` if the map is set to be auto-created during load, `false` otherwise.
120    #[doc(alias = "bpf_map__autocreate")]
121    pub fn autocreate(&self) -> bool {
122        unsafe { libbpf_sys::bpf_map__autocreate(self.ptr.as_ptr()) }
123    }
124
125    /// Retrieve the map flags.
126    #[doc(alias = "bpf_map__map_flags")]
127    pub fn map_flags(&self) -> u32 {
128        unsafe { libbpf_sys::bpf_map__map_flags(self.ptr.as_ptr()) }
129    }
130
131    /// Retrieve the map numa node.
132    #[doc(alias = "bpf_map__numa_node")]
133    pub fn numa_node(&self) -> u32 {
134        unsafe { libbpf_sys::bpf_map__numa_node(self.ptr.as_ptr()) }
135    }
136
137    /// Retrieve the key size of the map in bytes.
138    #[doc(alias = "bpf_map__key_size")]
139    pub fn key_size(&self) -> u32 {
140        unsafe { libbpf_sys::bpf_map__key_size(self.ptr.as_ptr()) }
141    }
142
143    /// Retrieve the value size of the map in bytes.
144    #[doc(alias = "bpf_map__value_size")]
145    pub fn value_size(&self) -> u32 {
146        unsafe { libbpf_sys::bpf_map__value_size(self.ptr.as_ptr()) }
147    }
148}
149
150impl<'obj> OpenMapMut<'obj> {
151    /// Create a new [`OpenMapMut`] from a ptr to a `libbpf_sys::bpf_map`.
152    pub fn new_mut(object: &'obj mut libbpf_sys::bpf_map) -> Self {
153        Self {
154            ptr: unsafe { NonNull::new_unchecked(object as *mut _) },
155            _phantom: PhantomData,
156        }
157    }
158
159    /// Retrieve the initial value of the map.
160    #[doc(alias = "bpf_map__initial_value")]
161    pub fn initial_value_mut(&mut self) -> Option<&mut [u8]> {
162        let (ptr, size) = self.initial_value_raw();
163        if ptr.is_null() {
164            None
165        } else {
166            let data = unsafe { slice::from_raw_parts_mut(ptr.cast::<u8>(), size) };
167            Some(data)
168        }
169    }
170
171    /// Bind map to a particular network device.
172    ///
173    /// Used for offloading maps to hardware.
174    #[doc(alias = "bpf_map__set_ifindex")]
175    pub fn set_map_ifindex(&mut self, idx: u32) {
176        unsafe { libbpf_sys::bpf_map__set_ifindex(self.ptr.as_ptr(), idx) };
177    }
178
179    /// Set the initial value of the map.
180    #[doc(alias = "bpf_map__set_initial_value")]
181    pub fn set_initial_value(&mut self, data: &[u8]) -> Result<()> {
182        let ret = unsafe {
183            libbpf_sys::bpf_map__set_initial_value(
184                self.ptr.as_ptr(),
185                data.as_ptr().cast::<c_void>(),
186                data.len() as libbpf_sys::size_t,
187            )
188        };
189
190        util::parse_ret(ret)
191    }
192
193    /// Set the type of the map.
194    #[doc(alias = "bpf_map__set_type")]
195    pub fn set_type(&mut self, ty: MapType) -> Result<()> {
196        let ret = unsafe { libbpf_sys::bpf_map__set_type(self.ptr.as_ptr(), ty as u32) };
197        util::parse_ret(ret)
198    }
199
200    /// Set the key size of the map in bytes.
201    #[doc(alias = "bpf_map__set_key_size")]
202    pub fn set_key_size(&mut self, size: u32) -> Result<()> {
203        let ret = unsafe { libbpf_sys::bpf_map__set_key_size(self.ptr.as_ptr(), size) };
204        util::parse_ret(ret)
205    }
206
207    /// Set the value size of the map in bytes.
208    #[doc(alias = "bpf_map__set_value_size")]
209    pub fn set_value_size(&mut self, size: u32) -> Result<()> {
210        let ret = unsafe { libbpf_sys::bpf_map__set_value_size(self.ptr.as_ptr(), size) };
211        util::parse_ret(ret)
212    }
213
214    /// Set the maximum number of entries this map can have.
215    #[doc(alias = "bpf_map__set_max_entries")]
216    pub fn set_max_entries(&mut self, count: u32) -> Result<()> {
217        let ret = unsafe { libbpf_sys::bpf_map__set_max_entries(self.ptr.as_ptr(), count) };
218        util::parse_ret(ret)
219    }
220
221    /// Set flags on this map.
222    #[doc(alias = "bpf_map__set_map_flags")]
223    pub fn set_map_flags(&mut self, flags: u32) -> Result<()> {
224        let ret = unsafe { libbpf_sys::bpf_map__set_map_flags(self.ptr.as_ptr(), flags) };
225        util::parse_ret(ret)
226    }
227
228    /// Set the NUMA node for this map.
229    ///
230    /// This can be used to ensure that the map is allocated on a particular
231    /// NUMA node, which can be useful for performance-critical applications.
232    #[doc(alias = "bpf_map__set_numa_node")]
233    pub fn set_numa_node(&mut self, numa_node: u32) -> Result<()> {
234        let ret = unsafe { libbpf_sys::bpf_map__set_numa_node(self.ptr.as_ptr(), numa_node) };
235        util::parse_ret(ret)
236    }
237
238    /// Set the inner map FD.
239    ///
240    /// This is used for nested maps, where the value type of the outer map is a pointer to the
241    /// inner map.
242    #[doc(alias = "bpf_map__set_inner_map_fd")]
243    pub fn set_inner_map_fd(&mut self, inner_map_fd: BorrowedFd<'_>) -> Result<()> {
244        let ret = unsafe {
245            libbpf_sys::bpf_map__set_inner_map_fd(self.ptr.as_ptr(), inner_map_fd.as_raw_fd())
246        };
247        util::parse_ret(ret)
248    }
249
250    /// Set the `map_extra` field for this map.
251    ///
252    /// Allows users to pass additional data to the
253    /// kernel when loading the map. The kernel will store this value in the
254    /// `bpf_map_info` struct associated with the map.
255    ///
256    /// This can be used to pass data to the kernel that is not otherwise
257    /// representable via the existing `bpf_map_def` fields.
258    #[doc(alias = "bpf_map__set_map_extra")]
259    pub fn set_map_extra(&mut self, map_extra: u64) -> Result<()> {
260        let ret = unsafe { libbpf_sys::bpf_map__set_map_extra(self.ptr.as_ptr(), map_extra) };
261        util::parse_ret(ret)
262    }
263
264    /// Set whether or not libbpf should automatically create this map during load phase.
265    #[doc(alias = "bpf_map__set_autocreate")]
266    pub fn set_autocreate(&mut self, autocreate: bool) -> Result<()> {
267        let ret = unsafe { libbpf_sys::bpf_map__set_autocreate(self.ptr.as_ptr(), autocreate) };
268        util::parse_ret(ret)
269    }
270
271    /// Set where the map should be pinned.
272    ///
273    /// Note this does not actually create the pin.
274    #[doc(alias = "bpf_map__set_pin_path")]
275    pub fn set_pin_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
276        let path_c = util::path_to_cstring(path)?;
277        let path_ptr = path_c.as_ptr();
278
279        let ret = unsafe { libbpf_sys::bpf_map__set_pin_path(self.ptr.as_ptr(), path_ptr) };
280        util::parse_ret(ret)
281    }
282
283    /// Reuse an fd for a BPF map
284    #[doc(alias = "bpf_map__reuse_fd")]
285    pub fn reuse_fd(&mut self, fd: BorrowedFd<'_>) -> Result<()> {
286        let ret = unsafe { libbpf_sys::bpf_map__reuse_fd(self.ptr.as_ptr(), fd.as_raw_fd()) };
287        util::parse_ret(ret)
288    }
289
290    /// Reuse an already-pinned map for `self`.
291    #[doc(alias = "bpf_obj_get")]
292    pub fn reuse_pinned_map<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
293        let cstring = util::path_to_cstring(path)?;
294
295        let fd = unsafe { libbpf_sys::bpf_obj_get(cstring.as_ptr()) };
296        if fd < 0 {
297            return Err(Error::from(io::Error::last_os_error()));
298        }
299
300        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
301
302        let reuse_result = self.reuse_fd(fd.as_fd());
303
304        reuse_result
305    }
306}
307
308impl<'obj> Deref for OpenMapMut<'obj> {
309    type Target = OpenMap<'obj>;
310
311    fn deref(&self) -> &Self::Target {
312        // SAFETY: `OpenMapImpl` is `repr(transparent)` and so in-memory
313        //         representation of both types is the same.
314        unsafe { transmute::<&OpenMapMut<'obj>, &OpenMap<'obj>>(self) }
315    }
316}
317
318impl<T> AsRawLibbpf for OpenMapImpl<'_, T> {
319    type LibbpfType = libbpf_sys::bpf_map;
320
321    /// Retrieve the underlying [`libbpf_sys::bpf_map`].
322    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
323        self.ptr
324    }
325}
326
327pub(crate) fn map_fd(map: NonNull<libbpf_sys::bpf_map>) -> Option<RawFd> {
328    let fd = unsafe { libbpf_sys::bpf_map__fd(map.as_ptr()) };
329    let fd = util::parse_ret_i32(fd).ok();
330    fd
331}
332
333/// Return the size of one value including padding for interacting with per-cpu
334/// maps. The values are aligned to 8 bytes.
335fn percpu_aligned_value_size<M>(map: &M) -> usize
336where
337    M: MapCore + ?Sized,
338{
339    let val_size = map.value_size() as usize;
340    util::roundup(val_size, 8)
341}
342
343/// Returns the size of the buffer needed for a lookup/update of a per-cpu map.
344fn percpu_buffer_size<M>(map: &M) -> Result<usize>
345where
346    M: MapCore + ?Sized,
347{
348    let aligned_val_size = percpu_aligned_value_size(map);
349    let ncpu = crate::num_possible_cpus()?;
350    Ok(ncpu * aligned_val_size)
351}
352
353/// Apply a key check and return a null pointer in case of dealing with queue/stack/bloom-filter
354/// map, before passing the key to the bpf functions that support the map of type
355/// queue/stack/bloom-filter.
356fn map_key<M>(map: &M, key: &[u8]) -> *const c_void
357where
358    M: MapCore + ?Sized,
359{
360    // For all they keyless maps we null out the key per documentation of libbpf
361    if map.key_size() == 0 && map.map_type().is_keyless() {
362        return ptr::null();
363    }
364
365    key.as_ptr().cast::<c_void>()
366}
367
368/// Internal selector for which underlying lookup operation to perform.
369///
370/// `bpf_map_lookup_elem_flags` accepts `MapFlags`, while
371/// `bpf_map_lookup_and_delete_elem` does not. Wrapping the choice in an
372/// enum keeps the two cases distinct without leaking an unused `flags`
373/// argument into the lookup-and-delete path.
374enum LookupOp {
375    Lookup(MapFlags),
376    LookupAndDelete,
377}
378
379/// Internal function to perform a map lookup and write the value into raw pointer.
380/// Returns `Ok(true)` if the key was found, `Ok(false)` if not found, or an error.
381fn lookup_raw<M>(
382    map: &M,
383    key: &[u8],
384    value: &mut [mem::MaybeUninit<u8>],
385    op: LookupOp,
386) -> Result<bool>
387where
388    M: MapCore + ?Sized,
389{
390    if key.len() != map.key_size() as usize {
391        return Err(Error::with_invalid_data(format!(
392            "key_size {} != {}",
393            key.len(),
394            map.key_size()
395        )));
396    }
397
398    // Make sure the internal users of this function pass the expected buffer size
399    debug_assert_eq!(
400        value.len(),
401        if map.map_type().is_percpu() {
402            percpu_buffer_size(map).unwrap()
403        } else {
404            map.value_size() as usize
405        }
406    );
407
408    let ret = unsafe {
409        match op {
410            LookupOp::Lookup(flags) => libbpf_sys::bpf_map_lookup_elem_flags(
411                map.as_fd().as_raw_fd(),
412                map_key(map, key),
413                // TODO: Use `MaybeUninit::slice_as_mut_ptr` once stable.
414                value.as_mut_ptr().cast(),
415                flags.bits(),
416            ),
417            LookupOp::LookupAndDelete => libbpf_sys::bpf_map_lookup_and_delete_elem(
418                map.as_fd().as_raw_fd(),
419                map_key(map, key),
420                value.as_mut_ptr().cast(),
421            ),
422        }
423    };
424
425    if ret == 0 {
426        Ok(true)
427    } else {
428        let err = io::Error::last_os_error();
429        if err.kind() == io::ErrorKind::NotFound {
430            Ok(false)
431        } else {
432            Err(Error::from(err))
433        }
434    }
435}
436
437/// Internal function to return a value from a map into a buffer of the given size.
438fn lookup_raw_vec<M>(map: &M, key: &[u8], op: LookupOp, out_size: usize) -> Result<Option<Vec<u8>>>
439where
440    M: MapCore + ?Sized,
441{
442    // Allocate without initializing (avoiding memset)
443    let mut out = Vec::with_capacity(out_size);
444
445    match lookup_raw(map, key, out.spare_capacity_mut(), op)? {
446        true => {
447            // SAFETY: `lookup_raw` successfully filled the buffer
448            unsafe {
449                out.set_len(out_size);
450            }
451            Ok(Some(out))
452        }
453        false => Ok(None),
454    }
455}
456
457/// Internal function to update a map. This does not check the length of the
458/// supplied value.
459fn update_raw<M>(map: &M, key: &[u8], value: &[u8], flags: MapFlags) -> Result<()>
460where
461    M: MapCore + ?Sized,
462{
463    if key.len() != map.key_size() as usize {
464        return Err(Error::with_invalid_data(format!(
465            "key_size {} != {}",
466            key.len(),
467            map.key_size()
468        )));
469    };
470
471    let ret = unsafe {
472        libbpf_sys::bpf_map_update_elem(
473            map.as_fd().as_raw_fd(),
474            map_key(map, key),
475            value.as_ptr().cast::<c_void>(),
476            flags.bits(),
477        )
478    };
479
480    util::parse_ret(ret)
481}
482
483/// Internal function to batch lookup (and delete) elements from a map.
484fn lookup_batch_raw<M>(
485    map: &M,
486    count: u32,
487    elem_flags: MapFlags,
488    flags: MapFlags,
489    delete: bool,
490) -> BatchedMapIter<'_>
491where
492    M: MapCore + ?Sized,
493{
494    #[allow(clippy::needless_update)]
495    let opts = libbpf_sys::bpf_map_batch_opts {
496        sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
497        elem_flags: elem_flags.bits(),
498        flags: flags.bits(),
499        // bpf_map_batch_opts might have padding fields on some platform
500        ..Default::default()
501    };
502
503    // for maps of type BPF_MAP_TYPE_{HASH, PERCPU_HASH, LRU_HASH, LRU_PERCPU_HASH}
504    // the key size must be at least 4 bytes
505    let key_size = if map.map_type().is_hash_map() {
506        map.key_size().max(4)
507    } else {
508        map.key_size()
509    };
510
511    BatchedMapIter::new(map.as_fd(), count, key_size, map.value_size(), opts, delete)
512}
513
514/// Intneral function that returns an error for per-cpu and bloom filter maps.
515fn check_not_bloom_or_percpu<M>(map: &M) -> Result<()>
516where
517    M: MapCore + ?Sized,
518{
519    if map.map_type().is_bloom_filter() {
520        return Err(Error::with_invalid_data(
521            "lookup_bloom_filter() must be used for bloom filter maps",
522        ));
523    }
524    if map.map_type().is_percpu() {
525        return Err(Error::with_invalid_data(format!(
526            "lookup_percpu() must be used for per-cpu maps (type of the map is {:?})",
527            map.map_type(),
528        )));
529    }
530
531    Ok(())
532}
533
534#[allow(clippy::wildcard_imports)]
535mod private {
536    use super::*;
537
538    pub trait Sealed {}
539
540    impl<T> Sealed for MapImpl<'_, T> {}
541    impl Sealed for MapHandle {}
542}
543
544/// A trait representing core functionality common to fully initialized maps.
545pub trait MapCore: Debug + AsFd + private::Sealed {
546    /// Retrieve the map's name.
547    #[doc(alias = "bpf_map__name")]
548    fn name(&self) -> &OsStr;
549
550    /// Retrieve type of the map.
551    #[doc(alias = "bpf_map__type")]
552    fn map_type(&self) -> MapType;
553
554    /// Retrieve the size of the map's keys.
555    #[doc(alias = "bpf_map__key_size")]
556    fn key_size(&self) -> u32;
557
558    /// Retrieve the size of the map's values.
559    #[doc(alias = "bpf_map__value_size")]
560    fn value_size(&self) -> u32;
561
562    /// Retrieve `max_entries` of the map.
563    #[doc(alias = "bpf_map__max_entries")]
564    fn max_entries(&self) -> u32;
565
566    /// Fetch extra map information
567    #[inline]
568    #[doc(alias = "bpf_obj_get_info_by_fd")]
569    fn info(&self) -> Result<MapInfo> {
570        MapInfo::new(self.as_fd())
571    }
572
573    /// Query map information from `/proc/self/fdinfo`.
574    ///
575    /// This provides information not available through [`MapInfo`],
576    /// such as [`memlock`][MapFdInfo::memlock] (memory usage).
577    #[inline]
578    fn query_fdinfo(&self) -> Result<MapFdInfo> {
579        MapFdInfo::from_fd(self.as_fd())
580    }
581
582    /// Returns an iterator over keys in this map
583    ///
584    /// Note that if the map is not stable (stable meaning no updates or deletes) during iteration,
585    /// iteration can skip keys, restart from the beginning, or duplicate keys. In other words,
586    /// iteration becomes unpredictable.
587    #[doc(alias = "bpf_map_get_next_key")]
588    fn keys(&self) -> MapKeyIter<'_> {
589        MapKeyIter::new(self.as_fd(), self.key_size())
590    }
591
592    /// Returns map value as `Vec` of `u8`.
593    ///
594    /// `key` must have exactly [`Self::key_size()`] elements.
595    ///
596    /// If the map is one of the per-cpu data structures, the function [`Self::lookup_percpu()`]
597    /// must be used.
598    /// If the map is of type `bloom_filter` the function [`Self::lookup_bloom_filter()`] must be
599    /// used
600    #[doc(alias = "bpf_map_lookup_elem_flags")]
601    fn lookup(&self, key: &[u8], flags: MapFlags) -> Result<Option<Vec<u8>>> {
602        check_not_bloom_or_percpu(self)?;
603        let out_size = self.value_size() as usize;
604        lookup_raw_vec(self, key, LookupOp::Lookup(flags), out_size)
605    }
606
607    /// Looks up a map value into a pre-allocated buffer, avoiding allocation.
608    ///
609    /// This method provides a zero-allocation alternative to [`Self::lookup()`].
610    ///
611    /// `key` must have exactly [`Self::key_size()`] elements.
612    /// `value` must have exactly [`Self::value_size()`] elements.
613    ///
614    /// Returns `Ok(true)` if the key was found and the buffer was filled,
615    /// `Ok(false)` if the key was not found, or an error.
616    ///
617    /// If the map is one of the per-cpu data structures, this function cannot be used.
618    /// If the map is of type `bloom_filter`, this function cannot be used.
619    #[doc(alias = "bpf_map_lookup_elem_flags")]
620    fn lookup_into(&self, key: &[u8], value: &mut [u8], flags: MapFlags) -> Result<bool> {
621        check_not_bloom_or_percpu(self)?;
622
623        if value.len() != self.value_size() as usize {
624            return Err(Error::with_invalid_data(format!(
625                "value buffer size {} != {}",
626                value.len(),
627                self.value_size()
628            )));
629        }
630
631        // SAFETY: `u8` and `MaybeUninit<u8>` have the same in-memory representation.
632        let value = unsafe {
633            slice::from_raw_parts_mut::<mem::MaybeUninit<u8>>(
634                value.as_mut_ptr().cast(),
635                value.len(),
636            )
637        };
638        lookup_raw(self, key, value, LookupOp::Lookup(flags))
639    }
640
641    /// Returns many elements in batch mode from the map.
642    ///
643    /// `count` specifies the batch size.
644    #[doc(alias = "bpf_map_lookup_batch")]
645    fn lookup_batch(
646        &self,
647        count: u32,
648        elem_flags: MapFlags,
649        flags: MapFlags,
650    ) -> Result<BatchedMapIter<'_>> {
651        check_not_bloom_or_percpu(self)?;
652        Ok(lookup_batch_raw(self, count, elem_flags, flags, false))
653    }
654
655    /// Returns many elements in batch mode from the map.
656    ///
657    /// `count` specifies the batch size.
658    #[doc(alias = "bpf_map_lookup_and_delete_batch")]
659    fn lookup_and_delete_batch(
660        &self,
661        count: u32,
662        elem_flags: MapFlags,
663        flags: MapFlags,
664    ) -> Result<BatchedMapIter<'_>> {
665        check_not_bloom_or_percpu(self)?;
666        Ok(lookup_batch_raw(self, count, elem_flags, flags, true))
667    }
668
669    /// Returns if the given value is likely present in `bloom_filter` as `bool`.
670    ///
671    /// `value` must have exactly [`Self::value_size()`] elements.
672    #[doc(alias = "bpf_map_lookup_elem")]
673    fn lookup_bloom_filter(&self, value: &[u8]) -> Result<bool> {
674        let ret = unsafe {
675            libbpf_sys::bpf_map_lookup_elem(
676                self.as_fd().as_raw_fd(),
677                ptr::null(),
678                value.to_vec().as_mut_ptr().cast::<c_void>(),
679            )
680        };
681
682        if ret == 0 {
683            Ok(true)
684        } else {
685            let err = io::Error::last_os_error();
686            if err.kind() == io::ErrorKind::NotFound {
687                Ok(false)
688            } else {
689                Err(Error::from(err))
690            }
691        }
692    }
693
694    /// Returns one value per cpu as `Vec` of `Vec` of `u8` for per per-cpu maps.
695    ///
696    /// For normal maps, [`Self::lookup()`] must be used.
697    #[doc(alias = "bpf_map_lookup_elem_flags")]
698    fn lookup_percpu(&self, key: &[u8], flags: MapFlags) -> Result<Option<Vec<Vec<u8>>>> {
699        if !self.map_type().is_percpu() && self.map_type() != MapType::Unknown {
700            return Err(Error::with_invalid_data(format!(
701                "lookup() must be used for maps that are not per-cpu (type of the map is {:?})",
702                self.map_type(),
703            )));
704        }
705
706        let val_size = self.value_size() as usize;
707        let aligned_val_size = percpu_aligned_value_size(self);
708        let out_size = percpu_buffer_size(self)?;
709
710        let raw_res = lookup_raw_vec(self, key, LookupOp::Lookup(flags), out_size)?;
711        if let Some(raw_vals) = raw_res {
712            let mut out = Vec::new();
713            for chunk in raw_vals.chunks_exact(aligned_val_size) {
714                out.push(chunk[..val_size].to_vec());
715            }
716            Ok(Some(out))
717        } else {
718            Ok(None)
719        }
720    }
721
722    /// Deletes an element from the map.
723    ///
724    /// `key` must have exactly [`Self::key_size()`] elements.
725    #[doc(alias = "bpf_map_delete_elem")]
726    fn delete(&self, key: &[u8]) -> Result<()> {
727        if key.len() != self.key_size() as usize {
728            return Err(Error::with_invalid_data(format!(
729                "key_size {} != {}",
730                key.len(),
731                self.key_size()
732            )));
733        };
734
735        let ret = unsafe {
736            libbpf_sys::bpf_map_delete_elem(self.as_fd().as_raw_fd(), key.as_ptr().cast::<c_void>())
737        };
738        util::parse_ret(ret)
739    }
740
741    /// Deletes many elements in batch mode from the map.
742    ///
743    /// `keys` must have exactly `Self::key_size() * count` elements.
744    #[doc(alias = "bpf_map_delete_batch")]
745    fn delete_batch(
746        &self,
747        keys: &[u8],
748        count: u32,
749        elem_flags: MapFlags,
750        flags: MapFlags,
751    ) -> Result<()> {
752        if keys.len() as u32 / count != self.key_size() || (keys.len() as u32) % count != 0 {
753            return Err(Error::with_invalid_data(format!(
754                "batch key_size {} != {} * {}",
755                keys.len(),
756                self.key_size(),
757                count
758            )));
759        };
760
761        #[allow(clippy::needless_update)]
762        let opts = libbpf_sys::bpf_map_batch_opts {
763            sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
764            elem_flags: elem_flags.bits(),
765            flags: flags.bits(),
766            // bpf_map_batch_opts might have padding fields on some platform
767            ..Default::default()
768        };
769
770        let mut count = count;
771        let ret = unsafe {
772            libbpf_sys::bpf_map_delete_batch(
773                self.as_fd().as_raw_fd(),
774                keys.as_ptr().cast::<c_void>(),
775                &mut count,
776                &opts as *const libbpf_sys::bpf_map_batch_opts,
777            )
778        };
779        util::parse_ret(ret)
780    }
781
782    /// Same as [`Self::lookup()`] except this also deletes the key from the map.
783    ///
784    /// Implemented in the kernel for [`MapType::Queue`] and [`MapType::Stack`],
785    /// and (since Linux 5.14) for [`MapType::Hash`] / [`MapType::LruHash`] /
786    /// [`MapType::PercpuHash`] / [`MapType::LruPercpuHash`].
787    ///
788    /// `key` must have exactly [`Self::key_size()`] elements.
789    #[doc(alias = "bpf_map_lookup_and_delete_elem")]
790    fn lookup_and_delete(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
791        let out_size = self.value_size() as usize;
792        lookup_raw_vec(self, key, LookupOp::LookupAndDelete, out_size)
793    }
794
795    /// Same as [`Self::lookup_into()`] except this also deletes the key from the map.
796    ///
797    /// This method provides a zero-allocation alternative to [`Self::lookup_and_delete()`].
798    ///
799    /// `key` must have exactly [`Self::key_size()`] elements.
800    /// `value` must have exactly [`Self::value_size()`] elements.
801    ///
802    /// Returns `Ok(true)` if the key was found and the buffer was filled,
803    /// `Ok(false)` if the key was not found, or an error.
804    ///
805    /// See [`Self::lookup_and_delete()`] for kernel support details.
806    #[doc(alias = "bpf_map_lookup_and_delete_elem")]
807    fn lookup_into_and_delete(&self, key: &[u8], value: &mut [u8]) -> Result<bool> {
808        if value.len() != self.value_size() as usize {
809            return Err(Error::with_invalid_data(format!(
810                "value buffer size {} != {}",
811                value.len(),
812                self.value_size()
813            )));
814        }
815
816        // SAFETY: `u8` and `MaybeUninit<u8>` have the same in-memory representation.
817        let value = unsafe {
818            slice::from_raw_parts_mut::<mem::MaybeUninit<u8>>(
819                value.as_mut_ptr().cast(),
820                value.len(),
821            )
822        };
823        lookup_raw(self, key, value, LookupOp::LookupAndDelete)
824    }
825
826    /// Update an element.
827    ///
828    /// `key` must have exactly [`Self::key_size()`] elements. `value` must have exactly
829    /// [`Self::value_size()`] elements.
830    ///
831    /// For per-cpu maps, [`Self::update_percpu()`] must be used.
832    #[doc(alias = "bpf_map_update_elem")]
833    fn update(&self, key: &[u8], value: &[u8], flags: MapFlags) -> Result<()> {
834        if self.map_type().is_percpu() {
835            return Err(Error::with_invalid_data(format!(
836                "update_percpu() must be used for per-cpu maps (type of the map is {:?})",
837                self.map_type(),
838            )));
839        }
840
841        if value.len() != self.value_size() as usize {
842            return Err(Error::with_invalid_data(format!(
843                "value_size {} != {}",
844                value.len(),
845                self.value_size()
846            )));
847        };
848
849        update_raw(self, key, value, flags)
850    }
851
852    /// Updates many elements in batch mode in the map
853    ///
854    /// `keys` must have exactly `Self::key_size() * count` elements. `values` must have exactly
855    /// `Self::key_size() * count` elements.
856    #[doc(alias = "bpf_map_update_batch")]
857    fn update_batch(
858        &self,
859        keys: &[u8],
860        values: &[u8],
861        count: u32,
862        elem_flags: MapFlags,
863        flags: MapFlags,
864    ) -> Result<()> {
865        if keys.len() as u32 / count != self.key_size() || (keys.len() as u32) % count != 0 {
866            return Err(Error::with_invalid_data(format!(
867                "batch key_size {} != {} * {}",
868                keys.len(),
869                self.key_size(),
870                count
871            )));
872        };
873
874        if values.len() as u32 / count != self.value_size() || (values.len() as u32) % count != 0 {
875            return Err(Error::with_invalid_data(format!(
876                "batch value_size {} != {} * {}",
877                values.len(),
878                self.value_size(),
879                count
880            )));
881        }
882
883        #[allow(clippy::needless_update)]
884        let opts = libbpf_sys::bpf_map_batch_opts {
885            sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
886            elem_flags: elem_flags.bits(),
887            flags: flags.bits(),
888            // bpf_map_batch_opts might have padding fields on some platform
889            ..Default::default()
890        };
891
892        let mut count = count;
893        let ret = unsafe {
894            libbpf_sys::bpf_map_update_batch(
895                self.as_fd().as_raw_fd(),
896                keys.as_ptr().cast::<c_void>(),
897                values.as_ptr().cast::<c_void>(),
898                &mut count,
899                &opts as *const libbpf_sys::bpf_map_batch_opts,
900            )
901        };
902
903        util::parse_ret(ret)
904    }
905
906    /// Update an element in an per-cpu map with one value per cpu.
907    ///
908    /// `key` must have exactly [`Self::key_size()`] elements. `value` must have one
909    /// element per cpu (see [`num_possible_cpus`][crate::num_possible_cpus])
910    /// with exactly [`Self::value_size()`] elements each.
911    ///
912    /// For per-cpu maps, [`Self::update_percpu()`] must be used.
913    #[doc(alias = "bpf_map_update_elem")]
914    fn update_percpu(&self, key: &[u8], values: &[Vec<u8>], flags: MapFlags) -> Result<()> {
915        if !self.map_type().is_percpu() && self.map_type() != MapType::Unknown {
916            return Err(Error::with_invalid_data(format!(
917                "update() must be used for maps that are not per-cpu (type of the map is {:?})",
918                self.map_type(),
919            )));
920        }
921
922        if values.len() != crate::num_possible_cpus()? {
923            return Err(Error::with_invalid_data(format!(
924                "number of values {} != number of cpus {}",
925                values.len(),
926                crate::num_possible_cpus()?
927            )));
928        };
929
930        let val_size = self.value_size() as usize;
931        let aligned_val_size = percpu_aligned_value_size(self);
932        let buf_size = percpu_buffer_size(self)?;
933
934        let mut value_buf = vec![0; buf_size];
935
936        for (i, val) in values.iter().enumerate() {
937            if val.len() != val_size {
938                return Err(Error::with_invalid_data(format!(
939                    "value size for cpu {} is {} != {}",
940                    i,
941                    val.len(),
942                    val_size
943                )));
944            }
945
946            value_buf[(i * aligned_val_size)..(i * aligned_val_size + val_size)]
947                .copy_from_slice(val);
948        }
949
950        update_raw(self, key, &value_buf, flags)
951    }
952}
953
954/// An immutable loaded BPF map.
955pub type Map<'obj> = MapImpl<'obj>;
956/// A mutable loaded BPF map.
957pub type MapMut<'obj> = MapImpl<'obj, Mut>;
958
959/// Represents a libbpf-created map.
960///
961/// Some methods require working with raw bytes. You may find libraries such as
962/// [`plain`](https://crates.io/crates/plain) helpful.
963#[derive(Debug)]
964#[doc(alias = "bpf_map")]
965pub struct MapImpl<'obj, T = ()> {
966    ptr: NonNull<libbpf_sys::bpf_map>,
967    _phantom: PhantomData<&'obj T>,
968}
969
970impl<'obj> Map<'obj> {
971    /// Create a [`Map`] from a [`libbpf_sys::bpf_map`].
972    pub fn new(map: &'obj libbpf_sys::bpf_map) -> Self {
973        // SAFETY: We inferred the address from a reference, which is always
974        //         valid.
975        let ptr = unsafe { NonNull::new_unchecked(map as *const _ as *mut _) };
976        assert!(
977            map_fd(ptr).is_some(),
978            "provided BPF map does not have file descriptor"
979        );
980
981        Self {
982            ptr,
983            _phantom: PhantomData,
984        }
985    }
986
987    /// Create a [`Map`] from a [`libbpf_sys::bpf_map`] that does not contain a
988    /// file descriptor.
989    ///
990    /// The caller has to ensure that the [`AsFd`] impl is not used, or a panic
991    /// will be the result.
992    ///
993    /// # Safety
994    ///
995    /// The pointer must point to a loaded map.
996    #[doc(hidden)]
997    pub unsafe fn from_map_without_fd(ptr: NonNull<libbpf_sys::bpf_map>) -> Self {
998        Self {
999            ptr,
1000            _phantom: PhantomData,
1001        }
1002    }
1003
1004    /// Returns whether map is pinned or not flag
1005    #[doc(alias = "bpf_map__is_pinned")]
1006    pub fn is_pinned(&self) -> bool {
1007        unsafe { libbpf_sys::bpf_map__is_pinned(self.ptr.as_ptr()) }
1008    }
1009
1010    /// Returns the `pin_path` if the map is pinned, otherwise, `None`
1011    /// is returned.
1012    #[doc(alias = "bpf_map__pin_path")]
1013    pub fn get_pin_path(&self) -> Option<&OsStr> {
1014        let path_ptr = unsafe { libbpf_sys::bpf_map__pin_path(self.ptr.as_ptr()) };
1015        if path_ptr.is_null() {
1016            // means map is not pinned
1017            return None;
1018        }
1019        let path_c_str = unsafe { CStr::from_ptr(path_ptr) };
1020        Some(OsStr::from_bytes(path_c_str.to_bytes()))
1021    }
1022
1023    /// Return `true` if the map was set to be auto-created during load, `false` otherwise.
1024    #[doc(alias = "bpf_map__autocreate")]
1025    pub fn autocreate(&self) -> bool {
1026        unsafe { libbpf_sys::bpf_map__autocreate(self.ptr.as_ptr()) }
1027    }
1028}
1029
1030impl<'obj> MapMut<'obj> {
1031    /// Create a [`MapMut`] from a [`libbpf_sys::bpf_map`].
1032    pub fn new_mut(map: &'obj mut libbpf_sys::bpf_map) -> Self {
1033        // SAFETY: We inferred the address from a reference, which is always
1034        //         valid.
1035        let ptr = unsafe { NonNull::new_unchecked(map as *mut _) };
1036        assert!(
1037            map_fd(ptr).is_some(),
1038            "provided BPF map does not have file descriptor"
1039        );
1040
1041        Self {
1042            ptr,
1043            _phantom: PhantomData,
1044        }
1045    }
1046
1047    /// [Pin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
1048    /// this map to bpffs.
1049    #[doc(alias = "bpf_map__pin")]
1050    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1051        let path_c = util::path_to_cstring(path)?;
1052        let path_ptr = path_c.as_ptr();
1053
1054        let ret = unsafe { libbpf_sys::bpf_map__pin(self.ptr.as_ptr(), path_ptr) };
1055        util::parse_ret(ret)
1056    }
1057
1058    /// [Unpin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
1059    /// this map from bpffs.
1060    #[doc(alias = "bpf_map__unpin")]
1061    pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1062        let path_c = util::path_to_cstring(path)?;
1063        let path_ptr = path_c.as_ptr();
1064        let ret = unsafe { libbpf_sys::bpf_map__unpin(self.ptr.as_ptr(), path_ptr) };
1065        util::parse_ret(ret)
1066    }
1067
1068    /// Attach a struct ops map
1069    #[doc(alias = "bpf_map__attach_struct_ops")]
1070    pub fn attach_struct_ops(&mut self) -> Result<Link> {
1071        if self.map_type() != MapType::StructOps {
1072            return Err(Error::with_invalid_data(format!(
1073                "Invalid map type ({:?}) for attach_struct_ops()",
1074                self.map_type(),
1075            )));
1076        }
1077
1078        let ptr = unsafe { libbpf_sys::bpf_map__attach_struct_ops(self.ptr.as_ptr()) };
1079        let ptr = validate_bpf_ret(ptr).context("failed to attach struct_ops")?;
1080        // SAFETY: the pointer came from libbpf and has been checked for errors.
1081        let link = unsafe { Link::new(ptr) };
1082        Ok(link)
1083    }
1084}
1085
1086impl<'obj> Deref for MapMut<'obj> {
1087    type Target = Map<'obj>;
1088
1089    fn deref(&self) -> &Self::Target {
1090        unsafe { transmute::<&MapMut<'obj>, &Map<'obj>>(self) }
1091    }
1092}
1093
1094impl<T> AsFd for MapImpl<'_, T> {
1095    #[inline]
1096    #[doc(alias = "bpf_map__fd")]
1097    fn as_fd(&self) -> BorrowedFd<'_> {
1098        // SANITY: Our map must always have a file descriptor associated with
1099        //         it.
1100        let fd = map_fd(self.ptr).unwrap();
1101        // SAFETY: `fd` is guaranteed to be valid for the lifetime of
1102        //         the created object.
1103        let fd = unsafe { BorrowedFd::borrow_raw(fd) };
1104        fd
1105    }
1106}
1107
1108impl<T> MapCore for MapImpl<'_, T>
1109where
1110    T: Debug,
1111{
1112    fn name(&self) -> &OsStr {
1113        // SAFETY: We ensured `ptr` is valid during construction.
1114        let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
1115        // SAFETY: `bpf_map__name` can return NULL but only if it's passed
1116        //          NULL. We know `ptr` is not NULL.
1117        let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
1118        OsStr::from_bytes(name_c_str.to_bytes())
1119    }
1120
1121    #[inline]
1122    fn map_type(&self) -> MapType {
1123        let ty = unsafe { libbpf_sys::bpf_map__type(self.ptr.as_ptr()) };
1124        MapType::from(ty)
1125    }
1126
1127    #[inline]
1128    fn key_size(&self) -> u32 {
1129        unsafe { libbpf_sys::bpf_map__key_size(self.ptr.as_ptr()) }
1130    }
1131
1132    #[inline]
1133    fn value_size(&self) -> u32 {
1134        unsafe { libbpf_sys::bpf_map__value_size(self.ptr.as_ptr()) }
1135    }
1136
1137    #[inline]
1138    fn max_entries(&self) -> u32 {
1139        unsafe { libbpf_sys::bpf_map__max_entries(self.ptr.as_ptr()) }
1140    }
1141}
1142
1143impl AsRawLibbpf for Map<'_> {
1144    type LibbpfType = libbpf_sys::bpf_map;
1145
1146    /// Retrieve the underlying [`libbpf_sys::bpf_map`].
1147    #[inline]
1148    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
1149        self.ptr
1150    }
1151}
1152
1153/// A handle to a map. Handles can be duplicated and dropped.
1154///
1155/// While possible to [create directly][MapHandle::create], in many cases it is
1156/// useful to create such a handle from an existing [`Map`]:
1157/// ```no_run
1158/// # use libbpf_rs::Map;
1159/// # use libbpf_rs::MapHandle;
1160/// # let get_map = || -> &Map { todo!() };
1161/// let map: &Map = get_map();
1162/// let map_handle = MapHandle::try_from(map).unwrap();
1163/// ```
1164///
1165/// Some methods require working with raw bytes. You may find libraries such as
1166/// [`plain`](https://crates.io/crates/plain) helpful.
1167#[derive(Debug)]
1168pub struct MapHandle {
1169    fd: OwnedFd,
1170    name: OsString,
1171    ty: MapType,
1172    key_size: u32,
1173    value_size: u32,
1174    max_entries: u32,
1175}
1176
1177impl MapHandle {
1178    /// Create a bpf map whose data is not managed by libbpf.
1179    #[doc(alias = "bpf_map_create")]
1180    pub fn create<T: AsRef<OsStr>>(
1181        map_type: MapType,
1182        name: Option<T>,
1183        key_size: u32,
1184        value_size: u32,
1185        max_entries: u32,
1186        opts: &libbpf_sys::bpf_map_create_opts,
1187    ) -> Result<Self> {
1188        let name = match name {
1189            Some(name) => name.as_ref().to_os_string(),
1190            // The old version kernel don't support specifying map name.
1191            None => OsString::new(),
1192        };
1193        let name_c_str = CString::new(name.as_bytes()).map_err(|_| {
1194            Error::with_invalid_data(format!("invalid name `{name:?}`: has NUL bytes"))
1195        })?;
1196        let name_c_ptr = if name.is_empty() {
1197            ptr::null()
1198        } else {
1199            name_c_str.as_bytes_with_nul().as_ptr()
1200        };
1201
1202        let fd = unsafe {
1203            libbpf_sys::bpf_map_create(
1204                map_type.into(),
1205                name_c_ptr.cast(),
1206                key_size,
1207                value_size,
1208                max_entries,
1209                opts,
1210            )
1211        };
1212        let () = util::parse_ret(fd)?;
1213
1214        Ok(Self {
1215            // SAFETY: A file descriptor coming from the `bpf_map_create`
1216            //         function is always suitable for ownership and can be
1217            //         cleaned up with close.
1218            fd: unsafe { OwnedFd::from_raw_fd(fd) },
1219            name,
1220            ty: map_type,
1221            key_size,
1222            value_size,
1223            max_entries,
1224        })
1225    }
1226
1227    /// Open a previously pinned map from its path.
1228    ///
1229    /// # Panics
1230    /// If the path contains null bytes.
1231    #[doc(alias = "bpf_obj_get_opts")]
1232    pub fn from_pinned_path<P: AsRef<Path>>(path: P) -> Result<Self> {
1233        Self::from_pinned_path_with_file_flags(path, 0)
1234    }
1235
1236    /// Open a previously pinned map from its path with the provided file flags.
1237    ///
1238    /// For example, pass [`libbpf_sys::BPF_F_RDONLY`] to open a map as
1239    /// read-only from user space.
1240    ///
1241    /// # Panics
1242    /// If the path contains null bytes.
1243    #[doc(alias = "bpf_obj_get_opts")]
1244    pub fn from_pinned_path_with_file_flags<P: AsRef<Path>>(
1245        path: P,
1246        file_flags: u32,
1247    ) -> Result<Self> {
1248        fn inner(path: &Path, file_flags: u32) -> Result<MapHandle> {
1249            let p = CString::new(path.as_os_str().as_bytes()).expect("path contained null bytes");
1250            let opts = libbpf_sys::bpf_obj_get_opts {
1251                sz: size_of::<libbpf_sys::bpf_obj_get_opts>() as libbpf_sys::size_t,
1252                file_flags,
1253                ..Default::default()
1254            };
1255            let fd = parse_ret_i32(unsafe {
1256                // SAFETY
1257                // p is never null since we allocated ourselves.
1258                libbpf_sys::bpf_obj_get_opts(p.as_ptr(), &opts)
1259            })?;
1260            MapHandle::from_fd(unsafe {
1261                // SAFETY
1262                // A file descriptor coming from the bpf_obj_get function is always suitable for
1263                // ownership and can be cleaned up with close.
1264                OwnedFd::from_raw_fd(fd)
1265            })
1266        }
1267
1268        inner(path.as_ref(), file_flags)
1269    }
1270
1271    /// Open a loaded map from its map id.
1272    #[doc(alias = "bpf_map_get_fd_by_id")]
1273    pub fn from_map_id(id: u32) -> Result<Self> {
1274        parse_ret_i32(unsafe {
1275            // SAFETY
1276            // This function is always safe to call.
1277            libbpf_sys::bpf_map_get_fd_by_id(id)
1278        })
1279        .map(|fd| unsafe {
1280            // SAFETY
1281            // A file descriptor coming from the bpf_map_get_fd_by_id function is always suitable
1282            // for ownership and can be cleaned up with close.
1283            OwnedFd::from_raw_fd(fd)
1284        })
1285        .and_then(Self::from_fd)
1286    }
1287
1288    fn from_fd(fd: OwnedFd) -> Result<Self> {
1289        let info = MapInfo::new(fd.as_fd())?;
1290        Ok(Self {
1291            fd,
1292            name: info.name()?.into(),
1293            ty: info.map_type(),
1294            key_size: info.info.key_size,
1295            value_size: info.info.value_size,
1296            max_entries: info.info.max_entries,
1297        })
1298    }
1299
1300    /// Freeze the map as read-only from user space.
1301    ///
1302    /// Entries from a frozen map can no longer be updated or deleted with the
1303    /// `bpf()` system call. This operation is not reversible, and the map remains
1304    /// immutable from user space until its destruction. However, read and write
1305    /// permissions for BPF programs to the map remain unchanged.
1306    #[doc(alias = "bpf_map_freeze")]
1307    pub fn freeze(&self) -> Result<()> {
1308        let ret = unsafe { libbpf_sys::bpf_map_freeze(self.fd.as_raw_fd()) };
1309
1310        util::parse_ret(ret)
1311    }
1312
1313    /// [Pin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
1314    /// this map to bpffs.
1315    #[doc(alias = "bpf_obj_pin")]
1316    pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1317        let path_c = util::path_to_cstring(path)?;
1318        let path_ptr = path_c.as_ptr();
1319
1320        let ret = unsafe { libbpf_sys::bpf_obj_pin(self.fd.as_raw_fd(), path_ptr) };
1321        util::parse_ret(ret)
1322    }
1323
1324    /// [Unpin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs)
1325    /// this map from bpffs.
1326    pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1327        remove_file(path).context("failed to remove pin map")
1328    }
1329}
1330
1331impl MapCore for MapHandle {
1332    #[inline]
1333    fn name(&self) -> &OsStr {
1334        &self.name
1335    }
1336
1337    #[inline]
1338    fn map_type(&self) -> MapType {
1339        self.ty
1340    }
1341
1342    #[inline]
1343    fn key_size(&self) -> u32 {
1344        self.key_size
1345    }
1346
1347    #[inline]
1348    fn value_size(&self) -> u32 {
1349        self.value_size
1350    }
1351
1352    #[inline]
1353    fn max_entries(&self) -> u32 {
1354        self.max_entries
1355    }
1356}
1357
1358impl AsFd for MapHandle {
1359    #[inline]
1360    fn as_fd(&self) -> BorrowedFd<'_> {
1361        self.fd.as_fd()
1362    }
1363}
1364
1365impl<T> TryFrom<&MapImpl<'_, T>> for MapHandle
1366where
1367    T: Debug,
1368{
1369    type Error = Error;
1370
1371    fn try_from(other: &MapImpl<'_, T>) -> Result<Self> {
1372        Ok(Self {
1373            fd: other
1374                .as_fd()
1375                .try_clone_to_owned()
1376                .context("failed to duplicate map file descriptor")?,
1377            name: other.name().to_os_string(),
1378            ty: other.map_type(),
1379            key_size: other.key_size(),
1380            value_size: other.value_size(),
1381            max_entries: other.max_entries(),
1382        })
1383    }
1384}
1385
1386impl TryFrom<&Self> for MapHandle {
1387    type Error = Error;
1388
1389    fn try_from(other: &Self) -> Result<Self> {
1390        Ok(Self {
1391            fd: other
1392                .as_fd()
1393                .try_clone_to_owned()
1394                .context("failed to duplicate map file descriptor")?,
1395            name: other.name().to_os_string(),
1396            ty: other.map_type(),
1397            key_size: other.key_size(),
1398            value_size: other.value_size(),
1399            max_entries: other.max_entries(),
1400        })
1401    }
1402}
1403
1404bitflags! {
1405    /// Flags to configure [`Map`] operations.
1406    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
1407    pub struct MapFlags: u64 {
1408        /// See [`libbpf_sys::BPF_ANY`].
1409        const ANY      = libbpf_sys::BPF_ANY as _;
1410        /// See [`libbpf_sys::BPF_NOEXIST`].
1411        const NO_EXIST = libbpf_sys::BPF_NOEXIST as _;
1412        /// See [`libbpf_sys::BPF_EXIST`].
1413        const EXIST    = libbpf_sys::BPF_EXIST as _;
1414        /// See [`libbpf_sys::BPF_F_LOCK`].
1415        const LOCK     = libbpf_sys::BPF_F_LOCK as _;
1416    }
1417}
1418
1419/// Type of a [`Map`]. Maps to `enum bpf_map_type` in kernel uapi.
1420// If you add a new per-cpu map, also update `is_percpu`.
1421#[non_exhaustive]
1422#[repr(u32)]
1423#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1424#[doc(alias = "bpf_map_type")]
1425pub enum MapType {
1426    /// An unspecified map type.
1427    Unspec = libbpf_sys::BPF_MAP_TYPE_UNSPEC,
1428    /// A general purpose Hash map storage type.
1429    ///
1430    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_hash.html) for more details.
1431    Hash = libbpf_sys::BPF_MAP_TYPE_HASH,
1432    /// An Array map storage type.
1433    ///
1434    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_array.html) for more details.
1435    Array = libbpf_sys::BPF_MAP_TYPE_ARRAY,
1436    /// A program array map which holds only the file descriptors to other eBPF programs. Used for
1437    /// tail-calls.
1438    ///
1439    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_PROG_ARRAY/) for more details.
1440    ProgArray = libbpf_sys::BPF_MAP_TYPE_PROG_ARRAY,
1441    /// An array map which holds only the file descriptors to perf events.
1442    ///
1443    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_PERF_EVENT_ARRAY/) for more details.
1444    PerfEventArray = libbpf_sys::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
1445    /// A Hash map with per CPU storage.
1446    ///
1447    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_hash.html#per-cpu-hashes) for more details.
1448    PercpuHash = libbpf_sys::BPF_MAP_TYPE_PERCPU_HASH,
1449    /// An Array map with per CPU storage.
1450    ///
1451    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_array.html) for more details.
1452    PercpuArray = libbpf_sys::BPF_MAP_TYPE_PERCPU_ARRAY,
1453    #[allow(missing_docs)]
1454    StackTrace = libbpf_sys::BPF_MAP_TYPE_STACK_TRACE,
1455    #[allow(missing_docs)]
1456    CgroupArray = libbpf_sys::BPF_MAP_TYPE_CGROUP_ARRAY,
1457    /// A Hash map with least recently used (LRU) eviction policy.
1458    ///
1459    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_hash.html#bpf-map-type-lru-hash-and-variants) for more details.
1460    LruHash = libbpf_sys::BPF_MAP_TYPE_LRU_HASH,
1461    /// A Hash map with least recently used (LRU) eviction policy with per CPU storage.
1462    ///
1463    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_hash.html#per-cpu-hashes) for more details.
1464    LruPercpuHash = libbpf_sys::BPF_MAP_TYPE_LRU_PERCPU_HASH,
1465    /// A Longest Prefix Match (LPM) algorithm based map.
1466    ///
1467    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_lpm_trie.html) for more details.
1468    LpmTrie = libbpf_sys::BPF_MAP_TYPE_LPM_TRIE,
1469    /// A map in map storage.
1470    /// One level of nesting is supported, where an outer map contains instances of a single type
1471    /// of inner map.
1472    ///
1473    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_of_maps.html) for more details.
1474    ArrayOfMaps = libbpf_sys::BPF_MAP_TYPE_ARRAY_OF_MAPS,
1475    /// A map in map storage.
1476    /// One level of nesting is supported, where an outer map contains instances of a single type
1477    /// of inner map.
1478    ///
1479    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_of_maps.html) for more details.
1480    HashOfMaps = libbpf_sys::BPF_MAP_TYPE_HASH_OF_MAPS,
1481    /// An array map that uses the key as the index to lookup a reference to a net device.
1482    /// Primarily used for XDP BPF Helper.
1483    ///
1484    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_devmap.html) for more details.
1485    Devmap = libbpf_sys::BPF_MAP_TYPE_DEVMAP,
1486    /// An array map holds references to a socket descriptor.
1487    ///
1488    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_sockmap.html) for more details.
1489    Sockmap = libbpf_sys::BPF_MAP_TYPE_SOCKMAP,
1490    /// A map that redirects raw XDP frames to another CPU.
1491    ///
1492    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_cpumap.html) for more details.
1493    Cpumap = libbpf_sys::BPF_MAP_TYPE_CPUMAP,
1494    /// A map that redirects raw XDP frames to `AF_XDP` sockets (XSKs), a new type of address
1495    /// family in the kernel that allows redirection of frames from a driver to user space
1496    /// without having to traverse the full network stack.
1497    ///
1498    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_xskmap.html) for more details.
1499    Xskmap = libbpf_sys::BPF_MAP_TYPE_XSKMAP,
1500    /// A Hash map that holds references to sockets via their socket descriptor.
1501    ///
1502    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_sockmap.html) for more details.
1503    Sockhash = libbpf_sys::BPF_MAP_TYPE_SOCKHASH,
1504    /// Deprecated. Use `CGrpStorage` instead.
1505    ///
1506    /// A Local storage for cgroups.
1507    /// Only available with `CONFIG_CGROUP_BPF` and to programs that attach to cgroups.
1508    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_cgroup_storage.html) for more details.
1509    CgroupStorage = libbpf_sys::BPF_MAP_TYPE_CGROUP_STORAGE,
1510    /// A Local storage for cgroups. Only available with `CONFIG_CGROUPS`.
1511    ///
1512    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_cgrp_storage.html) for more details.
1513    /// See also [Difference between cgrp_storage and cgroup_storage](https://docs.kernel.org/bpf/map_cgrp_storage.html#difference-between-bpf-map-type-cgrp-storage-and-bpf-map-type-cgroup-storage)
1514    CGrpStorage = libbpf_sys::BPF_MAP_TYPE_CGRP_STORAGE,
1515    /// A map that holds references to sockets with `SO_REUSEPORT` option set.
1516    ///
1517    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_REUSEPORT_SOCKARRAY/) for more details.
1518    ReuseportSockarray = libbpf_sys::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
1519    /// A per-CPU variant of [`BPF_MAP_TYPE_CGROUP_STORAGE`][`MapType::CgroupStorage`].
1520    ///
1521    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) for more details.
1522    PercpuCgroupStorage = libbpf_sys::BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
1523    /// A FIFO storage.
1524    ///
1525    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_queue_stack.html) for more details.
1526    Queue = libbpf_sys::BPF_MAP_TYPE_QUEUE,
1527    /// A LIFO storage.
1528    ///
1529    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_queue_stack.html) for more details.
1530    Stack = libbpf_sys::BPF_MAP_TYPE_STACK,
1531    /// A socket-local storage.
1532    ///
1533    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_sk_storage.html) for more details.
1534    SkStorage = libbpf_sys::BPF_MAP_TYPE_SK_STORAGE,
1535    /// A Hash map that uses the key as the index to lookup a reference to a net device.
1536    /// Primarily used for XDP BPF Helper.
1537    ///
1538    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_devmap.html) for more details.
1539    DevmapHash = libbpf_sys::BPF_MAP_TYPE_DEVMAP_HASH,
1540    /// A specialized map that act as implementations of "struct ops" structures defined in the
1541    /// kernel.
1542    ///
1543    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_STRUCT_OPS/) for more details.
1544    StructOps = libbpf_sys::BPF_MAP_TYPE_STRUCT_OPS,
1545    /// A ring buffer map to efficiently send large amount of data.
1546    ///
1547    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_RINGBUF/) for more details.
1548    RingBuf = libbpf_sys::BPF_MAP_TYPE_RINGBUF,
1549    /// A storage map that holds data keyed on inodes.
1550    ///
1551    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_INODE_STORAGE/) for more details.
1552    InodeStorage = libbpf_sys::BPF_MAP_TYPE_INODE_STORAGE,
1553    /// A storage map that holds data keyed on tasks.
1554    ///
1555    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_TASK_STORAGE/) for more details.
1556    TaskStorage = libbpf_sys::BPF_MAP_TYPE_TASK_STORAGE,
1557    /// Bloom filters are a space-efficient probabilistic data structure used to quickly test
1558    /// whether an element exists in a set. In a bloom filter, false positives are possible
1559    /// whereas false negatives are not.
1560    ///
1561    /// Refer the kernel [documentation](https://docs.kernel.org/bpf/map_bloom_filter.html) for more details.
1562    BloomFilter = libbpf_sys::BPF_MAP_TYPE_BLOOM_FILTER,
1563    #[allow(missing_docs)]
1564    UserRingBuf = libbpf_sys::BPF_MAP_TYPE_USER_RINGBUF,
1565    /// A sparse shared memory region that both BPF programs and user space can
1566    /// address directly.
1567    ///
1568    /// Refer [documentation](https://docs.ebpf.io/linux/map-type/BPF_MAP_TYPE_ARENA/)
1569    /// for more details.
1570    Arena = libbpf_sys::BPF_MAP_TYPE_ARENA,
1571    /// We choose to specify our own "unknown" type here b/c it's really up to the kernel
1572    /// to decide if it wants to reject the map. If it accepts it, it just means whoever
1573    /// using this library is a bit out of date.
1574    Unknown = u32::MAX,
1575}
1576
1577impl MapType {
1578    /// Returns if the map is of one of the per-cpu types.
1579    pub fn is_percpu(&self) -> bool {
1580        matches!(
1581            self,
1582            Self::PercpuArray | Self::PercpuHash | Self::LruPercpuHash | Self::PercpuCgroupStorage
1583        )
1584    }
1585
1586    /// Returns if the map is of one of the hashmap types.
1587    pub fn is_hash_map(&self) -> bool {
1588        matches!(
1589            self,
1590            Self::Hash | Self::PercpuHash | Self::LruHash | Self::LruPercpuHash
1591        )
1592    }
1593
1594    /// Returns if the map is keyless map type as per documentation of libbpf
1595    /// Keyless map types are: Queues, Stacks and Bloom Filters
1596    fn is_keyless(&self) -> bool {
1597        matches!(self, Self::Queue | Self::Stack | Self::BloomFilter)
1598    }
1599
1600    /// Returns if the map is of bloom filter type
1601    pub fn is_bloom_filter(&self) -> bool {
1602        Self::BloomFilter.eq(self)
1603    }
1604
1605    /// Detects if host kernel supports this BPF map type.
1606    ///
1607    /// Make sure the process has required set of CAP_* permissions (or runs as
1608    /// root) when performing feature checking.
1609    #[doc(alias = "libbpf_probe_bpf_map_type")]
1610    pub fn is_supported(&self) -> Result<bool> {
1611        let ret = unsafe { libbpf_sys::libbpf_probe_bpf_map_type(*self as u32, ptr::null()) };
1612        match ret {
1613            0 => Ok(false),
1614            1 => Ok(true),
1615            _ => Err(Error::from_raw_os_error(-ret)),
1616        }
1617    }
1618}
1619
1620impl From<u32> for MapType {
1621    fn from(value: u32) -> Self {
1622        use MapType::*;
1623
1624        match value {
1625            x if x == Unspec as u32 => Unspec,
1626            x if x == Hash as u32 => Hash,
1627            x if x == Array as u32 => Array,
1628            x if x == ProgArray as u32 => ProgArray,
1629            x if x == PerfEventArray as u32 => PerfEventArray,
1630            x if x == PercpuHash as u32 => PercpuHash,
1631            x if x == PercpuArray as u32 => PercpuArray,
1632            x if x == StackTrace as u32 => StackTrace,
1633            x if x == CgroupArray as u32 => CgroupArray,
1634            x if x == LruHash as u32 => LruHash,
1635            x if x == LruPercpuHash as u32 => LruPercpuHash,
1636            x if x == LpmTrie as u32 => LpmTrie,
1637            x if x == ArrayOfMaps as u32 => ArrayOfMaps,
1638            x if x == HashOfMaps as u32 => HashOfMaps,
1639            x if x == Devmap as u32 => Devmap,
1640            x if x == Sockmap as u32 => Sockmap,
1641            x if x == Cpumap as u32 => Cpumap,
1642            x if x == Xskmap as u32 => Xskmap,
1643            x if x == Sockhash as u32 => Sockhash,
1644            x if x == CgroupStorage as u32 => CgroupStorage,
1645            x if x == ReuseportSockarray as u32 => ReuseportSockarray,
1646            x if x == PercpuCgroupStorage as u32 => PercpuCgroupStorage,
1647            x if x == Queue as u32 => Queue,
1648            x if x == Stack as u32 => Stack,
1649            x if x == SkStorage as u32 => SkStorage,
1650            x if x == DevmapHash as u32 => DevmapHash,
1651            x if x == StructOps as u32 => StructOps,
1652            x if x == RingBuf as u32 => RingBuf,
1653            x if x == InodeStorage as u32 => InodeStorage,
1654            x if x == TaskStorage as u32 => TaskStorage,
1655            x if x == BloomFilter as u32 => BloomFilter,
1656            x if x == UserRingBuf as u32 => UserRingBuf,
1657            x if x == Arena as u32 => Arena,
1658            _ => Unknown,
1659        }
1660    }
1661}
1662
1663impl From<MapType> for u32 {
1664    fn from(value: MapType) -> Self {
1665        value as Self
1666    }
1667}
1668
1669/// An iterator over the keys of a BPF map.
1670#[derive(Debug)]
1671#[doc(alias = "bpf_map_get_next_key")]
1672pub struct MapKeyIter<'map> {
1673    map_fd: BorrowedFd<'map>,
1674    prev: Option<Vec<u8>>,
1675    next: Vec<u8>,
1676}
1677
1678impl<'map> MapKeyIter<'map> {
1679    fn new(map_fd: BorrowedFd<'map>, key_size: u32) -> Self {
1680        Self {
1681            map_fd,
1682            prev: None,
1683            next: vec![0; key_size as usize],
1684        }
1685    }
1686}
1687
1688impl Iterator for MapKeyIter<'_> {
1689    type Item = Vec<u8>;
1690
1691    fn next(&mut self) -> Option<Self::Item> {
1692        let prev = self.prev.as_ref().map_or(ptr::null(), Vec::as_ptr);
1693
1694        let ret = unsafe {
1695            libbpf_sys::bpf_map_get_next_key(
1696                self.map_fd.as_raw_fd(),
1697                prev.cast(),
1698                self.next.as_mut_ptr().cast(),
1699            )
1700        };
1701        if ret != 0 {
1702            None
1703        } else {
1704            self.prev = Some(self.next.clone());
1705            Some(self.next.clone())
1706        }
1707    }
1708}
1709
1710/// An iterator over batches of key value pairs of a BPF map.
1711#[derive(Debug)]
1712#[doc(alias = "bpf_map_lookup_batch")]
1713#[doc(alias = "bpf_map_lookup_and_delete_batch")]
1714pub struct BatchedMapIter<'map> {
1715    map_fd: BorrowedFd<'map>,
1716    delete: bool,
1717    count: usize,
1718    key_size: usize,
1719    value_size: usize,
1720    keys: Vec<u8>,
1721    values: Vec<u8>,
1722    prev: Option<Vec<u8>>,
1723    next: Vec<u8>,
1724    batch_opts: libbpf_sys::bpf_map_batch_opts,
1725    index: Option<usize>,
1726}
1727
1728impl<'map> BatchedMapIter<'map> {
1729    fn new(
1730        map_fd: BorrowedFd<'map>,
1731        count: u32,
1732        key_size: u32,
1733        value_size: u32,
1734        batch_opts: libbpf_sys::bpf_map_batch_opts,
1735        delete: bool,
1736    ) -> Self {
1737        Self {
1738            map_fd,
1739            delete,
1740            count: count as usize,
1741            key_size: key_size as usize,
1742            value_size: value_size as usize,
1743            keys: vec![0; (count * key_size) as usize],
1744            values: vec![0; (count * value_size) as usize],
1745            prev: None,
1746            next: vec![0; key_size as usize],
1747            batch_opts,
1748            index: None,
1749        }
1750    }
1751
1752    fn lookup_next_batch(&mut self) {
1753        let prev = self.prev.as_mut().map_or(ptr::null_mut(), Vec::as_mut_ptr);
1754        let mut count = self.count as u32;
1755
1756        let ret = unsafe {
1757            let lookup_fn = if self.delete {
1758                libbpf_sys::bpf_map_lookup_and_delete_batch
1759            } else {
1760                libbpf_sys::bpf_map_lookup_batch
1761            };
1762            lookup_fn(
1763                self.map_fd.as_raw_fd(),
1764                prev.cast(),
1765                self.next.as_mut_ptr().cast(),
1766                self.keys.as_mut_ptr().cast(),
1767                self.values.as_mut_ptr().cast(),
1768                &mut count,
1769                &self.batch_opts,
1770            )
1771        };
1772
1773        if let Err(e) = util::parse_ret(ret) {
1774            match e.kind() {
1775                // in this case we can trust the returned count value
1776                error::ErrorKind::NotFound => {}
1777                // retry with same input arguments
1778                error::ErrorKind::Interrupted => {
1779                    return self.lookup_next_batch();
1780                }
1781                _ => {
1782                    self.index = None;
1783                    return;
1784                }
1785            }
1786        }
1787
1788        self.prev = Some(self.next.clone());
1789        self.index = Some(0);
1790
1791        unsafe {
1792            self.keys.set_len(self.key_size * count as usize);
1793            self.values.set_len(self.value_size * count as usize);
1794        }
1795    }
1796}
1797
1798impl Iterator for BatchedMapIter<'_> {
1799    type Item = (Vec<u8>, Vec<u8>);
1800
1801    fn next(&mut self) -> Option<Self::Item> {
1802        let load_next_batch = match self.index {
1803            Some(index) => {
1804                let batch_finished = index * self.key_size >= self.keys.len();
1805                let last_batch = self.keys.len() < self.key_size * self.count;
1806                batch_finished && !last_batch
1807            }
1808            None => true,
1809        };
1810
1811        if load_next_batch {
1812            self.lookup_next_batch();
1813        }
1814
1815        let index = self.index?;
1816        let key = self.keys.chunks_exact(self.key_size).nth(index)?.to_vec();
1817        let val = self
1818            .values
1819            .chunks_exact(self.value_size)
1820            .nth(index)?
1821            .to_vec();
1822
1823        self.index = Some(index + 1);
1824        Some((key, val))
1825    }
1826}
1827
1828/// A convenience wrapper for [`bpf_map_info`]. It provides the ability
1829/// to retrieve the details of a certain map.
1830#[derive(Debug)]
1831#[doc(alias = "bpf_map_info")]
1832pub struct MapInfo {
1833    /// The inner [`bpf_map_info`] object.
1834    pub info: bpf_map_info,
1835}
1836
1837impl MapInfo {
1838    /// Create a `MapInfo` object from a fd.
1839    #[doc(alias = "bpf_obj_get_info_by_fd")]
1840    pub fn new(fd: BorrowedFd<'_>) -> Result<Self> {
1841        let mut map_info = bpf_map_info::default();
1842        let mut size = mem::size_of_val(&map_info) as u32;
1843        // SAFETY: All pointers are derived from references and hence valid.
1844        let () = util::parse_ret(unsafe {
1845            bpf_obj_get_info_by_fd(
1846                fd.as_raw_fd(),
1847                (&mut map_info as *mut bpf_map_info).cast::<c_void>(),
1848                &mut size as *mut u32,
1849            )
1850        })?;
1851        Ok(Self { info: map_info })
1852    }
1853
1854    /// Get the map type
1855    #[inline]
1856    pub fn map_type(&self) -> MapType {
1857        MapType::from(self.info.type_)
1858    }
1859
1860    /// Get the name of this map.
1861    ///
1862    /// Returns error if the underlying data in the structure is not a valid
1863    /// utf-8 string.
1864    pub fn name<'a>(&self) -> Result<&'a str> {
1865        // SAFETY: convert &[i8] to &[u8], and then cast that to &str. i8 and u8 has the same size.
1866        let char_slice =
1867            unsafe { from_raw_parts(self.info.name[..].as_ptr().cast(), self.info.name.len()) };
1868
1869        util::c_char_slice_to_cstr(char_slice)
1870            .ok_or_else(|| Error::with_invalid_data("no nul byte found"))?
1871            .to_str()
1872            .map_err(Error::with_invalid_data)
1873    }
1874
1875    /// Get the map flags.
1876    #[inline]
1877    pub fn flags(&self) -> MapFlags {
1878        MapFlags::from_bits_truncate(self.info.map_flags as u64)
1879    }
1880}
1881
1882/// Information about a BPF map obtained from `/proc/self/fdinfo`.
1883///
1884/// This provides information not available through [`MapInfo`], such as
1885/// [`memlock`][MapFdInfo::memlock] (memory usage) and [`frozen`][MapFdInfo::frozen] status.
1886///
1887/// The fields correspond to those printed by
1888/// [`bpf_map_show_fdinfo`](https://github.com/torvalds/linux/blob/37a93dd5c49b/kernel/bpf/syscall.c#L1007)
1889/// in the kernel source. See also bpftool's
1890/// [`get_fdinfo`](https://github.com/torvalds/linux/blob/37a93dd5c49/tools/bpf/bpftool/common.c#L485)
1891/// for the matching userspace parsing logic.
1892#[derive(Debug, Clone)]
1893pub struct MapFdInfo {
1894    /// The map type.
1895    pub map_type: MapType,
1896    /// The size of the map's keys in bytes.
1897    pub key_size: u32,
1898    /// The size of the map's values in bytes.
1899    pub value_size: u32,
1900    /// The maximum number of entries in the map.
1901    pub max_entries: u32,
1902    // The following fields were added in later kernel versions and may not be
1903    // present in older kernels.
1904    /// The map flags.
1905    pub map_flags: Option<u32>,
1906    /// Extra map-specific data.
1907    pub map_extra: Option<u64>,
1908    /// The amount of memory locked by the map in bytes.
1909    pub memlock: Option<u64>,
1910    /// The map's ID.
1911    pub map_id: Option<u32>,
1912    /// Whether the map is frozen.
1913    pub frozen: Option<bool>,
1914    /// The type of the owner program (only for `prog_array` maps).
1915    pub owner_prog_type: Option<ProgramType>,
1916    /// Whether the owner program is JIT-compiled (only for `prog_array` maps).
1917    pub owner_jited: Option<bool>,
1918}
1919
1920impl MapFdInfo {
1921    /// Create a `MapFdInfo` by reading `/proc/self/fdinfo` for the given fd.
1922    pub fn from_fd(fd: BorrowedFd<'_>) -> Result<Self> {
1923        let path = format!("/proc/self/fdinfo/{}", fd.as_raw_fd());
1924        let file = File::open(&path).with_context(|| format!("failed to open `{path}`"))?;
1925        let reader = BufReader::new(file);
1926
1927        let parse = |key: &str, val: &str| -> Result<u32> {
1928            val.parse()
1929                .map_err(|e| Error::with_invalid_data(format!("`{key}`: {e}")))
1930        };
1931
1932        let mut map_type = None;
1933        let mut key_size = None;
1934        let mut value_size = None;
1935        let mut max_entries = None;
1936        let mut map_flags = None;
1937        let mut map_extra = None;
1938        let mut memlock = None;
1939        let mut map_id = None;
1940        let mut frozen = None;
1941        let mut owner_prog_type = None;
1942        let mut owner_jited = None;
1943
1944        for result in reader.lines() {
1945            let line = result?;
1946            let Some((key, value)) = line.split_once('\t') else {
1947                continue;
1948            };
1949            // Keys have a trailing colon, e.g. "map_type:"
1950            let key = key.trim_end_matches(':');
1951            let value = value.trim();
1952
1953            match key {
1954                "map_type" => map_type = Some(parse(key, value)?),
1955                "key_size" => key_size = Some(parse(key, value)?),
1956                "value_size" => value_size = Some(parse(key, value)?),
1957                "max_entries" => max_entries = Some(parse(key, value)?),
1958                "map_flags" => {
1959                    map_flags =
1960                        Some(parse_hex(value).with_context(|| format!("bad `{key}`"))? as u32)
1961                }
1962                "map_extra" => {
1963                    map_extra = Some(parse_hex(value).with_context(|| format!("bad `{key}`"))?)
1964                }
1965                "memlock" => memlock = Some(parse(key, value)? as u64),
1966                "map_id" => map_id = Some(parse(key, value)?),
1967                "frozen" => frozen = Some(parse(key, value)? != 0),
1968                "owner_prog_type" => owner_prog_type = Some(parse(key, value)?),
1969                "owner_jited" => owner_jited = Some(parse(key, value)? != 0),
1970                _ => {}
1971            }
1972        }
1973
1974        let missing = |f| Error::with_invalid_data(format!("missing `{f}` in fdinfo"));
1975
1976        Ok(Self {
1977            map_type: MapType::from(map_type.ok_or_else(|| missing("map_type"))?),
1978            key_size: key_size.ok_or_else(|| missing("key_size"))?,
1979            value_size: value_size.ok_or_else(|| missing("value_size"))?,
1980            max_entries: max_entries.ok_or_else(|| missing("max_entries"))?,
1981            map_flags,
1982            map_extra,
1983            memlock,
1984            map_id,
1985            frozen,
1986            owner_prog_type: owner_prog_type.map(ProgramType::from),
1987            owner_jited,
1988        })
1989    }
1990}
1991
1992/// Parse a value that may be in hex (0x...) or decimal format.
1993fn parse_hex(s: &str) -> Result<u64> {
1994    if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
1995        u64::from_str_radix(hex, 16)
1996    } else {
1997        s.parse()
1998    }
1999    .map_err(Error::with_invalid_data)
2000}
2001
2002#[cfg(test)]
2003mod tests {
2004    use super::*;
2005
2006    use std::mem::discriminant;
2007
2008    #[test]
2009    fn map_type() {
2010        use MapType::*;
2011
2012        for t in [
2013            Unspec,
2014            Hash,
2015            Array,
2016            ProgArray,
2017            PerfEventArray,
2018            PercpuHash,
2019            PercpuArray,
2020            StackTrace,
2021            CgroupArray,
2022            LruHash,
2023            LruPercpuHash,
2024            LpmTrie,
2025            ArrayOfMaps,
2026            HashOfMaps,
2027            Devmap,
2028            Sockmap,
2029            Cpumap,
2030            Xskmap,
2031            Sockhash,
2032            CgroupStorage,
2033            ReuseportSockarray,
2034            PercpuCgroupStorage,
2035            Queue,
2036            Stack,
2037            SkStorage,
2038            DevmapHash,
2039            StructOps,
2040            RingBuf,
2041            InodeStorage,
2042            TaskStorage,
2043            BloomFilter,
2044            UserRingBuf,
2045            Arena,
2046            Unknown,
2047        ] {
2048            // check if discriminants match after a roundtrip conversion
2049            assert_eq!(discriminant(&t), discriminant(&MapType::from(t as u32)));
2050        }
2051    }
2052
2053    #[test]
2054    fn parse_hex_decimal() {
2055        assert_eq!(parse_hex("0").unwrap(), 0);
2056        assert_eq!(parse_hex("42").unwrap(), 42);
2057        assert_eq!(parse_hex("18446744073709551615").unwrap(), u64::MAX);
2058    }
2059
2060    #[test]
2061    fn parse_hex_hex_prefix() {
2062        assert_eq!(parse_hex("0x0").unwrap(), 0);
2063        assert_eq!(parse_hex("0xff").unwrap(), 255);
2064        assert_eq!(parse_hex("0X1A").unwrap(), 26);
2065        assert_eq!(parse_hex("0xdeadbeef").unwrap(), 0xdeadbeef);
2066    }
2067
2068    #[test]
2069    fn parse_hex_invalid() {
2070        assert!(parse_hex("").is_err());
2071        assert!(parse_hex("xyz").is_err());
2072        assert!(parse_hex("0xGG").is_err());
2073    }
2074}