diff --git a/aya/src/maps/map_lock.rs b/aya/src/maps/map_lock.rs index 36b1ef51..7a693173 100644 --- a/aya/src/maps/map_lock.rs +++ b/aya/src/maps/map_lock.rs @@ -46,11 +46,13 @@ impl MapLock { } } +/// A borrowed reference to a BPF map. pub struct MapRef { _lock: Arc>, guard: RwLockReadGuard<'static, Map>, } +/// A mutable borrowed reference to a BPF map. pub struct MapRefMut { _lock: Arc>, guard: RwLockWriteGuard<'static, Map>, diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index bd666c34..1930979d 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -117,6 +117,7 @@ pub enum MapError { BorrowMutError { name: String }, } +/// A generic handle to a BPF map. #[derive(Debug)] pub struct Map { pub(crate) obj: obj::Map, diff --git a/aya/src/maps/queue.rs b/aya/src/maps/queue.rs index 2d5a6554..ef82ccf9 100644 --- a/aya/src/maps/queue.rs +++ b/aya/src/maps/queue.rs @@ -1,3 +1,4 @@ +//! A FIFO queue. use std::{ convert::TryFrom, marker::PhantomData, diff --git a/aya/src/maps/sock/mod.rs b/aya/src/maps/sock/mod.rs index d8e05ed5..df63c64b 100644 --- a/aya/src/maps/sock/mod.rs +++ b/aya/src/maps/sock/mod.rs @@ -1,3 +1,4 @@ +//! Socket maps. mod sock_hash; mod sock_map; diff --git a/aya/src/maps/sock/sock_hash.rs b/aya/src/maps/sock/sock_hash.rs index 3301e955..a4452070 100644 --- a/aya/src/maps/sock/sock_hash.rs +++ b/aya/src/maps/sock/sock_hash.rs @@ -14,22 +14,48 @@ use crate::{ Pod, }; -/// A hash map that can be shared between eBPF programs and user space. +/// A hash map of TCP or UDP sockets. /// -/// It is required that both keys and values implement the [`Pod`] trait. +/// A `SockHash` is used to store TCP or UDP sockets. eBPF programs can then be +/// attached to the map to inspect, filter or redirect network buffers on those +/// sockets. +/// +/// A `SockHash` can also be used to redirect packets to sockets contained by the +/// map using `bpf_redirect_map()`, `bpf_sk_redirect_hash()` etc. /// /// # Example /// /// ```no_run -/// # let bpf = aya::Bpf::load(&[], None)?; +/// ##[derive(Debug, thiserror::Error)] +/// # enum Error { +/// # #[error(transparent)] +/// # IO(#[from] std::io::Error), +/// # #[error(transparent)] +/// # Map(#[from] aya::maps::MapError), +/// # #[error(transparent)] +/// # Program(#[from] aya::programs::ProgramError), +/// # #[error(transparent)] +/// # Bpf(#[from] aya::BpfError) +/// # } +/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// use std::convert::{TryFrom, TryInto}; +/// use std::io::Write; +/// use std::net::TcpStream; +/// use std::os::unix::io::AsRawFd; /// use aya::maps::SockHash; -/// use std::convert::TryFrom; +/// use aya::programs::SkMsg; +/// +/// let mut intercept_egress = SockHash::try_from(bpf.map_mut("INTERCEPT_EGRESS")?)?; +/// let prog: &mut SkMsg = bpf.program_mut("intercept_egress_packet")?.try_into()?; +/// prog.load()?; +/// prog.attach(&intercept_egress)?; /// -/// const CONFIG_KEY_NUM_RETRIES: u8 = 1; +/// let mut client = TcpStream::connect("127.0.0.1:1234")?; +/// intercept_egress.insert(1234, client.as_raw_fd(), 0)?; /// -/// let mut hm = SockHash::try_from(bpf.map_mut("CONFIG")?)?; -/// hm.insert(CONFIG_KEY_NUM_RETRIES, 3, 0 /* flags */); -/// # Ok::<(), aya::BpfError>(()) +/// // the write will be intercepted +/// client.write_all(b"foo")?; +/// # Ok::<(), Error>(()) /// ``` #[doc(alias = "BPF_MAP_TYPE_SOCKHASH")] pub struct SockHash, K> { @@ -56,8 +82,8 @@ impl, K: Pod> SockHash { }) } - /// Returns a copy of the value associated with the key. - pub unsafe fn get(&self, key: &K, flags: u64) -> Result { + /// Returns the fd of the socket stored at the given key. + pub unsafe fn get(&self, key: &K, flags: u64) -> Result { let fd = self.inner.deref().fd_or_err()?; let value = bpf_map_lookup_elem(fd, key, flags).map_err(|(code, io_error)| { MapError::SyscallError { @@ -71,7 +97,7 @@ impl, K: Pod> SockHash { /// 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, u32> { + pub unsafe fn iter(&self) -> MapIter<'_, K, RawFd> { MapIter::new(self) } @@ -83,23 +109,23 @@ impl, K: Pod> SockHash { } impl, K: Pod> SockHash { - /// Inserts a key-value pair into the map. + /// Inserts a socket under the given key. pub fn insert(&mut self, key: K, value: I, flags: u64) -> Result<(), MapError> { hash_map::insert(&mut self.inner, key, value.as_raw_fd(), flags) } - /// Removes a key from the map. + /// Removes a socket from the map. pub fn remove(&mut self, key: &K) -> Result<(), MapError> { hash_map::remove(&mut self.inner, key) } } -impl, K: Pod> IterableMap for SockHash { +impl, K: Pod> IterableMap for SockHash { fn map(&self) -> &Map { &self.inner } - unsafe fn get(&self, key: &K) -> Result { + unsafe fn get(&self, key: &K) -> Result { SockHash::get(self, key, 0) } } diff --git a/aya/src/maps/sock/sock_map.rs b/aya/src/maps/sock/sock_map.rs index f243d49b..6883475f 100644 --- a/aya/src/maps/sock/sock_map.rs +++ b/aya/src/maps/sock/sock_map.rs @@ -13,14 +13,29 @@ use crate::{ sys::{bpf_map_delete_elem, bpf_map_update_elem}, }; -/// An array of TCP or UDP sock objects. Primarly used for doing socket redirect with eBPF helpers. +/// An array of TCP or UDP sockets. /// -/// A sock map can have two eBPF programs attached: one to parse packets and one to provide a -/// redirect decision on packets. Whenever a sock object is added to the map, the map's programs -/// are automatically attached to the socket. +/// A `SockMap` is used to store TCP or UDP sockets. eBPF programs can then be +/// attached to the map to inspect, filter or redirect network buffers on those +/// sockets. +/// +/// A `SockMap` can also be used to redirect packets to sockets contained by the +/// map using `bpf_redirect_map()`, `bpf_sk_redirect_map()` etc. /// /// # Example /// +/// ```no_run +/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// use std::convert::{TryFrom, TryInto}; +/// use aya::maps::SockMap; +/// use aya::programs::SkMsg; +/// +/// let intercept_egress = SockMap::try_from(bpf.map_mut("INTERCEPT_EGRESS")?)?; +/// let prog: &mut SkMsg = bpf.program_mut("intercept_egress_packet")?.try_into()?; +/// prog.load()?; +/// prog.attach(&intercept_egress)?; +/// # Ok::<(), aya::BpfError>(()) +/// ``` pub struct SockMap> { pub(crate) inner: T, } @@ -66,10 +81,7 @@ impl> SockMap { } impl + DerefMut> SockMap { - /// Stores a TCP socket into the map. - /// - /// eBPF programs can then pass `index` to the `bpf_sk_redirect_map()` helper to redirect - /// packets to the corresponding socket. + /// Stores a socket into the map. pub fn set(&mut self, index: u32, socket: &I, flags: u64) -> Result<(), MapError> { let fd = self.inner.fd_or_err()?; self.check_bounds(index)?; @@ -83,7 +95,7 @@ impl + DerefMut> SockMap { Ok(()) } - /// Removes the TCP socket stored at `index` from the map. + /// Removes the socket stored at `index` from the map. pub fn clear_index(&mut self, index: &u32) -> Result<(), MapError> { let fd = self.inner.fd_or_err()?; self.check_bounds(*index)?; diff --git a/aya/src/maps/stack.rs b/aya/src/maps/stack.rs index 7277a71a..46519ecb 100644 --- a/aya/src/maps/stack.rs +++ b/aya/src/maps/stack.rs @@ -1,3 +1,4 @@ +//! A LIFO stack. use std::{ convert::TryFrom, marker::PhantomData, @@ -13,6 +14,19 @@ use crate::{ }; /// A LIFO stack. +/// +/// # Example +/// ```no_run +/// # let bpf = aya::Bpf::load(&[], None)?; +/// use aya::maps::Stack; +/// use std::convert::TryFrom; +/// +/// let mut stack = Stack::try_from(bpf.map_mut("STACK")?)?; +/// stack.push(42, 0)?; +/// stack.push(43, 0)?; +/// assert_eq!(stack.pop(0)?, 43); +/// # Ok::<(), aya::BpfError>(()) +/// ``` pub struct Stack, V: Pod> { inner: T, _v: PhantomData, @@ -78,19 +92,6 @@ impl + DerefMut, V: Pod> Stack { /// # Errors /// /// [`MapError::SyscallError`] if `bpf_map_update_elem` fails. - /// - /// # Example - /// ```no_run - /// # let bpf = aya::Bpf::load(&[], None)?; - /// use aya::maps::Stack; - /// use std::convert::TryFrom; - /// - /// let mut stack = Stack::try_from(bpf.map_mut("ARRAY")?)?; - /// stack.push(42, 0)?; - /// stack.push(43, 0)?; - /// assert_eq!(stack.pop(0)?, 43); - /// # Ok::<(), aya::BpfError>(()) - /// ``` pub fn push(&mut self, value: V, flags: u64) -> Result<(), MapError> { let fd = self.inner.fd_or_err()?; bpf_map_update_elem(fd, &0, &value, flags).map_err(|(code, io_error)| { diff --git a/aya/src/programs/sk_skb.rs b/aya/src/programs/sk_skb.rs index 35efaf20..f89b1394 100644 --- a/aya/src/programs/sk_skb.rs +++ b/aya/src/programs/sk_skb.rs @@ -14,6 +14,15 @@ pub enum SkSkbKind { StreamVerdict, } +/// A socket buffer program. +/// +/// Socket buffer programs are attached to [socket maps], and can be used to +/// inspect, redirect or filter packet. See [SockMap] and [SockHash] for more +/// info and examples. +/// +/// [socket maps]: crate::maps::sock +/// [SockMap]: crate::maps::SockMap +/// [SockHash]: crate::maps::SockHash #[derive(Debug)] pub struct SkSkb { pub(crate) data: ProgramData, @@ -33,7 +42,7 @@ impl SkSkb { self.data.name.to_string() } - /// Attaches the program to the given sockmap. + /// Attaches the program to the given socket map. pub fn attach(&mut self, map: &dyn SocketMap) -> Result { let prog_fd = self.data.fd_or_err()?; let map_fd = map.fd_or_err()?;