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::io;
9use std::marker::PhantomData;
10use std::mem;
11use std::mem::transmute;
12use std::ops::Deref;
13use std::os::unix::ffi::OsStrExt;
14use std::os::unix::io::AsFd;
15use std::os::unix::io::AsRawFd;
16use std::os::unix::io::BorrowedFd;
17use std::os::unix::io::FromRawFd;
18use std::os::unix::io::OwnedFd;
19use std::os::unix::io::RawFd;
20use std::path::Path;
21use std::ptr;
22use std::ptr::NonNull;
23use std::slice;
24use std::slice::from_raw_parts;
25
26use bitflags::bitflags;
27use libbpf_sys::bpf_map_info;
28use libbpf_sys::bpf_obj_get_info_by_fd;
29
30use crate::util;
31use crate::util::parse_ret_i32;
32use crate::util::validate_bpf_ret;
33use crate::AsRawLibbpf;
34use crate::Error;
35use crate::ErrorExt as _;
36use crate::Link;
37use crate::Mut;
38use crate::Result;
39
40pub type OpenMap<'obj> = OpenMapImpl<'obj>;
42pub type OpenMapMut<'obj> = OpenMapImpl<'obj, Mut>;
44
45
46#[derive(Debug)]
53#[repr(transparent)]
54pub struct OpenMapImpl<'obj, T = ()> {
55 ptr: NonNull<libbpf_sys::bpf_map>,
56 _phantom: PhantomData<&'obj T>,
57}
58
59#[allow(missing_docs)]
61impl<'obj> OpenMap<'obj> {
62 pub fn new(object: &'obj libbpf_sys::bpf_map) -> Self {
64 Self {
67 ptr: unsafe { NonNull::new_unchecked(object as *const _ as *mut _) },
68 _phantom: PhantomData,
69 }
70 }
71
72 pub fn name(&self) -> &OsStr {
74 let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
76 let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
79 OsStr::from_bytes(name_c_str.to_bytes())
80 }
81
82 pub fn map_type(&self) -> MapType {
84 let ty = unsafe { libbpf_sys::bpf_map__type(self.ptr.as_ptr()) };
85 MapType::from(ty)
86 }
87
88 fn initial_value_raw(&self) -> (*mut u8, usize) {
89 let mut size = 0u64;
90 let ptr = unsafe {
91 libbpf_sys::bpf_map__initial_value(self.ptr.as_ptr(), &mut size as *mut _ as _)
92 };
93 (ptr.cast(), size as _)
94 }
95
96 pub fn initial_value(&self) -> Option<&[u8]> {
98 let (ptr, size) = self.initial_value_raw();
99 if ptr.is_null() {
100 None
101 } else {
102 let data = unsafe { slice::from_raw_parts(ptr.cast::<u8>(), size) };
103 Some(data)
104 }
105 }
106}
107
108impl<'obj> OpenMapMut<'obj> {
109 pub fn new_mut(object: &'obj mut libbpf_sys::bpf_map) -> Self {
111 Self {
112 ptr: unsafe { NonNull::new_unchecked(object as *mut _) },
113 _phantom: PhantomData,
114 }
115 }
116
117 pub fn initial_value_mut(&mut self) -> Option<&mut [u8]> {
119 let (ptr, size) = self.initial_value_raw();
120 if ptr.is_null() {
121 None
122 } else {
123 let data = unsafe { slice::from_raw_parts_mut(ptr.cast::<u8>(), size) };
124 Some(data)
125 }
126 }
127
128 pub fn set_map_ifindex(&mut self, idx: u32) {
132 unsafe { libbpf_sys::bpf_map__set_ifindex(self.ptr.as_ptr(), idx) };
133 }
134
135 pub fn set_initial_value(&mut self, data: &[u8]) -> Result<()> {
137 let ret = unsafe {
138 libbpf_sys::bpf_map__set_initial_value(
139 self.ptr.as_ptr(),
140 data.as_ptr() as *const c_void,
141 data.len() as libbpf_sys::size_t,
142 )
143 };
144
145 util::parse_ret(ret)
146 }
147
148 pub fn set_type(&mut self, ty: MapType) -> Result<()> {
150 let ret = unsafe { libbpf_sys::bpf_map__set_type(self.ptr.as_ptr(), ty as u32) };
151 util::parse_ret(ret)
152 }
153
154 pub fn set_key_size(&mut self, size: u32) -> Result<()> {
156 let ret = unsafe { libbpf_sys::bpf_map__set_key_size(self.ptr.as_ptr(), size) };
157 util::parse_ret(ret)
158 }
159
160 pub fn set_value_size(&mut self, size: u32) -> Result<()> {
162 let ret = unsafe { libbpf_sys::bpf_map__set_value_size(self.ptr.as_ptr(), size) };
163 util::parse_ret(ret)
164 }
165
166 pub fn set_max_entries(&mut self, count: u32) -> Result<()> {
168 let ret = unsafe { libbpf_sys::bpf_map__set_max_entries(self.ptr.as_ptr(), count) };
169 util::parse_ret(ret)
170 }
171
172 pub fn set_map_flags(&mut self, flags: u32) -> Result<()> {
174 let ret = unsafe { libbpf_sys::bpf_map__set_map_flags(self.ptr.as_ptr(), flags) };
175 util::parse_ret(ret)
176 }
177
178 #[allow(missing_docs)]
180 pub fn set_numa_node(&mut self, numa_node: u32) -> Result<()> {
181 let ret = unsafe { libbpf_sys::bpf_map__set_numa_node(self.ptr.as_ptr(), numa_node) };
182 util::parse_ret(ret)
183 }
184
185 #[allow(missing_docs)]
187 pub fn set_inner_map_fd(&mut self, inner_map_fd: BorrowedFd<'_>) -> Result<()> {
188 let ret = unsafe {
189 libbpf_sys::bpf_map__set_inner_map_fd(self.ptr.as_ptr(), inner_map_fd.as_raw_fd())
190 };
191 util::parse_ret(ret)
192 }
193
194 #[allow(missing_docs)]
196 pub fn set_map_extra(&mut self, map_extra: u64) -> Result<()> {
197 let ret = unsafe { libbpf_sys::bpf_map__set_map_extra(self.ptr.as_ptr(), map_extra) };
198 util::parse_ret(ret)
199 }
200
201 pub fn set_autocreate(&mut self, autocreate: bool) -> Result<()> {
203 let ret = unsafe { libbpf_sys::bpf_map__set_autocreate(self.ptr.as_ptr(), autocreate) };
204 util::parse_ret(ret)
205 }
206
207 pub fn set_pin_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
211 let path_c = util::path_to_cstring(path)?;
212 let path_ptr = path_c.as_ptr();
213
214 let ret = unsafe { libbpf_sys::bpf_map__set_pin_path(self.ptr.as_ptr(), path_ptr) };
215 util::parse_ret(ret)
216 }
217
218 pub fn reuse_fd(&mut self, fd: BorrowedFd<'_>) -> Result<()> {
220 let ret = unsafe { libbpf_sys::bpf_map__reuse_fd(self.ptr.as_ptr(), fd.as_raw_fd()) };
221 util::parse_ret(ret)
222 }
223
224 pub fn reuse_pinned_map<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
226 let cstring = util::path_to_cstring(path)?;
227
228 let fd = unsafe { libbpf_sys::bpf_obj_get(cstring.as_ptr()) };
229 if fd < 0 {
230 return Err(Error::from(io::Error::last_os_error()));
231 }
232
233 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
234
235 let reuse_result = self.reuse_fd(fd.as_fd());
236
237 reuse_result
238 }
239}
240
241impl<'obj> Deref for OpenMapMut<'obj> {
242 type Target = OpenMap<'obj>;
243
244 fn deref(&self) -> &Self::Target {
245 unsafe { transmute::<&OpenMapMut<'obj>, &OpenMap<'obj>>(self) }
248 }
249}
250
251impl<T> AsRawLibbpf for OpenMapImpl<'_, T> {
252 type LibbpfType = libbpf_sys::bpf_map;
253
254 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
256 self.ptr
257 }
258}
259
260pub(crate) fn map_fd(map: NonNull<libbpf_sys::bpf_map>) -> Option<RawFd> {
261 let fd = unsafe { libbpf_sys::bpf_map__fd(map.as_ptr()) };
262 let fd = util::parse_ret_i32(fd).ok().map(|fd| fd as RawFd);
263 fd
264}
265
266fn percpu_aligned_value_size<M>(map: &M) -> usize
269where
270 M: MapCore + ?Sized,
271{
272 let val_size = map.value_size() as usize;
273 util::roundup(val_size, 8)
274}
275
276fn percpu_buffer_size<M>(map: &M) -> Result<usize>
278where
279 M: MapCore + ?Sized,
280{
281 let aligned_val_size = percpu_aligned_value_size(map);
282 let ncpu = crate::num_possible_cpus()?;
283 Ok(ncpu * aligned_val_size)
284}
285
286fn map_key<M>(map: &M, key: &[u8]) -> *const c_void
290where
291 M: MapCore + ?Sized,
292{
293 if map.key_size() == 0 && map.map_type().is_keyless() {
295 return ptr::null();
296 }
297
298 key.as_ptr() as *const c_void
299}
300
301fn lookup_raw<M>(map: &M, key: &[u8], flags: MapFlags, out_size: usize) -> Result<Option<Vec<u8>>>
303where
304 M: MapCore + ?Sized,
305{
306 if key.len() != map.key_size() as usize {
307 return Err(Error::with_invalid_data(format!(
308 "key_size {} != {}",
309 key.len(),
310 map.key_size()
311 )));
312 };
313
314 let mut out: Vec<u8> = Vec::with_capacity(out_size);
315
316 let ret = unsafe {
317 libbpf_sys::bpf_map_lookup_elem_flags(
318 map.as_fd().as_raw_fd(),
319 map_key(map, key),
320 out.as_mut_ptr() as *mut c_void,
321 flags.bits(),
322 )
323 };
324
325 if ret == 0 {
326 unsafe {
327 out.set_len(out_size);
328 }
329 Ok(Some(out))
330 } else {
331 let err = io::Error::last_os_error();
332 if err.kind() == io::ErrorKind::NotFound {
333 Ok(None)
334 } else {
335 Err(Error::from(err))
336 }
337 }
338}
339
340fn update_raw<M>(map: &M, key: &[u8], value: &[u8], flags: MapFlags) -> Result<()>
343where
344 M: MapCore + ?Sized,
345{
346 if key.len() != map.key_size() as usize {
347 return Err(Error::with_invalid_data(format!(
348 "key_size {} != {}",
349 key.len(),
350 map.key_size()
351 )));
352 };
353
354 let ret = unsafe {
355 libbpf_sys::bpf_map_update_elem(
356 map.as_fd().as_raw_fd(),
357 map_key(map, key),
358 value.as_ptr() as *const c_void,
359 flags.bits(),
360 )
361 };
362
363 util::parse_ret(ret)
364}
365
366#[allow(clippy::wildcard_imports)]
367mod private {
368 use super::*;
369
370 pub trait Sealed {}
371
372 impl<T> Sealed for MapImpl<'_, T> {}
373 impl Sealed for MapHandle {}
374}
375
376pub trait MapCore: Debug + AsFd + private::Sealed {
378 fn name(&self) -> &OsStr;
380
381 fn map_type(&self) -> MapType;
383
384 fn key_size(&self) -> u32;
386
387 fn value_size(&self) -> u32;
389
390 #[inline]
392 fn info(&self) -> Result<MapInfo> {
393 MapInfo::new(self.as_fd())
394 }
395
396 fn keys(&self) -> MapKeyIter<'_> {
402 MapKeyIter::new(self.as_fd(), self.key_size())
403 }
404
405 fn lookup(&self, key: &[u8], flags: MapFlags) -> Result<Option<Vec<u8>>> {
413 if self.map_type().is_bloom_filter() {
414 return Err(Error::with_invalid_data(
415 "lookup_bloom_filter() must be used for bloom filter maps",
416 ));
417 }
418 if self.map_type().is_percpu() {
419 return Err(Error::with_invalid_data(format!(
420 "lookup_percpu() must be used for per-cpu maps (type of the map is {:?})",
421 self.map_type(),
422 )));
423 }
424
425 let out_size = self.value_size() as usize;
426 lookup_raw(self, key, flags, out_size)
427 }
428
429 fn lookup_bloom_filter(&self, value: &[u8]) -> Result<bool> {
433 let ret = unsafe {
434 libbpf_sys::bpf_map_lookup_elem(
435 self.as_fd().as_raw_fd(),
436 ptr::null(),
437 value.to_vec().as_mut_ptr() as *mut c_void,
438 )
439 };
440
441 if ret == 0 {
442 Ok(true)
443 } else {
444 let err = io::Error::last_os_error();
445 if err.kind() == io::ErrorKind::NotFound {
446 Ok(false)
447 } else {
448 Err(Error::from(err))
449 }
450 }
451 }
452
453 fn lookup_percpu(&self, key: &[u8], flags: MapFlags) -> Result<Option<Vec<Vec<u8>>>> {
457 if !self.map_type().is_percpu() && self.map_type() != MapType::Unknown {
458 return Err(Error::with_invalid_data(format!(
459 "lookup() must be used for maps that are not per-cpu (type of the map is {:?})",
460 self.map_type(),
461 )));
462 }
463
464 let val_size = self.value_size() as usize;
465 let aligned_val_size = percpu_aligned_value_size(self);
466 let out_size = percpu_buffer_size(self)?;
467
468 let raw_res = lookup_raw(self, key, flags, out_size)?;
469 if let Some(raw_vals) = raw_res {
470 let mut out = Vec::new();
471 for chunk in raw_vals.chunks_exact(aligned_val_size) {
472 out.push(chunk[..val_size].to_vec());
473 }
474 Ok(Some(out))
475 } else {
476 Ok(None)
477 }
478 }
479
480 fn delete(&self, key: &[u8]) -> Result<()> {
484 if key.len() != self.key_size() as usize {
485 return Err(Error::with_invalid_data(format!(
486 "key_size {} != {}",
487 key.len(),
488 self.key_size()
489 )));
490 };
491
492 let ret = unsafe {
493 libbpf_sys::bpf_map_delete_elem(self.as_fd().as_raw_fd(), key.as_ptr() as *const c_void)
494 };
495 util::parse_ret(ret)
496 }
497
498 fn delete_batch(
502 &self,
503 keys: &[u8],
504 count: u32,
505 elem_flags: MapFlags,
506 flags: MapFlags,
507 ) -> Result<()> {
508 if keys.len() as u32 / count != self.key_size() || (keys.len() as u32) % count != 0 {
509 return Err(Error::with_invalid_data(format!(
510 "batch key_size {} != {} * {}",
511 keys.len(),
512 self.key_size(),
513 count
514 )));
515 };
516
517 #[allow(clippy::needless_update)]
518 let opts = libbpf_sys::bpf_map_batch_opts {
519 sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
520 elem_flags: elem_flags.bits(),
521 flags: flags.bits(),
522 ..Default::default()
524 };
525
526 let mut count = count;
527 let ret = unsafe {
528 libbpf_sys::bpf_map_delete_batch(
529 self.as_fd().as_raw_fd(),
530 keys.as_ptr() as *const c_void,
531 &mut count,
532 &opts as *const libbpf_sys::bpf_map_batch_opts,
533 )
534 };
535 util::parse_ret(ret)
536 }
537
538 fn lookup_and_delete(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
545 if key.len() != self.key_size() as usize {
546 return Err(Error::with_invalid_data(format!(
547 "key_size {} != {}",
548 key.len(),
549 self.key_size()
550 )));
551 };
552
553 let mut out: Vec<u8> = Vec::with_capacity(self.value_size() as usize);
554
555 let ret = unsafe {
556 libbpf_sys::bpf_map_lookup_and_delete_elem(
557 self.as_fd().as_raw_fd(),
558 map_key(self, key),
559 out.as_mut_ptr() as *mut c_void,
560 )
561 };
562
563 if ret == 0 {
564 unsafe {
565 out.set_len(self.value_size() as usize);
566 }
567 Ok(Some(out))
568 } else {
569 let err = io::Error::last_os_error();
570 if err.kind() == io::ErrorKind::NotFound {
571 Ok(None)
572 } else {
573 Err(Error::from(err))
574 }
575 }
576 }
577
578 fn update(&self, key: &[u8], value: &[u8], flags: MapFlags) -> Result<()> {
585 if self.map_type().is_percpu() {
586 return Err(Error::with_invalid_data(format!(
587 "update_percpu() must be used for per-cpu maps (type of the map is {:?})",
588 self.map_type(),
589 )));
590 }
591
592 if value.len() != self.value_size() as usize {
593 return Err(Error::with_invalid_data(format!(
594 "value_size {} != {}",
595 value.len(),
596 self.value_size()
597 )));
598 };
599
600 update_raw(self, key, value, flags)
601 }
602
603 fn update_batch(
608 &self,
609 keys: &[u8],
610 values: &[u8],
611 count: u32,
612 elem_flags: MapFlags,
613 flags: MapFlags,
614 ) -> Result<()> {
615 if keys.len() as u32 / count != self.key_size() || (keys.len() as u32) % count != 0 {
616 return Err(Error::with_invalid_data(format!(
617 "batch key_size {} != {} * {}",
618 keys.len(),
619 self.key_size(),
620 count
621 )));
622 };
623
624 if values.len() as u32 / count != self.value_size() || (values.len() as u32) % count != 0 {
625 return Err(Error::with_invalid_data(format!(
626 "batch value_size {} != {} * {}",
627 values.len(),
628 self.value_size(),
629 count
630 )));
631 }
632
633 #[allow(clippy::needless_update)]
634 let opts = libbpf_sys::bpf_map_batch_opts {
635 sz: mem::size_of::<libbpf_sys::bpf_map_batch_opts>() as _,
636 elem_flags: elem_flags.bits(),
637 flags: flags.bits(),
638 ..Default::default()
640 };
641
642 let mut count = count;
643 let ret = unsafe {
644 libbpf_sys::bpf_map_update_batch(
645 self.as_fd().as_raw_fd(),
646 keys.as_ptr() as *const c_void,
647 values.as_ptr() as *const c_void,
648 &mut count,
649 &opts as *const libbpf_sys::bpf_map_batch_opts,
650 )
651 };
652
653 util::parse_ret(ret)
654 }
655
656 fn update_percpu(&self, key: &[u8], values: &[Vec<u8>], flags: MapFlags) -> Result<()> {
664 if !self.map_type().is_percpu() && self.map_type() != MapType::Unknown {
665 return Err(Error::with_invalid_data(format!(
666 "update() must be used for maps that are not per-cpu (type of the map is {:?})",
667 self.map_type(),
668 )));
669 }
670
671 if values.len() != crate::num_possible_cpus()? {
672 return Err(Error::with_invalid_data(format!(
673 "number of values {} != number of cpus {}",
674 values.len(),
675 crate::num_possible_cpus()?
676 )));
677 };
678
679 let val_size = self.value_size() as usize;
680 let aligned_val_size = percpu_aligned_value_size(self);
681 let buf_size = percpu_buffer_size(self)?;
682
683 let mut value_buf = vec![0; buf_size];
684
685 for (i, val) in values.iter().enumerate() {
686 if val.len() != val_size {
687 return Err(Error::with_invalid_data(format!(
688 "value size for cpu {} is {} != {}",
689 i,
690 val.len(),
691 val_size
692 )));
693 }
694
695 value_buf[(i * aligned_val_size)..(i * aligned_val_size + val_size)]
696 .copy_from_slice(val);
697 }
698
699 update_raw(self, key, &value_buf, flags)
700 }
701}
702
703pub type Map<'obj> = MapImpl<'obj>;
705pub type MapMut<'obj> = MapImpl<'obj, Mut>;
707
708#[derive(Debug)]
713pub struct MapImpl<'obj, T = ()> {
714 ptr: NonNull<libbpf_sys::bpf_map>,
715 _phantom: PhantomData<&'obj T>,
716}
717
718impl<'obj> Map<'obj> {
719 pub fn new(map: &'obj libbpf_sys::bpf_map) -> Self {
721 let ptr = unsafe { NonNull::new_unchecked(map as *const _ as *mut _) };
724 assert!(
725 map_fd(ptr).is_some(),
726 "provided BPF map does not have file descriptor"
727 );
728
729 Self {
730 ptr,
731 _phantom: PhantomData,
732 }
733 }
734
735 #[doc(hidden)]
745 pub unsafe fn from_map_without_fd(ptr: NonNull<libbpf_sys::bpf_map>) -> Self {
746 Self {
747 ptr,
748 _phantom: PhantomData,
749 }
750 }
751
752 pub fn is_pinned(&self) -> bool {
754 unsafe { libbpf_sys::bpf_map__is_pinned(self.ptr.as_ptr()) }
755 }
756
757 pub fn get_pin_path(&self) -> Option<&OsStr> {
759 let path_ptr = unsafe { libbpf_sys::bpf_map__pin_path(self.ptr.as_ptr()) };
760 if path_ptr.is_null() {
761 return None;
763 }
764 let path_c_str = unsafe { CStr::from_ptr(path_ptr) };
765 Some(OsStr::from_bytes(path_c_str.to_bytes()))
766 }
767}
768
769impl<'obj> MapMut<'obj> {
770 pub fn new_mut(map: &'obj mut libbpf_sys::bpf_map) -> Self {
772 let ptr = unsafe { NonNull::new_unchecked(map as *mut _) };
775 assert!(
776 map_fd(ptr).is_some(),
777 "provided BPF map does not have file descriptor"
778 );
779
780 Self {
781 ptr,
782 _phantom: PhantomData,
783 }
784 }
785
786 pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
789 let path_c = util::path_to_cstring(path)?;
790 let path_ptr = path_c.as_ptr();
791
792 let ret = unsafe { libbpf_sys::bpf_map__pin(self.ptr.as_ptr(), path_ptr) };
793 util::parse_ret(ret)
794 }
795
796 pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
799 let path_c = util::path_to_cstring(path)?;
800 let path_ptr = path_c.as_ptr();
801 let ret = unsafe { libbpf_sys::bpf_map__unpin(self.ptr.as_ptr(), path_ptr) };
802 util::parse_ret(ret)
803 }
804
805 pub fn attach_struct_ops(&mut self) -> Result<Link> {
807 if self.map_type() != MapType::StructOps {
808 return Err(Error::with_invalid_data(format!(
809 "Invalid map type ({:?}) for attach_struct_ops()",
810 self.map_type(),
811 )));
812 }
813
814 let ptr = unsafe { libbpf_sys::bpf_map__attach_struct_ops(self.ptr.as_ptr()) };
815 let ptr = validate_bpf_ret(ptr).context("failed to attach struct_ops")?;
816 let link = unsafe { Link::new(ptr) };
818 Ok(link)
819 }
820}
821
822impl<'obj> Deref for MapMut<'obj> {
823 type Target = Map<'obj>;
824
825 fn deref(&self) -> &Self::Target {
826 unsafe { transmute::<&MapMut<'obj>, &Map<'obj>>(self) }
827 }
828}
829
830impl<T> AsFd for MapImpl<'_, T> {
831 #[inline]
832 fn as_fd(&self) -> BorrowedFd<'_> {
833 let fd = map_fd(self.ptr).unwrap();
836 let fd = unsafe { BorrowedFd::borrow_raw(fd as _) };
839 fd
840 }
841}
842
843impl<T> MapCore for MapImpl<'_, T>
844where
845 T: Debug,
846{
847 fn name(&self) -> &OsStr {
848 let name_ptr = unsafe { libbpf_sys::bpf_map__name(self.ptr.as_ptr()) };
850 let name_c_str = unsafe { CStr::from_ptr(name_ptr) };
853 OsStr::from_bytes(name_c_str.to_bytes())
854 }
855
856 #[inline]
857 fn map_type(&self) -> MapType {
858 let ty = unsafe { libbpf_sys::bpf_map__type(self.ptr.as_ptr()) };
859 MapType::from(ty)
860 }
861
862 #[inline]
863 fn key_size(&self) -> u32 {
864 unsafe { libbpf_sys::bpf_map__key_size(self.ptr.as_ptr()) }
865 }
866
867 #[inline]
868 fn value_size(&self) -> u32 {
869 unsafe { libbpf_sys::bpf_map__value_size(self.ptr.as_ptr()) }
870 }
871}
872
873impl AsRawLibbpf for Map<'_> {
874 type LibbpfType = libbpf_sys::bpf_map;
875
876 #[inline]
878 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
879 self.ptr
880 }
881}
882
883#[derive(Debug)]
898pub struct MapHandle {
899 fd: OwnedFd,
900 name: OsString,
901 ty: MapType,
902 key_size: u32,
903 value_size: u32,
904}
905
906impl MapHandle {
907 pub fn create<T: AsRef<OsStr>>(
909 map_type: MapType,
910 name: Option<T>,
911 key_size: u32,
912 value_size: u32,
913 max_entries: u32,
914 opts: &libbpf_sys::bpf_map_create_opts,
915 ) -> Result<Self> {
916 let name = match name {
917 Some(name) => name.as_ref().to_os_string(),
918 None => OsString::new(),
920 };
921 let name_c_str = CString::new(name.as_bytes()).map_err(|_| {
922 Error::with_invalid_data(format!("invalid name `{name:?}`: has NUL bytes"))
923 })?;
924 let name_c_ptr = if name.is_empty() {
925 ptr::null()
926 } else {
927 name_c_str.as_bytes_with_nul().as_ptr()
928 };
929
930 let fd = unsafe {
931 libbpf_sys::bpf_map_create(
932 map_type.into(),
933 name_c_ptr.cast(),
934 key_size,
935 value_size,
936 max_entries,
937 opts,
938 )
939 };
940 let () = util::parse_ret(fd)?;
941
942 Ok(Self {
943 fd: unsafe { OwnedFd::from_raw_fd(fd) },
947 name,
948 ty: map_type,
949 key_size,
950 value_size,
951 })
952 }
953
954 pub fn from_pinned_path<P: AsRef<Path>>(path: P) -> Result<Self> {
959 fn inner(path: &Path) -> Result<MapHandle> {
960 let p = CString::new(path.as_os_str().as_bytes()).expect("path contained null bytes");
961 let fd = parse_ret_i32(unsafe {
962 libbpf_sys::bpf_obj_get(p.as_ptr())
965 })?;
966 MapHandle::from_fd(unsafe {
967 OwnedFd::from_raw_fd(fd)
971 })
972 }
973
974 inner(path.as_ref())
975 }
976
977 pub fn from_map_id(id: u32) -> Result<Self> {
979 parse_ret_i32(unsafe {
980 libbpf_sys::bpf_map_get_fd_by_id(id)
983 })
984 .map(|fd| unsafe {
985 OwnedFd::from_raw_fd(fd)
989 })
990 .and_then(Self::from_fd)
991 }
992
993 fn from_fd(fd: OwnedFd) -> Result<Self> {
994 let info = MapInfo::new(fd.as_fd())?;
995 Ok(Self {
996 fd,
997 name: info.name()?.into(),
998 ty: info.map_type(),
999 key_size: info.info.key_size,
1000 value_size: info.info.value_size,
1001 })
1002 }
1003
1004 pub fn freeze(&self) -> Result<()> {
1011 let ret = unsafe { libbpf_sys::bpf_map_freeze(self.fd.as_raw_fd()) };
1012
1013 util::parse_ret(ret)
1014 }
1015
1016 pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1019 let path_c = util::path_to_cstring(path)?;
1020 let path_ptr = path_c.as_ptr();
1021
1022 let ret = unsafe { libbpf_sys::bpf_obj_pin(self.fd.as_raw_fd(), path_ptr) };
1023 util::parse_ret(ret)
1024 }
1025
1026 pub fn unpin<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
1029 remove_file(path).context("failed to remove pin map")
1030 }
1031}
1032
1033impl MapCore for MapHandle {
1034 #[inline]
1035 fn name(&self) -> &OsStr {
1036 &self.name
1037 }
1038
1039 #[inline]
1040 fn map_type(&self) -> MapType {
1041 self.ty
1042 }
1043
1044 #[inline]
1045 fn key_size(&self) -> u32 {
1046 self.key_size
1047 }
1048
1049 #[inline]
1050 fn value_size(&self) -> u32 {
1051 self.value_size
1052 }
1053}
1054
1055impl AsFd for MapHandle {
1056 #[inline]
1057 fn as_fd(&self) -> BorrowedFd<'_> {
1058 self.fd.as_fd()
1059 }
1060}
1061
1062impl<T> TryFrom<&MapImpl<'_, T>> for MapHandle
1063where
1064 T: Debug,
1065{
1066 type Error = Error;
1067
1068 fn try_from(other: &MapImpl<'_, T>) -> Result<Self> {
1069 Ok(Self {
1070 fd: other
1071 .as_fd()
1072 .try_clone_to_owned()
1073 .context("failed to duplicate map file descriptor")?,
1074 name: other.name().to_os_string(),
1075 ty: other.map_type(),
1076 key_size: other.key_size(),
1077 value_size: other.value_size(),
1078 })
1079 }
1080}
1081
1082impl TryFrom<&MapHandle> for MapHandle {
1083 type Error = Error;
1084
1085 fn try_from(other: &MapHandle) -> Result<Self> {
1086 Ok(Self {
1087 fd: other
1088 .as_fd()
1089 .try_clone_to_owned()
1090 .context("failed to duplicate map file descriptor")?,
1091 name: other.name().to_os_string(),
1092 ty: other.map_type(),
1093 key_size: other.key_size(),
1094 value_size: other.value_size(),
1095 })
1096 }
1097}
1098
1099bitflags! {
1100 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
1102 pub struct MapFlags: u64 {
1103 const ANY = libbpf_sys::BPF_ANY as _;
1105 const NO_EXIST = libbpf_sys::BPF_NOEXIST as _;
1107 const EXIST = libbpf_sys::BPF_EXIST as _;
1109 const LOCK = libbpf_sys::BPF_F_LOCK as _;
1111 }
1112}
1113
1114#[non_exhaustive]
1117#[repr(u32)]
1118#[derive(Copy, Clone, PartialEq, Eq, Debug)]
1119#[allow(missing_docs)]
1121pub enum MapType {
1122 Unspec = libbpf_sys::BPF_MAP_TYPE_UNSPEC,
1123 Hash = libbpf_sys::BPF_MAP_TYPE_HASH,
1124 Array = libbpf_sys::BPF_MAP_TYPE_ARRAY,
1125 ProgArray = libbpf_sys::BPF_MAP_TYPE_PROG_ARRAY,
1126 PerfEventArray = libbpf_sys::BPF_MAP_TYPE_PERF_EVENT_ARRAY,
1127 PercpuHash = libbpf_sys::BPF_MAP_TYPE_PERCPU_HASH,
1128 PercpuArray = libbpf_sys::BPF_MAP_TYPE_PERCPU_ARRAY,
1129 StackTrace = libbpf_sys::BPF_MAP_TYPE_STACK_TRACE,
1130 CgroupArray = libbpf_sys::BPF_MAP_TYPE_CGROUP_ARRAY,
1131 LruHash = libbpf_sys::BPF_MAP_TYPE_LRU_HASH,
1132 LruPercpuHash = libbpf_sys::BPF_MAP_TYPE_LRU_PERCPU_HASH,
1133 LpmTrie = libbpf_sys::BPF_MAP_TYPE_LPM_TRIE,
1134 ArrayOfMaps = libbpf_sys::BPF_MAP_TYPE_ARRAY_OF_MAPS,
1135 HashOfMaps = libbpf_sys::BPF_MAP_TYPE_HASH_OF_MAPS,
1136 Devmap = libbpf_sys::BPF_MAP_TYPE_DEVMAP,
1137 Sockmap = libbpf_sys::BPF_MAP_TYPE_SOCKMAP,
1138 Cpumap = libbpf_sys::BPF_MAP_TYPE_CPUMAP,
1139 Xskmap = libbpf_sys::BPF_MAP_TYPE_XSKMAP,
1140 Sockhash = libbpf_sys::BPF_MAP_TYPE_SOCKHASH,
1141 CgroupStorage = libbpf_sys::BPF_MAP_TYPE_CGROUP_STORAGE,
1142 ReuseportSockarray = libbpf_sys::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
1143 PercpuCgroupStorage = libbpf_sys::BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
1144 Queue = libbpf_sys::BPF_MAP_TYPE_QUEUE,
1145 Stack = libbpf_sys::BPF_MAP_TYPE_STACK,
1146 SkStorage = libbpf_sys::BPF_MAP_TYPE_SK_STORAGE,
1147 DevmapHash = libbpf_sys::BPF_MAP_TYPE_DEVMAP_HASH,
1148 StructOps = libbpf_sys::BPF_MAP_TYPE_STRUCT_OPS,
1149 RingBuf = libbpf_sys::BPF_MAP_TYPE_RINGBUF,
1150 InodeStorage = libbpf_sys::BPF_MAP_TYPE_INODE_STORAGE,
1151 TaskStorage = libbpf_sys::BPF_MAP_TYPE_TASK_STORAGE,
1152 BloomFilter = libbpf_sys::BPF_MAP_TYPE_BLOOM_FILTER,
1153 UserRingBuf = libbpf_sys::BPF_MAP_TYPE_USER_RINGBUF,
1154 Unknown = u32::MAX,
1158}
1159
1160impl MapType {
1161 pub fn is_percpu(&self) -> bool {
1163 matches!(
1164 self,
1165 MapType::PercpuArray
1166 | MapType::PercpuHash
1167 | MapType::LruPercpuHash
1168 | MapType::PercpuCgroupStorage
1169 )
1170 }
1171
1172 fn is_keyless(&self) -> bool {
1175 matches!(self, MapType::Queue | MapType::Stack | MapType::BloomFilter)
1176 }
1177
1178 pub fn is_bloom_filter(&self) -> bool {
1180 MapType::BloomFilter.eq(self)
1181 }
1182
1183 pub fn is_supported(&self) -> Result<bool> {
1188 let ret = unsafe { libbpf_sys::libbpf_probe_bpf_map_type(*self as u32, ptr::null()) };
1189 match ret {
1190 0 => Ok(false),
1191 1 => Ok(true),
1192 _ => Err(Error::from_raw_os_error(-ret)),
1193 }
1194 }
1195}
1196
1197impl From<u32> for MapType {
1198 fn from(value: u32) -> Self {
1199 use MapType::*;
1200
1201 match value {
1202 x if x == Unspec as u32 => Unspec,
1203 x if x == Hash as u32 => Hash,
1204 x if x == Array as u32 => Array,
1205 x if x == ProgArray as u32 => ProgArray,
1206 x if x == PerfEventArray as u32 => PerfEventArray,
1207 x if x == PercpuHash as u32 => PercpuHash,
1208 x if x == PercpuArray as u32 => PercpuArray,
1209 x if x == StackTrace as u32 => StackTrace,
1210 x if x == CgroupArray as u32 => CgroupArray,
1211 x if x == LruHash as u32 => LruHash,
1212 x if x == LruPercpuHash as u32 => LruPercpuHash,
1213 x if x == LpmTrie as u32 => LpmTrie,
1214 x if x == ArrayOfMaps as u32 => ArrayOfMaps,
1215 x if x == HashOfMaps as u32 => HashOfMaps,
1216 x if x == Devmap as u32 => Devmap,
1217 x if x == Sockmap as u32 => Sockmap,
1218 x if x == Cpumap as u32 => Cpumap,
1219 x if x == Xskmap as u32 => Xskmap,
1220 x if x == Sockhash as u32 => Sockhash,
1221 x if x == CgroupStorage as u32 => CgroupStorage,
1222 x if x == ReuseportSockarray as u32 => ReuseportSockarray,
1223 x if x == PercpuCgroupStorage as u32 => PercpuCgroupStorage,
1224 x if x == Queue as u32 => Queue,
1225 x if x == Stack as u32 => Stack,
1226 x if x == SkStorage as u32 => SkStorage,
1227 x if x == DevmapHash as u32 => DevmapHash,
1228 x if x == StructOps as u32 => StructOps,
1229 x if x == RingBuf as u32 => RingBuf,
1230 x if x == InodeStorage as u32 => InodeStorage,
1231 x if x == TaskStorage as u32 => TaskStorage,
1232 x if x == BloomFilter as u32 => BloomFilter,
1233 x if x == UserRingBuf as u32 => UserRingBuf,
1234 _ => Unknown,
1235 }
1236 }
1237}
1238
1239impl From<MapType> for u32 {
1240 fn from(value: MapType) -> Self {
1241 value as u32
1242 }
1243}
1244
1245#[derive(Debug)]
1247pub struct MapKeyIter<'map> {
1248 map_fd: BorrowedFd<'map>,
1249 prev: Option<Vec<u8>>,
1250 next: Vec<u8>,
1251}
1252
1253impl<'map> MapKeyIter<'map> {
1254 fn new(map_fd: BorrowedFd<'map>, key_size: u32) -> Self {
1255 Self {
1256 map_fd,
1257 prev: None,
1258 next: vec![0; key_size as usize],
1259 }
1260 }
1261}
1262
1263impl Iterator for MapKeyIter<'_> {
1264 type Item = Vec<u8>;
1265
1266 fn next(&mut self) -> Option<Self::Item> {
1267 let prev = self.prev.as_ref().map_or(ptr::null(), |p| p.as_ptr());
1268
1269 let ret = unsafe {
1270 libbpf_sys::bpf_map_get_next_key(
1271 self.map_fd.as_raw_fd(),
1272 prev as _,
1273 self.next.as_mut_ptr() as _,
1274 )
1275 };
1276 if ret != 0 {
1277 None
1278 } else {
1279 self.prev = Some(self.next.clone());
1280 Some(self.next.clone())
1281 }
1282 }
1283}
1284
1285#[derive(Debug)]
1288pub struct MapInfo {
1289 pub info: bpf_map_info,
1291}
1292
1293impl MapInfo {
1294 pub fn new(fd: BorrowedFd<'_>) -> Result<Self> {
1296 let mut map_info = bpf_map_info::default();
1297 let mut size = mem::size_of_val(&map_info) as u32;
1298 let () = util::parse_ret(unsafe {
1300 bpf_obj_get_info_by_fd(
1301 fd.as_raw_fd(),
1302 &mut map_info as *mut bpf_map_info as *mut c_void,
1303 &mut size as *mut u32,
1304 )
1305 })?;
1306 Ok(Self { info: map_info })
1307 }
1308
1309 #[inline]
1311 pub fn map_type(&self) -> MapType {
1312 MapType::from(self.info.type_)
1313 }
1314
1315 pub fn name<'a>(&self) -> Result<&'a str> {
1320 let char_slice =
1322 unsafe { from_raw_parts(self.info.name[..].as_ptr().cast(), self.info.name.len()) };
1323
1324 util::c_char_slice_to_cstr(char_slice)
1325 .ok_or_else(|| Error::with_invalid_data("no nul byte found"))?
1326 .to_str()
1327 .map_err(Error::with_invalid_data)
1328 }
1329
1330 #[inline]
1332 pub fn flags(&self) -> MapFlags {
1333 MapFlags::from_bits_truncate(self.info.map_flags as u64)
1334 }
1335}
1336
1337#[cfg(test)]
1338mod tests {
1339 use super::*;
1340
1341 use std::mem::discriminant;
1342
1343 #[test]
1344 fn map_type() {
1345 use MapType::*;
1346
1347 for t in [
1348 Unspec,
1349 Hash,
1350 Array,
1351 ProgArray,
1352 PerfEventArray,
1353 PercpuHash,
1354 PercpuArray,
1355 StackTrace,
1356 CgroupArray,
1357 LruHash,
1358 LruPercpuHash,
1359 LpmTrie,
1360 ArrayOfMaps,
1361 HashOfMaps,
1362 Devmap,
1363 Sockmap,
1364 Cpumap,
1365 Xskmap,
1366 Sockhash,
1367 CgroupStorage,
1368 ReuseportSockarray,
1369 PercpuCgroupStorage,
1370 Queue,
1371 Stack,
1372 SkStorage,
1373 DevmapHash,
1374 StructOps,
1375 RingBuf,
1376 InodeStorage,
1377 TaskStorage,
1378 BloomFilter,
1379 UserRingBuf,
1380 Unknown,
1381 ] {
1382 assert_eq!(discriminant(&t), discriminant(&MapType::from(t as u32)));
1384 }
1385 }
1386}