use bpf_rs::libbpf_sys::bpf_prog_load;
use bpf_rs::{BpfHelper, Error as BpfSysError, MapType, ProgramType};
use nix::errno::Errno;
use std::collections::HashMap;
use std::ptr;
use thiserror::Error as ThisError;
#[cfg(feature = "serde")]
use crate::serde_ext;
#[cfg(feature = "serde")]
use bpf_rs_macros::SerializeFromDisplay;
#[cfg(feature = "serde")]
use serde::Serialize;
#[non_exhaustive]
#[derive(ThisError, Debug)]
#[cfg_attr(feature = "serde", derive(SerializeFromDisplay))]
pub enum BpfError {
#[error("no bpf syscall on system")]
NoBpfSyscall,
#[error("bpf-rs::Error: {0}")]
ProbeErr(#[from] BpfSysError),
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Bpf {
pub has_bpf_syscall: bool,
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_ext::to_list"))]
pub program_types: HashMap<ProgramType, Result<bool, BpfError>>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_ext::to_list"))]
pub map_types: HashMap<MapType, Result<bool, BpfError>>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_ext::to_list_inner"))]
pub helpers: HashMap<ProgramType, Vec<Result<BpfHelper, BpfError>>>,
}
impl Bpf {
fn probe_syscall() -> bool {
Errno::clear();
unsafe {
bpf_prog_load(
ProgramType::Unspec.into(),
ptr::null(),
ptr::null(),
ptr::null(),
0,
ptr::null(),
);
}
Errno::last() != Errno::ENOSYS
}
fn probe_program_types() -> HashMap<ProgramType, Result<bool, BpfError>> {
ProgramType::iter()
.map(|program_type| {
(
program_type,
program_type.probe().map_err(BpfError::ProbeErr),
)
})
.collect()
}
fn probe_map_types() -> HashMap<MapType, Result<bool, BpfError>> {
MapType::iter()
.map(|map_type| (map_type, map_type.probe().map_err(BpfError::ProbeErr)))
.collect()
}
fn probe_helpers(full: bool) -> HashMap<ProgramType, Vec<Result<BpfHelper, BpfError>>> {
ProgramType::iter()
.map(|program_type| {
match program_type.probe() {
Ok(true) => {
let helpers = BpfHelper::iter()
.filter_map(|helper| {
if !full {
match helper {
BpfHelper::TracePrintk
| BpfHelper::TraceVprintk
| BpfHelper::ProbeWriteUser => return None,
_ => {}
};
}
match program_type.probe_helper(helper) {
Ok(true) => Some(Ok(helper)),
Ok(false) => None,
Err(err) => Some(Err(BpfError::ProbeErr(err))),
}
})
.collect();
(program_type, helpers)
}
Ok(false) | Err(_) => (program_type, vec![]),
}
})
.collect()
}
}
#[derive(Default)]
pub struct BpfFeaturesOpts {
pub full_helpers: bool,
}
pub fn features(opts: BpfFeaturesOpts) -> Result<Bpf, BpfError> {
if !Bpf::probe_syscall() {
return Err(BpfError::NoBpfSyscall);
}
Ok(Bpf {
has_bpf_syscall: true,
program_types: Bpf::probe_program_types(),
map_types: Bpf::probe_map_types(),
helpers: Bpf::probe_helpers(opts.full_helpers),
})
}