mirror of https://github.com/aya-rs/aya
Merge 0bf64aa0d1
into 756d8172f5
commit
e10f0a650e
@ -0,0 +1,308 @@
|
|||||||
|
//! Probes and identifies available eBPF features supported by the host kernel.
|
||||||
|
|
||||||
|
use std::{mem, os::fd::AsRawFd as _};
|
||||||
|
|
||||||
|
use aya_obj::{
|
||||||
|
btf::{Btf, BtfKind},
|
||||||
|
generated::{
|
||||||
|
BPF_F_MMAPABLE, BPF_F_NO_PREALLOC, bpf_attr, bpf_cmd, bpf_map_type, bpf_prog_info,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use libc::{E2BIG, EBADF, EINVAL};
|
||||||
|
|
||||||
|
use super::{SyscallError, bpf_prog_load, fd_sys_bpf, unit_sys_bpf, with_trivial_prog};
|
||||||
|
use crate::{
|
||||||
|
MockableFd,
|
||||||
|
maps::MapType,
|
||||||
|
programs::{ProgramError, ProgramType},
|
||||||
|
util::page_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Whether the host kernel supports the [`ProgramType`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use aya::{
|
||||||
|
/// # programs::ProgramType,
|
||||||
|
/// # sys::feature_probe::is_program_supported,
|
||||||
|
/// # };
|
||||||
|
/// #
|
||||||
|
/// match is_program_supported(ProgramType::Xdp) {
|
||||||
|
/// Ok(true) => println!("XDP supported :)"),
|
||||||
|
/// Ok(false) => println!("XDP not supported :("),
|
||||||
|
/// Err(err) => println!("Uh oh! Unexpected error while probing: {:?}", err),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`ProgramError::SyscallError`] if a syscall fails with an unexpected
|
||||||
|
/// error, or [`ProgramError::Btf`] for BTF related errors.
|
||||||
|
///
|
||||||
|
/// Certain errors are expected and handled internally; only unanticipated
|
||||||
|
/// failures during probing will result in these errors.
|
||||||
|
pub fn is_program_supported(program_type: ProgramType) -> Result<bool, ProgramError> {
|
||||||
|
if program_type == ProgramType::Unspecified {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut verifier_log = [0_u8; libc::PATH_MAX as usize];
|
||||||
|
let attach_btf_id = if matches!(program_type, ProgramType::Tracing | ProgramType::Lsm) {
|
||||||
|
let func_name = if program_type == ProgramType::Tracing {
|
||||||
|
"bpf_fentry_test1"
|
||||||
|
} else {
|
||||||
|
"bpf_lsm_bpf"
|
||||||
|
};
|
||||||
|
Btf::from_sys_fs()
|
||||||
|
.and_then(|btf| btf.id_by_type_name_kind(func_name, BtfKind::Func))
|
||||||
|
.unwrap_or(0)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let error = match with_trivial_prog(program_type, |attr| {
|
||||||
|
// SAFETY: union access
|
||||||
|
let u = unsafe { &mut attr.__bindgen_anon_3 };
|
||||||
|
|
||||||
|
u.attach_btf_id = attach_btf_id;
|
||||||
|
match program_type {
|
||||||
|
ProgramType::Tracing | ProgramType::Extension | ProgramType::Lsm => {
|
||||||
|
u.log_buf = verifier_log.as_mut_ptr() as u64;
|
||||||
|
u.log_level = 1;
|
||||||
|
u.log_size = verifier_log.len() as u32;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
bpf_prog_load(attr).err().map(|io_error| {
|
||||||
|
ProgramError::SyscallError(SyscallError {
|
||||||
|
call: "bpf_prog_load",
|
||||||
|
io_error,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
Some(err) => err,
|
||||||
|
None => return Ok(true),
|
||||||
|
};
|
||||||
|
|
||||||
|
match &error {
|
||||||
|
ProgramError::SyscallError(err) => {
|
||||||
|
match err.io_error.raw_os_error() {
|
||||||
|
Some(EINVAL) => {
|
||||||
|
// verifier/`bpf_check_attach_target()` (or `check_attach_btf_id()` on older
|
||||||
|
// kernels) produces this log message for these prog types if `attach_btf_id`
|
||||||
|
// is unset
|
||||||
|
let supported = matches!(
|
||||||
|
program_type,
|
||||||
|
ProgramType::Tracing | ProgramType::Extension | ProgramType::Lsm
|
||||||
|
if verifier_log.starts_with(b"Tracing programs must provide btf_id"));
|
||||||
|
Ok(supported)
|
||||||
|
}
|
||||||
|
Some(E2BIG) => Ok(false),
|
||||||
|
// `ENOTSUPP` from verifier/`check_struct_ops_btf_id()` for struct_ops
|
||||||
|
Some(524) if program_type == ProgramType::StructOps => Ok(true),
|
||||||
|
_ => Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the host kernel supports the [`MapType`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use aya::{
|
||||||
|
/// # maps::MapType,
|
||||||
|
/// # sys::feature_probe::is_map_supported,
|
||||||
|
/// # };
|
||||||
|
/// #
|
||||||
|
/// match is_map_supported(MapType::HashOfMaps) {
|
||||||
|
/// Ok(true) => println!("hash_of_maps supported :)"),
|
||||||
|
/// Ok(false) => println!("hash_of_maps not supported :("),
|
||||||
|
/// Err(err) => println!("Uh oh! Unexpected error while probing: {:?}", err),
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`SyscallError`] if kernel probing fails with an unexpected error.
|
||||||
|
///
|
||||||
|
/// Note that certain errors are expected and handled internally; only
|
||||||
|
/// unanticipated failures during probing will result in this error.
|
||||||
|
pub fn is_map_supported(map_type: MapType) -> Result<bool, SyscallError> {
|
||||||
|
if map_type == MapType::Unspecified {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: all-zero byte-pattern valid for `bpf_attr`
|
||||||
|
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
|
||||||
|
// SAFETY: union access
|
||||||
|
let u = unsafe { &mut attr.__bindgen_anon_1 };
|
||||||
|
|
||||||
|
// To pass `map_alloc_check`/`map_alloc`
|
||||||
|
let key_size = match map_type {
|
||||||
|
MapType::LpmTrie | MapType::CgroupStorage | MapType::PerCpuCgroupStorage => 16,
|
||||||
|
MapType::Queue
|
||||||
|
| MapType::Stack
|
||||||
|
| MapType::RingBuf
|
||||||
|
| MapType::BloomFilter
|
||||||
|
| MapType::UserRingBuf
|
||||||
|
| MapType::Arena => 0,
|
||||||
|
_ => 4,
|
||||||
|
};
|
||||||
|
let value_size = match map_type {
|
||||||
|
MapType::StackTrace | MapType::LpmTrie => 8,
|
||||||
|
MapType::RingBuf | MapType::UserRingBuf | MapType::Arena => 0,
|
||||||
|
_ => 4,
|
||||||
|
};
|
||||||
|
let max_entries = match map_type {
|
||||||
|
MapType::CgroupStorage
|
||||||
|
| MapType::PerCpuCgroupStorage
|
||||||
|
| MapType::SkStorage
|
||||||
|
| MapType::InodeStorage
|
||||||
|
| MapType::TaskStorage
|
||||||
|
| MapType::CgrpStorage => 0,
|
||||||
|
MapType::RingBuf | MapType::UserRingBuf => page_size() as u32,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure that fd doesn't get dropped due to scoping.
|
||||||
|
let inner_map_fd;
|
||||||
|
match map_type {
|
||||||
|
MapType::LpmTrie => u.map_flags = BPF_F_NO_PREALLOC,
|
||||||
|
MapType::SkStorage
|
||||||
|
| MapType::InodeStorage
|
||||||
|
| MapType::TaskStorage
|
||||||
|
| MapType::CgrpStorage => {
|
||||||
|
u.map_flags = BPF_F_NO_PREALLOC;
|
||||||
|
// Intentionally trigger `EBADF` from `btf_get_by_fd()`.
|
||||||
|
u.btf_fd = u32::MAX;
|
||||||
|
u.btf_key_type_id = 1;
|
||||||
|
u.btf_value_type_id = 1;
|
||||||
|
}
|
||||||
|
MapType::ArrayOfMaps | MapType::HashOfMaps => {
|
||||||
|
// SAFETY: all-zero byte-pattern valid for `bpf_attr`
|
||||||
|
let mut attr_map = unsafe { mem::zeroed::<bpf_attr>() };
|
||||||
|
// SAFETY: union access
|
||||||
|
let u_map = unsafe { &mut attr_map.__bindgen_anon_1 };
|
||||||
|
u_map.map_type = bpf_map_type::BPF_MAP_TYPE_HASH as u32;
|
||||||
|
u_map.key_size = 1;
|
||||||
|
u_map.value_size = 1;
|
||||||
|
u_map.max_entries = 1;
|
||||||
|
// SAFETY: BPF_MAP_CREATE returns a new file descriptor.
|
||||||
|
inner_map_fd = unsafe { fd_sys_bpf(bpf_cmd::BPF_MAP_CREATE, &mut attr_map) }.map_err(
|
||||||
|
|io_error| SyscallError {
|
||||||
|
call: "bpf_map_create",
|
||||||
|
io_error,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
u.inner_map_fd = inner_map_fd.as_raw_fd() as u32;
|
||||||
|
}
|
||||||
|
MapType::StructOps => u.btf_vmlinux_value_type_id = 1,
|
||||||
|
MapType::Arena => u.map_flags = BPF_F_MMAPABLE,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
u.map_type = map_type as u32;
|
||||||
|
u.key_size = key_size;
|
||||||
|
u.value_size = value_size;
|
||||||
|
u.max_entries = max_entries;
|
||||||
|
|
||||||
|
// SAFETY: BPF_MAP_CREATE returns a new file descriptor.
|
||||||
|
let io_error = match unsafe { fd_sys_bpf(bpf_cmd::BPF_MAP_CREATE, &mut attr) } {
|
||||||
|
Ok(_) => return Ok(true),
|
||||||
|
Err(io_error) => io_error,
|
||||||
|
};
|
||||||
|
match io_error.raw_os_error() {
|
||||||
|
Some(EINVAL) => Ok(false),
|
||||||
|
Some(E2BIG)
|
||||||
|
if matches!(
|
||||||
|
map_type,
|
||||||
|
MapType::SkStorage
|
||||||
|
| MapType::StructOps
|
||||||
|
| MapType::InodeStorage
|
||||||
|
| MapType::TaskStorage
|
||||||
|
| MapType::CgrpStorage
|
||||||
|
) =>
|
||||||
|
{
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
Some(EBADF)
|
||||||
|
if matches!(
|
||||||
|
map_type,
|
||||||
|
MapType::SkStorage
|
||||||
|
| MapType::InodeStorage
|
||||||
|
| MapType::TaskStorage
|
||||||
|
| MapType::CgrpStorage
|
||||||
|
) =>
|
||||||
|
{
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
// `ENOTSUPP` from `bpf_struct_ops_map_alloc()` for struct_ops.
|
||||||
|
Some(524) if map_type == MapType::StructOps => Ok(true),
|
||||||
|
_ => Err(SyscallError {
|
||||||
|
call: "bpf_map_create",
|
||||||
|
io_error,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether `nr_map_ids` & `map_ids` fields in `bpf_prog_info` are supported.
|
||||||
|
pub(crate) fn is_prog_info_map_ids_supported() -> Result<bool, ProgramError> {
|
||||||
|
let fd = with_trivial_prog(ProgramType::SocketFilter, |attr| {
|
||||||
|
bpf_prog_load(attr).map_err(|io_error| {
|
||||||
|
ProgramError::SyscallError(SyscallError {
|
||||||
|
call: "bpf_prog_load",
|
||||||
|
io_error,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
// SAFETY: all-zero byte-pattern valid for `bpf_prog_info`
|
||||||
|
let mut info = unsafe { mem::zeroed::<bpf_prog_info>() };
|
||||||
|
info.nr_map_ids = 1;
|
||||||
|
|
||||||
|
probe_bpf_info(fd, info).map_err(ProgramError::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests whether `bpf_prog_info.gpl_compatible` field is supported.
|
||||||
|
pub(crate) fn is_prog_info_license_supported() -> Result<bool, ProgramError> {
|
||||||
|
let fd = with_trivial_prog(ProgramType::SocketFilter, |attr| {
|
||||||
|
bpf_prog_load(attr).map_err(|io_error| {
|
||||||
|
ProgramError::SyscallError(SyscallError {
|
||||||
|
call: "bpf_prog_load",
|
||||||
|
io_error,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
// SAFETY: all-zero byte-pattern valid for `bpf_prog_info`
|
||||||
|
let mut info = unsafe { mem::zeroed::<bpf_prog_info>() };
|
||||||
|
info.set_gpl_compatible(1);
|
||||||
|
|
||||||
|
probe_bpf_info(fd, info).map_err(ProgramError::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Probes program and map info.
|
||||||
|
fn probe_bpf_info<T>(fd: MockableFd, info: T) -> Result<bool, SyscallError> {
|
||||||
|
// SAFETY: all-zero byte-pattern valid for `bpf_attr`
|
||||||
|
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
|
||||||
|
attr.info.bpf_fd = fd.as_raw_fd() as u32;
|
||||||
|
attr.info.info_len = mem::size_of_val(&info) as u32;
|
||||||
|
attr.info.info = &info as *const _ as u64;
|
||||||
|
|
||||||
|
let io_error = match unit_sys_bpf(bpf_cmd::BPF_OBJ_GET_INFO_BY_FD, &mut attr) {
|
||||||
|
Ok(()) => return Ok(true),
|
||||||
|
Err(io_error) => io_error,
|
||||||
|
};
|
||||||
|
match io_error.raw_os_error() {
|
||||||
|
// `E2BIG` from `bpf_check_uarg_tail_zero()`
|
||||||
|
Some(E2BIG) => Ok(false),
|
||||||
|
_ => Err(SyscallError {
|
||||||
|
call: "bpf_obj_get_info_by_fd",
|
||||||
|
io_error,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,408 @@
|
|||||||
|
//! Test feature probing against kernel version.
|
||||||
|
|
||||||
|
use assert_matches::assert_matches;
|
||||||
|
use aya::{Btf, maps::MapType, programs::ProgramType, sys::feature_probe::*, util::KernelVersion};
|
||||||
|
use procfs::kernel_config;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn probe_supported_programs() {
|
||||||
|
let current = KernelVersion::current().unwrap();
|
||||||
|
let kernel_config = kernel_config().unwrap_or_default();
|
||||||
|
|
||||||
|
let socket_filter = is_program_supported(ProgramType::SocketFilter);
|
||||||
|
if current >= KernelVersion::new(3, 19, 0) {
|
||||||
|
assert_matches!(socket_filter, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(socket_filter, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let kprobe = is_program_supported(ProgramType::KProbe);
|
||||||
|
let sched_cls = is_program_supported(ProgramType::SchedClassifier);
|
||||||
|
let sched_act = is_program_supported(ProgramType::SchedAction);
|
||||||
|
if current >= KernelVersion::new(4, 1, 0) {
|
||||||
|
assert_matches!(kprobe, Ok(true));
|
||||||
|
assert_matches!(sched_cls, Ok(true));
|
||||||
|
assert_matches!(sched_act, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(kprobe, Ok(false));
|
||||||
|
assert_matches!(sched_cls, Ok(false));
|
||||||
|
assert_matches!(sched_act, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let tracepoint = is_program_supported(ProgramType::TracePoint);
|
||||||
|
if current >= KernelVersion::new(4, 7, 0) {
|
||||||
|
assert_matches!(tracepoint, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(tracepoint, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let xdp = is_program_supported(ProgramType::Xdp);
|
||||||
|
if current >= KernelVersion::new(4, 8, 0) {
|
||||||
|
assert_matches!(xdp, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(xdp, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let perf_event = is_program_supported(ProgramType::PerfEvent);
|
||||||
|
if current >= KernelVersion::new(4, 9, 0) {
|
||||||
|
assert_matches!(perf_event, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(perf_event, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cgroup_skb = is_program_supported(ProgramType::CgroupSkb);
|
||||||
|
let cgroup_sock = is_program_supported(ProgramType::CgroupSock);
|
||||||
|
let lwt_in = is_program_supported(ProgramType::LwtInput);
|
||||||
|
let lwt_out = is_program_supported(ProgramType::LwtOutput);
|
||||||
|
let lwt_xmit = is_program_supported(ProgramType::LwtXmit);
|
||||||
|
if current >= KernelVersion::new(4, 10, 0) {
|
||||||
|
assert_matches!(cgroup_skb, Ok(true));
|
||||||
|
assert_matches!(cgroup_sock, Ok(true));
|
||||||
|
assert_matches!(lwt_in, Ok(true));
|
||||||
|
assert_matches!(lwt_out, Ok(true));
|
||||||
|
assert_matches!(lwt_xmit, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(cgroup_skb, Ok(false));
|
||||||
|
assert_matches!(cgroup_sock, Ok(false));
|
||||||
|
assert_matches!(lwt_in, Ok(false));
|
||||||
|
assert_matches!(lwt_out, Ok(false));
|
||||||
|
assert_matches!(lwt_xmit, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let sock_ops = is_program_supported(ProgramType::SockOps);
|
||||||
|
if current >= KernelVersion::new(4, 13, 0) {
|
||||||
|
assert_matches!(sock_ops, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(sock_ops, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let sk_skb = is_program_supported(ProgramType::SkSkb);
|
||||||
|
if current >= KernelVersion::new(4, 14, 0) {
|
||||||
|
assert_matches!(sk_skb, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(sk_skb, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cgroup_device = is_program_supported(ProgramType::CgroupDevice);
|
||||||
|
if current >= KernelVersion::new(4, 15, 0) {
|
||||||
|
assert_matches!(cgroup_device, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(cgroup_device, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let sk_msg = is_program_supported(ProgramType::SkMsg);
|
||||||
|
let raw_tp = is_program_supported(ProgramType::RawTracePoint);
|
||||||
|
let cgroup_sock_addr = is_program_supported(ProgramType::CgroupSockAddr);
|
||||||
|
if current >= KernelVersion::new(4, 17, 0) {
|
||||||
|
assert_matches!(sk_msg, Ok(true));
|
||||||
|
assert_matches!(raw_tp, Ok(true));
|
||||||
|
assert_matches!(cgroup_sock_addr, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(sk_msg, Ok(false));
|
||||||
|
assert_matches!(raw_tp, Ok(false));
|
||||||
|
assert_matches!(cgroup_sock_addr, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let lwt_seg6local = is_program_supported(ProgramType::LwtSeg6local);
|
||||||
|
let lirc_mode2 = is_program_supported(ProgramType::LircMode2); // Requires CONFIG_BPF_LIRC_MODE2=y
|
||||||
|
if current >= KernelVersion::new(4, 18, 0) {
|
||||||
|
assert_matches!(lwt_seg6local, Ok(true));
|
||||||
|
|
||||||
|
let lirc_mode2_config = matches!(
|
||||||
|
kernel_config.get("CONFIG_BPF_LIRC_MODE2"),
|
||||||
|
Some(procfs::ConfigSetting::Yes)
|
||||||
|
);
|
||||||
|
assert_matches!(lirc_mode2, Ok(lirc_mode2) if lirc_mode2 == lirc_mode2_config);
|
||||||
|
if !lirc_mode2_config {
|
||||||
|
eprintln!("CONFIG_BPF_LIRC_MODE2 required for lirc_mode2 program type");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert_matches!(lwt_seg6local, Ok(false));
|
||||||
|
assert_matches!(lirc_mode2, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let sk_reuseport = is_program_supported(ProgramType::SkReuseport);
|
||||||
|
if current >= KernelVersion::new(4, 19, 0) {
|
||||||
|
assert_matches!(sk_reuseport, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(sk_reuseport, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let flow_dissector = is_program_supported(ProgramType::FlowDissector);
|
||||||
|
if current >= KernelVersion::new(4, 20, 0) {
|
||||||
|
assert_matches!(flow_dissector, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(flow_dissector, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cgroup_sysctl = is_program_supported(ProgramType::CgroupSysctl);
|
||||||
|
let raw_tp_writable = is_program_supported(ProgramType::RawTracePointWritable);
|
||||||
|
if current >= KernelVersion::new(5, 2, 0) {
|
||||||
|
assert_matches!(cgroup_sysctl, Ok(true));
|
||||||
|
assert_matches!(raw_tp_writable, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(cgroup_sysctl, Ok(false));
|
||||||
|
assert_matches!(raw_tp_writable, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cgroup_sockopt = is_program_supported(ProgramType::CgroupSockopt);
|
||||||
|
if current >= KernelVersion::new(5, 3, 0) {
|
||||||
|
assert_matches!(cgroup_sockopt, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(cgroup_sockopt, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let tracing = is_program_supported(ProgramType::Tracing); // Requires `CONFIG_DEBUG_INFO_BTF=y`
|
||||||
|
if current >= KernelVersion::new(5, 5, 0) {
|
||||||
|
assert_matches!(tracing, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(tracing, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let struct_ops = is_program_supported(ProgramType::StructOps);
|
||||||
|
let extension = is_program_supported(ProgramType::Extension);
|
||||||
|
if current >= KernelVersion::new(5, 6, 0) {
|
||||||
|
assert_matches!(struct_ops, Ok(true));
|
||||||
|
assert_matches!(extension, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(struct_ops, Ok(false));
|
||||||
|
assert_matches!(extension, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let lsm = is_program_supported(ProgramType::Lsm); // Requires `CONFIG_DEBUG_INFO_BTF=y` & `CONFIG_BPF_LSM=y`
|
||||||
|
if current >= KernelVersion::new(5, 7, 0) {
|
||||||
|
// Ways to check if `CONFIG_BPF_LSM` is enabled:
|
||||||
|
// - kernel config has `CONFIG_BPF_LSM=y`, but config is not always exposed.
|
||||||
|
// - an LSM hooks is present in BTF, e.g. `bpf_lsm_bpf`. hooks are found in `bpf_lsm.c`
|
||||||
|
let lsm_enabled = matches!(
|
||||||
|
kernel_config.get("CONFIG_BPF_LSM"),
|
||||||
|
Some(procfs::ConfigSetting::Yes)
|
||||||
|
) || Btf::from_sys_fs()
|
||||||
|
.and_then(|btf| btf.id_by_type_name_kind("bpf_lsm_bpf", aya_obj::btf::BtfKind::Func))
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
|
assert_matches!(lsm, Ok(lsm_supported) if lsm_supported == lsm_enabled);
|
||||||
|
if !lsm_enabled {
|
||||||
|
eprintln!("CONFIG_BPF_LSM required for lsm program type");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert_matches!(lsm, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let sk_lookup = is_program_supported(ProgramType::SkLookup);
|
||||||
|
if current >= KernelVersion::new(5, 9, 0) {
|
||||||
|
assert_matches!(sk_lookup, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(sk_lookup, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let syscall = is_program_supported(ProgramType::Syscall);
|
||||||
|
if current >= KernelVersion::new(5, 14, 0) {
|
||||||
|
assert_matches!(syscall, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(syscall, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let netfilter = is_program_supported(ProgramType::Netfilter);
|
||||||
|
if current >= KernelVersion::new(6, 4, 0) {
|
||||||
|
assert_matches!(netfilter, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(netfilter, Ok(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn probe_supported_maps() {
|
||||||
|
let current = KernelVersion::current().unwrap();
|
||||||
|
|
||||||
|
let hash = is_map_supported(MapType::Hash);
|
||||||
|
let array = is_map_supported(MapType::Array);
|
||||||
|
if current >= KernelVersion::new(3, 19, 0) {
|
||||||
|
assert_matches!(hash, Ok(true));
|
||||||
|
assert_matches!(array, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(hash, Ok(false));
|
||||||
|
assert_matches!(array, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let prog_array = is_map_supported(MapType::ProgramArray);
|
||||||
|
if current >= KernelVersion::new(4, 2, 0) {
|
||||||
|
assert_matches!(prog_array, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(prog_array, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let perf_event_array = is_map_supported(MapType::PerfEventArray);
|
||||||
|
if current >= KernelVersion::new(4, 3, 0) {
|
||||||
|
assert_matches!(perf_event_array, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(perf_event_array, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let per_cpu_hash = is_map_supported(MapType::PerCpuHash);
|
||||||
|
let per_cpu_array = is_map_supported(MapType::PerCpuArray);
|
||||||
|
let stack_trace = is_map_supported(MapType::StackTrace);
|
||||||
|
if current >= KernelVersion::new(4, 6, 0) {
|
||||||
|
assert_matches!(per_cpu_hash, Ok(true));
|
||||||
|
assert_matches!(per_cpu_array, Ok(true));
|
||||||
|
assert_matches!(stack_trace, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(per_cpu_hash, Ok(false));
|
||||||
|
assert_matches!(per_cpu_array, Ok(false));
|
||||||
|
assert_matches!(stack_trace, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cgroup_array = is_map_supported(MapType::CgroupArray);
|
||||||
|
if current >= KernelVersion::new(4, 8, 0) {
|
||||||
|
assert_matches!(cgroup_array, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(cgroup_array, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let lru_hash = is_map_supported(MapType::LruHash);
|
||||||
|
let lru_per_cpu_hash = is_map_supported(MapType::LruPerCpuHash);
|
||||||
|
if current >= KernelVersion::new(4, 10, 0) {
|
||||||
|
assert_matches!(lru_hash, Ok(true));
|
||||||
|
assert_matches!(lru_per_cpu_hash, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(lru_hash, Ok(false));
|
||||||
|
assert_matches!(lru_per_cpu_hash, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let lpm_trie = is_map_supported(MapType::LpmTrie);
|
||||||
|
if current >= KernelVersion::new(4, 11, 0) {
|
||||||
|
assert_matches!(lpm_trie, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(lpm_trie, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let array_of_maps = is_map_supported(MapType::ArrayOfMaps);
|
||||||
|
let hash_of_maps = is_map_supported(MapType::HashOfMaps);
|
||||||
|
if current >= KernelVersion::new(4, 12, 0) {
|
||||||
|
assert_matches!(array_of_maps, Ok(true));
|
||||||
|
assert_matches!(hash_of_maps, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(array_of_maps, Ok(false));
|
||||||
|
assert_matches!(hash_of_maps, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let dev_map = is_map_supported(MapType::DevMap);
|
||||||
|
let sock_map = is_map_supported(MapType::SockMap);
|
||||||
|
if current >= KernelVersion::new(4, 14, 0) {
|
||||||
|
assert_matches!(dev_map, Ok(true));
|
||||||
|
assert_matches!(sock_map, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(dev_map, Ok(false));
|
||||||
|
assert_matches!(sock_map, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cpu_map = is_map_supported(MapType::CpuMap);
|
||||||
|
if current >= KernelVersion::new(4, 15, 0) {
|
||||||
|
assert_matches!(cpu_map, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(cpu_map, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let xsk_map = is_map_supported(MapType::XskMap);
|
||||||
|
let sock_hash = is_map_supported(MapType::SockHash);
|
||||||
|
if current >= KernelVersion::new(4, 18, 0) {
|
||||||
|
assert_matches!(xsk_map, Ok(true));
|
||||||
|
assert_matches!(sock_hash, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(xsk_map, Ok(false));
|
||||||
|
assert_matches!(sock_hash, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cgroup_storage = is_map_supported(MapType::CgroupStorage);
|
||||||
|
let reuseport_sock_array = is_map_supported(MapType::ReuseportSockArray);
|
||||||
|
if current >= KernelVersion::new(4, 19, 0) {
|
||||||
|
assert_matches!(cgroup_storage, Ok(true));
|
||||||
|
assert_matches!(reuseport_sock_array, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(cgroup_storage, Ok(false));
|
||||||
|
assert_matches!(reuseport_sock_array, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let per_cpu_cgroup_storage = is_map_supported(MapType::PerCpuCgroupStorage);
|
||||||
|
let queue = is_map_supported(MapType::Queue);
|
||||||
|
let stack = is_map_supported(MapType::Stack);
|
||||||
|
if current >= KernelVersion::new(4, 20, 0) {
|
||||||
|
assert_matches!(per_cpu_cgroup_storage, Ok(true));
|
||||||
|
assert_matches!(queue, Ok(true));
|
||||||
|
assert_matches!(stack, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(per_cpu_cgroup_storage, Ok(false));
|
||||||
|
assert_matches!(queue, Ok(false));
|
||||||
|
assert_matches!(stack, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let sk_storage = is_map_supported(MapType::SkStorage);
|
||||||
|
if current >= KernelVersion::new(5, 2, 0) {
|
||||||
|
assert_matches!(sk_storage, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(sk_storage, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let devmap_hash = is_map_supported(MapType::DevMapHash);
|
||||||
|
if current >= KernelVersion::new(5, 4, 0) {
|
||||||
|
assert_matches!(devmap_hash, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(devmap_hash, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let struct_ops = is_map_supported(MapType::StructOps);
|
||||||
|
if current >= KernelVersion::new(5, 6, 0) {
|
||||||
|
assert_matches!(struct_ops, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(struct_ops, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ring_buf = is_map_supported(MapType::RingBuf);
|
||||||
|
if current >= KernelVersion::new(5, 8, 0) {
|
||||||
|
assert_matches!(ring_buf, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(ring_buf, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let inode_storage = is_map_supported(MapType::InodeStorage); // Requires `CONFIG_BPF_LSM=y`
|
||||||
|
if current >= KernelVersion::new(5, 10, 0) {
|
||||||
|
assert_matches!(inode_storage, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(inode_storage, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let task_storage = is_map_supported(MapType::TaskStorage);
|
||||||
|
if current >= KernelVersion::new(5, 11, 0) {
|
||||||
|
assert_matches!(task_storage, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(task_storage, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let bloom_filter = is_map_supported(MapType::BloomFilter);
|
||||||
|
if current >= KernelVersion::new(5, 16, 0) {
|
||||||
|
assert_matches!(bloom_filter, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(bloom_filter, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let user_ring_buf = is_map_supported(MapType::UserRingBuf);
|
||||||
|
if current >= KernelVersion::new(6, 1, 0) {
|
||||||
|
assert_matches!(user_ring_buf, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(user_ring_buf, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cgrp_storage = is_map_supported(MapType::CgrpStorage);
|
||||||
|
if current >= KernelVersion::new(6, 2, 0) {
|
||||||
|
assert_matches!(cgrp_storage, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(cgrp_storage, Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
let arena = is_map_supported(MapType::Arena);
|
||||||
|
if current >= KernelVersion::new(6, 9, 0) {
|
||||||
|
assert_matches!(arena, Ok(true));
|
||||||
|
} else {
|
||||||
|
assert_matches!(arena, Ok(false));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue