diff --git a/bpf/aya-bpf/src/maps/array.rs b/bpf/aya-bpf/src/maps/array.rs index 2150dd6e..687facb0 100644 --- a/bpf/aya-bpf/src/maps/array.rs +++ b/bpf/aya-bpf/src/maps/array.rs @@ -8,6 +8,20 @@ use crate::{ maps::PinningType, }; +/// A fixed-size array that can be shared between eBPF programs and user-space. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 3.19. +/// +/// # Examples +/// +/// ```no_run +/// use aya_bpf::{macros::map, maps::Array}; +/// +/// #[map] +/// static MY_ARRAY: Array = Array::with_max_entries(1024, 0); +/// ``` #[repr(transparent)] pub struct Array { def: UnsafeCell, diff --git a/bpf/aya-bpf/src/maps/hash_map.rs b/bpf/aya-bpf/src/maps/hash_map.rs index 5877be90..672d0117 100644 --- a/bpf/aya-bpf/src/maps/hash_map.rs +++ b/bpf/aya-bpf/src/maps/hash_map.rs @@ -11,6 +11,28 @@ use crate::{ maps::PinningType, }; +/// A hash map that can be shared between eBPF programs and user-space. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 3.19. +/// +/// # Examples +/// +/// ```no_run +/// use aya_bpf::{macros::map, maps::HashMap}; +/// # use aya_bpf::programs::LsmContext; +/// +/// #[map] +/// static MY_MAP: HashMap = HashMap::with_max_entries(1024, 0); +/// +/// # unsafe fn try_test(ctx: &LsmContext) -> Result { +/// let key: u32 = 13; +/// let value: u32 = 42; +/// MY_MAP.insert(&key, &value, 0).map_err(|e| e as i32)?; +/// # Ok(0) +/// # } +/// ``` #[repr(transparent)] pub struct HashMap { def: UnsafeCell, @@ -21,6 +43,16 @@ pub struct HashMap { unsafe impl Sync for HashMap {} impl HashMap { + /// Creates a `HashMap` with the maximum number of elements. + /// + /// # Examples + /// + /// ```no_run + /// use aya_bpf::{macros::map, maps::HashMap}; + /// + /// #[map] + /// static mut MY_MAP: HashMap = HashMap::with_max_entries(1024, 0); + /// ``` pub const fn with_max_entries(max_entries: u32, flags: u32) -> HashMap { HashMap { def: UnsafeCell::new(build_def::( @@ -34,6 +66,17 @@ impl HashMap { } } + /// Creates an empty `HashMap` with the specified maximum number of + /// elements, and pins it to the BPF file system (BPFFS). + /// + /// # Examples + /// + /// ```no_run + /// use aya_bpf::{macros::map, maps::HashMap}; + /// + /// #[map] + /// static mut MY_MAP: HashMap = HashMap::pinned(1024, 0); + /// ``` pub const fn pinned(max_entries: u32, flags: u32) -> HashMap { HashMap { def: UnsafeCell::new(build_def::( @@ -52,12 +95,33 @@ impl HashMap { /// make guarantee on the atomicity of `insert` or `remove`, and any element removed from the /// map might get aliased by another element in the map, causing garbage to be read, or /// corruption in case of writes. + /// + /// # Examples + /// + /// ```no_run + /// use aya_bpf::{macros::map, maps::HashMap}; + /// # use aya_bpf::programs::XdpContext; + /// + /// #[map] + /// static REDIRECT_PORTS: HashMap = HashMap::with_max_entries(1024, 0); + /// + /// # fn parse_source(ctx: &XdpContext) -> u16 { 0 } + /// # fn try_test(ctx: &XdpContext) -> Result { + /// // Source port of the packet. + /// let source: u16 = parse_source(&ctx); + /// // Port to which we want redirect the packet to. + /// let redirect = MY_MAP.get(&source); + /// + /// // Redirect the packet. + /// # Ok(0) + /// # } + /// ``` #[inline] pub unsafe fn get(&self, key: &K) -> Option<&V> { get(self.def.get(), key) } - /// Retrieve the value associate with `key` from the map. + /// Retrieves the value associate with `key` from the map. /// The same caveat as `get` applies, but this returns a raw pointer and it's up to the caller /// to decide whether it's safe to dereference the pointer or not. #[inline] @@ -65,7 +129,7 @@ impl HashMap { get_ptr(self.def.get(), key) } - /// Retrieve the value associate with `key` from the map. + /// Retrieves the value associate with `key` from the map. /// The same caveat as `get` applies, and additionally cares should be taken to avoid /// concurrent writes, but it's up to the caller to decide whether it's safe to dereference the /// pointer or not. @@ -74,11 +138,47 @@ impl HashMap { get_ptr_mut(self.def.get(), key) } + /// Inserts a key-value pair into the map. + /// + /// # Examples + /// + /// ```no_run + /// use aya_bpf::{macros::map, maps::HashMap}; + /// # use aya_bpf::programs::TracePointContext; + /// + /// #[map] + /// static PID_TO_TGID: HashMap = HashMap::with_max_entries(1024, 0); + /// + /// # unsafe fn try_test(ctx: &LsmContext) -> Result { + /// let pid: u32 = 13; + /// let tgid: u32 = 42; + /// PID_TO_TGID.insert(&pid, &tgid, 0).map_err(|e| e as i32)?; + /// # Ok(0) + /// # } + /// ``` #[inline] pub fn insert(&self, key: &K, value: &V, flags: u64) -> Result<(), c_long> { insert(self.def.get(), key, value, flags) } + /// Removes a key-value pair from the map. + /// + /// # Examples + /// + /// ```no_run + /// use aya_bpf::{macros::map, maps::HashMap}; + /// # use aya_bpf::programs::TracePointContext; + /// + /// #[map] + /// PROCESSES: HashMap = HashMap::with_max_entries(1024, 0); + /// + /// # fn try_test(ctx: &TracePointContext) -> Result { + /// let pid: u32 = ctx.pid(); + /// // Remove the currently exiting process from the map. + /// PROCESSES.remove(&pid).map_err(|e| e as i32)?; + /// # Ok(0) + /// # } + /// ``` #[inline] pub fn remove(&self, key: &K) -> Result<(), c_long> { remove(self.def.get(), key) @@ -95,6 +195,7 @@ pub struct LruHashMap { unsafe impl Sync for LruHashMap {} impl LruHashMap { + /// Creates an `LruHashMap` with the maximum number of elements. pub const fn with_max_entries(max_entries: u32, flags: u32) -> LruHashMap { LruHashMap { def: UnsafeCell::new(build_def::( @@ -108,6 +209,8 @@ impl LruHashMap { } } + /// Creates an `LruHashMap` pinned in the BPFFS filesystem, with the maximum + /// number of elements. pub const fn pinned(max_entries: u32, flags: u32) -> LruHashMap { LruHashMap { def: UnsafeCell::new(build_def::( @@ -148,17 +251,45 @@ impl LruHashMap { get_ptr_mut(self.def.get(), key) } + /// Returns a mutable copy of the value associated with the key. #[inline] pub fn insert(&self, key: &K, value: &V, flags: u64) -> Result<(), c_long> { insert(self.def.get(), key, value, flags) } + /// Inserts a key-value pair into the map. #[inline] pub fn remove(&self, key: &K) -> Result<(), c_long> { remove(self.def.get(), key) } } +/// A hash map that can be shared between eBPF programs and user space. Each +/// CPU has its own separate copy of the map. The copies are not synchronized +/// in any way. +/// +/// Due to limits defined in the kernel, the `K` and `V` types cannot be larger +/// than 32KB in size. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.6. +/// +/// # Examples +/// +/// ```no_run +/// use aya_bpf::{macros::map, maps::PerCpuHashMap}; +/// # use aya_bpf::programs::LsmContext; +/// +/// #[map] +/// static mut PID_COUNTER: PerCpuHashMap = PerCpuHashMap::with_max_entries(1024, 0); +/// +/// # unsafe fn try_test(ctx: &LsmContext) -> Result { +/// let pid = ctx.pid(); +/// PID_COUNTER.insert(&key, &value, 0).map_err(|e| e as i32)?; +/// # Ok(0) +/// # } +/// ``` #[repr(transparent)] pub struct PerCpuHashMap { def: UnsafeCell, @@ -169,6 +300,7 @@ pub struct PerCpuHashMap { unsafe impl Sync for PerCpuHashMap {} impl PerCpuHashMap { + /// Creates a `PerCpuHashMap` with the maximum number of elements. pub const fn with_max_entries(max_entries: u32, flags: u32) -> PerCpuHashMap { PerCpuHashMap { def: UnsafeCell::new(build_def::( @@ -182,6 +314,8 @@ impl PerCpuHashMap { } } + /// Creates a `PerCpuHashMap` pinned in the BPFFS filesystem, with the maximum + /// number of elements. pub const fn pinned(max_entries: u32, flags: u32) -> PerCpuHashMap { PerCpuHashMap { def: UnsafeCell::new(build_def::( @@ -222,11 +356,13 @@ impl PerCpuHashMap { get_ptr_mut(self.def.get(), key) } + /// Returns a mutable copy of the value associated with the key. #[inline] pub fn insert(&self, key: &K, value: &V, flags: u64) -> Result<(), c_long> { insert(self.def.get(), key, value, flags) } + /// Inserts a key-value pair into the map. #[inline] pub fn remove(&self, key: &K) -> Result<(), c_long> { remove(self.def.get(), key) @@ -243,6 +379,7 @@ pub struct LruPerCpuHashMap { unsafe impl Sync for LruPerCpuHashMap {} impl LruPerCpuHashMap { + /// Creates an `LruPerCpuHashMap` with the maximum number of elements. pub const fn with_max_entries(max_entries: u32, flags: u32) -> LruPerCpuHashMap { LruPerCpuHashMap { def: UnsafeCell::new(build_def::( @@ -256,6 +393,8 @@ impl LruPerCpuHashMap { } } + /// Creates an `LruPerCpuHashMap` pinned in the BPFFS filesystem, with the maximum + /// number of elements. pub const fn pinned(max_entries: u32, flags: u32) -> LruPerCpuHashMap { LruPerCpuHashMap { def: UnsafeCell::new(build_def::( @@ -296,11 +435,13 @@ impl LruPerCpuHashMap { get_ptr_mut(self.def.get(), key) } + /// Returns a mutable copy of the value associated with the key. #[inline] pub fn insert(&self, key: &K, value: &V, flags: u64) -> Result<(), c_long> { insert(self.def.get(), key, value, flags) } + /// Inserts a key-value pair into the map. #[inline] pub fn remove(&self, key: &K) -> Result<(), c_long> { remove(self.def.get(), key) diff --git a/bpf/aya-bpf/src/maps/lpm_trie.rs b/bpf/aya-bpf/src/maps/lpm_trie.rs index d9314899..b32e8aaf 100644 --- a/bpf/aya-bpf/src/maps/lpm_trie.rs +++ b/bpf/aya-bpf/src/maps/lpm_trie.rs @@ -33,6 +33,7 @@ impl Key { } impl LpmTrie { + /// Creates an `LpmTrie` map with the maximum number of elements. pub const fn with_max_entries(max_entries: u32, flags: u32) -> LpmTrie { let flags = flags | BPF_F_NO_PREALLOC; LpmTrie { @@ -47,6 +48,8 @@ impl LpmTrie { } } + /// Creates an `LpmTrie` map pinned in the BPPFS filesystem, with the + /// maximum number of elements. pub const fn pinned(max_entries: u32, flags: u32) -> LpmTrie { let flags = flags | BPF_F_NO_PREALLOC; LpmTrie { @@ -61,6 +64,7 @@ impl LpmTrie { } } + /// Returns a copy of the value associated with the key. #[inline] pub fn get(&self, key: &Key) -> Option<&V> { unsafe { @@ -71,6 +75,7 @@ impl LpmTrie { } } + /// Inserts a key-value pair into the map. #[inline] pub fn insert(&self, key: &Key, value: &V, flags: u64) -> Result<(), c_long> { let ret = unsafe { @@ -84,6 +89,7 @@ impl LpmTrie { (ret == 0).then_some(()).ok_or(ret) } + /// Removes a key from the map. #[inline] pub fn remove(&self, key: &Key) -> Result<(), c_long> { let ret = unsafe { diff --git a/bpf/aya-bpf/src/maps/mod.rs b/bpf/aya-bpf/src/maps/mod.rs index 8fa375dd..91842e6d 100644 --- a/bpf/aya-bpf/src/maps/mod.rs +++ b/bpf/aya-bpf/src/maps/mod.rs @@ -1,3 +1,42 @@ +//! Data structures used to store eBPF programs data and share them with the +//! user space. +//! +//! The eBPF platform provides data structures - maps in eBPF speak - that are +//! used store data and share it with the user space. When you define a static +//! variable of a map type (i.e. [`HashMap`](crate::maps::HashMap), that map gets +//! initialized during the eBPF object load into the kernel and is ready to +//! use by programs. +//! + +//! +//! # Typed maps +//! +//! The eBPF API includes many map types each supporting different operations. +//! +//! Each type of map provides methods to access and modify the data in the map +//! (i.e. [`get`](crate::maps::HashMap::get), [`get_ptr_mut`](crate::maps::HashMap::get_ptr_mut), +//! [`insert`](crate::maps::HashMap::insert) and, [`remove`](crate::maps::HashMap::remove)). +//! +//! For example: +//! +//! ```no_run +//! # #![allow(dead_code)] +//! use aya_bpf::{macros::map, maps::HashMap}; +//! # use aya_bpf::programs::TracePointContext; +//! +//! #[map] +//! static PID_TO_TGID: HashMap = HashMap::with_max_entries(1024, 0); +//! +//! # fn try_test(ctx: &TracePointContext) -> Result { +//! let pid: u32 = ctx.pid(); +//! let tgid: u32 = ctx.tgid(); +//! PID_TO_TGID.insert(&pid, &tgid, 0).map_err(|e| e as i32)?; +//! # Ok(0) +//! # } +//! ``` +//! +//! Please refer to documentation for each map type for more details. + #[repr(u32)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum PinningType { diff --git a/bpf/aya-bpf/src/maps/perf/perf_event_array.rs b/bpf/aya-bpf/src/maps/perf/perf_event_array.rs index c881a885..725322d1 100644 --- a/bpf/aya-bpf/src/maps/perf/perf_event_array.rs +++ b/bpf/aya-bpf/src/maps/perf/perf_event_array.rs @@ -7,6 +7,40 @@ use crate::{ BpfContext, }; +/// An array for pushing out custom event data (as a struct defined by +/// developer) to user space. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version for this feature is 4.3. +/// +/// # Examples +/// +/// ```no_run +/// use aya_bpf::{macros::map, maps::PerfEventArray}; +/// use aya_bpf::programs::XdpContext}; +/// +/// #[repr(C)] +/// #[derive(Clone, Copy)] +/// pub struct PacketLog { +/// pub ipv4_address: u32, +/// pub port: u32, +/// } +/// +/// #[map] +/// static EVENTS: PerfEventArray = PerfEventArray::with_max_entries(1024, 0); +/// +/// # unsafe fn try_test(ctx: &XdpContext) -> Result { +/// let ipv4_address = parse_source_ipv4_address(&ctx.data); +/// let port = parse_source_port(&ctx.data); +/// let packet_log = Entry { +/// ipv4_address, +/// port, +/// }; +/// EVENTS.output(ctx, &packet_log, 0); +/// # Ok(0) +/// # } +/// ``` #[repr(transparent)] pub struct PerfEventArray { def: UnsafeCell, @@ -20,6 +54,7 @@ impl PerfEventArray { PerfEventArray::with_max_entries(0, flags) } + /// Creates an `PerfEventArray` with the maximum number of elements. pub const fn with_max_entries(max_entries: u32, flags: u32) -> PerfEventArray { PerfEventArray { def: UnsafeCell::new(bpf_map_def { @@ -35,6 +70,8 @@ impl PerfEventArray { } } + /// Creates an `PerfEventArray` pinned in the BPPFS filesystem, with the + /// maximum number of elements. pub const fn pinned(max_entries: u32, flags: u32) -> PerfEventArray { PerfEventArray { def: UnsafeCell::new(bpf_map_def { @@ -50,10 +87,31 @@ impl PerfEventArray { } } + /// Outputs the given event to the array. + /// + /// ```no_run + /// use aya_bpf::{macros::map, maps::PerfEventArray}; + /// # use aya_bpf::programs::LsmContext; + /// + /// #[repr(C)] + /// pub struct Entry { + /// pub some_field: u32, + /// } + /// + /// #[map] + /// static mut EVENTS: PerfEventArray = PerfEventArray::::with_max_entries(1024, 0); + /// + /// # unsafe fn try_test(ctx: &LsmContext) -> Result { + /// let entry = Entry { some_field: 42 }; + /// EVENTS.output(ctx, &entry, 0); + /// # Ok(0) + /// # } + /// ``` pub fn output(&self, ctx: &C, data: &T, flags: u32) { self.output_at_index(ctx, BPF_F_CURRENT_CPU as u32, data, flags) } + /// Outputs the given event to the array at the given index. pub fn output_at_index(&self, ctx: &C, index: u32, data: &T, flags: u32) { let flags = (flags as u64) << 32 | index as u64; unsafe { diff --git a/bpf/aya-bpf/src/maps/sock_hash.rs b/bpf/aya-bpf/src/maps/sock_hash.rs index 39eedcce..2b2c0406 100644 --- a/bpf/aya-bpf/src/maps/sock_hash.rs +++ b/bpf/aya-bpf/src/maps/sock_hash.rs @@ -21,7 +21,10 @@ pub struct SockHash { unsafe impl Sync for SockHash {} +/// A hash map that stores sockets to which messages or socket buffers can be +/// redirected. impl SockHash { + /// Creates a `SockHash` map with the maximum number of elements. pub const fn with_max_entries(max_entries: u32, flags: u32) -> SockHash { SockHash { def: UnsafeCell::new(bpf_map_def { @@ -37,6 +40,8 @@ impl SockHash { } } + /// Creates a `SockHash` map pinned in the BPPFS filesystem, with the + /// maximum number of elements. pub const fn pinned(max_entries: u32, flags: u32) -> SockHash { SockHash { def: UnsafeCell::new(bpf_map_def { @@ -52,7 +57,14 @@ impl SockHash { } } - pub fn update(&self, key: &mut K, sk_ops: &mut bpf_sock_ops, flags: u64) -> Result<(), i64> { + /// Adds an entry or updates an existing one in the map referencing + /// sockets. + pub fn update( + &self, + key: &mut K, + sk_ops: &mut bpf_sock_ops, + flags: u64, + ) -> Result<(), i64> { let ret = unsafe { bpf_sock_hash_update( sk_ops as *mut _, @@ -64,6 +76,7 @@ impl SockHash { (ret == 0).then_some(()).ok_or(ret) } + /// Redirects a message to the socket associated with the given key. pub fn redirect_msg(&self, ctx: &SkMsgContext, key: &mut K, flags: u64) -> i64 { unsafe { bpf_msg_redirect_hash( @@ -75,6 +88,7 @@ impl SockHash { } } + /// Redirects a socket buffer to the socket associated with the given key. pub fn redirect_skb(&self, ctx: &SkBuffContext, key: &mut K, flags: u64) -> i64 { unsafe { bpf_sk_redirect_hash(