123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908 |
- //! Data structures used to setup and share data with eBPF programs.
- //!
- //! The eBPF platform provides data structures - maps in eBPF speak - that are
- //! used to setup and share data with eBPF programs. When you call
- //! [`Bpf::load_file`](crate::Bpf::load_file) or
- //! [`Bpf::load`](crate::Bpf::load), all the maps defined in the eBPF code get
- //! initialized and can then be accessed using [`Bpf::map`](crate::Bpf::map),
- //! [`Bpf::map_mut`](crate::Bpf::map_mut), or [`Bpf::take_map`](crate::Bpf::take_map).
- //!
- //! # Typed maps
- //!
- //! The eBPF API includes many map types each supporting different operations.
- //! [`Bpf::map`](crate::Bpf::map), [`Bpf::map_mut`](crate::Bpf::map_mut), and
- //! [`Bpf::take_map`](crate::Bpf::take_map) always return the
- //! opaque [`&Map`](crate::maps::Map), [`&mut Map`](crate::maps::Map), and [`Map`](crate::maps::Map)
- //! types respectively. Those three types can be converted to *typed maps* using
- //! the [`TryFrom`](std::convert::TryFrom) or [`TryInto`](std::convert::TryInto)
- //! trait. For example:
- //!
- //! ```no_run
- //! # let mut bpf = aya::Bpf::load(&[])?;
- //! use aya::maps::SockMap;
- //! use aya::programs::SkMsg;
- //!
- //! let intercept_egress = SockMap::try_from(bpf.map_mut("INTERCEPT_EGRESS").unwrap())?;
- //! let map_fd = intercept_egress.fd()?;
- //! let prog: &mut SkMsg = bpf.program_mut("intercept_egress_packet").unwrap().try_into()?;
- //! prog.load()?;
- //! prog.attach(map_fd)?;
- //!
- //! # Ok::<(), aya::BpfError>(())
- //! ```
- //!
- //! # Maps and `Pod` values
- //!
- //! Many map operations copy data from kernel space to user space and vice
- //! versa. Because of that, all map values must be plain old data and therefore
- //! implement the [Pod] trait.
- use std::{
- ffi::CString,
- fmt, io,
- marker::PhantomData,
- mem,
- ops::Deref,
- os::fd::{AsRawFd, RawFd},
- path::Path,
- ptr,
- };
- use crate::util::KernelVersion;
- use libc::{getrlimit, rlimit, RLIMIT_MEMLOCK, RLIM_INFINITY};
- use log::warn;
- use thiserror::Error;
- use crate::{
- obj::{self, parse_map_info},
- pin::PinError,
- sys::{
- bpf_create_map, bpf_get_object, bpf_map_get_info_by_fd, bpf_map_get_next_key,
- bpf_pin_object,
- },
- util::nr_cpus,
- PinningType, Pod,
- };
- pub mod array;
- pub mod bloom_filter;
- pub mod hash_map;
- pub mod lpm_trie;
- pub mod perf;
- pub mod queue;
- pub mod sock;
- pub mod stack;
- pub mod stack_trace;
- pub use array::{Array, PerCpuArray, ProgramArray};
- pub use bloom_filter::BloomFilter;
- pub use hash_map::{HashMap, PerCpuHashMap};
- pub use lpm_trie::LpmTrie;
- #[cfg(feature = "async")]
- #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
- pub use perf::AsyncPerfEventArray;
- pub use perf::PerfEventArray;
- pub use queue::Queue;
- pub use sock::{SockHash, SockMap};
- pub use stack::Stack;
- pub use stack_trace::StackTraceMap;
- #[derive(Error, Debug)]
- /// Errors occuring from working with Maps
- pub enum MapError {
- /// Invalid map type encontered
- #[error("invalid map type {map_type}")]
- InvalidMapType {
- /// The map type
- map_type: u32,
- },
- /// Invalid map name encountered
- #[error("invalid map name `{name}`")]
- InvalidName {
- /// The map name
- name: String,
- },
- /// The map has not been created
- #[error("the map has not been created")]
- NotCreated,
- /// The map has already been created
- #[error("the map `{name}` has already been created")]
- AlreadyCreated {
- /// Map name
- name: String,
- },
- /// Failed to create map
- #[error("failed to create map `{name}` with code {code}")]
- CreateError {
- /// Map name
- name: String,
- /// Error code
- code: libc::c_long,
- #[source]
- /// Original io::Error
- io_error: io::Error,
- },
- /// Invalid key size
- #[error("invalid key size {size}, expected {expected}")]
- InvalidKeySize {
- /// Size encountered
- size: usize,
- /// Size expected
- expected: usize,
- },
- /// Invalid value size
- #[error("invalid value size {size}, expected {expected}")]
- InvalidValueSize {
- /// Size encountered
- size: usize,
- /// Size expected
- expected: usize,
- },
- /// Index is out of bounds
- #[error("the index is {index} but `max_entries` is {max_entries}")]
- OutOfBounds {
- /// Index accessed
- index: u32,
- /// Map size
- max_entries: u32,
- },
- /// Key not found
- #[error("key not found")]
- KeyNotFound,
- /// Element not found
- #[error("element not found")]
- ElementNotFound,
- /// Progam Not Loaded
- #[error("the program is not loaded")]
- ProgramNotLoaded,
- /// Syscall failed
- #[error("the `{call}` syscall failed")]
- SyscallError {
- /// Syscall Name
- call: &'static str,
- /// Original io::Error
- io_error: io::Error,
- },
- /// Could not pin map by name
- #[error("map `{name:?}` requested pinning by name. pinning failed")]
- PinError {
- /// The map name
- name: Option<String>,
- /// The reason for the failure
- #[source]
- error: PinError,
- },
- }
- /// A map file descriptor.
- pub struct MapFd(RawFd);
- impl AsRawFd for MapFd {
- fn as_raw_fd(&self) -> RawFd {
- self.0
- }
- }
- #[derive(PartialEq, Eq, PartialOrd, Ord)]
- struct RlimitSize(usize);
- impl fmt::Display for RlimitSize {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if self.0 < 1024 {
- write!(f, "{} bytes", self.0)
- } else if self.0 < 1024 * 1024 {
- write!(f, "{} KiB", self.0 / 1024)
- } else {
- write!(f, "{} MiB", self.0 / 1024 / 1024)
- }
- }
- }
- /// Raises a warning about rlimit. Should be used only if creating a map was not
- /// successful.
- fn maybe_warn_rlimit() {
- let mut limit = std::mem::MaybeUninit::<rlimit>::uninit();
- let ret = unsafe { getrlimit(RLIMIT_MEMLOCK, limit.as_mut_ptr()) };
- if ret == 0 {
- let limit = unsafe { limit.assume_init() };
- let limit: RlimitSize = RlimitSize(limit.rlim_cur.try_into().unwrap());
- if limit.0 == RLIM_INFINITY.try_into().unwrap() {
- return;
- }
- warn!(
- "RLIMIT_MEMLOCK value is {}, not RLIM_INFNITY; if experiencing problems with creating \
- maps, try raising RMILIT_MEMLOCK either to RLIM_INFINITY or to a higher value sufficient \
- for size of your maps",
- limit
- );
- }
- }
- /// eBPF map types.
- #[derive(Debug)]
- pub enum Map {
- /// A [`Array`] map
- Array(MapData),
- /// A [`PerCpuArray`] map
- PerCpuArray(MapData),
- /// A [`ProgramArray`] map
- ProgramArray(MapData),
- /// A [`HashMap`] map
- HashMap(MapData),
- /// A [`PerCpuHashMap`] map
- PerCpuHashMap(MapData),
- /// A [`HashMap`] map that uses a LRU eviction policy.
- LruHashMap(MapData),
- /// A [`PerCpuHashMap`] map that uses a LRU eviction policy.
- PerCpuLruHashMap(MapData),
- /// A [`PerfEventArray`] map
- PerfEventArray(MapData),
- /// A [`SockMap`] map
- SockMap(MapData),
- /// A [`SockHash`] map
- SockHash(MapData),
- /// A [`BloomFilter`] map
- BloomFilter(MapData),
- /// A [`LpmTrie`] map
- LpmTrie(MapData),
- /// A [`Stack`] map
- Stack(MapData),
- /// A [`StackTraceMap`] map
- StackTraceMap(MapData),
- /// A [`Queue`] map
- Queue(MapData),
- }
- impl Map {
- /// Returns the low level map type.
- fn map_type(&self) -> u32 {
- match self {
- Map::Array(map) => map.obj.map_type(),
- Map::PerCpuArray(map) => map.obj.map_type(),
- Map::ProgramArray(map) => map.obj.map_type(),
- Map::HashMap(map) => map.obj.map_type(),
- Map::LruHashMap(map) => map.obj.map_type(),
- Map::PerCpuHashMap(map) => map.obj.map_type(),
- Map::PerCpuLruHashMap(map) => map.obj.map_type(),
- Map::PerfEventArray(map) => map.obj.map_type(),
- Map::SockHash(map) => map.obj.map_type(),
- Map::SockMap(map) => map.obj.map_type(),
- Map::BloomFilter(map) => map.obj.map_type(),
- Map::LpmTrie(map) => map.obj.map_type(),
- Map::Stack(map) => map.obj.map_type(),
- Map::StackTraceMap(map) => map.obj.map_type(),
- Map::Queue(map) => map.obj.map_type(),
- }
- }
- }
- macro_rules! impl_try_from_map {
- ($($tx:ident from Map::$ty:ident),+ $(,)?) => {
- $(
- impl<'a> TryFrom<&'a Map> for $tx<&'a MapData> {
- type Error = MapError;
- fn try_from(map: &'a Map) -> Result<$tx<&'a MapData>, MapError> {
- match map {
- Map::$ty(m) => {
- $tx::new(m)
- },
- _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
- }
- }
- }
- impl<'a,> TryFrom<&'a mut Map> for $tx<&'a mut MapData> {
- type Error = MapError;
- fn try_from(map: &'a mut Map) -> Result<$tx<&'a mut MapData>, MapError> {
- match map {
- Map::$ty(m) => {
- $tx::new(m)
- },
- _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
- }
- }
- }
- impl TryFrom<Map> for $tx<MapData> {
- type Error = MapError;
- fn try_from(map: Map) -> Result<$tx<MapData>, MapError> {
- match map {
- Map::$ty(m) => {
- $tx::new(m)
- },
- _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
- }
- }
- }
- )+
- }
- }
- impl_try_from_map!(
- ProgramArray from Map::ProgramArray,
- SockMap from Map::SockMap,
- PerfEventArray from Map::PerfEventArray,
- StackTraceMap from Map::StackTraceMap,
- );
- #[cfg(feature = "async")]
- #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
- impl_try_from_map!(
- AsyncPerfEventArray from Map::PerfEventArray,
- );
- macro_rules! impl_try_from_map_generic_key_or_value {
- ($($ty:ident),+ $(,)?) => {
- $(
- impl<'a, V:Pod> TryFrom<&'a Map> for $ty<&'a MapData, V> {
- type Error = MapError;
- fn try_from(map: &'a Map) -> Result<$ty<&'a MapData , V>, MapError> {
- match map {
- Map::$ty(m) => {
- $ty::new(m)
- },
- _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
- }
- }
- }
- impl<'a,V: Pod> TryFrom<&'a mut Map> for $ty<&'a mut MapData, V> {
- type Error = MapError;
- fn try_from(map: &'a mut Map) -> Result<$ty<&'a mut MapData, V>, MapError> {
- match map {
- Map::$ty(m) => {
- $ty::new(m)
- },
- _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
- }
- }
- }
- impl<V: Pod> TryFrom<Map> for $ty<MapData, V> {
- type Error = MapError;
- fn try_from(map: Map) -> Result<$ty<MapData, V>, MapError> {
- match map {
- Map::$ty(m) => {
- $ty::new(m)
- },
- _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
- }
- }
- }
- )+
- }
- }
- impl_try_from_map_generic_key_or_value!(Array, PerCpuArray, SockHash, BloomFilter, Queue, Stack,);
- macro_rules! impl_try_from_map_generic_key_and_value {
- ($($ty:ident),+ $(,)?) => {
- $(
- impl<'a, V: Pod, K: Pod> TryFrom<&'a Map> for $ty<&'a MapData, V, K> {
- type Error = MapError;
- fn try_from(map: &'a Map) -> Result<$ty<&'a MapData,V,K>, MapError> {
- match map {
- Map::$ty(m) => {
- $ty::new(m)
- },
- _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
- }
- }
- }
- impl<'a,V: Pod,K: Pod> TryFrom<&'a mut Map> for $ty<&'a mut MapData, V, K> {
- type Error = MapError;
- fn try_from(map: &'a mut Map) -> Result<$ty<&'a mut MapData, V, K>, MapError> {
- match map {
- Map::$ty(m) => {
- $ty::new(m)
- },
- _ => Err(MapError::InvalidMapType{ map_type: map.map_type()}),
- }
- }
- }
- impl<V: Pod, K: Pod> TryFrom<Map> for $ty<MapData, V, K> {
- type Error = MapError;
- fn try_from(map: Map) -> Result<$ty<MapData, V, K>, MapError> {
- match map {
- Map::$ty(m) => $ty::new(m),
- _ => Err(MapError::InvalidMapType { map_type: map.map_type() }),
- }
- }
- }
- )+
- }
- }
- impl_try_from_map_generic_key_and_value!(HashMap, PerCpuHashMap, LpmTrie);
- pub(crate) fn check_bounds(map: &MapData, index: u32) -> Result<(), MapError> {
- let max_entries = map.obj.max_entries();
- if index >= max_entries {
- Err(MapError::OutOfBounds { index, max_entries })
- } else {
- Ok(())
- }
- }
- pub(crate) fn check_kv_size<K, V>(map: &MapData) -> Result<(), MapError> {
- let size = mem::size_of::<K>();
- let expected = map.obj.key_size() as usize;
- if size != expected {
- return Err(MapError::InvalidKeySize { size, expected });
- }
- let size = mem::size_of::<V>();
- let expected = map.obj.value_size() as usize;
- if size != expected {
- return Err(MapError::InvalidValueSize { size, expected });
- };
- Ok(())
- }
- pub(crate) fn check_v_size<V>(map: &MapData) -> Result<(), MapError> {
- let size = mem::size_of::<V>();
- let expected = map.obj.value_size() as usize;
- if size != expected {
- return Err(MapError::InvalidValueSize { size, expected });
- };
- Ok(())
- }
- /// A generic handle to a BPF map.
- ///
- /// You should never need to use this unless you're implementing a new map type.
- #[derive(Debug)]
- pub struct MapData {
- pub(crate) obj: obj::Map,
- pub(crate) fd: Option<RawFd>,
- pub(crate) btf_fd: Option<RawFd>,
- /// Indicates if this map has been pinned to bpffs
- pub pinned: bool,
- }
- impl MapData {
- /// Creates a new map with the provided `name`
- pub fn create(&mut self, name: &str) -> Result<RawFd, MapError> {
- if self.fd.is_some() {
- return Err(MapError::AlreadyCreated { name: name.into() });
- }
- let c_name = CString::new(name).map_err(|_| MapError::InvalidName { name: name.into() })?;
- #[cfg(not(test))]
- let kernel_version = KernelVersion::current().unwrap();
- #[cfg(test)]
- let kernel_version = KernelVersion::new(0xff, 0xff, 0xff);
- let fd = bpf_create_map(&c_name, &self.obj, self.btf_fd, kernel_version).map_err(
- |(code, io_error)| {
- if kernel_version < KernelVersion::new(5, 11, 0) {
- maybe_warn_rlimit();
- }
- MapError::CreateError {
- name: name.into(),
- code,
- io_error,
- }
- },
- )? as RawFd;
- self.fd = Some(fd);
- Ok(fd)
- }
- pub(crate) fn open_pinned<P: AsRef<Path>>(
- &mut self,
- name: &str,
- path: P,
- ) -> Result<RawFd, MapError> {
- if self.fd.is_some() {
- return Err(MapError::AlreadyCreated { name: name.into() });
- }
- let map_path = path.as_ref().join(name);
- let path_string = CString::new(map_path.to_str().unwrap()).unwrap();
- let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| MapError::SyscallError {
- call: "BPF_OBJ_GET",
- io_error,
- })? as RawFd;
- self.fd = Some(fd);
- Ok(fd)
- }
- /// Loads a map from a pinned path in bpffs.
- pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<MapData, MapError> {
- let path_string =
- CString::new(path.as_ref().to_string_lossy().into_owned()).map_err(|e| {
- MapError::PinError {
- name: None,
- error: PinError::InvalidPinPath {
- error: e.to_string(),
- },
- }
- })?;
- let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| MapError::SyscallError {
- call: "BPF_OBJ_GET",
- io_error,
- })? as RawFd;
- let info = bpf_map_get_info_by_fd(fd).map_err(|io_error| MapError::SyscallError {
- call: "BPF_MAP_GET_INFO_BY_FD",
- io_error,
- })?;
- Ok(MapData {
- obj: parse_map_info(info, PinningType::ByName),
- fd: Some(fd),
- btf_fd: None,
- pinned: true,
- })
- }
- /// Loads a map from a [`RawFd`].
- ///
- /// If loading from a BPF Filesystem (bpffs) you should use [`Map::from_pin`](crate::maps::MapData::from_pin).
- /// This API is intended for cases where you have received a valid BPF FD from some other means.
- /// For example, you received an FD over Unix Domain Socket.
- pub fn from_fd(fd: RawFd) -> Result<MapData, MapError> {
- let info = bpf_map_get_info_by_fd(fd).map_err(|io_error| MapError::SyscallError {
- call: "BPF_OBJ_GET",
- io_error,
- })?;
- Ok(MapData {
- obj: parse_map_info(info, PinningType::None),
- fd: Some(fd),
- btf_fd: None,
- pinned: false,
- })
- }
- pub(crate) fn fd_or_err(&self) -> Result<RawFd, MapError> {
- self.fd.ok_or(MapError::NotCreated)
- }
- pub(crate) fn pin<P: AsRef<Path>>(&mut self, name: &str, path: P) -> Result<(), PinError> {
- if self.pinned {
- return Err(PinError::AlreadyPinned { name: name.into() });
- }
- let map_path = path.as_ref().join(name);
- let fd = self.fd.ok_or(PinError::NoFd {
- name: name.to_string(),
- })?;
- let path_string = CString::new(map_path.to_string_lossy().into_owned()).map_err(|e| {
- PinError::InvalidPinPath {
- error: e.to_string(),
- }
- })?;
- bpf_pin_object(fd, &path_string).map_err(|(_, io_error)| PinError::SyscallError {
- name: "BPF_OBJ_PIN",
- io_error,
- })?;
- self.pinned = true;
- Ok(())
- }
- /// Returns the file descriptor of the map.
- ///
- /// Can be converted to [`RawFd`] using [`AsRawFd`].
- pub fn fd(&self) -> Option<MapFd> {
- self.fd.map(MapFd)
- }
- }
- impl Drop for MapData {
- fn drop(&mut self) {
- // TODO: Replace this with an OwnedFd once that is stabilized.
- if let Some(fd) = self.fd.take() {
- unsafe { libc::close(fd) };
- }
- }
- }
- impl Clone for MapData {
- fn clone(&self) -> MapData {
- MapData {
- obj: self.obj.clone(),
- fd: self.fd.map(|fd| unsafe { libc::dup(fd) }),
- btf_fd: self.btf_fd,
- pinned: self.pinned,
- }
- }
- }
- /// An iterable map
- pub trait IterableMap<K: Pod, V> {
- /// Get a generic map handle
- fn map(&self) -> &MapData;
- /// Get the value for the provided `key`
- fn get(&self, key: &K) -> Result<V, MapError>;
- }
- /// Iterator returned by `map.keys()`.
- pub struct MapKeys<'coll, K: Pod> {
- map: &'coll MapData,
- err: bool,
- key: Option<K>,
- }
- impl<'coll, K: Pod> MapKeys<'coll, K> {
- fn new(map: &'coll MapData) -> MapKeys<'coll, K> {
- MapKeys {
- map,
- err: false,
- key: None,
- }
- }
- }
- impl<K: Pod> Iterator for MapKeys<'_, K> {
- type Item = Result<K, MapError>;
- fn next(&mut self) -> Option<Result<K, MapError>> {
- if self.err {
- return None;
- }
- let fd = match self.map.fd_or_err() {
- Ok(fd) => fd,
- Err(e) => {
- self.err = true;
- return Some(Err(e));
- }
- };
- match bpf_map_get_next_key(fd, self.key.as_ref()) {
- Ok(Some(key)) => {
- self.key = Some(key);
- Some(Ok(key))
- }
- Ok(None) => {
- self.key = None;
- None
- }
- Err((_, io_error)) => {
- self.err = true;
- Some(Err(MapError::SyscallError {
- call: "bpf_map_get_next_key",
- io_error,
- }))
- }
- }
- }
- }
- /// Iterator returned by `map.iter()`.
- pub struct MapIter<'coll, K: Pod, V, I: IterableMap<K, V>> {
- keys: MapKeys<'coll, K>,
- map: &'coll I,
- _v: PhantomData<V>,
- }
- impl<'coll, K: Pod, V, I: IterableMap<K, V>> MapIter<'coll, K, V, I> {
- fn new(map: &'coll I) -> MapIter<'coll, K, V, I> {
- MapIter {
- keys: MapKeys::new(map.map()),
- map,
- _v: PhantomData,
- }
- }
- }
- impl<K: Pod, V, I: IterableMap<K, V>> Iterator for MapIter<'_, K, V, I> {
- type Item = Result<(K, V), MapError>;
- fn next(&mut self) -> Option<Self::Item> {
- loop {
- match self.keys.next() {
- Some(Ok(key)) => match self.map.get(&key) {
- Ok(value) => return Some(Ok((key, value))),
- Err(MapError::KeyNotFound) => continue,
- Err(e) => return Some(Err(e)),
- },
- Some(Err(e)) => return Some(Err(e)),
- None => return None,
- }
- }
- }
- }
- pub(crate) struct PerCpuKernelMem {
- bytes: Vec<u8>,
- }
- impl PerCpuKernelMem {
- pub(crate) fn as_mut_ptr(&mut self) -> *mut u8 {
- self.bytes.as_mut_ptr()
- }
- }
- /// A slice of per-CPU values.
- ///
- /// Used by maps that implement per-CPU storage like [`PerCpuHashMap`].
- ///
- /// # Examples
- ///
- /// ```no_run
- /// # #[derive(thiserror::Error, Debug)]
- /// # enum Error {
- /// # #[error(transparent)]
- /// # IO(#[from] std::io::Error),
- /// # #[error(transparent)]
- /// # Map(#[from] aya::maps::MapError),
- /// # #[error(transparent)]
- /// # Bpf(#[from] aya::BpfError)
- /// # }
- /// # let bpf = aya::Bpf::load(&[])?;
- /// use aya::maps::PerCpuValues;
- /// use aya::util::nr_cpus;
- ///
- /// let values = PerCpuValues::try_from(vec![42u32; nr_cpus()?])?;
- /// # Ok::<(), Error>(())
- /// ```
- #[derive(Debug)]
- pub struct PerCpuValues<T: Pod> {
- values: Box<[T]>,
- }
- impl<T: Pod> TryFrom<Vec<T>> for PerCpuValues<T> {
- type Error = io::Error;
- fn try_from(values: Vec<T>) -> Result<Self, Self::Error> {
- let nr_cpus = nr_cpus()?;
- if values.len() != nr_cpus {
- return Err(io::Error::new(
- io::ErrorKind::InvalidInput,
- format!("not enough values ({}), nr_cpus: {}", values.len(), nr_cpus),
- ));
- }
- Ok(PerCpuValues {
- values: values.into_boxed_slice(),
- })
- }
- }
- impl<T: Pod> PerCpuValues<T> {
- pub(crate) fn alloc_kernel_mem() -> Result<PerCpuKernelMem, io::Error> {
- let value_size = (mem::size_of::<T>() + 7) & !7;
- Ok(PerCpuKernelMem {
- bytes: vec![0u8; nr_cpus()? * value_size],
- })
- }
- pub(crate) unsafe fn from_kernel_mem(mem: PerCpuKernelMem) -> PerCpuValues<T> {
- let mem_ptr = mem.bytes.as_ptr() as usize;
- let value_size = (mem::size_of::<T>() + 7) & !7;
- let mut values = Vec::new();
- let mut offset = 0;
- while offset < mem.bytes.len() {
- values.push(ptr::read_unaligned((mem_ptr + offset) as *const _));
- offset += value_size;
- }
- PerCpuValues {
- values: values.into_boxed_slice(),
- }
- }
- pub(crate) fn build_kernel_mem(&self) -> Result<PerCpuKernelMem, io::Error> {
- let mut mem = PerCpuValues::<T>::alloc_kernel_mem()?;
- let mem_ptr = mem.as_mut_ptr() as usize;
- let value_size = (mem::size_of::<T>() + 7) & !7;
- for i in 0..self.values.len() {
- unsafe { ptr::write_unaligned((mem_ptr + i * value_size) as *mut _, self.values[i]) };
- }
- Ok(mem)
- }
- }
- impl<T: Pod> Deref for PerCpuValues<T> {
- type Target = Box<[T]>;
- fn deref(&self) -> &Self::Target {
- &self.values
- }
- }
- #[cfg(test)]
- mod tests {
- use libc::EFAULT;
- use matches::assert_matches;
- use crate::{
- bpf_map_def,
- generated::{bpf_cmd, bpf_map_type::BPF_MAP_TYPE_HASH},
- maps::MapData,
- obj::{maps::LegacyMap, BpfSectionKind},
- sys::{override_syscall, Syscall},
- };
- use super::*;
- fn new_obj_map() -> obj::Map {
- obj::Map::Legacy(LegacyMap {
- def: bpf_map_def {
- map_type: BPF_MAP_TYPE_HASH as u32,
- key_size: 4,
- value_size: 4,
- max_entries: 1024,
- ..Default::default()
- },
- section_index: 0,
- section_kind: BpfSectionKind::Maps,
- symbol_index: Some(0),
- data: Vec::new(),
- })
- }
- fn new_map() -> MapData {
- MapData {
- obj: new_obj_map(),
- fd: None,
- pinned: false,
- btf_fd: None,
- }
- }
- #[test]
- fn test_create() {
- override_syscall(|call| match call {
- Syscall::Bpf {
- cmd: bpf_cmd::BPF_MAP_CREATE,
- ..
- } => Ok(42),
- _ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
- });
- let mut map = new_map();
- assert_matches!(map.create("foo"), Ok(42));
- assert_eq!(map.fd, Some(42));
- assert_matches!(map.create("foo"), Err(MapError::AlreadyCreated { .. }));
- }
- #[test]
- fn test_create_failed() {
- override_syscall(|_| Err((-42, io::Error::from_raw_os_error(EFAULT))));
- let mut map = new_map();
- let ret = map.create("foo");
- assert_matches!(ret, Err(MapError::CreateError { .. }));
- if let Err(MapError::CreateError {
- name,
- code,
- io_error,
- }) = ret
- {
- assert_eq!(name, "foo");
- assert_eq!(code, -42);
- assert_eq!(io_error.raw_os_error(), Some(EFAULT));
- }
- assert_eq!(map.fd, None);
- }
- }
|