Skip to main content

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}