From ec8293ab8644cbf8f1c4e7b1c44b286bc0ae969a Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Mon, 30 May 2022 11:43:38 +0100 Subject: [PATCH 01/16] aya: Implement XDP Map Types This commit adds implementations for: - xskmap - devmap - devmap_hash - cpumap Which can all be used to redirect XDP packets to various different locations Signed-off-by: Dave Tucker --- aya/src/maps/mod.rs | 2 + aya/src/maps/xdp/cpu_map.rs | 120 +++++++++++++++++++++++++++++++ aya/src/maps/xdp/dev_map.rs | 117 ++++++++++++++++++++++++++++++ aya/src/maps/xdp/dev_map_hash.rs | 118 ++++++++++++++++++++++++++++++ aya/src/maps/xdp/mod.rs | 10 +++ aya/src/maps/xdp/xsk_map.rs | 118 ++++++++++++++++++++++++++++++ 6 files changed, 485 insertions(+) create mode 100644 aya/src/maps/xdp/cpu_map.rs create mode 100644 aya/src/maps/xdp/dev_map.rs create mode 100644 aya/src/maps/xdp/dev_map_hash.rs create mode 100644 aya/src/maps/xdp/mod.rs create mode 100644 aya/src/maps/xdp/xsk_map.rs diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index 191c5883..190d8c67 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -83,6 +83,7 @@ pub mod queue; pub mod sock; pub mod stack; pub mod stack_trace; +pub mod xdp; pub use array::{Array, PerCpuArray, ProgramArray}; pub use bloom_filter::BloomFilter; @@ -96,6 +97,7 @@ pub use queue::Queue; pub use sock::{SockHash, SockMap}; pub use stack::Stack; pub use stack_trace::StackTraceMap; +pub use xdp::XskMap; #[derive(Error, Debug)] /// Errors occuring from working with Maps diff --git a/aya/src/maps/xdp/cpu_map.rs b/aya/src/maps/xdp/cpu_map.rs new file mode 100644 index 00000000..2b6ba3cb --- /dev/null +++ b/aya/src/maps/xdp/cpu_map.rs @@ -0,0 +1,120 @@ +//! An array of available CPUs. + +use std::{ + convert::TryFrom, + mem, + ops::{Deref, DerefMut}, +}; + +use crate::{ + generated::bpf_map_type::BPF_MAP_TYPE_CPUMAP, + maps::{Map, MapError, MapRef, MapRefMut}, + sys::bpf_map_update_elem, +}; + +/// 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.2. +/// +/// # Examples +/// ```no_run +/// # let bpf = aya::Bpf::load(&[])?; +/// use aya::maps::xdp::CpuMap; +/// use std::convert::{TryFrom, TryInto}; +/// +/// let mut cpumap = CpuMap::try_from(bpf.map_mut("CPUS")?)?; +/// let flags = 0; +/// let queue_size = 2048; +/// for i in 0u32..8u32 { +/// cpumap.set(i, queue_size, flags); +/// } +/// +/// # Ok::<(), aya::BpfError>(()) +/// ``` +#[doc(alias = "BPF_MAP_TYPE_CPUMAP")] +pub struct CpuMap> { + inner: T, +} + +impl> CpuMap { + fn new(map: T) -> Result, MapError> { + let map_type = map.obj.def.map_type; + if map_type != BPF_MAP_TYPE_CPUMAP as u32 { + return Err(MapError::InvalidMapType { + map_type: map_type as u32, + }); + } + let expected = mem::size_of::(); + let size = map.obj.def.key_size as usize; + if size != expected { + return Err(MapError::InvalidKeySize { size, expected }); + } + + let expected = mem::size_of::(); + let size = map.obj.def.value_size as usize; + if size != expected { + return Err(MapError::InvalidValueSize { size, expected }); + } + let _fd = map.fd_or_err()?; + + Ok(CpuMap { inner: map }) + } + + /// Returns the number of elements in the array. + /// + /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. + pub fn len(&self) -> u32 { + self.inner.obj.def.max_entries + } + + fn check_bounds(&self, index: u32) -> Result<(), MapError> { + let max_entries = self.inner.obj.def.max_entries; + if index >= self.inner.obj.def.max_entries { + Err(MapError::OutOfBounds { index, max_entries }) + } else { + Ok(()) + } + } +} + +impl + DerefMut> CpuMap { + /// Sets the value of the element at the given index. + /// + /// # 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: u32, flags: u64) -> Result<(), MapError> { + let fd = self.inner.fd_or_err()?; + self.check_bounds(index)?; + bpf_map_update_elem(fd, &index, &value, flags).map_err(|(code, io_error)| { + MapError::SyscallError { + call: "bpf_map_update_elem".to_owned(), + code, + io_error, + } + })?; + Ok(()) + } +} + +impl TryFrom for CpuMap { + type Error = MapError; + + fn try_from(a: MapRef) -> Result, MapError> { + CpuMap::new(a) + } +} + +impl TryFrom for CpuMap { + type Error = MapError; + + fn try_from(a: MapRefMut) -> Result, MapError> { + CpuMap::new(a) + } +} diff --git a/aya/src/maps/xdp/dev_map.rs b/aya/src/maps/xdp/dev_map.rs new file mode 100644 index 00000000..365a5b1e --- /dev/null +++ b/aya/src/maps/xdp/dev_map.rs @@ -0,0 +1,117 @@ +//! An array of network devices. + +use std::{ + convert::TryFrom, + mem, + ops::{Deref, DerefMut}, +}; + +use crate::{ + generated::bpf_map_type::BPF_MAP_TYPE_DEVMAP, + maps::{Map, MapError, MapRef, MapRefMut}, + sys::bpf_map_update_elem, +}; + +/// An array of network devices. +/// +/// XDP programs can use this map to redirect to other network +/// devices. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.2. +/// +/// # Examples +/// ```no_run +/// # let bpf = aya::Bpf::load(&[])?; +/// use aya::maps::xdp::DevMap; +/// use std::convert::{TryFrom, TryInto}; +/// +/// let mut devmap = DevMap::try_from(bpf.map_mut("IFACES")?)?; +/// let ifindex = 32u32; +/// devmap.set(ifindex, ifindex, 0); +/// +/// # Ok::<(), aya::BpfError>(()) +/// ``` +#[doc(alias = "BPF_MAP_TYPE_DEVMAP")] +pub struct DevMap> { + inner: T, +} + +impl> DevMap { + fn new(map: T) -> Result, MapError> { + let map_type = map.obj.def.map_type; + if map_type != BPF_MAP_TYPE_DEVMAP as u32 { + return Err(MapError::InvalidMapType { + map_type: map_type as u32, + }); + } + let expected = mem::size_of::(); + let size = map.obj.def.key_size as usize; + if size != expected { + return Err(MapError::InvalidKeySize { size, expected }); + } + + let expected = mem::size_of::(); + let size = map.obj.def.value_size as usize; + if size != expected { + return Err(MapError::InvalidValueSize { size, expected }); + } + let _fd = map.fd_or_err()?; + + Ok(DevMap { inner: map }) + } + + /// Returns the number of elements in the array. + /// + /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. + pub fn len(&self) -> u32 { + self.inner.obj.def.max_entries + } + + fn check_bounds(&self, index: u32) -> Result<(), MapError> { + let max_entries = self.inner.obj.def.max_entries; + if index >= self.inner.obj.def.max_entries { + Err(MapError::OutOfBounds { index, max_entries }) + } else { + Ok(()) + } + } +} + +impl + DerefMut> DevMap { + /// Sets the value of the element at the given index. + /// + /// # 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: u32, flags: u64) -> Result<(), MapError> { + let fd = self.inner.fd_or_err()?; + self.check_bounds(index)?; + bpf_map_update_elem(fd, &index, &value, flags).map_err(|(code, io_error)| { + MapError::SyscallError { + call: "bpf_map_update_elem".to_owned(), + code, + io_error, + } + })?; + Ok(()) + } +} + +impl TryFrom for DevMap { + type Error = MapError; + + fn try_from(a: MapRef) -> Result, MapError> { + DevMap::new(a) + } +} + +impl TryFrom for DevMap { + type Error = MapError; + + fn try_from(a: MapRefMut) -> Result, MapError> { + DevMap::new(a) + } +} diff --git a/aya/src/maps/xdp/dev_map_hash.rs b/aya/src/maps/xdp/dev_map_hash.rs new file mode 100644 index 00000000..4cf16762 --- /dev/null +++ b/aya/src/maps/xdp/dev_map_hash.rs @@ -0,0 +1,118 @@ +//! An array of network devices. + +use std::{ + convert::TryFrom, + mem, + ops::{Deref, DerefMut}, +}; + +use crate::{ + generated::bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH, + maps::{Map, MapError, MapRef, MapRefMut}, + sys::bpf_map_update_elem, +}; + +/// An array of network devices. +/// +/// XDP programs can use this map to redirect to other network +/// devices. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.2. +/// +/// # Examples +/// ```no_run +/// # let bpf = aya::Bpf::load(&[])?; +/// use aya::maps::xdp::DevMapHash; +/// use std::convert::{TryFrom, TryInto}; +/// +/// let mut devmap = DevMapHash::try_from(bpf.map_mut("IFACES")?)?; +/// let flags = 0; +/// let ifindex = 32u32; +/// devmap.set(ifindex, ifindex, flags); +/// +/// # Ok::<(), aya::BpfError>(()) +/// ``` +#[doc(alias = "BPF_MAP_TYPE_DEVMAP_HASH")] +pub struct DevMapHash> { + inner: T, +} + +impl> DevMapHash { + fn new(map: T) -> Result, MapError> { + let map_type = map.obj.def.map_type; + if map_type != BPF_MAP_TYPE_DEVMAP_HASH as u32 { + return Err(MapError::InvalidMapType { + map_type: map_type as u32, + }); + } + let expected = mem::size_of::(); + let size = map.obj.def.key_size as usize; + if size != expected { + return Err(MapError::InvalidKeySize { size, expected }); + } + + let expected = mem::size_of::(); + let size = map.obj.def.value_size as usize; + if size != expected { + return Err(MapError::InvalidValueSize { size, expected }); + } + let _fd = map.fd_or_err()?; + + Ok(DevMapHash { inner: map }) + } + + /// Returns the number of elements in the array. + /// + /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. + pub fn len(&self) -> u32 { + self.inner.obj.def.max_entries + } + + fn check_bounds(&self, index: u32) -> Result<(), MapError> { + let max_entries = self.inner.obj.def.max_entries; + if index >= self.inner.obj.def.max_entries { + Err(MapError::OutOfBounds { index, max_entries }) + } else { + Ok(()) + } + } +} + +impl + DerefMut> DevMapHash { + /// Sets the value of the element at the given index. + /// + /// # 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: u32, flags: u64) -> Result<(), MapError> { + let fd = self.inner.fd_or_err()?; + self.check_bounds(index)?; + bpf_map_update_elem(fd, &index, &value, flags).map_err(|(code, io_error)| { + MapError::SyscallError { + call: "bpf_map_update_elem".to_owned(), + code, + io_error, + } + })?; + Ok(()) + } +} + +impl TryFrom for DevMapHash { + type Error = MapError; + + fn try_from(a: MapRef) -> Result, MapError> { + DevMapHash::new(a) + } +} + +impl TryFrom for DevMapHash { + type Error = MapError; + + fn try_from(a: MapRefMut) -> Result, MapError> { + DevMapHash::new(a) + } +} diff --git a/aya/src/maps/xdp/mod.rs b/aya/src/maps/xdp/mod.rs new file mode 100644 index 00000000..6120dc90 --- /dev/null +++ b/aya/src/maps/xdp/mod.rs @@ -0,0 +1,10 @@ +//! XDP maps. +mod cpu_map; +mod dev_map; +mod dev_map_hash; +mod xsk_map; + +pub use cpu_map::CpuMap; +pub use dev_map::DevMap; +pub use dev_map_hash::DevMapHash; +pub use xsk_map::XskMap; diff --git a/aya/src/maps/xdp/xsk_map.rs b/aya/src/maps/xdp/xsk_map.rs new file mode 100644 index 00000000..239c908c --- /dev/null +++ b/aya/src/maps/xdp/xsk_map.rs @@ -0,0 +1,118 @@ +//! An array of AF_XDP sockets. + +use std::{ + convert::TryFrom, + mem, + ops::{Deref, DerefMut}, + os::unix::prelude::{AsRawFd, RawFd}, +}; + +use crate::{ + generated::bpf_map_type::BPF_MAP_TYPE_XSKMAP, + maps::{Map, MapError, MapRef, MapRefMut}, + sys::bpf_map_update_elem, +}; + +/// 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.2. +/// +/// # Examples +/// ```no_run +/// # let bpf = aya::Bpf::load(&[])?; +/// # let socket_fd = 1; +/// use aya::maps::XskMap; +/// use std::convert::{TryFrom, TryInto}; +/// +/// let mut xskmap = XskMap::try_from(bpf.map_mut("SOCKETS")?)?; +/// // socket_fd is the RawFd of an AF_XDP socket +/// xskmap.set(0, socket_fd, 0); +/// # Ok::<(), aya::BpfError>(()) +/// ``` +#[doc(alias = "BPF_MAP_TYPE_XSKMAP")] +pub struct XskMap> { + inner: T, +} + +impl> XskMap { + fn new(map: T) -> Result, MapError> { + let map_type = map.obj.def.map_type; + if map_type != BPF_MAP_TYPE_XSKMAP as u32 { + return Err(MapError::InvalidMapType { + map_type: map_type as u32, + }); + } + let expected = mem::size_of::(); + let size = map.obj.def.key_size as usize; + if size != expected { + return Err(MapError::InvalidKeySize { size, expected }); + } + + let expected = mem::size_of::(); + let size = map.obj.def.value_size as usize; + if size != expected { + return Err(MapError::InvalidValueSize { size, expected }); + } + let _fd = map.fd_or_err()?; + + Ok(XskMap { inner: map }) + } + + /// Returns the number of elements in the array. + /// + /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. + pub fn len(&self) -> u32 { + self.inner.obj.def.max_entries + } + + fn check_bounds(&self, index: u32) -> Result<(), MapError> { + let max_entries = self.inner.obj.def.max_entries; + if index >= self.inner.obj.def.max_entries { + Err(MapError::OutOfBounds { index, max_entries }) + } else { + Ok(()) + } + } +} + +impl + DerefMut> XskMap { + /// Sets the value of the element at the given index. + /// + /// # 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> { + let fd = self.inner.fd_or_err()?; + self.check_bounds(index)?; + bpf_map_update_elem(fd, &index, &value.as_raw_fd(), flags).map_err( + |(code, io_error)| MapError::SyscallError { + call: "bpf_map_update_elem".to_owned(), + code, + io_error, + }, + )?; + Ok(()) + } +} + +impl TryFrom for XskMap { + type Error = MapError; + + fn try_from(a: MapRef) -> Result, MapError> { + XskMap::new(a) + } +} + +impl TryFrom for XskMap { + type Error = MapError; + + fn try_from(a: MapRefMut) -> Result, MapError> { + XskMap::new(a) + } +} From e90d521a21ebea7129de140ab8ae84707b03cd65 Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Mon, 30 May 2022 11:58:59 +0100 Subject: [PATCH 02/16] bpf: Implement XDP maps Signed-off-by: Dave Tucker --- bpf/aya-bpf/src/maps/mod.rs | 2 + bpf/aya-bpf/src/maps/xdp/cpu_map.rs | 55 ++++++++++++++++++++++++ bpf/aya-bpf/src/maps/xdp/dev_map.rs | 55 ++++++++++++++++++++++++ bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs | 55 ++++++++++++++++++++++++ bpf/aya-bpf/src/maps/xdp/mod.rs | 9 ++++ bpf/aya-bpf/src/maps/xdp/xsk_map.rs | 55 ++++++++++++++++++++++++ 6 files changed, 231 insertions(+) create mode 100644 bpf/aya-bpf/src/maps/xdp/cpu_map.rs create mode 100644 bpf/aya-bpf/src/maps/xdp/dev_map.rs create mode 100644 bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs create mode 100644 bpf/aya-bpf/src/maps/xdp/mod.rs create mode 100644 bpf/aya-bpf/src/maps/xdp/xsk_map.rs diff --git a/bpf/aya-bpf/src/maps/mod.rs b/bpf/aya-bpf/src/maps/mod.rs index 8fa375dd..b46bf084 100644 --- a/bpf/aya-bpf/src/maps/mod.rs +++ b/bpf/aya-bpf/src/maps/mod.rs @@ -17,6 +17,7 @@ pub mod sock_hash; pub mod sock_map; pub mod stack; pub mod stack_trace; +pub mod xdp; pub use array::Array; pub use bloom_filter::BloomFilter; @@ -30,3 +31,4 @@ pub use sock_hash::SockHash; pub use sock_map::SockMap; pub use stack::Stack; pub use stack_trace::StackTrace; +pub use xdp::{CpuMap, DevMap, DevMapHash, XskMap}; diff --git a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs new file mode 100644 index 00000000..bc16c271 --- /dev/null +++ b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs @@ -0,0 +1,55 @@ +use core::{mem, ptr::NonNull}; + +use aya_bpf_cty::c_void; + +use crate::{ + bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_CPUMAP}, + helpers::bpf_map_lookup_elem, + maps::PinningType, +}; + +#[repr(transparent)] +pub struct CpuMap { + def: bpf_map_def, +} + +impl CpuMap { + pub const fn with_max_entries(max_entries: u32, flags: u32) -> CpuMap { + CpuMap { + def: bpf_map_def { + type_: BPF_MAP_TYPE_CPUMAP, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries, + map_flags: flags, + id: 0, + pinning: PinningType::None as u32, + }, + } + } + + pub const fn pinned(max_entries: u32, flags: u32) -> CpuMap { + CpuMap { + def: bpf_map_def { + type_: BPF_MAP_TYPE_CPUMAP, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries, + map_flags: flags, + id: 0, + pinning: PinningType::None as u32, + }, + } + } + + pub fn get(&mut self, index: u32) -> Option<&u32> { + unsafe { + let value = bpf_map_lookup_elem( + &mut self.def as *mut _ as *mut _, + &index as *const _ as *const c_void, + ); + // FIXME: alignment + NonNull::new(value as *mut u32).map(|p| p.as_ref()) + } + } +} diff --git a/bpf/aya-bpf/src/maps/xdp/dev_map.rs b/bpf/aya-bpf/src/maps/xdp/dev_map.rs new file mode 100644 index 00000000..8a9415ec --- /dev/null +++ b/bpf/aya-bpf/src/maps/xdp/dev_map.rs @@ -0,0 +1,55 @@ +use core::{mem, ptr::NonNull}; + +use aya_bpf_cty::c_void; + +use crate::{ + bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_DEVMAP}, + helpers::bpf_map_lookup_elem, + maps::PinningType, +}; + +#[repr(transparent)] +pub struct DevMap { + def: bpf_map_def, +} + +impl DevMap { + pub const fn with_max_entries(max_entries: u32, flags: u32) -> DevMap { + DevMap { + def: bpf_map_def { + type_: BPF_MAP_TYPE_DEVMAP, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries, + map_flags: flags, + id: 0, + pinning: PinningType::None as u32, + }, + } + } + + pub const fn pinned(max_entries: u32, flags: u32) -> DevMap { + DevMap { + def: bpf_map_def { + type_: BPF_MAP_TYPE_DEVMAP, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries, + map_flags: flags, + id: 0, + pinning: PinningType::None as u32, + }, + } + } + + pub fn get(&mut self, index: u32) -> Option<&u32> { + unsafe { + let value = bpf_map_lookup_elem( + &mut self.def as *mut _ as *mut _, + &index as *const _ as *const c_void, + ); + // FIXME: alignment + NonNull::new(value as *mut u32).map(|p| p.as_ref()) + } + } +} diff --git a/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs b/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs new file mode 100644 index 00000000..6a0b1903 --- /dev/null +++ b/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs @@ -0,0 +1,55 @@ +use core::{mem, ptr::NonNull}; + +use aya_bpf_cty::c_void; + +use crate::{ + bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH}, + helpers::bpf_map_lookup_elem, + maps::PinningType, +}; + +#[repr(transparent)] +pub struct DevMapHash { + def: bpf_map_def, +} + +impl DevMapHash { + pub const fn with_max_entries(max_entries: u32, flags: u32) -> DevMapHash { + DevMapHash { + def: bpf_map_def { + type_: BPF_MAP_TYPE_DEVMAP_HASH, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries, + map_flags: flags, + id: 0, + pinning: PinningType::None as u32, + }, + } + } + + pub const fn pinned(max_entries: u32, flags: u32) -> DevMapHash { + DevMapHash { + def: bpf_map_def { + type_: BPF_MAP_TYPE_DEVMAP_HASH, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries, + map_flags: flags, + id: 0, + pinning: PinningType::None as u32, + }, + } + } + + pub fn get(&mut self, index: u32) -> Option<&u32> { + unsafe { + let value = bpf_map_lookup_elem( + &mut self.def as *mut _ as *mut _, + &index as *const _ as *const c_void, + ); + // FIXME: alignment + NonNull::new(value as *mut u32).map(|p| p.as_ref()) + } + } +} diff --git a/bpf/aya-bpf/src/maps/xdp/mod.rs b/bpf/aya-bpf/src/maps/xdp/mod.rs new file mode 100644 index 00000000..b7ced7b5 --- /dev/null +++ b/bpf/aya-bpf/src/maps/xdp/mod.rs @@ -0,0 +1,9 @@ +mod cpu_map; +mod dev_map; +mod dev_map_hash; +mod xsk_map; + +pub use cpu_map::CpuMap; +pub use dev_map::DevMap; +pub use dev_map_hash::DevMapHash; +pub use xsk_map::XskMap; diff --git a/bpf/aya-bpf/src/maps/xdp/xsk_map.rs b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs new file mode 100644 index 00000000..39fba557 --- /dev/null +++ b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs @@ -0,0 +1,55 @@ +use core::{mem, ptr::NonNull}; + +use aya_bpf_cty::c_void; + +use crate::{ + bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_XSKMAP}, + helpers::bpf_map_lookup_elem, + maps::PinningType, +}; + +#[repr(transparent)] +pub struct XskMap { + def: bpf_map_def, +} + +impl XskMap { + pub const fn with_max_entries(max_entries: u32, flags: u32) -> XskMap { + XskMap { + def: bpf_map_def { + type_: BPF_MAP_TYPE_XSKMAP, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries, + map_flags: flags, + id: 0, + pinning: PinningType::None as u32, + }, + } + } + + pub const fn pinned(max_entries: u32, flags: u32) -> XskMap { + XskMap { + def: bpf_map_def { + type_: BPF_MAP_TYPE_XSKMAP, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries, + map_flags: flags, + id: 0, + pinning: PinningType::None as u32, + }, + } + } + + pub fn get(&mut self, index: u32) -> Option<&u32> { + unsafe { + let value = bpf_map_lookup_elem( + &mut self.def as *mut _ as *mut _, + &index as *const _ as *const c_void, + ); + // FIXME: alignment + NonNull::new(value as *mut u32).map(|p| p.as_ref()) + } + } +} From ede3e91014075de01af02da624cad99861da2dad Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:15:37 +0200 Subject: [PATCH 03/16] aya: Update XDP maps implementations Map impls changed since this was first written. Fixes: 2b726c8 ("aya: Implement XDP Map Types") --- aya/src/bpf.rs | 4 + aya/src/maps/mod.rs | 18 ++++- aya/src/maps/xdp/cpu_map.rs | 111 +++++++++++++-------------- aya/src/maps/xdp/dev_map.rs | 112 ++++++++++++--------------- aya/src/maps/xdp/dev_map_hash.rs | 127 +++++++++++++------------------ aya/src/maps/xdp/xsk_map.rs | 87 +++++---------------- 6 files changed, 197 insertions(+), 262 deletions(-) diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index a5fb9273..999271ec 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -707,6 +707,10 @@ fn parse_map(data: (String, MapData)) -> Result<(String, Map), BpfError> { BPF_MAP_TYPE_STACK => Map::Stack(map), BPF_MAP_TYPE_STACK_TRACE => Map::StackTraceMap(map), BPF_MAP_TYPE_QUEUE => Map::Queue(map), + BPF_MAP_TYPE_CPUMAP => Map::CpuMap(map), + BPF_MAP_TYPE_DEVMAP => Map::DevMap(map), + BPF_MAP_TYPE_DEVMAP_HASH => Map::DevMapHash(map), + BPF_MAP_TYPE_XSKMAP => Map::XskMap(map), m => { warn!("The map {name} is of type {:#?} which is currently unsupported in Aya, use `allow_unsupported_maps()` to load it anyways", m); Map::Unsupported(map) diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index 190d8c67..2f7c556f 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -97,7 +97,7 @@ pub use queue::Queue; pub use sock::{SockHash, SockMap}; pub use stack::Stack; pub use stack_trace::StackTraceMap; -pub use xdp::XskMap; +pub use xdp::{CpuMap, DevMap, DevMapHash, XskMap}; #[derive(Error, Debug)] /// Errors occuring from working with Maps @@ -267,6 +267,14 @@ pub enum Map { StackTraceMap(MapData), /// A [`Queue`] map Queue(MapData), + /// A [`CpuMap`] map + CpuMap(MapData), + /// A [`DevMap`] map + DevMap(MapData), + /// A [`DevMapHash`] map + DevMapHash(MapData), + /// A [`XskMap`] map + XskMap(MapData), /// An unsupported map type Unsupported(MapData), } @@ -290,6 +298,10 @@ impl Map { Self::Stack(map) => map.obj.map_type(), Self::StackTraceMap(map) => map.obj.map_type(), Self::Queue(map) => map.obj.map_type(), + Self::CpuMap(map) => map.obj.map_type(), + Self::DevMap(map) => map.obj.map_type(), + Self::DevMapHash(map) => map.obj.map_type(), + Self::XskMap(map) => map.obj.map_type(), Self::Unsupported(map) => map.obj.map_type(), } } @@ -349,6 +361,10 @@ impl_try_from_map!(() { SockMap, PerfEventArray, StackTraceMap, + CpuMap, + DevMap, + DevMapHash, + XskMap, }); #[cfg(any(feature = "async_tokio", feature = "async_std"))] diff --git a/aya/src/maps/xdp/cpu_map.rs b/aya/src/maps/xdp/cpu_map.rs index 2b6ba3cb..cb6fbe45 100644 --- a/aya/src/maps/xdp/cpu_map.rs +++ b/aya/src/maps/xdp/cpu_map.rs @@ -1,15 +1,10 @@ //! An array of available CPUs. -use std::{ - convert::TryFrom, - mem, - ops::{Deref, DerefMut}, -}; +use std::borrow::{Borrow, BorrowMut}; use crate::{ - generated::bpf_map_type::BPF_MAP_TYPE_CPUMAP, - maps::{Map, MapError, MapRef, MapRefMut}, - sys::bpf_map_update_elem, + maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, + sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, }; /// An array of available CPUs. @@ -19,15 +14,18 @@ use crate::{ /// /// # Minimum kernel version /// -/// The minimum kernel version required to use this feature is 4.2. +/// The minimum kernel version required to use this feature is 4.15. /// /// # Examples /// ```no_run -/// # let bpf = aya::Bpf::load(&[])?; +/// # let elf_bytes = &[]; /// use aya::maps::xdp::CpuMap; -/// use std::convert::{TryFrom, TryInto}; /// -/// let mut cpumap = CpuMap::try_from(bpf.map_mut("CPUS")?)?; +/// let mut bpf = aya::BpfLoader::new() +/// .set_max_entries("CPUS", aya::util::nr_cpus().unwrap() as u32) +/// .load(elf_bytes) +/// .unwrap(); +/// let mut cpumap = CpuMap::try_from(bpf.map_mut("CPUS").unwrap())?; /// let flags = 0; /// let queue_size = 2048; /// for i in 0u32..8u32 { @@ -37,52 +35,51 @@ use crate::{ /// # Ok::<(), aya::BpfError>(()) /// ``` #[doc(alias = "BPF_MAP_TYPE_CPUMAP")] -pub struct CpuMap> { +pub struct CpuMap { inner: T, } -impl> CpuMap { - fn new(map: T) -> Result, MapError> { - let map_type = map.obj.def.map_type; - if map_type != BPF_MAP_TYPE_CPUMAP as u32 { - return Err(MapError::InvalidMapType { - map_type: map_type as u32, - }); - } - let expected = mem::size_of::(); - let size = map.obj.def.key_size as usize; - if size != expected { - return Err(MapError::InvalidKeySize { size, expected }); - } - - let expected = mem::size_of::(); - let size = map.obj.def.value_size as usize; - if size != expected { - return Err(MapError::InvalidValueSize { size, expected }); - } - let _fd = map.fd_or_err()?; +impl> CpuMap { + pub(crate) fn new(map: T) -> Result { + let data = map.borrow(); + check_kv_size::(data)?; - Ok(CpuMap { inner: map }) + Ok(Self { inner: map }) } /// Returns the number of elements in the array. /// /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. pub fn len(&self) -> u32 { - self.inner.obj.def.max_entries + self.inner.borrow().obj.max_entries() } - fn check_bounds(&self, index: u32) -> Result<(), MapError> { - let max_entries = self.inner.obj.def.max_entries; - if index >= self.inner.obj.def.max_entries { - Err(MapError::OutOfBounds { index, max_entries }) - } else { - Ok(()) - } + /// Returns the value stored at the given 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 { + let data = self.inner.borrow(); + check_bounds(data, index)?; + let fd = data.fd().as_fd(); + + let value = + bpf_map_lookup_elem(fd, &index, flags).map_err(|(_, io_error)| SyscallError { + call: "bpf_map_lookup_elem", + io_error, + })?; + value.ok_or(MapError::KeyNotFound) + } + + /// An iterator over the elements of the map. + pub fn iter(&self) -> impl Iterator> + '_ { + (0..self.len()).map(move |i| self.get(i, 0)) } } -impl + DerefMut> CpuMap { +impl> CpuMap { /// Sets the value of the element at the given index. /// /// # Errors @@ -90,12 +87,12 @@ impl + DerefMut> CpuMap { /// 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: u32, flags: u64) -> Result<(), MapError> { - let fd = self.inner.fd_or_err()?; - self.check_bounds(index)?; - bpf_map_update_elem(fd, &index, &value, flags).map_err(|(code, io_error)| { - MapError::SyscallError { - call: "bpf_map_update_elem".to_owned(), - code, + let data = self.inner.borrow_mut(); + check_bounds(data, index)?; + let fd = data.fd().as_fd(); + bpf_map_update_elem(fd, Some(&index), &value, flags).map_err(|(_, io_error)| { + SyscallError { + call: "bpf_map_update_elem", io_error, } })?; @@ -103,18 +100,12 @@ impl + DerefMut> CpuMap { } } -impl TryFrom for CpuMap { - type Error = MapError; - - fn try_from(a: MapRef) -> Result, MapError> { - CpuMap::new(a) +impl> IterableMap for CpuMap { + fn map(&self) -> &MapData { + self.inner.borrow() } -} - -impl TryFrom for CpuMap { - type Error = MapError; - fn try_from(a: MapRefMut) -> Result, MapError> { - CpuMap::new(a) + fn get(&self, key: &u32) -> Result { + self.get(*key, 0) } } diff --git a/aya/src/maps/xdp/dev_map.rs b/aya/src/maps/xdp/dev_map.rs index 365a5b1e..b8fad587 100644 --- a/aya/src/maps/xdp/dev_map.rs +++ b/aya/src/maps/xdp/dev_map.rs @@ -1,15 +1,10 @@ //! An array of network devices. -use std::{ - convert::TryFrom, - mem, - ops::{Deref, DerefMut}, -}; +use std::borrow::{Borrow, BorrowMut}; use crate::{ - generated::bpf_map_type::BPF_MAP_TYPE_DEVMAP, - maps::{Map, MapError, MapRef, MapRefMut}, - sys::bpf_map_update_elem, + maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, + sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, }; /// An array of network devices. @@ -19,67 +14,66 @@ use crate::{ /// /// # Minimum kernel version /// -/// The minimum kernel version required to use this feature is 4.2. +/// The minimum kernel version required to use this feature is 4.14. /// /// # Examples /// ```no_run -/// # let bpf = aya::Bpf::load(&[])?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use aya::maps::xdp::DevMap; -/// use std::convert::{TryFrom, TryInto}; /// -/// let mut devmap = DevMap::try_from(bpf.map_mut("IFACES")?)?; -/// let ifindex = 32u32; -/// devmap.set(ifindex, ifindex, 0); +/// let mut devmap = DevMap::try_from(bpf.map_mut("IFACES").unwrap())?; +/// let source = 32u32; +/// let dest = 42u32; +/// devmap.set(source, dest, 0); /// /// # Ok::<(), aya::BpfError>(()) /// ``` #[doc(alias = "BPF_MAP_TYPE_DEVMAP")] -pub struct DevMap> { +pub struct DevMap { inner: T, } -impl> DevMap { - fn new(map: T) -> Result, MapError> { - let map_type = map.obj.def.map_type; - if map_type != BPF_MAP_TYPE_DEVMAP as u32 { - return Err(MapError::InvalidMapType { - map_type: map_type as u32, - }); - } - let expected = mem::size_of::(); - let size = map.obj.def.key_size as usize; - if size != expected { - return Err(MapError::InvalidKeySize { size, expected }); - } - - let expected = mem::size_of::(); - let size = map.obj.def.value_size as usize; - if size != expected { - return Err(MapError::InvalidValueSize { size, expected }); - } - let _fd = map.fd_or_err()?; +impl> DevMap { + pub(crate) fn new(map: T) -> Result { + let data = map.borrow(); + check_kv_size::(data)?; - Ok(DevMap { inner: map }) + Ok(Self { inner: map }) } /// Returns the number of elements in the array. /// /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. pub fn len(&self) -> u32 { - self.inner.obj.def.max_entries + self.inner.borrow().obj.max_entries() } - fn check_bounds(&self, index: u32) -> Result<(), MapError> { - let max_entries = self.inner.obj.def.max_entries; - if index >= self.inner.obj.def.max_entries { - Err(MapError::OutOfBounds { index, max_entries }) - } else { - Ok(()) - } + /// Returns the value stored at the given 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 { + let data = self.inner.borrow(); + check_bounds(data, index)?; + let fd = data.fd().as_fd(); + + let value = + bpf_map_lookup_elem(fd, &index, flags).map_err(|(_, io_error)| SyscallError { + call: "bpf_map_lookup_elem", + io_error, + })?; + value.ok_or(MapError::KeyNotFound) + } + + /// An iterator over the elements of the array. + pub fn iter(&self) -> impl Iterator> + '_ { + (0..self.len()).map(move |i| self.get(i, 0)) } } -impl + DerefMut> DevMap { +impl> DevMap { /// Sets the value of the element at the given index. /// /// # Errors @@ -87,12 +81,12 @@ impl + DerefMut> DevMap { /// 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: u32, flags: u64) -> Result<(), MapError> { - let fd = self.inner.fd_or_err()?; - self.check_bounds(index)?; - bpf_map_update_elem(fd, &index, &value, flags).map_err(|(code, io_error)| { - MapError::SyscallError { - call: "bpf_map_update_elem".to_owned(), - code, + let data = self.inner.borrow_mut(); + check_bounds(data, index)?; + let fd = data.fd().as_fd(); + bpf_map_update_elem(fd, Some(&index), &value, flags).map_err(|(_, io_error)| { + SyscallError { + call: "bpf_map_update_elem", io_error, } })?; @@ -100,18 +94,12 @@ impl + DerefMut> DevMap { } } -impl TryFrom for DevMap { - type Error = MapError; - - fn try_from(a: MapRef) -> Result, MapError> { - DevMap::new(a) +impl> IterableMap for DevMap { + fn map(&self) -> &MapData { + self.inner.borrow() } -} - -impl TryFrom for DevMap { - type Error = MapError; - fn try_from(a: MapRefMut) -> Result, MapError> { - DevMap::new(a) + fn get(&self, key: &u32) -> Result { + self.get(*key, 0) } } diff --git a/aya/src/maps/xdp/dev_map_hash.rs b/aya/src/maps/xdp/dev_map_hash.rs index 4cf16762..47eed67d 100644 --- a/aya/src/maps/xdp/dev_map_hash.rs +++ b/aya/src/maps/xdp/dev_map_hash.rs @@ -1,118 +1,99 @@ -//! An array of network devices. +//! An hashmap of network devices. -use std::{ - convert::TryFrom, - mem, - ops::{Deref, DerefMut}, -}; +use std::borrow::{Borrow, BorrowMut}; use crate::{ - generated::bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH, - maps::{Map, MapError, MapRef, MapRefMut}, - sys::bpf_map_update_elem, + maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys}, + sys::{bpf_map_lookup_elem, SyscallError}, }; -/// An array of network devices. +/// An hashmap of network devices. /// /// XDP programs can use this map to redirect to other network /// devices. /// /// # Minimum kernel version /// -/// The minimum kernel version required to use this feature is 4.2. +/// The minimum kernel version required to use this feature is 5.4. /// /// # Examples /// ```no_run -/// # let bpf = aya::Bpf::load(&[])?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use aya::maps::xdp::DevMapHash; -/// use std::convert::{TryFrom, TryInto}; /// -/// let mut devmap = DevMapHash::try_from(bpf.map_mut("IFACES")?)?; +/// let mut devmap = DevMapHash::try_from(bpf.map_mut("IFACES").unwrap())?; /// let flags = 0; /// let ifindex = 32u32; -/// devmap.set(ifindex, ifindex, flags); +/// devmap.insert(ifindex, ifindex, flags); /// /// # Ok::<(), aya::BpfError>(()) /// ``` #[doc(alias = "BPF_MAP_TYPE_DEVMAP_HASH")] -pub struct DevMapHash> { +pub struct DevMapHash { inner: T, } -impl> DevMapHash { - fn new(map: T) -> Result, MapError> { - let map_type = map.obj.def.map_type; - if map_type != BPF_MAP_TYPE_DEVMAP_HASH as u32 { - return Err(MapError::InvalidMapType { - map_type: map_type as u32, - }); - } - let expected = mem::size_of::(); - let size = map.obj.def.key_size as usize; - if size != expected { - return Err(MapError::InvalidKeySize { size, expected }); - } - - let expected = mem::size_of::(); - let size = map.obj.def.value_size as usize; - if size != expected { - return Err(MapError::InvalidValueSize { size, expected }); - } - let _fd = map.fd_or_err()?; +impl> DevMapHash { + pub(crate) fn new(map: T) -> Result { + let data = map.borrow(); + check_kv_size::(data)?; - Ok(DevMapHash { inner: map }) + Ok(Self { inner: map }) } - /// Returns the number of elements in the array. + /// Returns the value stored at the given index. + /// + /// # Errors /// - /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. - pub fn len(&self) -> u32 { - self.inner.obj.def.max_entries + /// 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 { + 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, + })?; + value.ok_or(MapError::KeyNotFound) + } + + /// An iterator over the elements of the devmap in arbitrary order. + pub fn iter(&self) -> MapIter<'_, u32, u32, Self> { + MapIter::new(self) } - fn check_bounds(&self, index: u32) -> Result<(), MapError> { - let max_entries = self.inner.obj.def.max_entries; - if index >= self.inner.obj.def.max_entries { - Err(MapError::OutOfBounds { index, max_entries }) - } else { - Ok(()) - } + /// An iterator visiting all keys in arbitrary order. + pub fn keys(&self) -> MapKeys<'_, u32> { + MapKeys::new(self.inner.borrow()) } } -impl + DerefMut> DevMapHash { - /// Sets the value of the element at the given index. +impl> DevMapHash { + /// Inserts a value in the map. /// /// # 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: u32, flags: u64) -> Result<(), MapError> { - let fd = self.inner.fd_or_err()?; - self.check_bounds(index)?; - bpf_map_update_elem(fd, &index, &value, flags).map_err(|(code, io_error)| { - MapError::SyscallError { - call: "bpf_map_update_elem".to_owned(), - code, - io_error, - } - })?; - Ok(()) + /// Returns [`MapError::SyscallError`] if `bpf_map_update_elem` fails. + pub fn insert(&mut self, index: u32, value: u32, flags: u64) -> Result<(), MapError> { + hash_map::insert(self.inner.borrow_mut(), &index, &value, flags) } -} - -impl TryFrom for DevMapHash { - type Error = MapError; - fn try_from(a: MapRef) -> Result, MapError> { - DevMapHash::new(a) + /// Remove a value from the map. + /// + /// # Errors + /// + /// Returns [`MapError::SyscallError`] if `bpf_map_delete_elem` fails. + pub fn remove(&mut self, key: u32) -> Result<(), MapError> { + hash_map::remove(self.inner.borrow_mut(), &key) } } -impl TryFrom for DevMapHash { - type Error = MapError; +impl> IterableMap for DevMapHash { + fn map(&self) -> &MapData { + self.inner.borrow() + } - fn try_from(a: MapRefMut) -> Result, MapError> { - DevMapHash::new(a) + fn get(&self, key: &u32) -> Result { + self.get(*key, 0) } } diff --git a/aya/src/maps/xdp/xsk_map.rs b/aya/src/maps/xdp/xsk_map.rs index 239c908c..133173c0 100644 --- a/aya/src/maps/xdp/xsk_map.rs +++ b/aya/src/maps/xdp/xsk_map.rs @@ -1,16 +1,13 @@ //! An array of AF_XDP sockets. use std::{ - convert::TryFrom, - mem, - ops::{Deref, DerefMut}, - os::unix::prelude::{AsRawFd, RawFd}, + borrow::{Borrow, BorrowMut}, + os::fd::{AsFd, AsRawFd, RawFd}, }; use crate::{ - generated::bpf_map_type::BPF_MAP_TYPE_XSKMAP, - maps::{Map, MapError, MapRef, MapRefMut}, - sys::bpf_map_update_elem, + maps::{check_bounds, check_kv_size, MapData, MapError}, + sys::{bpf_map_update_elem, SyscallError}, }; /// An array of AF_XDP sockets. @@ -20,67 +17,41 @@ use crate::{ /// /// # Minimum kernel version /// -/// The minimum kernel version required to use this feature is 4.2. +/// The minimum kernel version required to use this feature is 4.18. /// /// # Examples /// ```no_run -/// # let bpf = aya::Bpf::load(&[])?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// # let socket_fd = 1; /// use aya::maps::XskMap; -/// use std::convert::{TryFrom, TryInto}; /// -/// let mut xskmap = XskMap::try_from(bpf.map_mut("SOCKETS")?)?; +/// let mut xskmap = XskMap::try_from(bpf.map_mut("SOCKETS").unwrap())?; /// // socket_fd is the RawFd of an AF_XDP socket /// xskmap.set(0, socket_fd, 0); /// # Ok::<(), aya::BpfError>(()) /// ``` #[doc(alias = "BPF_MAP_TYPE_XSKMAP")] -pub struct XskMap> { +pub struct XskMap { inner: T, } -impl> XskMap { - fn new(map: T) -> Result, MapError> { - let map_type = map.obj.def.map_type; - if map_type != BPF_MAP_TYPE_XSKMAP as u32 { - return Err(MapError::InvalidMapType { - map_type: map_type as u32, - }); - } - let expected = mem::size_of::(); - let size = map.obj.def.key_size as usize; - if size != expected { - return Err(MapError::InvalidKeySize { size, expected }); - } +impl> XskMap { + pub(crate) fn new(map: T) -> Result { + let data = map.borrow(); + check_kv_size::(data)?; - let expected = mem::size_of::(); - let size = map.obj.def.value_size as usize; - if size != expected { - return Err(MapError::InvalidValueSize { size, expected }); - } - let _fd = map.fd_or_err()?; - - Ok(XskMap { inner: map }) + Ok(Self { inner: map }) } /// Returns the number of elements in the array. /// /// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side. pub fn len(&self) -> u32 { - self.inner.obj.def.max_entries - } - - fn check_bounds(&self, index: u32) -> Result<(), MapError> { - let max_entries = self.inner.obj.def.max_entries; - if index >= self.inner.obj.def.max_entries { - Err(MapError::OutOfBounds { index, max_entries }) - } else { - Ok(()) - } + self.inner.borrow().obj.max_entries() } } -impl + DerefMut> XskMap { +impl> XskMap { /// Sets the value of the element at the given index. /// /// # Errors @@ -88,31 +59,15 @@ impl + DerefMut> XskMap { /// 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> { - let fd = self.inner.fd_or_err()?; - self.check_bounds(index)?; - bpf_map_update_elem(fd, &index, &value.as_raw_fd(), flags).map_err( - |(code, io_error)| MapError::SyscallError { - call: "bpf_map_update_elem".to_owned(), - code, + 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( + |(_, io_error)| SyscallError { + call: "bpf_map_update_elem", io_error, }, )?; Ok(()) } } - -impl TryFrom for XskMap { - type Error = MapError; - - fn try_from(a: MapRef) -> Result, MapError> { - XskMap::new(a) - } -} - -impl TryFrom for XskMap { - type Error = MapError; - - fn try_from(a: MapRefMut) -> Result, MapError> { - XskMap::new(a) - } -} From ad3087d7eb4f235c161dead521431ed1a5a6b500 Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:15:41 +0200 Subject: [PATCH 04/16] bpf: Update XDP maps implementation The implementation changed since the original commit was written, and some mistakes went in: - missing bpf_redirect_map wrapper - extra bpf_map_lookup_elem on maps for which it is forbidden --- bpf/aya-bpf/src/maps/xdp/cpu_map.rs | 33 +++++++++---------- bpf/aya-bpf/src/maps/xdp/dev_map.rs | 36 +++++++++++++------- bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs | 41 ++++++++++++++--------- bpf/aya-bpf/src/maps/xdp/xsk_map.rs | 42 +++++++++++++++--------- 4 files changed, 93 insertions(+), 59 deletions(-) diff --git a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs index bc16c271..3cf970e5 100644 --- a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs @@ -1,22 +1,22 @@ -use core::{mem, ptr::NonNull}; - -use aya_bpf_cty::c_void; +use core::{cell::UnsafeCell, mem}; use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_CPUMAP}, - helpers::bpf_map_lookup_elem, + helpers::bpf_redirect_map, maps::PinningType, }; #[repr(transparent)] pub struct CpuMap { - def: bpf_map_def, + def: UnsafeCell, } +unsafe impl Sync for CpuMap {} + impl CpuMap { pub const fn with_max_entries(max_entries: u32, flags: u32) -> CpuMap { CpuMap { - def: bpf_map_def { + def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_CPUMAP, key_size: mem::size_of::() as u32, value_size: mem::size_of::() as u32, @@ -24,32 +24,31 @@ impl CpuMap { map_flags: flags, id: 0, pinning: PinningType::None as u32, - }, + }), } } pub const fn pinned(max_entries: u32, flags: u32) -> CpuMap { CpuMap { - def: bpf_map_def { + def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_CPUMAP, key_size: mem::size_of::() as u32, value_size: mem::size_of::() as u32, max_entries, map_flags: flags, id: 0, - pinning: PinningType::None as u32, - }, + pinning: PinningType::ByName as u32, + }), } } - pub fn get(&mut self, index: u32) -> Option<&u32> { + #[inline(always)] + pub fn redirect(&self, index: u32, flags: u64) -> u32 { unsafe { - let value = bpf_map_lookup_elem( - &mut self.def as *mut _ as *mut _, - &index as *const _ as *const c_void, - ); - // FIXME: alignment - NonNull::new(value as *mut u32).map(|p| p.as_ref()) + // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags + // argument on error. Thus I have no idea why it returns a long (i64) instead of + // something saner, hence the unsigned_abs. + bpf_redirect_map(self.def.get() as *mut _, index.into(), flags).unsigned_abs() as u32 } } } diff --git a/bpf/aya-bpf/src/maps/xdp/dev_map.rs b/bpf/aya-bpf/src/maps/xdp/dev_map.rs index 8a9415ec..968deb69 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map.rs @@ -1,22 +1,24 @@ -use core::{mem, ptr::NonNull}; +use core::{cell::UnsafeCell, mem, ptr::NonNull}; use aya_bpf_cty::c_void; use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_DEVMAP}, - helpers::bpf_map_lookup_elem, + helpers::{bpf_map_lookup_elem, bpf_redirect_map}, maps::PinningType, }; #[repr(transparent)] pub struct DevMap { - def: bpf_map_def, + def: UnsafeCell, } +unsafe impl Sync for DevMap {} + impl DevMap { pub const fn with_max_entries(max_entries: u32, flags: u32) -> DevMap { DevMap { - def: bpf_map_def { + def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_DEVMAP, key_size: mem::size_of::() as u32, value_size: mem::size_of::() as u32, @@ -24,32 +26,42 @@ impl DevMap { map_flags: flags, id: 0, pinning: PinningType::None as u32, - }, + }), } } pub const fn pinned(max_entries: u32, flags: u32) -> DevMap { DevMap { - def: bpf_map_def { + def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_DEVMAP, key_size: mem::size_of::() as u32, value_size: mem::size_of::() as u32, max_entries, map_flags: flags, id: 0, - pinning: PinningType::None as u32, - }, + pinning: PinningType::ByName as u32, + }), } } - pub fn get(&mut self, index: u32) -> Option<&u32> { + #[inline(always)] + pub fn get(&self, index: u32) -> Option { unsafe { let value = bpf_map_lookup_elem( - &mut self.def as *mut _ as *mut _, + self.def.get() as *mut _, &index as *const _ as *const c_void, ); - // FIXME: alignment - NonNull::new(value as *mut u32).map(|p| p.as_ref()) + NonNull::new(value as *mut u32).map(|p| *p.as_ref()) + } + } + + #[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 + // argument on error. Thus I have no idea why it returns a long (i64) instead of + // something saner, hence the unsigned_abs. + unsafe { + bpf_redirect_map(self.def.get() as *mut _, index.into(), flags).unsigned_abs() as u32 } } } 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 6a0b1903..4933146b 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs @@ -1,22 +1,25 @@ -use core::{mem, ptr::NonNull}; +use core::{cell::UnsafeCell, mem, ptr::NonNull}; +use aya_bpf_bindings::bindings::bpf_devmap_val; use aya_bpf_cty::c_void; use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH}, - helpers::bpf_map_lookup_elem, + helpers::{bpf_map_lookup_elem, bpf_redirect_map}, maps::PinningType, }; #[repr(transparent)] pub struct DevMapHash { - def: bpf_map_def, + def: UnsafeCell, } +unsafe impl Sync for DevMapHash {} + impl DevMapHash { pub const fn with_max_entries(max_entries: u32, flags: u32) -> DevMapHash { DevMapHash { - def: bpf_map_def { + def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_DEVMAP_HASH, key_size: mem::size_of::() as u32, value_size: mem::size_of::() as u32, @@ -24,32 +27,40 @@ impl DevMapHash { map_flags: flags, id: 0, pinning: PinningType::None as u32, - }, + }), } } pub const fn pinned(max_entries: u32, flags: u32) -> DevMapHash { DevMapHash { - def: bpf_map_def { + def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_DEVMAP_HASH, key_size: mem::size_of::() as u32, value_size: mem::size_of::() as u32, max_entries, map_flags: flags, id: 0, - pinning: PinningType::None as u32, - }, + pinning: PinningType::ByName as u32, + }), } } - pub fn get(&mut self, index: u32) -> Option<&u32> { + #[inline(always)] + pub unsafe fn get(&self, index: u32) -> Option<&bpf_devmap_val> { + let value = bpf_map_lookup_elem( + self.def.get() as *mut _, + &index as *const _ as *const c_void, + ); + NonNull::new(value as *mut bpf_devmap_val).map(|p| p.as_ref()) + } + + #[inline(always)] + pub fn redirect(&self, index: u32, flags: u64) -> u32 { unsafe { - let value = bpf_map_lookup_elem( - &mut self.def as *mut _ as *mut _, - &index as *const _ as *const c_void, - ); - // FIXME: alignment - NonNull::new(value as *mut u32).map(|p| p.as_ref()) + // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags + // argument on error. Thus I have no idea why it returns a long (i64) instead of + // something saner, hence the unsigned_abs. + bpf_redirect_map(self.def.get() as *mut _, index.into(), flags).unsigned_abs() as u32 } } } diff --git a/bpf/aya-bpf/src/maps/xdp/xsk_map.rs b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs index 39fba557..362f258e 100644 --- a/bpf/aya-bpf/src/maps/xdp/xsk_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs @@ -1,22 +1,25 @@ -use core::{mem, ptr::NonNull}; +use core::{cell::UnsafeCell, mem, ptr::NonNull}; +use aya_bpf_bindings::bindings::bpf_xdp_sock; use aya_bpf_cty::c_void; use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_XSKMAP}, - helpers::bpf_map_lookup_elem, + helpers::{bpf_map_lookup_elem, bpf_redirect_map}, maps::PinningType, }; #[repr(transparent)] pub struct XskMap { - def: bpf_map_def, + def: UnsafeCell, } +unsafe impl Sync for XskMap {} + impl XskMap { pub const fn with_max_entries(max_entries: u32, flags: u32) -> XskMap { XskMap { - def: bpf_map_def { + def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_XSKMAP, key_size: mem::size_of::() as u32, value_size: mem::size_of::() as u32, @@ -24,32 +27,41 @@ impl XskMap { map_flags: flags, id: 0, pinning: PinningType::None as u32, - }, + }), } } pub const fn pinned(max_entries: u32, flags: u32) -> XskMap { XskMap { - def: bpf_map_def { + def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_XSKMAP, key_size: mem::size_of::() as u32, value_size: mem::size_of::() as u32, max_entries, map_flags: flags, id: 0, - pinning: PinningType::None as u32, - }, + pinning: PinningType::ByName as u32, + }), } } - pub fn get(&mut self, index: u32) -> Option<&u32> { + #[inline(always)] + pub unsafe fn get(&self, index: u32) -> Option<&bpf_xdp_sock> { + let value = bpf_map_lookup_elem( + self.def.get() as *mut _, + &index as *const _ as *const c_void, + ); + // FIXME: alignment + NonNull::new(value as *mut bpf_xdp_sock).map(|p| p.as_ref()) + } + + #[inline(always)] + pub fn redirect(&self, index: u32, flags: u64) -> u32 { unsafe { - let value = bpf_map_lookup_elem( - &mut self.def as *mut _ as *mut _, - &index as *const _ as *const c_void, - ); - // FIXME: alignment - NonNull::new(value as *mut u32).map(|p| p.as_ref()) + // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags + // argument on error. Thus I have no idea why it returns a long (i64) instead of + // something saner, hence the unsigned_abs. + bpf_redirect_map(self.def.get() as *mut _, index.into(), flags).unsigned_abs() as u32 } } } From 4452364c41c73943d4603fce4bab24ce70a2eff2 Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:15:44 +0200 Subject: [PATCH 05/16] macros: add 'map' option to xdp macro This option allows to place the program in the specific sections to chain programs with devmaps and cpumaps. --- aya-bpf-macros/src/xdp.rs | 157 +++++++++++++++++++++++-- test/integration-ebpf/Cargo.toml | 8 ++ test/integration-ebpf/src/redirect.rs | 44 +++++++ test/integration-ebpf/src/xdp_sec.rs | 26 ++++ test/integration-test/src/lib.rs | 2 + test/integration-test/src/tests.rs | 1 + test/integration-test/src/tests/xdp.rs | 35 ++++++ 7 files changed, 264 insertions(+), 9 deletions(-) create mode 100644 test/integration-ebpf/src/redirect.rs create mode 100644 test/integration-ebpf/src/xdp_sec.rs create mode 100644 test/integration-test/src/tests/xdp.rs diff --git a/aya-bpf-macros/src/xdp.rs b/aya-bpf-macros/src/xdp.rs index 9a5ff36a..6a8f61e7 100644 --- a/aya-bpf-macros/src/xdp.rs +++ b/aya-bpf-macros/src/xdp.rs @@ -1,31 +1,52 @@ -use std::borrow::Cow; - use proc_macro2::TokenStream; use quote::quote; -use syn::{ItemFn, Result}; +use syn::{Error, ItemFn, Result}; -use crate::args::{err_on_unknown_args, pop_bool_arg, Args}; +use crate::args::{err_on_unknown_args, pop_bool_arg, pop_string_arg, Args}; pub(crate) struct Xdp { item: ItemFn, frags: bool, + map: Option, +} + +#[derive(Clone, Copy)] +pub(crate) enum XdpMap { + CpuMap, + DevMap, } impl Xdp { pub(crate) fn parse(attrs: TokenStream, item: TokenStream) -> Result { let item = syn::parse2(item)?; let mut args: Args = syn::parse2(attrs)?; + let frags = pop_bool_arg(&mut args, "frags"); + let map = match pop_string_arg(&mut args, "map").as_deref() { + Some("cpumap") => Some(XdpMap::CpuMap), + Some("devmap") => Some(XdpMap::DevMap), + Some(name) => { + return Err(Error::new_spanned( + "map", + format!("invalid value. expected 'cpumap' or 'devmap', found '{name}'"), + )) + } + None => None, + }; + err_on_unknown_args(&args)?; - Ok(Xdp { item, frags }) + Ok(Xdp { item, frags, map }) } pub(crate) fn expand(&self) -> Result { - let section_name: Cow<'_, _> = if self.frags { - "xdp.frags".into() - } else { - "xdp".into() + let mut section_name = vec![if self.frags { "xdp.frags" } else { "xdp" }]; + match self.map { + Some(XdpMap::CpuMap) => section_name.push("cpumap"), + Some(XdpMap::DevMap) => section_name.push("devmap"), + None => (), }; + let section_name = section_name.join("/"); + let fn_vis = &self.item.vis; let fn_name = self.item.sig.ident.clone(); let item = &self.item; @@ -97,4 +118,122 @@ mod tests { }; assert_eq!(expected.to_string(), expanded.to_string()); } + + #[test] + fn test_xdp_cpumap() { + let prog = Xdp::parse( + parse_quote! { map = "cpumap" }, + parse_quote! { + fn prog(ctx: &mut ::aya_bpf::programs::XdpContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand().unwrap(); + let expected = quote! { + #[no_mangle] + #[link_section = "xdp/cpumap"] + fn prog(ctx: *mut ::aya_bpf::bindings::xdp_md) -> u32 { + return prog(::aya_bpf::programs::XdpContext::new(ctx)); + + fn prog(ctx: &mut ::aya_bpf::programs::XdpContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } + + #[test] + fn test_xdp_devmap() { + let prog = Xdp::parse( + parse_quote! { map = "devmap" }, + parse_quote! { + fn prog(ctx: &mut ::aya_bpf::programs::XdpContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand().unwrap(); + let expected = quote! { + #[no_mangle] + #[link_section = "xdp/devmap"] + fn prog(ctx: *mut ::aya_bpf::bindings::xdp_md) -> u32 { + return prog(::aya_bpf::programs::XdpContext::new(ctx)); + + fn prog(ctx: &mut ::aya_bpf::programs::XdpContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } + + #[test] + #[should_panic(expected = "Invalid value. Expected 'cpumap' or 'devmap', found 'badmap'")] + fn test_xdp_bad_map() { + Xdp::parse( + parse_quote! { map = "badmap" }, + parse_quote! { + fn prog(ctx: &mut ::aya_bpf::programs::XdpContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + } + + #[test] + fn test_xdp_frags_cpumap() { + let prog = Xdp::parse( + parse_quote! { frags, map = "cpumap" }, + parse_quote! { + fn prog(ctx: &mut ::aya_bpf::programs::XdpContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand().unwrap(); + let expected = quote! { + #[no_mangle] + #[link_section = "xdp.frags/cpumap"] + fn prog(ctx: *mut ::aya_bpf::bindings::xdp_md) -> u32 { + return prog(::aya_bpf::programs::XdpContext::new(ctx)); + + fn prog(ctx: &mut ::aya_bpf::programs::XdpContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } + + #[test] + fn test_xdp_frags_devmap() { + let prog = Xdp::parse( + parse_quote! { frags, map = "devmap" }, + parse_quote! { + fn prog(ctx: &mut ::aya_bpf::programs::XdpContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand().unwrap(); + let expected = quote! { + #[no_mangle] + #[link_section = "xdp.frags/devmap"] + fn prog(ctx: *mut ::aya_bpf::bindings::xdp_md) -> u32 { + return prog(::aya_bpf::programs::XdpContext::new(ctx)); + + fn prog(ctx: &mut ::aya_bpf::programs::XdpContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } } diff --git a/test/integration-ebpf/Cargo.toml b/test/integration-ebpf/Cargo.toml index 12b04546..e72e0a87 100644 --- a/test/integration-ebpf/Cargo.toml +++ b/test/integration-ebpf/Cargo.toml @@ -43,3 +43,11 @@ path = "src/bpf_probe_read.rs" [[bin]] name = "two_progs" path = "src/two_progs.rs" + +[[bin]] +name = "redirect" +path = "src/redirect.rs" + +[[bin]] +name = "xdp_sec" +path = "src/xdp_sec.rs" diff --git a/test/integration-ebpf/src/redirect.rs b/test/integration-ebpf/src/redirect.rs new file mode 100644 index 00000000..827ea451 --- /dev/null +++ b/test/integration-ebpf/src/redirect.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] + +use aya_bpf::{ + bindings::xdp_action, + macros::{map, xdp}, + maps::{CpuMap, DevMap, DevMapHash, XskMap}, + programs::XdpContext, +}; + +#[map] +static SOCKS: XskMap = XskMap::with_max_entries(1, 0); +#[map] +static DEVS: DevMap = DevMap::with_max_entries(1, 0); +#[map] +static DEVS_HASH: DevMapHash = DevMapHash::with_max_entries(1, 0); +#[map] +static CPUS: CpuMap = CpuMap::with_max_entries(1, 0); + +#[xdp] +pub fn redirect_sock(_ctx: XdpContext) -> u32 { + SOCKS.redirect(0, xdp_action::XDP_ABORTED as u64) +} + +#[xdp] +pub fn redirect_dev(_ctx: XdpContext) -> u32 { + DEVS.redirect(0, xdp_action::XDP_ABORTED as u64) +} + +#[xdp] +pub fn redirect_dev_hash(_ctx: XdpContext) -> u32 { + DEVS_HASH.redirect(10, xdp_action::XDP_ABORTED as u64) +} + +#[xdp] +pub fn redirect_cpu(_ctx: XdpContext) -> u32 { + CPUS.redirect(0, xdp_action::XDP_ABORTED as u64) +} + +#[cfg(not(test))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/test/integration-ebpf/src/xdp_sec.rs b/test/integration-ebpf/src/xdp_sec.rs new file mode 100644 index 00000000..5e64f56a --- /dev/null +++ b/test/integration-ebpf/src/xdp_sec.rs @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] + +use aya_bpf::{bindings::xdp_action::XDP_PASS, macros::xdp, programs::XdpContext}; + +macro_rules! probe { + ($name:ident, ($($arg:ident $(= $value:literal)?),*) ) => { + #[xdp($($arg $(= $value)?),*)] + pub fn $name(_ctx: XdpContext) -> u32 { + XDP_PASS + } + }; +} + +probe!(xdp_plain, ()); +probe!(xdp_frags, (frags)); +probe!(xdp_cpumap, (map = "cpumap")); +probe!(xdp_devmap, (map = "devmap")); +probe!(xdp_frags_cpumap, (frags, map = "cpumap")); +probe!(xdp_frags_devmap, (frags, map = "devmap")); + +#[cfg(not(test))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/test/integration-test/src/lib.rs b/test/integration-test/src/lib.rs index 08911a4e..79be5fd3 100644 --- a/test/integration-test/src/lib.rs +++ b/test/integration-test/src/lib.rs @@ -19,6 +19,8 @@ pub const RELOCATIONS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), " pub const TWO_PROGS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/two_progs")); pub const BPF_PROBE_READ: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/bpf_probe_read")); +pub const REDIRECT: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/redirect")); +pub const XDP_SEC: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/xdp_sec")); #[cfg(test)] mod tests; diff --git a/test/integration-test/src/tests.rs b/test/integration-test/src/tests.rs index dd8565b0..3a995180 100644 --- a/test/integration-test/src/tests.rs +++ b/test/integration-test/src/tests.rs @@ -6,3 +6,4 @@ mod log; mod rbpf; mod relocations; mod smoke; +mod xdp; diff --git a/test/integration-test/src/tests/xdp.rs b/test/integration-test/src/tests/xdp.rs new file mode 100644 index 00000000..0c2f56da --- /dev/null +++ b/test/integration-test/src/tests/xdp.rs @@ -0,0 +1,35 @@ +use object::{Object, ObjectSection, ObjectSymbol, SymbolSection}; + +#[test] +fn prog_sections() { + let obj_file = object::File::parse(crate::XDP_SEC).unwrap(); + + ensure_symbol(&obj_file, "xdp", "xdp_plain"); + ensure_symbol(&obj_file, "xdp.frags", "xdp_frags"); + ensure_symbol(&obj_file, "xdp/cpumap", "xdp_cpumap"); + ensure_symbol(&obj_file, "xdp/devmap", "xdp_devmap"); + ensure_symbol(&obj_file, "xdp.frags/cpumap", "xdp_frags_cpumap"); + ensure_symbol(&obj_file, "xdp.frags/devmap", "xdp_frags_devmap"); +} + +#[track_caller] +fn ensure_symbol(obj_file: &object::File, sec_name: &str, sym_name: &str) { + let sec = obj_file.section_by_name(sec_name).unwrap_or_else(|| { + let secs = obj_file + .sections() + .flat_map(|sec| sec.name().ok().map(|name| name.to_owned())) + .collect::>(); + panic!("section {sec_name} not found. available sections: {secs:?}"); + }); + let sec = SymbolSection::Section(sec.index()); + + let syms = obj_file + .symbols() + .filter(|sym| sym.section() == sec) + .filter_map(|sym| sym.name().ok()) + .collect::>(); + assert!( + syms.contains(&sym_name), + "symbol not found. available symbols in section: {syms:?}" + ); +} From 139f3826383daba9a10dc7aacc079f31d28980fc Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:15:49 +0200 Subject: [PATCH 06/16] aya: add support for map-bound XDP programs Such programs are to be bound to cpumap or devmap instead of the usual network interfaces. --- aya-obj/src/obj.rs | 23 +++++++++++----- aya-obj/src/programs/mod.rs | 2 ++ aya-obj/src/programs/xdp.rs | 24 +++++++++++++++++ aya/src/bpf.rs | 14 +++++++--- aya/src/programs/mod.rs | 1 - aya/src/programs/xdp.rs | 35 ++++++++++++++++++++++--- test/integration-test/src/tests/load.rs | 6 +++-- test/integration-test/src/tests/rbpf.rs | 7 +++-- test/integration-test/src/tests/xdp.rs | 13 +++++++++ 9 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 aya-obj/src/programs/xdp.rs diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index deadeeaf..ce599d3c 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -19,6 +19,7 @@ use crate::{ btf::BtfFeatures, generated::{BPF_CALL, BPF_JMP, BPF_K}, maps::{BtfMap, LegacyMap, Map, MINIMUM_MAP_SIZE}, + programs::XdpAttachType, relocation::*, util::HashMap, }; @@ -204,8 +205,6 @@ pub struct Function { /// - `struct_ops+` /// - `fmod_ret+`, `fmod_ret.s+` /// - `iter+`, `iter.s+` -/// - `xdp.frags/cpumap`, `xdp/cpumap` -/// - `xdp.frags/devmap`, `xdp/devmap` #[derive(Debug, Clone)] #[allow(missing_docs)] pub enum ProgramSection { @@ -221,6 +220,7 @@ pub enum ProgramSection { SocketFilter, Xdp { frags: bool, + attach_type: XdpAttachType, }, SkMsg, SkSkbStreamParser, @@ -283,8 +283,19 @@ impl FromStr for ProgramSection { "uprobe.s" => UProbe { sleepable: true }, "uretprobe" => URetProbe { sleepable: false }, "uretprobe.s" => URetProbe { sleepable: true }, - "xdp" => Xdp { frags: false }, - "xdp.frags" => Xdp { frags: true }, + "xdp" | "xdp.frags" => Xdp { + frags: kind == "xdp.frags", + attach_type: match pieces.next() { + None => XdpAttachType::Interface, + Some("cpumap") => XdpAttachType::CpuMap, + Some("devmap") => XdpAttachType::DevMap, + Some(_) => { + return Err(ParseError::InvalidProgramSection { + section: section.to_owned(), + }) + } + }, + }, "tp_btf" => BtfTracePoint, "tracepoint" | "tp" => TracePoint, "socket" => SocketFilter, @@ -2012,7 +2023,7 @@ mod tests { assert_matches!( obj.parse_section(fake_section( BpfSectionKind::Program, - "xdp/foo", + "xdp", bytes_of(&fake_ins()), None )), @@ -2035,7 +2046,7 @@ mod tests { assert_matches!( obj.parse_section(fake_section( BpfSectionKind::Program, - "xdp.frags/foo", + "xdp.frags", bytes_of(&fake_ins()), None )), diff --git a/aya-obj/src/programs/mod.rs b/aya-obj/src/programs/mod.rs index 4f76211a..6b66b005 100644 --- a/aya-obj/src/programs/mod.rs +++ b/aya-obj/src/programs/mod.rs @@ -3,7 +3,9 @@ pub mod cgroup_sock; pub mod cgroup_sock_addr; pub mod cgroup_sockopt; +pub mod xdp; pub use cgroup_sock::CgroupSockAttachType; pub use cgroup_sock_addr::CgroupSockAddrAttachType; pub use cgroup_sockopt::CgroupSockoptAttachType; +pub use xdp::XdpAttachType; diff --git a/aya-obj/src/programs/xdp.rs b/aya-obj/src/programs/xdp.rs new file mode 100644 index 00000000..17fab6ab --- /dev/null +++ b/aya-obj/src/programs/xdp.rs @@ -0,0 +1,24 @@ +//! XDP programs. + +use crate::generated::bpf_attach_type; + +/// Defines where to attach an `XDP` program. +#[derive(Copy, Clone, Debug)] +pub enum XdpAttachType { + /// Attach to a network interface. + Interface, + /// Attach to a cpumap. Requires kernel 5.9 or later. + CpuMap, + /// Attach to a devmap. Requires kernel 5.8 or later. + DevMap, +} + +impl From for bpf_attach_type { + fn from(value: XdpAttachType) -> Self { + match value { + XdpAttachType::Interface => bpf_attach_type::BPF_XDP, + XdpAttachType::CpuMap => bpf_attach_type::BPF_XDP_CPUMAP, + XdpAttachType::DevMap => bpf_attach_type::BPF_XDP_DEVMAP, + } + } +} diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 999271ec..0140886d 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -412,7 +412,10 @@ impl<'a> BpfLoader<'a> { | ProgramSection::URetProbe { sleepable: _ } | ProgramSection::TracePoint | ProgramSection::SocketFilter - | ProgramSection::Xdp { frags: _ } + | ProgramSection::Xdp { + frags: _, + attach_type: _, + } | ProgramSection::SkMsg | ProgramSection::SkSkbStreamParser | ProgramSection::SkSkbStreamVerdict @@ -556,13 +559,18 @@ impl<'a> BpfLoader<'a> { ProgramSection::SocketFilter => Program::SocketFilter(SocketFilter { data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), }), - ProgramSection::Xdp { frags, .. } => { + ProgramSection::Xdp { + frags, attach_type, .. + } => { let mut data = ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); if *frags { data.flags = BPF_F_XDP_HAS_FRAGS; } - Program::Xdp(Xdp { data }) + Program::Xdp(Xdp { + data, + attach_type: *attach_type, + }) } ProgramSection::SkMsg => Program::SkMsg(SkMsg { data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 0952a348..bb9622fe 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -879,7 +879,6 @@ macro_rules! impl_from_pin { impl_from_pin!( TracePoint, SocketFilter, - Xdp, SkMsg, CgroupSysctl, LircMode2, diff --git a/aya/src/programs/xdp.rs b/aya/src/programs/xdp.rs index 89574328..481d95d9 100644 --- a/aya/src/programs/xdp.rs +++ b/aya/src/programs/xdp.rs @@ -12,18 +12,21 @@ use std::{ hash::Hash, io, os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd}, + path::Path, }; use thiserror::Error; use crate::{ generated::{ - bpf_attach_type, bpf_link_type, bpf_prog_type, XDP_FLAGS_DRV_MODE, XDP_FLAGS_HW_MODE, - XDP_FLAGS_REPLACE, XDP_FLAGS_SKB_MODE, XDP_FLAGS_UPDATE_IF_NOEXIST, + bpf_link_type, bpf_prog_type, XDP_FLAGS_DRV_MODE, XDP_FLAGS_HW_MODE, XDP_FLAGS_REPLACE, + XDP_FLAGS_SKB_MODE, XDP_FLAGS_UPDATE_IF_NOEXIST, }, + obj::programs::XdpAttachType, programs::{ define_link_wrapper, load_program, FdLink, Link, LinkError, ProgramData, ProgramError, }, sys::{bpf_link_create, bpf_link_get_info_by_fd, bpf_link_update, netlink_set_xdp_fd}, + VerifierLogLevel, }; /// The type returned when attaching an [`Xdp`] program fails on kernels `< 5.9`. @@ -80,12 +83,13 @@ bitflags::bitflags! { #[doc(alias = "BPF_PROG_TYPE_XDP")] pub struct Xdp { pub(crate) data: ProgramData, + pub(crate) attach_type: XdpAttachType, } impl Xdp { /// Loads the program inside the kernel. pub fn load(&mut self) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(bpf_attach_type::BPF_XDP); + self.data.expected_attach_type = Some(self.attach_type.into()); load_program(bpf_prog_type::BPF_PROG_TYPE_XDP, &mut self.data) } @@ -133,10 +137,18 @@ impl Xdp { let prog_fd = prog_fd.as_fd(); if KernelVersion::current().unwrap() >= KernelVersion::new(5, 9, 0) { + // Unwrap safety: the function starts with `self.fd()?` that will succeed if and only + // if the program has been loaded, i.e. there is an fd. We get one by: + // - Using `Xdp::from_pin` that sets `expected_attach_type` + // - Calling `Xdp::attach` that sets `expected_attach_type`, as geting an `Xdp` + // instance trhough `Xdp:try_from(Program)` does not set any fd. + // So, in all cases where we have an fd, we have an expected_attach_type. Thus, if we + // reach this point, expected_attach_type is guaranteed to be Some(_). + let attach_type = self.data.expected_attach_type.unwrap(); let link_fd = bpf_link_create( prog_fd, LinkTarget::IfIndex(if_index), - bpf_attach_type::BPF_XDP, + attach_type, None, flags.bits(), ) @@ -163,6 +175,21 @@ impl Xdp { } } + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + pub fn from_pin>( + path: P, + attach_type: XdpAttachType, + ) -> Result { + let mut data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + data.expected_attach_type = Some(attach_type.into()); + Ok(Self { data, attach_type }) + } + /// Detaches the program. /// /// See [Xdp::attach]. diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index 4c99a008..d444b95d 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -9,6 +9,7 @@ use aya::{ util::KernelVersion, Bpf, }; +use aya_obj::programs::XdpAttachType; const MAX_RETRIES: usize = 100; const RETRY_DURATION: time::Duration = time::Duration::from_millis(10); @@ -283,7 +284,7 @@ fn pin_lifecycle() { // 2. Load program from bpffs but don't attach it { - let _ = Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog").unwrap(); + let _ = Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog", XdpAttachType::Interface).unwrap(); } // should still be loaded since prog was pinned @@ -291,7 +292,8 @@ fn pin_lifecycle() { // 3. Load program from bpffs and attach { - let mut prog = Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog").unwrap(); + let mut prog = + Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog", XdpAttachType::Interface).unwrap(); let link_id = prog.attach("lo", XdpFlags::default()).unwrap(); let link = prog.take_link(link_id).unwrap(); let fd_link: FdLink = link.try_into().unwrap(); diff --git a/test/integration-test/src/tests/rbpf.rs b/test/integration-test/src/tests/rbpf.rs index 9caab94c..3d2c0c97 100644 --- a/test/integration-test/src/tests/rbpf.rs +++ b/test/integration-test/src/tests/rbpf.rs @@ -2,7 +2,7 @@ use core::{mem::size_of, ptr::null_mut, slice::from_raw_parts}; use std::collections::HashMap; use assert_matches::assert_matches; -use aya_obj::{generated::bpf_insn, Object, ProgramSection}; +use aya_obj::{generated::bpf_insn, programs::XdpAttachType, Object, ProgramSection}; #[test] fn run_with_rbpf() { @@ -11,7 +11,10 @@ fn run_with_rbpf() { assert_eq!(object.programs.len(), 1); assert_matches!( object.programs["pass"].section, - ProgramSection::Xdp { frags: true } + ProgramSection::Xdp { + frags: true, + attach_type: XdpAttachType::Interface + } ); let instructions = &object diff --git a/test/integration-test/src/tests/xdp.rs b/test/integration-test/src/tests/xdp.rs index 0c2f56da..566ca5e5 100644 --- a/test/integration-test/src/tests/xdp.rs +++ b/test/integration-test/src/tests/xdp.rs @@ -1,3 +1,4 @@ +use aya::Bpf; use object::{Object, ObjectSection, ObjectSymbol, SymbolSection}; #[test] @@ -33,3 +34,15 @@ fn ensure_symbol(obj_file: &object::File, sec_name: &str, sym_name: &str) { "symbol not found. available symbols in section: {syms:?}" ); } + +#[test] +fn map_load() { + let bpf = Bpf::load(crate::XDP_SEC).unwrap(); + + bpf.program("xdp_plain").unwrap(); + bpf.program("xdp_frags").unwrap(); + bpf.program("xdp_cpumap").unwrap(); + bpf.program("xdp_devmap").unwrap(); + bpf.program("xdp_frags_cpumap").unwrap(); + bpf.program("xdp_frags_devmap").unwrap(); +} From 0647927e32333de662c6a065d5f5b9761c429e68 Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:16:10 +0200 Subject: [PATCH 07/16] xdp: add support for chained xdp programs in {cpu,dev}map set/insert functions can now take an optional bpf program fd to run once the packet has been redirected from the main probe --- aya/src/maps/xdp/cpu_map.rs | 55 +++++++++++++++++++---- aya/src/maps/xdp/dev_map.rs | 56 ++++++++++++++++++++---- aya/src/maps/xdp/dev_map_hash.rs | 48 ++++++++++++++++---- bpf/aya-bpf/src/maps/xdp/cpu_map.rs | 6 ++- bpf/aya-bpf/src/maps/xdp/dev_map.rs | 5 ++- bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs | 4 +- test/integration-ebpf/src/redirect.rs | 31 ++++++++++++- test/integration-test/src/tests/xdp.rs | 52 +++++++++++++++++++++- 8 files changed, 222 insertions(+), 35 deletions(-) diff --git a/aya/src/maps/xdp/cpu_map.rs b/aya/src/maps/xdp/cpu_map.rs index cb6fbe45..b01fc050 100644 --- a/aya/src/maps/xdp/cpu_map.rs +++ b/aya/src/maps/xdp/cpu_map.rs @@ -1,10 +1,17 @@ //! An array of available CPUs. -use std::borrow::{Borrow, BorrowMut}; +use std::{ + borrow::{Borrow, BorrowMut}, + num::NonZeroU32, + os::fd::AsRawFd, +}; + +use aya_obj::generated::{bpf_cpumap_val, bpf_cpumap_val__bindgen_ty_1}; use crate::{ maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, + Pod, }; /// An array of available CPUs. @@ -29,7 +36,7 @@ use crate::{ /// let flags = 0; /// let queue_size = 2048; /// for i in 0u32..8u32 { -/// cpumap.set(i, queue_size, flags); +/// cpumap.set(i, queue_size, None::, flags); /// } /// /// # Ok::<(), aya::BpfError>(()) @@ -42,7 +49,7 @@ pub struct CpuMap { impl> CpuMap { pub(crate) fn new(map: T) -> Result { let data = map.borrow(); - check_kv_size::(data)?; + check_kv_size::(data)?; Ok(Self { inner: map }) } @@ -60,7 +67,7 @@ impl> CpuMap { /// /// 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 { + pub fn get(&self, index: u32, flags: u64) -> Result { let data = self.inner.borrow(); check_bounds(data, index)?; let fd = data.fd().as_fd(); @@ -70,11 +77,18 @@ impl> CpuMap { call: "bpf_map_lookup_elem", io_error, })?; - value.ok_or(MapError::KeyNotFound) + let value: bpf_cpumap_val = value.ok_or(MapError::KeyNotFound)?; + + // SAFETY: map writes use fd, map reads use id. + // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6241 + Ok(CpuMapValue { + qsize: value.qsize, + prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), + }) } /// An iterator over the elements of the map. - pub fn iter(&self) -> impl Iterator> + '_ { + pub fn iter(&self) -> impl Iterator> + '_ { (0..self.len()).map(move |i| self.get(i, 0)) } } @@ -86,10 +100,25 @@ impl> CpuMap { /// /// 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: u32, flags: u64) -> Result<(), MapError> { + pub fn set( + &mut self, + index: u32, + value: u32, + program: Option, + flags: u64, + ) -> Result<(), MapError> { let data = self.inner.borrow_mut(); check_bounds(data, index)?; let fd = data.fd().as_fd(); + + let value = bpf_cpumap_val { + qsize: value, + 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)| { SyscallError { call: "bpf_map_update_elem", @@ -100,12 +129,20 @@ impl> CpuMap { } } -impl> IterableMap for CpuMap { +impl> IterableMap for CpuMap { fn map(&self) -> &MapData { self.inner.borrow() } - fn get(&self, key: &u32) -> Result { + fn get(&self, key: &u32) -> Result { self.get(*key, 0) } } + +unsafe impl Pod for bpf_cpumap_val {} + +#[derive(Clone, Copy, Debug)] +pub struct CpuMapValue { + pub qsize: u32, + pub prog_id: Option, +} diff --git a/aya/src/maps/xdp/dev_map.rs b/aya/src/maps/xdp/dev_map.rs index b8fad587..44c1d27a 100644 --- a/aya/src/maps/xdp/dev_map.rs +++ b/aya/src/maps/xdp/dev_map.rs @@ -1,10 +1,17 @@ //! An array of network devices. -use std::borrow::{Borrow, BorrowMut}; +use std::{ + borrow::{Borrow, BorrowMut}, + num::NonZeroU32, + os::fd::AsRawFd, +}; + +use aya_obj::generated::{bpf_devmap_val, bpf_devmap_val__bindgen_ty_1}; use crate::{ maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, + Pod, }; /// An array of network devices. @@ -24,7 +31,7 @@ use crate::{ /// let mut devmap = DevMap::try_from(bpf.map_mut("IFACES").unwrap())?; /// let source = 32u32; /// let dest = 42u32; -/// devmap.set(source, dest, 0); +/// devmap.set(source, dest, None::, 0); /// /// # Ok::<(), aya::BpfError>(()) /// ``` @@ -36,7 +43,7 @@ pub struct DevMap { impl> DevMap { pub(crate) fn new(map: T) -> Result { let data = map.borrow(); - check_kv_size::(data)?; + check_kv_size::(data)?; Ok(Self { inner: map }) } @@ -54,7 +61,7 @@ impl> DevMap { /// /// 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 { + pub fn get(&self, index: u32, flags: u64) -> Result { let data = self.inner.borrow(); check_bounds(data, index)?; let fd = data.fd().as_fd(); @@ -64,11 +71,18 @@ impl> DevMap { call: "bpf_map_lookup_elem", io_error, })?; - value.ok_or(MapError::KeyNotFound) + let value: bpf_devmap_val = value.ok_or(MapError::KeyNotFound)?; + + // SAFETY: map writes use fd, map reads use id. + // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6228 + Ok(DevMapValue { + ifindex: value.ifindex, + prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), + }) } /// An iterator over the elements of the array. - pub fn iter(&self) -> impl Iterator> + '_ { + pub fn iter(&self) -> impl Iterator> + '_ { (0..self.len()).map(move |i| self.get(i, 0)) } } @@ -80,10 +94,26 @@ impl> DevMap { /// /// 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: u32, flags: u64) -> Result<(), MapError> { + pub fn set( + &mut self, + index: u32, + value: u32, + program: Option, + flags: u64, + ) -> Result<(), MapError> { let data = self.inner.borrow_mut(); check_bounds(data, index)?; let fd = data.fd().as_fd(); + + let value = bpf_devmap_val { + ifindex: value, + 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 + // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918 + fd: program.map(|prog| prog.as_raw_fd()).unwrap_or_default(), + }, + }; bpf_map_update_elem(fd, Some(&index), &value, flags).map_err(|(_, io_error)| { SyscallError { call: "bpf_map_update_elem", @@ -94,12 +124,20 @@ impl> DevMap { } } -impl> IterableMap for DevMap { +impl> IterableMap for DevMap { fn map(&self) -> &MapData { self.inner.borrow() } - fn get(&self, key: &u32) -> Result { + fn get(&self, key: &u32) -> Result { self.get(*key, 0) } } + +unsafe impl Pod for bpf_devmap_val {} + +#[derive(Clone, Copy, Debug)] +pub struct DevMapValue { + pub ifindex: u32, + pub prog_id: Option, +} diff --git a/aya/src/maps/xdp/dev_map_hash.rs b/aya/src/maps/xdp/dev_map_hash.rs index 47eed67d..06d53258 100644 --- a/aya/src/maps/xdp/dev_map_hash.rs +++ b/aya/src/maps/xdp/dev_map_hash.rs @@ -1,12 +1,20 @@ //! An hashmap of network devices. -use std::borrow::{Borrow, BorrowMut}; +use std::{ + borrow::{Borrow, BorrowMut}, + num::NonZeroU32, + os::fd::AsRawFd, +}; + +use aya_obj::generated::{bpf_devmap_val, bpf_devmap_val__bindgen_ty_1}; use crate::{ maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys}, sys::{bpf_map_lookup_elem, SyscallError}, }; +use super::dev_map::DevMapValue; + /// An hashmap of network devices. /// /// XDP programs can use this map to redirect to other network @@ -24,7 +32,7 @@ use crate::{ /// let mut devmap = DevMapHash::try_from(bpf.map_mut("IFACES").unwrap())?; /// let flags = 0; /// let ifindex = 32u32; -/// devmap.insert(ifindex, ifindex, flags); +/// devmap.insert(ifindex, ifindex, None::, flags); /// /// # Ok::<(), aya::BpfError>(()) /// ``` @@ -36,7 +44,7 @@ pub struct DevMapHash { impl> DevMapHash { pub(crate) fn new(map: T) -> Result { let data = map.borrow(); - check_kv_size::(data)?; + check_kv_size::(data)?; Ok(Self { inner: map }) } @@ -47,18 +55,25 @@ impl> DevMapHash { /// /// 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 { + pub fn get(&self, index: 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, })?; - value.ok_or(MapError::KeyNotFound) + let value: bpf_devmap_val = value.ok_or(MapError::KeyNotFound)?; + + // SAFETY: map writes use fd, map reads use id. + // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6228 + Ok(DevMapValue { + ifindex: value.ifindex, + prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), + }) } /// An iterator over the elements of the devmap in arbitrary order. - pub fn iter(&self) -> MapIter<'_, u32, u32, Self> { + pub fn iter(&self) -> MapIter<'_, u32, DevMapValue, Self> { MapIter::new(self) } @@ -74,7 +89,22 @@ impl> DevMapHash { /// # Errors /// /// Returns [`MapError::SyscallError`] if `bpf_map_update_elem` fails. - pub fn insert(&mut self, index: u32, value: u32, flags: u64) -> Result<(), MapError> { + pub fn insert( + &mut self, + index: u32, + value: u32, + program: Option, + flags: u64, + ) -> Result<(), MapError> { + let value = bpf_devmap_val { + ifindex: value, + 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 + // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918 + fd: program.map(|prog| prog.as_raw_fd()).unwrap_or_default(), + }, + }; hash_map::insert(self.inner.borrow_mut(), &index, &value, flags) } @@ -88,12 +118,12 @@ impl> DevMapHash { } } -impl> IterableMap for DevMapHash { +impl> IterableMap for DevMapHash { fn map(&self) -> &MapData { self.inner.borrow() } - fn get(&self, key: &u32) -> Result { + fn get(&self, key: &u32) -> Result { self.get(*key, 0) } } diff --git a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs index 3cf970e5..f1d224b3 100644 --- a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs @@ -1,5 +1,7 @@ use core::{cell::UnsafeCell, mem}; +use aya_bpf_bindings::bindings::bpf_cpumap_val; + use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_CPUMAP}, helpers::bpf_redirect_map, @@ -19,7 +21,7 @@ impl CpuMap { def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_CPUMAP, key_size: mem::size_of::() as u32, - value_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, max_entries, map_flags: flags, id: 0, @@ -33,7 +35,7 @@ impl CpuMap { def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_CPUMAP, key_size: mem::size_of::() as u32, - value_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, max_entries, map_flags: flags, id: 0, diff --git a/bpf/aya-bpf/src/maps/xdp/dev_map.rs b/bpf/aya-bpf/src/maps/xdp/dev_map.rs index 968deb69..85064141 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map.rs @@ -1,5 +1,6 @@ use core::{cell::UnsafeCell, mem, ptr::NonNull}; +use aya_bpf_bindings::bindings::bpf_devmap_val; use aya_bpf_cty::c_void; use crate::{ @@ -21,7 +22,7 @@ impl DevMap { def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_DEVMAP, key_size: mem::size_of::() as u32, - value_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, max_entries, map_flags: flags, id: 0, @@ -35,7 +36,7 @@ impl DevMap { def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_DEVMAP, key_size: mem::size_of::() as u32, - value_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, max_entries, map_flags: flags, id: 0, 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 4933146b..6560bfc8 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs @@ -22,7 +22,7 @@ impl DevMapHash { def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_DEVMAP_HASH, key_size: mem::size_of::() as u32, - value_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, max_entries, map_flags: flags, id: 0, @@ -36,7 +36,7 @@ impl DevMapHash { def: UnsafeCell::new(bpf_map_def { type_: BPF_MAP_TYPE_DEVMAP_HASH, key_size: mem::size_of::() as u32, - value_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, max_entries, map_flags: flags, id: 0, diff --git a/test/integration-ebpf/src/redirect.rs b/test/integration-ebpf/src/redirect.rs index 827ea451..5817b418 100644 --- a/test/integration-ebpf/src/redirect.rs +++ b/test/integration-ebpf/src/redirect.rs @@ -4,7 +4,7 @@ use aya_bpf::{ bindings::xdp_action, macros::{map, xdp}, - maps::{CpuMap, DevMap, DevMapHash, XskMap}, + maps::{Array, CpuMap, DevMap, DevMapHash, XskMap}, programs::XdpContext, }; @@ -17,6 +17,13 @@ static DEVS_HASH: DevMapHash = DevMapHash::with_max_entries(1, 0); #[map] static CPUS: CpuMap = CpuMap::with_max_entries(1, 0); +/// Hits of a probe, used to test program chaining through CpuMap/DevMap. +/// The first slot counts how many times the "raw" xdp program got executed, while the second slot +/// counts how many times the map programs got executed. +/// This allows the test harness to assert that a specific step got executed. +#[map] +static mut HITS: Array = Array::with_max_entries(2, 0); + #[xdp] pub fn redirect_sock(_ctx: XdpContext) -> u32 { SOCKS.redirect(0, xdp_action::XDP_ABORTED as u64) @@ -24,19 +31,41 @@ pub fn redirect_sock(_ctx: XdpContext) -> u32 { #[xdp] pub fn redirect_dev(_ctx: XdpContext) -> u32 { + inc_hit(0); DEVS.redirect(0, xdp_action::XDP_ABORTED as u64) } #[xdp] pub fn redirect_dev_hash(_ctx: XdpContext) -> u32 { + inc_hit(0); DEVS_HASH.redirect(10, xdp_action::XDP_ABORTED as u64) } #[xdp] pub fn redirect_cpu(_ctx: XdpContext) -> u32 { + inc_hit(0); CPUS.redirect(0, xdp_action::XDP_ABORTED as u64) } +#[xdp(map = "cpumap")] +pub fn redirect_cpu_chain(_ctx: XdpContext) -> u32 { + inc_hit(1); + xdp_action::XDP_PASS +} + +#[xdp(map = "devmap")] +pub fn redirect_dev_chain(_ctx: XdpContext) -> u32 { + inc_hit(1); + xdp_action::XDP_PASS +} + +#[inline(always)] +fn inc_hit(index: u32) { + if let Some(hit) = unsafe { HITS.get_ptr_mut(index) } { + unsafe { *hit += 1 }; + } +} + #[cfg(not(test))] #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { diff --git a/test/integration-test/src/tests/xdp.rs b/test/integration-test/src/tests/xdp.rs index 566ca5e5..b0c25bff 100644 --- a/test/integration-test/src/tests/xdp.rs +++ b/test/integration-test/src/tests/xdp.rs @@ -1,6 +1,14 @@ -use aya::Bpf; +use std::{net::UdpSocket, os::fd::AsFd, time::Duration}; + +use aya::{ + maps::{Array, CpuMap}, + programs::{Xdp, XdpFlags}, + Bpf, +}; use object::{Object, ObjectSection, ObjectSymbol, SymbolSection}; +use crate::utils::NetNsGuard; + #[test] fn prog_sections() { let obj_file = object::File::parse(crate::XDP_SEC).unwrap(); @@ -46,3 +54,45 @@ fn map_load() { bpf.program("xdp_frags_cpumap").unwrap(); bpf.program("xdp_frags_devmap").unwrap(); } + +#[test] +fn cpumap_chain() { + let _netns = NetNsGuard::new(); + + let mut bpf = Bpf::load(crate::REDIRECT).unwrap(); + + // Load our cpumap and our canary map + let mut cpus: CpuMap<_> = bpf.take_map("CPUS").unwrap().try_into().unwrap(); + let hits: Array<_, u32> = bpf.take_map("HITS").unwrap().try_into().unwrap(); + + let xdp_chain_fd = { + // Load the chained program to run on the target CPU + let xdp: &mut Xdp = bpf + .program_mut("redirect_cpu_chain") + .unwrap() + .try_into() + .unwrap(); + xdp.load().unwrap(); + xdp.fd().unwrap() + }; + cpus.set(0, 2048, Some(xdp_chain_fd.as_fd()), 0).unwrap(); + + // Load the main program + let xdp: &mut Xdp = bpf.program_mut("redirect_cpu").unwrap().try_into().unwrap(); + xdp.load().unwrap(); + xdp.attach("lo", XdpFlags::default()).unwrap(); + + let sock = UdpSocket::bind("127.0.0.1:1777").unwrap(); + sock.set_read_timeout(Some(Duration::from_millis(1))) + .unwrap(); + sock.send_to(b"hello cpumap", "127.0.0.1:1777").unwrap(); + + // Read back the packet to ensure it wenth through the entire network stack, including our two + // probes. + let mut buf = vec![0u8; 1000]; + let n = sock.recv(&mut buf).unwrap(); + + assert_eq!(&buf[..n], b"hello cpumap"); + assert_eq!(hits.get(&0, 0).unwrap(), 1); + assert_eq!(hits.get(&1, 0).unwrap(), 1); +} From f7fbbcd0e5cad297ddc5407e201580f878b4c5ee Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:16:16 +0200 Subject: [PATCH 08/16] aya: fix docstring missing trailing period --- aya/src/maps/mod.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index 2f7c556f..e0d2e0ff 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -237,43 +237,43 @@ fn maybe_warn_rlimit() { /// eBPF map types. #[derive(Debug)] pub enum Map { - /// A [`Array`] map + /// An [`Array`] map. Array(MapData), - /// A [`PerCpuArray`] map + /// A [`PerCpuArray`] map. PerCpuArray(MapData), - /// A [`ProgramArray`] map + /// A [`ProgramArray`] map. ProgramArray(MapData), - /// A [`HashMap`] map + /// A [`HashMap`] map. HashMap(MapData), - /// A [`PerCpuHashMap`] map + /// A [`PerCpuHashMap`] map. PerCpuHashMap(MapData), /// A [`HashMap`] map that uses a LRU eviction policy. LruHashMap(MapData), /// A [`PerCpuHashMap`] map that uses a LRU eviction policy. PerCpuLruHashMap(MapData), - /// A [`PerfEventArray`] map + /// A [`PerfEventArray`] map. PerfEventArray(MapData), - /// A [`SockMap`] map + /// A [`SockMap`] map. SockMap(MapData), - /// A [`SockHash`] map + /// A [`SockHash`] map. SockHash(MapData), - /// A [`BloomFilter`] map + /// A [`BloomFilter`] map. BloomFilter(MapData), - /// A [`LpmTrie`] map + /// A [`LpmTrie`] map. LpmTrie(MapData), - /// A [`Stack`] map + /// A [`Stack`] map. Stack(MapData), - /// A [`StackTraceMap`] map + /// A [`StackTraceMap`] map. StackTraceMap(MapData), - /// A [`Queue`] map + /// A [`Queue`] map. Queue(MapData), - /// A [`CpuMap`] map + /// A [`CpuMap`] map. CpuMap(MapData), - /// A [`DevMap`] map + /// A [`DevMap`] map. DevMap(MapData), - /// A [`DevMapHash`] map + /// A [`DevMapHash`] map. DevMapHash(MapData), - /// A [`XskMap`] map + /// A [`XskMap`] map. XskMap(MapData), /// An unsupported map type Unsupported(MapData), From db496330737c387cb7a8ff49b0228813ccb5bee3 Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:16:18 +0200 Subject: [PATCH 09/16] bpf: make xdp maps functions safe Values in those map are small enough to return copied values instead of reference to values. --- bpf/aya-bpf/src/maps/xdp/dev_map.rs | 4 ++-- bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs | 16 ++++++++-------- bpf/aya-bpf/src/maps/xdp/xsk_map.rs | 15 ++++++++------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/bpf/aya-bpf/src/maps/xdp/dev_map.rs b/bpf/aya-bpf/src/maps/xdp/dev_map.rs index 85064141..ca54da42 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map.rs @@ -46,13 +46,13 @@ impl DevMap { } #[inline(always)] - pub fn get(&self, index: u32) -> Option { + pub fn get(&self, index: u32) -> Option { unsafe { let value = bpf_map_lookup_elem( self.def.get() as *mut _, &index as *const _ as *const c_void, ); - NonNull::new(value as *mut u32).map(|p| *p.as_ref()) + NonNull::new(value as *mut bpf_devmap_val).map(|p| *p.as_ref()) } } 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 6560bfc8..87a61299 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs @@ -46,21 +46,21 @@ impl DevMapHash { } #[inline(always)] - pub unsafe fn get(&self, index: u32) -> Option<&bpf_devmap_val> { - let value = bpf_map_lookup_elem( - self.def.get() as *mut _, - &index as *const _ as *const c_void, - ); - NonNull::new(value as *mut bpf_devmap_val).map(|p| p.as_ref()) + pub fn get(&self, key: u32) -> Option { + unsafe { + let value = + bpf_map_lookup_elem(self.def.get() as *mut _, &key as *const _ as *const c_void); + NonNull::new(value as *mut bpf_devmap_val).map(|p| *p.as_ref()) + } } #[inline(always)] - pub fn redirect(&self, index: u32, flags: u64) -> u32 { + pub fn redirect(&self, key: u32, flags: u64) -> u32 { unsafe { // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags // argument on error. Thus I have no idea why it returns a long (i64) instead of // something saner, hence the unsigned_abs. - bpf_redirect_map(self.def.get() as *mut _, index.into(), flags).unsigned_abs() as u32 + bpf_redirect_map(self.def.get() as *mut _, key.into(), flags).unsigned_abs() as u32 } } } diff --git a/bpf/aya-bpf/src/maps/xdp/xsk_map.rs b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs index 362f258e..a5e12767 100644 --- a/bpf/aya-bpf/src/maps/xdp/xsk_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs @@ -46,13 +46,14 @@ impl XskMap { } #[inline(always)] - pub unsafe fn get(&self, index: u32) -> Option<&bpf_xdp_sock> { - let value = bpf_map_lookup_elem( - self.def.get() as *mut _, - &index as *const _ as *const c_void, - ); - // FIXME: alignment - NonNull::new(value as *mut bpf_xdp_sock).map(|p| p.as_ref()) + pub fn get(&self, index: u32) -> Option { + unsafe { + let value = bpf_map_lookup_elem( + self.def.get() as *mut _, + &index as *const _ as *const c_void, + ); + NonNull::new(value as *mut bpf_xdp_sock).map(|p| p.as_ref().queue_id) + } } #[inline(always)] From 9ed1d3d2811db89dc7314914d92922c54032382c Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:16:22 +0200 Subject: [PATCH 10/16] 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 { From c6754c614ed3aca142bb27fae4e8d488aff72019 Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:16:35 +0200 Subject: [PATCH 11/16] maps/xdp: use ProgramFd instead of impl AsRawFd Not having a generic here allows to pass `None` without specifying the actual type you don't care about. --- aya/src/maps/xdp/cpu_map.rs | 11 +++++++---- aya/src/maps/xdp/dev_map.rs | 11 +++++++---- aya/src/maps/xdp/dev_map_hash.rs | 11 +++++++---- test/integration-test/src/tests/xdp.rs | 4 ++-- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/aya/src/maps/xdp/cpu_map.rs b/aya/src/maps/xdp/cpu_map.rs index 3fb4934f..a3c0a585 100644 --- a/aya/src/maps/xdp/cpu_map.rs +++ b/aya/src/maps/xdp/cpu_map.rs @@ -3,13 +3,14 @@ use std::{ borrow::{Borrow, BorrowMut}, num::NonZeroU32, - os::fd::AsRawFd, + os::fd::{AsFd, AsRawFd}, }; use aya_obj::generated::{bpf_cpumap_val, bpf_cpumap_val__bindgen_ty_1}; use crate::{ maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, + programs::ProgramFd, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, Pod, }; @@ -36,7 +37,7 @@ use crate::{ /// let flags = 0; /// let queue_size = 2048; /// for i in 0u32..8u32 { -/// cpumap.set(i, queue_size, None::, flags); +/// cpumap.set(i, queue_size, None, flags); /// } /// /// # Ok::<(), aya::BpfError>(()) @@ -115,7 +116,7 @@ impl> CpuMap { &mut self, cpu_index: u32, queue_size: u32, - program: Option, + program: Option<&ProgramFd>, flags: u64, ) -> Result<(), MapError> { let data = self.inner.borrow_mut(); @@ -127,7 +128,9 @@ impl> CpuMap { 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(), + fd: program + .map(|prog| prog.as_fd().as_raw_fd()) + .unwrap_or_default(), }, }; bpf_map_update_elem(fd, Some(&cpu_index), &value, flags).map_err(|(_, io_error)| { diff --git a/aya/src/maps/xdp/dev_map.rs b/aya/src/maps/xdp/dev_map.rs index 2a2797e8..e7a487d4 100644 --- a/aya/src/maps/xdp/dev_map.rs +++ b/aya/src/maps/xdp/dev_map.rs @@ -3,13 +3,14 @@ use std::{ borrow::{Borrow, BorrowMut}, num::NonZeroU32, - os::fd::AsRawFd, + os::fd::{AsFd, AsRawFd}, }; use aya_obj::generated::{bpf_devmap_val, bpf_devmap_val__bindgen_ty_1}; use crate::{ maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, + programs::ProgramFd, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, Pod, }; @@ -31,7 +32,7 @@ use crate::{ /// let mut devmap = DevMap::try_from(bpf.map_mut("IFACES").unwrap())?; /// let source = 32u32; /// let dest = 42u32; -/// devmap.set(source, dest, None::, 0); +/// devmap.set(source, dest, None, 0); /// /// # Ok::<(), aya::BpfError>(()) /// ``` @@ -108,7 +109,7 @@ impl> DevMap { &mut self, index: u32, ifindex: u32, - program: Option, + program: Option<&ProgramFd>, flags: u64, ) -> Result<(), MapError> { let data = self.inner.borrow_mut(); @@ -121,7 +122,9 @@ impl> DevMap { // 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#L918 - fd: program.map(|prog| prog.as_raw_fd()).unwrap_or_default(), + fd: program + .map(|prog| prog.as_fd().as_raw_fd()) + .unwrap_or_default(), }, }; bpf_map_update_elem(fd, Some(&index), &value, flags).map_err(|(_, io_error)| { diff --git a/aya/src/maps/xdp/dev_map_hash.rs b/aya/src/maps/xdp/dev_map_hash.rs index 34ad39ac..e5c91dde 100644 --- a/aya/src/maps/xdp/dev_map_hash.rs +++ b/aya/src/maps/xdp/dev_map_hash.rs @@ -3,13 +3,14 @@ use std::{ borrow::{Borrow, BorrowMut}, num::NonZeroU32, - os::fd::AsRawFd, + os::fd::{AsFd, AsRawFd}, }; use aya_obj::generated::{bpf_devmap_val, bpf_devmap_val__bindgen_ty_1}; use crate::{ maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys}, + programs::ProgramFd, sys::{bpf_map_lookup_elem, SyscallError}, }; @@ -32,7 +33,7 @@ use super::dev_map::DevMapValue; /// let mut devmap = DevMapHash::try_from(bpf.map_mut("IFACES").unwrap())?; /// let flags = 0; /// let ifindex = 32u32; -/// devmap.insert(ifindex, ifindex, None::, flags); +/// devmap.insert(ifindex, ifindex, None, flags); /// /// # Ok::<(), aya::BpfError>(()) /// ``` @@ -100,7 +101,7 @@ impl> DevMapHash { &mut self, key: u32, ifindex: u32, - program: Option, + program: Option<&ProgramFd>, flags: u64, ) -> Result<(), MapError> { let value = bpf_devmap_val { @@ -109,7 +110,9 @@ impl> DevMapHash { // 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#L918 - fd: program.map(|prog| prog.as_raw_fd()).unwrap_or_default(), + fd: program + .map(|prog| prog.as_fd().as_raw_fd()) + .unwrap_or_default(), }, }; hash_map::insert(self.inner.borrow_mut(), &key, &value, flags) diff --git a/test/integration-test/src/tests/xdp.rs b/test/integration-test/src/tests/xdp.rs index b0c25bff..88423fe9 100644 --- a/test/integration-test/src/tests/xdp.rs +++ b/test/integration-test/src/tests/xdp.rs @@ -1,4 +1,4 @@ -use std::{net::UdpSocket, os::fd::AsFd, time::Duration}; +use std::{net::UdpSocket, time::Duration}; use aya::{ maps::{Array, CpuMap}, @@ -75,7 +75,7 @@ fn cpumap_chain() { xdp.load().unwrap(); xdp.fd().unwrap() }; - cpus.set(0, 2048, Some(xdp_chain_fd.as_fd()), 0).unwrap(); + cpus.set(0, 2048, Some(xdp_chain_fd), 0).unwrap(); // Load the main program let xdp: &mut Xdp = bpf.program_mut("redirect_cpu").unwrap().try_into().unwrap(); From 63ce2f013a299c26ae01d729a7df47960af1f608 Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:16:40 +0200 Subject: [PATCH 12/16] bpf/devmap: don't expose `bpf_devmap_value` Use our own type that: - is stable as not from bindgen - does not have an union inside --- bpf/aya-bpf/src/maps/xdp/dev_map.rs | 15 +++++++++++++-- bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs | 11 +++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/bpf/aya-bpf/src/maps/xdp/dev_map.rs b/bpf/aya-bpf/src/maps/xdp/dev_map.rs index 66103758..18a76790 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map.rs @@ -104,13 +104,18 @@ impl DevMap { /// // redirect to ifindex /// ``` #[inline(always)] - pub fn get(&self, index: u32) -> Option { + pub fn get(&self, index: u32) -> Option { unsafe { let value = bpf_map_lookup_elem( self.def.get() as *mut _, &index as *const _ as *const c_void, ); - NonNull::new(value as *mut bpf_devmap_val).map(|p| *p.as_ref()) + NonNull::new(value as *mut bpf_devmap_val).map(|p| DevMapValue { + ifindex: p.as_ref().ifindex, + // SAFETY: map writes use fd, map reads use id. + // https://elixir.bootlin.com/linux/v6.2/source/include/uapi/linux/bpf.h#L6136 + prog_id: p.as_ref().bpf_prog.id, + }) } } @@ -142,3 +147,9 @@ impl DevMap { } } } + +#[derive(Clone, Copy)] +pub struct DevMapValue { + pub ifindex: u32, + pub prog_id: u32, +} 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 600aa9df..a6533e76 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,8 @@ use crate::{ maps::PinningType, }; +use super::dev_map::DevMapValue; + /// A map of network devices. /// /// XDP programs can use this map to redirect packets to other network devices. It is similar to @@ -106,11 +108,16 @@ impl DevMapHash { /// // redirect to ifindex /// ``` #[inline(always)] - pub fn get(&self, key: u32) -> Option { + pub fn get(&self, key: u32) -> Option { unsafe { let value = bpf_map_lookup_elem(self.def.get() as *mut _, &key as *const _ as *const c_void); - NonNull::new(value as *mut bpf_devmap_val).map(|p| *p.as_ref()) + NonNull::new(value as *mut bpf_devmap_val).map(|p| DevMapValue { + ifindex: p.as_ref().ifindex, + // SAFETY: map writes use fd, map reads use id. + // https://elixir.bootlin.com/linux/v6.2/source/include/uapi/linux/bpf.h#L6136 + prog_id: p.as_ref().bpf_prog.id, + }) } } From 00dc7a5bd4468b7d86d7f167a49e78d89016e2ac Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:16:43 +0200 Subject: [PATCH 13/16] maps/xdp: make maps work on kernels not supporting ProgIds On startup, the kernel is probed for support of chained program ids for CpuMap, DevMap and DevMapHash, and will patch maps at load time to have the proper size. Then, at runtime, the support is checked and will error out if a program id is passed when the kernel does not support it. --- aya-obj/src/maps.rs | 8 ++++ aya-obj/src/obj.rs | 25 ++++++++++ aya/Cargo.toml | 2 +- aya/src/bpf.rs | 18 ++++++- aya/src/maps/mod.rs | 4 ++ aya/src/maps/xdp/cpu_map.rs | 79 ++++++++++++++++++++----------- aya/src/maps/xdp/dev_map.rs | 81 ++++++++++++++++++++------------ aya/src/maps/xdp/dev_map_hash.rs | 79 +++++++++++++++++++++---------- aya/src/sys/bpf.rs | 47 ++++++++++++++++++ 9 files changed, 258 insertions(+), 85 deletions(-) diff --git a/aya-obj/src/maps.rs b/aya-obj/src/maps.rs index dbffed0c..aede3cfe 100644 --- a/aya-obj/src/maps.rs +++ b/aya-obj/src/maps.rs @@ -176,6 +176,14 @@ impl Map { } } + /// Set the value size in bytes + pub fn set_value_size(&mut self, size: u32) { + match self { + Map::Legacy(m) => m.def.value_size = size, + Map::Btf(m) => m.def.value_size = size, + } + } + /// Returns the max entry number pub fn max_entries(&self) -> u32 { match self { diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index ce599d3c..6909a229 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -48,17 +48,24 @@ pub struct Features { bpf_perf_link: bool, bpf_global_data: bool, bpf_cookie: bool, + cpumap_prog_id: bool, + devmap_prog_id: bool, + devmap_hash_prog_id: bool, btf: Option, } impl Features { #[doc(hidden)] + #[allow(clippy::too_many_arguments)] pub fn new( bpf_name: bool, bpf_probe_read_kernel: bool, bpf_perf_link: bool, bpf_global_data: bool, bpf_cookie: bool, + cpumap_prog_id: bool, + devmap_prog_id: bool, + devmap_hash_prog_id: bool, btf: Option, ) -> Self { Self { @@ -67,6 +74,9 @@ impl Features { bpf_perf_link, bpf_global_data, bpf_cookie, + cpumap_prog_id, + devmap_prog_id, + devmap_hash_prog_id, btf, } } @@ -96,6 +106,21 @@ impl Features { self.bpf_cookie } + /// Returns whether XDP CPU Maps support chained program IDs. + pub fn cpumap_prog_id(&self) -> bool { + self.cpumap_prog_id + } + + /// Returns whether XDP Device Maps support chained program IDs. + pub fn devmap_prog_id(&self) -> bool { + self.devmap_prog_id + } + + /// Returns whether XDP Hash Device Maps support chained program IDs. + pub fn devmap_hash_prog_id(&self) -> bool { + self.devmap_hash_prog_id + } + /// If BTF is supported, returns which BTF features are supported. pub fn btf(&self) -> Option<&BtfFeatures> { self.btf.as_ref() diff --git a/aya/Cargo.toml b/aya/Cargo.toml index 59025b2d..9cdae143 100644 --- a/aya/Cargo.toml +++ b/aya/Cargo.toml @@ -12,6 +12,7 @@ edition = "2021" rust-version = "1.66" [dependencies] +assert_matches = { workspace = true } async-io = { workspace = true, optional = true } aya-obj = { workspace = true, features = ["std"] } bitflags = { workspace = true } @@ -28,7 +29,6 @@ thiserror = { workspace = true } tokio = { workspace = true, features = ["rt"], optional = true } [dev-dependencies] -assert_matches = { workspace = true } tempfile = { workspace = true } [features] diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 0140886d..181d971f 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -40,7 +40,8 @@ use crate::{ is_btf_datasec_supported, is_btf_decl_tag_supported, is_btf_enum64_supported, is_btf_float_supported, is_btf_func_global_supported, is_btf_func_supported, is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported, - is_probe_read_kernel_supported, is_prog_name_supported, retry_with_verifier_logs, + is_probe_read_kernel_supported, is_prog_id_supported, is_prog_name_supported, + retry_with_verifier_logs, }, util::{bytes_of, bytes_of_slice, possible_cpus, POSSIBLE_CPUS}, }; @@ -93,6 +94,9 @@ fn detect_features() -> Features { is_perf_link_supported(), is_bpf_global_data_supported(), is_bpf_cookie_supported(), + is_prog_id_supported(BPF_MAP_TYPE_CPUMAP), + is_prog_id_supported(BPF_MAP_TYPE_DEVMAP), + is_prog_id_supported(BPF_MAP_TYPE_DEVMAP_HASH), btf, ); debug!("BPF Feature Detection: {:#?}", f); @@ -476,6 +480,18 @@ impl<'a> BpfLoader<'a> { } } } + match obj.map_type().try_into() { + Ok(BPF_MAP_TYPE_CPUMAP) => { + obj.set_value_size(if FEATURES.cpumap_prog_id() { 8 } else { 4 }) + } + Ok(BPF_MAP_TYPE_DEVMAP) => { + obj.set_value_size(if FEATURES.devmap_prog_id() { 8 } else { 4 }) + } + Ok(BPF_MAP_TYPE_DEVMAP_HASH) => { + obj.set_value_size(if FEATURES.devmap_hash_prog_id() { 8 } else { 4 }) + } + _ => (), + } let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd()); let mut map = match obj.pinning() { PinningType::None => MapData::create(obj, &name, btf_fd)?, diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index e0d2e0ff..d41600fe 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -181,6 +181,10 @@ pub enum MapError { error: PinError, }, + /// Program IDs are not supported + #[error("program ids are not supported by the current kernel")] + ProgIdNotSupported, + /// Unsupported Map type #[error("Unsupported map type found {map_type}")] Unsupported { diff --git a/aya/src/maps/xdp/cpu_map.rs b/aya/src/maps/xdp/cpu_map.rs index a3c0a585..e6349163 100644 --- a/aya/src/maps/xdp/cpu_map.rs +++ b/aya/src/maps/xdp/cpu_map.rs @@ -12,7 +12,7 @@ use crate::{ maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, programs::ProgramFd, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, - Pod, + Pod, FEATURES, }; /// An array of available CPUs. @@ -50,7 +50,12 @@ pub struct CpuMap { impl> CpuMap { pub(crate) fn new(map: T) -> Result { let data = map.borrow(); - check_kv_size::(data)?; + + if FEATURES.cpumap_prog_id() { + check_kv_size::(data)?; + } else { + check_kv_size::(data)?; + } Ok(Self { inner: map }) } @@ -73,19 +78,29 @@ impl> CpuMap { check_bounds(data, cpu_index)?; let fd = data.fd().as_fd(); - let value = - bpf_map_lookup_elem(fd, &cpu_index, flags).map_err(|(_, io_error)| SyscallError { + let value = if FEATURES.cpumap_prog_id() { + bpf_map_lookup_elem::<_, bpf_cpumap_val>(fd, &cpu_index, flags).map(|value| { + value.map(|value| CpuMapValue { + qsize: value.qsize, + // SAFETY: map writes use fd, map reads use id. + // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6241 + prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), + }) + }) + } else { + bpf_map_lookup_elem::<_, u32>(fd, &cpu_index, flags).map(|value| { + value.map(|qsize| CpuMapValue { + qsize, + prog_id: None, + }) + }) + }; + value + .map_err(|(_, io_error)| SyscallError { call: "bpf_map_lookup_elem", io_error, - })?; - let value: bpf_cpumap_val = value.ok_or(MapError::KeyNotFound)?; - - // SAFETY: map writes use fd, map reads use id. - // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6241 - Ok(CpuMapValue { - qsize: value.qsize, - prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), - }) + })? + .ok_or(MapError::KeyNotFound) } /// An iterator over the elements of the map. @@ -111,7 +126,8 @@ impl> CpuMap { /// # Errors /// /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] - /// if `bpf_map_update_elem` fails. + /// if `bpf_map_update_elem` fails, [`MapError::ProgIdNotSupported`] if the kernel does not + /// support program ids and one is provided. pub fn set( &mut self, cpu_index: u32, @@ -123,21 +139,28 @@ impl> CpuMap { check_bounds(data, cpu_index)?; let fd = data.fd().as_fd(); - let value = bpf_cpumap_val { - 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_fd().as_raw_fd()) - .unwrap_or_default(), - }, - }; - bpf_map_update_elem(fd, Some(&cpu_index), &value, flags).map_err(|(_, io_error)| { - SyscallError { - call: "bpf_map_update_elem", - io_error, + let res = if FEATURES.cpumap_prog_id() { + let value = bpf_cpumap_val { + 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_fd().as_raw_fd()) + .unwrap_or_default(), + }, + }; + bpf_map_update_elem(fd, Some(&cpu_index), &value, flags) + } else { + if program.is_some() { + return Err(MapError::ProgIdNotSupported); } + bpf_map_update_elem(fd, Some(&cpu_index), &queue_size, flags) + }; + + res.map_err(|(_, io_error)| SyscallError { + call: "bpf_map_update_elem", + io_error, })?; Ok(()) } diff --git a/aya/src/maps/xdp/dev_map.rs b/aya/src/maps/xdp/dev_map.rs index e7a487d4..df43461c 100644 --- a/aya/src/maps/xdp/dev_map.rs +++ b/aya/src/maps/xdp/dev_map.rs @@ -12,7 +12,7 @@ use crate::{ maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, programs::ProgramFd, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, - Pod, + Pod, FEATURES, }; /// An array of network devices. @@ -44,7 +44,12 @@ pub struct DevMap { impl> DevMap { pub(crate) fn new(map: T) -> Result { let data = map.borrow(); - check_kv_size::(data)?; + + if FEATURES.devmap_prog_id() { + check_kv_size::(data)?; + } else { + check_kv_size::(data)?; + } Ok(Self { inner: map }) } @@ -67,19 +72,29 @@ impl> DevMap { check_bounds(data, index)?; let fd = data.fd().as_fd(); - let value = - bpf_map_lookup_elem(fd, &index, flags).map_err(|(_, io_error)| SyscallError { + let value = if FEATURES.devmap_prog_id() { + bpf_map_lookup_elem::<_, bpf_devmap_val>(fd, &index, flags).map(|value| { + value.map(|value| DevMapValue { + ifindex: value.ifindex, + // SAFETY: map writes use fd, map reads use id. + // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6228 + prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), + }) + }) + } else { + bpf_map_lookup_elem::<_, u32>(fd, &index, flags).map(|value| { + value.map(|ifindex| DevMapValue { + ifindex, + prog_id: None, + }) + }) + }; + value + .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. - // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6228 - Ok(DevMapValue { - ifindex: value.ifindex, - prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), - }) + })? + .ok_or(MapError::KeyNotFound) } /// An iterator over the elements of the array. @@ -104,7 +119,8 @@ impl> DevMap { /// # Errors /// /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] - /// if `bpf_map_update_elem` fails. + /// if `bpf_map_update_elem` fails, [`MapError::ProgIdNotSupported`] if the kernel does not + /// support program ids and one is provided. pub fn set( &mut self, index: u32, @@ -116,22 +132,29 @@ impl> DevMap { check_bounds(data, index)?; let fd = data.fd().as_fd(); - let value = bpf_devmap_val { - 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 - // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918 - fd: program - .map(|prog| prog.as_fd().as_raw_fd()) - .unwrap_or_default(), - }, - }; - bpf_map_update_elem(fd, Some(&index), &value, flags).map_err(|(_, io_error)| { - SyscallError { - call: "bpf_map_update_elem", - io_error, + let res = if FEATURES.devmap_prog_id() { + let value = bpf_devmap_val { + 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 + // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918 + fd: program + .map(|prog| prog.as_fd().as_raw_fd()) + .unwrap_or_default(), + }, + }; + bpf_map_update_elem(fd, Some(&index), &value, flags) + } else { + if program.is_some() { + return Err(MapError::ProgIdNotSupported); } + bpf_map_update_elem(fd, Some(&index), &ifindex, flags) + }; + + res.map_err(|(_, io_error)| SyscallError { + call: "bpf_map_update_elem", + io_error, })?; Ok(()) } diff --git a/aya/src/maps/xdp/dev_map_hash.rs b/aya/src/maps/xdp/dev_map_hash.rs index e5c91dde..8311a29b 100644 --- a/aya/src/maps/xdp/dev_map_hash.rs +++ b/aya/src/maps/xdp/dev_map_hash.rs @@ -12,6 +12,7 @@ use crate::{ maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys}, programs::ProgramFd, sys::{bpf_map_lookup_elem, SyscallError}, + FEATURES, }; use super::dev_map::DevMapValue; @@ -45,7 +46,12 @@ pub struct DevMapHash { impl> DevMapHash { pub(crate) fn new(map: T) -> Result { let data = map.borrow(); - check_kv_size::(data)?; + + if FEATURES.devmap_hash_prog_id() { + check_kv_size::(data)?; + } else { + check_kv_size::(data)?; + } Ok(Self { inner: map }) } @@ -57,18 +63,30 @@ impl> DevMapHash { /// 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, &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. - // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6228 - Ok(DevMapValue { - ifindex: value.ifindex, - prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), - }) + + let value = if FEATURES.devmap_hash_prog_id() { + bpf_map_lookup_elem::<_, bpf_devmap_val>(fd, &key, flags).map(|value| { + value.map(|value| DevMapValue { + ifindex: value.ifindex, + // SAFETY: map writes use fd, map reads use id. + // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6228 + prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), + }) + }) + } else { + bpf_map_lookup_elem::<_, u32>(fd, &key, flags).map(|value| { + value.map(|ifindex| DevMapValue { + ifindex, + prog_id: None, + }) + }) + }; + value + .map_err(|(_, io_error)| SyscallError { + call: "bpf_map_lookup_elem", + io_error, + })? + .ok_or(MapError::KeyNotFound) } /// An iterator over the elements of the devmap in arbitrary order. @@ -96,7 +114,9 @@ impl> DevMapHash { /// /// # Errors /// - /// Returns [`MapError::SyscallError`] if `bpf_map_update_elem` fails. + /// Returns [`MapError::SyscallError`] if `bpf_map_update_elem` fails, + /// [`MapError::ProgIdNotSupported`] if the kernel does not support program ids and one is + /// provided. pub fn insert( &mut self, key: u32, @@ -104,18 +124,25 @@ impl> DevMapHash { program: Option<&ProgramFd>, flags: u64, ) -> Result<(), MapError> { - let value = bpf_devmap_val { - 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 - // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918 - fd: program - .map(|prog| prog.as_fd().as_raw_fd()) - .unwrap_or_default(), - }, - }; - hash_map::insert(self.inner.borrow_mut(), &key, &value, flags) + if FEATURES.devmap_hash_prog_id() { + let value = bpf_devmap_val { + 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 + // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918 + fd: program + .map(|prog| prog.as_fd().as_raw_fd()) + .unwrap_or_default(), + }, + }; + hash_map::insert(self.inner.borrow_mut(), &key, &value, flags) + } else { + if program.is_some() { + return Err(MapError::ProgIdNotSupported); + } + hash_map::insert(self.inner.borrow_mut(), &key, &ifindex, flags) + } } /// Removes a value from the map. diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index d3cca3ee..cf41b74a 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -8,6 +8,7 @@ use std::{ }; use crate::util::KernelVersion; +use assert_matches::assert_matches; use libc::{c_char, c_long, ENOENT, ENOSPC}; use obj::{ btf::{BtfEnum64, Enum64}, @@ -793,6 +794,28 @@ pub(crate) fn is_bpf_cookie_supported() -> bool { bpf_prog_load(&mut attr).is_ok() } +/// Tests whether CpuMap, DevMap and DevMapHash support program ids +pub(crate) fn is_prog_id_supported(map_type: bpf_map_type) -> bool { + assert_matches!( + map_type, + bpf_map_type::BPF_MAP_TYPE_CPUMAP + | bpf_map_type::BPF_MAP_TYPE_DEVMAP + | bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH + ); + + let mut attr = unsafe { mem::zeroed::() }; + let u = unsafe { &mut attr.__bindgen_anon_1 }; + + u.map_type = map_type as u32; + u.key_size = 4; + u.value_size = 8; // 4 for CPU ID, 8 for CPU ID + prog ID + u.max_entries = 1; + u.map_flags = 0; + + // SAFETY: BPF_MAP_CREATE returns a new file descriptor. + unsafe { fd_sys_bpf(bpf_cmd::BPF_MAP_CREATE, &mut attr) }.is_ok() +} + pub(crate) fn is_btf_supported() -> bool { let mut btf = Btf::new(); let name_offset = btf.add_string("int"); @@ -1072,4 +1095,28 @@ mod tests { let supported = is_perf_link_supported(); assert!(!supported); } + + #[test] + fn test_prog_id_supported() { + override_syscall(|_call| Ok(42)); + + // Ensure that the three map types we can check are accepted + let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_CPUMAP); + assert!(supported); + let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_DEVMAP); + assert!(supported); + let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH); + assert!(supported); + + override_syscall(|_call| Err((-1, io::Error::from_raw_os_error(EINVAL)))); + let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_CPUMAP); + assert!(!supported); + } + + #[test] + #[should_panic = "assertion failed: `BPF_MAP_TYPE_HASH` does not match `bpf_map_type::BPF_MAP_TYPE_CPUMAP | bpf_map_type::BPF_MAP_TYPE_DEVMAP | +bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH`"] + fn test_prog_id_supported_reject_types() { + is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_HASH); + } } From 46551de3e74a25229ce3bddd095f0b40e72fa770 Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Thu, 17 Aug 2023 19:32:21 +0200 Subject: [PATCH 14/16] xtask: bless public-api --- xtask/public-api/aya-bpf.txt | 215 ++++++++++++++++++++ xtask/public-api/aya-obj.txt | 83 ++++++++ xtask/public-api/aya.txt | 380 ++++++++++++++++++++++++++++++++++- 3 files changed, 674 insertions(+), 4 deletions(-) diff --git a/xtask/public-api/aya-bpf.txt b/xtask/public-api/aya-bpf.txt index 4d4aa0f7..b7de5541 100644 --- a/xtask/public-api/aya-bpf.txt +++ b/xtask/public-api/aya-bpf.txt @@ -563,6 +563,114 @@ impl core::borrow::BorrowMut for aya_bpf::maps::stack_trace::StackTrace wh pub fn aya_bpf::maps::stack_trace::StackTrace::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_bpf::maps::stack_trace::StackTrace pub fn aya_bpf::maps::stack_trace::StackTrace::from(t: T) -> T +pub mod aya_bpf::maps::xdp +#[repr(transparent)] pub struct aya_bpf::maps::xdp::CpuMap +impl aya_bpf::maps::CpuMap +pub const fn aya_bpf::maps::CpuMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::CpuMap +pub fn aya_bpf::maps::CpuMap::redirect(&self, index: u32, flags: u64) -> u32 +pub const fn aya_bpf::maps::CpuMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::CpuMap +impl core::marker::Sync for aya_bpf::maps::CpuMap +impl core::marker::Send for aya_bpf::maps::CpuMap +impl core::marker::Unpin for aya_bpf::maps::CpuMap +impl !core::panic::unwind_safe::RefUnwindSafe for aya_bpf::maps::CpuMap +impl core::panic::unwind_safe::UnwindSafe for aya_bpf::maps::CpuMap +impl core::convert::Into for aya_bpf::maps::CpuMap where U: core::convert::From +pub fn aya_bpf::maps::CpuMap::into(self) -> U +impl core::convert::TryFrom for aya_bpf::maps::CpuMap where U: core::convert::Into +pub type aya_bpf::maps::CpuMap::Error = core::convert::Infallible +pub fn aya_bpf::maps::CpuMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_bpf::maps::CpuMap where U: core::convert::TryFrom +pub type aya_bpf::maps::CpuMap::Error = >::Error +pub fn aya_bpf::maps::CpuMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_bpf::maps::CpuMap where T: 'static + core::marker::Sized +pub fn aya_bpf::maps::CpuMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_bpf::maps::CpuMap where T: core::marker::Sized +pub fn aya_bpf::maps::CpuMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_bpf::maps::CpuMap where T: core::marker::Sized +pub fn aya_bpf::maps::CpuMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_bpf::maps::CpuMap +pub fn aya_bpf::maps::CpuMap::from(t: T) -> T +#[repr(transparent)] pub struct aya_bpf::maps::xdp::DevMap +impl aya_bpf::maps::DevMap +pub fn aya_bpf::maps::DevMap::get(&self, index: u32) -> core::option::Option +pub const fn aya_bpf::maps::DevMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMap +pub fn aya_bpf::maps::DevMap::redirect(&self, index: u32, flags: u64) -> u32 +pub const fn aya_bpf::maps::DevMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMap +impl core::marker::Sync for aya_bpf::maps::DevMap +impl core::marker::Send for aya_bpf::maps::DevMap +impl core::marker::Unpin for aya_bpf::maps::DevMap +impl !core::panic::unwind_safe::RefUnwindSafe for aya_bpf::maps::DevMap +impl core::panic::unwind_safe::UnwindSafe for aya_bpf::maps::DevMap +impl core::convert::Into for aya_bpf::maps::DevMap where U: core::convert::From +pub fn aya_bpf::maps::DevMap::into(self) -> U +impl core::convert::TryFrom for aya_bpf::maps::DevMap where U: core::convert::Into +pub type aya_bpf::maps::DevMap::Error = core::convert::Infallible +pub fn aya_bpf::maps::DevMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_bpf::maps::DevMap where U: core::convert::TryFrom +pub type aya_bpf::maps::DevMap::Error = >::Error +pub fn aya_bpf::maps::DevMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_bpf::maps::DevMap where T: 'static + core::marker::Sized +pub fn aya_bpf::maps::DevMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_bpf::maps::DevMap where T: core::marker::Sized +pub fn aya_bpf::maps::DevMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_bpf::maps::DevMap where T: core::marker::Sized +pub fn aya_bpf::maps::DevMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_bpf::maps::DevMap +pub fn aya_bpf::maps::DevMap::from(t: T) -> T +#[repr(transparent)] pub struct aya_bpf::maps::xdp::DevMapHash +impl aya_bpf::maps::DevMapHash +pub fn aya_bpf::maps::DevMapHash::get(&self, key: u32) -> core::option::Option +pub const fn aya_bpf::maps::DevMapHash::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash +pub fn aya_bpf::maps::DevMapHash::redirect(&self, key: u32, flags: u64) -> u32 +pub const fn aya_bpf::maps::DevMapHash::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash +impl core::marker::Sync for aya_bpf::maps::DevMapHash +impl core::marker::Send for aya_bpf::maps::DevMapHash +impl core::marker::Unpin for aya_bpf::maps::DevMapHash +impl !core::panic::unwind_safe::RefUnwindSafe for aya_bpf::maps::DevMapHash +impl core::panic::unwind_safe::UnwindSafe for aya_bpf::maps::DevMapHash +impl core::convert::Into for aya_bpf::maps::DevMapHash where U: core::convert::From +pub fn aya_bpf::maps::DevMapHash::into(self) -> U +impl core::convert::TryFrom for aya_bpf::maps::DevMapHash where U: core::convert::Into +pub type aya_bpf::maps::DevMapHash::Error = core::convert::Infallible +pub fn aya_bpf::maps::DevMapHash::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_bpf::maps::DevMapHash where U: core::convert::TryFrom +pub type aya_bpf::maps::DevMapHash::Error = >::Error +pub fn aya_bpf::maps::DevMapHash::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_bpf::maps::DevMapHash where T: 'static + core::marker::Sized +pub fn aya_bpf::maps::DevMapHash::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_bpf::maps::DevMapHash where T: core::marker::Sized +pub fn aya_bpf::maps::DevMapHash::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_bpf::maps::DevMapHash where T: core::marker::Sized +pub fn aya_bpf::maps::DevMapHash::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_bpf::maps::DevMapHash +pub fn aya_bpf::maps::DevMapHash::from(t: T) -> T +#[repr(transparent)] pub struct aya_bpf::maps::xdp::XskMap +impl aya_bpf::maps::XskMap +pub fn aya_bpf::maps::XskMap::get(&self, index: u32) -> core::option::Option +pub const fn aya_bpf::maps::XskMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::XskMap +pub fn aya_bpf::maps::XskMap::redirect(&self, index: u32, flags: u64) -> u32 +pub const fn aya_bpf::maps::XskMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::XskMap +impl core::marker::Sync for aya_bpf::maps::XskMap +impl core::marker::Send for aya_bpf::maps::XskMap +impl core::marker::Unpin for aya_bpf::maps::XskMap +impl !core::panic::unwind_safe::RefUnwindSafe for aya_bpf::maps::XskMap +impl core::panic::unwind_safe::UnwindSafe for aya_bpf::maps::XskMap +impl core::convert::Into for aya_bpf::maps::XskMap where U: core::convert::From +pub fn aya_bpf::maps::XskMap::into(self) -> U +impl core::convert::TryFrom for aya_bpf::maps::XskMap where U: core::convert::Into +pub type aya_bpf::maps::XskMap::Error = core::convert::Infallible +pub fn aya_bpf::maps::XskMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_bpf::maps::XskMap where U: core::convert::TryFrom +pub type aya_bpf::maps::XskMap::Error = >::Error +pub fn aya_bpf::maps::XskMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_bpf::maps::XskMap where T: 'static + core::marker::Sized +pub fn aya_bpf::maps::XskMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_bpf::maps::XskMap where T: core::marker::Sized +pub fn aya_bpf::maps::XskMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_bpf::maps::XskMap where T: core::marker::Sized +pub fn aya_bpf::maps::XskMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_bpf::maps::XskMap +pub fn aya_bpf::maps::XskMap::from(t: T) -> T #[repr(transparent)] pub struct aya_bpf::maps::Array impl aya_bpf::maps::array::Array pub fn aya_bpf::maps::array::Array::get(&self, index: u32) -> core::option::Option<&T> @@ -618,6 +726,86 @@ impl core::borrow::BorrowMut for aya_bpf::maps::bloom_filter::BloomFilter< pub fn aya_bpf::maps::bloom_filter::BloomFilter::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_bpf::maps::bloom_filter::BloomFilter pub fn aya_bpf::maps::bloom_filter::BloomFilter::from(t: T) -> T +#[repr(transparent)] pub struct aya_bpf::maps::CpuMap +impl aya_bpf::maps::CpuMap +pub const fn aya_bpf::maps::CpuMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::CpuMap +pub fn aya_bpf::maps::CpuMap::redirect(&self, index: u32, flags: u64) -> u32 +pub const fn aya_bpf::maps::CpuMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::CpuMap +impl core::marker::Sync for aya_bpf::maps::CpuMap +impl core::marker::Send for aya_bpf::maps::CpuMap +impl core::marker::Unpin for aya_bpf::maps::CpuMap +impl !core::panic::unwind_safe::RefUnwindSafe for aya_bpf::maps::CpuMap +impl core::panic::unwind_safe::UnwindSafe for aya_bpf::maps::CpuMap +impl core::convert::Into for aya_bpf::maps::CpuMap where U: core::convert::From +pub fn aya_bpf::maps::CpuMap::into(self) -> U +impl core::convert::TryFrom for aya_bpf::maps::CpuMap where U: core::convert::Into +pub type aya_bpf::maps::CpuMap::Error = core::convert::Infallible +pub fn aya_bpf::maps::CpuMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_bpf::maps::CpuMap where U: core::convert::TryFrom +pub type aya_bpf::maps::CpuMap::Error = >::Error +pub fn aya_bpf::maps::CpuMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_bpf::maps::CpuMap where T: 'static + core::marker::Sized +pub fn aya_bpf::maps::CpuMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_bpf::maps::CpuMap where T: core::marker::Sized +pub fn aya_bpf::maps::CpuMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_bpf::maps::CpuMap where T: core::marker::Sized +pub fn aya_bpf::maps::CpuMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_bpf::maps::CpuMap +pub fn aya_bpf::maps::CpuMap::from(t: T) -> T +#[repr(transparent)] pub struct aya_bpf::maps::DevMap +impl aya_bpf::maps::DevMap +pub fn aya_bpf::maps::DevMap::get(&self, index: u32) -> core::option::Option +pub const fn aya_bpf::maps::DevMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMap +pub fn aya_bpf::maps::DevMap::redirect(&self, index: u32, flags: u64) -> u32 +pub const fn aya_bpf::maps::DevMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMap +impl core::marker::Sync for aya_bpf::maps::DevMap +impl core::marker::Send for aya_bpf::maps::DevMap +impl core::marker::Unpin for aya_bpf::maps::DevMap +impl !core::panic::unwind_safe::RefUnwindSafe for aya_bpf::maps::DevMap +impl core::panic::unwind_safe::UnwindSafe for aya_bpf::maps::DevMap +impl core::convert::Into for aya_bpf::maps::DevMap where U: core::convert::From +pub fn aya_bpf::maps::DevMap::into(self) -> U +impl core::convert::TryFrom for aya_bpf::maps::DevMap where U: core::convert::Into +pub type aya_bpf::maps::DevMap::Error = core::convert::Infallible +pub fn aya_bpf::maps::DevMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_bpf::maps::DevMap where U: core::convert::TryFrom +pub type aya_bpf::maps::DevMap::Error = >::Error +pub fn aya_bpf::maps::DevMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_bpf::maps::DevMap where T: 'static + core::marker::Sized +pub fn aya_bpf::maps::DevMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_bpf::maps::DevMap where T: core::marker::Sized +pub fn aya_bpf::maps::DevMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_bpf::maps::DevMap where T: core::marker::Sized +pub fn aya_bpf::maps::DevMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_bpf::maps::DevMap +pub fn aya_bpf::maps::DevMap::from(t: T) -> T +#[repr(transparent)] pub struct aya_bpf::maps::DevMapHash +impl aya_bpf::maps::DevMapHash +pub fn aya_bpf::maps::DevMapHash::get(&self, key: u32) -> core::option::Option +pub const fn aya_bpf::maps::DevMapHash::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash +pub fn aya_bpf::maps::DevMapHash::redirect(&self, key: u32, flags: u64) -> u32 +pub const fn aya_bpf::maps::DevMapHash::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash +impl core::marker::Sync for aya_bpf::maps::DevMapHash +impl core::marker::Send for aya_bpf::maps::DevMapHash +impl core::marker::Unpin for aya_bpf::maps::DevMapHash +impl !core::panic::unwind_safe::RefUnwindSafe for aya_bpf::maps::DevMapHash +impl core::panic::unwind_safe::UnwindSafe for aya_bpf::maps::DevMapHash +impl core::convert::Into for aya_bpf::maps::DevMapHash where U: core::convert::From +pub fn aya_bpf::maps::DevMapHash::into(self) -> U +impl core::convert::TryFrom for aya_bpf::maps::DevMapHash where U: core::convert::Into +pub type aya_bpf::maps::DevMapHash::Error = core::convert::Infallible +pub fn aya_bpf::maps::DevMapHash::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_bpf::maps::DevMapHash where U: core::convert::TryFrom +pub type aya_bpf::maps::DevMapHash::Error = >::Error +pub fn aya_bpf::maps::DevMapHash::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_bpf::maps::DevMapHash where T: 'static + core::marker::Sized +pub fn aya_bpf::maps::DevMapHash::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_bpf::maps::DevMapHash where T: core::marker::Sized +pub fn aya_bpf::maps::DevMapHash::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_bpf::maps::DevMapHash where T: core::marker::Sized +pub fn aya_bpf::maps::DevMapHash::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_bpf::maps::DevMapHash +pub fn aya_bpf::maps::DevMapHash::from(t: T) -> T #[repr(transparent)] pub struct aya_bpf::maps::HashMap impl aya_bpf::maps::hash_map::HashMap pub unsafe fn aya_bpf::maps::hash_map::HashMap::get(&self, key: &K) -> core::option::Option<&V> @@ -1014,6 +1202,33 @@ impl core::borrow::BorrowMut for aya_bpf::maps::stack_trace::StackTrace wh pub fn aya_bpf::maps::stack_trace::StackTrace::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_bpf::maps::stack_trace::StackTrace pub fn aya_bpf::maps::stack_trace::StackTrace::from(t: T) -> T +#[repr(transparent)] pub struct aya_bpf::maps::XskMap +impl aya_bpf::maps::XskMap +pub fn aya_bpf::maps::XskMap::get(&self, index: u32) -> core::option::Option +pub const fn aya_bpf::maps::XskMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::XskMap +pub fn aya_bpf::maps::XskMap::redirect(&self, index: u32, flags: u64) -> u32 +pub const fn aya_bpf::maps::XskMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::XskMap +impl core::marker::Sync for aya_bpf::maps::XskMap +impl core::marker::Send for aya_bpf::maps::XskMap +impl core::marker::Unpin for aya_bpf::maps::XskMap +impl !core::panic::unwind_safe::RefUnwindSafe for aya_bpf::maps::XskMap +impl core::panic::unwind_safe::UnwindSafe for aya_bpf::maps::XskMap +impl core::convert::Into for aya_bpf::maps::XskMap where U: core::convert::From +pub fn aya_bpf::maps::XskMap::into(self) -> U +impl core::convert::TryFrom for aya_bpf::maps::XskMap where U: core::convert::Into +pub type aya_bpf::maps::XskMap::Error = core::convert::Infallible +pub fn aya_bpf::maps::XskMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_bpf::maps::XskMap where U: core::convert::TryFrom +pub type aya_bpf::maps::XskMap::Error = >::Error +pub fn aya_bpf::maps::XskMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_bpf::maps::XskMap where T: 'static + core::marker::Sized +pub fn aya_bpf::maps::XskMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_bpf::maps::XskMap where T: core::marker::Sized +pub fn aya_bpf::maps::XskMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_bpf::maps::XskMap where T: core::marker::Sized +pub fn aya_bpf::maps::XskMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_bpf::maps::XskMap +pub fn aya_bpf::maps::XskMap::from(t: T) -> T pub mod aya_bpf::programs pub mod aya_bpf::programs::device pub struct aya_bpf::programs::device::DeviceContext diff --git a/xtask/public-api/aya-obj.txt b/xtask/public-api/aya-obj.txt index c6dea1ba..5c361372 100644 --- a/xtask/public-api/aya-obj.txt +++ b/xtask/public-api/aya-obj.txt @@ -1342,6 +1342,8 @@ impl core::convert::From aya_obj::generated::bpf_attach_type impl core::convert::From for aya_obj::generated::bpf_attach_type pub fn aya_obj::generated::bpf_attach_type::from(s: aya_obj::programs::cgroup_sockopt::CgroupSockoptAttachType) -> aya_obj::generated::bpf_attach_type +impl core::convert::From for aya_obj::generated::bpf_attach_type +pub fn aya_obj::generated::bpf_attach_type::from(value: aya_obj::programs::xdp::XdpAttachType) -> Self impl core::clone::Clone for aya_obj::generated::bpf_attach_type pub fn aya_obj::generated::bpf_attach_type::clone(&self) -> aya_obj::generated::bpf_attach_type impl core::cmp::Eq for aya_obj::generated::bpf_attach_type @@ -5411,6 +5413,7 @@ pub fn aya_obj::maps::Map::pinning(&self) -> aya_obj::maps::PinningType pub fn aya_obj::maps::Map::section_index(&self) -> usize pub fn aya_obj::maps::Map::section_kind(&self) -> aya_obj::BpfSectionKind pub fn aya_obj::maps::Map::set_max_entries(&mut self, v: u32) +pub fn aya_obj::maps::Map::set_value_size(&mut self, size: u32) pub fn aya_obj::maps::Map::symbol_index(&self) -> core::option::Option pub fn aya_obj::maps::Map::value_size(&self) -> u32 impl core::clone::Clone for aya_obj::maps::Map @@ -5848,6 +5851,7 @@ pub aya_obj::obj::ProgramSection::UProbe::sleepable: bool pub aya_obj::obj::ProgramSection::URetProbe pub aya_obj::obj::ProgramSection::URetProbe::sleepable: bool pub aya_obj::obj::ProgramSection::Xdp +pub aya_obj::obj::ProgramSection::Xdp::attach_type: aya_obj::programs::xdp::XdpAttachType pub aya_obj::obj::ProgramSection::Xdp::frags: bool impl core::str::traits::FromStr for aya_obj::ProgramSection pub type aya_obj::ProgramSection::Err = aya_obj::ParseError @@ -5889,6 +5893,8 @@ pub fn aya_obj::Features::bpf_name(&self) -> bool pub fn aya_obj::Features::bpf_perf_link(&self) -> bool pub fn aya_obj::Features::bpf_probe_read_kernel(&self) -> bool pub fn aya_obj::Features::btf(&self) -> core::option::Option<&aya_obj::btf::BtfFeatures> +pub fn aya_obj::Features::cpumap_prog_id(&self) -> bool +pub fn aya_obj::Features::devmap_prog_id(&self) -> bool impl core::default::Default for aya_obj::Features pub fn aya_obj::Features::default() -> aya_obj::Features impl core::fmt::Debug for aya_obj::Features @@ -6164,6 +6170,43 @@ impl core::borrow::BorrowMut for aya_obj::programs::cgroup_sockopt::Cgroup pub fn aya_obj::programs::cgroup_sockopt::CgroupSockoptAttachType::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_obj::programs::cgroup_sockopt::CgroupSockoptAttachType pub fn aya_obj::programs::cgroup_sockopt::CgroupSockoptAttachType::from(t: T) -> T +pub mod aya_obj::programs::xdp +pub enum aya_obj::programs::xdp::XdpAttachType +pub aya_obj::programs::xdp::XdpAttachType::CpuMap +pub aya_obj::programs::xdp::XdpAttachType::DevMap +pub aya_obj::programs::xdp::XdpAttachType::Interface +impl core::convert::From for aya_obj::generated::bpf_attach_type +pub fn aya_obj::generated::bpf_attach_type::from(value: aya_obj::programs::xdp::XdpAttachType) -> Self +impl core::clone::Clone for aya_obj::programs::xdp::XdpAttachType +pub fn aya_obj::programs::xdp::XdpAttachType::clone(&self) -> aya_obj::programs::xdp::XdpAttachType +impl core::fmt::Debug for aya_obj::programs::xdp::XdpAttachType +pub fn aya_obj::programs::xdp::XdpAttachType::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for aya_obj::programs::xdp::XdpAttachType +impl core::marker::Send for aya_obj::programs::xdp::XdpAttachType +impl core::marker::Sync for aya_obj::programs::xdp::XdpAttachType +impl core::marker::Unpin for aya_obj::programs::xdp::XdpAttachType +impl core::panic::unwind_safe::RefUnwindSafe for aya_obj::programs::xdp::XdpAttachType +impl core::panic::unwind_safe::UnwindSafe for aya_obj::programs::xdp::XdpAttachType +impl core::convert::Into for aya_obj::programs::xdp::XdpAttachType where U: core::convert::From +pub fn aya_obj::programs::xdp::XdpAttachType::into(self) -> U +impl core::convert::TryFrom for aya_obj::programs::xdp::XdpAttachType where U: core::convert::Into +pub type aya_obj::programs::xdp::XdpAttachType::Error = core::convert::Infallible +pub fn aya_obj::programs::xdp::XdpAttachType::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_obj::programs::xdp::XdpAttachType where U: core::convert::TryFrom +pub type aya_obj::programs::xdp::XdpAttachType::Error = >::Error +pub fn aya_obj::programs::xdp::XdpAttachType::try_into(self) -> core::result::Result>::Error> +impl alloc::borrow::ToOwned for aya_obj::programs::xdp::XdpAttachType where T: core::clone::Clone +pub type aya_obj::programs::xdp::XdpAttachType::Owned = T +pub fn aya_obj::programs::xdp::XdpAttachType::clone_into(&self, target: &mut T) +pub fn aya_obj::programs::xdp::XdpAttachType::to_owned(&self) -> T +impl core::any::Any for aya_obj::programs::xdp::XdpAttachType where T: 'static + core::marker::Sized +pub fn aya_obj::programs::xdp::XdpAttachType::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_obj::programs::xdp::XdpAttachType where T: core::marker::Sized +pub fn aya_obj::programs::xdp::XdpAttachType::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_obj::programs::xdp::XdpAttachType where T: core::marker::Sized +pub fn aya_obj::programs::xdp::XdpAttachType::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_obj::programs::xdp::XdpAttachType +pub fn aya_obj::programs::xdp::XdpAttachType::from(t: T) -> T pub enum aya_obj::programs::CgroupSockAddrAttachType pub aya_obj::programs::CgroupSockAddrAttachType::Bind4 pub aya_obj::programs::CgroupSockAddrAttachType::Bind6 @@ -6283,6 +6326,42 @@ impl core::borrow::BorrowMut for aya_obj::programs::cgroup_sockopt::Cgroup pub fn aya_obj::programs::cgroup_sockopt::CgroupSockoptAttachType::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_obj::programs::cgroup_sockopt::CgroupSockoptAttachType pub fn aya_obj::programs::cgroup_sockopt::CgroupSockoptAttachType::from(t: T) -> T +pub enum aya_obj::programs::XdpAttachType +pub aya_obj::programs::XdpAttachType::CpuMap +pub aya_obj::programs::XdpAttachType::DevMap +pub aya_obj::programs::XdpAttachType::Interface +impl core::convert::From for aya_obj::generated::bpf_attach_type +pub fn aya_obj::generated::bpf_attach_type::from(value: aya_obj::programs::xdp::XdpAttachType) -> Self +impl core::clone::Clone for aya_obj::programs::xdp::XdpAttachType +pub fn aya_obj::programs::xdp::XdpAttachType::clone(&self) -> aya_obj::programs::xdp::XdpAttachType +impl core::fmt::Debug for aya_obj::programs::xdp::XdpAttachType +pub fn aya_obj::programs::xdp::XdpAttachType::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for aya_obj::programs::xdp::XdpAttachType +impl core::marker::Send for aya_obj::programs::xdp::XdpAttachType +impl core::marker::Sync for aya_obj::programs::xdp::XdpAttachType +impl core::marker::Unpin for aya_obj::programs::xdp::XdpAttachType +impl core::panic::unwind_safe::RefUnwindSafe for aya_obj::programs::xdp::XdpAttachType +impl core::panic::unwind_safe::UnwindSafe for aya_obj::programs::xdp::XdpAttachType +impl core::convert::Into for aya_obj::programs::xdp::XdpAttachType where U: core::convert::From +pub fn aya_obj::programs::xdp::XdpAttachType::into(self) -> U +impl core::convert::TryFrom for aya_obj::programs::xdp::XdpAttachType where U: core::convert::Into +pub type aya_obj::programs::xdp::XdpAttachType::Error = core::convert::Infallible +pub fn aya_obj::programs::xdp::XdpAttachType::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_obj::programs::xdp::XdpAttachType where U: core::convert::TryFrom +pub type aya_obj::programs::xdp::XdpAttachType::Error = >::Error +pub fn aya_obj::programs::xdp::XdpAttachType::try_into(self) -> core::result::Result>::Error> +impl alloc::borrow::ToOwned for aya_obj::programs::xdp::XdpAttachType where T: core::clone::Clone +pub type aya_obj::programs::xdp::XdpAttachType::Owned = T +pub fn aya_obj::programs::xdp::XdpAttachType::clone_into(&self, target: &mut T) +pub fn aya_obj::programs::xdp::XdpAttachType::to_owned(&self) -> T +impl core::any::Any for aya_obj::programs::xdp::XdpAttachType where T: 'static + core::marker::Sized +pub fn aya_obj::programs::xdp::XdpAttachType::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_obj::programs::xdp::XdpAttachType where T: core::marker::Sized +pub fn aya_obj::programs::xdp::XdpAttachType::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_obj::programs::xdp::XdpAttachType where T: core::marker::Sized +pub fn aya_obj::programs::xdp::XdpAttachType::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_obj::programs::xdp::XdpAttachType +pub fn aya_obj::programs::xdp::XdpAttachType::from(t: T) -> T pub mod aya_obj::relocation pub enum aya_obj::relocation::RelocationError pub aya_obj::relocation::RelocationError::InvalidRelocationOffset @@ -6420,6 +6499,7 @@ pub fn aya_obj::maps::Map::pinning(&self) -> aya_obj::maps::PinningType pub fn aya_obj::maps::Map::section_index(&self) -> usize pub fn aya_obj::maps::Map::section_kind(&self) -> aya_obj::BpfSectionKind pub fn aya_obj::maps::Map::set_max_entries(&mut self, v: u32) +pub fn aya_obj::maps::Map::set_value_size(&mut self, size: u32) pub fn aya_obj::maps::Map::symbol_index(&self) -> core::option::Option pub fn aya_obj::maps::Map::value_size(&self) -> u32 impl core::clone::Clone for aya_obj::maps::Map @@ -6560,6 +6640,7 @@ pub aya_obj::ProgramSection::UProbe::sleepable: bool pub aya_obj::ProgramSection::URetProbe pub aya_obj::ProgramSection::URetProbe::sleepable: bool pub aya_obj::ProgramSection::Xdp +pub aya_obj::ProgramSection::Xdp::attach_type: aya_obj::programs::xdp::XdpAttachType pub aya_obj::ProgramSection::Xdp::frags: bool impl core::str::traits::FromStr for aya_obj::ProgramSection pub type aya_obj::ProgramSection::Err = aya_obj::ParseError @@ -6601,6 +6682,8 @@ pub fn aya_obj::Features::bpf_name(&self) -> bool pub fn aya_obj::Features::bpf_perf_link(&self) -> bool pub fn aya_obj::Features::bpf_probe_read_kernel(&self) -> bool pub fn aya_obj::Features::btf(&self) -> core::option::Option<&aya_obj::btf::BtfFeatures> +pub fn aya_obj::Features::cpumap_prog_id(&self) -> bool +pub fn aya_obj::Features::devmap_prog_id(&self) -> bool impl core::default::Default for aya_obj::Features pub fn aya_obj::Features::default() -> aya_obj::Features impl core::fmt::Debug for aya_obj::Features diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index 8c1b0b9c..86221498 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -795,9 +795,194 @@ impl core::borrow::BorrowMut for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::from(t: T) -> T +pub mod aya::maps::xdp +pub enum aya::maps::xdp::XdpMapError +pub aya::maps::xdp::XdpMapError::ChainedProgramNotSupported +pub aya::maps::xdp::XdpMapError::MapError(aya::maps::MapError) +impl core::convert::From for aya::maps::xdp::XdpMapError +pub fn aya::maps::xdp::XdpMapError::from(source: aya::maps::MapError) -> Self +impl core::error::Error for aya::maps::xdp::XdpMapError +pub fn aya::maps::xdp::XdpMapError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Display for aya::maps::xdp::XdpMapError +pub fn aya::maps::xdp::XdpMapError::fmt(&self, __formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::fmt::Debug for aya::maps::xdp::XdpMapError +pub fn aya::maps::xdp::XdpMapError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Send for aya::maps::xdp::XdpMapError +impl core::marker::Sync for aya::maps::xdp::XdpMapError +impl core::marker::Unpin for aya::maps::xdp::XdpMapError +impl !core::panic::unwind_safe::RefUnwindSafe for aya::maps::xdp::XdpMapError +impl !core::panic::unwind_safe::UnwindSafe for aya::maps::xdp::XdpMapError +impl core::convert::Into for aya::maps::xdp::XdpMapError where U: core::convert::From +pub fn aya::maps::xdp::XdpMapError::into(self) -> U +impl core::convert::TryFrom for aya::maps::xdp::XdpMapError where U: core::convert::Into +pub type aya::maps::xdp::XdpMapError::Error = core::convert::Infallible +pub fn aya::maps::xdp::XdpMapError::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::xdp::XdpMapError where U: core::convert::TryFrom +pub type aya::maps::xdp::XdpMapError::Error = >::Error +pub fn aya::maps::xdp::XdpMapError::try_into(self) -> core::result::Result>::Error> +impl alloc::string::ToString for aya::maps::xdp::XdpMapError where T: core::fmt::Display + core::marker::Sized +pub fn aya::maps::xdp::XdpMapError::to_string(&self) -> alloc::string::String +impl core::any::Any for aya::maps::xdp::XdpMapError where T: 'static + core::marker::Sized +pub fn aya::maps::xdp::XdpMapError::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::xdp::XdpMapError where T: core::marker::Sized +pub fn aya::maps::xdp::XdpMapError::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::xdp::XdpMapError where T: core::marker::Sized +pub fn aya::maps::xdp::XdpMapError::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::xdp::XdpMapError +pub fn aya::maps::xdp::XdpMapError::from(t: T) -> T +pub struct aya::maps::xdp::CpuMap +impl> aya::maps::CpuMap +pub fn aya::maps::CpuMap::get(&self, cpu_index: u32, flags: u64) -> core::result::Result +pub fn aya::maps::CpuMap::iter(&self) -> impl core::iter::traits::iterator::Iterator> + '_ +pub fn aya::maps::CpuMap::len(&self) -> u32 +impl> aya::maps::CpuMap +pub fn aya::maps::CpuMap::set(&mut self, cpu_index: u32, queue_size: u32, program: core::option::Option<&aya::programs::ProgramFd>, flags: u64) -> core::result::Result<(), aya::maps::xdp::XdpMapError> +impl core::convert::TryFrom for aya::maps::CpuMap +pub type aya::maps::CpuMap::Error = aya::maps::MapError +pub fn aya::maps::CpuMap::try_from(map: aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::CpuMap<&'a aya::maps::MapData> +pub type aya::maps::CpuMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::CpuMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::CpuMap<&'a mut aya::maps::MapData> +pub type aya::maps::CpuMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::CpuMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::marker::Send for aya::maps::CpuMap where T: core::marker::Send +impl core::marker::Sync for aya::maps::CpuMap where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::CpuMap where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::CpuMap where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::CpuMap where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::CpuMap where U: core::convert::From +pub fn aya::maps::CpuMap::into(self) -> U +impl core::convert::TryFrom for aya::maps::CpuMap where U: core::convert::Into +pub type aya::maps::CpuMap::Error = core::convert::Infallible +pub fn aya::maps::CpuMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::CpuMap where U: core::convert::TryFrom +pub type aya::maps::CpuMap::Error = >::Error +pub fn aya::maps::CpuMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::CpuMap where T: 'static + core::marker::Sized +pub fn aya::maps::CpuMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::CpuMap where T: core::marker::Sized +pub fn aya::maps::CpuMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::CpuMap where T: core::marker::Sized +pub fn aya::maps::CpuMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::CpuMap +pub fn aya::maps::CpuMap::from(t: T) -> T +pub struct aya::maps::xdp::DevMap +impl> aya::maps::DevMap +pub fn aya::maps::DevMap::get(&self, index: u32, flags: u64) -> core::result::Result +pub fn aya::maps::DevMap::iter(&self) -> impl core::iter::traits::iterator::Iterator> + '_ +pub fn aya::maps::DevMap::len(&self) -> u32 +impl> aya::maps::DevMap +pub fn aya::maps::DevMap::set(&mut self, index: u32, target_if_index: u32, program: core::option::Option<&aya::programs::ProgramFd>, flags: u64) -> core::result::Result<(), aya::maps::xdp::XdpMapError> +impl core::convert::TryFrom for aya::maps::DevMap +pub type aya::maps::DevMap::Error = aya::maps::MapError +pub fn aya::maps::DevMap::try_from(map: aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::DevMap<&'a aya::maps::MapData> +pub type aya::maps::DevMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::DevMap<&'a mut aya::maps::MapData> +pub type aya::maps::DevMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::marker::Send for aya::maps::DevMap where T: core::marker::Send +impl core::marker::Sync for aya::maps::DevMap where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::DevMap where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::DevMap where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::DevMap where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::DevMap where U: core::convert::From +pub fn aya::maps::DevMap::into(self) -> U +impl core::convert::TryFrom for aya::maps::DevMap where U: core::convert::Into +pub type aya::maps::DevMap::Error = core::convert::Infallible +pub fn aya::maps::DevMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::DevMap where U: core::convert::TryFrom +pub type aya::maps::DevMap::Error = >::Error +pub fn aya::maps::DevMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::DevMap where T: 'static + core::marker::Sized +pub fn aya::maps::DevMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::DevMap where T: core::marker::Sized +pub fn aya::maps::DevMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::DevMap where T: core::marker::Sized +pub fn aya::maps::DevMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::DevMap +pub fn aya::maps::DevMap::from(t: T) -> T +pub struct aya::maps::xdp::DevMapHash +impl> aya::maps::DevMapHash +pub fn aya::maps::DevMapHash::get(&self, key: u32, flags: u64) -> core::result::Result +pub fn aya::maps::DevMapHash::iter(&self) -> aya::maps::MapIter<'_, u32, DevMapValue, Self> +pub fn aya::maps::DevMapHash::keys(&self) -> aya::maps::MapKeys<'_, u32> +impl> aya::maps::DevMapHash +pub fn aya::maps::DevMapHash::insert(&mut self, key: u32, target_if_index: u32, program: core::option::Option<&aya::programs::ProgramFd>, flags: u64) -> core::result::Result<(), aya::maps::xdp::XdpMapError> +pub fn aya::maps::DevMapHash::remove(&mut self, key: u32) -> core::result::Result<(), aya::maps::MapError> +impl core::convert::TryFrom for aya::maps::DevMapHash +pub type aya::maps::DevMapHash::Error = aya::maps::MapError +pub fn aya::maps::DevMapHash::try_from(map: aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::DevMapHash<&'a aya::maps::MapData> +pub type aya::maps::DevMapHash<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMapHash<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::DevMapHash<&'a mut aya::maps::MapData> +pub type aya::maps::DevMapHash<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMapHash<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::marker::Send for aya::maps::DevMapHash where T: core::marker::Send +impl core::marker::Sync for aya::maps::DevMapHash where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::DevMapHash where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::DevMapHash where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::DevMapHash where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::DevMapHash where U: core::convert::From +pub fn aya::maps::DevMapHash::into(self) -> U +impl core::convert::TryFrom for aya::maps::DevMapHash where U: core::convert::Into +pub type aya::maps::DevMapHash::Error = core::convert::Infallible +pub fn aya::maps::DevMapHash::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::DevMapHash where U: core::convert::TryFrom +pub type aya::maps::DevMapHash::Error = >::Error +pub fn aya::maps::DevMapHash::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::DevMapHash where T: 'static + core::marker::Sized +pub fn aya::maps::DevMapHash::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::DevMapHash where T: core::marker::Sized +pub fn aya::maps::DevMapHash::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::DevMapHash where T: core::marker::Sized +pub fn aya::maps::DevMapHash::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::DevMapHash +pub fn aya::maps::DevMapHash::from(t: T) -> T +pub struct aya::maps::xdp::XskMap +impl> aya::maps::XskMap +pub fn aya::maps::XskMap::len(&self) -> u32 +impl> aya::maps::XskMap +pub fn aya::maps::XskMap::set(&mut self, index: u32, socket_fd: impl std::os::fd::raw::AsRawFd, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl core::convert::TryFrom for aya::maps::XskMap +pub type aya::maps::XskMap::Error = aya::maps::MapError +pub fn aya::maps::XskMap::try_from(map: aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::XskMap<&'a aya::maps::MapData> +pub type aya::maps::XskMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::XskMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::XskMap<&'a mut aya::maps::MapData> +pub type aya::maps::XskMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::XskMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::marker::Send for aya::maps::XskMap where T: core::marker::Send +impl core::marker::Sync for aya::maps::XskMap where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::XskMap where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::XskMap where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::XskMap where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::XskMap where U: core::convert::From +pub fn aya::maps::XskMap::into(self) -> U +impl core::convert::TryFrom for aya::maps::XskMap where U: core::convert::Into +pub type aya::maps::XskMap::Error = core::convert::Infallible +pub fn aya::maps::XskMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::XskMap where U: core::convert::TryFrom +pub type aya::maps::XskMap::Error = >::Error +pub fn aya::maps::XskMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::XskMap where T: 'static + core::marker::Sized +pub fn aya::maps::XskMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::XskMap where T: core::marker::Sized +pub fn aya::maps::XskMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::XskMap where T: core::marker::Sized +pub fn aya::maps::XskMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::XskMap +pub fn aya::maps::XskMap::from(t: T) -> T pub enum aya::maps::Map pub aya::maps::Map::Array(aya::maps::MapData) pub aya::maps::Map::BloomFilter(aya::maps::MapData) +pub aya::maps::Map::CpuMap(aya::maps::MapData) +pub aya::maps::Map::DevMap(aya::maps::MapData) +pub aya::maps::Map::DevMapHash(aya::maps::MapData) pub aya::maps::Map::HashMap(aya::maps::MapData) pub aya::maps::Map::LpmTrie(aya::maps::MapData) pub aya::maps::Map::LruHashMap(aya::maps::MapData) @@ -812,12 +997,25 @@ pub aya::maps::Map::SockMap(aya::maps::MapData) pub aya::maps::Map::Stack(aya::maps::MapData) pub aya::maps::Map::StackTraceMap(aya::maps::MapData) pub aya::maps::Map::Unsupported(aya::maps::MapData) +pub aya::maps::Map::XskMap(aya::maps::MapData) +impl core::convert::TryFrom for aya::maps::CpuMap +pub type aya::maps::CpuMap::Error = aya::maps::MapError +pub fn aya::maps::CpuMap::try_from(map: aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::DevMap +pub type aya::maps::DevMap::Error = aya::maps::MapError +pub fn aya::maps::DevMap::try_from(map: aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::DevMapHash +pub type aya::maps::DevMapHash::Error = aya::maps::MapError +pub fn aya::maps::DevMapHash::try_from(map: aya::maps::Map) -> core::result::Result impl core::convert::TryFrom for aya::maps::ProgramArray pub type aya::maps::ProgramArray::Error = aya::maps::MapError pub fn aya::maps::ProgramArray::try_from(map: aya::maps::Map) -> core::result::Result impl core::convert::TryFrom for aya::maps::SockMap pub type aya::maps::SockMap::Error = aya::maps::MapError pub fn aya::maps::SockMap::try_from(map: aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::XskMap +pub type aya::maps::XskMap::Error = aya::maps::MapError +pub fn aya::maps::XskMap::try_from(map: aya::maps::Map) -> core::result::Result impl core::convert::TryFrom for aya::maps::perf::AsyncPerfEventArray pub type aya::maps::perf::AsyncPerfEventArray::Error = aya::maps::MapError pub fn aya::maps::perf::AsyncPerfEventArray::try_from(map: aya::maps::Map) -> core::result::Result @@ -881,12 +1079,24 @@ pub fn aya::maps::queue::Queue<&'a mut aya::maps::MapData, V>::try_from(map: &'a impl<'a, V: aya::Pod> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::stack::Stack<&'a mut aya::maps::MapData, V> pub type aya::maps::stack::Stack<&'a mut aya::maps::MapData, V>::Error = aya::maps::MapError pub fn aya::maps::stack::Stack<&'a mut aya::maps::MapData, V>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::CpuMap<&'a aya::maps::MapData> +pub type aya::maps::CpuMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::CpuMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::DevMap<&'a aya::maps::MapData> +pub type aya::maps::DevMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::DevMapHash<&'a aya::maps::MapData> +pub type aya::maps::DevMapHash<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMapHash<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::ProgramArray<&'a aya::maps::MapData> pub type aya::maps::ProgramArray<&'a aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::ProgramArray<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::SockMap<&'a aya::maps::MapData> pub type aya::maps::SockMap<&'a aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::SockMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::XskMap<&'a aya::maps::MapData> +pub type aya::maps::XskMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::XskMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::perf::AsyncPerfEventArray<&'a aya::maps::MapData> pub type aya::maps::perf::AsyncPerfEventArray<&'a aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::perf::AsyncPerfEventArray<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result @@ -896,12 +1106,24 @@ pub fn aya::maps::perf::PerfEventArray<&'a aya::maps::MapData>::try_from(map: &' impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::stack_trace::StackTraceMap<&'a aya::maps::MapData> pub type aya::maps::stack_trace::StackTraceMap<&'a aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::stack_trace::StackTraceMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::CpuMap<&'a mut aya::maps::MapData> +pub type aya::maps::CpuMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::CpuMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::DevMap<&'a mut aya::maps::MapData> +pub type aya::maps::DevMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::DevMapHash<&'a mut aya::maps::MapData> +pub type aya::maps::DevMapHash<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMapHash<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::ProgramArray<&'a mut aya::maps::MapData> pub type aya::maps::ProgramArray<&'a mut aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::ProgramArray<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::SockMap<&'a mut aya::maps::MapData> pub type aya::maps::SockMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::SockMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::XskMap<&'a mut aya::maps::MapData> +pub type aya::maps::XskMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::XskMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::perf::AsyncPerfEventArray<&'a mut aya::maps::MapData> pub type aya::maps::perf::AsyncPerfEventArray<&'a mut aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::perf::AsyncPerfEventArray<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result @@ -984,12 +1206,15 @@ pub aya::maps::MapError::OutOfBounds::max_entries: u32 pub aya::maps::MapError::PinError pub aya::maps::MapError::PinError::error: aya::pin::PinError pub aya::maps::MapError::PinError::name: core::option::Option +pub aya::maps::MapError::ProgIdNotSupported pub aya::maps::MapError::ProgramNotLoaded pub aya::maps::MapError::SyscallError(crate::sys::SyscallError) pub aya::maps::MapError::Unsupported pub aya::maps::MapError::Unsupported::map_type: u32 impl core::convert::From for aya::BpfError pub fn aya::BpfError::from(source: aya::maps::MapError) -> Self +impl core::convert::From for aya::maps::xdp::XdpMapError +pub fn aya::maps::xdp::XdpMapError::from(source: aya::maps::MapError) -> Self impl core::convert::From for aya::programs::ProgramError pub fn aya::programs::ProgramError::from(source: aya::maps::MapError) -> Self impl core::error::Error for aya::maps::MapError @@ -1131,6 +1356,118 @@ impl core::borrow::BorrowMut for aya::maps::bloom_filter::BloomFilter::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::maps::bloom_filter::BloomFilter pub fn aya::maps::bloom_filter::BloomFilter::from(t: T) -> T +pub struct aya::maps::CpuMap +impl> aya::maps::CpuMap +pub fn aya::maps::CpuMap::get(&self, cpu_index: u32, flags: u64) -> core::result::Result +pub fn aya::maps::CpuMap::iter(&self) -> impl core::iter::traits::iterator::Iterator> + '_ +pub fn aya::maps::CpuMap::len(&self) -> u32 +impl> aya::maps::CpuMap +pub fn aya::maps::CpuMap::set(&mut self, cpu_index: u32, queue_size: u32, program: core::option::Option<&aya::programs::ProgramFd>, flags: u64) -> core::result::Result<(), aya::maps::xdp::XdpMapError> +impl core::convert::TryFrom for aya::maps::CpuMap +pub type aya::maps::CpuMap::Error = aya::maps::MapError +pub fn aya::maps::CpuMap::try_from(map: aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::CpuMap<&'a aya::maps::MapData> +pub type aya::maps::CpuMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::CpuMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::CpuMap<&'a mut aya::maps::MapData> +pub type aya::maps::CpuMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::CpuMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::marker::Send for aya::maps::CpuMap where T: core::marker::Send +impl core::marker::Sync for aya::maps::CpuMap where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::CpuMap where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::CpuMap where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::CpuMap where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::CpuMap where U: core::convert::From +pub fn aya::maps::CpuMap::into(self) -> U +impl core::convert::TryFrom for aya::maps::CpuMap where U: core::convert::Into +pub type aya::maps::CpuMap::Error = core::convert::Infallible +pub fn aya::maps::CpuMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::CpuMap where U: core::convert::TryFrom +pub type aya::maps::CpuMap::Error = >::Error +pub fn aya::maps::CpuMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::CpuMap where T: 'static + core::marker::Sized +pub fn aya::maps::CpuMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::CpuMap where T: core::marker::Sized +pub fn aya::maps::CpuMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::CpuMap where T: core::marker::Sized +pub fn aya::maps::CpuMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::CpuMap +pub fn aya::maps::CpuMap::from(t: T) -> T +pub struct aya::maps::DevMap +impl> aya::maps::DevMap +pub fn aya::maps::DevMap::get(&self, index: u32, flags: u64) -> core::result::Result +pub fn aya::maps::DevMap::iter(&self) -> impl core::iter::traits::iterator::Iterator> + '_ +pub fn aya::maps::DevMap::len(&self) -> u32 +impl> aya::maps::DevMap +pub fn aya::maps::DevMap::set(&mut self, index: u32, target_if_index: u32, program: core::option::Option<&aya::programs::ProgramFd>, flags: u64) -> core::result::Result<(), aya::maps::xdp::XdpMapError> +impl core::convert::TryFrom for aya::maps::DevMap +pub type aya::maps::DevMap::Error = aya::maps::MapError +pub fn aya::maps::DevMap::try_from(map: aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::DevMap<&'a aya::maps::MapData> +pub type aya::maps::DevMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::DevMap<&'a mut aya::maps::MapData> +pub type aya::maps::DevMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::marker::Send for aya::maps::DevMap where T: core::marker::Send +impl core::marker::Sync for aya::maps::DevMap where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::DevMap where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::DevMap where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::DevMap where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::DevMap where U: core::convert::From +pub fn aya::maps::DevMap::into(self) -> U +impl core::convert::TryFrom for aya::maps::DevMap where U: core::convert::Into +pub type aya::maps::DevMap::Error = core::convert::Infallible +pub fn aya::maps::DevMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::DevMap where U: core::convert::TryFrom +pub type aya::maps::DevMap::Error = >::Error +pub fn aya::maps::DevMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::DevMap where T: 'static + core::marker::Sized +pub fn aya::maps::DevMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::DevMap where T: core::marker::Sized +pub fn aya::maps::DevMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::DevMap where T: core::marker::Sized +pub fn aya::maps::DevMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::DevMap +pub fn aya::maps::DevMap::from(t: T) -> T +pub struct aya::maps::DevMapHash +impl> aya::maps::DevMapHash +pub fn aya::maps::DevMapHash::get(&self, key: u32, flags: u64) -> core::result::Result +pub fn aya::maps::DevMapHash::iter(&self) -> aya::maps::MapIter<'_, u32, DevMapValue, Self> +pub fn aya::maps::DevMapHash::keys(&self) -> aya::maps::MapKeys<'_, u32> +impl> aya::maps::DevMapHash +pub fn aya::maps::DevMapHash::insert(&mut self, key: u32, target_if_index: u32, program: core::option::Option<&aya::programs::ProgramFd>, flags: u64) -> core::result::Result<(), aya::maps::xdp::XdpMapError> +pub fn aya::maps::DevMapHash::remove(&mut self, key: u32) -> core::result::Result<(), aya::maps::MapError> +impl core::convert::TryFrom for aya::maps::DevMapHash +pub type aya::maps::DevMapHash::Error = aya::maps::MapError +pub fn aya::maps::DevMapHash::try_from(map: aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::DevMapHash<&'a aya::maps::MapData> +pub type aya::maps::DevMapHash<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMapHash<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::DevMapHash<&'a mut aya::maps::MapData> +pub type aya::maps::DevMapHash<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::DevMapHash<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::marker::Send for aya::maps::DevMapHash where T: core::marker::Send +impl core::marker::Sync for aya::maps::DevMapHash where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::DevMapHash where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::DevMapHash where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::DevMapHash where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::DevMapHash where U: core::convert::From +pub fn aya::maps::DevMapHash::into(self) -> U +impl core::convert::TryFrom for aya::maps::DevMapHash where U: core::convert::Into +pub type aya::maps::DevMapHash::Error = core::convert::Infallible +pub fn aya::maps::DevMapHash::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::DevMapHash where U: core::convert::TryFrom +pub type aya::maps::DevMapHash::Error = >::Error +pub fn aya::maps::DevMapHash::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::DevMapHash where T: 'static + core::marker::Sized +pub fn aya::maps::DevMapHash::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::DevMapHash where T: core::marker::Sized +pub fn aya::maps::DevMapHash::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::DevMapHash where T: core::marker::Sized +pub fn aya::maps::DevMapHash::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::DevMapHash +pub fn aya::maps::DevMapHash::from(t: T) -> T pub struct aya::maps::HashMap impl, K: aya::Pod, V: aya::Pod> aya::maps::hash_map::HashMap pub fn aya::maps::hash_map::HashMap::get(&self, key: &K, flags: u64) -> core::result::Result @@ -1712,6 +2049,41 @@ impl core::borrow::BorrowMut for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::from(t: T) -> T +pub struct aya::maps::XskMap +impl> aya::maps::XskMap +pub fn aya::maps::XskMap::len(&self) -> u32 +impl> aya::maps::XskMap +pub fn aya::maps::XskMap::set(&mut self, index: u32, socket_fd: impl std::os::fd::raw::AsRawFd, flags: u64) -> core::result::Result<(), aya::maps::MapError> +impl core::convert::TryFrom for aya::maps::XskMap +pub type aya::maps::XskMap::Error = aya::maps::MapError +pub fn aya::maps::XskMap::try_from(map: aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::XskMap<&'a aya::maps::MapData> +pub type aya::maps::XskMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::XskMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::XskMap<&'a mut aya::maps::MapData> +pub type aya::maps::XskMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::XskMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::marker::Send for aya::maps::XskMap where T: core::marker::Send +impl core::marker::Sync for aya::maps::XskMap where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::XskMap where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::XskMap where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::XskMap where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::XskMap where U: core::convert::From +pub fn aya::maps::XskMap::into(self) -> U +impl core::convert::TryFrom for aya::maps::XskMap where U: core::convert::Into +pub type aya::maps::XskMap::Error = core::convert::Infallible +pub fn aya::maps::XskMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::XskMap where U: core::convert::TryFrom +pub type aya::maps::XskMap::Error = >::Error +pub fn aya::maps::XskMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::XskMap where T: 'static + core::marker::Sized +pub fn aya::maps::XskMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::XskMap where T: core::marker::Sized +pub fn aya::maps::XskMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::XskMap where T: core::marker::Sized +pub fn aya::maps::XskMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::XskMap +pub fn aya::maps::XskMap::from(t: T) -> T pub trait aya::maps::IterableMap pub fn aya::maps::IterableMap::get(&self, key: &K) -> core::result::Result pub fn aya::maps::IterableMap::map(&self) -> &aya::maps::MapData @@ -4514,13 +4886,12 @@ pub fn aya::programs::xdp::Xdp::attach(&mut self, interface: &str, flags: aya::p pub fn aya::programs::xdp::Xdp::attach_to_if_index(&mut self, if_index: u32, flags: aya::programs::xdp::XdpFlags) -> core::result::Result pub fn aya::programs::xdp::Xdp::attach_to_link(&mut self, link: aya::programs::xdp::XdpLink) -> core::result::Result pub fn aya::programs::xdp::Xdp::detach(&mut self, link_id: aya::programs::xdp::XdpLinkId) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::xdp::Xdp::from_pin>(path: P, attach_type: aya_obj::programs::xdp::XdpAttachType) -> core::result::Result pub fn aya::programs::xdp::Xdp::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError> pub fn aya::programs::xdp::Xdp::take_link(&mut self, link_id: aya::programs::xdp::XdpLinkId) -> core::result::Result impl aya::programs::xdp::Xdp pub fn aya::programs::xdp::Xdp::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> impl aya::programs::xdp::Xdp -pub fn aya::programs::xdp::Xdp::from_pin>(path: P) -> core::result::Result -impl aya::programs::xdp::Xdp pub fn aya::programs::xdp::Xdp::info(&self) -> core::result::Result impl aya::programs::xdp::Xdp pub fn aya::programs::xdp::Xdp::pin>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError> @@ -6681,13 +7052,12 @@ pub fn aya::programs::xdp::Xdp::attach(&mut self, interface: &str, flags: aya::p pub fn aya::programs::xdp::Xdp::attach_to_if_index(&mut self, if_index: u32, flags: aya::programs::xdp::XdpFlags) -> core::result::Result pub fn aya::programs::xdp::Xdp::attach_to_link(&mut self, link: aya::programs::xdp::XdpLink) -> core::result::Result pub fn aya::programs::xdp::Xdp::detach(&mut self, link_id: aya::programs::xdp::XdpLinkId) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::xdp::Xdp::from_pin>(path: P, attach_type: aya_obj::programs::xdp::XdpAttachType) -> core::result::Result pub fn aya::programs::xdp::Xdp::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError> pub fn aya::programs::xdp::Xdp::take_link(&mut self, link_id: aya::programs::xdp::XdpLinkId) -> core::result::Result impl aya::programs::xdp::Xdp pub fn aya::programs::xdp::Xdp::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> impl aya::programs::xdp::Xdp -pub fn aya::programs::xdp::Xdp::from_pin>(path: P) -> core::result::Result -impl aya::programs::xdp::Xdp pub fn aya::programs::xdp::Xdp::info(&self) -> core::result::Result impl aya::programs::xdp::Xdp pub fn aya::programs::xdp::Xdp::pin>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError> @@ -7234,6 +7604,8 @@ pub fn aya::VerifierLogLevel::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::VerifierLogLevel pub fn aya::VerifierLogLevel::from(t: T) -> T pub unsafe trait aya::Pod: core::marker::Copy + 'static +impl aya::Pod for aya_obj::generated::linux_bindings_x86_64::bpf_cpumap_val +impl aya::Pod for aya_obj::generated::linux_bindings_x86_64::bpf_devmap_val impl aya::Pod for i128 impl aya::Pod for i16 impl aya::Pod for i32 From 579e3cee22ae8e932efb0894ca7fd9ceb91ca7fa Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Mon, 18 Sep 2023 16:16:27 +0200 Subject: [PATCH 15/16] aya, bpf: misc fixes following review comments --- aya-bpf-macros/src/lib.rs | 9 +-- aya-bpf-macros/src/xdp.rs | 2 +- aya-obj/src/obj.rs | 8 --- aya/src/bpf.rs | 6 +- aya/src/maps/mod.rs | 2 +- aya/src/maps/xdp/cpu_map.rs | 70 ++++++++++++---------- aya/src/maps/xdp/dev_map.rs | 76 +++++++++++++----------- aya/src/maps/xdp/dev_map_hash.rs | 68 +++++++++++---------- aya/src/maps/xdp/mod.rs | 15 +++++ aya/src/maps/xdp/xsk_map.rs | 4 ++ bpf/aya-bpf/src/maps/xdp/cpu_map.rs | 18 +++--- bpf/aya-bpf/src/maps/xdp/dev_map.rs | 37 +++++++----- bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs | 22 +++---- bpf/aya-bpf/src/maps/xdp/xsk_map.rs | 14 +++-- test/integration-ebpf/src/redirect.rs | 8 +-- xtask/public-api/aya-bpf.txt | 16 ++--- 16 files changed, 203 insertions(+), 172 deletions(-) diff --git a/aya-bpf-macros/src/lib.rs b/aya-bpf-macros/src/lib.rs index bdac2795..f29b725f 100644 --- a/aya-bpf-macros/src/lib.rs +++ b/aya-bpf-macros/src/lib.rs @@ -147,14 +147,7 @@ pub fn sk_msg(attrs: TokenStream, item: TokenStream) -> TokenStream { /// /// #[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) +/// XDP_PASS /// } /// ``` #[proc_macro_error] diff --git a/aya-bpf-macros/src/xdp.rs b/aya-bpf-macros/src/xdp.rs index 6a8f61e7..61f8c014 100644 --- a/aya-bpf-macros/src/xdp.rs +++ b/aya-bpf-macros/src/xdp.rs @@ -28,7 +28,7 @@ impl Xdp { Some(name) => { return Err(Error::new_spanned( "map", - format!("invalid value. expected 'cpumap' or 'devmap', found '{name}'"), + format!("Invalid value. Expected 'cpumap' or 'devmap', found '{name}'"), )) } None => None, diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index 6909a229..38221f96 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -50,7 +50,6 @@ pub struct Features { bpf_cookie: bool, cpumap_prog_id: bool, devmap_prog_id: bool, - devmap_hash_prog_id: bool, btf: Option, } @@ -65,7 +64,6 @@ impl Features { bpf_cookie: bool, cpumap_prog_id: bool, devmap_prog_id: bool, - devmap_hash_prog_id: bool, btf: Option, ) -> Self { Self { @@ -76,7 +74,6 @@ impl Features { bpf_cookie, cpumap_prog_id, devmap_prog_id, - devmap_hash_prog_id, btf, } } @@ -116,11 +113,6 @@ impl Features { self.devmap_prog_id } - /// Returns whether XDP Hash Device Maps support chained program IDs. - pub fn devmap_hash_prog_id(&self) -> bool { - self.devmap_hash_prog_id - } - /// If BTF is supported, returns which BTF features are supported. pub fn btf(&self) -> Option<&BtfFeatures> { self.btf.as_ref() diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 181d971f..2272bc52 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -96,7 +96,6 @@ fn detect_features() -> Features { is_bpf_cookie_supported(), is_prog_id_supported(BPF_MAP_TYPE_CPUMAP), is_prog_id_supported(BPF_MAP_TYPE_DEVMAP), - is_prog_id_supported(BPF_MAP_TYPE_DEVMAP_HASH), btf, ); debug!("BPF Feature Detection: {:#?}", f); @@ -484,12 +483,9 @@ impl<'a> BpfLoader<'a> { Ok(BPF_MAP_TYPE_CPUMAP) => { obj.set_value_size(if FEATURES.cpumap_prog_id() { 8 } else { 4 }) } - Ok(BPF_MAP_TYPE_DEVMAP) => { + Ok(BPF_MAP_TYPE_DEVMAP | BPF_MAP_TYPE_DEVMAP_HASH) => { obj.set_value_size(if FEATURES.devmap_prog_id() { 8 } else { 4 }) } - Ok(BPF_MAP_TYPE_DEVMAP_HASH) => { - obj.set_value_size(if FEATURES.devmap_hash_prog_id() { 8 } else { 4 }) - } _ => (), } let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd()); diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index d41600fe..1a590791 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -279,7 +279,7 @@ pub enum Map { DevMapHash(MapData), /// A [`XskMap`] map. XskMap(MapData), - /// An unsupported map type + /// An unsupported map type. Unsupported(MapData), } diff --git a/aya/src/maps/xdp/cpu_map.rs b/aya/src/maps/xdp/cpu_map.rs index e6349163..d9cc7d19 100644 --- a/aya/src/maps/xdp/cpu_map.rs +++ b/aya/src/maps/xdp/cpu_map.rs @@ -6,7 +6,7 @@ use std::{ os::fd::{AsFd, AsRawFd}, }; -use aya_obj::generated::{bpf_cpumap_val, bpf_cpumap_val__bindgen_ty_1}; +use aya_obj::generated::bpf_cpumap_val; use crate::{ maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, @@ -15,6 +15,8 @@ use crate::{ Pod, FEATURES, }; +use super::XdpMapError; + /// An array of available CPUs. /// /// XDP programs can use this map to redirect packets to a target @@ -29,19 +31,24 @@ use crate::{ /// # let elf_bytes = &[]; /// use aya::maps::xdp::CpuMap; /// +/// let ncpus = aya::util::nr_cpus().unwrap() as u32; /// let mut bpf = aya::BpfLoader::new() -/// .set_max_entries("CPUS", aya::util::nr_cpus().unwrap() as u32) +/// .set_max_entries("CPUS", ncpus) /// .load(elf_bytes) /// .unwrap(); /// let mut cpumap = CpuMap::try_from(bpf.map_mut("CPUS").unwrap())?; /// let flags = 0; /// let queue_size = 2048; -/// for i in 0u32..8u32 { +/// for i in 0..ncpus { /// cpumap.set(i, queue_size, None, flags); /// } /// /// # Ok::<(), aya::BpfError>(()) /// ``` +/// +/// # See also +/// +/// Kernel documentation: #[doc(alias = "BPF_MAP_TYPE_CPUMAP")] pub struct CpuMap { inner: T, @@ -67,7 +74,7 @@ impl> CpuMap { self.inner.borrow().obj.max_entries() } - /// Returns the queue size and possible program for a given CPU index. + /// Returns the queue size and optional program for a given CPU index. /// /// # Errors /// @@ -81,7 +88,7 @@ impl> CpuMap { let value = if FEATURES.cpumap_prog_id() { bpf_map_lookup_elem::<_, bpf_cpumap_val>(fd, &cpu_index, flags).map(|value| { value.map(|value| CpuMapValue { - qsize: value.qsize, + queue_size: value.qsize, // SAFETY: map writes use fd, map reads use id. // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6241 prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), @@ -90,7 +97,7 @@ impl> CpuMap { } else { bpf_map_lookup_elem::<_, u32>(fd, &cpu_index, flags).map(|value| { value.map(|qsize| CpuMapValue { - qsize, + queue_size: qsize, prog_id: None, }) }) @@ -115,52 +122,52 @@ impl> CpuMap { /// 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. + /// Starting from Linux kernel 5.9, 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. + /// The chained program must be loaded with the `BPF_XDP_CPUMAP` attach type. When using + /// `aya-ebpf`, that means XDP programs that specify the `map = "cpumap"` argument. See the + /// kernel-space `aya_ebpf::xdp` for more information. /// /// # Errors /// /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] - /// if `bpf_map_update_elem` fails, [`MapError::ProgIdNotSupported`] if the kernel does not - /// support program ids and one is provided. + /// if `bpf_map_update_elem` fails, [`XdpMapError::ChainedProgramNotSupported`] if the kernel + /// does not support chained programs and one is provided. pub fn set( &mut self, cpu_index: u32, queue_size: u32, program: Option<&ProgramFd>, flags: u64, - ) -> Result<(), MapError> { + ) -> Result<(), XdpMapError> { let data = self.inner.borrow_mut(); check_bounds(data, cpu_index)?; let fd = data.fd().as_fd(); let res = if FEATURES.cpumap_prog_id() { - let value = bpf_cpumap_val { - 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_fd().as_raw_fd()) - .unwrap_or_default(), - }, - }; + let mut value = unsafe { std::mem::zeroed::() }; + value.qsize = queue_size; + // Default is valid as the kernel will only consider fd > 0: + // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/cpumap.c#L466 + value.bpf_prog.fd = program + .map(|prog| prog.as_fd().as_raw_fd()) + .unwrap_or_default(); bpf_map_update_elem(fd, Some(&cpu_index), &value, flags) } else { if program.is_some() { - return Err(MapError::ProgIdNotSupported); + return Err(XdpMapError::ChainedProgramNotSupported); } bpf_map_update_elem(fd, Some(&cpu_index), &queue_size, flags) }; - res.map_err(|(_, io_error)| SyscallError { - call: "bpf_map_update_elem", - io_error, + res.map_err(|(_, io_error)| { + MapError::from(SyscallError { + call: "bpf_map_update_elem", + io_error, + }) })?; Ok(()) } @@ -179,7 +186,10 @@ impl> IterableMap for CpuMap { unsafe impl Pod for bpf_cpumap_val {} #[derive(Clone, Copy, Debug)] +/// The value of a CPU map. pub struct CpuMapValue { - pub qsize: u32, + /// Size of the for the CPU. + pub queue_size: u32, + /// Chained XDP program ID. pub prog_id: Option, } diff --git a/aya/src/maps/xdp/dev_map.rs b/aya/src/maps/xdp/dev_map.rs index df43461c..3c4240f5 100644 --- a/aya/src/maps/xdp/dev_map.rs +++ b/aya/src/maps/xdp/dev_map.rs @@ -6,7 +6,7 @@ use std::{ os::fd::{AsFd, AsRawFd}, }; -use aya_obj::generated::{bpf_devmap_val, bpf_devmap_val__bindgen_ty_1}; +use aya_obj::generated::bpf_devmap_val; use crate::{ maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, @@ -15,6 +15,8 @@ use crate::{ Pod, FEATURES, }; +use super::XdpMapError; + /// An array of network devices. /// /// XDP programs can use this map to redirect to other network @@ -30,12 +32,15 @@ use crate::{ /// use aya::maps::xdp::DevMap; /// /// let mut devmap = DevMap::try_from(bpf.map_mut("IFACES").unwrap())?; -/// let source = 32u32; -/// let dest = 42u32; -/// devmap.set(source, dest, None, 0); +/// // Lookups at index 2 will redirect packets to interface with index 3 (e.g. eth1) +/// devmap.set(2, 3, None, 0); /// /// # Ok::<(), aya::BpfError>(()) /// ``` +/// +/// # See also +/// +/// Kernel documentation: #[doc(alias = "BPF_MAP_TYPE_DEVMAP")] pub struct DevMap { inner: T, @@ -61,7 +66,7 @@ impl> DevMap { self.inner.borrow().obj.max_entries() } - /// Returns the target ifindex and possible program at a given index. + /// Returns the target interface index and optional program at a given index. /// /// # Errors /// @@ -75,7 +80,7 @@ impl> DevMap { let value = if FEATURES.devmap_prog_id() { bpf_map_lookup_elem::<_, bpf_devmap_val>(fd, &index, flags).map(|value| { value.map(|value| DevMapValue { - ifindex: value.ifindex, + if_index: value.ifindex, // SAFETY: map writes use fd, map reads use id. // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6228 prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), @@ -84,7 +89,7 @@ impl> DevMap { } else { bpf_map_lookup_elem::<_, u32>(fd, &index, flags).map(|value| { value.map(|ifindex| DevMapValue { - ifindex, + if_index: ifindex, prog_id: None, }) }) @@ -104,57 +109,57 @@ impl> DevMap { } impl> DevMap { - /// Sets the target ifindex at index, and optionally a chained program. + /// Sets the target interface index at index, and optionally a chained program. /// /// When redirecting using `index`, packets will be transmitted by the interface with - /// `ifindex`. + /// `target_if_index`. /// - /// 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. + /// Starting from Linux kernel 5.8, 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. + /// The chained program must be loaded with the `BPF_XDP_DEVMAP` attach type. When using + /// `aya-ebpf`, that means XDP programs that specify the `map = "devmap"` argument. See the + /// kernel-space `aya_ebpf::xdp` for more information. /// /// # Errors /// /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] /// if `bpf_map_update_elem` fails, [`MapError::ProgIdNotSupported`] if the kernel does not - /// support program ids and one is provided. + /// support chained programs and one is provided. pub fn set( &mut self, index: u32, - ifindex: u32, + target_if_index: u32, program: Option<&ProgramFd>, flags: u64, - ) -> Result<(), MapError> { + ) -> Result<(), XdpMapError> { let data = self.inner.borrow_mut(); check_bounds(data, index)?; let fd = data.fd().as_fd(); let res = if FEATURES.devmap_prog_id() { - let value = bpf_devmap_val { - 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 - // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918 - fd: program - .map(|prog| prog.as_fd().as_raw_fd()) - .unwrap_or_default(), - }, - }; + let mut value = unsafe { std::mem::zeroed::() }; + value.ifindex = target_if_index; + // 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#L918 + value.bpf_prog.fd = program + .map(|prog| prog.as_fd().as_raw_fd()) + .unwrap_or_default(); bpf_map_update_elem(fd, Some(&index), &value, flags) } else { if program.is_some() { - return Err(MapError::ProgIdNotSupported); + return Err(XdpMapError::ChainedProgramNotSupported); } - bpf_map_update_elem(fd, Some(&index), &ifindex, flags) + bpf_map_update_elem(fd, Some(&index), &target_if_index, flags) }; - res.map_err(|(_, io_error)| SyscallError { - call: "bpf_map_update_elem", - io_error, + res.map_err(|(_, io_error)| { + MapError::from(SyscallError { + call: "bpf_map_update_elem", + io_error, + }) })?; Ok(()) } @@ -173,7 +178,10 @@ impl> IterableMap for DevMap { unsafe impl Pod for bpf_devmap_val {} #[derive(Clone, Copy, Debug)] +/// The value of a device map. pub struct DevMapValue { - pub ifindex: u32, + /// Target interface index to redirect to. + pub if_index: u32, + /// Chained XDP program ID. pub prog_id: Option, } diff --git a/aya/src/maps/xdp/dev_map_hash.rs b/aya/src/maps/xdp/dev_map_hash.rs index 8311a29b..469c8420 100644 --- a/aya/src/maps/xdp/dev_map_hash.rs +++ b/aya/src/maps/xdp/dev_map_hash.rs @@ -6,7 +6,7 @@ use std::{ os::fd::{AsFd, AsRawFd}, }; -use aya_obj::generated::{bpf_devmap_val, bpf_devmap_val__bindgen_ty_1}; +use aya_obj::generated::bpf_devmap_val; use crate::{ maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys}, @@ -15,7 +15,7 @@ use crate::{ FEATURES, }; -use super::dev_map::DevMapValue; +use super::{dev_map::DevMapValue, XdpMapError}; /// An hashmap of network devices. /// @@ -32,12 +32,15 @@ use super::dev_map::DevMapValue; /// use aya::maps::xdp::DevMapHash; /// /// let mut devmap = DevMapHash::try_from(bpf.map_mut("IFACES").unwrap())?; -/// let flags = 0; -/// let ifindex = 32u32; -/// devmap.insert(ifindex, ifindex, None, flags); +/// // Lookups with key 2 will redirect packets to interface with index 3 (e.g. eth1) +/// devmap.insert(2, 3, None, 0); /// /// # Ok::<(), aya::BpfError>(()) /// ``` +/// +/// # See also +/// +/// Kernel documentation: #[doc(alias = "BPF_MAP_TYPE_DEVMAP_HASH")] pub struct DevMapHash { inner: T, @@ -47,7 +50,7 @@ impl> DevMapHash { pub(crate) fn new(map: T) -> Result { let data = map.borrow(); - if FEATURES.devmap_hash_prog_id() { + if FEATURES.devmap_prog_id() { check_kv_size::(data)?; } else { check_kv_size::(data)?; @@ -56,7 +59,7 @@ impl> DevMapHash { Ok(Self { inner: map }) } - /// Returns the target ifindex and possible program for a given key. + /// Returns the target interface index and optional program for a given key. /// /// # Errors /// @@ -64,10 +67,10 @@ impl> DevMapHash { pub fn get(&self, key: u32, flags: u64) -> Result { let fd = self.inner.borrow().fd().as_fd(); - let value = if FEATURES.devmap_hash_prog_id() { + let value = if FEATURES.devmap_prog_id() { bpf_map_lookup_elem::<_, bpf_devmap_val>(fd, &key, flags).map(|value| { value.map(|value| DevMapValue { - ifindex: value.ifindex, + if_index: value.ifindex, // SAFETY: map writes use fd, map reads use id. // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6228 prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }), @@ -76,7 +79,7 @@ impl> DevMapHash { } else { bpf_map_lookup_elem::<_, u32>(fd, &key, flags).map(|value| { value.map(|ifindex| DevMapValue { - ifindex, + if_index: ifindex, prog_id: None, }) }) @@ -105,44 +108,43 @@ impl> DevMapHash { /// /// 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. + /// Starting from Linux kernel 5.8, 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. + /// The chained program must be loaded with the `BPF_XDP_DEVMAP` attach type. When using + /// `aya-ebpf`, that means XDP programs that specify the `map = "devmap"` argument. See the + /// kernel-space `aya_ebpf::xdp` for more information. /// /// # Errors /// /// Returns [`MapError::SyscallError`] if `bpf_map_update_elem` fails, - /// [`MapError::ProgIdNotSupported`] if the kernel does not support program ids and one is + /// [`MapError::ProgIdNotSupported`] if the kernel does not support chained programs and one is /// provided. pub fn insert( &mut self, key: u32, - ifindex: u32, + target_if_index: u32, program: Option<&ProgramFd>, flags: u64, - ) -> Result<(), MapError> { - if FEATURES.devmap_hash_prog_id() { - let value = bpf_devmap_val { - 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 - // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918 - fd: program - .map(|prog| prog.as_fd().as_raw_fd()) - .unwrap_or_default(), - }, - }; - hash_map::insert(self.inner.borrow_mut(), &key, &value, flags) + ) -> Result<(), XdpMapError> { + if FEATURES.devmap_prog_id() { + let mut value = unsafe { std::mem::zeroed::() }; + value.ifindex = target_if_index; + // 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#L918 + value.bpf_prog.fd = program + .map(|prog| prog.as_fd().as_raw_fd()) + .unwrap_or_default(); + hash_map::insert(self.inner.borrow_mut(), &key, &value, flags)?; } else { if program.is_some() { - return Err(MapError::ProgIdNotSupported); + return Err(XdpMapError::ChainedProgramNotSupported); } - hash_map::insert(self.inner.borrow_mut(), &key, &ifindex, flags) + hash_map::insert(self.inner.borrow_mut(), &key, &target_if_index, flags)?; } + Ok(()) } /// Removes a value from the map. diff --git a/aya/src/maps/xdp/mod.rs b/aya/src/maps/xdp/mod.rs index 6120dc90..0faa41bb 100644 --- a/aya/src/maps/xdp/mod.rs +++ b/aya/src/maps/xdp/mod.rs @@ -8,3 +8,18 @@ pub use cpu_map::CpuMap; pub use dev_map::DevMap; pub use dev_map_hash::DevMapHash; pub use xsk_map::XskMap; + +use super::MapError; +use thiserror::Error; + +#[derive(Error, Debug)] +/// Errors occuring from working with XDP maps. +pub enum XdpMapError { + /// Chained programs are not supported. + #[error("chained programs are not supported by the current kernel")] + ChainedProgramNotSupported, + + /// Map operation failed. + #[error(transparent)] + MapError(#[from] MapError), +} diff --git a/aya/src/maps/xdp/xsk_map.rs b/aya/src/maps/xdp/xsk_map.rs index 99477dbd..5382ae4c 100644 --- a/aya/src/maps/xdp/xsk_map.rs +++ b/aya/src/maps/xdp/xsk_map.rs @@ -30,6 +30,10 @@ use crate::{ /// xskmap.set(0, socket_fd, 0); /// # Ok::<(), aya::BpfError>(()) /// ``` +/// +/// # See also +/// +/// Kernel documentation: #[doc(alias = "BPF_MAP_TYPE_XSKMAP")] pub struct XskMap { inner: T, diff --git a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs index df1a32d2..0179382c 100644 --- a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs @@ -1,6 +1,6 @@ use core::{cell::UnsafeCell, mem}; -use aya_bpf_bindings::bindings::bpf_cpumap_val; +use aya_bpf_bindings::bindings::{bpf_cpumap_val, xdp_action::XDP_REDIRECT}; use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_CPUMAP}, @@ -40,9 +40,9 @@ 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. + /// 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. `max_entries` can be set to zero here, and updated by userspace + /// at runtime. Refer to the userspace documentation for more information. /// /// # Examples /// @@ -109,16 +109,18 @@ impl CpuMap { /// #[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) + /// MAP.redirect(7, 0).unwrap_or(xdp_action::XDP_DROP) /// } /// ``` #[inline(always)] - pub fn redirect(&self, index: u32, flags: u64) -> u32 { - unsafe { + pub fn redirect(&self, index: u32, flags: u64) -> Result { + let ret = unsafe { bpf_redirect_map(self.def.get() as *mut _, index.into(), flags) }; + match ret.unsigned_abs() as u32 { + XDP_REDIRECT => Ok(XDP_REDIRECT), // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags // argument on error. Thus I have no idea why it returns a long (i64) instead of // something saner, hence the unsigned_abs. - bpf_redirect_map(self.def.get() as *mut _, index.into(), flags).unsigned_abs() as u32 + ret => Err(ret), } } } diff --git a/bpf/aya-bpf/src/maps/xdp/dev_map.rs b/bpf/aya-bpf/src/maps/xdp/dev_map.rs index 18a76790..6142d151 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map.rs @@ -1,6 +1,6 @@ -use core::{cell::UnsafeCell, mem, ptr::NonNull}; +use core::{cell::UnsafeCell, mem, num::NonZeroU32, ptr::NonNull}; -use aya_bpf_bindings::bindings::bpf_devmap_val; +use aya_bpf_bindings::bindings::{bpf_devmap_val, xdp_action::XDP_REDIRECT}; use aya_bpf_cty::c_void; use crate::{ @@ -99,9 +99,9 @@ impl DevMap { /// #[map] /// static MAP: DevMap = DevMap::with_max_entries(1, 0); /// - /// let ifindex = MAP.get(0); + /// let target_if_index = MAP.get(0).target_if_index; /// - /// // redirect to ifindex + /// // redirect to if_index /// ``` #[inline(always)] pub fn get(&self, index: u32) -> Option { @@ -111,10 +111,10 @@ impl DevMap { &index as *const _ as *const c_void, ); NonNull::new(value as *mut bpf_devmap_val).map(|p| DevMapValue { - ifindex: p.as_ref().ifindex, + if_index: p.as_ref().ifindex, // SAFETY: map writes use fd, map reads use id. // https://elixir.bootlin.com/linux/v6.2/source/include/uapi/linux/bpf.h#L6136 - prog_id: p.as_ref().bpf_prog.id, + prog_id: NonZeroU32::new(p.as_ref().bpf_prog.id), }) } } @@ -133,23 +133,28 @@ impl DevMap { /// static MAP: DevMap = DevMap::with_max_entries(8, 0); /// /// #[xdp] - /// fn xdp(_ctx: XdpContext) -> i32 { - /// MAP.redirect(7, xdp_action::XDP_PASS as u64) + /// fn xdp(_ctx: XdpContext) -> u32 { + /// MAP.redirect(7, 0).unwrap_or(xdp_action::XDP_DROP) /// } /// ``` #[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 - // argument on error. Thus I have no idea why it returns a long (i64) instead of - // something saner, hence the unsigned_abs. - unsafe { - bpf_redirect_map(self.def.get() as *mut _, index.into(), flags).unsigned_abs() as u32 + pub fn redirect(&self, index: u32, flags: u64) -> Result { + let ret = unsafe { bpf_redirect_map(self.def.get() as *mut _, index.into(), flags) }; + match ret.unsigned_abs() as u32 { + XDP_REDIRECT => Ok(XDP_REDIRECT), + // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags + // argument on error. Thus I have no idea why it returns a long (i64) instead of + // something saner, hence the unsigned_abs. + ret => Err(ret), } } } #[derive(Clone, Copy)] +/// The value of a device map. pub struct DevMapValue { - pub ifindex: u32, - pub prog_id: u32, + /// Target interface index to redirect to. + pub if_index: u32, + /// Chained XDP program ID. + pub prog_id: Option, } 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 a6533e76..eb65b105 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs @@ -1,6 +1,6 @@ -use core::{cell::UnsafeCell, mem, ptr::NonNull}; +use core::{cell::UnsafeCell, mem, num::NonZeroU32, ptr::NonNull}; -use aya_bpf_bindings::bindings::bpf_devmap_val; +use aya_bpf_bindings::bindings::{bpf_devmap_val, xdp_action::XDP_REDIRECT}; use aya_bpf_cty::c_void; use crate::{ @@ -103,7 +103,7 @@ impl DevMapHash { /// #[map] /// static MAP: DevMapHash = DevMapHash::with_max_entries(1, 0); /// - /// let ifindex = MAP.get(42); + /// let target_if_index = MAP.get(42).target_if_index; /// /// // redirect to ifindex /// ``` @@ -113,10 +113,10 @@ impl DevMapHash { let value = bpf_map_lookup_elem(self.def.get() as *mut _, &key as *const _ as *const c_void); NonNull::new(value as *mut bpf_devmap_val).map(|p| DevMapValue { - ifindex: p.as_ref().ifindex, + if_index: p.as_ref().ifindex, // SAFETY: map writes use fd, map reads use id. // https://elixir.bootlin.com/linux/v6.2/source/include/uapi/linux/bpf.h#L6136 - prog_id: p.as_ref().bpf_prog.id, + prog_id: NonZeroU32::new(p.as_ref().bpf_prog.id), }) } } @@ -135,17 +135,19 @@ impl DevMapHash { /// static MAP: DevMapHash = DevMapHash::with_max_entries(8, 0); /// /// #[xdp] - /// fn xdp(_ctx: XdpContext) -> i32 { - /// MAP.redirect(7, xdp_action::XDP_PASS as u64) + /// fn xdp(_ctx: XdpContext) -> u32 { + /// MAP.redirect(7, 0).unwrap_or(xdp_action::XDP_DROP) /// } /// ``` #[inline(always)] - pub fn redirect(&self, key: u32, flags: u64) -> u32 { - unsafe { + pub fn redirect(&self, index: u32, flags: u64) -> Result { + let ret = unsafe { bpf_redirect_map(self.def.get() as *mut _, index.into(), flags) }; + match ret.unsigned_abs() as u32 { + XDP_REDIRECT => Ok(XDP_REDIRECT), // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags // argument on error. Thus I have no idea why it returns a long (i64) instead of // something saner, hence the unsigned_abs. - bpf_redirect_map(self.def.get() as *mut _, key.into(), flags).unsigned_abs() as u32 + ret => Err(ret), } } } diff --git a/bpf/aya-bpf/src/maps/xdp/xsk_map.rs b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs index ee766fa5..f4546edb 100644 --- a/bpf/aya-bpf/src/maps/xdp/xsk_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs @@ -1,6 +1,6 @@ use core::{cell::UnsafeCell, mem, ptr::NonNull}; -use aya_bpf_bindings::bindings::bpf_xdp_sock; +use aya_bpf_bindings::bindings::{bpf_xdp_sock, xdp_action::XDP_REDIRECT}; use aya_bpf_cty::c_void; use crate::{ @@ -150,18 +150,20 @@ impl XskMap { /// static SOCKS: XskMap = XskMap::with_max_entries(8, 0); /// /// #[xdp] - /// fn xdp(ctx, XdpContext) -> i32 { + /// fn xdp(ctx, XdpContext) -> u32 { /// let queue_id = unsafe { (*ctx.ctx).rx_queue_index }; - /// MAP.redirect(queue_id, xdp_action::XDP_DROP as u64) + /// MAP.redirect(queue_id, 0).unwrap_or(xdp_action::XDP_DROP) /// } /// ``` #[inline(always)] - pub fn redirect(&self, index: u32, flags: u64) -> u32 { - unsafe { + pub fn redirect(&self, index: u32, flags: u64) -> Result { + let ret = unsafe { bpf_redirect_map(self.def.get() as *mut _, index.into(), flags) }; + match ret.unsigned_abs() as u32 { + XDP_REDIRECT => Ok(XDP_REDIRECT), // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags // argument on error. Thus I have no idea why it returns a long (i64) instead of // something saner, hence the unsigned_abs. - bpf_redirect_map(self.def.get() as *mut _, index.into(), flags).unsigned_abs() as u32 + ret => Err(ret), } } } diff --git a/test/integration-ebpf/src/redirect.rs b/test/integration-ebpf/src/redirect.rs index 5817b418..41812cd0 100644 --- a/test/integration-ebpf/src/redirect.rs +++ b/test/integration-ebpf/src/redirect.rs @@ -26,25 +26,25 @@ static mut HITS: Array = Array::with_max_entries(2, 0); #[xdp] pub fn redirect_sock(_ctx: XdpContext) -> u32 { - SOCKS.redirect(0, xdp_action::XDP_ABORTED as u64) + SOCKS.redirect(0, 0).unwrap_or(xdp_action::XDP_ABORTED) } #[xdp] pub fn redirect_dev(_ctx: XdpContext) -> u32 { inc_hit(0); - DEVS.redirect(0, xdp_action::XDP_ABORTED as u64) + DEVS.redirect(0, 0).unwrap_or(xdp_action::XDP_ABORTED) } #[xdp] pub fn redirect_dev_hash(_ctx: XdpContext) -> u32 { inc_hit(0); - DEVS_HASH.redirect(10, xdp_action::XDP_ABORTED as u64) + DEVS_HASH.redirect(10, 0).unwrap_or(xdp_action::XDP_ABORTED) } #[xdp] pub fn redirect_cpu(_ctx: XdpContext) -> u32 { inc_hit(0); - CPUS.redirect(0, xdp_action::XDP_ABORTED as u64) + CPUS.redirect(0, 0).unwrap_or(xdp_action::XDP_ABORTED) } #[xdp(map = "cpumap")] diff --git a/xtask/public-api/aya-bpf.txt b/xtask/public-api/aya-bpf.txt index b7de5541..4121839f 100644 --- a/xtask/public-api/aya-bpf.txt +++ b/xtask/public-api/aya-bpf.txt @@ -567,7 +567,7 @@ pub mod aya_bpf::maps::xdp #[repr(transparent)] pub struct aya_bpf::maps::xdp::CpuMap impl aya_bpf::maps::CpuMap pub const fn aya_bpf::maps::CpuMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::CpuMap -pub fn aya_bpf::maps::CpuMap::redirect(&self, index: u32, flags: u64) -> u32 +pub fn aya_bpf::maps::CpuMap::redirect(&self, index: u32, flags: u64) -> core::result::Result pub const fn aya_bpf::maps::CpuMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::CpuMap impl core::marker::Sync for aya_bpf::maps::CpuMap impl core::marker::Send for aya_bpf::maps::CpuMap @@ -594,7 +594,7 @@ pub fn aya_bpf::maps::CpuMap::from(t: T) -> T impl aya_bpf::maps::DevMap pub fn aya_bpf::maps::DevMap::get(&self, index: u32) -> core::option::Option pub const fn aya_bpf::maps::DevMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMap -pub fn aya_bpf::maps::DevMap::redirect(&self, index: u32, flags: u64) -> u32 +pub fn aya_bpf::maps::DevMap::redirect(&self, index: u32, flags: u64) -> core::result::Result pub const fn aya_bpf::maps::DevMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMap impl core::marker::Sync for aya_bpf::maps::DevMap impl core::marker::Send for aya_bpf::maps::DevMap @@ -621,7 +621,7 @@ pub fn aya_bpf::maps::DevMap::from(t: T) -> T impl aya_bpf::maps::DevMapHash pub fn aya_bpf::maps::DevMapHash::get(&self, key: u32) -> core::option::Option pub const fn aya_bpf::maps::DevMapHash::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash -pub fn aya_bpf::maps::DevMapHash::redirect(&self, key: u32, flags: u64) -> u32 +pub fn aya_bpf::maps::DevMapHash::redirect(&self, index: u32, flags: u64) -> core::result::Result pub const fn aya_bpf::maps::DevMapHash::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash impl core::marker::Sync for aya_bpf::maps::DevMapHash impl core::marker::Send for aya_bpf::maps::DevMapHash @@ -648,7 +648,7 @@ pub fn aya_bpf::maps::DevMapHash::from(t: T) -> T impl aya_bpf::maps::XskMap pub fn aya_bpf::maps::XskMap::get(&self, index: u32) -> core::option::Option pub const fn aya_bpf::maps::XskMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::XskMap -pub fn aya_bpf::maps::XskMap::redirect(&self, index: u32, flags: u64) -> u32 +pub fn aya_bpf::maps::XskMap::redirect(&self, index: u32, flags: u64) -> core::result::Result pub const fn aya_bpf::maps::XskMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::XskMap impl core::marker::Sync for aya_bpf::maps::XskMap impl core::marker::Send for aya_bpf::maps::XskMap @@ -729,7 +729,7 @@ pub fn aya_bpf::maps::bloom_filter::BloomFilter::from(t: T) -> T #[repr(transparent)] pub struct aya_bpf::maps::CpuMap impl aya_bpf::maps::CpuMap pub const fn aya_bpf::maps::CpuMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::CpuMap -pub fn aya_bpf::maps::CpuMap::redirect(&self, index: u32, flags: u64) -> u32 +pub fn aya_bpf::maps::CpuMap::redirect(&self, index: u32, flags: u64) -> core::result::Result pub const fn aya_bpf::maps::CpuMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::CpuMap impl core::marker::Sync for aya_bpf::maps::CpuMap impl core::marker::Send for aya_bpf::maps::CpuMap @@ -756,7 +756,7 @@ pub fn aya_bpf::maps::CpuMap::from(t: T) -> T impl aya_bpf::maps::DevMap pub fn aya_bpf::maps::DevMap::get(&self, index: u32) -> core::option::Option pub const fn aya_bpf::maps::DevMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMap -pub fn aya_bpf::maps::DevMap::redirect(&self, index: u32, flags: u64) -> u32 +pub fn aya_bpf::maps::DevMap::redirect(&self, index: u32, flags: u64) -> core::result::Result pub const fn aya_bpf::maps::DevMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMap impl core::marker::Sync for aya_bpf::maps::DevMap impl core::marker::Send for aya_bpf::maps::DevMap @@ -783,7 +783,7 @@ pub fn aya_bpf::maps::DevMap::from(t: T) -> T impl aya_bpf::maps::DevMapHash pub fn aya_bpf::maps::DevMapHash::get(&self, key: u32) -> core::option::Option pub const fn aya_bpf::maps::DevMapHash::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash -pub fn aya_bpf::maps::DevMapHash::redirect(&self, key: u32, flags: u64) -> u32 +pub fn aya_bpf::maps::DevMapHash::redirect(&self, index: u32, flags: u64) -> core::result::Result pub const fn aya_bpf::maps::DevMapHash::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash impl core::marker::Sync for aya_bpf::maps::DevMapHash impl core::marker::Send for aya_bpf::maps::DevMapHash @@ -1206,7 +1206,7 @@ pub fn aya_bpf::maps::stack_trace::StackTrace::from(t: T) -> T impl aya_bpf::maps::XskMap pub fn aya_bpf::maps::XskMap::get(&self, index: u32) -> core::option::Option pub const fn aya_bpf::maps::XskMap::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::XskMap -pub fn aya_bpf::maps::XskMap::redirect(&self, index: u32, flags: u64) -> u32 +pub fn aya_bpf::maps::XskMap::redirect(&self, index: u32, flags: u64) -> core::result::Result pub const fn aya_bpf::maps::XskMap::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::XskMap impl core::marker::Sync for aya_bpf::maps::XskMap impl core::marker::Send for aya_bpf::maps::XskMap From 0edc13b4d4b87809b41b1a14101962a7defba65e Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Mon, 25 Sep 2023 00:33:17 +0200 Subject: [PATCH 16/16] bpf: add a shared try_redirect_map function for XDP maps --- bpf/aya-bpf/src/maps/xdp/cpu_map.rs | 14 ++++---------- bpf/aya-bpf/src/maps/xdp/dev_map.rs | 15 +++++---------- bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs | 17 +++++------------ bpf/aya-bpf/src/maps/xdp/mod.rs | 24 ++++++++++++++++++++++++ bpf/aya-bpf/src/maps/xdp/xsk_map.rs | 15 +++++---------- xtask/public-api/aya-bpf.txt | 4 ++-- 6 files changed, 45 insertions(+), 44 deletions(-) diff --git a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs index 0179382c..133be94b 100644 --- a/bpf/aya-bpf/src/maps/xdp/cpu_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/cpu_map.rs @@ -1,13 +1,14 @@ use core::{cell::UnsafeCell, mem}; -use aya_bpf_bindings::bindings::{bpf_cpumap_val, xdp_action::XDP_REDIRECT}; +use aya_bpf_bindings::bindings::bpf_cpumap_val; use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_CPUMAP}, - helpers::bpf_redirect_map, maps::PinningType, }; +use super::try_redirect_map; + /// An array of available CPUs. /// /// XDP programs can use this map to redirect packets to a target CPU for processing. @@ -114,13 +115,6 @@ impl CpuMap { /// ``` #[inline(always)] pub fn redirect(&self, index: u32, flags: u64) -> Result { - let ret = unsafe { bpf_redirect_map(self.def.get() as *mut _, index.into(), flags) }; - match ret.unsigned_abs() as u32 { - XDP_REDIRECT => Ok(XDP_REDIRECT), - // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags - // argument on error. Thus I have no idea why it returns a long (i64) instead of - // something saner, hence the unsigned_abs. - ret => Err(ret), - } + try_redirect_map(&self.def, index, flags) } } diff --git a/bpf/aya-bpf/src/maps/xdp/dev_map.rs b/bpf/aya-bpf/src/maps/xdp/dev_map.rs index 6142d151..cfa44cb3 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map.rs @@ -1,14 +1,16 @@ use core::{cell::UnsafeCell, mem, num::NonZeroU32, ptr::NonNull}; -use aya_bpf_bindings::bindings::{bpf_devmap_val, xdp_action::XDP_REDIRECT}; +use aya_bpf_bindings::bindings::bpf_devmap_val; use aya_bpf_cty::c_void; use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_DEVMAP}, - helpers::{bpf_map_lookup_elem, bpf_redirect_map}, + helpers::bpf_map_lookup_elem, maps::PinningType, }; +use super::try_redirect_map; + /// An array of network devices. /// /// XDP programs can use this map to redirect packets to other network deviecs. @@ -139,14 +141,7 @@ impl DevMap { /// ``` #[inline(always)] pub fn redirect(&self, index: u32, flags: u64) -> Result { - let ret = unsafe { bpf_redirect_map(self.def.get() as *mut _, index.into(), flags) }; - match ret.unsigned_abs() as u32 { - XDP_REDIRECT => Ok(XDP_REDIRECT), - // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags - // argument on error. Thus I have no idea why it returns a long (i64) instead of - // something saner, hence the unsigned_abs. - ret => Err(ret), - } + try_redirect_map(&self.def, index, 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 eb65b105..cbec50bb 100644 --- a/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs +++ b/bpf/aya-bpf/src/maps/xdp/dev_map_hash.rs @@ -1,15 +1,15 @@ use core::{cell::UnsafeCell, mem, num::NonZeroU32, ptr::NonNull}; -use aya_bpf_bindings::bindings::{bpf_devmap_val, xdp_action::XDP_REDIRECT}; +use aya_bpf_bindings::bindings::bpf_devmap_val; use aya_bpf_cty::c_void; use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH}, - helpers::{bpf_map_lookup_elem, bpf_redirect_map}, + helpers::bpf_map_lookup_elem, maps::PinningType, }; -use super::dev_map::DevMapValue; +use super::{dev_map::DevMapValue, try_redirect_map}; /// A map of network devices. /// @@ -140,14 +140,7 @@ impl DevMapHash { /// } /// ``` #[inline(always)] - pub fn redirect(&self, index: u32, flags: u64) -> Result { - let ret = unsafe { bpf_redirect_map(self.def.get() as *mut _, index.into(), flags) }; - match ret.unsigned_abs() as u32 { - XDP_REDIRECT => Ok(XDP_REDIRECT), - // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags - // argument on error. Thus I have no idea why it returns a long (i64) instead of - // something saner, hence the unsigned_abs. - ret => Err(ret), - } + pub fn redirect(&self, key: u32, flags: u64) -> Result { + try_redirect_map(&self.def, key, flags) } } diff --git a/bpf/aya-bpf/src/maps/xdp/mod.rs b/bpf/aya-bpf/src/maps/xdp/mod.rs index b7ced7b5..5c8df0e5 100644 --- a/bpf/aya-bpf/src/maps/xdp/mod.rs +++ b/bpf/aya-bpf/src/maps/xdp/mod.rs @@ -3,7 +3,31 @@ mod dev_map; mod dev_map_hash; mod xsk_map; +use core::cell::UnsafeCell; + +use aya_bpf_bindings::{ + bindings::{bpf_map_def, xdp_action::XDP_REDIRECT}, + helpers::bpf_redirect_map, +}; pub use cpu_map::CpuMap; pub use dev_map::DevMap; pub use dev_map_hash::DevMapHash; pub use xsk_map::XskMap; + +/// Wrapper aroung the `bpf_redirect_map` function. +/// +/// # Return value +/// +/// - `Ok(XDP_REDIRECT)` on success. +/// - `Err(_)` of the lowest two bits of `flags` on failure. +#[inline(always)] +fn try_redirect_map(def: &UnsafeCell, key: u32, flags: u64) -> Result { + // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags argument on + // error. Thus I have no idea why it returns a long (i64) instead of something saner, hence the + // unsigned_abs. + let ret = unsafe { bpf_redirect_map(def.get() as *mut _, key.into(), flags) }; + match ret.unsigned_abs() as u32 { + XDP_REDIRECT => Ok(XDP_REDIRECT), + ret => Err(ret), + } +} diff --git a/bpf/aya-bpf/src/maps/xdp/xsk_map.rs b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs index f4546edb..455dfc84 100644 --- a/bpf/aya-bpf/src/maps/xdp/xsk_map.rs +++ b/bpf/aya-bpf/src/maps/xdp/xsk_map.rs @@ -1,14 +1,16 @@ use core::{cell::UnsafeCell, mem, ptr::NonNull}; -use aya_bpf_bindings::bindings::{bpf_xdp_sock, xdp_action::XDP_REDIRECT}; +use aya_bpf_bindings::bindings::bpf_xdp_sock; use aya_bpf_cty::c_void; use crate::{ bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_XSKMAP}, - helpers::{bpf_map_lookup_elem, bpf_redirect_map}, + helpers::bpf_map_lookup_elem, maps::PinningType, }; +use super::try_redirect_map; + /// An array of AF_XDP sockets. /// /// XDP programs can use this map to redirect packets to a target AF_XDP socket using the @@ -157,13 +159,6 @@ impl XskMap { /// ``` #[inline(always)] pub fn redirect(&self, index: u32, flags: u64) -> Result { - let ret = unsafe { bpf_redirect_map(self.def.get() as *mut _, index.into(), flags) }; - match ret.unsigned_abs() as u32 { - XDP_REDIRECT => Ok(XDP_REDIRECT), - // Return XDP_REDIRECT on success, or the value of the two lower bits of the flags - // argument on error. Thus I have no idea why it returns a long (i64) instead of - // something saner, hence the unsigned_abs. - ret => Err(ret), - } + try_redirect_map(&self.def, index, flags) } } diff --git a/xtask/public-api/aya-bpf.txt b/xtask/public-api/aya-bpf.txt index 4121839f..e221d2cc 100644 --- a/xtask/public-api/aya-bpf.txt +++ b/xtask/public-api/aya-bpf.txt @@ -621,7 +621,7 @@ pub fn aya_bpf::maps::DevMap::from(t: T) -> T impl aya_bpf::maps::DevMapHash pub fn aya_bpf::maps::DevMapHash::get(&self, key: u32) -> core::option::Option pub const fn aya_bpf::maps::DevMapHash::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash -pub fn aya_bpf::maps::DevMapHash::redirect(&self, index: u32, flags: u64) -> core::result::Result +pub fn aya_bpf::maps::DevMapHash::redirect(&self, key: u32, flags: u64) -> core::result::Result pub const fn aya_bpf::maps::DevMapHash::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash impl core::marker::Sync for aya_bpf::maps::DevMapHash impl core::marker::Send for aya_bpf::maps::DevMapHash @@ -783,7 +783,7 @@ pub fn aya_bpf::maps::DevMap::from(t: T) -> T impl aya_bpf::maps::DevMapHash pub fn aya_bpf::maps::DevMapHash::get(&self, key: u32) -> core::option::Option pub const fn aya_bpf::maps::DevMapHash::pinned(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash -pub fn aya_bpf::maps::DevMapHash::redirect(&self, index: u32, flags: u64) -> core::result::Result +pub fn aya_bpf::maps::DevMapHash::redirect(&self, key: u32, flags: u64) -> core::result::Result pub const fn aya_bpf::maps::DevMapHash::with_max_entries(max_entries: u32, flags: u32) -> aya_bpf::maps::DevMapHash impl core::marker::Sync for aya_bpf::maps::DevMapHash impl core::marker::Send for aya_bpf::maps::DevMapHash