mirror of https://github.com/aya-rs/aya
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
5.3 KiB
Rust
183 lines
5.3 KiB
Rust
//! [](https://aya-rs.dev)
|
|
//!
|
|
//! A library to write eBPF programs.
|
|
//!
|
|
//! Aya-bpf is an eBPF library built with a focus on operability and developer experience.
|
|
//! It is the kernel-space counterpart of [Aya](https://docs.rs/aya)
|
|
#![doc(
|
|
html_logo_url = "https://aya-rs.dev/assets/images/crabby.svg",
|
|
html_favicon_url = "https://aya-rs.dev/assets/images/crabby.svg"
|
|
)]
|
|
#![cfg_attr(unstable, expect(incomplete_features), feature(generic_const_exprs))]
|
|
#![cfg_attr(unstable, feature(never_type))]
|
|
#![cfg_attr(target_arch = "bpf", feature(asm_experimental_arch))]
|
|
#![warn(clippy::cast_lossless, clippy::cast_sign_loss)]
|
|
#![no_std]
|
|
|
|
pub use aya_ebpf_bindings::bindings;
|
|
|
|
mod args;
|
|
pub use args::{PtRegs, RawTracepointArgs};
|
|
#[expect(clippy::missing_safety_doc, unsafe_op_in_unsafe_fn)]
|
|
pub mod helpers;
|
|
pub mod maps;
|
|
pub mod programs;
|
|
|
|
use core::{ffi::c_void, ptr::NonNull};
|
|
|
|
pub use aya_ebpf_cty as cty;
|
|
pub use aya_ebpf_macros as macros;
|
|
use cty::{c_int, c_long};
|
|
use helpers::{
|
|
bpf_get_current_comm, bpf_get_current_pid_tgid, bpf_get_current_uid_gid, bpf_map_delete_elem,
|
|
bpf_map_lookup_elem, bpf_map_update_elem,
|
|
};
|
|
|
|
pub const TASK_COMM_LEN: usize = 16;
|
|
|
|
pub trait EbpfContext {
|
|
fn as_ptr(&self) -> *mut c_void;
|
|
|
|
#[inline]
|
|
fn command(&self) -> Result<[u8; TASK_COMM_LEN], c_long> {
|
|
bpf_get_current_comm()
|
|
}
|
|
|
|
fn pid(&self) -> u32 {
|
|
bpf_get_current_pid_tgid() as u32
|
|
}
|
|
|
|
fn tgid(&self) -> u32 {
|
|
(bpf_get_current_pid_tgid() >> 32) as u32
|
|
}
|
|
|
|
fn uid(&self) -> u32 {
|
|
bpf_get_current_uid_gid() as u32
|
|
}
|
|
|
|
fn gid(&self) -> u32 {
|
|
(bpf_get_current_uid_gid() >> 32) as u32
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
#[expect(clippy::missing_safety_doc, unsafe_op_in_unsafe_fn)]
|
|
pub unsafe extern "C" fn memset(s: *mut u8, c: c_int, n: usize) {
|
|
#[expect(clippy::cast_sign_loss)]
|
|
let b = c as u8;
|
|
for i in 0..n {
|
|
*s.add(i) = b;
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
#[expect(clippy::missing_safety_doc, unsafe_op_in_unsafe_fn)]
|
|
pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *mut u8, n: usize) {
|
|
copy_forward(dest, src, n);
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
#[expect(clippy::missing_safety_doc, unsafe_op_in_unsafe_fn)]
|
|
pub unsafe extern "C" fn memmove(dest: *mut u8, src: *mut u8, n: usize) {
|
|
let delta = (dest as usize).wrapping_sub(src as usize);
|
|
if delta >= n {
|
|
// We can copy forwards because either dest is far enough ahead of src,
|
|
// or src is ahead of dest (and delta overflowed).
|
|
copy_forward(dest, src, n);
|
|
} else {
|
|
copy_backward(dest, src, n);
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
#[expect(unsafe_op_in_unsafe_fn)]
|
|
unsafe fn copy_forward(dest: *mut u8, src: *mut u8, n: usize) {
|
|
for i in 0..n {
|
|
*dest.add(i) = *src.add(i);
|
|
}
|
|
}
|
|
|
|
#[inline(always)]
|
|
#[expect(unsafe_op_in_unsafe_fn)]
|
|
unsafe fn copy_backward(dest: *mut u8, src: *mut u8, n: usize) {
|
|
for i in (0..n).rev() {
|
|
*dest.add(i) = *src.add(i);
|
|
}
|
|
}
|
|
|
|
/// Check if a value is within a range, using conditional forms compatible with
|
|
/// the verifier.
|
|
#[inline(always)]
|
|
pub fn check_bounds_signed(value: i64, lower: i64, upper: i64) -> bool {
|
|
#[cfg(target_arch = "bpf")]
|
|
unsafe {
|
|
let mut in_bounds = 0u64;
|
|
core::arch::asm!(
|
|
"if {value} s< {lower} goto +2",
|
|
"if {value} s> {upper} goto +1",
|
|
"{i} = 1",
|
|
i = inout(reg) in_bounds,
|
|
lower = in(reg) lower,
|
|
upper = in(reg) upper,
|
|
value = in(reg) value,
|
|
);
|
|
in_bounds == 1
|
|
}
|
|
// We only need this for doc tests which are compiled for the host target
|
|
#[cfg(not(target_arch = "bpf"))]
|
|
{
|
|
let _ = value;
|
|
let _ = lower;
|
|
let _ = upper;
|
|
unimplemented!()
|
|
}
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! panic_handler {
|
|
() => {
|
|
/// Defines the panic handler when compiling for eBPF.
|
|
///
|
|
/// eBPF programs don't support the concept of a panic and therefore, compiling a program that includes panics should fail at link time.
|
|
///
|
|
/// Yet, as part of compiling for `no_std`, we need to define a panic handler.
|
|
/// The eBPF verifier enforces that programs terminate and will thus reject unbounded loops.
|
|
/// By using an endless loop within our panic handler, we can ensure that if this panic handler were somehow linked into the program, the eBPF verifier would reject it.
|
|
#[cfg(not(test))]
|
|
#[panic_handler]
|
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
|
loop {}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[inline]
|
|
fn insert<K, V>(
|
|
def: *mut bindings::bpf_map_def,
|
|
key: &K,
|
|
value: &V,
|
|
flags: u64,
|
|
) -> Result<(), c_long> {
|
|
let key: *const _ = key;
|
|
let value: *const _ = value;
|
|
match unsafe { bpf_map_update_elem(def.cast(), key.cast(), value.cast(), flags) } {
|
|
0 => Ok(()),
|
|
ret => Err(ret),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn remove<K>(def: *mut bindings::bpf_map_def, key: &K) -> Result<(), c_long> {
|
|
let key: *const _ = key;
|
|
match unsafe { bpf_map_delete_elem(def.cast(), key.cast()) } {
|
|
0 => Ok(()),
|
|
ret => Err(ret),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn lookup<K, V>(def: *mut bindings::bpf_map_def, key: &K) -> Option<NonNull<V>> {
|
|
let key: *const _ = key;
|
|
NonNull::new(unsafe { bpf_map_lookup_elem(def.cast(), key.cast()) }.cast())
|
|
}
|