Tyrone Wu 3 weeks ago committed by GitHub
commit e10f0a650e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -90,6 +90,7 @@ octorust = { version = "0.10", default-features = false }
once_cell = { version = "1.20.1", default-features = false } once_cell = { version = "1.20.1", default-features = false }
proc-macro2 = { version = "1", default-features = false } proc-macro2 = { version = "1", default-features = false }
proc-macro2-diagnostics = { version = "0.10.1", default-features = false } proc-macro2-diagnostics = { version = "0.10.1", default-features = false }
procfs = { version = "0.17.0", default-features = false }
public-api = { version = "0.44.0", default-features = false } public-api = { version = "0.44.0", default-features = false }
quote = { version = "1", default-features = false } quote = { version = "1", default-features = false }
rand = { version = "0.9", default-features = false } rand = { version = "0.9", default-features = false }

@ -45,8 +45,6 @@ pub struct Features {
bpf_cookie: bool, bpf_cookie: bool,
cpumap_prog_id: bool, cpumap_prog_id: bool,
devmap_prog_id: bool, devmap_prog_id: bool,
prog_info_map_ids: bool,
prog_info_gpl_compatible: bool,
btf: Option<BtfFeatures>, btf: Option<BtfFeatures>,
} }
@ -61,8 +59,6 @@ impl Features {
bpf_cookie: bool, bpf_cookie: bool,
cpumap_prog_id: bool, cpumap_prog_id: bool,
devmap_prog_id: bool, devmap_prog_id: bool,
prog_info_map_ids: bool,
prog_info_gpl_compatible: bool,
btf: Option<BtfFeatures>, btf: Option<BtfFeatures>,
) -> Self { ) -> Self {
Self { Self {
@ -73,8 +69,6 @@ impl Features {
bpf_cookie, bpf_cookie,
cpumap_prog_id, cpumap_prog_id,
devmap_prog_id, devmap_prog_id,
prog_info_map_ids,
prog_info_gpl_compatible,
btf, btf,
} }
} }
@ -117,16 +111,6 @@ impl Features {
self.devmap_prog_id self.devmap_prog_id
} }
/// Returns whether `bpf_prog_info` supports `nr_map_ids` & `map_ids` fields.
pub fn prog_info_map_ids(&self) -> bool {
self.prog_info_map_ids
}
/// Returns whether `bpf_prog_info` supports `gpl_compatible` field.
pub fn prog_info_gpl_compatible(&self) -> bool {
self.prog_info_gpl_compatible
}
/// 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()

@ -32,9 +32,9 @@ use crate::{
bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported, bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported,
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_info_gpl_compatible_supported, is_btf_supported, is_btf_type_tag_supported, is_perf_link_supported,
is_info_map_ids_supported, is_perf_link_supported, is_probe_read_kernel_supported, is_probe_read_kernel_supported, is_prog_id_supported, is_prog_name_supported,
is_prog_id_supported, is_prog_name_supported, retry_with_verifier_logs, retry_with_verifier_logs,
}, },
util::{bytes_of, bytes_of_slice, nr_cpus, page_size}, util::{bytes_of, bytes_of_slice, nr_cpus, page_size},
}; };
@ -82,8 +82,6 @@ fn detect_features() -> Features {
is_bpf_cookie_supported(), is_bpf_cookie_supported(),
is_prog_id_supported(BPF_MAP_TYPE_CPUMAP), is_prog_id_supported(BPF_MAP_TYPE_CPUMAP),
is_prog_id_supported(BPF_MAP_TYPE_DEVMAP), is_prog_id_supported(BPF_MAP_TYPE_DEVMAP),
is_info_map_ids_supported(),
is_info_gpl_compatible_supported(),
btf, btf,
); );
debug!("BPF Feature Detection: {:#?}", f); debug!("BPF Feature Detection: {:#?}", f);

@ -4,6 +4,7 @@ use std::{
ffi::CString, ffi::CString,
os::fd::{AsFd as _, BorrowedFd}, os::fd::{AsFd as _, BorrowedFd},
path::Path, path::Path,
sync::OnceLock,
time::{Duration, SystemTime}, time::{Duration, SystemTime},
}; };
@ -16,7 +17,9 @@ use super::{
use crate::{ use crate::{
FEATURES, FEATURES,
sys::{ sys::{
SyscallError, bpf_get_object, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd, iter_prog_ids, SyscallError, bpf_get_object, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd,
feature_probe::{is_prog_info_license_supported, is_prog_info_map_ids_supported},
iter_prog_ids,
}, },
util::bytes_of_bpf_name, util::bytes_of_bpf_name,
}; };
@ -108,13 +111,17 @@ impl ProgramInfo {
/// ///
/// Introduced in kernel v4.15. /// Introduced in kernel v4.15.
pub fn map_ids(&self) -> Result<Option<Vec<u32>>, ProgramError> { pub fn map_ids(&self) -> Result<Option<Vec<u32>>, ProgramError> {
if FEATURES.prog_info_map_ids() { static CACHE: OnceLock<bool> = OnceLock::new();
let mut map_ids = vec![0u32; self.0.nr_map_ids as usize]; CACHE
bpf_prog_get_info_by_fd(self.fd()?.as_fd(), &mut map_ids)?; .get_or_init(|| {
Ok(Some(map_ids)) self.0.nr_map_ids > 0 || matches!(is_prog_info_map_ids_supported(), Ok(true))
} else { })
Ok(None) .then(|| {
} let mut map_ids = vec![0u32; self.0.nr_map_ids as usize];
bpf_prog_get_info_by_fd(self.fd()?.as_fd(), &mut map_ids)?;
Ok(map_ids)
})
.transpose()
} }
/// The name of the program as was provided when it was load. This is limited to 16 bytes. /// The name of the program as was provided when it was load. This is limited to 16 bytes.
@ -140,8 +147,11 @@ impl ProgramInfo {
/// ///
/// Introduced in kernel v4.18. /// Introduced in kernel v4.18.
pub fn gpl_compatible(&self) -> Option<bool> { pub fn gpl_compatible(&self) -> Option<bool> {
FEATURES static CACHE: OnceLock<bool> = OnceLock::new();
.prog_info_gpl_compatible() CACHE
.get_or_init(|| {
self.0.gpl_compatible() != 0 || matches!(is_prog_info_license_supported(), Ok(true))
})
.then_some(self.0.gpl_compatible() != 0) .then_some(self.0.gpl_compatible() != 0)
} }

@ -25,9 +25,9 @@ use aya_obj::{
use libc::{BPF_ADD, ENOENT, ENOSPC}; use libc::{BPF_ADD, ENOENT, ENOSPC};
use crate::{ use crate::{
Btf, FEATURES, Pod, VerifierLogLevel, Btf, Pod, VerifierLogLevel,
maps::{MapData, PerCpuValues}, maps::{MapData, PerCpuValues},
programs::links::LinkRef, programs::{ProgramType, links::LinkRef},
sys::{Syscall, SyscallError, syscall}, sys::{Syscall, SyscallError, syscall},
util::KernelVersion, util::KernelVersion,
}; };
@ -593,7 +593,7 @@ pub(crate) fn bpf_prog_get_info_by_fd(
// An `E2BIG` error can occur on kernels below v4.15 when handing over a large struct where the // An `E2BIG` error can occur on kernels below v4.15 when handing over a large struct where the
// extra space is not all-zero bytes. // extra space is not all-zero bytes.
bpf_obj_get_info_by_fd(fd, |info: &mut bpf_prog_info| { bpf_obj_get_info_by_fd(fd, |info: &mut bpf_prog_info| {
if FEATURES.prog_info_map_ids() { if !map_ids.is_empty() {
info.nr_map_ids = map_ids.len() as _; info.nr_map_ids = map_ids.len() as _;
info.map_ids = map_ids.as_mut_ptr() as _; info.map_ids = map_ids.as_mut_ptr() as _;
} }
@ -681,7 +681,10 @@ pub(crate) fn bpf_load_btf(
} }
// SAFETY: only use for bpf_cmd that return a new file descriptor on success. // SAFETY: only use for bpf_cmd that return a new file descriptor on success.
unsafe fn fd_sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> io::Result<crate::MockableFd> { pub(super) unsafe fn fd_sys_bpf(
cmd: bpf_cmd,
attr: &mut bpf_attr,
) -> io::Result<crate::MockableFd> {
let fd = sys_bpf(cmd, attr)?; let fd = sys_bpf(cmd, attr)?;
let fd = fd.try_into().map_err(|std::num::TryFromIntError { .. }| { let fd = fd.try_into().map_err(|std::num::TryFromIntError { .. }| {
io::Error::new( io::Error::new(
@ -706,7 +709,7 @@ pub(crate) fn bpf_btf_get_fd_by_id(id: u32) -> Result<crate::MockableFd, Syscall
} }
pub(crate) fn is_prog_name_supported() -> bool { pub(crate) fn is_prog_name_supported() -> bool {
with_trivial_prog(|attr| { with_trivial_prog(ProgramType::TracePoint, |attr| {
let u = unsafe { &mut attr.__bindgen_anon_3 }; let u = unsafe { &mut attr.__bindgen_anon_3 };
let name = c"aya_name_check"; let name = c"aya_name_check";
let name_bytes = name.to_bytes(); let name_bytes = name.to_bytes();
@ -727,7 +730,7 @@ fn new_insn(code: u8, dst_reg: u8, src_reg: u8, offset: i16, imm: i32) -> bpf_in
insn insn
} }
fn with_trivial_prog<T, F>(op: F) -> T pub(super) fn with_trivial_prog<T, F>(program_type: ProgramType, op: F) -> T
where where
F: FnOnce(&mut bpf_attr) -> T, F: FnOnce(&mut bpf_attr) -> T,
{ {
@ -743,37 +746,35 @@ where
u.insn_cnt = insns.len() as u32; u.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64; u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32;
op(&mut attr) // `bpf_prog_load_fixup_attach_type()` sets this for us for cgroup_sock and
} // and sk_reuseport.
let expected_attach_type = match program_type {
/// Tests whether `nr_map_ids` & `map_ids` fields in `bpf_prog_info` is available. ProgramType::CgroupSkb => Some(bpf_attach_type::BPF_CGROUP_INET_INGRESS),
pub(crate) fn is_info_map_ids_supported() -> bool { ProgramType::CgroupSockAddr => Some(bpf_attach_type::BPF_CGROUP_INET4_BIND),
with_trivial_prog(|attr| { ProgramType::LircMode2 => Some(bpf_attach_type::BPF_LIRC_MODE2),
let prog_fd = match bpf_prog_load(attr) { ProgramType::CgroupSockopt => Some(bpf_attach_type::BPF_CGROUP_GETSOCKOPT),
Ok(fd) => fd, ProgramType::Tracing => Some(bpf_attach_type::BPF_TRACE_FENTRY),
Err(_) => return false, ProgramType::Lsm => Some(bpf_attach_type::BPF_LSM_MAC),
}; ProgramType::SkLookup => Some(bpf_attach_type::BPF_SK_LOOKUP),
bpf_obj_get_info_by_fd(prog_fd.as_fd(), |info: &mut bpf_prog_info| { ProgramType::Netfilter => Some(bpf_attach_type::BPF_NETFILTER),
info.nr_map_ids = 1 _ => None,
}) };
.is_ok()
})
}
/// Tests whether `gpl_compatible` field in `bpf_prog_info` is available. match program_type {
pub(crate) fn is_info_gpl_compatible_supported() -> bool { ProgramType::KProbe if KernelVersion::current().is_ok() => {
with_trivial_prog(|attr| { u.kern_version = KernelVersion::current().unwrap().code()
let prog_fd = match bpf_prog_load(attr) {
Ok(fd) => fd,
Err(_) => return false,
};
if let Ok::<bpf_prog_info, _>(info) = bpf_obj_get_info_by_fd(prog_fd.as_fd(), |_| {}) {
return info.gpl_compatible() != 0;
} }
false ProgramType::Syscall => u.prog_flags = aya_obj::generated::BPF_F_SLEEPABLE,
}) _ => {}
}
u.prog_type = program_type as u32;
if let Some(expected_attach_type) = expected_attach_type {
u.expected_attach_type = expected_attach_type as u32;
}
op(&mut attr)
} }
pub(crate) fn is_probe_read_kernel_supported() -> bool { pub(crate) fn is_probe_read_kernel_supported() -> bool {
@ -805,7 +806,7 @@ pub(crate) fn is_probe_read_kernel_supported() -> bool {
} }
pub(crate) fn is_perf_link_supported() -> bool { pub(crate) fn is_perf_link_supported() -> bool {
with_trivial_prog(|attr| { with_trivial_prog(ProgramType::TracePoint, |attr| {
if let Ok(fd) = bpf_prog_load(attr) { if let Ok(fd) = bpf_prog_load(attr) {
let fd = fd.as_fd(); let fd = fd.as_fd();
// Uses an invalid target FD so we get EBADF if supported. // Uses an invalid target FD so we get EBADF if supported.
@ -1073,7 +1074,7 @@ pub(crate) fn is_btf_type_tag_supported() -> bool {
bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok() bpf_load_btf(btf_bytes.as_slice(), &mut [], Default::default()).is_ok()
} }
fn bpf_prog_load(attr: &mut bpf_attr) -> io::Result<crate::MockableFd> { pub(super) fn bpf_prog_load(attr: &mut bpf_attr) -> io::Result<crate::MockableFd> {
// SAFETY: BPF_PROG_LOAD returns a new file descriptor. // SAFETY: BPF_PROG_LOAD returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_PROG_LOAD, attr) } unsafe { fd_sys_bpf(bpf_cmd::BPF_PROG_LOAD, attr) }
} }
@ -1085,7 +1086,7 @@ fn sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> io::Result<i64> {
}) })
} }
fn unit_sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> io::Result<()> { pub(super) fn unit_sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> io::Result<()> {
sys_bpf(cmd, attr).map(|code| assert_eq!(code, 0)) sys_bpf(cmd, attr).map(|code| assert_eq!(code, 0))
} }

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

@ -1,6 +1,7 @@
//! A collection of system calls for performing eBPF related operations. //! A collection of system calls for performing eBPF related operations.
mod bpf; mod bpf;
pub mod feature_probe;
mod netlink; mod netlink;
mod perf_event; mod perf_event;

@ -27,6 +27,7 @@ libc = { workspace = true }
log = { workspace = true } log = { workspace = true }
netns-rs = { workspace = true } netns-rs = { workspace = true }
object = { workspace = true, features = ["elf", "read_core", "std"] } object = { workspace = true, features = ["elf", "read_core", "std"] }
procfs = { workspace = true, features = ["flate2"] }
rand = { workspace = true, features = ["thread_rng"] } rand = { workspace = true, features = ["thread_rng"] }
rbpf = { workspace = true } rbpf = { workspace = true }
scopeguard = { workspace = true } scopeguard = { workspace = true }

@ -1,6 +1,7 @@
mod bpf_probe_read; mod bpf_probe_read;
mod btf_relocations; mod btf_relocations;
mod elf; mod elf;
mod feature_probe;
mod info; mod info;
mod iter; mod iter;
mod load; mod load;

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

@ -12,6 +12,7 @@ use aya::{
Ebpf, Ebpf,
maps::{Array, HashMap, IterableMap as _, MapError, MapType, loaded_maps}, maps::{Array, HashMap, IterableMap as _, MapError, MapType, loaded_maps},
programs::{ProgramError, ProgramType, SocketFilter, TracePoint, UProbe, loaded_programs}, programs::{ProgramError, ProgramType, SocketFilter, TracePoint, UProbe, loaded_programs},
sys::feature_probe::{is_map_supported, is_program_supported},
util::KernelVersion, util::KernelVersion,
}; };
use libc::EINVAL; use libc::EINVAL;
@ -20,7 +21,11 @@ use crate::utils::{kernel_assert, kernel_assert_eq};
#[test] #[test]
fn test_loaded_programs() { fn test_loaded_programs() {
// Load a program. if !is_program_supported(ProgramType::SocketFilter).unwrap() {
eprintln!("skipping test - socket_filter program not supported");
return;
}
// Since we are only testing the programs for their metadata, there is no need to "attach" them. // Since we are only testing the programs for their metadata, there is no need to "attach" them.
let mut bpf = Ebpf::load(crate::TEST).unwrap(); let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut UProbe = bpf.program_mut("test_uprobe").unwrap().try_into().unwrap(); let prog: &mut UProbe = bpf.program_mut("test_uprobe").unwrap().try_into().unwrap();
@ -33,9 +38,7 @@ fn test_loaded_programs() {
if let ProgramError::SyscallError(err) = &err { if let ProgramError::SyscallError(err) = &err {
// Skip entire test since feature not available // Skip entire test since feature not available
if err.io_error.raw_os_error() == Some(EINVAL) { if err.io_error.raw_os_error() == Some(EINVAL) {
eprintln!( eprintln!("skipping test - `loaded_programs()` not supported");
"ignoring test completely as `loaded_programs()` is not available on the host"
);
return; return;
} }
} }
@ -71,6 +74,11 @@ fn test_loaded_programs() {
#[test] #[test]
fn test_program_info() { fn test_program_info() {
if !is_program_supported(ProgramType::SocketFilter).unwrap() {
eprintln!("skipping test - socket_filter program not supported");
return;
}
// Kernels below v4.15 have been observed to have `bpf_jit_enable` disabled by default. // Kernels below v4.15 have been observed to have `bpf_jit_enable` disabled by default.
let _guard = ensure_sysctl_enabled("/proc/sys/net/core/bpf_jit_enable"); let _guard = ensure_sysctl_enabled("/proc/sys/net/core/bpf_jit_enable");
@ -129,6 +137,11 @@ fn test_program_info() {
#[test] #[test]
fn test_loaded_at() { fn test_loaded_at() {
if !is_program_supported(ProgramType::SocketFilter).unwrap() {
eprintln!("skipping test - socket_filter program not supported");
return;
}
let mut bpf: Ebpf = Ebpf::load(crate::SIMPLE_PROG).unwrap(); let mut bpf: Ebpf = Ebpf::load(crate::SIMPLE_PROG).unwrap();
let prog: &mut SocketFilter = bpf.program_mut("simple_prog").unwrap().try_into().unwrap(); let prog: &mut SocketFilter = bpf.program_mut("simple_prog").unwrap().try_into().unwrap();
@ -144,9 +157,7 @@ fn test_loaded_at() {
let loaded_at = match prog.info().unwrap().loaded_at() { let loaded_at = match prog.info().unwrap().loaded_at() {
Some(time) => time, Some(time) => time,
None => { None => {
eprintln!( eprintln!("skipping test - `bpf_prog_info.load_time` field not supported");
"ignoring test completely as `load_time` field of `bpf_prog_info` is not available on the host"
);
return; return;
} }
}; };
@ -175,11 +186,12 @@ fn test_loaded_at() {
#[test] #[test]
fn test_prog_stats() { fn test_prog_stats() {
// Test depends on whether trace point exists. if !is_program_supported(ProgramType::TracePoint).unwrap() {
eprintln!("skipping test - tracepoint program not supported");
return;
}
if !Path::new("/sys/kernel/debug/tracing/events/syscalls/sys_enter_bpf").exists() { if !Path::new("/sys/kernel/debug/tracing/events/syscalls/sys_enter_bpf").exists() {
eprintln!( eprintln!("skipping test - `syscalls/sys_enter_bpf` not available");
"ignoring test completely as `syscalls/sys_enter_bpf` is not available on the host"
);
return; return;
} }
@ -200,6 +212,17 @@ fn test_prog_stats() {
#[test] #[test]
fn list_loaded_maps() { fn list_loaded_maps() {
if !is_program_supported(ProgramType::SocketFilter).unwrap() {
eprintln!("skipping test - socket_filter program not supported");
return;
} else if !is_map_supported(MapType::Hash).unwrap() {
eprintln!("skipping test - hash map not supported");
return;
} else if !is_map_supported(MapType::Array).unwrap() {
eprintln!("skipping test - array map not supported");
return;
}
// Load a program with maps. // Load a program with maps.
let mut bpf: Ebpf = Ebpf::load(crate::MAP_TEST).unwrap(); let mut bpf: Ebpf = Ebpf::load(crate::MAP_TEST).unwrap();
let prog: &mut SocketFilter = bpf.program_mut("simple_prog").unwrap().try_into().unwrap(); let prog: &mut SocketFilter = bpf.program_mut("simple_prog").unwrap().try_into().unwrap();
@ -210,9 +233,7 @@ fn list_loaded_maps() {
if let Err(err) = maps.peek().unwrap() { if let Err(err) = maps.peek().unwrap() {
if let MapError::SyscallError(err) = &err { if let MapError::SyscallError(err) = &err {
if err.io_error.raw_os_error() == Some(EINVAL) { if err.io_error.raw_os_error() == Some(EINVAL) {
eprintln!( eprintln!("skipping test - `loaded_maps()` not supported");
"ignoring test completely as `loaded_maps()` is not available on the host"
);
return; return;
} }
} }
@ -250,6 +271,17 @@ fn list_loaded_maps() {
#[test] #[test]
fn test_map_info() { fn test_map_info() {
if !is_program_supported(ProgramType::SocketFilter).unwrap() {
eprintln!("skipping test - socket_filter program not supported");
return;
} else if !is_map_supported(MapType::Hash).unwrap() {
eprintln!("skipping test - hash map not supported");
return;
} else if !is_map_supported(MapType::Array).unwrap() {
eprintln!("skipping test - array map not supported");
return;
}
let mut bpf: Ebpf = Ebpf::load(crate::MAP_TEST).unwrap(); let mut bpf: Ebpf = Ebpf::load(crate::MAP_TEST).unwrap();
let prog: &mut SocketFilter = bpf.program_mut("simple_prog").unwrap().try_into().unwrap(); let prog: &mut SocketFilter = bpf.program_mut("simple_prog").unwrap().try_into().unwrap();
prog.load().unwrap(); prog.load().unwrap();

@ -14,7 +14,7 @@ use aya_obj::programs::XdpAttachType;
use test_log::test; use test_log::test;
const MAX_RETRIES: usize = 100; const MAX_RETRIES: usize = 100;
const RETRY_DURATION: Duration = Duration::from_millis(10); pub(crate) const RETRY_DURATION: Duration = Duration::from_millis(10);
#[test] #[test]
fn long_name() { fn long_name() {

@ -8678,8 +8678,6 @@ pub fn aya_obj::Features::bpf_probe_read_kernel(&self) -> bool
pub fn aya_obj::Features::btf(&self) -> core::option::Option<&aya_obj::btf::BtfFeatures> pub fn aya_obj::Features::btf(&self) -> core::option::Option<&aya_obj::btf::BtfFeatures>
pub fn aya_obj::Features::cpumap_prog_id(&self) -> bool pub fn aya_obj::Features::cpumap_prog_id(&self) -> bool
pub fn aya_obj::Features::devmap_prog_id(&self) -> bool pub fn aya_obj::Features::devmap_prog_id(&self) -> bool
pub fn aya_obj::Features::prog_info_gpl_compatible(&self) -> bool
pub fn aya_obj::Features::prog_info_map_ids(&self) -> bool
impl core::default::Default for aya_obj::Features impl core::default::Default for aya_obj::Features
pub fn aya_obj::Features::default() -> aya_obj::Features pub fn aya_obj::Features::default() -> aya_obj::Features
impl core::fmt::Debug for aya_obj::Features impl core::fmt::Debug for aya_obj::Features
@ -9541,8 +9539,6 @@ pub fn aya_obj::Features::bpf_probe_read_kernel(&self) -> bool
pub fn aya_obj::Features::btf(&self) -> core::option::Option<&aya_obj::btf::BtfFeatures> pub fn aya_obj::Features::btf(&self) -> core::option::Option<&aya_obj::btf::BtfFeatures>
pub fn aya_obj::Features::cpumap_prog_id(&self) -> bool pub fn aya_obj::Features::cpumap_prog_id(&self) -> bool
pub fn aya_obj::Features::devmap_prog_id(&self) -> bool pub fn aya_obj::Features::devmap_prog_id(&self) -> bool
pub fn aya_obj::Features::prog_info_gpl_compatible(&self) -> bool
pub fn aya_obj::Features::prog_info_map_ids(&self) -> bool
impl core::default::Default for aya_obj::Features impl core::default::Default for aya_obj::Features
pub fn aya_obj::Features::default() -> aya_obj::Features pub fn aya_obj::Features::default() -> aya_obj::Features
impl core::fmt::Debug for aya_obj::Features impl core::fmt::Debug for aya_obj::Features

@ -9997,6 +9997,9 @@ impl aya::programs::MultiProgram for aya::programs::tc::SchedClassifier
pub fn aya::programs::tc::SchedClassifier::fd(&self) -> core::result::Result<std::os::fd::owned::BorrowedFd<'_>, aya::programs::ProgramError> pub fn aya::programs::tc::SchedClassifier::fd(&self) -> core::result::Result<std::os::fd::owned::BorrowedFd<'_>, aya::programs::ProgramError>
pub fn aya::programs::loaded_programs() -> impl core::iter::traits::iterator::Iterator<Item = core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>> pub fn aya::programs::loaded_programs() -> impl core::iter::traits::iterator::Iterator<Item = core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>>
pub mod aya::sys pub mod aya::sys
pub mod aya::sys::feature_probe
pub fn aya::sys::feature_probe::is_map_supported(map_type: aya::maps::MapType) -> core::result::Result<bool, aya::sys::SyscallError>
pub fn aya::sys::feature_probe::is_program_supported(program_type: aya::programs::ProgramType) -> core::result::Result<bool, aya::programs::ProgramError>
#[non_exhaustive] pub enum aya::sys::Stats #[non_exhaustive] pub enum aya::sys::Stats
pub aya::sys::Stats::RunTime pub aya::sys::Stats::RunTime
impl core::clone::Clone for aya::sys::Stats impl core::clone::Clone for aya::sys::Stats

Loading…
Cancel
Save