diff --git a/bpf/aya-bpf/src/lib.rs b/bpf/aya-bpf/src/lib.rs index 020fc772..6f7a384d 100644 --- a/bpf/aya-bpf/src/lib.rs +++ b/bpf/aya-bpf/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(never_type)] #![allow(clippy::missing_safety_doc)] #![no_std] diff --git a/bpf/aya-bpf/src/maps/mod.rs b/bpf/aya-bpf/src/maps/mod.rs index 8499ff38..c3ae05ed 100644 --- a/bpf/aya-bpf/src/maps/mod.rs +++ b/bpf/aya-bpf/src/maps/mod.rs @@ -9,6 +9,7 @@ pub mod array; pub mod hash_map; pub mod per_cpu_array; pub mod perf; +pub mod program_array; pub mod queue; pub mod sock_hash; pub mod sock_map; @@ -18,6 +19,7 @@ pub use array::Array; pub use hash_map::{HashMap, LruHashMap, LruPerCpuHashMap, PerCpuHashMap}; pub use per_cpu_array::PerCpuArray; pub use perf::{PerfEventArray, PerfEventByteArray}; +pub use program_array::ProgramArray; pub use queue::Queue; pub use sock_hash::SockHash; pub use sock_map::SockMap; diff --git a/bpf/aya-bpf/src/maps/program_array.rs b/bpf/aya-bpf/src/maps/program_array.rs new file mode 100644 index 00000000..06c91b36 --- /dev/null +++ b/bpf/aya-bpf/src/maps/program_array.rs @@ -0,0 +1,89 @@ +use core::{hint::unreachable_unchecked, mem}; + +use aya_bpf_cty::c_long; + +use crate::{ + bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_PROG_ARRAY}, + helpers::bpf_tail_call, + maps::PinningType, + BpfContext, +}; + +/// A BPF map that stores an array of program indices for tail calling. +/// +/// # Examples +/// +/// ```no_run +/// # #![allow(dead_code)] +/// use aya_bpf::{macros::map, maps::ProgramArray, cty::c_long}; +/// # use aya_bpf::{programs::LsmContext}; +/// +/// #[map] +/// static mut JUMP_TABLE: ProgramArray = ProgramArray::with_max_entries(16, 0); +/// +/// # unsafe fn try_test(ctx: &LsmContext) -> Result<(), c_long> { +/// let index: u32 = 13; +/// +/// if let Err(e) = JUMP_TABLE.tail_call(ctx, index) { +/// return Err(e); +/// } +/// +/// # Err(-1) +/// } +/// ``` +#[repr(transparent)] +pub struct ProgramArray { + def: bpf_map_def, +} + +impl ProgramArray { + pub const fn with_max_entries(max_entries: u32, flags: u32) -> ProgramArray { + ProgramArray { + def: bpf_map_def { + type_: BPF_MAP_TYPE_PROG_ARRAY, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries, + map_flags: flags, + id: 0, + pinning: PinningType::None as u32, + }, + } + } + + pub const fn pinned(max_entries: u32, flags: u32) -> ProgramArray { + ProgramArray { + def: bpf_map_def { + type_: BPF_MAP_TYPE_PROG_ARRAY, + key_size: mem::size_of::() as u32, + value_size: mem::size_of::() as u32, + max_entries, + map_flags: flags, + id: 0, + pinning: PinningType::ByName as u32, + }, + } + } + + /// Perform a tail call into a program indexed by this map. + /// + /// # Safety + /// + /// This function is inherently unsafe, since it causes control flow to jump into + /// another eBPF program. This can have side effects, such as drop methods not being + /// called. Note that tail calling into an eBPF program is not the same thing as + /// a function call -- control flow never returns to the caller. + /// + /// # Return Value + /// + /// On success, this function **does not return** into the original program. + /// On failure, a negative error is returned, wrapped in `Err()`. + pub unsafe fn tail_call(&mut self, ctx: &C, index: u32) -> Result { + let res = bpf_tail_call(ctx.as_ptr(), &mut self.def as *mut _ as *mut _, index); + if res != 0 { + Err(res) + } else { + unreachable_unchecked() + } + } +}