aya_friday/maps/xdp/dev_map_hash.rs
1//! A hashmap of network devices.
2
3use std::{
4 borrow::{Borrow, BorrowMut},
5 num::NonZeroU32,
6 os::fd::{AsFd as _, AsRawFd as _},
7};
8
9use aya_obj::generated::bpf_devmap_val;
10
11use super::{XdpMapError, dev_map::DevMapValue};
12use crate::{
13 FEATURES,
14 maps::{IterableMap, MapData, MapError, MapIter, MapKeys, check_kv_size, hash_map},
15 programs::ProgramFd,
16 sys::{SyscallError, bpf_map_lookup_elem},
17};
18
19/// An hashmap of network devices.
20///
21/// XDP programs can use this map to redirect to other network
22/// devices.
23///
24/// # Minimum kernel version
25///
26/// The minimum kernel version required to use this feature is 5.4.
27///
28/// # Examples
29/// ```no_run
30/// # let mut bpf = aya::Ebpf::load(&[])?;
31/// use aya::maps::xdp::DevMapHash;
32///
33/// let mut devmap = DevMapHash::try_from(bpf.map_mut("IFACES").unwrap())?;
34/// // Lookups with key 2 will redirect packets to interface with index 3 (e.g. eth1)
35/// devmap.insert(2, 3, None, 0);
36///
37/// # Ok::<(), aya::EbpfError>(())
38/// ```
39///
40/// # See also
41///
42/// Kernel documentation: <https://docs.kernel.org/next/bpf/map_devmap.html>
43#[doc(alias = "BPF_MAP_TYPE_DEVMAP_HASH")]
44pub struct DevMapHash<T> {
45 pub(crate) inner: T,
46}
47
48impl<T: Borrow<MapData>> DevMapHash<T> {
49 pub(crate) fn new(map: T) -> Result<Self, MapError> {
50 let data = map.borrow();
51
52 if FEATURES.devmap_prog_id() {
53 check_kv_size::<u32, bpf_devmap_val>(data)?;
54 } else {
55 check_kv_size::<u32, u32>(data)?;
56 }
57
58 Ok(Self { inner: map })
59 }
60
61 /// Returns the target interface index and optional program for a given key.
62 ///
63 /// # Errors
64 ///
65 /// Returns [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails.
66 pub fn get(&self, key: u32, flags: u64) -> Result<DevMapValue, MapError> {
67 let fd = self.inner.borrow().fd().as_fd();
68
69 let value = if FEATURES.devmap_prog_id() {
70 bpf_map_lookup_elem::<_, bpf_devmap_val>(fd, &key, flags).map(|value| {
71 value.map(|value| DevMapValue {
72 if_index: value.ifindex,
73 // SAFETY: map writes use fd, map reads use id.
74 // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6228
75 prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }),
76 })
77 })
78 } else {
79 bpf_map_lookup_elem::<_, u32>(fd, &key, flags).map(|value| {
80 value.map(|ifindex| DevMapValue {
81 if_index: ifindex,
82 prog_id: None,
83 })
84 })
85 };
86 value
87 .map_err(|io_error| SyscallError {
88 call: "bpf_map_lookup_elem",
89 io_error,
90 })?
91 .ok_or(MapError::KeyNotFound)
92 }
93
94 /// An iterator over the elements of the devmap in arbitrary order.
95 pub fn iter(&self) -> MapIter<'_, u32, DevMapValue, Self> {
96 MapIter::new(self)
97 }
98
99 /// An iterator visiting all keys in arbitrary order.
100 pub fn keys(&self) -> MapKeys<'_, u32> {
101 MapKeys::new(self.inner.borrow())
102 }
103}
104
105impl<'a, T: Borrow<MapData>> IntoIterator for &'a DevMapHash<T> {
106 type Item = Result<(u32, DevMapValue), MapError>;
107 type IntoIter = MapIter<'a, u32, DevMapValue, DevMapHash<T>>;
108
109 fn into_iter(self) -> Self::IntoIter {
110 self.iter()
111 }
112}
113
114impl<T: BorrowMut<MapData>> DevMapHash<T> {
115 /// Inserts an ifindex and optionally a chained program in the map.
116 ///
117 /// When redirecting using `key`, packets will be transmitted by the interface with `ifindex`.
118 ///
119 /// Starting from Linux kernel 5.8, another XDP program can be passed in that will be run before
120 /// actual transmission. It can be used to modify the packet before transmission with NIC
121 /// specific data (MAC address update, checksum computations, etc) or other purposes.
122 ///
123 /// The chained program must be loaded with the `BPF_XDP_DEVMAP` attach type. When using
124 /// `aya-ebpf`, that means XDP programs that specify the `map = "devmap"` argument. See the
125 /// kernel-space `aya_ebpf::xdp` for more information.
126 ///
127 /// # Errors
128 ///
129 /// Returns [`MapError::SyscallError`] if `bpf_map_update_elem` fails,
130 /// [`MapError::ProgIdNotSupported`] if the kernel does not support chained programs and one is
131 /// provided.
132 pub fn insert(
133 &mut self,
134 key: u32,
135 target_if_index: u32,
136 program: Option<&ProgramFd>,
137 flags: u64,
138 ) -> Result<(), XdpMapError> {
139 if FEATURES.devmap_prog_id() {
140 let mut value = unsafe { std::mem::zeroed::<bpf_devmap_val>() };
141 value.ifindex = target_if_index;
142 // Default is valid as the kernel will only consider fd > 0:
143 // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L866
144 // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918
145 value.bpf_prog.fd = program
146 .map(|prog| prog.as_fd().as_raw_fd())
147 .unwrap_or_default();
148 hash_map::insert(self.inner.borrow_mut(), &key, &value, flags)?;
149 } else {
150 if program.is_some() {
151 return Err(XdpMapError::ChainedProgramNotSupported);
152 }
153 hash_map::insert(self.inner.borrow_mut(), &key, &target_if_index, flags)?;
154 }
155 Ok(())
156 }
157
158 /// Removes a value from the map.
159 ///
160 /// # Errors
161 ///
162 /// Returns [`MapError::SyscallError`] if `bpf_map_delete_elem` fails.
163 pub fn remove(&mut self, key: u32) -> Result<(), MapError> {
164 hash_map::remove(self.inner.borrow_mut(), &key)
165 }
166}
167
168impl<T: Borrow<MapData>> IterableMap<u32, DevMapValue> for DevMapHash<T> {
169 fn map(&self) -> &MapData {
170 self.inner.borrow()
171 }
172
173 fn get(&self, key: &u32) -> Result<DevMapValue, MapError> {
174 self.get(*key, 0)
175 }
176}