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    /// We choose to specify our own "unknown" type here b/c it's really up to the kernel
1566    /// to decide if it wants to reject the map. If it accepts it, it just means whoever
1567    /// using this library is a bit out of date.
1568    Unknown = u32::MAX,
1569}
1570
1571impl MapType {
1572    /// Returns if the map is of one of the per-cpu types.
1573    pub fn is_percpu(&self) -> bool {
1574        matches!(
1575            self,
1576            Self::PercpuArray | Self::PercpuHash | Self::LruPercpuHash | Self::PercpuCgroupStorage
1577        )
1578    }
1579
1580    /// Returns if the map is of one of the hashmap types.
1581    pub fn is_hash_map(&self) -> bool {
1582        matches!(
1583            self,
1584            Self::Hash | Self::PercpuHash | Self::LruHash | Self::LruPercpuHash
1585        )
1586    }
1587
1588    /// Returns if the map is keyless map type as per documentation of libbpf
1589    /// Keyless map types are: Queues, Stacks and Bloom Filters
1590    fn is_keyless(&self) -> bool {
1591        matches!(self, Self::Queue | Self::Stack | Self::BloomFilter)
1592    }
1593
1594    /// Returns if the map is of bloom filter type
1595    pub fn is_bloom_filter(&self) -> bool {
1596        Self::BloomFilter.eq(self)
1597    }
1598
1599    /// Detects if host kernel supports this BPF map type.
1600    ///
1601    /// Make sure the process has required set of CAP_* permissions (or runs as
1602    /// root) when performing feature checking.
1603    #[doc(alias = "libbpf_probe_bpf_map_type")]
1604    pub fn is_supported(&self) -> Result<bool> {
1605        let ret = unsafe { libbpf_sys::libbpf_probe_bpf_map_type(*self as u32, ptr::null()) };
1606        match ret {
1607            0 => Ok(false),
1608            1 => Ok(true),
1609            _ => Err(Error::from_raw_os_error(-ret)),
1610        }
1611    }
1612}
1613
1614impl From<u32> for MapType {
1615    fn from(value: u32) -> Self {
1616        use MapType::*;
1617
1618        match value {
1619            x if x == Unspec as u32 => Unspec,
1620            x if x == Hash as u32 => Hash,
1621            x if x == Array as u32 => Array,
1622            x if x == ProgArray as u32 => ProgArray,
1623            x if x == PerfEventArray as u32 => PerfEventArray,
1624            x if x == PercpuHash as u32 => PercpuHash,
1625            x if x == PercpuArray as u32 => PercpuArray,
1626            x if x == StackTrace as u32 => StackTrace,
1627            x if x == CgroupArray as u32 => CgroupArray,
1628            x if x == LruHash as u32 => LruHash,
1629            x if x == LruPercpuHash as u32 => LruPercpuHash,
1630            x if x == LpmTrie as u32 => LpmTrie,
1631            x if x == ArrayOfMaps as u32 => ArrayOfMaps,
1632            x if x == HashOfMaps as u32 => HashOfMaps,
1633            x if x == Devmap as u32 => Devmap,
1634            x if x == Sockmap as u32 => Sockmap,
1635            x if x == Cpumap as u32 => Cpumap,
1636            x if x == Xskmap as u32 => Xskmap,
1637            x if x == Sockhash as u32 => Sockhash,
1638            x if x == CgroupStorage as u32 => CgroupStorage,
1639            x if x == ReuseportSockarray as u32 => ReuseportSockarray,
1640            x if x == PercpuCgroupStorage as u32 => PercpuCgroupStorage,
1641            x if x == Queue as u32 => Queue,
1642            x if x == Stack as u32 => Stack,
1643            x if x == SkStorage as u32 => SkStorage,
1644            x if x == DevmapHash as u32 => DevmapHash,
1645            x if x == StructOps as u32 => StructOps,
1646            x if x == RingBuf as u32 => RingBuf,
1647            x if x == InodeStorage as u32 => InodeStorage,
1648            x if x == TaskStorage as u32 => TaskStorage,
1649            x if x == BloomFilter as u32 => BloomFilter,
1650            x if x == UserRingBuf as u32 => UserRingBuf,
1651            _ => Unknown,
1652        }
1653    }
1654}
1655
1656impl From<MapType> for u32 {
1657    fn from(value: MapType) -> Self {
1658        value as Self
1659    }
1660}
1661
1662/// An iterator over the keys of a BPF map.
1663#[derive(Debug)]
1664#[doc(alias = "bpf_map_get_next_key")]
1665pub struct MapKeyIter<'map> {
1666    map_fd: BorrowedFd<'map>,
1667    prev: Option<Vec<u8>>,
1668    next: Vec<u8>,
1669}
1670
1671impl<'map> MapKeyIter<'map> {
1672    fn new(map_fd: BorrowedFd<'map>, key_size: u32) -> Self {
1673        Self {
1674            map_fd,
1675            prev: None,
1676            next: vec![0; key_size as usize],
1677        }
1678    }
1679}
1680
1681impl Iterator for MapKeyIter<'_> {
1682    type Item = Vec<u8>;
1683
1684    fn next(&mut self) -> Option<Self::Item> {
1685        let prev = self.prev.as_ref().map_or(ptr::null(), Vec::as_ptr);
1686
1687        let ret = unsafe {
1688            libbpf_sys::bpf_map_get_next_key(
1689                self.map_fd.as_raw_fd(),
1690                prev.cast(),
1691                self.next.as_mut_ptr().cast(),
1692            )
1693        };
1694        if ret != 0 {
1695            None
1696        } else {
1697            self.prev = Some(self.next.clone());
1698            Some(self.next.clone())
1699        }
1700    }
1701}
1702
1703/// An iterator over batches of key value pairs of a BPF map.
1704#[derive(Debug)]
1705#[doc(alias = "bpf_map_lookup_batch")]
1706#[doc(alias = "bpf_map_lookup_and_delete_batch")]
1707pub struct BatchedMapIter<'map> {
1708    map_fd: BorrowedFd<'map>,
1709    delete: bool,
1710    count: usize,
1711    key_size: usize,
1712    value_size: usize,
1713    keys: Vec<u8>,
1714    values: Vec<u8>,
1715    prev: Option<Vec<u8>>,
1716    next: Vec<u8>,
1717    batch_opts: libbpf_sys::bpf_map_batch_opts,
1718    index: Option<usize>,
1719}
1720
1721impl<'map> BatchedMapIter<'map> {
1722    fn new(
1723        map_fd: BorrowedFd<'map>,
1724        count: u32,
1725        key_size: u32,
1726        value_size: u32,
1727        batch_opts: libbpf_sys::bpf_map_batch_opts,
1728        delete: bool,
1729    ) -> Self {
1730        Self {
1731            map_fd,
1732            delete,
1733            count: count as usize,
1734            key_size: key_size as usize,
1735            value_size: value_size as usize,
1736            keys: vec![0; (count * key_size) as usize],
1737            values: vec![0; (count * value_size) as usize],
1738            prev: None,
1739            next: vec![0; key_size as usize],
1740            batch_opts,
1741            index: None,
1742        }
1743    }
1744
1745    fn lookup_next_batch(&mut self) {
1746        let prev = self.prev.as_mut().map_or(ptr::null_mut(), Vec::as_mut_ptr);
1747        let mut count = self.count as u32;
1748
1749        let ret = unsafe {
1750            let lookup_fn = if self.delete {
1751                libbpf_sys::bpf_map_lookup_and_delete_batch
1752            } else {
1753                libbpf_sys::bpf_map_lookup_batch
1754            };
1755            lookup_fn(
1756                self.map_fd.as_raw_fd(),
1757                prev.cast(),
1758                self.next.as_mut_ptr().cast(),
1759                self.keys.as_mut_ptr().cast(),
1760                self.values.as_mut_ptr().cast(),
1761                &mut count,
1762                &self.batch_opts,
1763            )
1764        };
1765
1766        if let Err(e) = util::parse_ret(ret) {
1767            match e.kind() {
1768                // in this case we can trust the returned count value
1769                error::ErrorKind::NotFound => {}
1770                // retry with same input arguments
1771                error::ErrorKind::Interrupted => {
1772                    return self.lookup_next_batch();
1773                }
1774                _ => {
1775                    self.index = None;
1776                    return;
1777                }
1778            }
1779        }
1780
1781        self.prev = Some(self.next.clone());
1782        self.index = Some(0);
1783
1784        unsafe {
1785            self.keys.set_len(self.key_size * count as usize);
1786            self.values.set_len(self.value_size * count as usize);
1787        }
1788    }
1789}
1790
1791impl Iterator for BatchedMapIter<'_> {
1792    type Item = (Vec<u8>, Vec<u8>);
1793
1794    fn next(&mut self) -> Option<Self::Item> {
1795        let load_next_batch = match self.index {
1796            Some(index) => {
1797                let batch_finished = index * self.key_size >= self.keys.len();
1798                let last_batch = self.keys.len() < self.key_size * self.count;
1799                batch_finished && !last_batch
1800            }
1801            None => true,
1802        };
1803
1804        if load_next_batch {
1805            self.lookup_next_batch();
1806        }
1807
1808        let index = self.index?;
1809        let key = self.keys.chunks_exact(self.key_size).nth(index)?.to_vec();
1810        let val = self
1811            .values
1812            .chunks_exact(self.value_size)
1813            .nth(index)?
1814            .to_vec();
1815
1816        self.index = Some(index + 1);
1817        Some((key, val))
1818    }
1819}
1820
1821/// A convenience wrapper for [`bpf_map_info`]. It provides the ability
1822/// to retrieve the details of a certain map.
1823#[derive(Debug)]
1824#[doc(alias = "bpf_map_info")]
1825pub struct MapInfo {
1826    /// The inner [`bpf_map_info`] object.
1827    pub info: bpf_map_info,
1828}
1829
1830impl MapInfo {
1831    /// Create a `MapInfo` object from a fd.
1832    #[doc(alias = "bpf_obj_get_info_by_fd")]
1833    pub fn new(fd: BorrowedFd<'_>) -> Result<Self> {
1834        let mut map_info = bpf_map_info::default();
1835        let mut size = mem::size_of_val(&map_info) as u32;
1836        // SAFETY: All pointers are derived from references and hence valid.
1837        let () = util::parse_ret(unsafe {
1838            bpf_obj_get_info_by_fd(
1839                fd.as_raw_fd(),
1840                (&mut map_info as *mut bpf_map_info).cast::<c_void>(),
1841                &mut size as *mut u32,
1842            )
1843        })?;
1844        Ok(Self { info: map_info })
1845    }
1846
1847    /// Get the map type
1848    #[inline]
1849    pub fn map_type(&self) -> MapType {
1850        MapType::from(self.info.type_)
1851    }
1852
1853    /// Get the name of this map.
1854    ///
1855    /// Returns error if the underlying data in the structure is not a valid
1856    /// utf-8 string.
1857    pub fn name<'a>(&self) -> Result<&'a str> {
1858        // SAFETY: convert &[i8] to &[u8], and then cast that to &str. i8 and u8 has the same size.
1859        let char_slice =
1860            unsafe { from_raw_parts(self.info.name[..].as_ptr().cast(), self.info.name.len()) };
1861
1862        util::c_char_slice_to_cstr(char_slice)
1863            .ok_or_else(|| Error::with_invalid_data("no nul byte found"))?
1864            .to_str()
1865            .map_err(Error::with_invalid_data)
1866    }
1867
1868    /// Get the map flags.
1869    #[inline]
1870    pub fn flags(&self) -> MapFlags {
1871        MapFlags::from_bits_truncate(self.info.map_flags as u64)
1872    }
1873}
1874
1875/// Information about a BPF map obtained from `/proc/self/fdinfo`.
1876///
1877/// This provides information not available through [`MapInfo`], such as
1878/// [`memlock`][MapFdInfo::memlock] (memory usage) and [`frozen`][MapFdInfo::frozen] status.
1879///
1880/// The fields correspond to those printed by
1881/// [`bpf_map_show_fdinfo`](https://github.com/torvalds/linux/blob/37a93dd5c49b/kernel/bpf/syscall.c#L1007)
1882/// in the kernel source. See also bpftool's
1883/// [`get_fdinfo`](https://github.com/torvalds/linux/blob/37a93dd5c49/tools/bpf/bpftool/common.c#L485)
1884/// for the matching userspace parsing logic.
1885#[derive(Debug, Clone)]
1886pub struct MapFdInfo {
1887    /// The map type.
1888    pub map_type: MapType,
1889    /// The size of the map's keys in bytes.
1890    pub key_size: u32,
1891    /// The size of the map's values in bytes.
1892    pub value_size: u32,
1893    /// The maximum number of entries in the map.
1894    pub max_entries: u32,
1895    // The following fields were added in later kernel versions and may not be
1896    // present in older kernels.
1897    /// The map flags.
1898    pub map_flags: Option<u32>,
1899    /// Extra map-specific data.
1900    pub map_extra: Option<u64>,
1901    /// The amount of memory locked by the map in bytes.
1902    pub memlock: Option<u64>,
1903    /// The map's ID.
1904    pub map_id: Option<u32>,
1905    /// Whether the map is frozen.
1906    pub frozen: Option<bool>,
1907    /// The type of the owner program (only for `prog_array` maps).
1908    pub owner_prog_type: Option<ProgramType>,
1909    /// Whether the owner program is JIT-compiled (only for `prog_array` maps).
1910    pub owner_jited: Option<bool>,
1911}
1912
1913impl MapFdInfo {
1914    /// Create a `MapFdInfo` by reading `/proc/self/fdinfo` for the given fd.
1915    pub fn from_fd(fd: BorrowedFd<'_>) -> Result<Self> {
1916        let path = format!("/proc/self/fdinfo/{}", fd.as_raw_fd());
1917        let file = File::open(&path).with_context(|| format!("failed to open `{path}`"))?;
1918        let reader = BufReader::new(file);
1919
1920        let parse = |key: &str, val: &str| -> Result<u32> {
1921            val.parse()
1922                .map_err(|e| Error::with_invalid_data(format!("`{key}`: {e}")))
1923        };
1924
1925        let mut map_type = None;
1926        let mut key_size = None;
1927        let mut value_size = None;
1928        let mut max_entries = None;
1929        let mut map_flags = None;
1930        let mut map_extra = None;
1931        let mut memlock = None;
1932        let mut map_id = None;
1933        let mut frozen = None;
1934        let mut owner_prog_type = None;
1935        let mut owner_jited = None;
1936
1937        for result in reader.lines() {
1938            let line = result?;
1939            let Some((key, value)) = line.split_once('\t') else {
1940                continue;
1941            };
1942            // Keys have a trailing colon, e.g. "map_type:"
1943            let key = key.trim_end_matches(':');
1944            let value = value.trim();
1945
1946            match key {
1947                "map_type" => map_type = Some(parse(key, value)?),
1948                "key_size" => key_size = Some(parse(key, value)?),
1949                "value_size" => value_size = Some(parse(key, value)?),
1950                "max_entries" => max_entries = Some(parse(key, value)?),
1951                "map_flags" => {
1952                    map_flags =
1953                        Some(parse_hex(value).with_context(|| format!("bad `{key}`"))? as u32)
1954                }
1955                "map_extra" => {
1956                    map_extra = Some(parse_hex(value).with_context(|| format!("bad `{key}`"))?)
1957                }
1958                "memlock" => memlock = Some(parse(key, value)? as u64),
1959                "map_id" => map_id = Some(parse(key, value)?),
1960                "frozen" => frozen = Some(parse(key, value)? != 0),
1961                "owner_prog_type" => owner_prog_type = Some(parse(key, value)?),
1962                "owner_jited" => owner_jited = Some(parse(key, value)? != 0),
1963                _ => {}
1964            }
1965        }
1966
1967        let missing = |f| Error::with_invalid_data(format!("missing `{f}` in fdinfo"));
1968
1969        Ok(Self {
1970            map_type: MapType::from(map_type.ok_or_else(|| missing("map_type"))?),
1971            key_size: key_size.ok_or_else(|| missing("key_size"))?,
1972            value_size: value_size.ok_or_else(|| missing("value_size"))?,
1973            max_entries: max_entries.ok_or_else(|| missing("max_entries"))?,
1974            map_flags,
1975            map_extra,
1976            memlock,
1977            map_id,
1978            frozen,
1979            owner_prog_type: owner_prog_type.map(ProgramType::from),
1980            owner_jited,
1981        })
1982    }
1983}
1984
1985/// Parse a value that may be in hex (0x...) or decimal format.
1986fn parse_hex(s: &str) -> Result<u64> {
1987    if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
1988        u64::from_str_radix(hex, 16)
1989    } else {
1990        s.parse()
1991    }
1992    .map_err(Error::with_invalid_data)
1993}
1994
1995#[cfg(test)]
1996mod tests {
1997    use super::*;
1998
1999    use std::mem::discriminant;
2000
2001    #[test]
2002    fn map_type() {
2003        use MapType::*;
2004
2005        for t in [
2006            Unspec,
2007            Hash,
2008            Array,
2009            ProgArray,
2010            PerfEventArray,
2011            PercpuHash,
2012            PercpuArray,
2013            StackTrace,
2014            CgroupArray,
2015            LruHash,
2016            LruPercpuHash,
2017            LpmTrie,
2018            ArrayOfMaps,
2019            HashOfMaps,
2020            Devmap,
2021            Sockmap,
2022            Cpumap,
2023            Xskmap,
2024            Sockhash,
2025            CgroupStorage,
2026            ReuseportSockarray,
2027            PercpuCgroupStorage,
2028            Queue,
2029            Stack,
2030            SkStorage,
2031            DevmapHash,
2032            StructOps,
2033            RingBuf,
2034            InodeStorage,
2035            TaskStorage,
2036            BloomFilter,
2037            UserRingBuf,
2038            Unknown,
2039        ] {
2040            // check if discriminants match after a roundtrip conversion
2041            assert_eq!(discriminant(&t), discriminant(&MapType::from(t as u32)));
2042        }
2043    }
2044
2045    #[test]
2046    fn parse_hex_decimal() {
2047        assert_eq!(parse_hex("0").unwrap(), 0);
2048        assert_eq!(parse_hex("42").unwrap(), 42);
2049        assert_eq!(parse_hex("18446744073709551615").unwrap(), u64::MAX);
2050    }
2051
2052    #[test]
2053    fn parse_hex_hex_prefix() {
2054        assert_eq!(parse_hex("0x0").unwrap(), 0);
2055        assert_eq!(parse_hex("0xff").unwrap(), 255);
2056        assert_eq!(parse_hex("0X1A").unwrap(), 26);
2057        assert_eq!(parse_hex("0xdeadbeef").unwrap(), 0xdeadbeef);
2058    }
2059
2060    #[test]
2061    fn parse_hex_invalid() {
2062        assert!(parse_hex("").is_err());
2063        assert!(parse_hex("xyz").is_err());
2064        assert!(parse_hex("0xGG").is_err());
2065    }
2066}