From 9ed1d3d2811db89dc7314914d92922c54032382c Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:16:22 +0200 Subject: [PATCH] bpf: add documentation for XDP maps --- 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 | 68 ++++++++++++++++ bpf/aya-bpf/src/maps/xdp/dev_map.rs | 76 ++++++++++++++++++ bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs | 78 +++++++++++++++++++ bpf/aya-bpf/src/maps/xdp/xsk_map.rs | 99 ++++++++++++++++++++++++ 9 files changed, 416 insertions(+), 34 deletions(-) diff --git a/aya-bpf-macros/src/lib.rs b/aya-bpf-macros/src/lib.rs index 5696b82d..bdac2795 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` 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)] +/// 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 b01fc050..3fb4934f 100644 --- a/aya/src/maps/xdp/cpu_map.rs +++ b/aya/src/maps/xdp/cpu_map.rs @@ -61,19 +61,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().as_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, })?; @@ -94,7 +94,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 /// @@ -102,24 +113,24 @@ 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().as_fd(); let value = bpf_cpumap_val { - qsize: value, + qsize: queue_size, bpf_prog: bpf_cpumap_val__bindgen_ty_1 { // Default is valid as the kernel will only consider fd > 0: // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/cpumap.c#L466 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 44c1d27a..2a2797e8 100644 --- a/aya/src/maps/xdp/dev_map.rs +++ b/aya/src/maps/xdp/dev_map.rs @@ -55,7 +55,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 /// @@ -88,7 +88,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 /// @@ -97,7 +107,7 @@ impl> DevMap { pub fn set( &mut self, index: u32, - value: u32, + ifindex: u32, program: Option, flags: u64, ) -> Result<(), MapError> { @@ -106,7 +116,7 @@ impl> DevMap { let fd = data.fd().as_fd(); let value = bpf_devmap_val { - ifindex: value, + ifindex, bpf_prog: bpf_devmap_val__bindgen_ty_1 { // Default is valid as the kernel will only consider fd > 0: // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L866 diff --git a/aya/src/maps/xdp/dev_map_hash.rs b/aya/src/maps/xdp/dev_map_hash.rs index 06d53258..34ad39ac 100644 --- a/aya/src/maps/xdp/dev_map_hash.rs +++ b/aya/src/maps/xdp/dev_map_hash.rs @@ -49,19 +49,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().as_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)?; // SAFETY: map writes use fd, map reads use id. @@ -84,20 +82,29 @@ 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 { // Default is valid as the kernel will only consider fd > 0: // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L866 @@ -105,10 +112,10 @@ impl> DevMapHash { 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 133173c0..99477dbd 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().as_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 f1d224b3..df1a32d2 100644 --- a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs @@ -8,6 +8,28 @@ use crate::{ maps::PinningType, }; +/// An array of available CPUs. +/// +/// XDP programs can use this map to redirect packets to a target CPU for processing. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.15. +/// +/// # Examples +/// +/// ```rust,no_run +/// use aya_bpf::{bindings::xdp_action, macros::{map, xdp}, maps::CpuMap, programs::XdpContext}; +/// +/// #[map] +/// static MAP: CpuMap = CpuMap::with_max_entries(8, 0); +/// +/// #[xdp] +/// fn xdp(_ctx: XdpContext) -> i32 { +/// // Redirect to CPU 7 or drop packet if no entry found. +/// MAP.redirect(7, xdp_action::XDP_DROP as u64) +/// } +/// ``` #[repr(transparent)] pub struct CpuMap { def: UnsafeCell, @@ -16,6 +38,20 @@ pub struct CpuMap { unsafe impl Sync for CpuMap {} 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. It can be set to zero here, and updated by userspace at + /// runtime. Refer to the userspace documentation for more information. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{macros::map, maps::CpuMap}; + /// + /// #[map] + /// static MAP: CpuMap = CpuMap::with_max_entries(8, 0); + /// ``` pub const fn with_max_entries(max_entries: u32, flags: u32) -> CpuMap { CpuMap { def: UnsafeCell::new(bpf_map_def { @@ -30,6 +66,19 @@ impl CpuMap { } } + /// Creates a [`CpuMap`] with a set maximum number of elements that can be pinned to the BPF + /// File System (bpffs). + /// + /// See [`CpuMap::with_max_entries`] for more information. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{macros::map, maps::CpuMap}; + /// + /// #[map] + /// static MAP: CpuMap = CpuMap::pinned(8, 0); + /// ``` pub const fn pinned(max_entries: u32, flags: u32) -> CpuMap { CpuMap { def: UnsafeCell::new(bpf_map_def { @@ -44,6 +93,25 @@ impl CpuMap { } } + /// Redirects the current packet on the CPU at `index`. + /// + /// The lower two bits of `flags` are used for the return code if the map lookup fails, which + /// can be used as the XDP program's return code if a CPU cannot be found. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{bindings::xdp_action, macros::{map, xdp}, maps::CpuMap, programs::XdpContext}; + /// + /// #[map] + /// static MAP: CpuMap = CpuMap::with_max_entries(8, 0); + /// + /// #[xdp] + /// fn xdp(_ctx: XdpContext) -> u32 { + /// // Redirect to CPU 7 or drop packet if no entry found. + /// MAP.redirect(7, xdp_action::XDP_DROP as u64) + /// } + /// ``` #[inline(always)] pub fn redirect(&self, index: u32, flags: u64) -> u32 { unsafe { diff --git a/bpf/aya-bpf/src/maps/xdp/dev_map.rs b/bpf/aya-bpf/src/maps/xdp/dev_map.rs index ca54da42..66103758 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map.rs @@ -9,6 +9,27 @@ use crate::{ maps::PinningType, }; +/// An array of network devices. +/// +/// XDP programs can use this map to redirect packets to other network deviecs. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.14. +/// +/// # Examples +/// +/// ```rust,no_run +/// use aya_bpf::{bindings::xdp_action, macros::{map, xdp}, maps::DevMap, programs::XdpContext}; +/// +/// #[map] +/// static MAP: DevMap = DevMap::with_max_entries(1, 0); +/// +/// #[xdp] +/// fn xdp(_ctx: XdpContext) -> i32 { +/// MAP.redirect(0, xdp_action::XDP_PASS as u64) +/// } +/// ``` #[repr(transparent)] pub struct DevMap { def: UnsafeCell, @@ -17,6 +38,16 @@ pub struct DevMap { unsafe impl Sync for DevMap {} impl DevMap { + /// Creates a [`DevMap`] with a set maximum number of elements. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{macros::map, maps::DevMap}; + /// + /// #[map] + /// static MAP: DevMap = DevMap::with_max_entries(8, 0); + /// ``` pub const fn with_max_entries(max_entries: u32, flags: u32) -> DevMap { DevMap { def: UnsafeCell::new(bpf_map_def { @@ -31,6 +62,17 @@ impl DevMap { } } + /// Creates a [`DevMap`] with a set maximum number of elements that can be pinned to the BPF + /// File System (bpffs). + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{macros::map, maps::DevMap}; + /// + /// #[map] + /// static MAP: DevMap = DevMap::pinned(8, 0); + /// ``` pub const fn pinned(max_entries: u32, flags: u32) -> DevMap { DevMap { def: UnsafeCell::new(bpf_map_def { @@ -45,6 +87,22 @@ impl DevMap { } } + /// Retrieves the interface index at `index` in the array. + /// + /// To actually redirect a packet, see [`DevMap::redirect`]. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{macros::map, maps::DevMap}; + /// + /// #[map] + /// static MAP: DevMap = DevMap::with_max_entries(1, 0); + /// + /// let ifindex = MAP.get(0); + /// + /// // redirect to ifindex + /// ``` #[inline(always)] pub fn get(&self, index: u32) -> Option { unsafe { @@ -56,6 +114,24 @@ impl DevMap { } } + /// Redirects the current packet on the interface at `index`. + /// + /// The lower two bits of `flags` are used for the return code if the map lookup fails, which + /// can be used as the XDP program's return code if a CPU cannot be found. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{bindings::xdp_action, macros::{map, xdp}, maps::DevMap, programs::XdpContext}; + /// + /// #[map] + /// static MAP: DevMap = DevMap::with_max_entries(8, 0); + /// + /// #[xdp] + /// fn xdp(_ctx: XdpContext) -> i32 { + /// MAP.redirect(7, xdp_action::XDP_PASS as u64) + /// } + /// ``` #[inline(always)] pub fn redirect(&self, index: u32, flags: u64) -> u32 { // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags diff --git a/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs b/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs index 87a61299..600aa9df 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs @@ -9,6 +9,29 @@ use crate::{ maps::PinningType, }; +/// A map of network devices. +/// +/// XDP programs can use this map to redirect packets to other network devices. It is similar to +/// [`DevMap`](super::DevMap), but is an hash map rather than an array. Keys do not need to be +/// contiguous nor start at zero, but there is a hashing cost to every lookup. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.4. +/// +/// # Examples +/// +/// ```rust,no_run +/// use aya_bpf::{bindings::xdp_action, macros::{map, xdp}, maps::DevMapHash, programs::XdpContext}; +/// +/// #[map] +/// static MAP: DevMapHash = DevMapHash::with_max_entries(1, 0); +/// +/// #[xdp] +/// fn xdp(_ctx: XdpContext) -> i32 { +/// MAP.redirect(42, xdp_action::XDP_PASS as u64) +/// } +/// ``` #[repr(transparent)] pub struct DevMapHash { def: UnsafeCell, @@ -17,6 +40,16 @@ pub struct DevMapHash { unsafe impl Sync for DevMapHash {} impl DevMapHash { + /// Creates a [`DevMapHash`] with a set maximum number of elements. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{macros::map, maps::DevMapHash}; + /// + /// #[map] + /// static MAP: DevMapHash = DevMapHash::with_max_entries(8, 0); + /// ``` pub const fn with_max_entries(max_entries: u32, flags: u32) -> DevMapHash { DevMapHash { def: UnsafeCell::new(bpf_map_def { @@ -31,6 +64,17 @@ impl DevMapHash { } } + /// Creates a [`DevMapHash`] with a set maximum number of elements that can be pinned to the BPF + /// File System (bpffs). + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{macros::map, maps::DevMapHash}; + /// + /// #[map] + /// static MAP: DevMapHash = DevMapHash::pinned(8, 0); + /// ``` pub const fn pinned(max_entries: u32, flags: u32) -> DevMapHash { DevMapHash { def: UnsafeCell::new(bpf_map_def { @@ -45,6 +89,22 @@ impl DevMapHash { } } + /// Retrieves the interface index with `key` in the map. + /// + /// To actually redirect a packet, see [`DevMapHash::redirect`]. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{macros::map, maps::DevMapHash}; + /// + /// #[map] + /// static MAP: DevMapHash = DevMapHash::with_max_entries(1, 0); + /// + /// let ifindex = MAP.get(42); + /// + /// // redirect to ifindex + /// ``` #[inline(always)] pub fn get(&self, key: u32) -> Option { unsafe { @@ -54,6 +114,24 @@ impl DevMapHash { } } + /// Redirects the current packet on the interface at `key`. + /// + /// The lower two bits of `flags` are used for the return code if the map lookup fails, which + /// can be used as the XDP program's return code if a CPU cannot be found. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{bindings::xdp_action, macros::{map, xdp}, maps::DevMapHash, programs::XdpContext}; + /// + /// #[map] + /// static MAP: DevMapHash = DevMapHash::with_max_entries(8, 0); + /// + /// #[xdp] + /// fn xdp(_ctx: XdpContext) -> i32 { + /// MAP.redirect(7, xdp_action::XDP_PASS as u64) + /// } + /// ``` #[inline(always)] pub fn redirect(&self, key: u32, flags: u64) -> u32 { unsafe { diff --git a/bpf/aya-bpf/src/maps/xdp/xsk_map.rs b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs index a5e12767..ee766fa5 100644 --- a/bpf/aya-bpf/src/maps/xdp/xsk_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs @@ -9,6 +9,48 @@ use crate::{ maps::PinningType, }; +/// An array of AF_XDP sockets. +/// +/// XDP programs can use this map to redirect packets to a target AF_XDP socket using the +/// `XDP_REDIRECT` action. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.18. +/// +/// # Examples +/// +/// ```rust,no_run +/// use aya_bpf::{bindings::xdp_action, macros::{map, xdp}, maps::XskMap, programs::XdpContext}; +/// +/// #[map] +/// static SOCKS: XskMap = XskMap::with_max_entries(8, 0); +/// +/// #[xdp] +/// fn xdp(ctx, XdpContext) -> i32 { +/// let queue_id = unsafe { (*ctx.ctx).rx_queue_index }; +/// MAP.redirect(queue_id, xdp_action::XDP_DROP as u64) +/// } +/// ``` +/// +/// # Queue management +/// +/// Packets received on a RX queue can only be redirected to sockets bound on the same queue. Most +/// hardware NICs have multiple RX queue to spread the load across multiple CPU cores using RSS. +/// +/// Three strategies are possible: +/// +/// - Reduce the RX queue count to a single one. This option is great for development, but is +/// detrimental for performance as the single CPU core recieving packets will get overwhelmed. +/// Setting the queue count for a NIC can be achieved using `ethtool -L combined 1`. +/// - Create a socket for every RX queue. Most modern NICs will have an RX queue per CPU thread, so +/// a socket per CPU thread is best for performance. To dynamically size the map depending on the +/// recieve queue count, see the userspace documentation of `CpuMap`. +/// - Create a single socket and use a [`CpuMap`](super::CpuMap) to redirect the packet to the +/// correct CPU core. This way, the packet is sent to another CPU, and a chained XDP program can +/// the redirect to the AF_XDP socket. Using a single socket simplifies the userspace code but +/// will not perform great unless not a lot of traffic is redirected to the socket. Regular +/// traffic however will not be impacted, contrary to reducing the queue count. #[repr(transparent)] pub struct XskMap { def: UnsafeCell, @@ -17,6 +59,16 @@ pub struct XskMap { unsafe impl Sync for XskMap {} impl XskMap { + /// Creates a [`XskMap`] with a set maximum number of elements. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{macros::map, maps::XskMap}; + /// + /// #[map] + /// static SOCKS: XskMap::with_max_entries(8, 0); + /// ``` pub const fn with_max_entries(max_entries: u32, flags: u32) -> XskMap { XskMap { def: UnsafeCell::new(bpf_map_def { @@ -31,6 +83,17 @@ impl XskMap { } } + /// Creates a [`XskMap`] with a set maximum number of elements that can be pinned to the BPF + /// filesystem (bpffs). + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{macros::map, maps::XskMap}; + /// + /// #[map] + /// static SOCKS: XskMap::pinned(8, 0); + /// ``` pub const fn pinned(max_entries: u32, flags: u32) -> XskMap { XskMap { def: UnsafeCell::new(bpf_map_def { @@ -45,6 +108,20 @@ impl XskMap { } } + /// Retrieves the queue to which the socket is bound at `index` in the array. + /// + /// To actually redirect a packet, see [`XskMap::redirect`]. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{macros::map, maps::XskMap}; + /// + /// #[map] + /// static SOCKS: XskMap = XskMap::with_max_entries(8, 0); + /// + /// let queue_id = SOCKS.get(0); + /// ``` #[inline(always)] pub fn get(&self, index: u32) -> Option { unsafe { @@ -56,6 +133,28 @@ impl XskMap { } } + /// Redirects the current packet to the AF_XDP socket at `index`. + /// + /// The lower two bits of `flags` are used for the return code if the map lookup fails, which + /// can be used as the XDP program's return code if a matching socket cannot be found. + /// + /// However, if the socket at `index` is bound to a RX queue which is not the current RX queue, + /// the packet will be dropped. + /// + /// # Examples + /// + /// ```rust,no_run + /// use aya_bpf::{bindings::xdp_action, macros::{map, xdp}, maps::XskMap, programs::XdpContext}; + /// + /// #[map] + /// static SOCKS: XskMap = XskMap::with_max_entries(8, 0); + /// + /// #[xdp] + /// fn xdp(ctx, XdpContext) -> i32 { + /// let queue_id = unsafe { (*ctx.ctx).rx_queue_index }; + /// MAP.redirect(queue_id, xdp_action::XDP_DROP as u64) + /// } + /// ``` #[inline(always)] pub fn redirect(&self, index: u32, flags: u64) -> u32 { unsafe {