mirror of https://github.com/aya-rs/aya
Merge 078c6381ec
into 67a0595f87
commit
f69363f213
@ -0,0 +1,82 @@
|
|||||||
|
//! An array of eBPF maps.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
borrow::{Borrow, BorrowMut},
|
||||||
|
os::fd::{AsFd as _, AsRawFd},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
maps::{check_bounds, check_kv_size, MapData, MapError, MapFd},
|
||||||
|
sys::{bpf_map_get_fd_by_id, bpf_map_lookup_elem, bpf_map_update_elem, SyscallError},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// An array of eBPF Maps
|
||||||
|
///
|
||||||
|
/// A `Array` is used to store references to other maps.
|
||||||
|
///
|
||||||
|
/// # Minimum kernel version
|
||||||
|
///
|
||||||
|
/// The minimum kernel version required to use this feature is 4.14.
|
||||||
|
#[doc(alias = "BPF_MAP_TYPE_ARRAY_OF_MAPS")]
|
||||||
|
pub struct Array<T> {
|
||||||
|
pub(crate) inner: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Borrow<MapData>> Array<T> {
|
||||||
|
pub(crate) fn new(map: T) -> Result<Self, MapError> {
|
||||||
|
let data = map.borrow();
|
||||||
|
check_kv_size::<u32, u32>(data)?;
|
||||||
|
Ok(Self { inner: map })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of elements in the array.
|
||||||
|
///
|
||||||
|
/// This corresponds to the value of `bpf_map_def::max_entries` on the eBPF side.
|
||||||
|
pub fn len(&self) -> u32 {
|
||||||
|
self.inner.borrow().obj.max_entries()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value stored at the given index.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
|
||||||
|
/// if `bpf_map_lookup_elem` fails.
|
||||||
|
pub fn get(&self, index: &u32, flags: u64) -> Result<MapFd, MapError> {
|
||||||
|
let data = self.inner.borrow();
|
||||||
|
check_bounds(data, *index)?;
|
||||||
|
let fd = data.fd().as_fd();
|
||||||
|
|
||||||
|
let value: Option<u32> =
|
||||||
|
bpf_map_lookup_elem(fd, index, flags).map_err(|(_, io_error)| SyscallError {
|
||||||
|
call: "bpf_map_lookup_elem",
|
||||||
|
io_error,
|
||||||
|
})?;
|
||||||
|
if let Some(value) = value {
|
||||||
|
let fd = bpf_map_get_fd_by_id(value)?;
|
||||||
|
Ok(MapFd::from_fd(fd))
|
||||||
|
} else {
|
||||||
|
Err(MapError::KeyNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: BorrowMut<MapData>> Array<T> {
|
||||||
|
/// Sets the value of the element at the given index.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
|
||||||
|
/// if `bpf_map_update_elem` fails.
|
||||||
|
pub fn set(&mut self, index: u32, value: &MapFd, flags: u64) -> Result<(), MapError> {
|
||||||
|
let data = self.inner.borrow_mut();
|
||||||
|
check_bounds(data, index)?;
|
||||||
|
let fd = data.fd().as_fd();
|
||||||
|
bpf_map_update_elem(fd, Some(&index), &value.as_fd().as_raw_fd(), flags).map_err(
|
||||||
|
|(_, io_error)| SyscallError {
|
||||||
|
call: "bpf_map_update_elem",
|
||||||
|
io_error,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
use std::{
|
||||||
|
borrow::{Borrow, BorrowMut},
|
||||||
|
marker::PhantomData,
|
||||||
|
os::fd::{AsFd as _, AsRawFd as _},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
maps::{check_kv_size, hash_map, MapData, MapError, MapFd},
|
||||||
|
sys::{bpf_map_get_fd_by_id, bpf_map_lookup_elem, SyscallError},
|
||||||
|
Pod,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// An hashmap of eBPF Maps
|
||||||
|
///
|
||||||
|
/// A `HashMap` is used to store references to other maps.
|
||||||
|
///
|
||||||
|
/// # Minimum kernel version
|
||||||
|
///
|
||||||
|
/// The minimum kernel version required to use this feature is 4.14.
|
||||||
|
#[doc(alias = "BPF_MAP_TYPE_HASH")]
|
||||||
|
#[doc(alias = "BPF_MAP_TYPE_LRU_HASH")]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct HashMap<T, K> {
|
||||||
|
pub(crate) inner: T,
|
||||||
|
_k: PhantomData<K>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Borrow<MapData>, K: Pod> HashMap<T, K> {
|
||||||
|
pub(crate) fn new(map: T) -> Result<Self, MapError> {
|
||||||
|
let data = map.borrow();
|
||||||
|
check_kv_size::<K, u32>(data)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
inner: map,
|
||||||
|
_k: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a copy of the value associated with the key.
|
||||||
|
pub fn get(&self, key: &K, flags: u64) -> Result<MapFd, MapError> {
|
||||||
|
let fd = self.inner.borrow().fd().as_fd();
|
||||||
|
let value = bpf_map_lookup_elem(fd, key, flags).map_err(|(_, io_error)| SyscallError {
|
||||||
|
call: "bpf_map_lookup_elem",
|
||||||
|
io_error,
|
||||||
|
})?;
|
||||||
|
if let Some(value) = value {
|
||||||
|
let fd = bpf_map_get_fd_by_id(value)?;
|
||||||
|
Ok(MapFd::from_fd(fd))
|
||||||
|
} else {
|
||||||
|
Err(MapError::KeyNotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BorrowMut<MapData>, K: Pod> HashMap<T, K> {
|
||||||
|
/// Inserts a key-value pair into the map.
|
||||||
|
pub fn insert(
|
||||||
|
&mut self,
|
||||||
|
key: impl Borrow<K>,
|
||||||
|
value: &MapFd,
|
||||||
|
flags: u64,
|
||||||
|
) -> Result<(), MapError> {
|
||||||
|
hash_map::insert(
|
||||||
|
self.inner.borrow_mut(),
|
||||||
|
key.borrow(),
|
||||||
|
&value.as_fd().as_raw_fd(),
|
||||||
|
flags,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a key from the map.
|
||||||
|
pub fn remove(&mut self, key: &K) -> Result<(), MapError> {
|
||||||
|
hash_map::remove(self.inner.borrow_mut(), key)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
//! Maps of maps
|
||||||
|
mod array;
|
||||||
|
mod hash_map;
|
||||||
|
|
||||||
|
pub use array::Array;
|
||||||
|
pub use hash_map::HashMap;
|
@ -0,0 +1,66 @@
|
|||||||
|
use core::{cell::UnsafeCell, marker::PhantomData, mem, ptr::NonNull};
|
||||||
|
|
||||||
|
use aya_ebpf_cty::c_void;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_ARRAY_OF_MAPS},
|
||||||
|
helpers::bpf_map_lookup_elem,
|
||||||
|
maps::{InnerMap, PinningType},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct ArrayOfMaps<T: InnerMap> {
|
||||||
|
def: UnsafeCell<bpf_map_def>,
|
||||||
|
_t: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: InnerMap> Sync for ArrayOfMaps<T> {}
|
||||||
|
|
||||||
|
impl<T: InnerMap> ArrayOfMaps<T> {
|
||||||
|
pub const fn with_max_entries(max_entries: u32, flags: u32) -> ArrayOfMaps<T> {
|
||||||
|
ArrayOfMaps {
|
||||||
|
def: UnsafeCell::new(bpf_map_def {
|
||||||
|
type_: BPF_MAP_TYPE_ARRAY_OF_MAPS,
|
||||||
|
key_size: mem::size_of::<u32>() as u32,
|
||||||
|
value_size: mem::size_of::<u32>() as u32,
|
||||||
|
max_entries,
|
||||||
|
map_flags: flags,
|
||||||
|
id: 0,
|
||||||
|
pinning: PinningType::None as u32,
|
||||||
|
}),
|
||||||
|
_t: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn pinned(max_entries: u32, flags: u32) -> ArrayOfMaps<T> {
|
||||||
|
ArrayOfMaps {
|
||||||
|
def: UnsafeCell::new(bpf_map_def {
|
||||||
|
type_: BPF_MAP_TYPE_ARRAY_OF_MAPS,
|
||||||
|
key_size: mem::size_of::<u32>() as u32,
|
||||||
|
value_size: mem::size_of::<u32>() as u32,
|
||||||
|
max_entries,
|
||||||
|
map_flags: flags,
|
||||||
|
id: 0,
|
||||||
|
pinning: PinningType::ByName as u32,
|
||||||
|
}),
|
||||||
|
_t: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn get(&self, index: u32) -> Option<&T> {
|
||||||
|
// FIXME: alignment
|
||||||
|
unsafe { self.lookup(index).map(|p| p.as_ref()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
unsafe fn lookup(&self, index: u32) -> Option<NonNull<T>> {
|
||||||
|
let ptr = unsafe {
|
||||||
|
bpf_map_lookup_elem(
|
||||||
|
self.def.get() as *mut _,
|
||||||
|
&index as *const _ as *const c_void,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
NonNull::new(ptr as *mut T)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
use core::{cell::UnsafeCell, marker::PhantomData, mem, ptr::NonNull};
|
||||||
|
|
||||||
|
use aya_ebpf_cty::c_void;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bindings::{bpf_map_def, bpf_map_type::BPF_MAP_TYPE_HASH_OF_MAPS},
|
||||||
|
helpers::bpf_map_lookup_elem,
|
||||||
|
maps::{InnerMap, PinningType},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct HashOfMaps<K, V> {
|
||||||
|
def: UnsafeCell<bpf_map_def>,
|
||||||
|
_k: PhantomData<K>,
|
||||||
|
_v: PhantomData<V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V: InnerMap> HashOfMaps<K, V> {
|
||||||
|
pub const fn with_max_entries(max_entries: u32, flags: u32) -> HashOfMaps<K, V> {
|
||||||
|
HashOfMaps {
|
||||||
|
def: UnsafeCell::new(bpf_map_def {
|
||||||
|
type_: BPF_MAP_TYPE_HASH_OF_MAPS,
|
||||||
|
key_size: mem::size_of::<K>() as u32,
|
||||||
|
value_size: mem::size_of::<u32>() as u32,
|
||||||
|
max_entries,
|
||||||
|
map_flags: flags,
|
||||||
|
id: 0,
|
||||||
|
pinning: PinningType::None as u32,
|
||||||
|
}),
|
||||||
|
_k: PhantomData,
|
||||||
|
_v: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn pinned(max_entries: u32, flags: u32) -> HashOfMaps<K, V> {
|
||||||
|
HashOfMaps {
|
||||||
|
def: UnsafeCell::new(bpf_map_def {
|
||||||
|
type_: BPF_MAP_TYPE_HASH_OF_MAPS,
|
||||||
|
key_size: mem::size_of::<K>() as u32,
|
||||||
|
value_size: mem::size_of::<u32>() as u32,
|
||||||
|
max_entries,
|
||||||
|
map_flags: flags,
|
||||||
|
id: 0,
|
||||||
|
pinning: PinningType::ByName as u32,
|
||||||
|
}),
|
||||||
|
_k: PhantomData,
|
||||||
|
_v: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve the value associate with `key` from the map.
|
||||||
|
/// This function is unsafe. Unless the map flag `BPF_F_NO_PREALLOC` is used, the kernel does not
|
||||||
|
/// make guarantee on 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]
|
||||||
|
pub unsafe fn get(&self, key: &K) -> Option<NonNull<u32>> {
|
||||||
|
let value = unsafe {
|
||||||
|
bpf_map_lookup_elem(self.def.get() as *mut _, key as *const _ as *const c_void)
|
||||||
|
};
|
||||||
|
// FIXME: alignment
|
||||||
|
NonNull::new(value as *mut _)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use aya_ebpf::{
|
||||||
|
bindings::xdp_action,
|
||||||
|
macros::{map, uprobe},
|
||||||
|
maps::{Array, ArrayOfMaps},
|
||||||
|
programs::ProbeContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[map]
|
||||||
|
static OUTER: ArrayOfMaps<Array<u32>> = ArrayOfMaps::with_max_entries(10, 0);
|
||||||
|
|
||||||
|
#[map]
|
||||||
|
static INNER: Array<u32> = Array::with_max_entries(10, 0);
|
||||||
|
|
||||||
|
#[map]
|
||||||
|
static INNER_2: Array<u32> = Array::with_max_entries(10, 0);
|
||||||
|
|
||||||
|
#[uprobe]
|
||||||
|
pub fn mim_test_array(_ctx: ProbeContext) -> u32 {
|
||||||
|
if let Some(map) = OUTER.get(0) {
|
||||||
|
if let Some(idx_0) = map.get_ptr_mut(0) {
|
||||||
|
unsafe {
|
||||||
|
*idx_0 = 42;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(map) = OUTER.get(1) {
|
||||||
|
if let Some(idx_0) = map.get_ptr_mut(0) {
|
||||||
|
unsafe {
|
||||||
|
*idx_0 = 24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xdp_action::XDP_PASS
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
// clang-format off
|
||||||
|
#include <vmlinux.h>
|
||||||
|
#include <bpf/bpf_helpers.h>
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
struct inner_map_type {
|
||||||
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
|
__type(key, __u32);
|
||||||
|
__type(value, __u32);
|
||||||
|
__uint(max_entries, 10); // Size is different from the outer map
|
||||||
|
__uint(map_flags, BPF_F_INNER_MAP); // Flag required due to ^^^
|
||||||
|
} inner_map SEC(".maps");
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
|
||||||
|
__type(key, __u32); // value omitted as should be fixed by loader
|
||||||
|
__uint(max_entries, 1);
|
||||||
|
__array(values, struct inner_map_type);
|
||||||
|
} outer_array_map SEC(".maps") = {
|
||||||
|
.values =
|
||||||
|
{
|
||||||
|
[0] = &inner_map,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
|
||||||
|
__type(key, __u32); // value omitted as should be fixed by loader
|
||||||
|
__uint(max_entries, 1);
|
||||||
|
__array(values, struct inner_map_type);
|
||||||
|
} outer_hash_map SEC(".maps") = {
|
||||||
|
.values =
|
||||||
|
{
|
||||||
|
[0] = &inner_map,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int map_in_map_test(void *outer_map) {
|
||||||
|
int key = 0;
|
||||||
|
int value = 42;
|
||||||
|
void *inner_map;
|
||||||
|
|
||||||
|
inner_map = bpf_map_lookup_elem(outer_map, &key);
|
||||||
|
if (!inner_map)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bpf_map_update_elem(inner_map, &key, &value, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEC("xdp")
|
||||||
|
int mim_test_array(struct xdp_md *ctx) {
|
||||||
|
map_in_map_test(&outer_array_map);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEC("xdp")
|
||||||
|
int mim_test_hash(struct xdp_md *ctx) {
|
||||||
|
map_in_map_test(&outer_hash_map);
|
||||||
|
return XDP_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
char _license[] SEC("license") = "GPL";
|
Loading…
Reference in New Issue