1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use bpf_rs::libbpf_sys::{
bpf_prog_load, BPF_FUNC_probe_write_user, BPF_FUNC_trace_printk, BPF_FUNC_trace_vprintk,
};
use bpf_rs::{BpfHelper, BpfHelperIter, 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;
#[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),
}
pub struct BpfFeaturesOpts {
pub full_helpers: bool,
}
impl Default for BpfFeaturesOpts {
fn default() -> Self {
Self {
full_helpers: false,
}
}
}
#[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(|err| BpfError::ProbeErr(err)),
)
})
.collect()
}
fn probe_map_types() -> HashMap<MapType, Result<bool, BpfError>> {
MapType::iter()
.map(|map_type| {
(
map_type,
map_type.probe().map_err(|err| BpfError::ProbeErr(err)),
)
})
.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 = BpfHelperIter::new()
.filter_map(|helper| {
if !full {
#[allow(non_upper_case_globals)]
match helper.0 {
BPF_FUNC_trace_printk
| BPF_FUNC_trace_vprintk
| BPF_FUNC_probe_write_user => 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()
}
}
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),
})
}