aya/bpf.rs
1use std::{
2 borrow::Cow,
3 collections::{HashMap, HashSet},
4 fs, io,
5 os::{
6 fd::{AsFd as _, AsRawFd as _},
7 raw::c_int,
8 },
9 path::{Path, PathBuf},
10 sync::{Arc, LazyLock},
11};
12
13use aya_obj::{
14 btf::{BtfFeatures, BtfRelocationError},
15 generated::{BPF_F_SLEEPABLE, BPF_F_XDP_HAS_FRAGS},
16 relocation::EbpfRelocationError,
17 EbpfSectionKind, Features,
18};
19use log::{debug, warn};
20use thiserror::Error;
21
22use crate::{
23 generated::{
24 bpf_map_type::{self, *},
25 AYA_PERF_EVENT_IOC_DISABLE, AYA_PERF_EVENT_IOC_ENABLE, AYA_PERF_EVENT_IOC_SET_BPF,
26 },
27 maps::{Map, MapData, MapError},
28 obj::{
29 btf::{Btf, BtfError},
30 Object, ParseError, ProgramSection,
31 },
32 programs::{
33 BtfTracePoint, CgroupDevice, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr,
34 CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, KProbe, LircMode2, Lsm, PerfEvent,
35 ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkLookup,
36 SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
37 },
38 sys::{
39 bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported,
40 is_btf_datasec_supported, is_btf_decl_tag_supported, is_btf_enum64_supported,
41 is_btf_float_supported, is_btf_func_global_supported, is_btf_func_supported,
42 is_btf_supported, is_btf_type_tag_supported, is_info_gpl_compatible_supported,
43 is_info_map_ids_supported, is_perf_link_supported, is_probe_read_kernel_supported,
44 is_prog_id_supported, is_prog_name_supported, retry_with_verifier_logs,
45 },
46 util::{bytes_of, bytes_of_slice, nr_cpus, page_size},
47};
48
49pub(crate) const BPF_OBJ_NAME_LEN: usize = 16;
50
51pub(crate) const PERF_EVENT_IOC_ENABLE: c_int = AYA_PERF_EVENT_IOC_ENABLE;
52pub(crate) const PERF_EVENT_IOC_DISABLE: c_int = AYA_PERF_EVENT_IOC_DISABLE;
53pub(crate) const PERF_EVENT_IOC_SET_BPF: c_int = AYA_PERF_EVENT_IOC_SET_BPF;
54
55/// Marker trait for types that can safely be converted to and from byte slices.
56pub unsafe trait Pod: Copy + 'static {}
57
58macro_rules! unsafe_impl_pod {
59 ($($struct_name:ident),+ $(,)?) => {
60 $(
61 unsafe impl Pod for $struct_name { }
62 )+
63 }
64}
65
66unsafe_impl_pod!(i8, u8, i16, u16, i32, u32, i64, u64, u128, i128);
67
68// It only makes sense that an array of POD types is itself POD
69unsafe impl<T: Pod, const N: usize> Pod for [T; N] {}
70
71pub use aya_obj::maps::{bpf_map_def, PinningType};
72
73pub(crate) static FEATURES: LazyLock<Features> = LazyLock::new(detect_features);
74
75fn detect_features() -> Features {
76 let btf = if is_btf_supported() {
77 Some(BtfFeatures::new(
78 is_btf_func_supported(),
79 is_btf_func_global_supported(),
80 is_btf_datasec_supported(),
81 is_btf_float_supported(),
82 is_btf_decl_tag_supported(),
83 is_btf_type_tag_supported(),
84 is_btf_enum64_supported(),
85 ))
86 } else {
87 None
88 };
89 let f = Features::new(
90 is_prog_name_supported(),
91 is_probe_read_kernel_supported(),
92 is_perf_link_supported(),
93 is_bpf_global_data_supported(),
94 is_bpf_cookie_supported(),
95 is_prog_id_supported(BPF_MAP_TYPE_CPUMAP),
96 is_prog_id_supported(BPF_MAP_TYPE_DEVMAP),
97 is_info_map_ids_supported(),
98 is_info_gpl_compatible_supported(),
99 btf,
100 );
101 debug!("BPF Feature Detection: {:#?}", f);
102 f
103}
104
105/// Returns a reference to the detected BPF features.
106pub fn features() -> &'static Features {
107 &FEATURES
108}
109
110/// Builder style API for advanced loading of eBPF programs.
111///
112/// Loading eBPF code involves a few steps, including loading maps and applying
113/// relocations. You can use `EbpfLoader` to customize some of the loading
114/// options.
115///
116/// # Examples
117///
118/// ```no_run
119/// use aya::{EbpfLoader, Btf};
120/// use std::fs;
121///
122/// let bpf = EbpfLoader::new()
123/// // load the BTF data from /sys/kernel/btf/vmlinux
124/// .btf(Btf::from_sys_fs().ok().as_ref())
125/// // load pinned maps from /sys/fs/bpf/my-program
126/// .map_pin_path("/sys/fs/bpf/my-program")
127/// // finally load the code
128/// .load_file("file.o")?;
129/// # Ok::<(), aya::EbpfError>(())
130/// ```
131#[derive(Debug)]
132pub struct EbpfLoader<'a> {
133 btf: Option<Cow<'a, Btf>>,
134 map_pin_path: Option<PathBuf>,
135 globals: HashMap<&'a str, (&'a [u8], bool)>,
136 max_entries: HashMap<&'a str, u32>,
137 extensions: HashSet<&'a str>,
138 verifier_log_level: VerifierLogLevel,
139 allow_unsupported_maps: bool,
140}
141
142/// Builder style API for advanced loading of eBPF programs.
143#[deprecated(since = "0.13.0", note = "use `EbpfLoader` instead")]
144pub type BpfLoader<'a> = EbpfLoader<'a>;
145
146bitflags::bitflags! {
147 /// Used to set the verifier log level flags in [EbpfLoader](EbpfLoader::verifier_log_level()).
148 #[derive(Clone, Copy, Debug)]
149 pub struct VerifierLogLevel: u32 {
150 /// Sets no verifier logging.
151 const DISABLE = 0;
152 /// Enables debug verifier logging.
153 const DEBUG = 1;
154 /// Enables verbose verifier logging.
155 const VERBOSE = 2 | Self::DEBUG.bits();
156 /// Enables verifier stats.
157 const STATS = 4;
158 }
159}
160
161impl Default for VerifierLogLevel {
162 fn default() -> Self {
163 Self::DEBUG | Self::STATS
164 }
165}
166
167impl<'a> EbpfLoader<'a> {
168 /// Creates a new loader instance.
169 pub fn new() -> Self {
170 Self {
171 btf: Btf::from_sys_fs().ok().map(Cow::Owned),
172 map_pin_path: None,
173 globals: HashMap::new(),
174 max_entries: HashMap::new(),
175 extensions: HashSet::new(),
176 verifier_log_level: VerifierLogLevel::default(),
177 allow_unsupported_maps: false,
178 }
179 }
180
181 /// Sets the target [BTF](Btf) info.
182 ///
183 /// The loader defaults to loading `BTF` info using [Btf::from_sys_fs].
184 /// Use this method if you want to load `BTF` from a custom location or
185 /// pass `None` to disable `BTF` relocations entirely.
186 /// # Example
187 ///
188 /// ```no_run
189 /// use aya::{EbpfLoader, Btf, Endianness};
190 ///
191 /// let bpf = EbpfLoader::new()
192 /// // load the BTF data from a custom location
193 /// .btf(Btf::parse_file("/custom_btf_file", Endianness::default()).ok().as_ref())
194 /// .load_file("file.o")?;
195 ///
196 /// # Ok::<(), aya::EbpfError>(())
197 /// ```
198 pub fn btf(&mut self, btf: Option<&'a Btf>) -> &mut Self {
199 self.btf = btf.map(Cow::Borrowed);
200 self
201 }
202
203 /// Allows programs containing unsupported maps to be loaded.
204 ///
205 /// By default programs containing unsupported maps will fail to load. This
206 /// method can be used to configure the loader so that unsupported maps will
207 /// be loaded, but won't be accessible from userspace. Can be useful when
208 /// using unsupported maps that are only accessed from eBPF code and don't
209 /// require any userspace interaction.
210 ///
211 /// # Example
212 ///
213 /// ```no_run
214 /// use aya::EbpfLoader;
215 ///
216 /// let bpf = EbpfLoader::new()
217 /// .allow_unsupported_maps()
218 /// .load_file("file.o")?;
219 /// # Ok::<(), aya::EbpfError>(())
220 /// ```
221 ///
222 pub fn allow_unsupported_maps(&mut self) -> &mut Self {
223 self.allow_unsupported_maps = true;
224 self
225 }
226
227 /// Sets the base directory path for pinned maps.
228 ///
229 /// Pinned maps will be loaded from `path/MAP_NAME`.
230 /// The caller is responsible for ensuring the directory exists.
231 ///
232 /// # Example
233 ///
234 /// ```no_run
235 /// use aya::EbpfLoader;
236 ///
237 /// let bpf = EbpfLoader::new()
238 /// .map_pin_path("/sys/fs/bpf/my-program")
239 /// .load_file("file.o")?;
240 /// # Ok::<(), aya::EbpfError>(())
241 /// ```
242 ///
243 pub fn map_pin_path<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
244 self.map_pin_path = Some(path.as_ref().to_owned());
245 self
246 }
247
248 /// Sets the value of a global variable.
249 ///
250 /// If the `must_exist` argument is `true`, [`EbpfLoader::load`] will fail with [`ParseError::SymbolNotFound`] if the loaded object code does not contain the variable.
251 ///
252 /// From Rust eBPF, a global variable can be defined as follows:
253 ///
254 /// ```no_run
255 /// #[no_mangle]
256 /// static VERSION: i32 = 0;
257 /// ```
258 ///
259 /// Then it can be accessed using `core::ptr::read_volatile`:
260 ///
261 /// ```no_run
262 /// # #[no_mangle]
263 /// # static VERSION: i32 = 0;
264 /// # unsafe fn try_test() {
265 /// let version = core::ptr::read_volatile(&VERSION);
266 /// # }
267 /// ```
268 ///
269 /// The type of a global variable must be `Pod` (plain old data), for instance `u8`, `u32` and
270 /// all other primitive types. You may use custom types as well, but you must ensure that those
271 /// types are `#[repr(C)]` and only contain other `Pod` types.
272 ///
273 /// From C eBPF, you would annotate a global variable as `volatile const`.
274 ///
275 /// # Example
276 ///
277 /// ```no_run
278 /// use aya::EbpfLoader;
279 ///
280 /// let bpf = EbpfLoader::new()
281 /// .set_global("VERSION", &2, true)
282 /// .set_global("PIDS", &[1234u16, 5678], true)
283 /// .load_file("file.o")?;
284 /// # Ok::<(), aya::EbpfError>(())
285 /// ```
286 ///
287 pub fn set_global<T: Into<GlobalData<'a>>>(
288 &mut self,
289 name: &'a str,
290 value: T,
291 must_exist: bool,
292 ) -> &mut Self {
293 self.globals.insert(name, (value.into().bytes, must_exist));
294 self
295 }
296
297 /// Set the max_entries for specified map.
298 ///
299 /// Overwrite the value of max_entries of the map that matches
300 /// the provided name before the map is created.
301 ///
302 /// # Example
303 ///
304 /// ```no_run
305 /// use aya::EbpfLoader;
306 ///
307 /// let bpf = EbpfLoader::new()
308 /// .set_max_entries("map", 64)
309 /// .load_file("file.o")?;
310 /// # Ok::<(), aya::EbpfError>(())
311 /// ```
312 ///
313 pub fn set_max_entries(&mut self, name: &'a str, size: u32) -> &mut Self {
314 self.max_entries.insert(name, size);
315 self
316 }
317
318 /// Treat the provided program as an [`Extension`]
319 ///
320 /// When attempting to load the program with the provided `name`
321 /// the program type is forced to be ] [`Extension`] and is not
322 /// inferred from the ELF section name.
323 ///
324 /// # Example
325 ///
326 /// ```no_run
327 /// use aya::EbpfLoader;
328 ///
329 /// let bpf = EbpfLoader::new()
330 /// .extension("myfunc")
331 /// .load_file("file.o")?;
332 /// # Ok::<(), aya::EbpfError>(())
333 /// ```
334 ///
335 pub fn extension(&mut self, name: &'a str) -> &mut Self {
336 self.extensions.insert(name);
337 self
338 }
339
340 /// Sets BPF verifier log level.
341 ///
342 /// # Example
343 ///
344 /// ```no_run
345 /// use aya::{EbpfLoader, VerifierLogLevel};
346 ///
347 /// let bpf = EbpfLoader::new()
348 /// .verifier_log_level(VerifierLogLevel::VERBOSE | VerifierLogLevel::STATS)
349 /// .load_file("file.o")?;
350 /// # Ok::<(), aya::EbpfError>(())
351 /// ```
352 ///
353 pub fn verifier_log_level(&mut self, level: VerifierLogLevel) -> &mut Self {
354 self.verifier_log_level = level;
355 self
356 }
357
358 /// Loads eBPF bytecode from a file.
359 ///
360 /// # Examples
361 ///
362 /// ```no_run
363 /// use aya::EbpfLoader;
364 ///
365 /// let bpf = EbpfLoader::new().load_file("file.o")?;
366 /// # Ok::<(), aya::EbpfError>(())
367 /// ```
368 pub fn load_file<P: AsRef<Path>>(&mut self, path: P) -> Result<Ebpf, EbpfError> {
369 let path = path.as_ref();
370 self.load(&fs::read(path).map_err(|error| EbpfError::FileError {
371 path: path.to_owned(),
372 error,
373 })?)
374 }
375
376 /// Loads eBPF bytecode from a buffer.
377 ///
378 /// # Examples
379 ///
380 /// ```no_run
381 /// use aya::EbpfLoader;
382 /// use std::fs;
383 ///
384 /// let data = fs::read("file.o").unwrap();
385 /// let bpf = EbpfLoader::new().load(&data)?;
386 /// # Ok::<(), aya::EbpfError>(())
387 /// ```
388 pub fn load(&mut self, data: &[u8]) -> Result<Ebpf, EbpfError> {
389 let Self {
390 btf,
391 map_pin_path,
392 globals,
393 max_entries,
394 extensions,
395 verifier_log_level,
396 allow_unsupported_maps,
397 } = self;
398 let mut obj = Object::parse(data)?;
399 obj.patch_map_data(globals.clone())?;
400
401 let btf_fd = if let Some(features) = &FEATURES.btf() {
402 if let Some(btf) = obj.fixup_and_sanitize_btf(features)? {
403 match load_btf(btf.to_bytes(), *verifier_log_level) {
404 Ok(btf_fd) => Some(Arc::new(btf_fd)),
405 // Only report an error here if the BTF is truly needed, otherwise proceed without.
406 Err(err) => {
407 for program in obj.programs.values() {
408 match program.section {
409 ProgramSection::Extension
410 | ProgramSection::FEntry { sleepable: _ }
411 | ProgramSection::FExit { sleepable: _ }
412 | ProgramSection::Lsm { sleepable: _ }
413 | ProgramSection::BtfTracePoint => {
414 return Err(EbpfError::BtfError(err))
415 }
416 ProgramSection::KRetProbe
417 | ProgramSection::KProbe
418 | ProgramSection::UProbe { sleepable: _ }
419 | ProgramSection::URetProbe { sleepable: _ }
420 | ProgramSection::TracePoint
421 | ProgramSection::SocketFilter
422 | ProgramSection::Xdp {
423 frags: _,
424 attach_type: _,
425 }
426 | ProgramSection::SkMsg
427 | ProgramSection::SkSkbStreamParser
428 | ProgramSection::SkSkbStreamVerdict
429 | ProgramSection::SockOps
430 | ProgramSection::SchedClassifier
431 | ProgramSection::CgroupSkb
432 | ProgramSection::CgroupSkbIngress
433 | ProgramSection::CgroupSkbEgress
434 | ProgramSection::CgroupSockAddr { attach_type: _ }
435 | ProgramSection::CgroupSysctl
436 | ProgramSection::CgroupSockopt { attach_type: _ }
437 | ProgramSection::LircMode2
438 | ProgramSection::PerfEvent
439 | ProgramSection::RawTracePoint
440 | ProgramSection::SkLookup
441 | ProgramSection::CgroupSock { attach_type: _ }
442 | ProgramSection::CgroupDevice => {}
443 }
444 }
445
446 warn!("Object BTF couldn't be loaded in the kernel: {err}");
447
448 None
449 }
450 }
451 } else {
452 None
453 }
454 } else {
455 None
456 };
457
458 if let Some(btf) = &btf {
459 obj.relocate_btf(btf)?;
460 }
461 let mut maps = HashMap::new();
462 for (name, mut obj) in obj.maps.drain() {
463 if let (false, EbpfSectionKind::Bss | EbpfSectionKind::Data | EbpfSectionKind::Rodata) =
464 (FEATURES.bpf_global_data(), obj.section_kind())
465 {
466 continue;
467 }
468 let num_cpus = || {
469 Ok(nr_cpus().map_err(|(path, error)| EbpfError::FileError {
470 path: PathBuf::from(path),
471 error,
472 })? as u32)
473 };
474 let map_type: bpf_map_type = obj.map_type().try_into().map_err(MapError::from)?;
475 if let Some(max_entries) = max_entries_override(
476 map_type,
477 max_entries.get(name.as_str()).copied(),
478 || obj.max_entries(),
479 num_cpus,
480 || page_size() as u32,
481 )? {
482 obj.set_max_entries(max_entries)
483 }
484 match obj.map_type().try_into() {
485 Ok(BPF_MAP_TYPE_CPUMAP) => {
486 obj.set_value_size(if FEATURES.cpumap_prog_id() { 8 } else { 4 })
487 }
488 Ok(BPF_MAP_TYPE_DEVMAP | BPF_MAP_TYPE_DEVMAP_HASH) => {
489 obj.set_value_size(if FEATURES.devmap_prog_id() { 8 } else { 4 })
490 }
491 _ => (),
492 }
493 let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd());
494 let mut map = match obj.pinning() {
495 PinningType::None => MapData::create(obj, &name, btf_fd)?,
496 PinningType::ByName => {
497 // pin maps in /sys/fs/bpf by default to align with libbpf
498 // behavior https://github.com/libbpf/libbpf/blob/v1.2.2/src/libbpf.c#L2161.
499 let path = map_pin_path
500 .as_deref()
501 .unwrap_or_else(|| Path::new("/sys/fs/bpf"));
502
503 MapData::create_pinned_by_name(path, obj, &name, btf_fd)?
504 }
505 };
506 map.finalize()?;
507 maps.insert(name, map);
508 }
509
510 let text_sections = obj
511 .functions
512 .keys()
513 .map(|(section_index, _)| *section_index)
514 .collect();
515
516 obj.relocate_maps(
517 maps.iter()
518 .map(|(s, data)| (s.as_str(), data.fd().as_fd().as_raw_fd(), data.obj())),
519 &text_sections,
520 )?;
521 obj.relocate_calls(&text_sections)?;
522 obj.sanitize_functions(&FEATURES);
523
524 let programs = obj
525 .programs
526 .drain()
527 .map(|(name, prog_obj)| {
528 let function_obj = obj.functions.get(&prog_obj.function_key()).unwrap().clone();
529
530 let prog_name = if FEATURES.bpf_name() {
531 Some(name.clone())
532 } else {
533 None
534 };
535 let section = prog_obj.section.clone();
536 let obj = (prog_obj, function_obj);
537
538 let btf_fd = btf_fd.as_ref().map(Arc::clone);
539 let program = if extensions.contains(name.as_str()) {
540 Program::Extension(Extension {
541 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
542 })
543 } else {
544 match §ion {
545 ProgramSection::KProbe => Program::KProbe(KProbe {
546 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
547 kind: ProbeKind::KProbe,
548 }),
549 ProgramSection::KRetProbe => Program::KProbe(KProbe {
550 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
551 kind: ProbeKind::KRetProbe,
552 }),
553 ProgramSection::UProbe { sleepable } => {
554 let mut data =
555 ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
556 if *sleepable {
557 data.flags = BPF_F_SLEEPABLE;
558 }
559 Program::UProbe(UProbe {
560 data,
561 kind: ProbeKind::UProbe,
562 })
563 }
564 ProgramSection::URetProbe { sleepable } => {
565 let mut data =
566 ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
567 if *sleepable {
568 data.flags = BPF_F_SLEEPABLE;
569 }
570 Program::UProbe(UProbe {
571 data,
572 kind: ProbeKind::URetProbe,
573 })
574 }
575 ProgramSection::TracePoint => Program::TracePoint(TracePoint {
576 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
577 }),
578 ProgramSection::SocketFilter => Program::SocketFilter(SocketFilter {
579 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
580 }),
581 ProgramSection::Xdp {
582 frags, attach_type, ..
583 } => {
584 let mut data =
585 ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
586 if *frags {
587 data.flags = BPF_F_XDP_HAS_FRAGS;
588 }
589 Program::Xdp(Xdp {
590 data,
591 attach_type: *attach_type,
592 })
593 }
594 ProgramSection::SkMsg => Program::SkMsg(SkMsg {
595 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
596 }),
597 ProgramSection::CgroupSysctl => Program::CgroupSysctl(CgroupSysctl {
598 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
599 }),
600 ProgramSection::CgroupSockopt { attach_type, .. } => {
601 Program::CgroupSockopt(CgroupSockopt {
602 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
603 attach_type: *attach_type,
604 })
605 }
606 ProgramSection::SkSkbStreamParser => Program::SkSkb(SkSkb {
607 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
608 kind: SkSkbKind::StreamParser,
609 }),
610 ProgramSection::SkSkbStreamVerdict => Program::SkSkb(SkSkb {
611 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
612 kind: SkSkbKind::StreamVerdict,
613 }),
614 ProgramSection::SockOps => Program::SockOps(SockOps {
615 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
616 }),
617 ProgramSection::SchedClassifier => {
618 Program::SchedClassifier(SchedClassifier {
619 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
620 })
621 }
622 ProgramSection::CgroupSkb => Program::CgroupSkb(CgroupSkb {
623 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
624 expected_attach_type: None,
625 }),
626 ProgramSection::CgroupSkbIngress => Program::CgroupSkb(CgroupSkb {
627 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
628 expected_attach_type: Some(CgroupSkbAttachType::Ingress),
629 }),
630 ProgramSection::CgroupSkbEgress => Program::CgroupSkb(CgroupSkb {
631 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
632 expected_attach_type: Some(CgroupSkbAttachType::Egress),
633 }),
634 ProgramSection::CgroupSockAddr { attach_type, .. } => {
635 Program::CgroupSockAddr(CgroupSockAddr {
636 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
637 attach_type: *attach_type,
638 })
639 }
640 ProgramSection::LircMode2 => Program::LircMode2(LircMode2 {
641 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
642 }),
643 ProgramSection::PerfEvent => Program::PerfEvent(PerfEvent {
644 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
645 }),
646 ProgramSection::RawTracePoint => Program::RawTracePoint(RawTracePoint {
647 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
648 }),
649 ProgramSection::Lsm { sleepable } => {
650 let mut data =
651 ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
652 if *sleepable {
653 data.flags = BPF_F_SLEEPABLE;
654 }
655 Program::Lsm(Lsm { data })
656 }
657 ProgramSection::BtfTracePoint => Program::BtfTracePoint(BtfTracePoint {
658 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
659 }),
660 ProgramSection::FEntry { sleepable } => {
661 let mut data =
662 ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
663 if *sleepable {
664 data.flags = BPF_F_SLEEPABLE;
665 }
666 Program::FEntry(FEntry { data })
667 }
668 ProgramSection::FExit { sleepable } => {
669 let mut data =
670 ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
671 if *sleepable {
672 data.flags = BPF_F_SLEEPABLE;
673 }
674 Program::FExit(FExit { data })
675 }
676 ProgramSection::Extension => Program::Extension(Extension {
677 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
678 }),
679 ProgramSection::SkLookup => Program::SkLookup(SkLookup {
680 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
681 }),
682 ProgramSection::CgroupSock { attach_type, .. } => {
683 Program::CgroupSock(CgroupSock {
684 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
685 attach_type: *attach_type,
686 })
687 }
688 ProgramSection::CgroupDevice => Program::CgroupDevice(CgroupDevice {
689 data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
690 }),
691 }
692 };
693 (name, program)
694 })
695 .collect();
696 let maps = maps
697 .drain()
698 .map(parse_map)
699 .collect::<Result<HashMap<String, Map>, EbpfError>>()?;
700
701 if !*allow_unsupported_maps {
702 maps.iter().try_for_each(|(_, x)| match x {
703 Map::Unsupported(map) => Err(EbpfError::MapError(MapError::Unsupported {
704 map_type: map.obj().map_type(),
705 })),
706 _ => Ok(()),
707 })?;
708 };
709
710 Ok(Ebpf { maps, programs })
711 }
712}
713
714fn parse_map(data: (String, MapData)) -> Result<(String, Map), EbpfError> {
715 let (name, map) = data;
716 let map_type = bpf_map_type::try_from(map.obj().map_type()).map_err(MapError::from)?;
717 let map = match map_type {
718 BPF_MAP_TYPE_ARRAY => Map::Array(map),
719 BPF_MAP_TYPE_PERCPU_ARRAY => Map::PerCpuArray(map),
720 BPF_MAP_TYPE_PROG_ARRAY => Map::ProgramArray(map),
721 BPF_MAP_TYPE_HASH => Map::HashMap(map),
722 BPF_MAP_TYPE_LRU_HASH => Map::LruHashMap(map),
723 BPF_MAP_TYPE_PERCPU_HASH => Map::PerCpuHashMap(map),
724 BPF_MAP_TYPE_LRU_PERCPU_HASH => Map::PerCpuLruHashMap(map),
725 BPF_MAP_TYPE_PERF_EVENT_ARRAY => Map::PerfEventArray(map),
726 BPF_MAP_TYPE_RINGBUF => Map::RingBuf(map),
727 BPF_MAP_TYPE_SOCKHASH => Map::SockHash(map),
728 BPF_MAP_TYPE_SOCKMAP => Map::SockMap(map),
729 BPF_MAP_TYPE_BLOOM_FILTER => Map::BloomFilter(map),
730 BPF_MAP_TYPE_LPM_TRIE => Map::LpmTrie(map),
731 BPF_MAP_TYPE_STACK => Map::Stack(map),
732 BPF_MAP_TYPE_STACK_TRACE => Map::StackTraceMap(map),
733 BPF_MAP_TYPE_QUEUE => Map::Queue(map),
734 BPF_MAP_TYPE_CPUMAP => Map::CpuMap(map),
735 BPF_MAP_TYPE_DEVMAP => Map::DevMap(map),
736 BPF_MAP_TYPE_DEVMAP_HASH => Map::DevMapHash(map),
737 BPF_MAP_TYPE_XSKMAP => Map::XskMap(map),
738 m => {
739 warn!("The map {name} is of type {:#?} which is currently unsupported in Aya, use `allow_unsupported_maps()` to load it anyways", m);
740 Map::Unsupported(map)
741 }
742 };
743
744 Ok((name, map))
745}
746
747/// Computes the value which should be used to override the max_entries value of the map
748/// based on the user-provided override and the rules for that map type.
749fn max_entries_override(
750 map_type: bpf_map_type,
751 user_override: Option<u32>,
752 current_value: impl Fn() -> u32,
753 num_cpus: impl Fn() -> Result<u32, EbpfError>,
754 page_size: impl Fn() -> u32,
755) -> Result<Option<u32>, EbpfError> {
756 let max_entries = || user_override.unwrap_or_else(¤t_value);
757 Ok(match map_type {
758 BPF_MAP_TYPE_PERF_EVENT_ARRAY if max_entries() == 0 => Some(num_cpus()?),
759 BPF_MAP_TYPE_RINGBUF => Some(adjust_to_page_size(max_entries(), page_size()))
760 .filter(|adjusted| *adjusted != max_entries())
761 .or(user_override),
762 _ => user_override,
763 })
764}
765
766// Adjusts the byte size of a RingBuf map to match a power-of-two multiple of the page size.
767//
768// This mirrors the logic used by libbpf.
769// See https://github.com/libbpf/libbpf/blob/ec6f716eda43/src/libbpf.c#L2461-L2463
770fn adjust_to_page_size(byte_size: u32, page_size: u32) -> u32 {
771 // If the byte_size is zero, return zero and let the verifier reject the map
772 // when it is loaded. This is the behavior of libbpf.
773 if byte_size == 0 {
774 return 0;
775 }
776 // TODO: Replace with primitive method when int_roundings (https://github.com/rust-lang/rust/issues/88581)
777 // is stabilized.
778 fn div_ceil(n: u32, rhs: u32) -> u32 {
779 let d = n / rhs;
780 let r = n % rhs;
781 if r > 0 && rhs > 0 {
782 d + 1
783 } else {
784 d
785 }
786 }
787 let pages_needed = div_ceil(byte_size, page_size);
788 page_size * pages_needed.next_power_of_two()
789}
790
791#[cfg(test)]
792mod tests {
793 use crate::generated::bpf_map_type::*;
794
795 const PAGE_SIZE: u32 = 4096;
796 const NUM_CPUS: u32 = 4;
797
798 #[test]
799 fn test_adjust_to_page_size() {
800 use super::adjust_to_page_size;
801 [
802 (0, 0),
803 (4096, 1),
804 (4096, 4095),
805 (4096, 4096),
806 (8192, 4097),
807 (8192, 8192),
808 (16384, 8193),
809 ]
810 .into_iter()
811 .for_each(|(exp, input)| assert_eq!(exp, adjust_to_page_size(input, PAGE_SIZE)))
812 }
813
814 #[test]
815 fn test_max_entries_override() {
816 use super::max_entries_override;
817 [
818 (BPF_MAP_TYPE_RINGBUF, Some(1), 1, Some(PAGE_SIZE)),
819 (BPF_MAP_TYPE_RINGBUF, None, 1, Some(PAGE_SIZE)),
820 (BPF_MAP_TYPE_RINGBUF, None, PAGE_SIZE, None),
821 (BPF_MAP_TYPE_PERF_EVENT_ARRAY, None, 1, None),
822 (BPF_MAP_TYPE_PERF_EVENT_ARRAY, Some(42), 1, Some(42)),
823 (BPF_MAP_TYPE_PERF_EVENT_ARRAY, Some(0), 1, Some(NUM_CPUS)),
824 (BPF_MAP_TYPE_PERF_EVENT_ARRAY, None, 0, Some(NUM_CPUS)),
825 (BPF_MAP_TYPE_PERF_EVENT_ARRAY, None, 42, None),
826 (BPF_MAP_TYPE_ARRAY, None, 1, None),
827 (BPF_MAP_TYPE_ARRAY, Some(2), 1, Some(2)),
828 ]
829 .into_iter()
830 .for_each(|(map_type, user_override, current_value, exp)| {
831 assert_eq!(
832 exp,
833 max_entries_override(
834 map_type,
835 user_override,
836 || { current_value },
837 || Ok(NUM_CPUS),
838 || PAGE_SIZE
839 )
840 .unwrap()
841 )
842 })
843 }
844}
845
846impl Default for EbpfLoader<'_> {
847 fn default() -> Self {
848 EbpfLoader::new()
849 }
850}
851
852/// The main entry point into the library, used to work with eBPF programs and maps.
853#[derive(Debug)]
854pub struct Ebpf {
855 maps: HashMap<String, Map>,
856 programs: HashMap<String, Program>,
857}
858
859/// The main entry point into the library, used to work with eBPF programs and maps.
860#[deprecated(since = "0.13.0", note = "use `Ebpf` instead")]
861pub type Bpf = Ebpf;
862
863impl Ebpf {
864 /// Loads eBPF bytecode from a file.
865 ///
866 /// Parses the given object code file and initializes the [maps](crate::maps) defined in it. If
867 /// the kernel supports [BTF](Btf) debug info, it is automatically loaded from
868 /// `/sys/kernel/btf/vmlinux`.
869 ///
870 /// For more loading options, see [EbpfLoader].
871 ///
872 /// # Examples
873 ///
874 /// ```no_run
875 /// use aya::Ebpf;
876 ///
877 /// let bpf = Ebpf::load_file("file.o")?;
878 /// # Ok::<(), aya::EbpfError>(())
879 /// ```
880 pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Self, EbpfError> {
881 EbpfLoader::new()
882 .btf(Btf::from_sys_fs().ok().as_ref())
883 .load_file(path)
884 }
885
886 /// Loads eBPF bytecode from a buffer.
887 ///
888 /// Parses the object code contained in `data` and initializes the
889 /// [maps](crate::maps) defined in it. If the kernel supports [BTF](Btf)
890 /// debug info, it is automatically loaded from `/sys/kernel/btf/vmlinux`.
891 ///
892 /// For more loading options, see [EbpfLoader].
893 ///
894 /// # Examples
895 ///
896 /// ```no_run
897 /// use aya::{Ebpf, Btf};
898 /// use std::fs;
899 ///
900 /// let data = fs::read("file.o").unwrap();
901 /// // load the BTF data from /sys/kernel/btf/vmlinux
902 /// let bpf = Ebpf::load(&data)?;
903 /// # Ok::<(), aya::EbpfError>(())
904 /// ```
905 pub fn load(data: &[u8]) -> Result<Self, EbpfError> {
906 EbpfLoader::new()
907 .btf(Btf::from_sys_fs().ok().as_ref())
908 .load(data)
909 }
910
911 /// Returns a reference to the map with the given name.
912 ///
913 /// The returned type is mostly opaque. In order to do anything useful with it you need to
914 /// convert it to a [typed map](crate::maps).
915 ///
916 /// For more details and examples on maps and their usage, see the [maps module
917 /// documentation][crate::maps].
918 pub fn map(&self, name: &str) -> Option<&Map> {
919 self.maps.get(name)
920 }
921
922 /// Returns a mutable reference to the map with the given name.
923 ///
924 /// The returned type is mostly opaque. In order to do anything useful with it you need to
925 /// convert it to a [typed map](crate::maps).
926 ///
927 /// For more details and examples on maps and their usage, see the [maps module
928 /// documentation][crate::maps].
929 pub fn map_mut(&mut self, name: &str) -> Option<&mut Map> {
930 self.maps.get_mut(name)
931 }
932
933 /// Takes ownership of a map with the given name.
934 ///
935 /// Use this when borrowing with [`map`](crate::Ebpf::map) or [`map_mut`](crate::Ebpf::map_mut)
936 /// is not possible (eg when using the map from an async task). The returned
937 /// map will be closed on `Drop`, therefore the caller is responsible for
938 /// managing its lifetime.
939 ///
940 /// The returned type is mostly opaque. In order to do anything useful with it you need to
941 /// convert it to a [typed map](crate::maps).
942 ///
943 /// For more details and examples on maps and their usage, see the [maps module
944 /// documentation][crate::maps].
945 pub fn take_map(&mut self, name: &str) -> Option<Map> {
946 self.maps.remove(name)
947 }
948
949 /// An iterator over all the maps.
950 ///
951 /// # Examples
952 /// ```no_run
953 /// # let mut bpf = aya::Ebpf::load(&[])?;
954 /// for (name, map) in bpf.maps() {
955 /// println!(
956 /// "found map `{}`",
957 /// name,
958 /// );
959 /// }
960 /// # Ok::<(), aya::EbpfError>(())
961 /// ```
962 pub fn maps(&self) -> impl Iterator<Item = (&str, &Map)> {
963 self.maps.iter().map(|(name, map)| (name.as_str(), map))
964 }
965
966 /// A mutable iterator over all the maps.
967 ///
968 /// # Examples
969 /// ```no_run
970 /// # use std::path::Path;
971 /// # #[derive(thiserror::Error, Debug)]
972 /// # enum Error {
973 /// # #[error(transparent)]
974 /// # Ebpf(#[from] aya::EbpfError),
975 /// # #[error(transparent)]
976 /// # Pin(#[from] aya::pin::PinError)
977 /// # }
978 /// # let mut bpf = aya::Ebpf::load(&[])?;
979 /// # let pin_path = Path::new("/tmp/pin_path");
980 /// for (_, map) in bpf.maps_mut() {
981 /// map.pin(pin_path)?;
982 /// }
983 /// # Ok::<(), Error>(())
984 /// ```
985 pub fn maps_mut(&mut self) -> impl Iterator<Item = (&str, &mut Map)> {
986 self.maps.iter_mut().map(|(name, map)| (name.as_str(), map))
987 }
988
989 /// Returns a reference to the program with the given name.
990 ///
991 /// You can use this to inspect a program and its properties. To load and attach a program, use
992 /// [program_mut](Self::program_mut) instead.
993 ///
994 /// For more details on programs and their usage, see the [programs module
995 /// documentation](crate::programs).
996 ///
997 /// # Examples
998 ///
999 /// ```no_run
1000 /// # let bpf = aya::Ebpf::load(&[])?;
1001 /// let program = bpf.program("SSL_read").unwrap();
1002 /// println!("program SSL_read is of type {:?}", program.prog_type());
1003 /// # Ok::<(), aya::EbpfError>(())
1004 /// ```
1005 pub fn program(&self, name: &str) -> Option<&Program> {
1006 self.programs.get(name)
1007 }
1008
1009 /// Returns a mutable reference to the program with the given name.
1010 ///
1011 /// Used to get a program before loading and attaching it. For more details on programs and
1012 /// their usage, see the [programs module documentation](crate::programs).
1013 ///
1014 /// # Examples
1015 ///
1016 /// ```no_run
1017 /// # let mut bpf = aya::Ebpf::load(&[])?;
1018 /// use aya::programs::UProbe;
1019 ///
1020 /// let program: &mut UProbe = bpf.program_mut("SSL_read").unwrap().try_into()?;
1021 /// program.load()?;
1022 /// program.attach(Some("SSL_read"), 0, "libssl", None)?;
1023 /// # Ok::<(), aya::EbpfError>(())
1024 /// ```
1025 pub fn program_mut(&mut self, name: &str) -> Option<&mut Program> {
1026 self.programs.get_mut(name)
1027 }
1028
1029 /// An iterator over all the programs.
1030 ///
1031 /// # Examples
1032 /// ```no_run
1033 /// # let bpf = aya::Ebpf::load(&[])?;
1034 /// for (name, program) in bpf.programs() {
1035 /// println!(
1036 /// "found program `{}` of type `{:?}`",
1037 /// name,
1038 /// program.prog_type()
1039 /// );
1040 /// }
1041 /// # Ok::<(), aya::EbpfError>(())
1042 /// ```
1043 pub fn programs(&self) -> impl Iterator<Item = (&str, &Program)> {
1044 self.programs.iter().map(|(s, p)| (s.as_str(), p))
1045 }
1046
1047 /// An iterator mutably referencing all of the programs.
1048 ///
1049 /// # Examples
1050 /// ```no_run
1051 /// # use std::path::Path;
1052 /// # #[derive(thiserror::Error, Debug)]
1053 /// # enum Error {
1054 /// # #[error(transparent)]
1055 /// # Ebpf(#[from] aya::EbpfError),
1056 /// # #[error(transparent)]
1057 /// # Pin(#[from] aya::pin::PinError)
1058 /// # }
1059 /// # let mut bpf = aya::Ebpf::load(&[])?;
1060 /// # let pin_path = Path::new("/tmp/pin_path");
1061 /// for (_, program) in bpf.programs_mut() {
1062 /// program.pin(pin_path)?;
1063 /// }
1064 /// # Ok::<(), Error>(())
1065 /// ```
1066 pub fn programs_mut(&mut self) -> impl Iterator<Item = (&str, &mut Program)> {
1067 self.programs.iter_mut().map(|(s, p)| (s.as_str(), p))
1068 }
1069}
1070
1071/// The error type returned by [`Ebpf::load_file`] and [`Ebpf::load`].
1072#[derive(Debug, Error)]
1073pub enum EbpfError {
1074 /// Error loading file
1075 #[error("error loading {path}")]
1076 FileError {
1077 /// The file path
1078 path: PathBuf,
1079 #[source]
1080 /// The original io::Error
1081 error: io::Error,
1082 },
1083
1084 /// Unexpected pinning type
1085 #[error("unexpected pinning type {name}")]
1086 UnexpectedPinningType {
1087 /// The value encountered
1088 name: u32,
1089 },
1090
1091 /// Error parsing BPF object
1092 #[error("error parsing BPF object: {0}")]
1093 ParseError(#[from] ParseError),
1094
1095 /// Error parsing BTF object
1096 #[error("BTF error: {0}")]
1097 BtfError(#[from] BtfError),
1098
1099 /// Error performing relocations
1100 #[error("error relocating function")]
1101 RelocationError(#[from] EbpfRelocationError),
1102
1103 /// Error performing relocations
1104 #[error("error relocating section")]
1105 BtfRelocationError(#[from] BtfRelocationError),
1106
1107 /// No BTF parsed for object
1108 #[error("no BTF parsed for object")]
1109 NoBTF,
1110
1111 #[error("map error: {0}")]
1112 /// A map error
1113 MapError(#[from] MapError),
1114
1115 #[error("program error: {0}")]
1116 /// A program error
1117 ProgramError(#[from] ProgramError),
1118}
1119
1120/// The error type returned by [`Bpf::load_file`] and [`Bpf::load`].
1121#[deprecated(since = "0.13.0", note = "use `EbpfError` instead")]
1122pub type BpfError = EbpfError;
1123
1124fn load_btf(
1125 raw_btf: Vec<u8>,
1126 verifier_log_level: VerifierLogLevel,
1127) -> Result<crate::MockableFd, BtfError> {
1128 let (ret, verifier_log) = retry_with_verifier_logs(10, |logger| {
1129 bpf_load_btf(raw_btf.as_slice(), logger, verifier_log_level)
1130 });
1131 ret.map_err(|(_, io_error)| BtfError::LoadError {
1132 io_error,
1133 verifier_log,
1134 })
1135}
1136
1137/// Global data that can be exported to eBPF programs before they are loaded.
1138///
1139/// Valid global data includes `Pod` types and slices of `Pod` types. See also
1140/// [EbpfLoader::set_global].
1141pub struct GlobalData<'a> {
1142 bytes: &'a [u8],
1143}
1144
1145impl<'a, T: Pod> From<&'a [T]> for GlobalData<'a> {
1146 fn from(s: &'a [T]) -> Self {
1147 GlobalData {
1148 bytes: bytes_of_slice(s),
1149 }
1150 }
1151}
1152
1153impl<'a, T: Pod> From<&'a T> for GlobalData<'a> {
1154 fn from(v: &'a T) -> Self {
1155 GlobalData {
1156 // Safety: v is Pod
1157 bytes: unsafe { bytes_of(v) },
1158 }
1159 }
1160}