More docs

pull/1/head
Alessandro Decina 3 years ago
parent 6c7df27bd0
commit 11e21e83be

@ -213,7 +213,7 @@ impl Bpf {
/// Returns a reference to the map with the given name. /// Returns a reference to the map with the given name.
/// ///
/// The returned type is mostly opaque. In order to do anything useful with it you need to /// The returned type is mostly opaque. In order to do anything useful with it you need to
/// convert it to a [concrete map type](crate::maps). /// convert it to a [typed map](crate::maps).
/// ///
/// For more details and examples on maps and their usage, see the [maps module /// For more details and examples on maps and their usage, see the [maps module
/// documentation][crate::maps]. /// documentation][crate::maps].
@ -238,7 +238,7 @@ impl Bpf {
/// Returns a mutable reference to the map with the given name. /// Returns a mutable reference to the map with the given name.
/// ///
/// The returned type is mostly opaque. In order to do anything useful with it you need to /// The returned type is mostly opaque. In order to do anything useful with it you need to
/// convert it to a [concrete map type](crate::maps). /// convert it to a [typed map](crate::maps).
/// ///
/// For more details and examples on maps and their usage, see the [maps module /// For more details and examples on maps and their usage, see the [maps module
/// documentation][crate::maps]. /// documentation][crate::maps].
@ -361,6 +361,7 @@ impl Bpf {
} }
} }
/// The error type returned by [`Bpf::load_file`] and [`Bpf::load`].
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum BpfError { pub enum BpfError {
#[error("error loading {path}")] #[error("error loading {path}")]

@ -10,28 +10,25 @@
//! crate to execute syscalls. With BTF support and when linked with musl, it offers a true //! crate to execute syscalls. With BTF support and when linked with musl, it offers a true
//! [compile once, run everywhere //! [compile once, run everywhere
//! solution](https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf-portability-and-co-re.html), //! solution](https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf-portability-and-co-re.html),
//! where one self-contained binary can be deployed on many linux distributions //! where a single self-contained binary can be deployed on many linux distributions
//! and kernel versions. //! and kernel versions.
//! //!
//! Some of the major features provided include: //! Some of the major features provided include:
//! //!
//! * Support for the BPF Type Format (BTF), which is transparently enabled when //! * Support for the **BPF Type Format** (BTF), which is transparently enabled when
//! supported by the target kernel. This allows eBPF programs compiled against //! supported by the target kernel. This allows eBPF programs compiled against
//! one kernel version to run on different kernel versions without the need to //! one kernel version to run on different kernel versions without the need to
//! recompile. //! recompile.
//! * Support for global data maps, which allows eBPF programs to make use of global data and //! * Support for function call relocation and global data maps, which
//! variables. This is especially useful when the eBPF code itself is written in Rust, and makes //! allows eBPF programs to make **function calls** and use **global variables
//! use of byte literals and other initializers that get place in global data sections. //! and initializers**.
//! * Support for function calls, so eBPF programs can call other functions and are not //! * **Async support** with both [tokio] and [async-std].
//! forced to inline everything.
//! * Async support with both [tokio](https://docs.rs/tokio) and [async-std](https://docs.rs/async-std).
//! * Easy to deploy and fast to build: aya doesn't require a kernel build or //! * Easy to deploy and fast to build: aya doesn't require a kernel build or
//! compiled headers, and not even a C toolchain; a release build completes in a matter //! compiled headers, and not even a C toolchain; a release build completes in a matter
//! of seconds. //! of seconds.
//! //!
//! # Minimum kernel version //! [tokio]: https://docs.rs/tokio
//! //! [async-std]: https://docs.rs/async-std
//! Aya currently supports kernels version 5.4 (latest LTS) and newer.
#![deny(clippy::all)] #![deny(clippy::all)]
#[macro_use] #[macro_use]

@ -16,6 +16,18 @@ use crate::{
/// ///
/// The size of the array is defined on the eBPF side using the `bpf_map_def::max_entries` field. /// The size of the array is defined on the eBPF side using the `bpf_map_def::max_entries` field.
/// All the entries are zero-initialized when the map is created. /// All the entries are zero-initialized when the map is created.
///
/// # Example
/// ```no_run
/// # let bpf = aya::Bpf::load(&[], None)?;
/// use aya::maps::Array;
/// use std::convert::TryFrom;
///
/// let mut array = Array::try_from(bpf.map_mut("ARRAY")?)?;
/// array.set(1, 42, 0)?;
/// assert_eq!(array.get(&1, 0)?, 42);
/// # Ok::<(), aya::BpfError>(())
/// ```
pub struct Array<T: Deref<Target = Map>, V: Pod> { pub struct Array<T: Deref<Target = Map>, V: Pod> {
inner: T, inner: T,
_v: PhantomData<V>, _v: PhantomData<V>,
@ -98,18 +110,6 @@ impl<T: Deref<Target = Map> + DerefMut<Target = Map>, V: Pod> Array<T, V> {
/// ///
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`] /// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
/// if `bpf_map_update_elem` fails. /// if `bpf_map_update_elem` fails.
///
/// # Example
/// ```no_run
/// # let bpf = aya::Bpf::load(&[], None)?;
/// use aya::maps::Array;
/// use std::convert::TryFrom;
///
/// let mut array = Array::try_from(bpf.map_mut("ARRAY")?)?;
/// array.set(1, 42, 0)?;
/// assert_eq!(array.get(&1, 0)?, 42);
/// # Ok::<(), aya::BpfError>(())
/// ```
pub fn set(&mut self, index: u32, value: V, flags: u64) -> Result<(), MapError> { pub fn set(&mut self, index: u32, value: V, flags: u64) -> Result<(), MapError> {
let fd = self.inner.fd_or_err()?; let fd = self.inner.fd_or_err()?;
self.check_bounds(index)?; self.check_bounds(index)?;

@ -13,8 +13,6 @@ use crate::{
/// A hash map that can be shared between eBPF programs and user space. /// A hash map that can be shared between eBPF programs and user space.
/// ///
/// It is required that both keys and values implement the [`Pod`] trait.
///
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
@ -24,8 +22,10 @@ use crate::{
/// ///
/// let mut redirect_ports = HashMap::try_from(bpf.map_mut("REDIRECT_PORTS")?)?; /// let mut redirect_ports = HashMap::try_from(bpf.map_mut("REDIRECT_PORTS")?)?;
/// ///
/// redirect_ports.insert(80, 8080, 0 /* flags */); /// // redirect port 80 to 8080
/// redirect_ports.insert(443, 8443, 0 /* flags */); /// redirect_ports.insert(80, 8080, 0);
/// // redirect port 443 to 8443
/// redirect_ports.insert(443, 8443, 0);
/// # Ok::<(), aya::BpfError>(()) /// # Ok::<(), aya::BpfError>(())
/// ``` /// ```
#[doc(alias = "BPF_MAP_TYPE_HASH")] #[doc(alias = "BPF_MAP_TYPE_HASH")]

@ -1,34 +1,37 @@
//! Data structures used to exchange data with eBPF programs. //! Data structures used to setup and share data with eBPF programs.
//! //!
//! The eBPF platform provides data structures - maps in eBPF speak - that can be used by eBPF //! The eBPF platform provides data structures - maps in eBPF speak - that are
//! programs and user-space to exchange data. When you call //! 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 //! [`Bpf::load_file`](crate::Bpf::load_file) or
//! defined in the eBPF code get initialized and can then be accessed using //! [`Bpf::load`](crate::Bpf::load), all the maps defined in the eBPF code get
//! [`Bpf::map`](crate::Bpf::map) and [`Bpf::map_mut`](crate::Bpf::map_mut). //! initialized and can then be accessed using [`Bpf::map`](crate::Bpf::map) and
//! [`Bpf::map_mut`](crate::Bpf::map_mut).
//! //!
//! # Concrete map types //! # Typed maps
//! //!
//! The eBPF platform provides many map types each supporting different operations. //! The eBPF API includes many map types each supporting different operations.
//! [`Bpf::map`](crate::Bpf::map) and [`Bpf::map_mut`](crate::Bpf::map_mut) always return the //! [`Bpf::map`](crate::Bpf::map) and [`Bpf::map_mut`](crate::Bpf::map_mut) always return the
//! opaque [`MapRef`] and [`MapRefMut`] types respectively. Those two types can be converted to //! opaque [`MapRef`] and [`MapRefMut`] types respectively. Those two types can be converted to
//! *concrete map types* using the [`TryFrom`](std::convert::TryFrom) trait. For example: //! *typed maps* using the [`TryFrom`](std::convert::TryFrom) trait. For example:
//! //!
//! ```no_run //! ```no_run
//! # let bpf = aya::Bpf::load(&[], None)?; //! # let mut bpf = aya::Bpf::load(&[], None)?;
//! use aya::maps::HashMap; //! use std::convert::{TryFrom, TryInto};
//! use std::convert::TryFrom; //! use aya::maps::SockMap;
//! use aya::programs::SkMsg;
//! //!
//! const CONFIG_KEY_NUM_RETRIES: u8 = 1; //! let intercept_egress = SockMap::try_from(bpf.map_mut("INTERCEPT_EGRESS")?)?;
//! //! let prog: &mut SkMsg = bpf.program_mut("intercept_egress_packet")?.try_into()?;
//! // HashMap::try_from() converts MapRefMut to HashMap. It will fail if CONFIG is not an eBPF //! prog.load()?;
//! // hash map. //! prog.attach(&intercept_egress)?;
//! let mut hm = HashMap::try_from(bpf.map_mut("CONFIG")?)?;
//! hm.insert(CONFIG_KEY_NUM_RETRIES, 3, 0 /* flags */);
//! # Ok::<(), aya::BpfError>(()) //! # Ok::<(), aya::BpfError>(())
//! ``` //! ```
//! //!
//! The code above uses `HashMap`, but all the concrete map types implement the //! # Maps and `Pod` values
//! `TryFrom` trait. //!
//! 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::{ use std::{
convert::TryFrom, ffi::CString, io, marker::PhantomData, mem, ops::Deref, os::unix::io::RawFd, convert::TryFrom, ffi::CString, io, marker::PhantomData, mem, ops::Deref, os::unix::io::RawFd,
ptr, ptr,
@ -118,6 +121,8 @@ pub enum MapError {
} }
/// A generic handle to a BPF map. /// A generic handle to a BPF map.
///
/// You should never need to use this unless you're implementing a new map type.
#[derive(Debug)] #[derive(Debug)]
pub struct Map { pub struct Map {
pub(crate) obj: obj::Map, pub(crate) obj: obj::Map,

@ -28,12 +28,12 @@ use crate::{
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[], None)?;
/// use std::convert::{TryFrom, TryInto}; /// use std::convert::{TryFrom, TryInto};
/// use aya::maps::SockMap; /// use aya::maps::SockMap;
/// use aya::programs::SkMsg; /// use aya::programs::SkSkb;
/// ///
/// let intercept_egress = SockMap::try_from(bpf.map_mut("INTERCEPT_EGRESS")?)?; /// let intercept_ingress = SockMap::try_from(bpf.map_mut("INTERCEPT_INGRESS")?)?;
/// let prog: &mut SkMsg = bpf.program_mut("intercept_egress_packet")?.try_into()?; /// let prog: &mut SkSkb = bpf.program_mut("intercept_ingress_packet")?.try_into()?;
/// prog.load()?; /// prog.load()?;
/// prog.attach(&intercept_egress)?; /// prog.attach(&intercept_ingress)?;
/// # Ok::<(), aya::BpfError>(()) /// # Ok::<(), aya::BpfError>(())
/// ``` /// ```
pub struct SockMap<T: Deref<Target = Map>> { pub struct SockMap<T: Deref<Target = Map>> {

@ -18,6 +18,7 @@ use crate::{
pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32; pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32;
pub(crate) const MAX_SPEC_LEN: usize = 64; pub(crate) const MAX_SPEC_LEN: usize = 64;
/// The error type returned when `BTF` operations fail.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum BtfError { pub enum BtfError {
#[error("error parsing {path}")] #[error("error parsing {path}")]

@ -14,9 +14,36 @@ use super::FdLink;
/// A program used to inspect or filter network activity for a given cgroup. /// A program used to inspect or filter network activity for a given cgroup.
/// ///
/// [`CgroupSkb`] programs can be used to inspect or filter network activity /// [`CgroupSkb`] programs can be used to inspect or filter network activity
/// generated on all the sockets belonging to a given [cgroup]. /// generated on all the sockets belonging to a given [cgroup]. They can be
/// attached to both _ingress_ and _egress_.
/// ///
/// [cgroup]: https://man7.org/linux/man-pages/man7/cgroups.7.html /// [cgroup]: https://man7.org/linux/man-pages/man7/cgroups.7.html
///
/// # Example
///
/// ```no_run
/// # #[derive(thiserror::Error, Debug)]
/// # 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::fs::File;
/// use std::convert::TryInto;
/// use aya::programs::{CgroupSkb, CgroupSkbAttachType};
///
/// let file = File::open("/sys/fs/cgroup/unified")?;
/// let egress: &mut CgroupSkb = bpf.program_mut("egress_filter")?.try_into()?;
/// egress.load()?;
/// egress.attach(file, CgroupSkbAttachType::Egress)?;
/// # Ok::<(), Error>(())
/// ```
#[derive(Debug)] #[derive(Debug)]
pub struct CgroupSkb { pub struct CgroupSkb {
pub(crate) data: ProgramData, pub(crate) data: ProgramData,
@ -47,32 +74,6 @@ impl CgroupSkb {
} }
/// Attaches the program to the given cgroup. /// Attaches the program to the given cgroup.
///
/// # Example
///
/// ```no_run
/// # #[derive(thiserror::Error, Debug)]
/// # 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::fs::File;
/// use std::convert::TryInto;
/// use aya::programs::{CgroupSkb, CgroupSkbAttachType};
///
/// let file = File::open("/sys/fs/cgroup/unified")?;
/// let egress: &mut CgroupSkb = bpf.program_mut("egress_filter")?.try_into()?;
/// egress.load()?;
/// egress.attach(file, CgroupSkbAttachType::Egress)?;
/// # Ok::<(), Error>(())
/// ```
pub fn attach<T: AsRawFd>( pub fn attach<T: AsRawFd>(
&mut self, &mut self,
cgroup: T, cgroup: T,
@ -110,8 +111,11 @@ impl CgroupSkb {
} }
} }
/// Defines where to attach a [`CgroupSkb`] program.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum CgroupSkbAttachType { pub enum CgroupSkbAttachType {
/// Attach to ingress.
Ingress, Ingress,
/// Attach to egress.
Egress, Egress,
} }

@ -19,6 +19,18 @@ use crate::{
/// ///
/// - `kprobe`: get attached to the *start* of the target functions /// - `kprobe`: get attached to the *start* of the target functions
/// - `kretprobe`: get attached to the *return address* of the target functions /// - `kretprobe`: get attached to the *return address* of the target functions
///
/// # Example
///
/// ```no_run
/// # let mut bpf = Bpf::load_file("ebpf_programs.o")?;
/// use aya::{Bpf, programs::KProbe};
/// use std::convert::TryInto;
///
/// let program: &mut KProbe = bpf.program_mut("intercept_wakeups")?.try_into()?;
/// program.attach("try_to_wake_up", 0, None)?;
/// # Ok::<(), aya::BpfError>(())
/// ```
#[derive(Debug)] #[derive(Debug)]
pub struct KProbe { pub struct KProbe {
pub(crate) data: ProgramData, pub(crate) data: ProgramData,
@ -54,20 +66,6 @@ impl KProbe {
/// If the program is a `kprobe`, it is attached to the *start* address of the target function. /// If the program is a `kprobe`, it is attached to the *start* address of the target function.
/// Conversely if the program is a `kretprobe`, it is attached to the return address of the /// Conversely if the program is a `kretprobe`, it is attached to the return address of the
/// target function. /// target function.
///
///
/// # Example
///
/// ```no_run
/// # let mut bpf = Bpf::load_file("ebpf_programs.o")?;
/// use aya::{Bpf, programs::KProbe};
/// use std::convert::TryInto;
///
/// let program: &mut KProbe = bpf.program_mut("intercept_wakeups")?.try_into()?;
/// program.attach("try_to_wake_up", 0, None)?;
/// # Ok::<(), aya::BpfError>(())
/// ```
///
pub fn attach( pub fn attach(
&mut self, &mut self,
fn_name: &str, fn_name: &str,
@ -78,6 +76,7 @@ impl KProbe {
} }
} }
/// The type returned when attaching a [`KProbe`] fails.
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum KProbeError { pub enum KProbeError {
#[error("`{filename}`")] #[error("`{filename}`")]

@ -1,14 +1,17 @@
//! eBPF program types. //! eBPF program types.
//! //!
//! eBPF programs are loaded inside the kernel and attached to one or more hook points. Whenever //! eBPF programs are loaded inside the kernel and attached to one or more hook
//! the kernel or an application reaches those hook points, the programs are executed. //! points. Whenever the hook points are reached, the programs are executed.
//! //!
//! # Loading programs //! # Loading and attaching programs
//! //!
//! When you call [`Bpf::load_file`] or [`Bpf::load`], all the programs present in the code are //! When you call [`Bpf::load_file`] or [`Bpf::load`], all the programs included
//! parsed and can be retrieved using the [`Bpf::program`] and [`Bpf::program_mut`] methods. In //! in the object code are parsed and relocated. Programs are not loaded
//! order to load a program, you need to get a handle to it and call the `load()` method, for //! automatically though, since often you will need to do some application
//! example: //! specific setup before you can actually load them.
//!
//! In order to load and attach a program, you need to retrieve it using [`Bpf::program_mut`],
//! then call the `load()` and `attach()` methods, for example:
//! //!
//! ```no_run //! ```no_run
//! use aya::{Bpf, programs::KProbe}; //! use aya::{Bpf, programs::KProbe};
@ -18,32 +21,21 @@
//! // intercept_wakeups is the name of the program we want to load //! // intercept_wakeups is the name of the program we want to load
//! let program: &mut KProbe = bpf.program_mut("intercept_wakeups")?.try_into()?; //! let program: &mut KProbe = bpf.program_mut("intercept_wakeups")?.try_into()?;
//! program.load()?; //! program.load()?;
//! // intercept_wakeups will be called every time try_to_wake_up() is called
//! // inside the kernel
//! program.attach("try_to_wake_up", 0, None)?;
//! # Ok::<(), aya::BpfError>(()) //! # Ok::<(), aya::BpfError>(())
//! ``` //! ```
//! //!
//! # Attaching programs //! The signature of the `attach()` method varies depending on what kind of
//! //! program you're trying to attach.
//! After being loaded, programs must be attached to their target hook points to be executed. The
//! eBPF platform supports many different program types, with each type providing different
//! attachment options. For example when attaching a [`KProbe`], you must provide the name of the
//! kernel function you want instrument; when loading an [`Xdp`] program, you need to specify the
//! network card name you want to hook into, and so forth.
//!
//! Currently aya supports [`KProbe`], [`UProbe`], [`SocketFilter`], [`TracePoint`] and [`Xdp`]
//! programs. To see how to attach them, see the documentation of the respective `attach()` method.
//!
//! # Interacting with programs
//!
//! eBPF programs are event-driven and execute when the hook points they are attached to are hit.
//! To communicate with user-space, programs use data structures provided by the eBPF platform,
//! which can be found in the [maps] module.
//! //!
//! [`Bpf::load_file`]: crate::Bpf::load_file //! [`Bpf::load_file`]: crate::Bpf::load_file
//! [`Bpf::load`]: crate::Bpf::load //! [`Bpf::load`]: crate::Bpf::load
//! [`Bpf::programs`]: crate::Bpf::programs //! [`Bpf::programs`]: crate::Bpf::programs
//! [`Bpf::program`]: crate::Bpf::program //! [`Bpf::program`]: crate::Bpf::program
//! [`Bpf::program_mut`]: crate::Bpf::program_mut //! [`Bpf::program_mut`]: crate::Bpf::program_mut
//! [maps]: crate::maps //! [`maps`]: crate::maps
mod cgroup_skb; mod cgroup_skb;
mod kprobe; mod kprobe;
mod perf_attach; mod perf_attach;
@ -80,61 +72,83 @@ use crate::{
obj::{self, Function}, obj::{self, Function},
sys::{bpf_load_program, bpf_prog_detach}, sys::{bpf_load_program, bpf_prog_detach},
}; };
/// Error type returned when working with programs.
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum ProgramError { pub enum ProgramError {
/// The program could not be found in the object code.
#[error("program `{name}` not found")] #[error("program `{name}` not found")]
NotFound { name: String }, NotFound { name: String },
/// The program is already loaded.
#[error("the program is already loaded")] #[error("the program is already loaded")]
AlreadyLoaded, AlreadyLoaded,
/// The program is not loaded.
#[error("the program is not loaded")] #[error("the program is not loaded")]
NotLoaded, NotLoaded,
/// The program is already detached.
#[error("the program was already detached")] #[error("the program was already detached")]
AlreadyDetached, AlreadyDetached,
/// The program is not attached.
#[error("the program is not attached")] #[error("the program is not attached")]
NotAttached, NotAttached,
/// Loading the program failed.
#[error("the BPF_PROG_LOAD syscall failed. Verifier output: {verifier_log}")] #[error("the BPF_PROG_LOAD syscall failed. Verifier output: {verifier_log}")]
LoadError { LoadError {
/// The [`io::Error`] returned by the `BPF_PROG_LOAD` syscall.
#[source] #[source]
io_error: io::Error, io_error: io::Error,
/// The error log produced by the kernel verifier.
verifier_log: String, verifier_log: String,
}, },
/// A syscall failed.
#[error("`{call}` failed")] #[error("`{call}` failed")]
SyscallError { SyscallError {
/// The name of the syscall which failed.
call: String, call: String,
/// The [`io::Error`] returned by the syscall.
#[source] #[source]
io_error: io::Error, io_error: io::Error,
}, },
/// The network interface does not exist.
#[error("unknown network interface {name}")] #[error("unknown network interface {name}")]
UnknownInterface { name: String }, UnknownInterface { name: String },
/// The program is not of the expected type.
#[error("unexpected program type")] #[error("unexpected program type")]
UnexpectedProgramType, UnexpectedProgramType,
/// A map error occurred while loading or attaching a program.
#[error(transparent)] #[error(transparent)]
MapError(#[from] MapError), MapError(#[from] MapError),
/// An error occurred while working with a [`KProbe`].
#[error(transparent)] #[error(transparent)]
KProbeError(#[from] KProbeError), KProbeError(#[from] KProbeError),
/// An error occurred while working with an [`UProbe`].
#[error(transparent)] #[error(transparent)]
UProbeError(#[from] UProbeError), UProbeError(#[from] UProbeError),
/// An error occurred while working with a [`TracePoint`].
#[error(transparent)] #[error(transparent)]
TracePointError(#[from] TracePointError), TracePointError(#[from] TracePointError),
/// An error occurred while working with a [`SocketFilter`].
#[error(transparent)] #[error(transparent)]
SocketFilterError(#[from] SocketFilterError), SocketFilterError(#[from] SocketFilterError),
/// An error occurred while working with an [`Xdp`] program.
#[error(transparent)] #[error(transparent)]
XdpError(#[from] XdpError), XdpError(#[from] XdpError),
/// An error occurred while working with a TC program.
#[error(transparent)] #[error(transparent)]
TcError(#[from] TcError), TcError(#[from] TcError),
} }
@ -353,10 +367,15 @@ fn load_program(prog_type: bpf_prog_type, data: &mut ProgramData) -> Result<(),
Ok(()) Ok(())
} }
/// Detach an attached program.
pub trait Link: std::fmt::Debug { pub trait Link: std::fmt::Debug {
fn detach(&mut self) -> Result<(), ProgramError>; fn detach(&mut self) -> Result<(), ProgramError>;
} }
/// The return type of `program.attach(...)`.
///
/// [`LinkRef`] implements the [`Link`] trait and can be used to detach a
/// program.
#[derive(Debug)] #[derive(Debug)]
pub struct LinkRef { pub struct LinkRef {
inner: Rc<RefCell<dyn Link>>, inner: Rc<RefCell<dyn Link>>,

@ -10,9 +10,13 @@ use crate::{
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ProbeKind { pub enum ProbeKind {
/// Kernel probe
KProbe, KProbe,
/// Kernel return probe
KRetProbe, KRetProbe,
/// User space probe
UProbe, UProbe,
/// User space return probe
URetProbe, URetProbe,
} }

@ -5,14 +5,50 @@ use crate::{
sys::bpf_prog_attach, sys::bpf_prog_attach,
}; };
/// A socket buffer program. /// A program used to intercept messages sent with `sendmsg()`/`sendfile()`.
/// ///
/// Socket buffer programs are attached to [sockmaps], and can be used to /// [`SkMsg`] programs are attached to [socket maps], and can be used inspect,
/// redirect or drop packets. See the [`SockMap` documentation] for more info /// filter and redirect messages sent on sockets. See also [`SockMap`] and
/// and examples. /// [`SockHash`].
/// ///
/// [sockmaps]: crate::maps::SockMap /// # Example
/// [`SockMap` documentation]: crate::maps::SockMap ///
/// ```no_run
/// ##[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 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)?;
///
/// let mut client = TcpStream::connect("127.0.0.1:1234")?;
/// intercept_egress.insert(1234, client.as_raw_fd(), 0)?;
///
/// // the write will be intercepted
/// client.write_all(b"foo")?;
/// # Ok::<(), Error>(())
/// ```
///
/// [socket maps]: crate::maps::sock
/// [`SockMap`]: crate::maps::SockMap
/// [`SockHash`]: crate::maps::SockHash
#[derive(Debug)] #[derive(Debug)]
pub struct SkMsg { pub struct SkMsg {
pub(crate) data: ProgramData, pub(crate) data: ProgramData,

@ -8,21 +8,37 @@ use crate::{
sys::bpf_prog_attach, sys::bpf_prog_attach,
}; };
/// The kind of [`SkSkb`] program.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum SkSkbKind { pub enum SkSkbKind {
StreamParser, StreamParser,
StreamVerdict, StreamVerdict,
} }
/// A socket buffer program. /// A program used to intercept ingress socket buffers.
/// ///
/// Socket buffer programs are attached to [socket maps], and can be used to /// [`SkSkb`] programs are attached to [socket maps], and can be used to
/// inspect, redirect or filter packet. See [SockMap] and [SockHash] for more /// inspect, redirect or filter incoming packet. See also [`SockMap`] and
/// info and examples. /// [`SockHash`].
///
/// # Example
///
/// ```no_run
/// # let mut bpf = aya::Bpf::load(&[], None)?;
/// use std::convert::{TryFrom, TryInto};
/// use aya::maps::SockMap;
/// use aya::programs::SkSkb;
///
/// let intercept_ingress = SockMap::try_from(bpf.map_mut("INTERCEPT_INGRESS")?)?;
/// let prog: &mut SkSkb = bpf.program_mut("intercept_ingress_packet")?.try_into()?;
/// prog.load()?;
/// prog.attach(&intercept_ingress)?;
/// # Ok::<(), aya::BpfError>(())
/// ```
/// ///
/// [socket maps]: crate::maps::sock /// [socket maps]: crate::maps::sock
/// [SockMap]: crate::maps::SockMap /// [`SockMap`]: crate::maps::SockMap
/// [SockHash]: crate::maps::SockHash /// [`SockHash`]: crate::maps::SockHash
#[derive(Debug)] #[derive(Debug)]
pub struct SkSkb { pub struct SkSkb {
pub(crate) data: ProgramData, pub(crate) data: ProgramData,

@ -6,6 +6,36 @@ use crate::{
sys::bpf_prog_attach, sys::bpf_prog_attach,
}; };
/// A program used to work with sockets.
///
/// [`SockOps`] programs can access or set socket options, connection
/// parameters, watch connection state changes and more. They are attached to
/// cgroups.
///
/// # Example
///
/// ```no_run
/// # #[derive(thiserror::Error, Debug)]
/// # 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::fs::File;
/// use std::convert::TryInto;
/// use aya::programs::SockOps;
///
/// let file = File::open("/sys/fs/cgroup/unified")?;
/// let prog: &mut SockOps = bpf.program_mut("intercept_active_sockets")?.try_into()?;
/// prog.load()?;
/// prog.attach(file)?;
/// # Ok::<(), Error>(())
#[derive(Debug)] #[derive(Debug)]
pub struct SockOps { pub struct SockOps {
pub(crate) data: ProgramData, pub(crate) data: ProgramData,
@ -24,7 +54,7 @@ impl SockOps {
self.data.name.to_string() self.data.name.to_string()
} }
/// Attaches the program to the given sockmap. /// Attaches the program to the given cgroup.
pub fn attach<T: AsRawFd>(&mut self, cgroup: T) -> Result<LinkRef, ProgramError> { pub fn attach<T: AsRawFd>(&mut self, cgroup: T) -> Result<LinkRef, ProgramError> {
let prog_fd = self.data.fd_or_err()?; let prog_fd = self.data.fd_or_err()?;
let cgroup_fd = cgroup.as_raw_fd(); let cgroup_fd = cgroup.as_raw_fd();

@ -1,5 +1,8 @@
use libc::{setsockopt, SOL_SOCKET}; use libc::{setsockopt, SOL_SOCKET};
use std::{io, mem, os::unix::prelude::RawFd}; use std::{
io, mem,
os::unix::prelude::{AsRawFd, RawFd},
};
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
@ -7,8 +10,10 @@ use crate::{
programs::{load_program, Link, LinkRef, ProgramData, ProgramError}, programs::{load_program, Link, LinkRef, ProgramData, ProgramError},
}; };
/// The type returned when attaching a [`SocketFilter`] fails.
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum SocketFilterError { pub enum SocketFilterError {
/// Setting the `SO_ATTACH_BPF` socket option failed.
#[error("setsockopt SO_ATTACH_BPF failed")] #[error("setsockopt SO_ATTACH_BPF failed")]
SoAttachBpfError { SoAttachBpfError {
#[source] #[source]
@ -16,18 +21,56 @@ pub enum SocketFilterError {
}, },
} }
/// A program used to inspect and filter incoming packets on a socket.
///
/// [`SocketFilter`] programs are attached on sockets and can be used to inspect
/// and filter incoming packets.
///
///
/// # Example
///
/// ```no_run
/// ##[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::programs::SocketFilter;
///
/// let mut client = TcpStream::connect("127.0.0.1:1234")?;
/// let prog: &mut SocketFilter = bpf.program_mut("filter_packets")?.try_into()?;
/// prog.load()?;
/// prog.attach(client.as_raw_fd())?;
/// # Ok::<(), Error>(())
/// ```
#[derive(Debug)] #[derive(Debug)]
pub struct SocketFilter { pub struct SocketFilter {
pub(crate) data: ProgramData, pub(crate) data: ProgramData,
} }
impl SocketFilter { impl SocketFilter {
/// Loads the program inside the kernel.
///
/// See also [`Program::load`](crate::programs::Program::load).
pub fn load(&mut self) -> Result<(), ProgramError> { pub fn load(&mut self) -> Result<(), ProgramError> {
load_program(BPF_PROG_TYPE_SOCKET_FILTER, &mut self.data) load_program(BPF_PROG_TYPE_SOCKET_FILTER, &mut self.data)
} }
pub fn attach(&mut self, socket: RawFd) -> Result<LinkRef, ProgramError> { /// Attaches the filter on the given socket.
pub fn attach<T: AsRawFd>(&mut self, socket: T) -> Result<LinkRef, ProgramError> {
let prog_fd = self.data.fd_or_err()?; let prog_fd = self.data.fd_or_err()?;
let socket = socket.as_raw_fd();
let ret = unsafe { let ret = unsafe {
setsockopt( setsockopt(

@ -5,6 +5,7 @@ use crate::{generated::bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT, sys::perf_event_
use super::{load_program, perf_attach, LinkRef, ProgramData, ProgramError}; use super::{load_program, perf_attach, LinkRef, ProgramData, ProgramError};
/// The type returned when attaching a [`TracePoint`] fails.
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum TracePointError { pub enum TracePointError {
#[error("`{filename}`")] #[error("`{filename}`")]
@ -15,16 +16,52 @@ pub enum TracePointError {
}, },
} }
/// A program that can be attached at a pre-defined kernel trace point.
///
/// The kernel provides a set of pre-defined trace points that eBPF programs can
/// be attached to. See `/sys/kernel/debug/tracing/events` for a list of which
/// events can be traced.
///
/// # Example
///
/// ```no_run
/// ##[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 aya::programs::TracePoint;
///
/// let prog: &mut TracePoint = bpf.program_mut("trace_context_switch")?.try_into()?;
/// prog.load()?;
/// prog.attach("sched", "sched_switch")?;
/// # Ok::<(), Error>(())
/// ```
#[derive(Debug)] #[derive(Debug)]
pub struct TracePoint { pub struct TracePoint {
pub(crate) data: ProgramData, pub(crate) data: ProgramData,
} }
impl TracePoint { impl TracePoint {
/// Loads the program inside the kernel.
///
/// See also [`Program::load`](crate::programs::Program::load).
pub fn load(&mut self) -> Result<(), ProgramError> { pub fn load(&mut self) -> Result<(), ProgramError> {
load_program(BPF_PROG_TYPE_TRACEPOINT, &mut self.data) load_program(BPF_PROG_TYPE_TRACEPOINT, &mut self.data)
} }
/// Attaches to a given trace point.
///
/// For a list of the available event categories and names, see
/// `/sys/kernel/debug/tracing/events`.
pub fn attach(&mut self, category: &str, name: &str) -> Result<LinkRef, ProgramError> { pub fn attach(&mut self, category: &str, name: &str) -> Result<LinkRef, ProgramError> {
let id = read_sys_fs_trace_point_id(category, name)?; let id = read_sys_fs_trace_point_id(category, name)?;
let fd = perf_event_open_trace_point(id).map_err(|(_code, io_error)| { let fd = perf_event_open_trace_point(id).map_err(|(_code, io_error)| {

@ -127,17 +127,21 @@ impl UProbe {
} }
} }
/// The type returned when attaching an [`UProbe`] fails.
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum UProbeError { pub enum UProbeError {
/// There was an error parsing `/etc/ld.so.cache`.
#[error("error reading `{}` file", LD_SO_CACHE_FILE)] #[error("error reading `{}` file", LD_SO_CACHE_FILE)]
InvalidLdSoCache { InvalidLdSoCache {
#[source] #[source]
io_error: Arc<io::Error>, io_error: Arc<io::Error>,
}, },
/// The target program could not be found.
#[error("could not resolve uprobe target `{path}`")] #[error("could not resolve uprobe target `{path}`")]
InvalidTarget { path: PathBuf }, InvalidTarget { path: PathBuf },
/// There was an error resolving the target symbol.
#[error("error resolving symbol")] #[error("error resolving symbol")]
SymbolError { SymbolError {
symbol: String, symbol: String,
@ -145,6 +149,7 @@ pub enum UProbeError {
error: Box<dyn Error + Send + Sync>, error: Box<dyn Error + Send + Sync>,
}, },
/// There was an error accessing `filename`.
#[error("`{filename}`")] #[error("`{filename}`")]
FileError { FileError {
filename: String, filename: String,

@ -12,6 +12,7 @@ use crate::{
sys::{bpf_link_create, kernel_version, netlink_set_xdp_fd}, sys::{bpf_link_create, kernel_version, netlink_set_xdp_fd},
}; };
/// The type returned when attaching an [`Xdp`] program fails on kernels `< 5.9`.
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum XdpError { pub enum XdpError {
#[error("netlink error while attaching XDP program")] #[error("netlink error while attaching XDP program")]
@ -22,12 +23,18 @@ pub enum XdpError {
} }
bitflags! { bitflags! {
/// Flags passed to [`Xdp::attach()`].
#[derive(Default)] #[derive(Default)]
pub struct XdpFlags: u32 { pub struct XdpFlags: u32 {
/// Skb mode.
const SKB_MODE = XDP_FLAGS_SKB_MODE; const SKB_MODE = XDP_FLAGS_SKB_MODE;
/// Driver mode.
const DRV_MODE = XDP_FLAGS_DRV_MODE; const DRV_MODE = XDP_FLAGS_DRV_MODE;
/// Hardware mode.
const HW_MODE = XDP_FLAGS_HW_MODE; const HW_MODE = XDP_FLAGS_HW_MODE;
/// Replace a previously attached XDP program.
const REPLACE = XDP_FLAGS_REPLACE; const REPLACE = XDP_FLAGS_REPLACE;
/// Only attach if there isn't another XDP program already attached.
const UPDATE_IF_NOEXIST = XDP_FLAGS_UPDATE_IF_NOEXIST; const UPDATE_IF_NOEXIST = XDP_FLAGS_UPDATE_IF_NOEXIST;
} }
} }
@ -38,6 +45,18 @@ bitflags! {
/// processing, where they can apply custom packet processing logic. When supported by the /// processing, where they can apply custom packet processing logic. When supported by the
/// underlying network driver, XDP programs can execute directly on network cards, greatly /// underlying network driver, XDP programs can execute directly on network cards, greatly
/// reducing CPU load. /// reducing CPU load.
///
/// # Example
///
/// ```no_run
/// # let mut bpf = Bpf::load_file("ebpf_programs.o")?;
/// use aya::{Bpf, programs::{Xdp, XdpFlags}};
/// use std::convert::TryInto;
///
/// let program: &mut Xdp = bpf.program_mut("intercept_packets")?.try_into()?;
/// program.attach("eth0", XdpFlags::default())?;
/// # Ok::<(), aya::BpfError>(())
/// ```
#[derive(Debug)] #[derive(Debug)]
pub struct Xdp { pub struct Xdp {
pub(crate) data: ProgramData, pub(crate) data: ProgramData,
@ -56,8 +75,6 @@ impl Xdp {
self.data.name.to_string() self.data.name.to_string()
} }
/// Attaches the program.
///
/// Attaches the program to the given `interface`. /// Attaches the program to the given `interface`.
/// ///
/// # Errors /// # Errors
@ -69,18 +86,6 @@ impl Xdp {
/// kernels `>= 5.9.0`, and instead /// kernels `>= 5.9.0`, and instead
/// [`XdpError::NetlinkError`] is returned for older /// [`XdpError::NetlinkError`] is returned for older
/// kernels. /// kernels.
///
/// # Example
///
/// ```no_run
/// # let mut bpf = Bpf::load_file("ebpf_programs.o")?;
/// use aya::{Bpf, programs::{Xdp, XdpFlags}};
/// use std::convert::TryInto;
///
/// let program: &mut Xdp = bpf.program_mut("intercept_packets")?.try_into()?;
/// program.attach("eth0", XdpFlags::default())?;
/// # Ok::<(), aya::BpfError>(())
/// ```
pub fn attach(&mut self, interface: &str, flags: XdpFlags) -> Result<LinkRef, ProgramError> { pub fn attach(&mut self, interface: &str, flags: XdpFlags) -> Result<LinkRef, ProgramError> {
let prog_fd = self.data.fd_or_err()?; let prog_fd = self.data.fd_or_err()?;

@ -1,16 +1,14 @@
//! Utility functions. //! Utility functions.
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
fs::{self, File},
ffi::CString, ffi::CString,
os::raw::c_char, fs::{self, File},
io::{self, BufReader}, io::{self, BufReader},
os::raw::c_char,
str::FromStr, str::FromStr,
}; };
use crate::{ use crate::generated::{TC_H_MAJ_MASK, TC_H_MIN_MASK};
generated::{TC_H_MAJ_MASK, TC_H_MIN_MASK},
};
use libc::if_nametoindex; use libc::if_nametoindex;
@ -19,7 +17,7 @@ use io::BufRead;
const ONLINE_CPUS: &str = "/sys/devices/system/cpu/online"; const ONLINE_CPUS: &str = "/sys/devices/system/cpu/online";
pub(crate) const POSSIBLE_CPUS: &str = "/sys/devices/system/cpu/possible"; pub(crate) const POSSIBLE_CPUS: &str = "/sys/devices/system/cpu/possible";
/// Returns the numeric IDs of the available CPUs. /// Returns the numeric IDs of the CPUs currently online.
pub fn online_cpus() -> Result<Vec<u32>, io::Error> { pub fn online_cpus() -> Result<Vec<u32>, io::Error> {
let data = fs::read_to_string(ONLINE_CPUS)?; let data = fs::read_to_string(ONLINE_CPUS)?;
parse_cpu_ranges(data.trim()).map_err(|_| { parse_cpu_ranges(data.trim()).map_err(|_| {
@ -30,6 +28,9 @@ pub fn online_cpus() -> Result<Vec<u32>, io::Error> {
}) })
} }
/// Get the number of possible cpus.
///
/// See `/sys/devices/system/cpu/possible`.
pub fn nr_cpus() -> Result<usize, io::Error> { pub fn nr_cpus() -> Result<usize, io::Error> {
Ok(possible_cpus()?.len()) Ok(possible_cpus()?.len())
} }
@ -38,6 +39,9 @@ pub(crate) fn tc_handler_make(major: u32, minor: u32) -> u32 {
(major & TC_H_MAJ_MASK) | (minor & TC_H_MIN_MASK) (major & TC_H_MAJ_MASK) | (minor & TC_H_MIN_MASK)
} }
/// Get the list of possible cpus.
///
/// See `/sys/devices/system/cpu/possible`.
pub(crate) fn possible_cpus() -> Result<Vec<u32>, io::Error> { pub(crate) fn possible_cpus() -> Result<Vec<u32>, io::Error> {
let data = fs::read_to_string(POSSIBLE_CPUS)?; let data = fs::read_to_string(POSSIBLE_CPUS)?;
parse_cpu_ranges(data.trim()).map_err(|_| { parse_cpu_ranges(data.trim()).map_err(|_| {
@ -81,7 +85,6 @@ pub unsafe fn ifindex_from_ifname(if_name: &str) -> Result<u32, io::Error> {
Ok(if_index) Ok(if_index)
} }
/// htons and ntohs util functions /// htons and ntohs util functions
pub fn htons(u: u16) -> u16 { pub fn htons(u: u16) -> u16 {
u.to_be() u.to_be()

Loading…
Cancel
Save