From 545199a3dacfe40f3c1c8705fc6c4a4b3cb1db0a Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:16:30 +0200 Subject: [PATCH] maps/xdp: improve XDP maps documentation --- aya-bpf-macros/src/lib.rs | 29 +++++++++++++++++++++ aya/src/maps/xdp/cpu_map.rs | 35 +++++++++++++++++--------- aya/src/maps/xdp/dev_map.rs | 18 ++++++++++--- aya/src/maps/xdp/dev_map_hash.rs | 39 +++++++++++++++++------------ aya/src/maps/xdp/xsk_map.rs | 10 +++++--- bpf/aya-bpf/src/maps/xdp/cpu_map.rs | 4 +-- 6 files changed, 98 insertions(+), 37 deletions(-) diff --git a/aya-bpf-macros/src/lib.rs b/aya-bpf-macros/src/lib.rs index 5696b82d..5ffbd9ff 100644 --- a/aya-bpf-macros/src/lib.rs +++ b/aya-bpf-macros/src/lib.rs @@ -128,6 +128,35 @@ pub fn sk_msg(attrs: TokenStream, item: TokenStream) -> TokenStream { } } +/// Marks a function as an eBPF XDP program that can be attached to a network interface. +/// +/// On some NIC drivers, XDP probes are compatible with jumbo frames through the use of +/// multi-buffer packets. Programs can opt-in this support by passing the `frags = "true"` argument. +/// +/// XDP programs can also be chained through the use of CPU maps and dev maps, but must opt-in +/// with the `map = "cpumap"` or `map = "devmap"` arguments. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.8. +/// +/// # Examples +/// +/// ```no_run +/// use aya_bpf::{bindings::xdp_action::XDP_PASS, macros::xdp, programs::XdpContext}; +/// +/// #[xdp(frags = "true")] +/// pub fn xdp(ctx: XdpContext) -> u32 { +/// match unsafe { try_xdp(ctx) } { +/// Ok(ret) => ret, +/// Err(ret) => ret, +/// } +/// } +/// +/// unsafe fn try_xdp(_ctx: XdpContext) -> Result { +/// Ok(XDP_PASS) +/// } +/// ``` #[proc_macro_error] #[proc_macro_attribute] pub fn xdp(attrs: TokenStream, item: TokenStream) -> TokenStream { diff --git a/aya/src/maps/xdp/cpu_map.rs b/aya/src/maps/xdp/cpu_map.rs index c494ce9f..20455b00 100644 --- a/aya/src/maps/xdp/cpu_map.rs +++ b/aya/src/maps/xdp/cpu_map.rs @@ -60,19 +60,19 @@ impl> CpuMap { self.inner.borrow().obj.max_entries() } - /// Returns the value stored at the given index. + /// Returns the queue size and possible program for a given CPU index. /// /// # Errors /// - /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] - /// if `bpf_map_lookup_elem` fails. - pub fn get(&self, index: u32, flags: u64) -> Result { + /// Returns [`MapError::OutOfBounds`] if `cpu_index` is out of bounds, + /// [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails. + pub fn get(&self, cpu_index: u32, flags: u64) -> Result { let data = self.inner.borrow(); - check_bounds(data, index)?; + check_bounds(data, cpu_index)?; let fd = data.fd; let value = - bpf_map_lookup_elem(fd, &index, flags).map_err(|(_, io_error)| SyscallError { + bpf_map_lookup_elem(fd, &cpu_index, flags).map_err(|(_, io_error)| SyscallError { call: "bpf_map_lookup_elem", io_error, })?; @@ -88,7 +88,18 @@ impl> CpuMap { } impl> CpuMap { - /// Sets the value of the element at the given index. + /// Sets the queue size at the given CPU index, and optionally a chained program. + /// + /// When sending the packet to the CPU at the given index, the kernel will queue up to + /// `queue_size` packets before dropping them. + /// + /// Another XDP program can be passed in that will be run on the target CPU, instead of the CPU + /// that receives the packets. This allows to perform minimal computations on CPUs that + /// directly handle packets from a NIC's RX queues, and perform possibly heavier ones in other, + /// less busy CPUs. + /// + /// Note that only XDP programs with the `map = "cpumap"` argument can be passed. See the + /// kernel-space `aya_bpf::xdp` for more information. /// /// # Errors /// @@ -96,22 +107,22 @@ impl> CpuMap { /// if `bpf_map_update_elem` fails. pub fn set( &mut self, - index: u32, - value: u32, + cpu_index: u32, + queue_size: u32, program: Option, flags: u64, ) -> Result<(), MapError> { let data = self.inner.borrow_mut(); - check_bounds(data, index)?; + check_bounds(data, cpu_index)?; let fd = data.fd; let value = bpf_cpumap_val { - qsize: value, + qsize: queue_size, bpf_prog: bpf_cpumap_val__bindgen_ty_1 { fd: program.map(|prog| prog.as_raw_fd()).unwrap_or_default(), }, }; - bpf_map_update_elem(fd, Some(&index), &value, flags).map_err(|(_, io_error)| { + bpf_map_update_elem(fd, Some(&cpu_index), &value, flags).map_err(|(_, io_error)| { SyscallError { call: "bpf_map_update_elem", io_error, diff --git a/aya/src/maps/xdp/dev_map.rs b/aya/src/maps/xdp/dev_map.rs index eb8042a4..fe45176e 100644 --- a/aya/src/maps/xdp/dev_map.rs +++ b/aya/src/maps/xdp/dev_map.rs @@ -54,7 +54,7 @@ impl> DevMap { self.inner.borrow().obj.max_entries() } - /// Returns the value stored at the given index. + /// Returns the target ifindex and possible program at a given index. /// /// # Errors /// @@ -82,7 +82,17 @@ impl> DevMap { } impl> DevMap { - /// Sets the value of the element at the given index. + /// Sets the target ifindex at index, and optionally a chained program. + /// + /// When redirecting using `index`, packets will be transmitted by the interface with + /// `ifindex`. + /// + /// Another XDP program can be passed in that will be run before actual transmission. It can be + /// used to modify the packet before transmission with NIC specific data (MAC address update, + /// checksum computations, etc) or other purposes. + /// + /// Note that only XDP programs with the `map = "devmap"` argument can be passed. See the + /// kernel-space `aya_bpf::xdp` for more information. /// /// # Errors /// @@ -91,7 +101,7 @@ impl> DevMap { pub fn set( &mut self, index: u32, - value: u32, + ifindex: u32, program: Option, flags: u64, ) -> Result<(), MapError> { @@ -100,7 +110,7 @@ impl> DevMap { let fd = data.fd; let value = bpf_devmap_val { - ifindex: value, + ifindex, bpf_prog: bpf_devmap_val__bindgen_ty_1 { fd: program.map(|prog| prog.as_raw_fd()).unwrap_or_default(), }, diff --git a/aya/src/maps/xdp/dev_map_hash.rs b/aya/src/maps/xdp/dev_map_hash.rs index 8d65f14e..7907e949 100644 --- a/aya/src/maps/xdp/dev_map_hash.rs +++ b/aya/src/maps/xdp/dev_map_hash.rs @@ -48,25 +48,23 @@ impl> DevMapHash { Ok(Self { inner: map }) } - /// Returns the value stored at the given index. + /// Returns the target ifindex and possible program for a given key. /// /// # Errors /// - /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] - /// if `bpf_map_lookup_elem` fails. - pub fn get(&self, index: u32, flags: u64) -> Result { + /// Returns [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails. + pub fn get(&self, key: u32, flags: u64) -> Result { let fd = self.inner.borrow().fd; - let value = - bpf_map_lookup_elem(fd, &index, flags).map_err(|(_, io_error)| SyscallError { - call: "bpf_map_lookup_elem", - io_error, - })?; + let value = bpf_map_lookup_elem(fd, &key, flags).map_err(|(_, io_error)| SyscallError { + call: "bpf_map_lookup_elem", + io_error, + })?; let value: bpf_devmap_val = value.ok_or(MapError::KeyNotFound)?; Ok(value.into()) } /// An iterator over the elements of the devmap in arbitrary order. The iterator item type is - /// `Result<(u32, u32), MapError>`. + /// `Result<(u32, DevMapValue), MapError>`. pub fn iter(&self) -> MapIter<'_, u32, DevMapValue, Self> { MapIter::new(self) } @@ -79,28 +77,37 @@ impl> DevMapHash { } impl> DevMapHash { - /// Inserts a value in the map. + /// Inserts an ifindex and optionally a chained program in the map. + /// + /// When redirecting using `key`, packets will be transmitted by the interface with `ifindex`. + /// + /// Another XDP program can be passed in that will be run before actual transmission. It can be + /// used to modify the packet before transmission with NIC specific data (MAC address update, + /// checksum computations, etc) or other purposes. + /// + /// Note that only XDP programs with the `map = "devmap"` argument can be passed. See the + /// kernel-space `aya_bpf::xdp` for more information. /// /// # Errors /// /// Returns [`MapError::SyscallError`] if `bpf_map_update_elem` fails. pub fn insert( &mut self, - index: u32, - value: u32, + key: u32, + ifindex: u32, program: Option, flags: u64, ) -> Result<(), MapError> { let value = bpf_devmap_val { - ifindex: value, + ifindex, bpf_prog: bpf_devmap_val__bindgen_ty_1 { fd: program.map(|prog| prog.as_raw_fd()).unwrap_or_default(), }, }; - hash_map::insert(self.inner.borrow_mut(), &index, &value, flags) + hash_map::insert(self.inner.borrow_mut(), &key, &value, flags) } - /// Remove a value from the map. + /// Removes a value from the map. /// /// # Errors /// diff --git a/aya/src/maps/xdp/xsk_map.rs b/aya/src/maps/xdp/xsk_map.rs index bfd213a8..3e232a69 100644 --- a/aya/src/maps/xdp/xsk_map.rs +++ b/aya/src/maps/xdp/xsk_map.rs @@ -52,17 +52,21 @@ impl> XskMap { } impl> XskMap { - /// Sets the value of the element at the given index. + /// Sets the `AF_XDP` socket at a given index. + /// + /// When redirecting a packet, the `AF_XDP` socket at `index` will recieve the packet. Note + /// that it will do so only if the socket is bound to the same queue the packet was recieved + /// on. /// /// # Errors /// /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] /// if `bpf_map_update_elem` fails. - pub fn set(&mut self, index: u32, value: V, flags: u64) -> Result<(), MapError> { + pub fn set(&mut self, index: u32, socket_fd: impl AsRawFd, flags: u64) -> Result<(), MapError> { let data = self.inner.borrow_mut(); check_bounds(data, index)?; let fd = data.fd; - bpf_map_update_elem(fd, Some(&index), &value.as_raw_fd(), flags).map_err( + bpf_map_update_elem(fd, Some(&index), &socket_fd.as_raw_fd(), flags).map_err( |(_, io_error)| SyscallError { call: "bpf_map_update_elem", io_error, diff --git a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs index 0d8a1a4a..df1a32d2 100644 --- a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs @@ -41,8 +41,8 @@ impl CpuMap { /// Creates a [`CpuMap`] with a set maximum number of elements. /// /// In a CPU Map, an entry represents a CPU core. Thus there should be as many entries as there - /// are CPU cores on the system. To dynamically set the entry count at runtime, refer to the - /// userspace documentation. + /// are CPU cores on the system. It can be set to zero here, and updated by userspace at + /// runtime. Refer to the userspace documentation for more information. /// /// # Examples ///