1mod bpf;
4pub(crate) mod feature_probe;
5mod netlink;
6mod perf_event;
7
8#[cfg(test)]
9mod fake;
10
11use std::{
12 ffi::{c_int, c_void},
13 io,
14 os::fd::{BorrowedFd, OwnedFd},
15};
16
17use aya_obj::generated::{bpf_attr, bpf_cmd, bpf_stats_type, perf_event_attr};
18pub(crate) use bpf::*;
19#[cfg(test)]
20pub(crate) use fake::*;
21pub use feature_probe::{is_map_supported, is_program_supported};
22#[doc(hidden)]
23pub use netlink::netlink_set_link_up;
24pub(crate) use netlink::*;
25pub(crate) use perf_event::*;
26use thiserror::Error;
27
28pub(crate) type SysResult = Result<i64, (i64, io::Error)>;
29
30#[cfg_attr(test, expect(dead_code, reason = "test stubs cut above this"))]
31#[derive(Debug)]
32pub(crate) enum PerfEventIoctlRequest<'a> {
33 Enable,
34 Disable,
35 SetBpf(BorrowedFd<'a>),
36}
37
38#[cfg_attr(test, expect(dead_code, reason = "test stubs cut above this"))]
39pub(crate) enum Syscall<'a> {
40 Ebpf {
41 cmd: bpf_cmd,
42 attr: &'a mut bpf_attr,
43 },
44 PerfEventOpen {
45 attr: perf_event_attr,
46 pid: libc::pid_t,
47 cpu: i32,
48 group: i32,
49 flags: u32,
50 },
51 PerfEventIoctl {
52 fd: BorrowedFd<'a>,
53 request: PerfEventIoctlRequest<'a>,
54 },
55}
56
57#[derive(Debug, Error)]
59#[error("`{call}` failed")]
60pub struct SyscallError {
61 pub call: &'static str,
63 #[source]
65 pub io_error: io::Error,
66}
67
68impl std::fmt::Debug for Syscall<'_> {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 match self {
71 Self::Ebpf { cmd, attr: _ } => f
72 .debug_struct("Syscall::Ebpf")
73 .field("cmd", cmd)
74 .field("attr", &format_args!("_"))
75 .finish(),
76 Self::PerfEventOpen {
77 attr: _,
78 pid,
79 cpu,
80 group,
81 flags,
82 } => f
83 .debug_struct("Syscall::PerfEventOpen")
84 .field("attr", &format_args!("_"))
85 .field("pid", pid)
86 .field("cpu", cpu)
87 .field("group", group)
88 .field("flags", flags)
89 .finish(),
90 Self::PerfEventIoctl { fd, request } => f
91 .debug_struct("Syscall::PerfEventIoctl")
92 .field("fd", fd)
93 .field("request", request)
94 .finish(),
95 }
96 }
97}
98
99fn syscall(call: Syscall<'_>) -> SysResult {
100 #[cfg(test)]
101 {
102 TEST_SYSCALL.with(|test_impl| unsafe { test_impl.borrow()(call) })
103 }
104
105 #[cfg(not(test))]
106 {
107 let ret = unsafe {
108 match call {
109 Syscall::Ebpf { cmd, attr } => {
110 libc::syscall(libc::SYS_bpf, cmd, attr, size_of::<bpf_attr>())
111 }
112 Syscall::PerfEventOpen {
113 attr,
114 pid,
115 cpu,
116 group,
117 flags,
118 } => libc::syscall(libc::SYS_perf_event_open, &attr, pid, cpu, group, flags),
119 Syscall::PerfEventIoctl { fd, request } => {
120 use std::os::fd::AsRawFd as _;
121
122 let fd = fd.as_raw_fd();
123 match request {
124 PerfEventIoctlRequest::Enable => libc::syscall(
125 libc::SYS_ioctl,
126 fd,
127 aya_obj::generated::PERF_EVENT_IOC_ENABLE,
128 ),
129 PerfEventIoctlRequest::Disable => libc::syscall(
130 libc::SYS_ioctl,
131 fd,
132 aya_obj::generated::PERF_EVENT_IOC_DISABLE,
133 ),
134 PerfEventIoctlRequest::SetBpf(bpf_fd) => libc::syscall(
135 libc::SYS_ioctl,
136 fd,
137 aya_obj::generated::PERF_EVENT_IOC_SET_BPF,
138 bpf_fd.as_raw_fd(),
139 ),
140 }
141 }
142 }
143 };
144 #[expect(clippy::allow_attributes, reason = "architecture specific")]
146 #[allow(clippy::useless_conversion, reason = "architecture specific")]
147 let ret: i64 = ret.into();
148
149 match ret {
150 0.. => Ok(ret),
151 ret => Err((ret, io::Error::last_os_error())),
152 }
153 }
154}
155
156#[cfg_attr(
157 test,
158 expect(unused_variables, reason = "TODO: we should validate all arguments")
159)]
160pub(crate) unsafe fn mmap(
161 addr: *mut c_void,
162 len: usize,
163 prot: c_int,
164 flags: c_int,
165 fd: BorrowedFd<'_>,
166 offset: libc::off_t,
167) -> *mut c_void {
168 #[cfg(test)]
169 {
170 TEST_MMAP_RET.with(|ret| *ret.borrow())
171 }
172
173 #[cfg(not(test))]
174 {
175 use std::os::fd::AsRawFd as _;
176
177 unsafe { libc::mmap(addr, len, prot, flags, fd.as_raw_fd(), offset) }
178 }
179}
180
181#[cfg_attr(
182 test,
183 expect(clippy::missing_const_for_fn, reason = "only const in cfg(test)"),
184 expect(unused_variables, reason = "TODO: we should validate all arguments")
185)]
186pub(crate) unsafe fn munmap(addr: *mut c_void, len: usize) -> c_int {
187 #[cfg(test)]
188 {
189 0
190 }
191
192 #[cfg(not(test))]
193 {
194 unsafe { libc::munmap(addr, len) }
195 }
196}
197
198#[non_exhaustive]
200#[doc(alias = "bpf_stats_type")]
201#[derive(Copy, Clone, Debug)]
202pub enum Stats {
203 #[doc(alias = "BPF_STATS_RUN_TIME")]
206 RunTime,
207}
208
209impl From<Stats> for bpf_stats_type {
210 fn from(value: Stats) -> Self {
211 match value {
212 Stats::RunTime => Self::BPF_STATS_RUN_TIME,
213 }
214 }
215}
216
217#[doc(alias = "BPF_ENABLE_STATS")]
243pub fn enable_stats(stats_type: Stats) -> Result<OwnedFd, SyscallError> {
244 bpf_enable_stats(stats_type.into()).map(super::MockableFd::into_inner)
245}