Merge pull request #343 from dave-tucker/pinning-redux

Small Pinning API improvements
pull/355/head
Dave Tucker 2 years ago committed by GitHub
commit 8e6c9ad0d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -400,7 +400,10 @@ impl<'a> BpfLoader<'a> {
} }
Err(_) => { Err(_) => {
let fd = map.create(&name)?; let fd = map.create(&name)?;
map.pin(&name, path)?; map.pin(&name, path).map_err(|error| MapError::PinError {
name: name.to_string(),
error,
})?;
fd fd
} }
} }
@ -774,12 +777,19 @@ impl Bpf {
/// # Examples /// # Examples
/// ```no_run /// ```no_run
/// # use std::path::Path; /// # use std::path::Path;
/// # #[derive(thiserror::Error, Debug)]
/// # enum Error {
/// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError),
/// # #[error(transparent)]
/// # Pin(#[from] aya::pin::PinError)
/// # }
/// # let mut bpf = aya::Bpf::load(&[])?; /// # let mut bpf = aya::Bpf::load(&[])?;
/// # let pin_path = Path::new("/tmp/pin_path"); /// # let pin_path = Path::new("/tmp/pin_path");
/// for (_, program) in bpf.programs_mut() { /// for (_, program) in bpf.programs_mut() {
/// program.pin(pin_path)?; /// program.pin(pin_path)?;
/// } /// }
/// # Ok::<(), aya::BpfError>(()) /// # Ok::<(), Error>(())
/// ``` /// ```
pub fn programs_mut(&mut self) -> impl Iterator<Item = (&str, &mut Program)> { pub fn programs_mut(&mut self) -> impl Iterator<Item = (&str, &mut Program)> {
self.programs.iter_mut().map(|(s, p)| (s.as_str(), p)) self.programs.iter_mut().map(|(s, p)| (s.as_str(), p))

@ -48,6 +48,7 @@ mod bpf;
mod generated; mod generated;
pub mod maps; pub mod maps;
mod obj; mod obj;
pub mod pin;
pub mod programs; pub mod programs;
mod sys; mod sys;
pub mod util; pub mod util;

@ -51,6 +51,7 @@ use thiserror::Error;
use crate::{ use crate::{
generated::bpf_map_type, generated::bpf_map_type,
obj, obj,
pin::PinError,
sys::{bpf_create_map, bpf_get_object, bpf_map_get_next_key, bpf_pin_object, kernel_version}, sys::{bpf_create_map, bpf_get_object, bpf_map_get_next_key, bpf_pin_object, kernel_version},
util::nr_cpus, util::nr_cpus,
Pod, Pod,
@ -119,13 +120,6 @@ pub enum MapError {
name: String, name: String,
}, },
/// The map has already been pinned
#[error("the map `{name}` has already been pinned")]
AlreadyPinned {
/// Map name
name: String,
},
/// Failed to create map /// Failed to create map
#[error("failed to create map `{name}` with code {code}")] #[error("failed to create map `{name}` with code {code}")]
CreateError { CreateError {
@ -138,18 +132,6 @@ pub enum MapError {
io_error: io::Error, io_error: io::Error,
}, },
/// Failed to pin map
#[error("failed to pin map `{name}` with code {code}")]
PinError {
/// Map Name
name: String,
/// Error code
code: libc::c_long,
#[source]
/// Original io::Error
io_error: io::Error,
},
/// Invalid key size /// Invalid key size
#[error("invalid key size {size}, expected {expected}")] #[error("invalid key size {size}, expected {expected}")]
InvalidKeySize { InvalidKeySize {
@ -214,6 +196,16 @@ pub enum MapError {
/// Map name /// Map name
name: String, name: String,
}, },
/// Could not pin map by name
#[error("map `{name}` requested pinning by name. pinning failed")]
PinError {
/// The map name
name: String,
/// The reason for the failure
#[source]
error: PinError,
},
} }
/// A map file descriptor. /// A map file descriptor.
@ -316,8 +308,9 @@ impl Map {
}) })
} }
}; };
let fd = bpf_get_object(&path_string).map_err(|(code, io_error)| MapError::PinError { let fd =
name: name.into(), bpf_get_object(&path_string).map_err(|(code, io_error)| MapError::SyscallError {
call: "BPF_OBJ_GET".to_string(),
code, code,
io_error, io_error,
})? as RawFd; })? as RawFd;
@ -336,20 +329,21 @@ impl Map {
self.fd.ok_or(MapError::NotCreated) self.fd.ok_or(MapError::NotCreated)
} }
pub(crate) fn pin<P: AsRef<Path>>(&mut self, name: &str, path: P) -> Result<(), MapError> { pub(crate) fn pin<P: AsRef<Path>>(&mut self, name: &str, path: P) -> Result<(), PinError> {
if self.pinned { if self.pinned {
return Err(MapError::AlreadyPinned { name: name.into() }); return Err(PinError::AlreadyPinned { name: name.into() });
} }
let map_path = path.as_ref().join(name); let map_path = path.as_ref().join(name);
let fd = self.fd_or_err()?; let fd = self.fd.ok_or(PinError::NoFd {
name: name.to_string(),
})?;
let path_string = CString::new(map_path.to_string_lossy().into_owned()).map_err(|e| { let path_string = CString::new(map_path.to_string_lossy().into_owned()).map_err(|e| {
MapError::InvalidPinPath { PinError::InvalidPinPath {
error: e.to_string(), error: e.to_string(),
} }
})?; })?;
bpf_pin_object(fd, &path_string).map_err(|(code, io_error)| MapError::SyscallError { bpf_pin_object(fd, &path_string).map_err(|(_, io_error)| PinError::SyscallError {
call: "BPF_OBJ_PIN".to_string(), name: "BPF_OBJ_GET".to_string(),
code,
io_error, io_error,
})?; })?;
self.pinned = true; self.pinned = true;

@ -0,0 +1,35 @@
//! Pinning BPF objects to the BPF filesystem.
use std::io;
use thiserror::Error;
/// An error ocurred working with a pinned BPF object.
#[derive(Error, Debug)]
pub enum PinError {
/// The object has already been pinned.
#[error("the BPF object `{name}` has already been pinned")]
AlreadyPinned {
/// Object name.
name: String,
},
/// The object FD is not known by Aya.
#[error("the BPF object `{name}`'s FD is not known")]
NoFd {
/// Object name.
name: String,
},
/// The path for the BPF object is not valid.
#[error("invalid pin path `{error}`")]
InvalidPinPath {
/// The error message.
error: String,
},
/// An error ocurred making a syscall.
#[error("{name} failed")]
SyscallError {
/// The syscall name.
name: String,
/// The [`io::Error`] returned by the syscall.
#[source]
io_error: io::Error,
},
}

@ -4,11 +4,18 @@ use libc::{close, dup};
use std::{ use std::{
borrow::Borrow, borrow::Borrow,
collections::{hash_map::Entry, HashMap}, collections::{hash_map::Entry, HashMap},
ffi::CString,
ops::Deref, ops::Deref,
os::unix::prelude::RawFd, os::unix::prelude::RawFd,
path::Path,
}; };
use crate::{generated::bpf_attach_type, programs::ProgramError, sys::bpf_prog_detach}; use crate::{
generated::bpf_attach_type,
pin::PinError,
programs::ProgramError,
sys::{bpf_pin_object, bpf_prog_detach},
};
/// A Link. /// A Link.
pub trait Link: std::fmt::Debug + 'static { pub trait Link: std::fmt::Debug + 'static {
@ -111,6 +118,26 @@ impl FdLink {
pub(crate) fn new(fd: RawFd) -> FdLink { pub(crate) fn new(fd: RawFd) -> FdLink {
FdLink { fd } FdLink { fd }
} }
/// Pins the FdLink to a BPF filesystem.
///
/// When a BPF object is pinned to a BPF filesystem it will remain attached after
/// Aya has detached the link.
/// To remove the attachment, the file on the BPF filesystem must be removed.
/// Any directories in the the path provided should have been created by the caller.
pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), PinError> {
let path_string =
CString::new(path.as_ref().to_string_lossy().into_owned()).map_err(|e| {
PinError::InvalidPinPath {
error: e.to_string(),
}
})?;
bpf_pin_object(self.fd, &path_string).map_err(|(_, io_error)| PinError::SyscallError {
name: "BPF_OBJ_PIN".to_string(),
io_error,
})?;
Ok(())
}
} }
impl Link for FdLink { impl Link for FdLink {

@ -106,6 +106,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, btf::BtfError, Function, KernelVersion}, obj::{self, btf::BtfError, Function, KernelVersion},
pin::PinError,
sys::{ sys::{
bpf_get_object, bpf_load_program, bpf_obj_get_info_by_fd, bpf_pin_object, bpf_get_object, bpf_load_program, bpf_obj_get_info_by_fd, bpf_pin_object,
bpf_prog_get_fd_by_id, bpf_prog_query, retry_with_verifier_logs, BpfLoadProgramAttrs, bpf_prog_get_fd_by_id, bpf_prog_query, retry_with_verifier_logs, BpfLoadProgramAttrs,
@ -163,13 +164,6 @@ pub enum ProgramError {
#[error("unexpected program type")] #[error("unexpected program type")]
UnexpectedProgramType, UnexpectedProgramType,
/// Invalid pin path
#[error("invalid pin path `{error}`")]
InvalidPinPath {
/// the error message
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),
@ -307,31 +301,31 @@ impl Program {
} }
/// Pin the program to the provided path /// Pin the program to the provided path
pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ProgramError> { pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError> {
match self { match self {
Program::KProbe(p) => p.data.pin(path), Program::KProbe(p) => p.pin(path),
Program::UProbe(p) => p.data.pin(path), Program::UProbe(p) => p.pin(path),
Program::TracePoint(p) => p.data.pin(path), Program::TracePoint(p) => p.pin(path),
Program::SocketFilter(p) => p.data.pin(path), Program::SocketFilter(p) => p.pin(path),
Program::Xdp(p) => p.data.pin(path), Program::Xdp(p) => p.pin(path),
Program::SkMsg(p) => p.data.pin(path), Program::SkMsg(p) => p.pin(path),
Program::SkSkb(p) => p.data.pin(path), Program::SkSkb(p) => p.pin(path),
Program::SockOps(p) => p.data.pin(path), Program::SockOps(p) => p.pin(path),
Program::SchedClassifier(p) => p.data.pin(path), Program::SchedClassifier(p) => p.pin(path),
Program::CgroupSkb(p) => p.data.pin(path), Program::CgroupSkb(p) => p.pin(path),
Program::CgroupSysctl(p) => p.data.pin(path), Program::CgroupSysctl(p) => p.pin(path),
Program::CgroupSockopt(p) => p.data.pin(path), Program::CgroupSockopt(p) => p.pin(path),
Program::LircMode2(p) => p.data.pin(path), Program::LircMode2(p) => p.pin(path),
Program::PerfEvent(p) => p.data.pin(path), Program::PerfEvent(p) => p.pin(path),
Program::RawTracePoint(p) => p.data.pin(path), Program::RawTracePoint(p) => p.pin(path),
Program::Lsm(p) => p.data.pin(path), Program::Lsm(p) => p.pin(path),
Program::BtfTracePoint(p) => p.data.pin(path), Program::BtfTracePoint(p) => p.pin(path),
Program::FEntry(p) => p.data.pin(path), Program::FEntry(p) => p.pin(path),
Program::FExit(p) => p.data.pin(path), Program::FExit(p) => p.pin(path),
Program::Extension(p) => p.data.pin(path), Program::Extension(p) => p.pin(path),
Program::CgroupSockAddr(p) => p.data.pin(path), Program::CgroupSockAddr(p) => p.pin(path),
Program::SkLookup(p) => p.data.pin(path), Program::SkLookup(p) => p.pin(path),
Program::CgroupSock(p) => p.data.pin(path), Program::CgroupSock(p) => p.pin(path),
} }
} }
@ -441,23 +435,6 @@ impl<T: Link> ProgramData<T> {
self.fd.ok_or(ProgramError::NotLoaded) self.fd.ok_or(ProgramError::NotLoaded)
} }
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(())
}
pub(crate) fn take_link(&mut self, link_id: T::Id) -> Result<T, ProgramError> { pub(crate) fn take_link(&mut self, link_id: T::Id) -> Result<T, ProgramError> {
self.links.forget(link_id) self.links.forget(link_id)
} }
@ -472,6 +449,29 @@ fn unload_program<T: Link>(data: &mut ProgramData<T>) -> Result<(), ProgramError
Ok(()) Ok(())
} }
fn pin_program<T: Link, P: AsRef<Path>>(
data: &mut ProgramData<T>,
path: P,
) -> Result<(), PinError> {
let fd = data.fd.ok_or(PinError::NoFd {
name: data
.name
.as_ref()
.unwrap_or(&"<unknown program>".to_string())
.to_string(),
})?;
let path_string = CString::new(path.as_ref().to_string_lossy().into_owned()).map_err(|e| {
PinError::InvalidPinPath {
error: e.to_string(),
}
})?;
bpf_pin_object(fd, &path_string).map_err(|(_, io_error)| PinError::SyscallError {
name: "BPF_OBJ_PIN".to_string(),
io_error,
})?;
Ok(())
}
fn load_program<T: Link>( fn load_program<T: Link>(
prog_type: bpf_prog_type, prog_type: bpf_prog_type,
data: &mut ProgramData<T>, data: &mut ProgramData<T>,
@ -673,6 +673,50 @@ impl_fd!(
CgroupSock, CgroupSock,
); );
macro_rules! impl_program_pin{
($($struct_name:ident),+ $(,)?) => {
$(
impl $struct_name {
/// Pins the program to a BPF filesystem.
///
/// When a BPF object is pinned to a BPF filesystem it will remain loaded after
/// Aya has unloaded the program.
/// To remove the program, the file on the BPF filesystem must be removed.
/// Any directories in the the path provided should have been created by the caller.
pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError> {
pin_program(&mut self.data, path)
}
}
)+
}
}
impl_program_pin!(
KProbe,
UProbe,
TracePoint,
SocketFilter,
Xdp,
SkMsg,
SkSkb,
SchedClassifier,
CgroupSkb,
CgroupSysctl,
CgroupSockopt,
LircMode2,
PerfEvent,
Lsm,
RawTracePoint,
BtfTracePoint,
FEntry,
FExit,
Extension,
CgroupSockAddr,
SkLookup,
SockOps,
CgroupSock,
);
macro_rules! impl_try_from_program { macro_rules! impl_try_from_program {
($($ty:ident),+ $(,)?) => { ($($ty:ident),+ $(,)?) => {
$( $(
@ -770,17 +814,10 @@ impl ProgramInfo {
/// Loads a program from a pinned path in bpffs. /// Loads a program from a pinned path in bpffs.
pub fn from_pinned<P: AsRef<Path>>(path: P) -> Result<ProgramInfo, ProgramError> { pub fn from_pinned<P: AsRef<Path>>(path: P) -> Result<ProgramInfo, ProgramError> {
let path_string = match CString::new(path.as_ref().to_str().unwrap()) { let path_string = CString::new(path.as_ref().to_str().unwrap()).unwrap();
Ok(s) => s,
Err(e) => {
return Err(ProgramError::InvalidPinPath {
error: e.to_string(),
})
}
};
let fd = let fd =
bpf_get_object(&path_string).map_err(|(_, io_error)| ProgramError::SyscallError { bpf_get_object(&path_string).map_err(|(_, io_error)| ProgramError::SyscallError {
call: "bpf_obj_get".to_owned(), call: "BPF_OBJ_GET".to_owned(),
io_error, io_error,
})? as RawFd; })? as RawFd;
@ -788,7 +825,6 @@ impl ProgramInfo {
call: "bpf_obj_get_info_by_fd".to_owned(), call: "bpf_obj_get_info_by_fd".to_owned(),
io_error, io_error,
})?; })?;
unsafe { unsafe {
libc::close(fd); libc::close(fd);
} }

Loading…
Cancel
Save