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 Arena = libbpf_sys::BPF_MAP_TYPE_ARENA,
1571 Unknown = u32::MAX,
1575}
1576
1577impl MapType {
1578 pub fn is_percpu(&self) -> bool {
1580 matches!(
1581 self,
1582 Self::PercpuArray | Self::PercpuHash | Self::LruPercpuHash | Self::PercpuCgroupStorage
1583 )
1584 }
1585
1586 pub fn is_hash_map(&self) -> bool {
1588 matches!(
1589 self,
1590 Self::Hash | Self::PercpuHash | Self::LruHash | Self::LruPercpuHash
1591 )
1592 }
1593
1594 fn is_keyless(&self) -> bool {
1597 matches!(self, Self::Queue | Self::Stack | Self::BloomFilter)
1598 }
1599
1600 pub fn is_bloom_filter(&self) -> bool {
1602 Self::BloomFilter.eq(self)
1603 }
1604
1605 #[doc(alias = "libbpf_probe_bpf_map_type")]
1610 pub fn is_supported(&self) -> Result<bool> {
1611 let ret = unsafe { libbpf_sys::libbpf_probe_bpf_map_type(*self as u32, ptr::null()) };
1612 match ret {
1613 0 => Ok(false),
1614 1 => Ok(true),
1615 _ => Err(Error::from_raw_os_error(-ret)),
1616 }
1617 }
1618}
1619
1620impl From<u32> for MapType {
1621 fn from(value: u32) -> Self {
1622 use MapType::*;
1623
1624 match value {
1625 x if x == Unspec as u32 => Unspec,
1626 x if x == Hash as u32 => Hash,
1627 x if x == Array as u32 => Array,
1628 x if x == ProgArray as u32 => ProgArray,
1629 x if x == PerfEventArray as u32 => PerfEventArray,
1630 x if x == PercpuHash as u32 => PercpuHash,
1631 x if x == PercpuArray as u32 => PercpuArray,
1632 x if x == StackTrace as u32 => StackTrace,
1633 x if x == CgroupArray as u32 => CgroupArray,
1634 x if x == LruHash as u32 => LruHash,
1635 x if x == LruPercpuHash as u32 => LruPercpuHash,
1636 x if x == LpmTrie as u32 => LpmTrie,
1637 x if x == ArrayOfMaps as u32 => ArrayOfMaps,
1638 x if x == HashOfMaps as u32 => HashOfMaps,
1639 x if x == Devmap as u32 => Devmap,
1640 x if x == Sockmap as u32 => Sockmap,
1641 x if x == Cpumap as u32 => Cpumap,
1642 x if x == Xskmap as u32 => Xskmap,
1643 x if x == Sockhash as u32 => Sockhash,
1644 x if x == CgroupStorage as u32 => CgroupStorage,
1645 x if x == ReuseportSockarray as u32 => ReuseportSockarray,
1646 x if x == PercpuCgroupStorage as u32 => PercpuCgroupStorage,
1647 x if x == Queue as u32 => Queue,
1648 x if x == Stack as u32 => Stack,
1649 x if x == SkStorage as u32 => SkStorage,
1650 x if x == DevmapHash as u32 => DevmapHash,
1651 x if x == StructOps as u32 => StructOps,
1652 x if x == RingBuf as u32 => RingBuf,
1653 x if x == InodeStorage as u32 => InodeStorage,
1654 x if x == TaskStorage as u32 => TaskStorage,
1655 x if x == BloomFilter as u32 => BloomFilter,
1656 x if x == UserRingBuf as u32 => UserRingBuf,
1657 x if x == Arena as u32 => Arena,
1658 _ => Unknown,
1659 }
1660 }
1661}
1662
1663impl From<MapType> for u32 {
1664 fn from(value: MapType) -> Self {
1665 value as Self
1666 }
1667}
1668
1669#[derive(Debug)]
1671#[doc(alias = "bpf_map_get_next_key")]
1672pub struct MapKeyIter<'map> {
1673 map_fd: BorrowedFd<'map>,
1674 prev: Option<Vec<u8>>,
1675 next: Vec<u8>,
1676}
1677
1678impl<'map> MapKeyIter<'map> {
1679 fn new(map_fd: BorrowedFd<'map>, key_size: u32) -> Self {
1680 Self {
1681 map_fd,
1682 prev: None,
1683 next: vec![0; key_size as usize],
1684 }
1685 }
1686}
1687
1688impl Iterator for MapKeyIter<'_> {
1689 type Item = Vec<u8>;
1690
1691 fn next(&mut self) -> Option<Self::Item> {
1692 let prev = self.prev.as_ref().map_or(ptr::null(), Vec::as_ptr);
1693
1694 let ret = unsafe {
1695 libbpf_sys::bpf_map_get_next_key(
1696 self.map_fd.as_raw_fd(),
1697 prev.cast(),
1698 self.next.as_mut_ptr().cast(),
1699 )
1700 };
1701 if ret != 0 {
1702 None
1703 } else {
1704 self.prev = Some(self.next.clone());
1705 Some(self.next.clone())
1706 }
1707 }
1708}
1709
1710#[derive(Debug)]
1712#[doc(alias = "bpf_map_lookup_batch")]
1713#[doc(alias = "bpf_map_lookup_and_delete_batch")]
1714pub struct BatchedMapIter<'map> {
1715 map_fd: BorrowedFd<'map>,
1716 delete: bool,
1717 count: usize,
1718 key_size: usize,
1719 value_size: usize,
1720 keys: Vec<u8>,
1721 values: Vec<u8>,
1722 prev: Option<Vec<u8>>,
1723 next: Vec<u8>,
1724 batch_opts: libbpf_sys::bpf_map_batch_opts,
1725 index: Option<usize>,
1726}
1727
1728impl<'map> BatchedMapIter<'map> {
1729 fn new(
1730 map_fd: BorrowedFd<'map>,
1731 count: u32,
1732 key_size: u32,
1733 value_size: u32,
1734 batch_opts: libbpf_sys::bpf_map_batch_opts,
1735 delete: bool,
1736 ) -> Self {
1737 Self {
1738 map_fd,
1739 delete,
1740 count: count as usize,
1741 key_size: key_size as usize,
1742 value_size: value_size as usize,
1743 keys: vec![0; (count * key_size) as usize],
1744 values: vec![0; (count * value_size) as usize],
1745 prev: None,
1746 next: vec![0; key_size as usize],
1747 batch_opts,
1748 index: None,
1749 }
1750 }
1751
1752 fn lookup_next_batch(&mut self) {
1753 let prev = self.prev.as_mut().map_or(ptr::null_mut(), Vec::as_mut_ptr);
1754 let mut count = self.count as u32;
1755
1756 let ret = unsafe {
1757 let lookup_fn = if self.delete {
1758 libbpf_sys::bpf_map_lookup_and_delete_batch
1759 } else {
1760 libbpf_sys::bpf_map_lookup_batch
1761 };
1762 lookup_fn(
1763 self.map_fd.as_raw_fd(),
1764 prev.cast(),
1765 self.next.as_mut_ptr().cast(),
1766 self.keys.as_mut_ptr().cast(),
1767 self.values.as_mut_ptr().cast(),
1768 &mut count,
1769 &self.batch_opts,
1770 )
1771 };
1772
1773 if let Err(e) = util::parse_ret(ret) {
1774 match e.kind() {
1775 error::ErrorKind::NotFound => {}
1777 error::ErrorKind::Interrupted => {
1779 return self.lookup_next_batch();
1780 }
1781 _ => {
1782 self.index = None;
1783 return;
1784 }
1785 }
1786 }
1787
1788 self.prev = Some(self.next.clone());
1789 self.index = Some(0);
1790
1791 unsafe {
1792 self.keys.set_len(self.key_size * count as usize);
1793 self.values.set_len(self.value_size * count as usize);
1794 }
1795 }
1796}
1797
1798impl Iterator for BatchedMapIter<'_> {
1799 type Item = (Vec<u8>, Vec<u8>);
1800
1801 fn next(&mut self) -> Option<Self::Item> {
1802 let load_next_batch = match self.index {
1803 Some(index) => {
1804 let batch_finished = index * self.key_size >= self.keys.len();
1805 let last_batch = self.keys.len() < self.key_size * self.count;
1806 batch_finished && !last_batch
1807 }
1808 None => true,
1809 };
1810
1811 if load_next_batch {
1812 self.lookup_next_batch();
1813 }
1814
1815 let index = self.index?;
1816 let key = self.keys.chunks_exact(self.key_size).nth(index)?.to_vec();
1817 let val = self
1818 .values
1819 .chunks_exact(self.value_size)
1820 .nth(index)?
1821 .to_vec();
1822
1823 self.index = Some(index + 1);
1824 Some((key, val))
1825 }
1826}
1827
1828#[derive(Debug)]
1831#[doc(alias = "bpf_map_info")]
1832pub struct MapInfo {
1833 pub info: bpf_map_info,
1835}
1836
1837impl MapInfo {
1838 #[doc(alias = "bpf_obj_get_info_by_fd")]
1840 pub fn new(fd: BorrowedFd<'_>) -> Result<Self> {
1841 let mut map_info = bpf_map_info::default();
1842 let mut size = mem::size_of_val(&map_info) as u32;
1843 let () = util::parse_ret(unsafe {
1845 bpf_obj_get_info_by_fd(
1846 fd.as_raw_fd(),
1847 (&mut map_info as *mut bpf_map_info).cast::<c_void>(),
1848 &mut size as *mut u32,
1849 )
1850 })?;
1851 Ok(Self { info: map_info })
1852 }
1853
1854 #[inline]
1856 pub fn map_type(&self) -> MapType {
1857 MapType::from(self.info.type_)
1858 }
1859
1860 pub fn name<'a>(&self) -> Result<&'a str> {
1865 let char_slice =
1867 unsafe { from_raw_parts(self.info.name[..].as_ptr().cast(), self.info.name.len()) };
1868
1869 util::c_char_slice_to_cstr(char_slice)
1870 .ok_or_else(|| Error::with_invalid_data("no nul byte found"))?
1871 .to_str()
1872 .map_err(Error::with_invalid_data)
1873 }
1874
1875 #[inline]
1877 pub fn flags(&self) -> MapFlags {
1878 MapFlags::from_bits_truncate(self.info.map_flags as u64)
1879 }
1880}
1881
1882#[derive(Debug, Clone)]
1893pub struct MapFdInfo {
1894 pub map_type: MapType,
1896 pub key_size: u32,
1898 pub value_size: u32,
1900 pub max_entries: u32,
1902 pub map_flags: Option<u32>,
1906 pub map_extra: Option<u64>,
1908 pub memlock: Option<u64>,
1910 pub map_id: Option<u32>,
1912 pub frozen: Option<bool>,
1914 pub owner_prog_type: Option<ProgramType>,
1916 pub owner_jited: Option<bool>,
1918}
1919
1920impl MapFdInfo {
1921 pub fn from_fd(fd: BorrowedFd<'_>) -> Result<Self> {
1923 let path = format!("/proc/self/fdinfo/{}", fd.as_raw_fd());
1924 let file = File::open(&path).with_context(|| format!("failed to open `{path}`"))?;
1925 let reader = BufReader::new(file);
1926
1927 let parse = |key: &str, val: &str| -> Result<u32> {
1928 val.parse()
1929 .map_err(|e| Error::with_invalid_data(format!("`{key}`: {e}")))
1930 };
1931
1932 let mut map_type = None;
1933 let mut key_size = None;
1934 let mut value_size = None;
1935 let mut max_entries = None;
1936 let mut map_flags = None;
1937 let mut map_extra = None;
1938 let mut memlock = None;
1939 let mut map_id = None;
1940 let mut frozen = None;
1941 let mut owner_prog_type = None;
1942 let mut owner_jited = None;
1943
1944 for result in reader.lines() {
1945 let line = result?;
1946 let Some((key, value)) = line.split_once('\t') else {
1947 continue;
1948 };
1949 let key = key.trim_end_matches(':');
1951 let value = value.trim();
1952
1953 match key {
1954 "map_type" => map_type = Some(parse(key, value)?),
1955 "key_size" => key_size = Some(parse(key, value)?),
1956 "value_size" => value_size = Some(parse(key, value)?),
1957 "max_entries" => max_entries = Some(parse(key, value)?),
1958 "map_flags" => {
1959 map_flags =
1960 Some(parse_hex(value).with_context(|| format!("bad `{key}`"))? as u32)
1961 }
1962 "map_extra" => {
1963 map_extra = Some(parse_hex(value).with_context(|| format!("bad `{key}`"))?)
1964 }
1965 "memlock" => memlock = Some(parse(key, value)? as u64),
1966 "map_id" => map_id = Some(parse(key, value)?),
1967 "frozen" => frozen = Some(parse(key, value)? != 0),
1968 "owner_prog_type" => owner_prog_type = Some(parse(key, value)?),
1969 "owner_jited" => owner_jited = Some(parse(key, value)? != 0),
1970 _ => {}
1971 }
1972 }
1973
1974 let missing = |f| Error::with_invalid_data(format!("missing `{f}` in fdinfo"));
1975
1976 Ok(Self {
1977 map_type: MapType::from(map_type.ok_or_else(|| missing("map_type"))?),
1978 key_size: key_size.ok_or_else(|| missing("key_size"))?,
1979 value_size: value_size.ok_or_else(|| missing("value_size"))?,
1980 max_entries: max_entries.ok_or_else(|| missing("max_entries"))?,
1981 map_flags,
1982 map_extra,
1983 memlock,
1984 map_id,
1985 frozen,
1986 owner_prog_type: owner_prog_type.map(ProgramType::from),
1987 owner_jited,
1988 })
1989 }
1990}
1991
1992fn parse_hex(s: &str) -> Result<u64> {
1994 if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
1995 u64::from_str_radix(hex, 16)
1996 } else {
1997 s.parse()
1998 }
1999 .map_err(Error::with_invalid_data)
2000}
2001
2002#[cfg(test)]
2003mod tests {
2004 use super::*;
2005
2006 use std::mem::discriminant;
2007
2008 #[test]
2009 fn map_type() {
2010 use MapType::*;
2011
2012 for t in [
2013 Unspec,
2014 Hash,
2015 Array,
2016 ProgArray,
2017 PerfEventArray,
2018 PercpuHash,
2019 PercpuArray,
2020 StackTrace,
2021 CgroupArray,
2022 LruHash,
2023 LruPercpuHash,
2024 LpmTrie,
2025 ArrayOfMaps,
2026 HashOfMaps,
2027 Devmap,
2028 Sockmap,
2029 Cpumap,
2030 Xskmap,
2031 Sockhash,
2032 CgroupStorage,
2033 ReuseportSockarray,
2034 PercpuCgroupStorage,
2035 Queue,
2036 Stack,
2037 SkStorage,
2038 DevmapHash,
2039 StructOps,
2040 RingBuf,
2041 InodeStorage,
2042 TaskStorage,
2043 BloomFilter,
2044 UserRingBuf,
2045 Arena,
2046 Unknown,
2047 ] {
2048 assert_eq!(discriminant(&t), discriminant(&MapType::from(t as u32)));
2050 }
2051 }
2052
2053 #[test]
2054 fn parse_hex_decimal() {
2055 assert_eq!(parse_hex("0").unwrap(), 0);
2056 assert_eq!(parse_hex("42").unwrap(), 42);
2057 assert_eq!(parse_hex("18446744073709551615").unwrap(), u64::MAX);
2058 }
2059
2060 #[test]
2061 fn parse_hex_hex_prefix() {
2062 assert_eq!(parse_hex("0x0").unwrap(), 0);
2063 assert_eq!(parse_hex("0xff").unwrap(), 255);
2064 assert_eq!(parse_hex("0X1A").unwrap(), 26);
2065 assert_eq!(parse_hex("0xdeadbeef").unwrap(), 0xdeadbeef);
2066 }
2067
2068 #[test]
2069 fn parse_hex_invalid() {
2070 assert!(parse_hex("").is_err());
2071 assert!(parse_hex("xyz").is_err());
2072 assert!(parse_hex("0xGG").is_err());
2073 }
2074}