Add support for BPF_MAP_TYPE_BLOOM_FILTER

This patch adds support for `BPF_MAP_TYPE_BLOOM_FILTER`.
pull/286/head
Kenjiro Nakayama 2 years ago
parent 4acd996cb8
commit c4262f793d

@ -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)
));
}
}

@ -49,6 +49,7 @@ use crate::{
mod map_lock; mod map_lock;
pub mod array; pub mod array;
pub mod bloom_filter;
pub mod hash_map; pub mod hash_map;
pub mod lpm_trie; pub mod lpm_trie;
pub mod perf; 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_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_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_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_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_HASH_OF_MAPS as u32 => BPF_MAP_TYPE_HASH_OF_MAPS,
x if x == BPF_MAP_TYPE_DEVMAP as u32 => BPF_MAP_TYPE_DEVMAP, x if x == BPF_MAP_TYPE_DEVMAP as u32 => BPF_MAP_TYPE_DEVMAP,

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

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

@ -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,
}
}

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

Loading…
Cancel
Save