aya-ebpf 0.2.1

A library for writing eBPF programs
Documentation
use core::ptr::NonNull;

use crate::{btf_maps::btf_map_def, lookup};

btf_map_def!(
    /// A BTF-compatible BPF hash map that stores references to other maps (hash of maps).
    ///
    /// This map type allows you to store file descriptors of other BPF maps
    /// indexed by arbitrary keys, enabling dynamic map selection at runtime.
    ///
    /// # Minimum kernel version
    ///
    /// The minimum kernel version required to use this feature is 5.7.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use aya_ebpf::{btf_maps::{Array, HashOfMaps}, macros::btf_map};
    ///
    /// // The inner map definition is parsed from BTF at load time.
    /// #[btf_map]
    /// static OUTER: HashOfMaps<u32, Array<u32, 10>, 4> = HashOfMaps::new();
    /// ```
    pub struct HashOfMaps<K, V; const MAX_ENTRIES: usize, const FLAGS: usize = 0>,
    map_type: BPF_MAP_TYPE_HASH_OF_MAPS,
    max_entries: MAX_ENTRIES,
    map_flags: FLAGS,
    key_type: K,
    value_type: u32,
    inner_map: V,
);

impl<K, V, const MAX_ENTRIES: usize, const FLAGS: usize> HashOfMaps<K, V, MAX_ENTRIES, FLAGS> {
    /// Retrieve the inner map associated with `key` from the map.
    ///
    /// # Safety
    ///
    /// Unless the map flag `BPF_F_NO_PREALLOC` is used, the kernel does not guarantee 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.
    #[inline(always)]
    pub unsafe fn get(&self, key: &K) -> Option<&V> {
        // SAFETY: We only read from the map through BPF helpers.
        // The struct fields are never accessed - only the address is used.
        unsafe { self.lookup(key).map(|p| p.as_ref()) }
    }

    #[inline(always)]
    unsafe fn lookup(&self, key: &K) -> Option<NonNull<V>> {
        lookup(self.as_ptr(), key)
    }
}

impl<K, V: crate::btf_maps::MapDef, const MAX_ENTRIES: usize, const FLAGS: usize>
    HashOfMaps<K, V, MAX_ENTRIES, FLAGS>
{
    /// Looks up a value directly in the inner map associated with `outer_key`.
    ///
    /// Performs both the outer and inner `bpf_map_lookup_elem` calls in a
    /// single method, producing fewer BPF instructions between the two
    /// helpers. This reduces verifier state explosion in tight loops.
    ///
    /// # Safety
    ///
    /// See [`get`](Self::get).
    #[inline(always)]
    pub unsafe fn get_value(&self, outer_key: &K, inner_key: &V::Key) -> Option<&V::Value> {
        let inner: NonNull<V> = lookup(self.as_ptr(), outer_key)?;
        // SAFETY: The caller upholds the aliasing invariants (see `get`).
        unsafe { crate::btf_maps::lookup_inner(inner, inner_key) }
    }

    /// Same as [`get_value`](Self::get_value) but returns a mutable pointer.
    ///
    /// # Safety
    ///
    /// See [`get_value`](Self::get_value).
    #[inline(always)]
    pub unsafe fn get_value_ptr_mut(
        &self,
        outer_key: &K,
        inner_key: &V::Key,
    ) -> Option<*mut V::Value> {
        let inner: NonNull<V> = lookup(self.as_ptr(), outer_key)?;
        crate::btf_maps::lookup_inner_ptr_mut(inner, inner_key)
    }
}