diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index e481cb7d..bc1d9f81 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -50,6 +50,7 @@ mod map_lock; pub mod array; pub mod hash_map; +pub mod of_maps; pub mod perf; pub mod queue; pub mod sock; @@ -59,6 +60,7 @@ pub mod stack_trace; pub use array::{Array, PerCpuArray, ProgramArray}; pub use hash_map::{HashMap, PerCpuHashMap}; pub use map_lock::*; +pub use of_maps::{Array as ArrayOfMaps, HashMap as HashMapOfMaps}; pub use perf::PerfEventArray; pub use queue::Queue; pub use sock::{SockHash, SockMap}; diff --git a/aya/src/maps/of_maps/array.rs b/aya/src/maps/of_maps/array.rs new file mode 100644 index 00000000..525b7902 --- /dev/null +++ b/aya/src/maps/of_maps/array.rs @@ -0,0 +1,147 @@ +//! An array of eBPF maps. + +use std::{ + convert::TryFrom, + mem, + ops::{Deref, DerefMut}, + os::unix::{io::IntoRawFd, prelude::RawFd}, +}; + +use crate::{ + generated::bpf_map_type::BPF_MAP_TYPE_ARRAY_OF_MAPS, + maps::{of_maps::MapOfMaps, Map, MapError, MapKeys, MapRef, MapRefMut}, + sys::{bpf_map_delete_elem, bpf_map_get_fd_by_id, bpf_map_lookup_elem, bpf_map_update_elem}, +}; + +/// 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> { + pub(crate) inner: T, +} + +impl> Array { + fn new(map: T) -> Result, MapError> { + let map_type = map.obj.def.map_type; + if map_type != BPF_MAP_TYPE_ARRAY_OF_MAPS as u32 { + return Err(MapError::InvalidMapType { + map_type: map_type as u32, + }); + } + let expected = mem::size_of::(); + let size = map.obj.def.key_size as usize; + if size != expected { + return Err(MapError::InvalidKeySize { size, expected }); + } + + let expected = mem::size_of::(); + let size = map.obj.def.value_size as usize; + if size != expected { + return Err(MapError::InvalidValueSize { size, expected }); + } + let _fd = map.fd_or_err()?; + + Ok(Array { inner: map }) + } + + /// An iterator over the indices of the array that point to a map. The iterator item type + /// is `Result`. + pub unsafe fn indices(&self) -> MapKeys<'_, u32> { + MapKeys::new(&self.inner) + } + + fn check_bounds(&self, index: u32) -> Result<(), MapError> { + let max_entries = self.inner.obj.def.max_entries; + if index >= self.inner.obj.def.max_entries { + Err(MapError::OutOfBounds { index, max_entries }) + } else { + Ok(()) + } + } + + /// Returns the fd of the map 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 { + self.check_bounds(*index)?; + let fd = self.inner.fd_or_err()?; + + let id = bpf_map_lookup_elem(fd, index, flags) + .map_err(|(code, io_error)| MapError::SyscallError { + call: "bpf_map_lookup_elem".to_owned(), + code, + io_error, + })? + .ok_or(MapError::KeyNotFound)?; + let inner_fd = bpf_map_get_fd_by_id(id).map_err(|io_error| MapError::SyscallError { + call: "bpf_map_get_fd_by_id".to_owned(), + code: 0, + io_error, + })?; + Ok(inner_fd as RawFd) + } +} + +impl + DerefMut> Array { + /// Stores a map fd into the map. + pub fn set(&mut self, index: u32, map: I, flags: u64) -> Result<(), MapError> { + let fd = self.inner.fd_or_err()?; + let map_fd = map.into_raw_fd(); + self.check_bounds(index)?; + bpf_map_update_elem(fd, &index, &map_fd, flags).map_err(|(code, io_error)| { + MapError::SyscallError { + call: "bpf_map_update_elem".to_owned(), + code, + io_error, + } + })?; + // safety: we're closing a RawFd which we have ownership of + // this is required because inserting this in to the map causes + // there to be a reference to the map in both kernel and userspace + unsafe { libc::close(map_fd) }; + Ok(()) + } + + /// Removes the map stored at `index` from the map. + pub fn delete(&mut self, index: &u32) -> Result<(), MapError> { + let fd = self.inner.fd_or_err()?; + self.check_bounds(*index)?; + bpf_map_delete_elem(fd, index) + .map(|_| ()) + .map_err(|(code, io_error)| MapError::SyscallError { + call: "bpf_map_delete_elem".to_owned(), + code, + io_error, + }) + } +} + +impl + DerefMut> MapOfMaps for Array { + fn fd_or_err(&self) -> Result { + self.inner.fd_or_err() + } +} + +impl TryFrom for Array { + type Error = MapError; + + fn try_from(a: MapRef) -> Result, MapError> { + Array::new(a) + } +} + +impl TryFrom for Array { + type Error = MapError; + + fn try_from(a: MapRefMut) -> Result, MapError> { + Array::new(a) + } +} diff --git a/aya/src/maps/of_maps/hash_map.rs b/aya/src/maps/of_maps/hash_map.rs new file mode 100644 index 00000000..b3516c34 --- /dev/null +++ b/aya/src/maps/of_maps/hash_map.rs @@ -0,0 +1,129 @@ +use std::{ + convert::TryFrom, + marker::PhantomData, + ops::{Deref, DerefMut}, + os::unix::io::{IntoRawFd, RawFd}, +}; + +use crate::{ + generated::bpf_map_type::BPF_MAP_TYPE_HASH_OF_MAPS, + maps::{ + hash_map, of_maps::MapOfMaps, IterableMap, Map, MapError, MapIter, MapKeys, MapRef, + MapRefMut, + }, + sys::{bpf_map_get_fd_by_id, bpf_map_lookup_elem}, + Pod, +}; + +/// A hash map of eBPF Maps. +/// +/// A `HashMap` is used to store references to eBPF Maps +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.18. +#[doc(alias = "BPF_MAP_TYPE_HASH_OF_MAPS")] +pub struct HashMap, K> { + inner: T, + _k: PhantomData, +} + +impl, K: Pod> HashMap { + pub(crate) fn new(map: T) -> Result, MapError> { + let map_type = map.obj.def.map_type; + + // validate the map definition + if map_type != BPF_MAP_TYPE_HASH_OF_MAPS as u32 { + return Err(MapError::InvalidMapType { + map_type: map_type as u32, + }); + } + hash_map::check_kv_size::(&map)?; + let _ = map.fd_or_err()?; + + Ok(HashMap { + inner: map, + _k: PhantomData, + }) + } + + /// Returns the fd of the map stored at the given key. + pub unsafe fn get(&self, key: &K, flags: u64) -> Result { + let fd = self.inner.deref().fd_or_err()?; + let id = bpf_map_lookup_elem(fd, key, flags) + .map_err(|(code, io_error)| MapError::SyscallError { + call: "bpf_map_lookup_elem".to_owned(), + code, + io_error, + })? + .ok_or(MapError::KeyNotFound)?; + let inner_fd = bpf_map_get_fd_by_id(id).map_err(|io_error| MapError::SyscallError { + call: "bpf_map_get_fd_by_id".to_owned(), + code: 0, + io_error, + })?; + Ok(inner_fd as RawFd) + } + + /// An iterator visiting all key-value pairs in arbitrary order. The + /// iterator item type is `Result<(K, V), MapError>`. + pub unsafe fn iter(&self) -> MapIter<'_, K, RawFd> { + MapIter::new(self) + } + + /// An iterator visiting all keys in arbitrary order. The iterator element + /// type is `Result`. + pub unsafe fn keys(&self) -> MapKeys<'_, K> { + MapKeys::new(&self.inner) + } +} + +impl, K: Pod> HashMap { + /// Inserts a map under the given key. + pub fn insert(&mut self, key: K, value: I, flags: u64) -> Result<(), MapError> { + let map_fd = value.into_raw_fd(); + hash_map::insert(&mut self.inner, key, map_fd, flags)?; + // safety: we're closing a RawFd which we have ownership of + // this is required because inserting this in to the map causes + // there to be a reference to the map in both kernel and userspace + unsafe { libc::close(map_fd) }; + Ok(()) + } + + /// Removes a map from the map. + pub fn remove(&mut self, key: &K) -> Result<(), MapError> { + hash_map::remove(&mut self.inner, key) + } +} + +impl, K: Pod> IterableMap for HashMap { + fn map(&self) -> &Map { + &self.inner + } + + unsafe fn get(&self, key: &K) -> Result { + HashMap::get(self, key, 0) + } +} + +impl, K: Pod> MapOfMaps for HashMap { + fn fd_or_err(&self) -> Result { + self.inner.fd_or_err() + } +} + +impl TryFrom for HashMap { + type Error = MapError; + + fn try_from(a: MapRef) -> Result, MapError> { + HashMap::new(a) + } +} + +impl TryFrom for HashMap { + type Error = MapError; + + fn try_from(a: MapRefMut) -> Result, MapError> { + HashMap::new(a) + } +} diff --git a/aya/src/maps/of_maps/mod.rs b/aya/src/maps/of_maps/mod.rs new file mode 100644 index 00000000..f175d6ab --- /dev/null +++ b/aya/src/maps/of_maps/mod.rs @@ -0,0 +1,14 @@ +//! Maps of maps +mod array; +mod hash_map; + +use std::os::unix::io::RawFd; + +use crate::maps::MapError; + +pub use array::Array; +pub use hash_map::HashMap; + +pub trait MapOfMaps { + fn fd_or_err(&self) -> Result; +} diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index a9902015..da39cc8f 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -234,6 +234,17 @@ pub(crate) fn bpf_map_get_next_key( } } +pub(crate) fn bpf_map_get_fd_by_id(map_id: u32) -> Result { + let mut attr = unsafe { mem::zeroed::() }; + + attr.__bindgen_anon_6.__bindgen_anon_1.map_id = map_id; + + match sys_bpf(bpf_cmd::BPF_MAP_GET_FD_BY_ID, &attr) { + Ok(v) => Ok(v as RawFd), + Err((_, err)) => Err(err), + } +} + // since kernel 5.7 pub(crate) fn bpf_link_create( prog_fd: RawFd,