Skip to content

Commit 946feb5

Browse files
barthrdanielocfb
authored andcommitted
Add crud support for BPF_MAP_TYPE_BLOOM_FILTER
Update map_key documentation to include bloom filter Add bloom filter test Add specific lookup for bloom filter Finalize PR Improve error message
1 parent 1fcc68c commit 946feb5

File tree

4 files changed

+104
-3
lines changed

4 files changed

+104
-3
lines changed

libbpf-rs/src/map.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -556,10 +556,11 @@ impl MapHandle {
556556
Ok(ncpu * aligned_val_size)
557557
}
558558

559-
/// Apply a key check and return a null pointer in case of dealing with queue/stack map,
560-
/// before passing the key to the bpf functions that support the map of type queue/stack.
559+
/// Apply a key check and return a null pointer in case of dealing with queue/stack/bloom-filter map,
560+
/// before passing the key to the bpf functions that support the map of type queue/stack/bloom-filter.
561561
fn map_key(&self, key: &[u8]) -> *const c_void {
562-
if self.key_size() == 0 && matches!(self.map_type(), MapType::Queue | MapType::Stack) {
562+
// For all they keyless maps we null out the key per documentation of libbpf
563+
if self.key_size() == 0 && self.map_type().is_keyless() {
563564
return ptr::null();
564565
}
565566

@@ -631,7 +632,13 @@ impl MapHandle {
631632
///
632633
/// If the map is one of the per-cpu data structures, the function [`MapHandle::lookup_percpu()`]
633634
/// must be used.
635+
/// If the map is of type bloom_filter the function [`MapHandle::lookup_bloom_filter()`] must be used
634636
pub fn lookup(&self, key: &[u8], flags: MapFlags) -> Result<Option<Vec<u8>>> {
637+
if self.map_type().is_bloom_filter() {
638+
return Err(Error::with_invalid_data(
639+
"lookup_bloom_filter() must be used for bloom filter maps",
640+
));
641+
}
635642
if self.map_type().is_percpu() {
636643
return Err(Error::with_invalid_data(format!(
637644
"lookup_percpu() must be used for per-cpu maps (type of the map is {})",
@@ -643,6 +650,30 @@ impl MapHandle {
643650
self.lookup_raw(key, flags, out_size)
644651
}
645652

653+
/// Returns if the given value is likely present in bloom_filter as `bool`.
654+
///
655+
/// `value` must have exactly [`MapHandle::value_size()`] elements.
656+
pub fn lookup_bloom_filter(&self, value: &[u8]) -> Result<bool> {
657+
let ret = unsafe {
658+
libbpf_sys::bpf_map_lookup_elem(
659+
self.fd.as_raw_fd(),
660+
ptr::null(),
661+
value.to_vec().as_mut_ptr() as *mut c_void,
662+
)
663+
};
664+
665+
if ret == 0 {
666+
Ok(true)
667+
} else {
668+
let err = io::Error::last_os_error();
669+
if err.kind() == io::ErrorKind::NotFound {
670+
Ok(false)
671+
} else {
672+
Err(Error::from(err))
673+
}
674+
}
675+
}
676+
646677
/// Returns one value per cpu as `Vec` of `Vec` of `u8` for per per-cpu maps.
647678
///
648679
/// For normal maps, [`MapHandle::lookup()`] must be used.
@@ -1009,6 +1040,17 @@ impl MapType {
10091040
)
10101041
}
10111042

1043+
/// Returns if the map is keyless map type as per documentation of libbpf
1044+
/// Keyless map types are: Queues, Stacks and Bloom Filters
1045+
fn is_keyless(&self) -> bool {
1046+
matches!(self, MapType::Queue | MapType::Stack | MapType::BloomFilter)
1047+
}
1048+
1049+
/// Returns if the map is of bloom filter type
1050+
pub fn is_bloom_filter(&self) -> bool {
1051+
MapType::BloomFilter.eq(self)
1052+
}
1053+
10121054
/// Detects if host kernel supports this BPF map type.
10131055
///
10141056
/// Make sure the process has required set of CAP_* permissions (or runs as

libbpf-rs/tests/bin/src/tracepoint.bpf.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,10 @@ struct {
6565
__type(value, __u32);
6666
} stack SEC(".maps");
6767

68+
struct {
69+
__uint(type, BPF_MAP_TYPE_BLOOM_FILTER);
70+
__uint(max_entries, 5);
71+
__type(value, __u32);
72+
} bloom_filter SEC(".maps");
73+
6874
char LICENSE[] SEC("license") = "GPL";
144 Bytes
Binary file not shown.

libbpf-rs/tests/test.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,58 @@ fn test_sudo_object_map_queue_crud() {
527527
.is_none());
528528
}
529529

530+
/// Test CRUD operations on map of type bloomfilter.
531+
#[test]
532+
fn test_sudo_object_map_bloom_filter_crud() {
533+
bump_rlimit_mlock();
534+
535+
let obj = get_test_object("tracepoint.bpf.o");
536+
let bloom_filter = obj
537+
.map("bloom_filter")
538+
.expect("failed to find bloom_filter map");
539+
540+
let key: [u8; 0] = [];
541+
let value1 = 1337u32.to_ne_bytes();
542+
let value2 = 2674u32.to_ne_bytes();
543+
544+
bloom_filter
545+
.update(&key, &value1, MapFlags::ANY)
546+
.expect("failed to add entry value1 to bloom filter");
547+
548+
bloom_filter
549+
.update(&key, &value2, MapFlags::ANY)
550+
.expect("failed to add entry value2 in bloom filter");
551+
552+
// Non empty keys should result in an error
553+
bloom_filter
554+
.update(&value1, &value1, MapFlags::ANY)
555+
.expect_err("Non empty key should return an error");
556+
557+
for inserted_value in [value1, value2] {
558+
let val = bloom_filter
559+
.lookup_bloom_filter(&inserted_value)
560+
.expect("failed retrieve item from bloom filter");
561+
562+
assert!(val);
563+
}
564+
// Test non existing element
565+
let enoent_found = bloom_filter
566+
.lookup_bloom_filter(&[1, 2, 3, 4])
567+
.expect("failed retrieve item from bloom filter");
568+
569+
assert!(!enoent_found);
570+
571+
// Calling lookup should result in an error
572+
bloom_filter
573+
.lookup(&[1, 2, 3, 4], MapFlags::ANY)
574+
.expect_err("lookup should fail since we should use lookup_bloom_filter");
575+
576+
// Deleting should not be possible
577+
bloom_filter
578+
.lookup_and_delete(&key)
579+
.expect_err("Expect delete to fail");
580+
}
581+
530582
/// Test CRUD operations on map of type stack.
531583
#[test]
532584
fn test_sudo_object_map_stack_crud() {
@@ -550,6 +602,7 @@ fn test_sudo_object_map_stack_crud() {
550602
.lookup(&key, MapFlags::ANY)
551603
.expect("failed to pop from stack")
552604
.expect("failed to retrieve value");
605+
553606
assert_eq!(val.len(), 4);
554607
assert_eq!(&val, &value2);
555608

0 commit comments

Comments
 (0)