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.
reviewable/pr527/r18
Tuetuopay 1 year ago
parent 63ce2f013a
commit 00dc7a5bd4

@ -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 /// Returns the max entry number
pub fn max_entries(&self) -> u32 { pub fn max_entries(&self) -> u32 {
match self { match self {

@ -48,17 +48,24 @@ pub struct Features {
bpf_perf_link: bool, bpf_perf_link: bool,
bpf_global_data: bool, bpf_global_data: bool,
bpf_cookie: bool, bpf_cookie: bool,
cpumap_prog_id: bool,
devmap_prog_id: bool,
devmap_hash_prog_id: bool,
btf: Option<BtfFeatures>, btf: Option<BtfFeatures>,
} }
impl Features { impl Features {
#[doc(hidden)] #[doc(hidden)]
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
bpf_name: bool, bpf_name: bool,
bpf_probe_read_kernel: bool, bpf_probe_read_kernel: bool,
bpf_perf_link: bool, bpf_perf_link: bool,
bpf_global_data: bool, bpf_global_data: bool,
bpf_cookie: bool, bpf_cookie: bool,
cpumap_prog_id: bool,
devmap_prog_id: bool,
devmap_hash_prog_id: bool,
btf: Option<BtfFeatures>, btf: Option<BtfFeatures>,
) -> Self { ) -> Self {
Self { Self {
@ -67,6 +74,9 @@ impl Features {
bpf_perf_link, bpf_perf_link,
bpf_global_data, bpf_global_data,
bpf_cookie, bpf_cookie,
cpumap_prog_id,
devmap_prog_id,
devmap_hash_prog_id,
btf, btf,
} }
} }
@ -96,6 +106,21 @@ impl Features {
self.bpf_cookie 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. /// If BTF is supported, returns which BTF features are supported.
pub fn btf(&self) -> Option<&BtfFeatures> { pub fn btf(&self) -> Option<&BtfFeatures> {
self.btf.as_ref() self.btf.as_ref()

@ -12,6 +12,7 @@ edition = "2021"
rust-version = "1.66" rust-version = "1.66"
[dependencies] [dependencies]
assert_matches = { workspace = true }
async-io = { workspace = true, optional = true } async-io = { workspace = true, optional = true }
aya-obj = { workspace = true, features = ["std"] } aya-obj = { workspace = true, features = ["std"] }
bitflags = { workspace = true } bitflags = { workspace = true }
@ -28,7 +29,6 @@ thiserror = { workspace = true }
tokio = { workspace = true, features = ["rt"], optional = true } tokio = { workspace = true, features = ["rt"], optional = true }
[dev-dependencies] [dev-dependencies]
assert_matches = { workspace = true }
tempfile = { workspace = true } tempfile = { workspace = true }
[features] [features]

@ -40,7 +40,8 @@ use crate::{
is_btf_datasec_supported, is_btf_decl_tag_supported, is_btf_enum64_supported, 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_float_supported, is_btf_func_global_supported, is_btf_func_supported,
is_btf_supported, is_btf_type_tag_supported, is_perf_link_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}, util::{bytes_of, bytes_of_slice, possible_cpus, POSSIBLE_CPUS},
}; };
@ -93,6 +94,9 @@ fn detect_features() -> Features {
is_perf_link_supported(), is_perf_link_supported(),
is_bpf_global_data_supported(), is_bpf_global_data_supported(),
is_bpf_cookie_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, btf,
); );
debug!("BPF Feature Detection: {:#?}", f); 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 btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd());
let mut map = match obj.pinning() { let mut map = match obj.pinning() {
PinningType::None => MapData::create(obj, &name, btf_fd)?, PinningType::None => MapData::create(obj, &name, btf_fd)?,

@ -181,6 +181,10 @@ pub enum MapError {
error: PinError, error: PinError,
}, },
/// Program IDs are not supported
#[error("program ids are not supported by the current kernel")]
ProgIdNotSupported,
/// Unsupported Map type /// Unsupported Map type
#[error("Unsupported map type found {map_type}")] #[error("Unsupported map type found {map_type}")]
Unsupported { Unsupported {

@ -12,7 +12,7 @@ use crate::{
maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError},
programs::ProgramFd, programs::ProgramFd,
sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError},
Pod, Pod, FEATURES,
}; };
/// An array of available CPUs. /// An array of available CPUs.
@ -50,7 +50,12 @@ pub struct CpuMap<T> {
impl<T: Borrow<MapData>> CpuMap<T> { impl<T: Borrow<MapData>> CpuMap<T> {
pub(crate) fn new(map: T) -> Result<Self, MapError> { pub(crate) fn new(map: T) -> Result<Self, MapError> {
let data = map.borrow(); let data = map.borrow();
check_kv_size::<u32, bpf_cpumap_val>(data)?;
if FEATURES.cpumap_prog_id() {
check_kv_size::<u32, bpf_cpumap_val>(data)?;
} else {
check_kv_size::<u32, u32>(data)?;
}
Ok(Self { inner: map }) Ok(Self { inner: map })
} }
@ -73,19 +78,29 @@ impl<T: Borrow<MapData>> CpuMap<T> {
check_bounds(data, cpu_index)?; check_bounds(data, cpu_index)?;
let fd = data.fd().as_fd(); let fd = data.fd().as_fd();
let value = let value = if FEATURES.cpumap_prog_id() {
bpf_map_lookup_elem(fd, &cpu_index, flags).map_err(|(_, io_error)| SyscallError { 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", call: "bpf_map_lookup_elem",
io_error, io_error,
})?; })?
let value: bpf_cpumap_val = value.ok_or(MapError::KeyNotFound)?; .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. /// An iterator over the elements of the map.
@ -111,7 +126,8 @@ impl<T: BorrowMut<MapData>> CpuMap<T> {
/// # Errors /// # Errors
/// ///
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
/// if `bpf_map_update_elem` fails. /// if `bpf_map_update_elem` fails, [`MapError::ProgIdNotSupported`] if the kernel does not
/// support program ids and one is provided.
pub fn set( pub fn set(
&mut self, &mut self,
cpu_index: u32, cpu_index: u32,
@ -123,21 +139,28 @@ impl<T: BorrowMut<MapData>> CpuMap<T> {
check_bounds(data, cpu_index)?; check_bounds(data, cpu_index)?;
let fd = data.fd().as_fd(); let fd = data.fd().as_fd();
let value = bpf_cpumap_val { let res = if FEATURES.cpumap_prog_id() {
qsize: queue_size, let value = bpf_cpumap_val {
bpf_prog: bpf_cpumap_val__bindgen_ty_1 { qsize: queue_size,
// Default is valid as the kernel will only consider fd > 0: bpf_prog: bpf_cpumap_val__bindgen_ty_1 {
// https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/cpumap.c#L466 // Default is valid as the kernel will only consider fd > 0:
fd: program // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/cpumap.c#L466
.map(|prog| prog.as_fd().as_raw_fd()) fd: program
.unwrap_or_default(), .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 { bpf_map_update_elem(fd, Some(&cpu_index), &value, flags)
call: "bpf_map_update_elem", } else {
io_error, 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(()) Ok(())
} }

@ -12,7 +12,7 @@ use crate::{
maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError}, maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError},
programs::ProgramFd, programs::ProgramFd,
sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError}, sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError},
Pod, Pod, FEATURES,
}; };
/// An array of network devices. /// An array of network devices.
@ -44,7 +44,12 @@ pub struct DevMap<T> {
impl<T: Borrow<MapData>> DevMap<T> { impl<T: Borrow<MapData>> DevMap<T> {
pub(crate) fn new(map: T) -> Result<Self, MapError> { pub(crate) fn new(map: T) -> Result<Self, MapError> {
let data = map.borrow(); let data = map.borrow();
check_kv_size::<u32, bpf_devmap_val>(data)?;
if FEATURES.devmap_prog_id() {
check_kv_size::<u32, bpf_devmap_val>(data)?;
} else {
check_kv_size::<u32, u32>(data)?;
}
Ok(Self { inner: map }) Ok(Self { inner: map })
} }
@ -67,19 +72,29 @@ impl<T: Borrow<MapData>> DevMap<T> {
check_bounds(data, index)?; check_bounds(data, index)?;
let fd = data.fd().as_fd(); let fd = data.fd().as_fd();
let value = let value = if FEATURES.devmap_prog_id() {
bpf_map_lookup_elem(fd, &index, flags).map_err(|(_, io_error)| SyscallError { 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", call: "bpf_map_lookup_elem",
io_error, io_error,
})?; })?
let value: bpf_devmap_val = value.ok_or(MapError::KeyNotFound)?; .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. /// An iterator over the elements of the array.
@ -104,7 +119,8 @@ impl<T: BorrowMut<MapData>> DevMap<T> {
/// # Errors /// # Errors
/// ///
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
/// if `bpf_map_update_elem` fails. /// if `bpf_map_update_elem` fails, [`MapError::ProgIdNotSupported`] if the kernel does not
/// support program ids and one is provided.
pub fn set( pub fn set(
&mut self, &mut self,
index: u32, index: u32,
@ -116,22 +132,29 @@ impl<T: BorrowMut<MapData>> DevMap<T> {
check_bounds(data, index)?; check_bounds(data, index)?;
let fd = data.fd().as_fd(); let fd = data.fd().as_fd();
let value = bpf_devmap_val { let res = if FEATURES.devmap_prog_id() {
ifindex, let value = bpf_devmap_val {
bpf_prog: bpf_devmap_val__bindgen_ty_1 { ifindex,
// Default is valid as the kernel will only consider fd > 0: bpf_prog: bpf_devmap_val__bindgen_ty_1 {
// https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L866 // Default is valid as the kernel will only consider fd > 0:
// https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918 // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L866
fd: program // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918
.map(|prog| prog.as_fd().as_raw_fd()) fd: program
.unwrap_or_default(), .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 { bpf_map_update_elem(fd, Some(&index), &value, flags)
call: "bpf_map_update_elem", } else {
io_error, 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(()) Ok(())
} }

@ -12,6 +12,7 @@ use crate::{
maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys}, maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys},
programs::ProgramFd, programs::ProgramFd,
sys::{bpf_map_lookup_elem, SyscallError}, sys::{bpf_map_lookup_elem, SyscallError},
FEATURES,
}; };
use super::dev_map::DevMapValue; use super::dev_map::DevMapValue;
@ -45,7 +46,12 @@ pub struct DevMapHash<T> {
impl<T: Borrow<MapData>> DevMapHash<T> { impl<T: Borrow<MapData>> DevMapHash<T> {
pub(crate) fn new(map: T) -> Result<Self, MapError> { pub(crate) fn new(map: T) -> Result<Self, MapError> {
let data = map.borrow(); let data = map.borrow();
check_kv_size::<u32, bpf_devmap_val>(data)?;
if FEATURES.devmap_hash_prog_id() {
check_kv_size::<u32, bpf_devmap_val>(data)?;
} else {
check_kv_size::<u32, u32>(data)?;
}
Ok(Self { inner: map }) Ok(Self { inner: map })
} }
@ -57,18 +63,30 @@ impl<T: Borrow<MapData>> DevMapHash<T> {
/// Returns [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails. /// Returns [`MapError::SyscallError`] if `bpf_map_lookup_elem` fails.
pub fn get(&self, key: u32, flags: u64) -> Result<DevMapValue, MapError> { pub fn get(&self, key: u32, flags: u64) -> Result<DevMapValue, MapError> {
let fd = self.inner.borrow().fd().as_fd(); 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", let value = if FEATURES.devmap_hash_prog_id() {
io_error, bpf_map_lookup_elem::<_, bpf_devmap_val>(fd, &key, flags).map(|value| {
})?; value.map(|value| DevMapValue {
let value: bpf_devmap_val = value.ok_or(MapError::KeyNotFound)?; ifindex: value.ifindex,
// SAFETY: map writes use fd, map reads use id.
// SAFETY: map writes use fd, map reads use id. // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6228
// https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/include/uapi/linux/bpf.h#L6228 prog_id: NonZeroU32::new(unsafe { value.bpf_prog.id }),
Ok(DevMapValue { })
ifindex: value.ifindex, })
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. /// An iterator over the elements of the devmap in arbitrary order.
@ -96,7 +114,9 @@ impl<T: BorrowMut<MapData>> DevMapHash<T> {
/// ///
/// # Errors /// # 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( pub fn insert(
&mut self, &mut self,
key: u32, key: u32,
@ -104,18 +124,25 @@ impl<T: BorrowMut<MapData>> DevMapHash<T> {
program: Option<&ProgramFd>, program: Option<&ProgramFd>,
flags: u64, flags: u64,
) -> Result<(), MapError> { ) -> Result<(), MapError> {
let value = bpf_devmap_val { if FEATURES.devmap_hash_prog_id() {
ifindex, let value = bpf_devmap_val {
bpf_prog: bpf_devmap_val__bindgen_ty_1 { ifindex,
// Default is valid as the kernel will only consider fd > 0: bpf_prog: bpf_devmap_val__bindgen_ty_1 {
// https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L866 // Default is valid as the kernel will only consider fd > 0:
// https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918 // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L866
fd: program // https://github.com/torvalds/linux/blob/2dde18cd1d8fac735875f2e4987f11817cc0bc2c/kernel/bpf/devmap.c#L918
.map(|prog| prog.as_fd().as_raw_fd()) fd: program
.unwrap_or_default(), .map(|prog| prog.as_fd().as_raw_fd())
}, .unwrap_or_default(),
}; },
hash_map::insert(self.inner.borrow_mut(), &key, &value, flags) };
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. /// Removes a value from the map.

@ -8,6 +8,7 @@ use std::{
}; };
use crate::util::KernelVersion; use crate::util::KernelVersion;
use assert_matches::assert_matches;
use libc::{c_char, c_long, ENOENT, ENOSPC}; use libc::{c_char, c_long, ENOENT, ENOSPC};
use obj::{ use obj::{
btf::{BtfEnum64, Enum64}, btf::{BtfEnum64, Enum64},
@ -793,6 +794,28 @@ pub(crate) fn is_bpf_cookie_supported() -> bool {
bpf_prog_load(&mut attr).is_ok() 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::<bpf_attr>() };
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 { pub(crate) fn is_btf_supported() -> bool {
let mut btf = Btf::new(); let mut btf = Btf::new();
let name_offset = btf.add_string("int"); let name_offset = btf.add_string("int");
@ -1072,4 +1095,28 @@ mod tests {
let supported = is_perf_link_supported(); let supported = is_perf_link_supported();
assert!(!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);
}
} }

Loading…
Cancel
Save