From ec8293ab8644cbf8f1c4e7b1c44b286bc0ae969a Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Mon, 30 May 2022 11:43:38 +0100 Subject: [PATCH] aya: Implement XDP Map Types This commit adds implementations for: - xskmap - devmap - devmap_hash - cpumap Which can all be used to redirect XDP packets to various different locations Signed-off-by: Dave Tucker --- aya/src/maps/mod.rs | 2 + aya/src/maps/xdp/cpu_map.rs | 120 +++++++++++++++++++++++++++++++ aya/src/maps/xdp/dev_map.rs | 117 ++++++++++++++++++++++++++++++ aya/src/maps/xdp/dev_map_hash.rs | 118 ++++++++++++++++++++++++++++++ aya/src/maps/xdp/mod.rs | 10 +++ aya/src/maps/xdp/xsk_map.rs | 118 ++++++++++++++++++++++++++++++ 6 files changed, 485 insertions(+) create mode 100644 aya/src/maps/xdp/cpu_map.rs create mode 100644 aya/src/maps/xdp/dev_map.rs create mode 100644 aya/src/maps/xdp/dev_map_hash.rs create mode 100644 aya/src/maps/xdp/mod.rs create mode 100644 aya/src/maps/xdp/xsk_map.rs diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index 191c5883..190d8c67 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -83,6 +83,7 @@ pub mod queue; pub mod sock; pub mod stack; pub mod stack_trace; +pub mod xdp; pub use array::{Array, PerCpuArray, ProgramArray}; pub use bloom_filter::BloomFilter; @@ -96,6 +97,7 @@ pub use queue::Queue; pub use sock::{SockHash, SockMap}; pub use stack::Stack; pub use stack_trace::StackTraceMap; +pub use xdp::XskMap; #[derive(Error, Debug)] /// Errors occuring from working with Maps diff --git a/aya/src/maps/xdp/cpu_map.rs b/aya/src/maps/xdp/cpu_map.rs new file mode 100644 index 00000000..2b6ba3cb --- /dev/null +++ b/aya/src/maps/xdp/cpu_map.rs @@ -0,0 +1,120 @@ +//! An array of available CPUs. + +use std::{ + convert::TryFrom, + mem, + ops::{Deref, DerefMut}, +}; + +use crate::{ + generated::bpf_map_type::BPF_MAP_TYPE_CPUMAP, + maps::{Map, MapError, MapRef, MapRefMut}, + sys::bpf_map_update_elem, +}; + +/// An array of available CPUs. +/// +/// XDP programs can use this map to redirect packets to a target +/// CPU for processing. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.2. +/// +/// # Examples +/// ```no_run +/// # let bpf = aya::Bpf::load(&[])?; +/// use aya::maps::xdp::CpuMap; +/// use std::convert::{TryFrom, TryInto}; +/// +/// let mut cpumap = CpuMap::try_from(bpf.map_mut("CPUS")?)?; +/// let flags = 0; +/// let queue_size = 2048; +/// for i in 0u32..8u32 { +/// cpumap.set(i, queue_size, flags); +/// } +/// +/// # Ok::<(), aya::BpfError>(()) +/// ``` +#[doc(alias = "BPF_MAP_TYPE_CPUMAP")] +pub struct CpuMap> { + inner: T, +} + +impl> CpuMap { + fn new(map: T) -> Result, MapError> { + let map_type = map.obj.def.map_type; + if map_type != BPF_MAP_TYPE_CPUMAP 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(CpuMap { 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.obj.def.max_entries + } + + 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(()) + } + } +} + +impl + DerefMut> CpuMap { + /// 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: u32, flags: u64) -> Result<(), MapError> { + let fd = self.inner.fd_or_err()?; + self.check_bounds(index)?; + bpf_map_update_elem(fd, &index, &value, flags).map_err(|(code, io_error)| { + MapError::SyscallError { + call: "bpf_map_update_elem".to_owned(), + code, + io_error, + } + })?; + Ok(()) + } +} + +impl TryFrom for CpuMap { + type Error = MapError; + + fn try_from(a: MapRef) -> Result, MapError> { + CpuMap::new(a) + } +} + +impl TryFrom for CpuMap { + type Error = MapError; + + fn try_from(a: MapRefMut) -> Result, MapError> { + CpuMap::new(a) + } +} diff --git a/aya/src/maps/xdp/dev_map.rs b/aya/src/maps/xdp/dev_map.rs new file mode 100644 index 00000000..365a5b1e --- /dev/null +++ b/aya/src/maps/xdp/dev_map.rs @@ -0,0 +1,117 @@ +//! An array of network devices. + +use std::{ + convert::TryFrom, + mem, + ops::{Deref, DerefMut}, +}; + +use crate::{ + generated::bpf_map_type::BPF_MAP_TYPE_DEVMAP, + maps::{Map, MapError, MapRef, MapRefMut}, + sys::bpf_map_update_elem, +}; + +/// An array of network devices. +/// +/// XDP programs can use this map to redirect to other network +/// devices. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.2. +/// +/// # Examples +/// ```no_run +/// # let bpf = aya::Bpf::load(&[])?; +/// use aya::maps::xdp::DevMap; +/// use std::convert::{TryFrom, TryInto}; +/// +/// let mut devmap = DevMap::try_from(bpf.map_mut("IFACES")?)?; +/// let ifindex = 32u32; +/// devmap.set(ifindex, ifindex, 0); +/// +/// # Ok::<(), aya::BpfError>(()) +/// ``` +#[doc(alias = "BPF_MAP_TYPE_DEVMAP")] +pub struct DevMap> { + inner: T, +} + +impl> DevMap { + fn new(map: T) -> Result, MapError> { + let map_type = map.obj.def.map_type; + if map_type != BPF_MAP_TYPE_DEVMAP 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(DevMap { 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.obj.def.max_entries + } + + 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(()) + } + } +} + +impl + DerefMut> DevMap { + /// 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: u32, flags: u64) -> Result<(), MapError> { + let fd = self.inner.fd_or_err()?; + self.check_bounds(index)?; + bpf_map_update_elem(fd, &index, &value, flags).map_err(|(code, io_error)| { + MapError::SyscallError { + call: "bpf_map_update_elem".to_owned(), + code, + io_error, + } + })?; + Ok(()) + } +} + +impl TryFrom for DevMap { + type Error = MapError; + + fn try_from(a: MapRef) -> Result, MapError> { + DevMap::new(a) + } +} + +impl TryFrom for DevMap { + type Error = MapError; + + fn try_from(a: MapRefMut) -> Result, MapError> { + DevMap::new(a) + } +} diff --git a/aya/src/maps/xdp/dev_map_hash.rs b/aya/src/maps/xdp/dev_map_hash.rs new file mode 100644 index 00000000..4cf16762 --- /dev/null +++ b/aya/src/maps/xdp/dev_map_hash.rs @@ -0,0 +1,118 @@ +//! An array of network devices. + +use std::{ + convert::TryFrom, + mem, + ops::{Deref, DerefMut}, +}; + +use crate::{ + generated::bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH, + maps::{Map, MapError, MapRef, MapRefMut}, + sys::bpf_map_update_elem, +}; + +/// An array of network devices. +/// +/// XDP programs can use this map to redirect to other network +/// devices. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.2. +/// +/// # Examples +/// ```no_run +/// # let bpf = aya::Bpf::load(&[])?; +/// use aya::maps::xdp::DevMapHash; +/// use std::convert::{TryFrom, TryInto}; +/// +/// let mut devmap = DevMapHash::try_from(bpf.map_mut("IFACES")?)?; +/// let flags = 0; +/// let ifindex = 32u32; +/// devmap.set(ifindex, ifindex, flags); +/// +/// # Ok::<(), aya::BpfError>(()) +/// ``` +#[doc(alias = "BPF_MAP_TYPE_DEVMAP_HASH")] +pub struct DevMapHash> { + inner: T, +} + +impl> DevMapHash { + fn new(map: T) -> Result, MapError> { + let map_type = map.obj.def.map_type; + if map_type != BPF_MAP_TYPE_DEVMAP_HASH 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(DevMapHash { 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.obj.def.max_entries + } + + 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(()) + } + } +} + +impl + DerefMut> DevMapHash { + /// 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: u32, flags: u64) -> Result<(), MapError> { + let fd = self.inner.fd_or_err()?; + self.check_bounds(index)?; + bpf_map_update_elem(fd, &index, &value, flags).map_err(|(code, io_error)| { + MapError::SyscallError { + call: "bpf_map_update_elem".to_owned(), + code, + io_error, + } + })?; + Ok(()) + } +} + +impl TryFrom for DevMapHash { + type Error = MapError; + + fn try_from(a: MapRef) -> Result, MapError> { + DevMapHash::new(a) + } +} + +impl TryFrom for DevMapHash { + type Error = MapError; + + fn try_from(a: MapRefMut) -> Result, MapError> { + DevMapHash::new(a) + } +} diff --git a/aya/src/maps/xdp/mod.rs b/aya/src/maps/xdp/mod.rs new file mode 100644 index 00000000..6120dc90 --- /dev/null +++ b/aya/src/maps/xdp/mod.rs @@ -0,0 +1,10 @@ +//! XDP maps. +mod cpu_map; +mod dev_map; +mod dev_map_hash; +mod xsk_map; + +pub use cpu_map::CpuMap; +pub use dev_map::DevMap; +pub use dev_map_hash::DevMapHash; +pub use xsk_map::XskMap; diff --git a/aya/src/maps/xdp/xsk_map.rs b/aya/src/maps/xdp/xsk_map.rs new file mode 100644 index 00000000..239c908c --- /dev/null +++ b/aya/src/maps/xdp/xsk_map.rs @@ -0,0 +1,118 @@ +//! An array of AF_XDP sockets. + +use std::{ + convert::TryFrom, + mem, + ops::{Deref, DerefMut}, + os::unix::prelude::{AsRawFd, RawFd}, +}; + +use crate::{ + generated::bpf_map_type::BPF_MAP_TYPE_XSKMAP, + maps::{Map, MapError, MapRef, MapRefMut}, + sys::bpf_map_update_elem, +}; + +/// An array of AF_XDP sockets. +/// +/// XDP programs can use this map to redirect packets to a target +/// AF_XDP socket using the `XDP_REDIRECT` action. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.2. +/// +/// # Examples +/// ```no_run +/// # let bpf = aya::Bpf::load(&[])?; +/// # let socket_fd = 1; +/// use aya::maps::XskMap; +/// use std::convert::{TryFrom, TryInto}; +/// +/// let mut xskmap = XskMap::try_from(bpf.map_mut("SOCKETS")?)?; +/// // socket_fd is the RawFd of an AF_XDP socket +/// xskmap.set(0, socket_fd, 0); +/// # Ok::<(), aya::BpfError>(()) +/// ``` +#[doc(alias = "BPF_MAP_TYPE_XSKMAP")] +pub struct XskMap> { + inner: T, +} + +impl> XskMap { + fn new(map: T) -> Result, MapError> { + let map_type = map.obj.def.map_type; + if map_type != BPF_MAP_TYPE_XSKMAP 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(XskMap { 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.obj.def.max_entries + } + + 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(()) + } + } +} + +impl + DerefMut> XskMap { + /// 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: V, flags: u64) -> Result<(), MapError> { + let fd = self.inner.fd_or_err()?; + self.check_bounds(index)?; + bpf_map_update_elem(fd, &index, &value.as_raw_fd(), flags).map_err( + |(code, io_error)| MapError::SyscallError { + call: "bpf_map_update_elem".to_owned(), + code, + io_error, + }, + )?; + Ok(()) + } +} + +impl TryFrom for XskMap { + type Error = MapError; + + fn try_from(a: MapRef) -> Result, MapError> { + XskMap::new(a) + } +} + +impl TryFrom for XskMap { + type Error = MapError; + + fn try_from(a: MapRefMut) -> Result, MapError> { + XskMap::new(a) + } +}