mirror of https://github.com/aya-rs/aya
Add support for BPF_MAP_TYPE_BLOOM_FILTER
This patch adds support for `BPF_MAP_TYPE_BLOOM_FILTER`.pull/286/head
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)
|
||||
));
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue