Browse Source

bpf: add documentation for XDP maps

Tuetuopay 1 year ago
parent
commit
9ed1d3d281

+ 29 - 0
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<u32, u32> {
+///     Ok(XDP_PASS)
+/// }
+/// ```
 #[proc_macro_error]
 #[proc_macro_attribute]
 pub fn xdp(attrs: TokenStream, item: TokenStream) -> TokenStream {

+ 23 - 12
aya/src/maps/xdp/cpu_map.rs

@@ -61,19 +61,19 @@ impl<T: Borrow<MapData>> CpuMap<T> {
         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<CpuMapValue, MapError> {
+    /// 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<CpuMapValue, MapError> {
         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<T: Borrow<MapData>> CpuMap<T> {
 }
 
 impl<T: BorrowMut<MapData>> CpuMap<T> {
-    /// 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<T: BorrowMut<MapData>> CpuMap<T> {
     /// if `bpf_map_update_elem` fails.
     pub fn set(
         &mut self,
-        index: u32,
-        value: u32,
+        cpu_index: u32,
+        queue_size: u32,
         program: Option<impl AsRawFd>,
         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,

+ 14 - 4
aya/src/maps/xdp/dev_map.rs

@@ -55,7 +55,7 @@ impl<T: Borrow<MapData>> DevMap<T> {
         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<T: Borrow<MapData>> DevMap<T> {
 }
 
 impl<T: BorrowMut<MapData>> DevMap<T> {
-    /// 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<T: BorrowMut<MapData>> DevMap<T> {
     pub fn set(
         &mut self,
         index: u32,
-        value: u32,
+        ifindex: u32,
         program: Option<impl AsRawFd>,
         flags: u64,
     ) -> Result<(), MapError> {
@@ -106,7 +116,7 @@ impl<T: BorrowMut<MapData>> DevMap<T> {
         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

+ 22 - 15
aya/src/maps/xdp/dev_map_hash.rs

@@ -49,19 +49,17 @@ impl<T: Borrow<MapData>> DevMapHash<T> {
         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<DevMapValue, MapError> {
+    /// Returns [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails.
+    pub fn get(&self, key: u32, flags: u64) -> Result<DevMapValue, MapError> {
         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<T: Borrow<MapData>> DevMapHash<T> {
 }
 
 impl<T: BorrowMut<MapData>> DevMapHash<T> {
-    /// 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<impl AsRawFd>,
         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<T: BorrowMut<MapData>> DevMapHash<T> {
                 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
     ///

+ 7 - 3
aya/src/maps/xdp/xsk_map.rs

@@ -52,17 +52,21 @@ impl<T: Borrow<MapData>> XskMap<T> {
 }
 
 impl<T: BorrowMut<MapData>> XskMap<T> {
-    /// 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<V: AsRawFd>(&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,

+ 68 - 0
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<bpf_map_def>,
@@ -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 {

+ 76 - 0
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<bpf_map_def>,
@@ -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<bpf_devmap_val> {
         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

+ 78 - 0
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<bpf_map_def>,
@@ -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<bpf_devmap_val> {
         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 {

+ 99 - 0
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 <ifname> 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<bpf_map_def>,
@@ -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<u32> {
         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 {