aya_ebpf/maps/xdp/dev_map_hash.rs
1use core::{cell::UnsafeCell, mem, num::NonZeroU32, ptr::NonNull};
2
3use aya_ebpf_bindings::bindings::bpf_devmap_val;
4use aya_ebpf_cty::c_void;
5
6use super::{dev_map::DevMapValue, try_redirect_map};
7use crate::{
8 bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH},
9 helpers::bpf_map_lookup_elem,
10 maps::PinningType,
11};
12
13/// A map of network devices.
14///
15/// XDP programs can use this map to redirect packets to other network devices. It is similar to
16/// [`DevMap`](super::DevMap), but is an hash map rather than an array. Keys do not need to be
17/// contiguous nor start at zero, but there is a hashing cost to every lookup.
18///
19/// # Minimum kernel version
20///
21/// The minimum kernel version required to use this feature is 5.4.
22///
23/// # Examples
24///
25/// ```rust,no_run
26/// use aya_ebpf::{bindings::xdp_action, macros::{map, xdp}, maps::DevMapHash, programs::XdpContext};
27///
28/// #[map]
29/// static MAP: DevMapHash = DevMapHash::with_max_entries(1, 0);
30///
31/// #[xdp]
32/// fn xdp(_ctx: XdpContext) -> u32 {
33/// MAP.redirect(42, xdp_action::XDP_PASS as u64).unwrap_or(xdp_action::XDP_DROP)
34/// }
35/// ```
36#[repr(transparent)]
37pub struct DevMapHash {
38 def: UnsafeCell<bpf_map_def>,
39}
40
41unsafe impl Sync for DevMapHash {}
42
43impl DevMapHash {
44 /// Creates a [`DevMapHash`] with a set maximum number of elements.
45 ///
46 /// # Examples
47 ///
48 /// ```rust,no_run
49 /// use aya_ebpf::{macros::map, maps::DevMapHash};
50 ///
51 /// #[map]
52 /// static MAP: DevMapHash = DevMapHash::with_max_entries(8, 0);
53 /// ```
54 pub const fn with_max_entries(max_entries: u32, flags: u32) -> DevMapHash {
55 DevMapHash {
56 def: UnsafeCell::new(bpf_map_def {
57 type_: BPF_MAP_TYPE_DEVMAP_HASH,
58 key_size: mem::size_of::<u32>() as u32,
59 value_size: mem::size_of::<bpf_devmap_val>() as u32,
60 max_entries,
61 map_flags: flags,
62 id: 0,
63 pinning: PinningType::None as u32,
64 }),
65 }
66 }
67
68 /// Creates a [`DevMapHash`] with a set maximum number of elements that can be pinned to the BPF
69 /// File System (bpffs).
70 ///
71 /// # Examples
72 ///
73 /// ```rust,no_run
74 /// use aya_ebpf::{macros::map, maps::DevMapHash};
75 ///
76 /// #[map]
77 /// static MAP: DevMapHash = DevMapHash::pinned(8, 0);
78 /// ```
79 pub const fn pinned(max_entries: u32, flags: u32) -> DevMapHash {
80 DevMapHash {
81 def: UnsafeCell::new(bpf_map_def {
82 type_: BPF_MAP_TYPE_DEVMAP_HASH,
83 key_size: mem::size_of::<u32>() as u32,
84 value_size: mem::size_of::<bpf_devmap_val>() as u32,
85 max_entries,
86 map_flags: flags,
87 id: 0,
88 pinning: PinningType::ByName as u32,
89 }),
90 }
91 }
92
93 /// Retrieves the interface index with `key` in the map.
94 ///
95 /// To actually redirect a packet, see [`DevMapHash::redirect`].
96 ///
97 /// # Examples
98 ///
99 /// ```rust,no_run
100 /// use aya_ebpf::{macros::map, maps::DevMapHash};
101 ///
102 /// #[map]
103 /// static MAP: DevMapHash = DevMapHash::with_max_entries(1, 0);
104 ///
105 /// let target_if_index = MAP.get(42).unwrap().if_index;
106 ///
107 /// // redirect to ifindex
108 /// ```
109 #[inline(always)]
110 pub fn get(&self, key: u32) -> Option<DevMapValue> {
111 unsafe {
112 let value =
113 bpf_map_lookup_elem(self.def.get() as *mut _, &key as *const _ as *const c_void);
114 NonNull::new(value as *mut bpf_devmap_val).map(|p| DevMapValue {
115 if_index: p.as_ref().ifindex,
116 // SAFETY: map writes use fd, map reads use id.
117 // https://elixir.bootlin.com/linux/v6.2/source/include/uapi/linux/bpf.h#L6136
118 prog_id: NonZeroU32::new(p.as_ref().bpf_prog.id),
119 })
120 }
121 }
122
123 /// Redirects the current packet on the interface at `key`.
124 ///
125 /// The lower two bits of `flags` are used for the return code if the map lookup fails, which
126 /// can be used as the XDP program's return code if a CPU cannot be found.
127 ///
128 /// # Examples
129 ///
130 /// ```rust,no_run
131 /// use aya_ebpf::{bindings::xdp_action, macros::{map, xdp}, maps::DevMapHash, programs::XdpContext};
132 ///
133 /// #[map]
134 /// static MAP: DevMapHash = DevMapHash::with_max_entries(8, 0);
135 ///
136 /// #[xdp]
137 /// fn xdp(_ctx: XdpContext) -> u32 {
138 /// MAP.redirect(7, 0).unwrap_or(xdp_action::XDP_DROP)
139 /// }
140 /// ```
141 #[inline(always)]
142 pub fn redirect(&self, key: u32, flags: u64) -> Result<u32, u32> {
143 try_redirect_map(&self.def, key, flags)
144 }
145}