From 1450b9be63a6f289e38b7a6793b435f72117cd37 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 | 37 +++++++++++++++++------------ aya/src/maps/xdp/xsk_map.rs | 10 +++++--- bpf/aya-bpf/src/maps/xdp/cpu_map.rs | 4 ++-- 6 files changed, 97 insertions(+), 36 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 10e82315..67507869 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, })?; @@ -87,7 +87,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 /// @@ -95,22 +106,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 6dc033d1..6e8fd441 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 /// @@ -81,7 +81,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 /// @@ -90,7 +100,7 @@ impl> DevMap { pub fn set( &mut self, index: u32, - value: u32, + ifindex: u32, program: Option, flags: u64, ) -> Result<(), MapError> { @@ -99,7 +109,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 1d45c450..6b7aff80 100644 --- a/aya/src/maps/xdp/dev_map_hash.rs +++ b/aya/src/maps/xdp/dev_map_hash.rs @@ -48,19 +48,17 @@ 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()) } @@ -77,28 +75,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 ///