diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 621b2e33..26ca2a42 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -400,7 +400,10 @@ impl<'a> BpfLoader<'a> { } Err(_) => { let fd = map.create(&name)?; - map.pin(&name, path)?; + map.pin(&name, path).map_err(|error| MapError::PinError { + name: name.to_string(), + error, + })?; fd } } @@ -774,12 +777,19 @@ impl Bpf { /// # Examples /// ```no_run /// # 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 pin_path = Path::new("/tmp/pin_path"); /// for (_, program) in bpf.programs_mut() { /// program.pin(pin_path)?; /// } - /// # Ok::<(), aya::BpfError>(()) + /// # Ok::<(), Error>(()) /// ``` pub fn programs_mut(&mut self) -> impl Iterator { self.programs.iter_mut().map(|(s, p)| (s.as_str(), p)) diff --git a/aya/src/lib.rs b/aya/src/lib.rs index 9d31efea..6666f20d 100644 --- a/aya/src/lib.rs +++ b/aya/src/lib.rs @@ -48,6 +48,7 @@ mod bpf; mod generated; pub mod maps; mod obj; +pub mod pin; pub mod programs; mod sys; pub mod util; diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index e0e8d066..a251a8b3 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -51,6 +51,7 @@ use thiserror::Error; use crate::{ generated::bpf_map_type, obj, + pin::PinError, sys::{bpf_create_map, bpf_get_object, bpf_map_get_next_key, bpf_pin_object, kernel_version}, util::nr_cpus, Pod, @@ -119,13 +120,6 @@ pub enum MapError { 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 #[error("failed to create map `{name}` with code {code}")] CreateError { @@ -138,18 +132,6 @@ pub enum MapError { 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 #[error("invalid key size {size}, expected {expected}")] InvalidKeySize { @@ -214,6 +196,16 @@ pub enum MapError { /// Map name 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. @@ -316,11 +308,12 @@ impl Map { }) } }; - let fd = bpf_get_object(&path_string).map_err(|(code, io_error)| MapError::PinError { - name: name.into(), - code, - io_error, - })? as RawFd; + let fd = + bpf_get_object(&path_string).map_err(|(code, io_error)| MapError::SyscallError { + call: "BPF_OBJ_GET".to_string(), + code, + io_error, + })? as RawFd; self.fd = Some(fd); @@ -336,20 +329,21 @@ impl Map { self.fd.ok_or(MapError::NotCreated) } - pub(crate) fn pin>(&mut self, name: &str, path: P) -> Result<(), MapError> { + pub(crate) fn pin>(&mut self, name: &str, path: P) -> Result<(), PinError> { 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 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| { - MapError::InvalidPinPath { + PinError::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, + bpf_pin_object(fd, &path_string).map_err(|(_, io_error)| PinError::SyscallError { + name: "BPF_OBJ_GET".to_string(), io_error, })?; self.pinned = true; diff --git a/aya/src/pin.rs b/aya/src/pin.rs new file mode 100644 index 00000000..6bb9d5a9 --- /dev/null +++ b/aya/src/pin.rs @@ -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, + }, +} diff --git a/aya/src/programs/links.rs b/aya/src/programs/links.rs index b3ee0e06..c16edefc 100644 --- a/aya/src/programs/links.rs +++ b/aya/src/programs/links.rs @@ -4,11 +4,18 @@ use libc::{close, dup}; use std::{ borrow::Borrow, collections::{hash_map::Entry, HashMap}, + ffi::CString, ops::Deref, 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. pub trait Link: std::fmt::Debug + 'static { @@ -111,6 +118,26 @@ impl FdLink { pub(crate) fn new(fd: RawFd) -> FdLink { 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>(&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 { diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index b2f3fe7a..ad1ec20b 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -106,6 +106,7 @@ use crate::{ generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type}, maps::MapError, obj::{self, btf::BtfError, Function, KernelVersion}, + pin::PinError, sys::{ 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, @@ -163,13 +164,6 @@ pub enum ProgramError { #[error("unexpected program type")] 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. #[error(transparent)] MapError(#[from] MapError), @@ -307,31 +301,31 @@ impl Program { } /// Pin the program to the provided path - pub fn pin>(&mut self, path: P) -> Result<(), ProgramError> { + pub fn pin>(&mut self, path: P) -> Result<(), PinError> { match self { - Program::KProbe(p) => p.data.pin(path), - Program::UProbe(p) => p.data.pin(path), - Program::TracePoint(p) => p.data.pin(path), - Program::SocketFilter(p) => p.data.pin(path), - Program::Xdp(p) => p.data.pin(path), - Program::SkMsg(p) => p.data.pin(path), - Program::SkSkb(p) => p.data.pin(path), - Program::SockOps(p) => p.data.pin(path), - Program::SchedClassifier(p) => p.data.pin(path), - Program::CgroupSkb(p) => p.data.pin(path), - Program::CgroupSysctl(p) => p.data.pin(path), - Program::CgroupSockopt(p) => p.data.pin(path), - Program::LircMode2(p) => p.data.pin(path), - Program::PerfEvent(p) => p.data.pin(path), - Program::RawTracePoint(p) => p.data.pin(path), - Program::Lsm(p) => p.data.pin(path), - Program::BtfTracePoint(p) => p.data.pin(path), - Program::FEntry(p) => p.data.pin(path), - Program::FExit(p) => p.data.pin(path), - Program::Extension(p) => p.data.pin(path), - Program::CgroupSockAddr(p) => p.data.pin(path), - Program::SkLookup(p) => p.data.pin(path), - Program::CgroupSock(p) => p.data.pin(path), + Program::KProbe(p) => p.pin(path), + Program::UProbe(p) => p.pin(path), + Program::TracePoint(p) => p.pin(path), + Program::SocketFilter(p) => p.pin(path), + Program::Xdp(p) => p.pin(path), + Program::SkMsg(p) => p.pin(path), + Program::SkSkb(p) => p.pin(path), + Program::SockOps(p) => p.pin(path), + Program::SchedClassifier(p) => p.pin(path), + Program::CgroupSkb(p) => p.pin(path), + Program::CgroupSysctl(p) => p.pin(path), + Program::CgroupSockopt(p) => p.pin(path), + Program::LircMode2(p) => p.pin(path), + Program::PerfEvent(p) => p.pin(path), + Program::RawTracePoint(p) => p.pin(path), + Program::Lsm(p) => p.pin(path), + Program::BtfTracePoint(p) => p.pin(path), + Program::FEntry(p) => p.pin(path), + Program::FExit(p) => p.pin(path), + Program::Extension(p) => p.pin(path), + Program::CgroupSockAddr(p) => p.pin(path), + Program::SkLookup(p) => p.pin(path), + Program::CgroupSock(p) => p.pin(path), } } @@ -441,23 +435,6 @@ impl ProgramData { self.fd.ok_or(ProgramError::NotLoaded) } - 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(()) - } - pub(crate) fn take_link(&mut self, link_id: T::Id) -> Result { self.links.forget(link_id) } @@ -472,6 +449,29 @@ fn unload_program(data: &mut ProgramData) -> Result<(), ProgramError Ok(()) } +fn pin_program>( + data: &mut ProgramData, + path: P, +) -> Result<(), PinError> { + let fd = data.fd.ok_or(PinError::NoFd { + name: data + .name + .as_ref() + .unwrap_or(&"".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( prog_type: bpf_prog_type, data: &mut ProgramData, @@ -673,6 +673,50 @@ impl_fd!( 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>(&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 { ($($ty:ident),+ $(,)?) => { $( @@ -770,17 +814,10 @@ impl ProgramInfo { /// Loads a program from a pinned path in bpffs. pub fn from_pinned>(path: P) -> Result { - let path_string = match CString::new(path.as_ref().to_str().unwrap()) { - Ok(s) => s, - Err(e) => { - return Err(ProgramError::InvalidPinPath { - error: e.to_string(), - }) - } - }; + let path_string = CString::new(path.as_ref().to_str().unwrap()).unwrap(); let fd = 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, })? as RawFd; @@ -788,7 +825,6 @@ impl ProgramInfo { call: "bpf_obj_get_info_by_fd".to_owned(), io_error, })?; - unsafe { libc::close(fd); }