Browse Source

Add support for BPF_MAP_TYPE_BLOOM_FILTER

This patch adds support for `BPF_MAP_TYPE_BLOOM_FILTER`.
Kenjiro Nakayama 2 năm trước cách đây
mục cha
commit
c4262f7

+ 313 - 0
aya/src/maps/bloom_filter.rs

@@ -0,0 +1,313 @@
+//! A Bloom Filter.
+use std::{convert::TryFrom, marker::PhantomData, ops::Deref};
+
+use core::mem;
+
+use crate::{
+    generated::bpf_map_type::BPF_MAP_TYPE_BLOOM_FILTER,
+    maps::{Map, MapError, MapRef, MapRefMut},
+    sys::{bpf_map_lookup_elem_ptr, bpf_map_push_elem},
+    Pod,
+};
+
+/// A Bloom Filter.
+///
+/// # Minimum kernel version
+///
+/// The minimum kernel version required to use this feature is 5.16.
+///
+/// # Examples
+///
+/// ```no_run
+/// # let bpf = aya::Bpf::load(&[])?;
+/// use aya::maps::bloom_filter::BloomFilter;
+/// use std::convert::TryFrom;
+///
+/// let mut bloom_filter = BloomFilter::try_from(bpf.map_mut("BLOOM_FILTER")?)?;
+///
+/// bloom_filter.insert(1, 0)?;
+///
+/// assert!(bloom_filter.contains(1, 0).is_ok());
+/// assert!(bloom_filter.contains(2, 0).is_err());
+///
+/// # Ok::<(), aya::BpfError>(())
+/// ```
+
+#[doc(alias = "BPF_MAP_TYPE_BLOOM_FILTER")]
+pub struct BloomFilter<T: Deref<Target = Map>, V: Pod> {
+    inner: T,
+    _v: PhantomData<V>,
+}
+
+impl<T: Deref<Target = Map>, V: Pod> BloomFilter<T, V> {
+    pub(crate) fn new(map: T) -> Result<BloomFilter<T, V>, MapError> {
+        let map_type = map.obj.def.map_type;
+
+        // validate the map definition
+        if map_type != BPF_MAP_TYPE_BLOOM_FILTER as u32 {
+            return Err(MapError::InvalidMapType {
+                map_type: map_type as u32,
+            });
+        }
+
+        let size = mem::size_of::<V>();
+        let expected = map.obj.def.value_size as usize;
+        if size != expected {
+            return Err(MapError::InvalidValueSize { size, expected });
+        };
+
+        let _ = map.fd_or_err()?;
+
+        Ok(BloomFilter {
+            inner: map,
+            _v: PhantomData,
+        })
+    }
+
+    /// Query the existence of the elelment.
+    pub fn contains(&self, mut value: V, flags: u64) -> Result<(), MapError> {
+        let fd = self.inner.deref().fd_or_err()?;
+
+        bpf_map_lookup_elem_ptr::<u32, _>(fd, None, &mut value, flags)
+            .map_err(|(code, io_error)| MapError::SyscallError {
+                call: "bpf_map_lookup_elem".to_owned(),
+                code,
+                io_error,
+            })?
+            .ok_or(MapError::ElementNotFound)?;
+        Ok(())
+    }
+
+    /// Inserts a value into the map.
+    pub fn insert(&self, value: V, flags: u64) -> Result<(), MapError> {
+        let fd = self.inner.deref().fd_or_err()?;
+        bpf_map_push_elem(fd, &value, flags).map_err(|(code, io_error)| {
+            MapError::SyscallError {
+                call: "bpf_map_push_elem".to_owned(),
+                code,
+                io_error,
+            }
+        })?;
+        Ok(())
+    }
+}
+
+impl<V: Pod> TryFrom<MapRef> for BloomFilter<MapRef, V> {
+    type Error = MapError;
+
+    fn try_from(a: MapRef) -> Result<BloomFilter<MapRef, V>, MapError> {
+        BloomFilter::new(a)
+    }
+}
+
+impl<V: Pod> TryFrom<MapRefMut> for BloomFilter<MapRefMut, V> {
+    type Error = MapError;
+
+    fn try_from(a: MapRefMut) -> Result<BloomFilter<MapRefMut, V>, MapError> {
+        BloomFilter::new(a)
+    }
+}
+
+impl<'a, V: Pod> TryFrom<&'a Map> for BloomFilter<&'a Map, V> {
+    type Error = MapError;
+
+    fn try_from(a: &'a Map) -> Result<BloomFilter<&'a Map, V>, MapError> {
+        BloomFilter::new(a)
+    }
+}
+
+impl<'a, V: Pod> TryFrom<&'a mut Map> for BloomFilter<&'a mut Map, V> {
+    type Error = MapError;
+
+    fn try_from(a: &'a mut Map) -> Result<BloomFilter<&'a mut Map, V>, MapError> {
+        BloomFilter::new(a)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::{
+        bpf_map_def,
+        generated::{
+            bpf_cmd,
+            bpf_map_type::{BPF_MAP_TYPE_BLOOM_FILTER, BPF_MAP_TYPE_PERF_EVENT_ARRAY},
+        },
+        obj,
+        sys::{override_syscall, SysResult, Syscall},
+    };
+    use libc::{EFAULT, ENOENT};
+    use std::io;
+
+    fn new_obj_map() -> obj::Map {
+        obj::Map {
+            def: bpf_map_def {
+                map_type: BPF_MAP_TYPE_BLOOM_FILTER as u32,
+                key_size: 4,
+                value_size: 4,
+                max_entries: 1024,
+                ..Default::default()
+            },
+            section_index: 0,
+            symbol_index: 0,
+            data: Vec::new(),
+            kind: obj::MapKind::Other,
+        }
+    }
+
+    fn sys_error(value: i32) -> SysResult {
+        Err((-1, io::Error::from_raw_os_error(value)))
+    }
+
+    #[test]
+    fn test_wrong_value_size() {
+        let map = Map {
+            obj: new_obj_map(),
+            fd: None,
+            pinned: false,
+        };
+        assert!(matches!(
+            BloomFilter::<_, u16>::new(&map),
+            Err(MapError::InvalidValueSize {
+                size: 2,
+                expected: 4
+            })
+        ));
+    }
+
+    #[test]
+    fn test_try_from_wrong_map() {
+        let map = Map {
+            obj: obj::Map {
+                def: bpf_map_def {
+                    map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32,
+                    key_size: 4,
+                    value_size: 4,
+                    max_entries: 1024,
+                    ..Default::default()
+                },
+                section_index: 0,
+                symbol_index: 0,
+                data: Vec::new(),
+                kind: obj::MapKind::Other,
+            },
+            fd: None,
+            pinned: false,
+        };
+
+        assert!(matches!(
+            BloomFilter::<_, u32>::try_from(&map),
+            Err(MapError::InvalidMapType { .. })
+        ));
+    }
+
+    #[test]
+    fn test_new_not_created() {
+        let mut map = Map {
+            obj: new_obj_map(),
+            fd: None,
+            pinned: false,
+        };
+
+        assert!(matches!(
+            BloomFilter::<_, u32>::new(&mut map),
+            Err(MapError::NotCreated { .. })
+        ));
+    }
+
+    #[test]
+    fn test_new_ok() {
+        let mut map = Map {
+            obj: new_obj_map(),
+            fd: Some(42),
+            pinned: false,
+        };
+
+        assert!(BloomFilter::<_, u32>::new(&mut map).is_ok());
+    }
+
+    #[test]
+    fn test_try_from_ok() {
+        let map = Map {
+            obj: new_obj_map(),
+            fd: Some(42),
+            pinned: false,
+        };
+        assert!(BloomFilter::<_, u32>::try_from(&map).is_ok())
+    }
+
+    #[test]
+    fn test_insert_syscall_error() {
+        override_syscall(|_| sys_error(EFAULT));
+
+        let mut map = Map {
+            obj: new_obj_map(),
+            fd: Some(42),
+            pinned: false,
+        };
+        let bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap();
+
+        assert!(matches!(
+            bloom_filter.insert(1, 0),
+            Err(MapError::SyscallError { call, code: -1, io_error }) if call == "bpf_map_push_elem" && io_error.raw_os_error() == Some(EFAULT)
+        ));
+    }
+
+    #[test]
+    fn test_insert_ok() {
+        override_syscall(|call| match call {
+            Syscall::Bpf {
+                cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
+                ..
+            } => Ok(1),
+            _ => sys_error(EFAULT),
+        });
+
+        let mut map = Map {
+            obj: new_obj_map(),
+            fd: Some(42),
+            pinned: false,
+        };
+
+        let bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap();
+        assert!(bloom_filter.insert(0, 42).is_ok());
+    }
+
+    #[test]
+    fn test_contains_syscall_error() {
+        override_syscall(|_| sys_error(EFAULT));
+        let map = Map {
+            obj: new_obj_map(),
+            fd: Some(42),
+            pinned: false,
+        };
+        let bloom_filter = BloomFilter::<_, u32>::new(&map).unwrap();
+
+        assert!(matches!(
+            bloom_filter.contains(1, 0),
+            Err(MapError::SyscallError { call, code: -1, io_error }) if call == "bpf_map_lookup_elem" && io_error.raw_os_error() == Some(EFAULT)
+        ));
+    }
+
+    #[test]
+    fn test_contains_not_found() {
+        override_syscall(|call| match call {
+            Syscall::Bpf {
+                cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
+                ..
+            } => sys_error(ENOENT),
+            _ => sys_error(EFAULT),
+        });
+        let map = Map {
+            obj: new_obj_map(),
+            fd: Some(42),
+            pinned: false,
+        };
+        let bloom_filter = BloomFilter::<_, u32>::new(&map).unwrap();
+
+        assert!(matches!(
+            bloom_filter.contains(1, 0),
+            Err(MapError::ElementNotFound)
+        ));
+    }
+}

+ 2 - 0
aya/src/maps/mod.rs

@@ -49,6 +49,7 @@ use crate::{
 mod map_lock;
 
 pub mod array;
+pub mod bloom_filter;
 pub mod hash_map;
 pub mod lpm_trie;
 pub mod perf;
@@ -421,6 +422,7 @@ impl TryFrom<u32> for bpf_map_type {
             x if x == BPF_MAP_TYPE_LRU_HASH as u32 => BPF_MAP_TYPE_LRU_HASH,
             x if x == BPF_MAP_TYPE_LRU_PERCPU_HASH as u32 => BPF_MAP_TYPE_LRU_PERCPU_HASH,
             x if x == BPF_MAP_TYPE_LPM_TRIE as u32 => BPF_MAP_TYPE_LPM_TRIE,
+            x if x == BPF_MAP_TYPE_BLOOM_FILTER as u32 => BPF_MAP_TYPE_BLOOM_FILTER,
             x if x == BPF_MAP_TYPE_ARRAY_OF_MAPS as u32 => BPF_MAP_TYPE_ARRAY_OF_MAPS,
             x if x == BPF_MAP_TYPE_HASH_OF_MAPS as u32 => BPF_MAP_TYPE_HASH_OF_MAPS,
             x if x == BPF_MAP_TYPE_DEVMAP as u32 => BPF_MAP_TYPE_DEVMAP,

+ 1 - 1
aya/src/maps/stack_trace.rs

@@ -115,7 +115,7 @@ impl<T: Deref<Target = Map>> StackTraceMap<T> {
         let fd = self.inner.fd_or_err()?;
 
         let mut frames = vec![0; self.max_stack_depth];
-        bpf_map_lookup_elem_ptr(fd, stack_id, frames.as_mut_ptr(), flags)
+        bpf_map_lookup_elem_ptr(fd, Some(stack_id), frames.as_mut_ptr(), flags)
             .map_err(|(code, io_error)| MapError::SyscallError {
                 call: "bpf_map_lookup_elem".to_owned(),
                 code,

+ 5 - 3
aya/src/sys/bpf.rs

@@ -193,7 +193,7 @@ pub(crate) fn bpf_map_lookup_elem_per_cpu<K: Pod, V: Pod>(
     flags: u64,
 ) -> Result<Option<PerCpuValues<V>>, (c_long, io::Error)> {
     let mut mem = PerCpuValues::<V>::alloc_kernel_mem().map_err(|io_error| (-1, io_error))?;
-    match bpf_map_lookup_elem_ptr(fd, key, mem.as_mut_ptr(), flags) {
+    match bpf_map_lookup_elem_ptr(fd, Some(key), mem.as_mut_ptr(), flags) {
         Ok(_) => Ok(Some(unsafe { PerCpuValues::from_kernel_mem(mem) })),
         Err((_, io_error)) if io_error.raw_os_error() == Some(ENOENT) => Ok(None),
         Err(e) => Err(e),
@@ -202,7 +202,7 @@ pub(crate) fn bpf_map_lookup_elem_per_cpu<K: Pod, V: Pod>(
 
 pub(crate) fn bpf_map_lookup_elem_ptr<K: Pod, V>(
     fd: RawFd,
-    key: &K,
+    key: Option<&K>,
     value: *mut V,
     flags: u64,
 ) -> Result<Option<()>, (c_long, io::Error)> {
@@ -210,7 +210,9 @@ pub(crate) fn bpf_map_lookup_elem_ptr<K: Pod, V>(
 
     let u = unsafe { &mut attr.__bindgen_anon_2 };
     u.map_fd = fd as u32;
-    u.key = key as *const _ as u64;
+    if let Some(key) = key {
+        u.key = key as *const _ as u64;
+    }
     u.__bindgen_anon_1.value = value as u64;
     u.flags = flags;
 

+ 76 - 0
bpf/aya-bpf/src/maps/bloom_filter.rs

@@ -0,0 +1,76 @@
+use core::{marker::PhantomData, mem};
+
+use aya_bpf_cty::c_void;
+
+use crate::{
+    bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_BLOOM_FILTER},
+    helpers::{bpf_map_peek_elem, bpf_map_push_elem},
+    maps::PinningType,
+};
+
+#[repr(transparent)]
+pub struct BloomFilter<T> {
+    def: bpf_map_def,
+    _t: PhantomData<T>,
+}
+
+impl<T> BloomFilter<T> {
+    pub const fn with_max_entries(max_entries: u32, flags: u32) -> BloomFilter<T> {
+        BloomFilter {
+            def: build_def::<T>(
+                BPF_MAP_TYPE_BLOOM_FILTER,
+                max_entries,
+                flags,
+                PinningType::None,
+            ),
+            _t: PhantomData,
+        }
+    }
+
+    pub const fn pinned(max_entries: u32, flags: u32) -> BloomFilter<T> {
+        BloomFilter {
+            def: build_def::<T>(
+                BPF_MAP_TYPE_BLOOM_FILTER,
+                max_entries,
+                flags,
+                PinningType::ByName,
+            ),
+            _t: PhantomData,
+        }
+    }
+
+    #[inline]
+    pub fn contains(&mut self, value: &T) -> Result<(), i64> {
+        let ret = unsafe {
+            bpf_map_peek_elem(
+                &mut self.def as *mut _ as *mut _,
+                value as *const _ as *mut c_void,
+            )
+        };
+        (ret == 0).then(|| ()).ok_or(ret)
+    }
+
+    #[inline]
+    pub fn insert(&mut self, value: &T, flags: u64) -> Result<(), i64> {
+        let ret = unsafe {
+            bpf_map_push_elem(
+                &mut self.def as *mut _ as *mut _,
+                value as *const _ as *const _,
+                flags,
+            )
+        };
+        (ret == 0).then(|| ()).ok_or(ret)
+    }
+}
+
+const fn build_def<T>(ty: u32, max_entries: u32, flags: u32, pin: PinningType) -> bpf_map_def {
+    bpf_map_def {
+        type_: ty,
+        key_size: 0,
+        value_size: mem::size_of::<T>() as u32,
+        max_entries,
+        map_flags: flags,
+        id: 0,
+        pinning: pin as u32,
+    }
+}

+ 2 - 0
bpf/aya-bpf/src/maps/mod.rs

@@ -6,6 +6,7 @@ pub(crate) enum PinningType {
 }
 
 pub mod array;
+pub mod bloom_filter;
 pub mod hash_map;
 pub mod lpm_trie;
 pub mod per_cpu_array;
@@ -17,6 +18,7 @@ pub mod sock_map;
 pub mod stack_trace;
 
 pub use array::Array;
+pub use bloom_filter::BloomFilter;
 pub use hash_map::{HashMap, LruHashMap, LruPerCpuHashMap, PerCpuHashMap};
 pub use lpm_trie::LpmTrie;
 pub use per_cpu_array::PerCpuArray;