From 9426f36f79101e296ec3ffc4bbef8913a1130eff Mon Sep 17 00:00:00 2001 From: Dave Tucker Date: Fri, 24 Sep 2021 17:30:48 +0100 Subject: [PATCH] Implement Pinning For Programs and Maps This commit adds 2 new methods to aya::sys - bpf_pin_object - bpf_get_object Which allow the pinning and retrieval of programs/maps to bpffs. It adds a `Program.pin` API, such that a loaded program can be pinned. For map pinning, the user must ensure the `pinning u32` in the `bpf_map_def` is set to 1, maps will be pinned using a new builder API. BpfLoader::new().map_pin_path("/sys/fs/bpf/myapp").load_file("myapp.o") This will pin all maps whose definition requests pinning to path + name. Signed-off-by: Dave Tucker --- aya/src/bpf.rs | 188 ++++++++++++++++---- aya/src/maps/array/array.rs | 2 +- aya/src/maps/array/per_cpu_array.rs | 2 +- aya/src/maps/array/program_array.rs | 2 +- aya/src/maps/hash_map/hash_map.rs | 22 ++- aya/src/maps/hash_map/per_cpu_hash_map.rs | 4 +- aya/src/maps/mod.rs | 70 +++++++- aya/src/maps/perf/async_perf_event_array.rs | 2 +- aya/src/maps/perf/perf_event_array.rs | 2 +- aya/src/maps/queue.rs | 2 +- aya/src/maps/sock/sock_hash.rs | 2 +- aya/src/maps/sock/sock_map.rs | 2 +- aya/src/maps/stack.rs | 2 +- aya/src/maps/stack_trace.rs | 2 +- aya/src/obj/mod.rs | 13 +- aya/src/programs/cgroup_skb.rs | 2 +- aya/src/programs/lirc_mode2.rs | 2 +- aya/src/programs/mod.rs | 30 +++- aya/src/programs/perf_event.rs | 2 +- aya/src/programs/sk_msg.rs | 2 +- aya/src/programs/sk_skb.rs | 2 +- aya/src/programs/sock_ops.rs | 2 +- aya/src/programs/socket_filter.rs | 2 +- aya/src/programs/tc.rs | 2 +- aya/src/programs/trace_point.rs | 2 +- aya/src/sys/bpf.rs | 15 ++ 26 files changed, 311 insertions(+), 69 deletions(-) diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 5ed1beba..7a107ac8 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -2,7 +2,7 @@ use std::{ collections::HashMap, error::Error, fs, io, - os::raw::c_int, + os::{raw::c_int, unix::io::RawFd}, path::{Path, PathBuf}, }; @@ -58,64 +58,92 @@ pub(crate) struct bpf_map_def { pub(crate) map_flags: u32, // optional features pub(crate) id: u32, - pub(crate) pinning: u32, + pub(crate) pinning: PinningType, } -/// The main entry point into the library, used to work with eBPF programs and maps. -#[derive(Debug)] -pub struct Bpf { - maps: HashMap, - programs: HashMap, +#[repr(u32)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub(crate) enum PinningType { + None = 0, + #[allow(dead_code)] // ByName is constructed from the BPF side + ByName = 1, } -impl Bpf { +impl Default for PinningType { + fn default() -> Self { + PinningType::None + } +} + +#[derive(Default, Debug)] +pub struct BpfLoader { + btf: Option, + map_pin_path: Option, +} + +impl BpfLoader { + pub fn new() -> BpfLoader { + BpfLoader { + btf: None, + map_pin_path: None, + } + } + + // Set the target BTF + pub fn btf(&mut self, btf: Btf) -> &mut BpfLoader { + self.btf = Some(btf); + self + } + + // Set the map pin path + pub fn map_pin_path>(&mut self, path: P) -> &mut BpfLoader { + self.map_pin_path = Some(path.as_ref().to_owned()); + self + } + /// Loads eBPF bytecode from a file. /// - /// Parses the given object code file and initializes the [maps](crate::maps) defined in it. If - /// the kernel supports [BTF](Btf) debug info, it is automatically loaded from - /// `/sys/kernel/btf/vmlinux`. + /// Parses the given object code file and initializes the [maps](crate::maps) defined in it. /// /// # Examples /// /// ```no_run - /// use aya::Bpf; + /// use aya::BpfLoader; /// - /// let bpf = Bpf::load_file("file.o")?; + /// let bpf = BpfLoader::new().load_file("file.o")?; /// # Ok::<(), aya::BpfError>(()) /// ``` - pub fn load_file>(path: P) -> Result { + pub fn load_file>(&mut self, path: P) -> Result { let path = path.as_ref(); - Bpf::load( - &fs::read(path).map_err(|error| BpfError::FileError { - path: path.to_owned(), - error, - })?, - Btf::from_sys_fs().ok().as_ref(), - ) + self.load(&fs::read(path).map_err(|error| BpfError::FileError { + path: path.to_owned(), + error, + })?) } /// Load eBPF bytecode. /// /// Parses the object code contained in `data` and initializes the [maps](crate::maps) defined - /// in it. If `target_btf` is not `None` and `data` includes BTF debug info, [BTF](Btf) relocations - /// are applied as well. In order to allow sharing of a single [BTF](Btf) object among multiple - /// eBPF programs, `target_btf` is passed by reference. + /// in it. If `BpfLoader.btf` is not `None` and `data` includes BTF debug info, [BTF](Btf) relocations + /// are applied as well. Any maps that require pinning will be pinned to `BpfLoader.map_pin_path` /// /// # Examples /// /// ```no_run - /// use aya::{Bpf, Btf}; + /// use aya::{BpfLoader, Btf}; /// use std::fs; /// /// let data = fs::read("file.o").unwrap(); /// // load the BTF data from /sys/kernel/btf/vmlinux - /// let bpf = Bpf::load(&data, Btf::from_sys_fs().ok().as_ref()); + + /// let target_btf = Btf::from_sys_fs().unwrap(); + /// let bpf = BpfLoader::new().btf(target_btf).load(&data); /// # Ok::<(), aya::BpfError>(()) /// ``` - pub fn load(data: &[u8], target_btf: Option<&Btf>) -> Result { + pub fn load(&mut self, data: &[u8]) -> Result { let mut obj = Object::parse(data)?; - if let Some(btf) = target_btf { + if let Some(btf) = &self.btf { obj.relocate_btf(btf)?; } @@ -130,8 +158,32 @@ impl Bpf { })? .len() as u32; } - let mut map = Map { obj, fd: None }; - let fd = map.create()?; + let mut map = Map { + obj, + fd: None, + pinned: false, + }; + let fd = match map.obj.def.pinning { + PinningType::ByName => { + let path = match &self.map_pin_path { + Some(p) => p, + None => return Err(BpfError::NoPinPath), + }; + // try to open map in case it's already pinned + match map.from_pinned(path) { + Ok(fd) => { + map.pinned = true; + fd as RawFd + } + Err(_) => { + let fd = map.create()?; + map.pin(path)?; + fd + } + } + } + PinningType::None => map.create()?, + }; if !map.obj.data.is_empty() && map.obj.name != ".bss" { bpf_map_update_elem_ptr(fd, &0 as *const _, map.obj.data.as_mut_ptr(), 0).map_err( |(code, io_error)| MapError::SyscallError { @@ -208,7 +260,6 @@ impl Bpf { (name, program) }) .collect(); - Ok(Bpf { maps: maps .drain(..) @@ -217,6 +268,64 @@ impl Bpf { programs, }) } +} + +/// The main entry point into the library, used to work with eBPF programs and maps. +#[derive(Debug)] +pub struct Bpf { + maps: HashMap, + programs: HashMap, +} + +impl Bpf { + /// Loads eBPF bytecode from a file. + /// + /// Parses the given object code file and initializes the [maps](crate::maps) defined in it. If + /// the kernel supports [BTF](Btf) debug info, it is automatically loaded from + /// `/sys/kernel/btf/vmlinux`. + /// + /// # Examples + /// + /// ```no_run + /// use aya::Bpf; + /// + /// let bpf = Bpf::load_file("file.o")?; + /// # Ok::<(), aya::BpfError>(()) + /// ``` + pub fn load_file>(path: P) -> Result { + let mut loader = BpfLoader::new(); + let path = path.as_ref(); + if let Ok(btf) = Btf::from_sys_fs() { + loader.btf(btf); + }; + loader.load_file(path) + } + + /// Load eBPF bytecode. + /// + /// Parses the object code contained in `data` and initializes the [maps](crate::maps) defined + /// in it. If `target_btf` is not `None` and `data` includes BTF debug info, [BTF](Btf) relocations + /// are applied as well. In order to allow sharing of a single [BTF](Btf) object among multiple + /// eBPF programs, `target_btf` is passed by reference. + /// + /// # Examples + /// + /// ```no_run + /// use aya::{Bpf, Btf}; + /// use std::fs; + /// + /// let data = fs::read("file.o").unwrap(); + /// // load the BTF data from /sys/kernel/btf/vmlinux + /// let bpf = Bpf::load(&data); + /// # Ok::<(), aya::BpfError>(()) + /// ``` + pub fn load(data: &[u8]) -> Result { + let mut loader = BpfLoader::new(); + if let Ok(btf) = Btf::from_sys_fs() { + loader.btf(btf); + }; + loader.load(data) + } /// Returns a reference to the map with the given name. /// @@ -272,7 +381,7 @@ impl Bpf { /// /// # Examples /// ```no_run - /// # let mut bpf = aya::Bpf::load(&[], None)?; + /// # let mut bpf = aya::Bpf::load(&[])?; /// for (name, map) in bpf.maps() { /// println!( /// "found map `{}` of type `{:?}`", @@ -308,7 +417,7 @@ impl Bpf { /// # Examples /// /// ```no_run - /// # let bpf = aya::Bpf::load(&[], None)?; + /// # let bpf = aya::Bpf::load(&[])?; /// let program = bpf.program("SSL_read")?; /// println!("program SSL_read is of type {:?}", program.prog_type()); /// # Ok::<(), aya::BpfError>(()) @@ -333,7 +442,7 @@ impl Bpf { /// # Examples /// /// ```no_run - /// # let mut bpf = aya::Bpf::load(&[], None)?; + /// # let mut bpf = aya::Bpf::load(&[])?; /// use aya::programs::UProbe; /// use std::convert::TryInto; /// @@ -354,7 +463,7 @@ impl Bpf { /// /// # Examples /// ```no_run - /// # let mut bpf = aya::Bpf::load(&[], None)?; + /// # let mut bpf = aya::Bpf::load(&[])?; /// for program in bpf.programs() { /// println!( /// "found program `{}` of type `{:?}`", @@ -379,6 +488,15 @@ pub enum BpfError { error: io::Error, }, + #[error("pinning requested but no path provided")] + NoPinPath, + + #[error("unexpected pinning type {name}")] + UnexpectedPinningType { name: u32 }, + + #[error("invalid path `{error}`")] + InvalidPath { error: String }, + #[error("error parsing BPF object")] ParseError(#[from] ParseError), diff --git a/aya/src/maps/array/array.rs b/aya/src/maps/array/array.rs index 8aa9abce..6b41cc5d 100644 --- a/aya/src/maps/array/array.rs +++ b/aya/src/maps/array/array.rs @@ -23,7 +23,7 @@ use crate::{ /// /// # Examples /// ```no_run -/// # let bpf = aya::Bpf::load(&[], None)?; +/// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::Array; /// use std::convert::TryFrom; /// diff --git a/aya/src/maps/array/per_cpu_array.rs b/aya/src/maps/array/per_cpu_array.rs index 400553b8..099778e8 100644 --- a/aya/src/maps/array/per_cpu_array.rs +++ b/aya/src/maps/array/per_cpu_array.rs @@ -32,7 +32,7 @@ use crate::{ /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let bpf = aya::Bpf::load(&[], None)?; +/// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::{PerCpuArray, PerCpuValues}; /// use aya::util::nr_cpus; /// use std::convert::TryFrom; diff --git a/aya/src/maps/array/program_array.rs b/aya/src/maps/array/program_array.rs index 840033a2..9b2ae51d 100644 --- a/aya/src/maps/array/program_array.rs +++ b/aya/src/maps/array/program_array.rs @@ -26,7 +26,7 @@ use crate::{ /// /// # Examples /// ```no_run -/// # let bpf = aya::Bpf::load(&[], None)?; +/// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::ProgramArray; /// use aya::programs::CgroupSkb; /// use std::convert::{TryFrom, TryInto}; diff --git a/aya/src/maps/hash_map/hash_map.rs b/aya/src/maps/hash_map/hash_map.rs index f9d20c9d..fc841f34 100644 --- a/aya/src/maps/hash_map/hash_map.rs +++ b/aya/src/maps/hash_map/hash_map.rs @@ -20,7 +20,7 @@ use crate::{ /// # Examples /// /// ```no_run -/// # let bpf = aya::Bpf::load(&[], None)?; +/// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::HashMap; /// use std::convert::TryFrom; /// @@ -182,6 +182,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: None, + pinned: false, }; assert!(matches!( HashMap::<_, u8, u32>::new(&map), @@ -197,6 +198,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: None, + pinned: false, }; assert!(matches!( HashMap::<_, u32, u16>::new(&map), @@ -223,6 +225,7 @@ mod tests { data: Vec::new(), }, fd: None, + pinned: false, }; assert!(matches!( @@ -236,6 +239,7 @@ mod tests { let mut map = Map { obj: new_obj_map("TEST"), fd: None, + pinned: false, }; assert!(matches!( @@ -249,6 +253,7 @@ mod tests { let mut map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; assert!(HashMap::<_, u32, u32>::new(&mut map).is_ok()); @@ -259,6 +264,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok()) } @@ -279,6 +285,7 @@ mod tests { data: Vec::new(), }, fd: Some(42), + pinned: false, }; assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok()) @@ -291,6 +298,7 @@ mod tests { let mut map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); @@ -313,6 +321,7 @@ mod tests { let mut map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); @@ -326,6 +335,7 @@ mod tests { let mut map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); @@ -348,6 +358,7 @@ mod tests { let mut map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); @@ -360,6 +371,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); @@ -381,6 +393,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); @@ -419,6 +432,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let keys = unsafe { hm.keys() }.collect::, _>>(); @@ -462,6 +476,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); @@ -489,6 +504,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); @@ -518,6 +534,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let items = unsafe { hm.iter() }.collect::, _>>().unwrap(); @@ -550,6 +567,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); @@ -583,6 +601,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); @@ -622,6 +641,7 @@ mod tests { let map = Map { obj: new_obj_map("TEST"), fd: Some(42), + pinned: false, }; let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); diff --git a/aya/src/maps/hash_map/per_cpu_hash_map.rs b/aya/src/maps/hash_map/per_cpu_hash_map.rs index 991282ed..34b8453c 100644 --- a/aya/src/maps/hash_map/per_cpu_hash_map.rs +++ b/aya/src/maps/hash_map/per_cpu_hash_map.rs @@ -27,7 +27,7 @@ use crate::{ /// # Examples /// /// ```no_run -/// # let bpf = aya::Bpf::load(&[], None)?; +/// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::PerCpuHashMap; /// use std::convert::TryFrom; /// @@ -113,7 +113,7 @@ impl, K: Pod, V: Pod> PerCpuHashMap { /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } - /// # let bpf = aya::Bpf::load(&[], None)?; + /// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::{PerCpuHashMap, PerCpuValues}; /// use aya::util::nr_cpus; /// use std::convert::TryFrom; diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index bf7b1293..e481cb7d 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -15,7 +15,7 @@ //! *typed maps* using the [`TryFrom`](std::convert::TryFrom) trait. For example: //! //! ```no_run -//! # let mut bpf = aya::Bpf::load(&[], None)?; +//! # let mut bpf = aya::Bpf::load(&[])?; //! use std::convert::{TryFrom, TryInto}; //! use aya::maps::SockMap; //! use aya::programs::SkMsg; @@ -34,14 +34,14 @@ //! implement the [Pod] trait. use std::{ convert::TryFrom, ffi::CString, io, marker::PhantomData, mem, ops::Deref, os::unix::io::RawFd, - ptr, + path::Path, ptr, }; use thiserror::Error; use crate::{ generated::bpf_map_type, obj, - sys::{bpf_create_map, bpf_map_get_next_key}, + sys::{bpf_create_map, bpf_get_object, bpf_map_get_next_key, bpf_pin_object}, util::nr_cpus, Pod, }; @@ -76,12 +76,18 @@ pub enum MapError { #[error("invalid map name `{name}`")] InvalidName { name: String }, + #[error("invalid map path `{error}`")] + InvalidPinPath { error: String }, + #[error("the map `{name}` has not been created")] NotCreated { name: String }, #[error("the map `{name}` has already been created")] AlreadyCreated { name: String }, + #[error("the map `{name}` has already been pinned")] + AlreadyPinned { name: String }, + #[error("failed to create map `{name}`: {code}")] CreateError { name: String, @@ -89,6 +95,13 @@ pub enum MapError { io_error: io::Error, }, + #[error("failed to pin map `{name}`: {code}")] + PinError { + name: String, + code: libc::c_long, + io_error: io::Error, + }, + #[error("invalid key size {size}, expected {expected}")] InvalidKeySize { size: usize, expected: usize }, @@ -128,6 +141,7 @@ pub enum MapError { pub struct Map { pub(crate) obj: obj::Map, pub(crate) fd: Option, + pub pinned: bool, } impl Map { @@ -153,6 +167,31 @@ impl Map { Ok(fd) } + pub(crate) fn from_pinned>(&mut self, path: P) -> Result { + let name = self.obj.name.clone(); + if self.fd.is_some() { + return Err(MapError::AlreadyCreated { name }); + } + let map_path = path.as_ref().join(self.name()); + let path_string = match CString::new(map_path.to_str().unwrap()) { + Ok(s) => s, + Err(e) => { + return Err(MapError::InvalidPinPath { + error: e.to_string(), + }) + } + }; + let fd = bpf_get_object(&path_string).map_err(|(code, io_error)| MapError::PinError { + name, + code, + io_error, + })? as RawFd; + + self.fd = Some(fd); + + Ok(fd) + } + pub fn name(&self) -> &str { &self.obj.name } @@ -166,6 +205,28 @@ impl Map { name: self.obj.name.clone(), }) } + + pub(crate) fn pin>(&mut self, path: P) -> Result<(), MapError> { + if self.pinned { + return Err(MapError::AlreadyPinned { + name: self.name().to_string(), + }); + } + let map_path = path.as_ref().join(self.name()); + let fd = self.fd_or_err()?; + let path_string = CString::new(map_path.to_string_lossy().into_owned()).map_err(|e| { + MapError::InvalidPinPath { + error: e.to_string(), + } + })?; + bpf_pin_object(fd, &path_string).map_err(|(code, io_error)| MapError::SyscallError { + call: "BPF_OBJ_PIN".to_string(), + code, + io_error, + })?; + self.pinned = true; + Ok(()) + } } pub(crate) trait IterableMap { @@ -334,7 +395,7 @@ impl PerCpuKernelMem { /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let bpf = aya::Bpf::load(&[], None)?; +/// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::PerCpuValues; /// use aya::util::nr_cpus; /// use std::convert::TryFrom; @@ -438,6 +499,7 @@ mod tests { Map { obj: new_obj_map(name), fd: None, + pinned: false, } } diff --git a/aya/src/maps/perf/async_perf_event_array.rs b/aya/src/maps/perf/async_perf_event_array.rs index 623191c4..fdea6db5 100644 --- a/aya/src/maps/perf/async_perf_event_array.rs +++ b/aya/src/maps/perf/async_perf_event_array.rs @@ -46,7 +46,7 @@ use crate::maps::{ /// # } /// # #[cfg(feature = "async_tokio")] /// # async fn try_main() -> Result<(), Error> { -/// # let bpf = aya::Bpf::load(&[], None)?; +/// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::perf::{AsyncPerfEventArray, PerfBufferError}; /// use aya::util::online_cpus; /// use std::convert::TryFrom; diff --git a/aya/src/maps/perf/perf_event_array.rs b/aya/src/maps/perf/perf_event_array.rs index 240cde18..9695ea60 100644 --- a/aya/src/maps/perf/perf_event_array.rs +++ b/aya/src/maps/perf/perf_event_array.rs @@ -109,7 +109,7 @@ impl> AsRawFd for PerfEventArrayBuffer { /// # #[error(transparent)] /// # PerfBuf(#[from] aya::maps::perf::PerfBufferError), /// # } -/// # let bpf = aya::Bpf::load(&[], None)?; +/// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::PerfEventArray; /// use aya::util::online_cpus; /// use std::convert::{TryFrom, TryInto}; diff --git a/aya/src/maps/queue.rs b/aya/src/maps/queue.rs index f4b53e05..a6257807 100644 --- a/aya/src/maps/queue.rs +++ b/aya/src/maps/queue.rs @@ -21,7 +21,7 @@ use crate::{ /// /// # Examples /// ```no_run -/// # let bpf = aya::Bpf::load(&[], None)?; +/// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::Queue; /// use std::convert::TryFrom; /// diff --git a/aya/src/maps/sock/sock_hash.rs b/aya/src/maps/sock/sock_hash.rs index d3b02e00..7e06173c 100644 --- a/aya/src/maps/sock/sock_hash.rs +++ b/aya/src/maps/sock/sock_hash.rs @@ -41,7 +41,7 @@ use crate::{ /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use std::convert::{TryFrom, TryInto}; /// use std::io::Write; /// use std::net::TcpStream; diff --git a/aya/src/maps/sock/sock_map.rs b/aya/src/maps/sock/sock_map.rs index ae00769d..d39f449c 100644 --- a/aya/src/maps/sock/sock_map.rs +++ b/aya/src/maps/sock/sock_map.rs @@ -29,7 +29,7 @@ use crate::{ /// # Examples /// /// ```no_run -/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use std::convert::{TryFrom, TryInto}; /// use aya::maps::SockMap; /// use aya::programs::SkSkb; diff --git a/aya/src/maps/stack.rs b/aya/src/maps/stack.rs index b86828ee..c8f36083 100644 --- a/aya/src/maps/stack.rs +++ b/aya/src/maps/stack.rs @@ -21,7 +21,7 @@ use crate::{ /// /// # Examples /// ```no_run -/// # let bpf = aya::Bpf::load(&[], None)?; +/// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::Stack; /// use std::convert::TryFrom; /// diff --git a/aya/src/maps/stack_trace.rs b/aya/src/maps/stack_trace.rs index d00189d9..aa006b24 100644 --- a/aya/src/maps/stack_trace.rs +++ b/aya/src/maps/stack_trace.rs @@ -34,7 +34,7 @@ use crate::{ /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let bpf = aya::Bpf::load(&[], None)?; +/// # let bpf = aya::Bpf::load(&[])?; /// use aya::maps::StackTraceMap; /// use aya::util::kernel_symbols; /// use std::convert::TryFrom; diff --git a/aya/src/obj/mod.rs b/aya/src/obj/mod.rs index 1849abcf..edf6341e 100644 --- a/aya/src/obj/mod.rs +++ b/aya/src/obj/mod.rs @@ -586,6 +586,7 @@ mod tests { use std::slice; use super::*; + use crate::PinningType; fn fake_section<'a>(name: &'a str, data: &'a [u8]) -> Section<'a> { Section { @@ -688,7 +689,7 @@ mod tests { max_entries: 4, map_flags: 5, id: 0, - pinning: 0, + pinning: PinningType::None, }; assert_eq!( @@ -706,7 +707,7 @@ mod tests { max_entries: 4, map_flags: 5, id: 6, - pinning: 7, + pinning: PinningType::ByName, }; assert_eq!(parse_map_def("foo", bytes_of(&def)).unwrap(), def); @@ -721,7 +722,7 @@ mod tests { max_entries: 4, map_flags: 5, id: 6, - pinning: 7, + pinning: PinningType::ByName, }; let mut buf = [0u8; 128]; unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, def) }; @@ -750,7 +751,7 @@ mod tests { max_entries: 4, map_flags: 5, id: 0, - pinning: 0 + pinning: PinningType::None, }) ), "foo" @@ -765,7 +766,7 @@ mod tests { max_entries: 4, map_flags: 5, id: 0, - pinning: 0 + pinning: PinningType::None, }, data }) if name == "foo" && data.is_empty() @@ -793,7 +794,7 @@ mod tests { max_entries: 1, map_flags: 0, id: 0, - pinning: 0, + pinning: PinningType::None, }, data }) if name == ".bss" && data == map_data && value_size == map_data.len() as u32 diff --git a/aya/src/programs/cgroup_skb.rs b/aya/src/programs/cgroup_skb.rs index 68971886..6b798ad1 100644 --- a/aya/src/programs/cgroup_skb.rs +++ b/aya/src/programs/cgroup_skb.rs @@ -37,7 +37,7 @@ use super::FdLink; /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use std::fs::File; /// use std::convert::TryInto; /// use aya::programs::{CgroupSkb, CgroupSkbAttachType}; diff --git a/aya/src/programs/lirc_mode2.rs b/aya/src/programs/lirc_mode2.rs index 8ae1ccd2..06d4cd8a 100644 --- a/aya/src/programs/lirc_mode2.rs +++ b/aya/src/programs/lirc_mode2.rs @@ -33,7 +33,7 @@ use libc::{close, dup}; /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use std::fs::File; /// use std::convert::TryInto; /// use aya::programs::LircMode2; diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 6179f389..8d39c4e8 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -56,9 +56,10 @@ use std::{ cell::RefCell, cmp, convert::TryFrom, - ffi::CStr, + ffi::{CStr, CString}, io, os::unix::io::{AsRawFd, RawFd}, + path::Path, rc::Rc, }; use thiserror::Error; @@ -82,7 +83,7 @@ use crate::{ generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type}, maps::MapError, obj::{self, Function}, - sys::{bpf_load_program, bpf_prog_detach, bpf_prog_query}, + sys::{bpf_load_program, bpf_pin_object, bpf_prog_detach, bpf_prog_query}, }; /// Error type returned when working with programs. @@ -136,6 +137,9 @@ pub enum ProgramError { #[error("unexpected program type")] UnexpectedProgramType, + #[error("invalid pin path `{error}`")] + InvalidPinPath { error: String }, + /// A map error occurred while loading or attaching a program. #[error(transparent)] MapError(#[from] MapError), @@ -225,6 +229,11 @@ impl Program { &self.data().name } + /// Pin the program to the provided path + pub fn pin>(&mut self, path: P) -> Result<(), ProgramError> { + self.data_mut().pin(path) + } + fn data(&self) -> &ProgramData { match self { Program::KProbe(p) => &p.data, @@ -278,6 +287,23 @@ impl ProgramData { self.links.push(Rc::clone(&link)); LinkRef::new(link) } + + pub fn pin>(&mut self, path: P) -> Result<(), ProgramError> { + let fd = self.fd_or_err()?; + let path_string = + CString::new(path.as_ref().to_string_lossy().into_owned()).map_err(|e| { + MapError::InvalidPinPath { + error: e.to_string(), + } + })?; + bpf_pin_object(fd, &path_string).map_err(|(_code, io_error)| { + ProgramError::SyscallError { + call: "BPF_OBJ_PIN".to_string(), + io_error, + } + })?; + Ok(()) + } } const MIN_LOG_BUF_SIZE: usize = 1024 * 10; diff --git a/aya/src/programs/perf_event.rs b/aya/src/programs/perf_event.rs index 56f3ed67..209e2ddc 100644 --- a/aya/src/programs/perf_event.rs +++ b/aya/src/programs/perf_event.rs @@ -59,7 +59,7 @@ pub enum PerfEventScope { /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use std::convert::TryInto; /// use aya::util::online_cpus; /// use aya::programs::perf_event::{ diff --git a/aya/src/programs/sk_msg.rs b/aya/src/programs/sk_msg.rs index 4e42136e..9298a478 100644 --- a/aya/src/programs/sk_msg.rs +++ b/aya/src/programs/sk_msg.rs @@ -29,7 +29,7 @@ use crate::{ /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use std::convert::{TryFrom, TryInto}; /// use std::io::Write; /// use std::net::TcpStream; diff --git a/aya/src/programs/sk_skb.rs b/aya/src/programs/sk_skb.rs index ffaebf6a..6ab8a0fb 100644 --- a/aya/src/programs/sk_skb.rs +++ b/aya/src/programs/sk_skb.rs @@ -28,7 +28,7 @@ pub enum SkSkbKind { /// # Examples /// /// ```no_run -/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use std::convert::{TryFrom, TryInto}; /// use aya::maps::SockMap; /// use aya::programs::SkSkb; diff --git a/aya/src/programs/sock_ops.rs b/aya/src/programs/sock_ops.rs index 048bb950..5852c4d7 100644 --- a/aya/src/programs/sock_ops.rs +++ b/aya/src/programs/sock_ops.rs @@ -30,7 +30,7 @@ use crate::{ /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use std::fs::File; /// use std::convert::TryInto; /// use aya::programs::SockOps; diff --git a/aya/src/programs/socket_filter.rs b/aya/src/programs/socket_filter.rs index c01dc1de..1509b22b 100644 --- a/aya/src/programs/socket_filter.rs +++ b/aya/src/programs/socket_filter.rs @@ -44,7 +44,7 @@ pub enum SocketFilterError { /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use std::convert::TryInto; /// use std::net::TcpStream; /// use std::os::unix::io::AsRawFd; diff --git a/aya/src/programs/tc.rs b/aya/src/programs/tc.rs index 1d6fae6b..4d16e0d4 100644 --- a/aya/src/programs/tc.rs +++ b/aya/src/programs/tc.rs @@ -51,7 +51,7 @@ pub enum TcAttachType { /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use std::convert::TryInto; /// use aya::programs::{tc, SchedClassifier, TcAttachType}; /// diff --git a/aya/src/programs/trace_point.rs b/aya/src/programs/trace_point.rs index a3cb52f6..331be6ca 100644 --- a/aya/src/programs/trace_point.rs +++ b/aya/src/programs/trace_point.rs @@ -40,7 +40,7 @@ pub enum TracePointError { /// # #[error(transparent)] /// # Bpf(#[from] aya::BpfError) /// # } -/// # let mut bpf = aya::Bpf::load(&[], None)?; +/// # let mut bpf = aya::Bpf::load(&[])?; /// use std::convert::TryInto; /// use aya::programs::TracePoint; /// diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index 86b38e0f..a9902015 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -38,6 +38,21 @@ pub(crate) fn bpf_create_map(name: &CStr, def: &bpf_map_def) -> SysResult { sys_bpf(bpf_cmd::BPF_MAP_CREATE, &attr) } +pub(crate) fn bpf_pin_object(fd: RawFd, path: &CStr) -> SysResult { + let mut attr = unsafe { mem::zeroed::() }; + let u = unsafe { &mut attr.__bindgen_anon_4 }; + u.bpf_fd = fd as u32; + u.pathname = path.as_ptr() as u64; + sys_bpf(bpf_cmd::BPF_OBJ_PIN, &attr) +} + +pub(crate) fn bpf_get_object(path: &CStr) -> SysResult { + let mut attr = unsafe { mem::zeroed::() }; + let u = unsafe { &mut attr.__bindgen_anon_4 }; + u.pathname = path.as_ptr() as u64; + sys_bpf(bpf_cmd::BPF_OBJ_GET, &attr) +} + pub(crate) fn bpf_load_program( ty: bpf_prog_type, insns: &[bpf_insn],