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 <dave@dtucker.co.uk>
pull/65/head
Dave Tucker 3 years ago committed by Alessandro Decina
parent a7f5b3775d
commit 9426f36f79

@ -2,7 +2,7 @@ use std::{
collections::HashMap, collections::HashMap,
error::Error, error::Error,
fs, io, fs, io,
os::raw::c_int, os::{raw::c_int, unix::io::RawFd},
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
@ -58,64 +58,92 @@ pub(crate) struct bpf_map_def {
pub(crate) map_flags: u32, pub(crate) map_flags: u32,
// optional features // optional features
pub(crate) id: u32, 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. #[repr(u32)]
#[derive(Debug)] #[derive(Copy, Clone, Debug, PartialEq)]
pub struct Bpf { pub(crate) enum PinningType {
maps: HashMap<String, MapLock>, None = 0,
programs: HashMap<String, Program>, #[allow(dead_code)] // ByName is constructed from the BPF side
ByName = 1,
}
impl Default for PinningType {
fn default() -> Self {
PinningType::None
}
}
#[derive(Default, Debug)]
pub struct BpfLoader {
btf: Option<Btf>,
map_pin_path: Option<PathBuf>,
}
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<P: AsRef<Path>>(&mut self, path: P) -> &mut BpfLoader {
self.map_pin_path = Some(path.as_ref().to_owned());
self
} }
impl Bpf {
/// Loads eBPF bytecode from a file. /// Loads eBPF bytecode from a file.
/// ///
/// Parses the given object code file and initializes the [maps](crate::maps) defined in it. If /// Parses the given object code file and initializes the [maps](crate::maps) defined in it.
/// the kernel supports [BTF](Btf) debug info, it is automatically loaded from
/// `/sys/kernel/btf/vmlinux`.
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```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>(()) /// # Ok::<(), aya::BpfError>(())
/// ``` /// ```
pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Bpf, BpfError> { pub fn load_file<P: AsRef<Path>>(&mut self, path: P) -> Result<Bpf, BpfError> {
let path = path.as_ref(); let path = path.as_ref();
Bpf::load( self.load(&fs::read(path).map_err(|error| BpfError::FileError {
&fs::read(path).map_err(|error| BpfError::FileError {
path: path.to_owned(), path: path.to_owned(),
error, error,
})?, })?)
Btf::from_sys_fs().ok().as_ref(),
)
} }
/// Load eBPF bytecode. /// Load eBPF bytecode.
/// ///
/// Parses the object code contained in `data` and initializes the [maps](crate::maps) defined /// 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 /// in it. If `BpfLoader.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 /// are applied as well. Any maps that require pinning will be pinned to `BpfLoader.map_pin_path`
/// eBPF programs, `target_btf` is passed by reference.
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// use aya::{Bpf, Btf}; /// use aya::{BpfLoader, Btf};
/// use std::fs; /// use std::fs;
/// ///
/// let data = fs::read("file.o").unwrap(); /// let data = fs::read("file.o").unwrap();
/// // load the BTF data from /sys/kernel/btf/vmlinux /// // 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>(()) /// # Ok::<(), aya::BpfError>(())
/// ``` /// ```
pub fn load(data: &[u8], target_btf: Option<&Btf>) -> Result<Bpf, BpfError> { pub fn load(&mut self, data: &[u8]) -> Result<Bpf, BpfError> {
let mut obj = Object::parse(data)?; let mut obj = Object::parse(data)?;
if let Some(btf) = target_btf { if let Some(btf) = &self.btf {
obj.relocate_btf(btf)?; obj.relocate_btf(btf)?;
} }
@ -130,8 +158,32 @@ impl Bpf {
})? })?
.len() as u32; .len() as u32;
} }
let mut map = Map { obj, fd: None }; 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()?; let fd = map.create()?;
map.pin(path)?;
fd
}
}
}
PinningType::None => map.create()?,
};
if !map.obj.data.is_empty() && map.obj.name != ".bss" { 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( bpf_map_update_elem_ptr(fd, &0 as *const _, map.obj.data.as_mut_ptr(), 0).map_err(
|(code, io_error)| MapError::SyscallError { |(code, io_error)| MapError::SyscallError {
@ -208,7 +260,6 @@ impl Bpf {
(name, program) (name, program)
}) })
.collect(); .collect();
Ok(Bpf { Ok(Bpf {
maps: maps maps: maps
.drain(..) .drain(..)
@ -217,6 +268,64 @@ impl Bpf {
programs, programs,
}) })
} }
}
/// The main entry point into the library, used to work with eBPF programs and maps.
#[derive(Debug)]
pub struct Bpf {
maps: HashMap<String, MapLock>,
programs: HashMap<String, Program>,
}
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<P: AsRef<Path>>(path: P) -> Result<Bpf, BpfError> {
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<Bpf, BpfError> {
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. /// Returns a reference to the map with the given name.
/// ///
@ -272,7 +381,7 @@ impl Bpf {
/// ///
/// # Examples /// # Examples
/// ```no_run /// ```no_run
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// for (name, map) in bpf.maps() { /// for (name, map) in bpf.maps() {
/// println!( /// println!(
/// "found map `{}` of type `{:?}`", /// "found map `{}` of type `{:?}`",
@ -308,7 +417,7 @@ impl Bpf {
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// let program = bpf.program("SSL_read")?; /// let program = bpf.program("SSL_read")?;
/// println!("program SSL_read is of type {:?}", program.prog_type()); /// println!("program SSL_read is of type {:?}", program.prog_type());
/// # Ok::<(), aya::BpfError>(()) /// # Ok::<(), aya::BpfError>(())
@ -333,7 +442,7 @@ impl Bpf {
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use aya::programs::UProbe; /// use aya::programs::UProbe;
/// use std::convert::TryInto; /// use std::convert::TryInto;
/// ///
@ -354,7 +463,7 @@ impl Bpf {
/// ///
/// # Examples /// # Examples
/// ```no_run /// ```no_run
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// for program in bpf.programs() { /// for program in bpf.programs() {
/// println!( /// println!(
/// "found program `{}` of type `{:?}`", /// "found program `{}` of type `{:?}`",
@ -379,6 +488,15 @@ pub enum BpfError {
error: io::Error, 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")] #[error("error parsing BPF object")]
ParseError(#[from] ParseError), ParseError(#[from] ParseError),

@ -23,7 +23,7 @@ use crate::{
/// ///
/// # Examples /// # Examples
/// ```no_run /// ```no_run
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// use aya::maps::Array; /// use aya::maps::Array;
/// use std::convert::TryFrom; /// use std::convert::TryFrom;
/// ///

@ -32,7 +32,7 @@ use crate::{
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// use aya::maps::{PerCpuArray, PerCpuValues}; /// use aya::maps::{PerCpuArray, PerCpuValues};
/// use aya::util::nr_cpus; /// use aya::util::nr_cpus;
/// use std::convert::TryFrom; /// use std::convert::TryFrom;

@ -26,7 +26,7 @@ use crate::{
/// ///
/// # Examples /// # Examples
/// ```no_run /// ```no_run
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// use aya::maps::ProgramArray; /// use aya::maps::ProgramArray;
/// use aya::programs::CgroupSkb; /// use aya::programs::CgroupSkb;
/// use std::convert::{TryFrom, TryInto}; /// use std::convert::{TryFrom, TryInto};

@ -20,7 +20,7 @@ use crate::{
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// use aya::maps::HashMap; /// use aya::maps::HashMap;
/// use std::convert::TryFrom; /// use std::convert::TryFrom;
/// ///
@ -182,6 +182,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: None, fd: None,
pinned: false,
}; };
assert!(matches!( assert!(matches!(
HashMap::<_, u8, u32>::new(&map), HashMap::<_, u8, u32>::new(&map),
@ -197,6 +198,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: None, fd: None,
pinned: false,
}; };
assert!(matches!( assert!(matches!(
HashMap::<_, u32, u16>::new(&map), HashMap::<_, u32, u16>::new(&map),
@ -223,6 +225,7 @@ mod tests {
data: Vec::new(), data: Vec::new(),
}, },
fd: None, fd: None,
pinned: false,
}; };
assert!(matches!( assert!(matches!(
@ -236,6 +239,7 @@ mod tests {
let mut map = Map { let mut map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: None, fd: None,
pinned: false,
}; };
assert!(matches!( assert!(matches!(
@ -249,6 +253,7 @@ mod tests {
let mut map = Map { let mut map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
assert!(HashMap::<_, u32, u32>::new(&mut map).is_ok()); assert!(HashMap::<_, u32, u32>::new(&mut map).is_ok());
@ -259,6 +264,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok()) assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
} }
@ -279,6 +285,7 @@ mod tests {
data: Vec::new(), data: Vec::new(),
}, },
fd: Some(42), fd: Some(42),
pinned: false,
}; };
assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok()) assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
@ -291,6 +298,7 @@ mod tests {
let mut map = Map { let mut map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -313,6 +321,7 @@ mod tests {
let mut map = Map { let mut map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -326,6 +335,7 @@ mod tests {
let mut map = Map { let mut map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -348,6 +358,7 @@ mod tests {
let mut map = Map { let mut map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap(); let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
@ -360,6 +371,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -381,6 +393,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -419,6 +432,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let keys = unsafe { hm.keys() }.collect::<Result<Vec<_>, _>>(); let keys = unsafe { hm.keys() }.collect::<Result<Vec<_>, _>>();
@ -462,6 +476,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -489,6 +504,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -518,6 +534,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let items = unsafe { hm.iter() }.collect::<Result<Vec<_>, _>>().unwrap(); let items = unsafe { hm.iter() }.collect::<Result<Vec<_>, _>>().unwrap();
@ -550,6 +567,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -583,6 +601,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -622,6 +641,7 @@ mod tests {
let map = Map { let map = Map {
obj: new_obj_map("TEST"), obj: new_obj_map("TEST"),
fd: Some(42), fd: Some(42),
pinned: false,
}; };
let hm = HashMap::<_, u32, u32>::new(&map).unwrap(); let hm = HashMap::<_, u32, u32>::new(&map).unwrap();

@ -27,7 +27,7 @@ use crate::{
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// use aya::maps::PerCpuHashMap; /// use aya::maps::PerCpuHashMap;
/// use std::convert::TryFrom; /// use std::convert::TryFrom;
/// ///
@ -113,7 +113,7 @@ impl<T: DerefMut<Target = Map>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// use aya::maps::{PerCpuHashMap, PerCpuValues}; /// use aya::maps::{PerCpuHashMap, PerCpuValues};
/// use aya::util::nr_cpus; /// use aya::util::nr_cpus;
/// use std::convert::TryFrom; /// use std::convert::TryFrom;

@ -15,7 +15,7 @@
//! *typed maps* 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 mut bpf = aya::Bpf::load(&[], None)?; //! # let mut bpf = aya::Bpf::load(&[])?;
//! 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::SkMsg;
@ -34,14 +34,14 @@
//! implement the [Pod] trait. //! 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, path::Path, ptr,
}; };
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
generated::bpf_map_type, generated::bpf_map_type,
obj, 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, util::nr_cpus,
Pod, Pod,
}; };
@ -76,12 +76,18 @@ pub enum MapError {
#[error("invalid map name `{name}`")] #[error("invalid map name `{name}`")]
InvalidName { name: String }, InvalidName { name: String },
#[error("invalid map path `{error}`")]
InvalidPinPath { error: String },
#[error("the map `{name}` has not been created")] #[error("the map `{name}` has not been created")]
NotCreated { name: String }, NotCreated { name: String },
#[error("the map `{name}` has already been created")] #[error("the map `{name}` has already been created")]
AlreadyCreated { name: String }, AlreadyCreated { name: String },
#[error("the map `{name}` has already been pinned")]
AlreadyPinned { name: String },
#[error("failed to create map `{name}`: {code}")] #[error("failed to create map `{name}`: {code}")]
CreateError { CreateError {
name: String, name: String,
@ -89,6 +95,13 @@ pub enum MapError {
io_error: io::Error, 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}")] #[error("invalid key size {size}, expected {expected}")]
InvalidKeySize { size: usize, expected: usize }, InvalidKeySize { size: usize, expected: usize },
@ -128,6 +141,7 @@ pub enum MapError {
pub struct Map { pub struct Map {
pub(crate) obj: obj::Map, pub(crate) obj: obj::Map,
pub(crate) fd: Option<RawFd>, pub(crate) fd: Option<RawFd>,
pub pinned: bool,
} }
impl Map { impl Map {
@ -153,6 +167,31 @@ impl Map {
Ok(fd) Ok(fd)
} }
pub(crate) fn from_pinned<P: AsRef<Path>>(&mut self, path: P) -> Result<RawFd, MapError> {
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 { pub fn name(&self) -> &str {
&self.obj.name &self.obj.name
} }
@ -166,6 +205,28 @@ impl Map {
name: self.obj.name.clone(), name: self.obj.name.clone(),
}) })
} }
pub(crate) fn pin<P: AsRef<Path>>(&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<K: Pod, V> { pub(crate) trait IterableMap<K: Pod, V> {
@ -334,7 +395,7 @@ impl PerCpuKernelMem {
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// use aya::maps::PerCpuValues; /// use aya::maps::PerCpuValues;
/// use aya::util::nr_cpus; /// use aya::util::nr_cpus;
/// use std::convert::TryFrom; /// use std::convert::TryFrom;
@ -438,6 +499,7 @@ mod tests {
Map { Map {
obj: new_obj_map(name), obj: new_obj_map(name),
fd: None, fd: None,
pinned: false,
} }
} }

@ -46,7 +46,7 @@ use crate::maps::{
/// # } /// # }
/// # #[cfg(feature = "async_tokio")] /// # #[cfg(feature = "async_tokio")]
/// # async fn try_main() -> Result<(), Error> { /// # 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::maps::perf::{AsyncPerfEventArray, PerfBufferError};
/// use aya::util::online_cpus; /// use aya::util::online_cpus;
/// use std::convert::TryFrom; /// use std::convert::TryFrom;

@ -109,7 +109,7 @@ impl<T: DerefMut<Target = Map>> AsRawFd for PerfEventArrayBuffer<T> {
/// # #[error(transparent)] /// # #[error(transparent)]
/// # PerfBuf(#[from] aya::maps::perf::PerfBufferError), /// # PerfBuf(#[from] aya::maps::perf::PerfBufferError),
/// # } /// # }
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// use aya::maps::PerfEventArray; /// use aya::maps::PerfEventArray;
/// use aya::util::online_cpus; /// use aya::util::online_cpus;
/// use std::convert::{TryFrom, TryInto}; /// use std::convert::{TryFrom, TryInto};

@ -21,7 +21,7 @@ use crate::{
/// ///
/// # Examples /// # Examples
/// ```no_run /// ```no_run
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// use aya::maps::Queue; /// use aya::maps::Queue;
/// use std::convert::TryFrom; /// use std::convert::TryFrom;
/// ///

@ -41,7 +41,7 @@ use crate::{
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::convert::{TryFrom, TryInto}; /// use std::convert::{TryFrom, TryInto};
/// use std::io::Write; /// use std::io::Write;
/// use std::net::TcpStream; /// use std::net::TcpStream;

@ -29,7 +29,7 @@ use crate::{
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::convert::{TryFrom, TryInto}; /// use std::convert::{TryFrom, TryInto};
/// use aya::maps::SockMap; /// use aya::maps::SockMap;
/// use aya::programs::SkSkb; /// use aya::programs::SkSkb;

@ -21,7 +21,7 @@ use crate::{
/// ///
/// # Examples /// # Examples
/// ```no_run /// ```no_run
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// use aya::maps::Stack; /// use aya::maps::Stack;
/// use std::convert::TryFrom; /// use std::convert::TryFrom;
/// ///

@ -34,7 +34,7 @@ use crate::{
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let bpf = aya::Bpf::load(&[], None)?; /// # let bpf = aya::Bpf::load(&[])?;
/// use aya::maps::StackTraceMap; /// use aya::maps::StackTraceMap;
/// use aya::util::kernel_symbols; /// use aya::util::kernel_symbols;
/// use std::convert::TryFrom; /// use std::convert::TryFrom;

@ -586,6 +586,7 @@ mod tests {
use std::slice; use std::slice;
use super::*; use super::*;
use crate::PinningType;
fn fake_section<'a>(name: &'a str, data: &'a [u8]) -> Section<'a> { fn fake_section<'a>(name: &'a str, data: &'a [u8]) -> Section<'a> {
Section { Section {
@ -688,7 +689,7 @@ mod tests {
max_entries: 4, max_entries: 4,
map_flags: 5, map_flags: 5,
id: 0, id: 0,
pinning: 0, pinning: PinningType::None,
}; };
assert_eq!( assert_eq!(
@ -706,7 +707,7 @@ mod tests {
max_entries: 4, max_entries: 4,
map_flags: 5, map_flags: 5,
id: 6, id: 6,
pinning: 7, pinning: PinningType::ByName,
}; };
assert_eq!(parse_map_def("foo", bytes_of(&def)).unwrap(), def); assert_eq!(parse_map_def("foo", bytes_of(&def)).unwrap(), def);
@ -721,7 +722,7 @@ mod tests {
max_entries: 4, max_entries: 4,
map_flags: 5, map_flags: 5,
id: 6, id: 6,
pinning: 7, pinning: PinningType::ByName,
}; };
let mut buf = [0u8; 128]; let mut buf = [0u8; 128];
unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, def) }; unsafe { ptr::write_unaligned(buf.as_mut_ptr() as *mut _, def) };
@ -750,7 +751,7 @@ mod tests {
max_entries: 4, max_entries: 4,
map_flags: 5, map_flags: 5,
id: 0, id: 0,
pinning: 0 pinning: PinningType::None,
}) })
), ),
"foo" "foo"
@ -765,7 +766,7 @@ mod tests {
max_entries: 4, max_entries: 4,
map_flags: 5, map_flags: 5,
id: 0, id: 0,
pinning: 0 pinning: PinningType::None,
}, },
data data
}) if name == "foo" && data.is_empty() }) if name == "foo" && data.is_empty()
@ -793,7 +794,7 @@ mod tests {
max_entries: 1, max_entries: 1,
map_flags: 0, map_flags: 0,
id: 0, id: 0,
pinning: 0, pinning: PinningType::None,
}, },
data data
}) if name == ".bss" && data == map_data && value_size == map_data.len() as u32 }) if name == ".bss" && data == map_data && value_size == map_data.len() as u32

@ -37,7 +37,7 @@ use super::FdLink;
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::fs::File; /// use std::fs::File;
/// use std::convert::TryInto; /// use std::convert::TryInto;
/// use aya::programs::{CgroupSkb, CgroupSkbAttachType}; /// use aya::programs::{CgroupSkb, CgroupSkbAttachType};

@ -33,7 +33,7 @@ use libc::{close, dup};
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::fs::File; /// use std::fs::File;
/// use std::convert::TryInto; /// use std::convert::TryInto;
/// use aya::programs::LircMode2; /// use aya::programs::LircMode2;

@ -56,9 +56,10 @@ use std::{
cell::RefCell, cell::RefCell,
cmp, cmp,
convert::TryFrom, convert::TryFrom,
ffi::CStr, ffi::{CStr, CString},
io, io,
os::unix::io::{AsRawFd, RawFd}, os::unix::io::{AsRawFd, RawFd},
path::Path,
rc::Rc, rc::Rc,
}; };
use thiserror::Error; use thiserror::Error;
@ -82,7 +83,7 @@ use crate::{
generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type}, generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type},
maps::MapError, maps::MapError,
obj::{self, Function}, 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. /// Error type returned when working with programs.
@ -136,6 +137,9 @@ pub enum ProgramError {
#[error("unexpected program type")] #[error("unexpected program type")]
UnexpectedProgramType, UnexpectedProgramType,
#[error("invalid pin path `{error}`")]
InvalidPinPath { error: String },
/// A map error occurred while loading or attaching a program. /// A map error occurred while loading or attaching a program.
#[error(transparent)] #[error(transparent)]
MapError(#[from] MapError), MapError(#[from] MapError),
@ -225,6 +229,11 @@ impl Program {
&self.data().name &self.data().name
} }
/// Pin the program to the provided path
pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ProgramError> {
self.data_mut().pin(path)
}
fn data(&self) -> &ProgramData { fn data(&self) -> &ProgramData {
match self { match self {
Program::KProbe(p) => &p.data, Program::KProbe(p) => &p.data,
@ -278,6 +287,23 @@ impl ProgramData {
self.links.push(Rc::clone(&link)); self.links.push(Rc::clone(&link));
LinkRef::new(link) LinkRef::new(link)
} }
pub fn pin<P: AsRef<Path>>(&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; const MIN_LOG_BUF_SIZE: usize = 1024 * 10;

@ -59,7 +59,7 @@ pub enum PerfEventScope {
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::convert::TryInto; /// use std::convert::TryInto;
/// use aya::util::online_cpus; /// use aya::util::online_cpus;
/// use aya::programs::perf_event::{ /// use aya::programs::perf_event::{

@ -29,7 +29,7 @@ use crate::{
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::convert::{TryFrom, TryInto}; /// use std::convert::{TryFrom, TryInto};
/// use std::io::Write; /// use std::io::Write;
/// use std::net::TcpStream; /// use std::net::TcpStream;

@ -28,7 +28,7 @@ pub enum SkSkbKind {
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::convert::{TryFrom, TryInto}; /// use std::convert::{TryFrom, TryInto};
/// use aya::maps::SockMap; /// use aya::maps::SockMap;
/// use aya::programs::SkSkb; /// use aya::programs::SkSkb;

@ -30,7 +30,7 @@ use crate::{
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::fs::File; /// use std::fs::File;
/// use std::convert::TryInto; /// use std::convert::TryInto;
/// use aya::programs::SockOps; /// use aya::programs::SockOps;

@ -44,7 +44,7 @@ pub enum SocketFilterError {
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::convert::TryInto; /// use std::convert::TryInto;
/// use std::net::TcpStream; /// use std::net::TcpStream;
/// use std::os::unix::io::AsRawFd; /// use std::os::unix::io::AsRawFd;

@ -51,7 +51,7 @@ pub enum TcAttachType {
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::convert::TryInto; /// use std::convert::TryInto;
/// use aya::programs::{tc, SchedClassifier, TcAttachType}; /// use aya::programs::{tc, SchedClassifier, TcAttachType};
/// ///

@ -40,7 +40,7 @@ pub enum TracePointError {
/// # #[error(transparent)] /// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError) /// # Bpf(#[from] aya::BpfError)
/// # } /// # }
/// # let mut bpf = aya::Bpf::load(&[], None)?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::convert::TryInto; /// use std::convert::TryInto;
/// use aya::programs::TracePoint; /// use aya::programs::TracePoint;
/// ///

@ -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) 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::<bpf_attr>() };
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::<bpf_attr>() };
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( pub(crate) fn bpf_load_program(
ty: bpf_prog_type, ty: bpf_prog_type,
insns: &[bpf_insn], insns: &[bpf_insn],

Loading…
Cancel
Save