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
45pub type OpenMap<'obj> = OpenMapImpl<'obj>;
47pub type OpenMapMut<'obj> = OpenMapImpl<'obj, Mut>;
49
50#[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 pub fn new(object: &'obj libbpf_sys::bpf_map) -> Self {
67 Self {
70 ptr: unsafe { NonNull::new_unchecked(object as *const _ as *mut _) },
71 _phantom: PhantomData,
72 }
73 }
74
75 #[doc(alias = "bpf_map__name")]
77 pub fn name(&self) -> &'obj OsStr {
78 let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
80 let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
83 OsStr::from_bytes(name_c_str.to_bytes())
84 }
85
86 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 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
333fn 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
343fn 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
353fn map_key<M>(map: &M, key: &[u8]) -> *const c_void
357where
358 M: MapCore + ?Sized,
359{
360 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
368enum LookupOp {
375 Lookup(MapFlags),
376 LookupAndDelete,
377}
378
379fn 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 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 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
437fn lookup_raw_vec<M>(map: &M, key: &[u8], op: LookupOp, out_size: usize) -> Result<Option<Vec<u8>>>
439where
440 M: MapCore + ?Sized,
441{
442 let mut out = Vec::with_capacity(out_size);
444
445 match lookup_raw(map, key, out.spare_capacity_mut(), op)? {
446 true => {
447 unsafe {
449 out.set_len(out_size);
450 }
451 Ok(Some(out))
452 }
453 false => Ok(None),
454 }
455}
456
457fn 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
483fn 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 ..Default::default()
501 };
502
503 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
514fn 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
544pub trait MapCore: Debug + AsFd + private::Sealed {
546 #[doc(alias = "bpf_map__name")]
548 fn name(&self) -> &OsStr;
549
550 #[doc(alias = "bpf_map__type")]
552 fn map_type(&self) -> MapType;
553
554 #[doc(alias = "bpf_map__key_size")]
556 fn key_size(&self) -> u32;
557
558 #[doc(alias = "bpf_map__value_size")]
560 fn value_size(&self) -> u32;
561
562 #[doc(alias = "bpf_map__max_entries")]
564 fn max_entries(&self) -> u32;
565
566 #[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 #[inline]
578 fn query_fdinfo(&self) -> Result<MapFdInfo> {
579 MapFdInfo::from_fd(self.as_fd())
580 }
581
582 #[doc(alias = "bpf_map_get_next_key")]
588 fn keys(&self) -> MapKeyIter<'_> {
589 MapKeyIter::new(self.as_fd(), self.key_size())
590 }
591
592 #[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 #[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 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 #[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 #[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 #[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 #[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 #[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 #[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 ..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 #[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 #[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 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 #[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 #[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 ..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 #[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
954pub type Map<'obj> = MapImpl<'obj>;
956pub type MapMut<'obj> = MapImpl<'obj, Mut>;
958
959#[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 pub fn new(map: &'obj libbpf_sys::bpf_map) -> Self {
973 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 #[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 #[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 #[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 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 #[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 pub fn new_mut(map: &'obj mut libbpf_sys::bpf_map) -> Self {
1033 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 #[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 #[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 #[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 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 let fd = map_fd(self.ptr).unwrap();
1101 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 let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
1115 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 #[inline]
1148 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
1149 self.ptr
1150 }
1151}
1152
1153#[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 #[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 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 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 #[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 #[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 libbpf_sys::bpf_obj_get_opts(p.as_ptr(), &opts)
1259 })?;
1260 MapHandle::from_fd(unsafe {
1261 OwnedFd::from_raw_fd(fd)
1265 })
1266 }
1267
1268 inner(path.as_ref(), file_flags)
1269 }
1270
1271 #[doc(alias = "bpf_map_get_fd_by_id")]
1273 pub fn from_map_id(id: u32) -> Result<Self> {
1274 parse_ret_i32(unsafe {
1275 libbpf_sys::bpf_map_get_fd_by_id(id)
1278 })
1279 .map(|fd| unsafe {
1280 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 #[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 #[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 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 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
1407 pub struct MapFlags: u64 {
1408 const ANY = libbpf_sys::BPF_ANY as _;
1410 const NO_EXIST = libbpf_sys::BPF_NOEXIST as _;
1412 const EXIST = libbpf_sys::BPF_EXIST as _;
1414 const LOCK = libbpf_sys::BPF_F_LOCK as _;
1416 }
1417}
1418
1419#[non_exhaustive]
1422#[repr(u32)]
1423#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1424#[doc(alias = "bpf_map_type")]
1425pub enum MapType {
1426 Unspec = libbpf_sys::BPF_MAP_TYPE_UNSPEC,
1428 Hash = libbpf_sys::BPF_MAP_TYPE_HASH,
1432 Array = libbpf_sys::BPF_MAP_TYPE_ARRAY,
1436 ProgArray = libbpf_sys::BPF_MAP_TYPE_PROG_ARRAY,
1441 PerfEventArray = libbpf_sys::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
1445 PercpuHash = libbpf_sys::BPF_MAP_TYPE_PERCPU_HASH,
1449 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 LruHash = libbpf_sys::BPF_MAP_TYPE_LRU_HASH,
1461 LruPercpuHash = libbpf_sys::BPF_MAP_TYPE_LRU_PERCPU_HASH,
1465 LpmTrie = libbpf_sys::BPF_MAP_TYPE_LPM_TRIE,
1469 ArrayOfMaps = libbpf_sys::BPF_MAP_TYPE_ARRAY_OF_MAPS,
1475 HashOfMaps = libbpf_sys::BPF_MAP_TYPE_HASH_OF_MAPS,
1481 Devmap = libbpf_sys::BPF_MAP_TYPE_DEVMAP,
1486 Sockmap = libbpf_sys::BPF_MAP_TYPE_SOCKMAP,
1490 Cpumap = libbpf_sys::BPF_MAP_TYPE_CPUMAP,
1494 Xskmap = libbpf_sys::BPF_MAP_TYPE_XSKMAP,
1500 Sockhash = libbpf_sys::BPF_MAP_TYPE_SOCKHASH,
1504 CgroupStorage = libbpf_sys::BPF_MAP_TYPE_CGROUP_STORAGE,
1510 CGrpStorage = libbpf_sys::BPF_MAP_TYPE_CGRP_STORAGE,
1515 ReuseportSockarray = libbpf_sys::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
1519 PercpuCgroupStorage = libbpf_sys::BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
1523 Queue = libbpf_sys::BPF_MAP_TYPE_QUEUE,
1527 Stack = libbpf_sys::BPF_MAP_TYPE_STACK,
1531 SkStorage = libbpf_sys::BPF_MAP_TYPE_SK_STORAGE,
1535 DevmapHash = libbpf_sys::BPF_MAP_TYPE_DEVMAP_HASH,
1540 StructOps = libbpf_sys::BPF_MAP_TYPE_STRUCT_OPS,
1545 RingBuf = libbpf_sys::BPF_MAP_TYPE_RINGBUF,
1549 InodeStorage = libbpf_sys::BPF_MAP_TYPE_INODE_STORAGE,
1553 TaskStorage = libbpf_sys::BPF_MAP_TYPE_TASK_STORAGE,
1557 BloomFilter = libbpf_sys::BPF_MAP_TYPE_BLOOM_FILTER,
1563 #[allow(missing_docs)]
1564 UserRingBuf = libbpf_sys::BPF_MAP_TYPE_USER_RINGBUF,
1565 Unknown = u32::MAX,
1569}
1570
1571impl MapType {
1572 pub fn is_percpu(&self) -> bool {
1574 matches!(
1575 self,
1576 Self::PercpuArray | Self::PercpuHash | Self::LruPercpuHash | Self::PercpuCgroupStorage
1577 )
1578 }
1579
1580 pub fn is_hash_map(&self) -> bool {
1582 matches!(
1583 self,
1584 Self::Hash | Self::PercpuHash | Self::LruHash | Self::LruPercpuHash
1585 )
1586 }
1587
1588 fn is_keyless(&self) -> bool {
1591 matches!(self, Self::Queue | Self::Stack | Self::BloomFilter)
1592 }
1593
1594 pub fn is_bloom_filter(&self) -> bool {
1596 Self::BloomFilter.eq(self)
1597 }
1598
1599 #[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#[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#[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 error::ErrorKind::NotFound => {}
1770 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#[derive(Debug)]
1824#[doc(alias = "bpf_map_info")]
1825pub struct MapInfo {
1826 pub info: bpf_map_info,
1828}
1829
1830impl MapInfo {
1831 #[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 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 #[inline]
1849 pub fn map_type(&self) -> MapType {
1850 MapType::from(self.info.type_)
1851 }
1852
1853 pub fn name<'a>(&self) -> Result<&'a str> {
1858 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 #[inline]
1870 pub fn flags(&self) -> MapFlags {
1871 MapFlags::from_bits_truncate(self.info.map_flags as u64)
1872 }
1873}
1874
1875#[derive(Debug, Clone)]
1886pub struct MapFdInfo {
1887 pub map_type: MapType,
1889 pub key_size: u32,
1891 pub value_size: u32,
1893 pub max_entries: u32,
1895 pub map_flags: Option<u32>,
1899 pub map_extra: Option<u64>,
1901 pub memlock: Option<u64>,
1903 pub map_id: Option<u32>,
1905 pub frozen: Option<bool>,
1907 pub owner_prog_type: Option<ProgramType>,
1909 pub owner_jited: Option<bool>,
1911}
1912
1913impl MapFdInfo {
1914 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 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
1985fn 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 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}