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