Skip to content

feat(aya): Add is_program_type_supported #1145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions aya/src/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,18 @@ use crate::{
programs::{
BtfTracePoint, CgroupDevice, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr,
CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, Iter, KProbe, LircMode2, Lsm,
PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier,
SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
PerfEvent, ProbeKind, Program, ProgramData, ProgramError, ProgramType, RawTracePoint,
SchedClassifier, SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint,
UProbe, Xdp,
},
sys::{
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_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_info_map_ids_supported, is_perf_link_supported, is_probe_read_kernel_supported,
is_prog_id_supported, is_prog_name_supported, retry_with_verifier_logs,
is_prog_id_supported, is_prog_name_supported, is_prog_type_supported,
retry_with_verifier_logs,
},
util::{bytes_of, bytes_of_slice, nr_cpus, page_size},
};
Expand Down Expand Up @@ -105,6 +107,26 @@ pub fn features() -> &'static Features {
&FEATURES
}

/// Returns whether a program type is supported by the running kernel.
///
/// # Errors
///
/// Returns an error if an unexpected error occurs while checking the program
/// type support.
///
/// # Example
///
/// ```no_run
/// use aya::{is_program_type_supported, programs::ProgramType};
///
/// let supported = is_program_type_supported(ProgramType::Xdp)?;
/// # Ok::<(), aya::EbpfError>(())
/// ```
pub fn is_program_type_supported(program_type: ProgramType) -> Result<bool, EbpfError> {
is_prog_type_supported(program_type.into())
.map_err(|e| EbpfError::ProgramError(ProgramError::from(e)))
}

/// Builder style API for advanced loading of eBPF programs.
///
/// Loading eBPF code involves a few steps, including loading maps and applying
Expand Down
40 changes: 40 additions & 0 deletions aya/src/programs/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,46 @@ pub enum ProgramType {
Netfilter = bpf_prog_type::BPF_PROG_TYPE_NETFILTER as isize,
}

impl From<ProgramType> for bpf_prog_type {
fn from(value: ProgramType) -> Self {
match value {
ProgramType::Unspecified => Self::BPF_PROG_TYPE_UNSPEC,
ProgramType::SocketFilter => Self::BPF_PROG_TYPE_SOCKET_FILTER,
ProgramType::KProbe => Self::BPF_PROG_TYPE_KPROBE,
ProgramType::SchedClassifier => Self::BPF_PROG_TYPE_SCHED_CLS,
ProgramType::SchedAction => Self::BPF_PROG_TYPE_SCHED_ACT,
ProgramType::TracePoint => Self::BPF_PROG_TYPE_TRACEPOINT,
ProgramType::Xdp => Self::BPF_PROG_TYPE_XDP,
ProgramType::PerfEvent => Self::BPF_PROG_TYPE_PERF_EVENT,
ProgramType::CgroupSkb => Self::BPF_PROG_TYPE_CGROUP_SKB,
ProgramType::CgroupSock => Self::BPF_PROG_TYPE_CGROUP_SOCK,
ProgramType::LwtInput => Self::BPF_PROG_TYPE_LWT_IN,
ProgramType::LwtOutput => Self::BPF_PROG_TYPE_LWT_OUT,
ProgramType::LwtXmit => Self::BPF_PROG_TYPE_LWT_XMIT,
ProgramType::SockOps => Self::BPF_PROG_TYPE_SOCK_OPS,
ProgramType::SkSkb => Self::BPF_PROG_TYPE_SK_SKB,
ProgramType::CgroupDevice => Self::BPF_PROG_TYPE_CGROUP_DEVICE,
ProgramType::SkMsg => Self::BPF_PROG_TYPE_SK_MSG,
ProgramType::RawTracePoint => Self::BPF_PROG_TYPE_RAW_TRACEPOINT,
ProgramType::CgroupSockAddr => Self::BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
ProgramType::LwtSeg6local => Self::BPF_PROG_TYPE_LWT_SEG6LOCAL,
ProgramType::LircMode2 => Self::BPF_PROG_TYPE_LIRC_MODE2,
ProgramType::SkReuseport => Self::BPF_PROG_TYPE_SK_REUSEPORT,
ProgramType::FlowDissector => Self::BPF_PROG_TYPE_FLOW_DISSECTOR,
ProgramType::CgroupSysctl => Self::BPF_PROG_TYPE_CGROUP_SYSCTL,
ProgramType::RawTracePointWritable => Self::BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
ProgramType::CgroupSockopt => Self::BPF_PROG_TYPE_CGROUP_SOCKOPT,
ProgramType::Tracing => Self::BPF_PROG_TYPE_TRACING,
ProgramType::StructOps => Self::BPF_PROG_TYPE_STRUCT_OPS,
ProgramType::Extension => Self::BPF_PROG_TYPE_EXT,
ProgramType::Lsm => Self::BPF_PROG_TYPE_LSM,
ProgramType::SkLookup => Self::BPF_PROG_TYPE_SK_LOOKUP,
ProgramType::Syscall => Self::BPF_PROG_TYPE_SYSCALL,
ProgramType::Netfilter => Self::BPF_PROG_TYPE_NETFILTER,
}
}
}

impl TryFrom<bpf_prog_type> for ProgramType {
type Error = ProgramError;

Expand Down
88 changes: 55 additions & 33 deletions aya/src/sys/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::{
};

use assert_matches::assert_matches;
use libc::{ENOENT, ENOSPC};
use libc::{E2BIG, EINVAL, ENOENT, ENOSPC};
use obj::{
btf::{BtfEnum64, Enum64},
generated::bpf_stats_type,
Expand Down Expand Up @@ -736,6 +736,56 @@ pub(crate) fn bpf_btf_get_fd_by_id(id: u32) -> Result<crate::MockableFd, Syscall
})
}

// This program is to test the availability of eBPF features.
// It is a simple program that returns immediately and should always pass the
// verifier regardless of the program type.
// The fields conforming an encoded basic instruction are stored in the following order:
// opcode:8 src_reg:4 dst_reg:4 offset:16 imm:32 - In little-endian BPF.
// opcode:8 dst_reg:4 src_reg:4 offset:16 imm:32 - In big-endian BPF.
// Multi-byte fields ('imm' and 'offset') are stored using endian order.
// https://www.kernel.org/doc/html/v6.4-rc7/bpf/instruction-set.html#instruction-encoding
const TEST_PROG: &[u8] = &[
0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r0 = 0
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
];

pub(crate) fn is_prog_type_supported(kind: bpf_prog_type) -> Result<bool, SyscallError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 };
u.prog_type = kind as u32;
let mut name: [c_char; 16] = [0; 16];
let cstring = CString::new("aya_prog_check").unwrap();
let name_bytes = cstring.to_bytes();
let len = cmp::min(name.len(), name_bytes.len());
name[..len].copy_from_slice(unsafe {
slice::from_raw_parts(name_bytes.as_ptr() as *const c_char, len)
});
u.prog_name = name;

let gpl = b"GPL\0";
u.license = gpl.as_ptr() as u64;

let insns = copy_instructions(TEST_PROG).unwrap();
u.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32;

let res = bpf_prog_load(&mut attr);
match res {
Ok(_) => Ok(true),
Err((_, e)) => {
if e.raw_os_error() == Some(EINVAL) || e.raw_os_error() == Some(E2BIG) {
Ok(false)
} else {
Err(SyscallError {
call: "bpf_prog_load",
io_error: e,
})
}
}
}
}

pub(crate) fn is_prog_name_supported() -> bool {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 };
Expand All @@ -748,20 +798,10 @@ pub(crate) fn is_prog_name_supported() -> bool {
});
u.prog_name = name;

// The fields conforming an encoded basic instruction are stored in the following order:
// opcode:8 src_reg:4 dst_reg:4 offset:16 imm:32 - In little-endian BPF.
// opcode:8 dst_reg:4 src_reg:4 offset:16 imm:32 - In big-endian BPF.
// Multi-byte fields ('imm' and 'offset') are stored using endian order.
// https://www.kernel.org/doc/html/v6.4-rc7/bpf/instruction-set.html#instruction-encoding
let prog: &[u8] = &[
0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r0 = 0
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
];

let gpl = b"GPL\0";
u.license = gpl.as_ptr() as u64;

let insns = copy_instructions(prog).unwrap();
let insns = copy_instructions(TEST_PROG).unwrap();
u.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32;
Expand All @@ -776,11 +816,7 @@ pub(crate) fn is_info_map_ids_supported() -> bool {

u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32;

let prog: &[u8] = &[
0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r0 = 0
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
];
let insns = copy_instructions(prog).unwrap();
let insns = copy_instructions(TEST_PROG).unwrap();
u.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64;

Expand All @@ -804,11 +840,7 @@ pub(crate) fn is_info_gpl_compatible_supported() -> bool {

u.prog_type = bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER as u32;

let prog: &[u8] = &[
0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r0 = 0
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
];
let insns = copy_instructions(prog).unwrap();
let insns = copy_instructions(TEST_PROG).unwrap();
u.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64;

Expand Down Expand Up @@ -868,20 +900,10 @@ pub(crate) fn is_perf_link_supported() -> bool {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 };

// The fields conforming an encoded basic instruction are stored in the following order:
// opcode:8 src_reg:4 dst_reg:4 offset:16 imm:32 - In little-endian BPF.
// opcode:8 dst_reg:4 src_reg:4 offset:16 imm:32 - In big-endian BPF.
// Multi-byte fields ('imm' and 'offset') are stored using endian order.
// https://www.kernel.org/doc/html/v6.4-rc7/bpf/instruction-set.html#instruction-encoding
let prog: &[u8] = &[
0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r0 = 0
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
];

let gpl = b"GPL\0";
u.license = gpl.as_ptr() as u64;

let insns = copy_instructions(prog).unwrap();
let insns = copy_instructions(TEST_PROG).unwrap();
u.insn_cnt = insns.len() as u32;
u.insns = insns.as_ptr() as u64;
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32;
Expand Down
15 changes: 14 additions & 1 deletion test/integration-test/src/tests/smoke.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
use aya::{
programs::{Extension, TracePoint, Xdp, XdpFlags},
is_program_type_supported,
programs::{Extension, ProgramType, TracePoint, Xdp, XdpFlags},
util::KernelVersion,
Ebpf, EbpfLoader,
};
use test_log::test;

use crate::utils::NetNsGuard;

#[test]
fn progam_is_supported() {
// All of these program types have been supported for a long time and are
// used in our tests without any special checks.

assert!(is_program_type_supported(ProgramType::Xdp).unwrap());
assert!(is_program_type_supported(ProgramType::TracePoint).unwrap());
// Kprobe and uprobe are the same program type
assert!(is_program_type_supported(ProgramType::KProbe).unwrap());
assert!(is_program_type_supported(ProgramType::SchedClassifier).unwrap());
}

#[test]
fn xdp() {
let kernel_version = KernelVersion::current().unwrap();
Expand Down
3 changes: 3 additions & 0 deletions xtask/public-api/aya.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7651,6 +7651,8 @@ impl core::clone::Clone for aya::programs::ProgramType
pub fn aya::programs::ProgramType::clone(&self) -> aya::programs::ProgramType
impl core::cmp::PartialEq for aya::programs::ProgramType
pub fn aya::programs::ProgramType::eq(&self, other: &aya::programs::ProgramType) -> bool
impl core::convert::From<aya::programs::ProgramType> for aya_obj::generated::linux_bindings_x86_64::bpf_prog_type
pub fn aya_obj::generated::linux_bindings_x86_64::bpf_prog_type::from(value: aya::programs::ProgramType) -> Self
impl core::convert::TryFrom<aya_obj::generated::linux_bindings_x86_64::bpf_prog_type> for aya::programs::ProgramType
pub type aya::programs::ProgramType::Error = aya::programs::ProgramError
pub fn aya::programs::ProgramType::try_from(prog_type: aya_obj::generated::linux_bindings_x86_64::bpf_prog_type) -> core::result::Result<Self, Self::Error>
Expand Down Expand Up @@ -10034,6 +10036,7 @@ impl aya::Pod for u8
impl<K: aya::Pod> aya::Pod for aya::maps::lpm_trie::Key<K>
impl<T: aya::Pod, const N: usize> aya::Pod for [T; N]
pub fn aya::features() -> &'static aya_obj::obj::Features
pub fn aya::is_program_type_supported(program_type: aya::programs::ProgramType) -> core::result::Result<bool, aya::EbpfError>
pub type aya::Bpf = aya::Ebpf
pub type aya::BpfError = aya::EbpfError
pub type aya::BpfLoader<'a> = aya::EbpfLoader<'a>
Loading