bpf: add documentation for XDP maps

reviewable/pr527/r18
Tuetuopay 1 year ago
parent db49633073
commit 9ed1d3d281

@ -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_error]
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn xdp(attrs: TokenStream, item: TokenStream) -> TokenStream { pub fn xdp(attrs: TokenStream, item: TokenStream) -> TokenStream {

@ -61,19 +61,19 @@ impl<T: Borrow<MapData>> CpuMap<T> {
self.inner.borrow().obj.max_entries() 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 /// # Errors
/// ///
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] /// Returns [`MapError::OutOfBounds`] if `cpu_index` is out of bounds,
/// if `bpf_map_lookup_elem` fails. /// [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails.
pub fn get(&self, index: u32, flags: u64) -> Result<CpuMapValue, MapError> { pub fn get(&self, cpu_index: u32, flags: u64) -> Result<CpuMapValue, MapError> {
let data = self.inner.borrow(); let data = self.inner.borrow();
check_bounds(data, index)?; check_bounds(data, cpu_index)?;
let fd = data.fd().as_fd(); let fd = data.fd().as_fd();
let value = 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", call: "bpf_map_lookup_elem",
io_error, io_error,
})?; })?;
@ -94,7 +94,18 @@ impl<T: Borrow<MapData>> CpuMap<T> {
} }
impl<T: BorrowMut<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 /// # Errors
/// ///
@ -102,24 +113,24 @@ impl<T: BorrowMut<MapData>> CpuMap<T> {
/// if `bpf_map_update_elem` fails. /// if `bpf_map_update_elem` fails.
pub fn set( pub fn set(
&mut self, &mut self,
index: u32, cpu_index: u32,
value: u32, queue_size: u32,
program: Option<impl AsRawFd>, program: Option<impl AsRawFd>,
flags: u64, flags: u64,
) -> Result<(), MapError> { ) -> Result<(), MapError> {
let data = self.inner.borrow_mut(); let data = self.inner.borrow_mut();
check_bounds(data, index)?; check_bounds(data, cpu_index)?;
let fd = data.fd().as_fd(); let fd = data.fd().as_fd();
let value = bpf_cpumap_val { let value = bpf_cpumap_val {
qsize: value, qsize: queue_size,
bpf_prog: bpf_cpumap_val__bindgen_ty_1 { bpf_prog: bpf_cpumap_val__bindgen_ty_1 {
// Default is valid as the kernel will only consider fd > 0: // Default is valid as the kernel will only consider fd > 0:
// https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/cpumap.c#L466 // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/cpumap.c#L466
fd: program.map(|prog| prog.as_raw_fd()).unwrap_or_default(), 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 { SyscallError {
call: "bpf_map_update_elem", call: "bpf_map_update_elem",
io_error, io_error,

@ -55,7 +55,7 @@ impl<T: Borrow<MapData>> DevMap<T> {
self.inner.borrow().obj.max_entries() 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 /// # Errors
/// ///
@ -88,7 +88,17 @@ impl<T: Borrow<MapData>> DevMap<T> {
} }
impl<T: BorrowMut<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 /// # Errors
/// ///
@ -97,7 +107,7 @@ impl<T: BorrowMut<MapData>> DevMap<T> {
pub fn set( pub fn set(
&mut self, &mut self,
index: u32, index: u32,
value: u32, ifindex: u32,
program: Option<impl AsRawFd>, program: Option<impl AsRawFd>,
flags: u64, flags: u64,
) -> Result<(), MapError> { ) -> Result<(), MapError> {
@ -106,7 +116,7 @@ impl<T: BorrowMut<MapData>> DevMap<T> {
let fd = data.fd().as_fd(); let fd = data.fd().as_fd();
let value = bpf_devmap_val { let value = bpf_devmap_val {
ifindex: value, ifindex,
bpf_prog: bpf_devmap_val__bindgen_ty_1 { bpf_prog: bpf_devmap_val__bindgen_ty_1 {
// Default is valid as the kernel will only consider fd > 0: // Default is valid as the kernel will only consider fd > 0:
// https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L866 // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L866

@ -49,19 +49,17 @@ impl<T: Borrow<MapData>> DevMapHash<T> {
Ok(Self { inner: map }) Ok(Self { inner: map })
} }
/// Returns the value stored at the given index. /// Returns the target ifindex and possible program for a given key.
/// ///
/// # Errors /// # Errors
/// ///
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] /// Returns [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails.
/// if `bpf_map_lookup_elem` fails. pub fn get(&self, key: u32, flags: u64) -> Result<DevMapValue, MapError> {
pub fn get(&self, index: u32, flags: u64) -> Result<DevMapValue, MapError> {
let fd = self.inner.borrow().fd().as_fd(); let fd = self.inner.borrow().fd().as_fd();
let value = let value = bpf_map_lookup_elem(fd, &key, flags).map_err(|(_, io_error)| SyscallError {
bpf_map_lookup_elem(fd, &index, flags).map_err(|(_, io_error)| SyscallError { call: "bpf_map_lookup_elem",
call: "bpf_map_lookup_elem", io_error,
io_error, })?;
})?;
let value: bpf_devmap_val = value.ok_or(MapError::KeyNotFound)?; let value: bpf_devmap_val = value.ok_or(MapError::KeyNotFound)?;
// SAFETY: map writes use fd, map reads use id. // 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> { 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 /// # Errors
/// ///
/// Returns [`MapError::SyscallError`] if `bpf_map_update_elem` fails. /// Returns [`MapError::SyscallError`] if `bpf_map_update_elem` fails.
pub fn insert( pub fn insert(
&mut self, &mut self,
index: u32, key: u32,
value: u32, ifindex: u32,
program: Option<impl AsRawFd>, program: Option<impl AsRawFd>,
flags: u64, flags: u64,
) -> Result<(), MapError> { ) -> Result<(), MapError> {
let value = bpf_devmap_val { let value = bpf_devmap_val {
ifindex: value, ifindex,
bpf_prog: bpf_devmap_val__bindgen_ty_1 { bpf_prog: bpf_devmap_val__bindgen_ty_1 {
// Default is valid as the kernel will only consider fd > 0: // Default is valid as the kernel will only consider fd > 0:
// https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L866 // 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(), 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 /// # Errors
/// ///

@ -52,17 +52,21 @@ impl<T: Borrow<MapData>> XskMap<T> {
} }
impl<T: BorrowMut<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 /// # Errors
/// ///
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
/// if `bpf_map_update_elem` fails. /// 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(); let data = self.inner.borrow_mut();
check_bounds(data, index)?; check_bounds(data, index)?;
let fd = data.fd().as_fd(); 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 { |(_, io_error)| SyscallError {
call: "bpf_map_update_elem", call: "bpf_map_update_elem",
io_error, io_error,

@ -8,6 +8,28 @@ use crate::{
maps::PinningType, 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)] #[repr(transparent)]
pub struct CpuMap { pub struct CpuMap {
def: UnsafeCell<bpf_map_def>, def: UnsafeCell<bpf_map_def>,
@ -16,6 +38,20 @@ pub struct CpuMap {
unsafe impl Sync for CpuMap {} unsafe impl Sync for CpuMap {}
impl 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 { pub const fn with_max_entries(max_entries: u32, flags: u32) -> CpuMap {
CpuMap { CpuMap {
def: UnsafeCell::new(bpf_map_def { 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 { pub const fn pinned(max_entries: u32, flags: u32) -> CpuMap {
CpuMap { CpuMap {
def: UnsafeCell::new(bpf_map_def { 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)] #[inline(always)]
pub fn redirect(&self, index: u32, flags: u64) -> u32 { pub fn redirect(&self, index: u32, flags: u64) -> u32 {
unsafe { unsafe {

@ -9,6 +9,27 @@ use crate::{
maps::PinningType, 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)] #[repr(transparent)]
pub struct DevMap { pub struct DevMap {
def: UnsafeCell<bpf_map_def>, def: UnsafeCell<bpf_map_def>,
@ -17,6 +38,16 @@ pub struct DevMap {
unsafe impl Sync for DevMap {} unsafe impl Sync for DevMap {}
impl 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 { pub const fn with_max_entries(max_entries: u32, flags: u32) -> DevMap {
DevMap { DevMap {
def: UnsafeCell::new(bpf_map_def { 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 { pub const fn pinned(max_entries: u32, flags: u32) -> DevMap {
DevMap { DevMap {
def: UnsafeCell::new(bpf_map_def { 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)] #[inline(always)]
pub fn get(&self, index: u32) -> Option<bpf_devmap_val> { pub fn get(&self, index: u32) -> Option<bpf_devmap_val> {
unsafe { 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)] #[inline(always)]
pub fn redirect(&self, index: u32, flags: u64) -> u32 { 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 // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags

@ -9,6 +9,29 @@ use crate::{
maps::PinningType, 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)] #[repr(transparent)]
pub struct DevMapHash { pub struct DevMapHash {
def: UnsafeCell<bpf_map_def>, def: UnsafeCell<bpf_map_def>,
@ -17,6 +40,16 @@ pub struct DevMapHash {
unsafe impl Sync for DevMapHash {} unsafe impl Sync for DevMapHash {}
impl 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 { pub const fn with_max_entries(max_entries: u32, flags: u32) -> DevMapHash {
DevMapHash { DevMapHash {
def: UnsafeCell::new(bpf_map_def { 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 { pub const fn pinned(max_entries: u32, flags: u32) -> DevMapHash {
DevMapHash { DevMapHash {
def: UnsafeCell::new(bpf_map_def { 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)] #[inline(always)]
pub fn get(&self, key: u32) -> Option<bpf_devmap_val> { pub fn get(&self, key: u32) -> Option<bpf_devmap_val> {
unsafe { 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)] #[inline(always)]
pub fn redirect(&self, key: u32, flags: u64) -> u32 { pub fn redirect(&self, key: u32, flags: u64) -> u32 {
unsafe { unsafe {

@ -9,6 +9,48 @@ use crate::{
maps::PinningType, 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)] #[repr(transparent)]
pub struct XskMap { pub struct XskMap {
def: UnsafeCell<bpf_map_def>, def: UnsafeCell<bpf_map_def>,
@ -17,6 +59,16 @@ pub struct XskMap {
unsafe impl Sync for XskMap {} unsafe impl Sync for XskMap {}
impl 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 { pub const fn with_max_entries(max_entries: u32, flags: u32) -> XskMap {
XskMap { XskMap {
def: UnsafeCell::new(bpf_map_def { 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 { pub const fn pinned(max_entries: u32, flags: u32) -> XskMap {
XskMap { XskMap {
def: UnsafeCell::new(bpf_map_def { 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)] #[inline(always)]
pub fn get(&self, index: u32) -> Option<u32> { pub fn get(&self, index: u32) -> Option<u32> {
unsafe { 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)] #[inline(always)]
pub fn redirect(&self, index: u32, flags: u64) -> u32 { pub fn redirect(&self, index: u32, flags: u64) -> u32 {
unsafe { unsafe {

Loading…
Cancel
Save