aya, bpf: misc fixes following review comments

reviewable/pr527/r17
Tuetuopay 2 years ago
parent 46551de3e7
commit b1cfce48dd

@ -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<u32, u32> {
/// Ok(XDP_PASS)
/// XDP_PASS
/// }
/// ```
#[proc_macro_error]

@ -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,

@ -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<BtfFeatures>,
}
@ -65,7 +64,6 @@ impl Features {
bpf_cookie: bool,
cpumap_prog_id: bool,
devmap_prog_id: bool,
devmap_hash_prog_id: bool,
btf: Option<BtfFeatures>,
) -> 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()

@ -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());

@ -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: <https://docs.kernel.org/next/bpf/map_cpumap.html>
#[doc(alias = "BPF_MAP_TYPE_CPUMAP")]
pub struct CpuMap<T> {
inner: T,
@ -67,7 +74,7 @@ impl<T: Borrow<MapData>> CpuMap<T> {
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<T: Borrow<MapData>> CpuMap<T> {
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<T: Borrow<MapData>> CpuMap<T> {
} 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<T: BorrowMut<MapData>> CpuMap<T> {
/// 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.
/// Note that, when using `aya-ebpf`, only XDP programs with the `map = "cpumap"` argument can
/// be passed. See the kernel-space `aya_ebpf::xdp` for more information. Otherwise, the
/// program must be loaded with the `BPF_XDP_CPUMAP` attach type.
///
/// # 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::<bpf_cpumap_val>() };
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(())
}
@ -180,6 +187,6 @@ unsafe impl Pod for bpf_cpumap_val {}
#[derive(Clone, Copy, Debug)]
pub struct CpuMapValue {
pub qsize: u32,
pub queue_size: u32,
pub prog_id: Option<NonZeroU32>,
}

@ -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: <https://docs.kernel.org/next/bpf/map_devmap.html>
#[doc(alias = "BPF_MAP_TYPE_DEVMAP")]
pub struct DevMap<T> {
inner: T,
@ -61,7 +66,7 @@ impl<T: Borrow<MapData>> DevMap<T> {
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<T: Borrow<MapData>> DevMap<T> {
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<T: Borrow<MapData>> DevMap<T> {
} 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<T: Borrow<MapData>> DevMap<T> {
}
impl<T: BorrowMut<MapData>> DevMap<T> {
/// 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.
/// Note that, when using `aya-ebpf`, only XDP programs with the `map = "devmap"` argument can
/// be passed. See the kernel-space `aya_ebpf::xdp` for more information. Otherwise, the
/// program must be loaded with the `BPF_XDP_DEVMAP` attach type.
///
/// # 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::<bpf_devmap_val>() };
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(())
}
@ -174,6 +179,6 @@ unsafe impl Pod for bpf_devmap_val {}
#[derive(Clone, Copy, Debug)]
pub struct DevMapValue {
pub ifindex: u32,
pub if_index: u32,
pub prog_id: Option<NonZeroU32>,
}

@ -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: <https://docs.kernel.org/next/bpf/map_devmap.html>
#[doc(alias = "BPF_MAP_TYPE_DEVMAP_HASH")]
pub struct DevMapHash<T> {
inner: T,
@ -47,7 +50,7 @@ impl<T: Borrow<MapData>> DevMapHash<T> {
pub(crate) fn new(map: T) -> Result<Self, MapError> {
let data = map.borrow();
if FEATURES.devmap_hash_prog_id() {
if FEATURES.devmap_prog_id() {
check_kv_size::<u32, bpf_devmap_val>(data)?;
} else {
check_kv_size::<u32, u32>(data)?;
@ -56,7 +59,7 @@ impl<T: Borrow<MapData>> DevMapHash<T> {
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<T: Borrow<MapData>> DevMapHash<T> {
pub fn get(&self, key: u32, flags: u64) -> Result<DevMapValue, MapError> {
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<T: Borrow<MapData>> DevMapHash<T> {
} 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<T: BorrowMut<MapData>> DevMapHash<T> {
///
/// 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.
/// Note that, when using `aya-ebpf`, only XDP programs with the `map = "devmap"` argument can
/// be passed. See the kernel-space `aya_ebpf::xdp` for more information. Otherwise, the
/// program must be loaded with the `BPF_XDP_DEVMAP` attach type.
///
/// # 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::<bpf_devmap_val>() };
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.

@ -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),
}

@ -30,6 +30,10 @@ use crate::{
/// xskmap.set(0, socket_fd, 0);
/// # Ok::<(), aya::BpfError>(())
/// ```
///
/// # See also
///
/// Kernel documentation: <https://docs.kernel.org/next/bpf/map_xskmap.html>
#[doc(alias = "BPF_MAP_TYPE_XSKMAP")]
pub struct XskMap<T> {
inner: T,

@ -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
///

Loading…
Cancel
Save