pull/1061/merge
Dave Tucker committed by GitHub
commit 1d8cc984ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -60,11 +60,12 @@ use std::{
const MAP_NAME: &str = "AYA_LOGS";
use aya::{
errors::{MapError, PerfBufferError, ProgramError},
maps::{
perf::{AsyncPerfEventArray, Events, PerfBufferError},
Map, MapData, MapError, MapInfo,
perf::{AsyncPerfEventArray, Events},
Map, MapData, MapInfo,
},
programs::{loaded_programs, ProgramError},
programs::loaded_programs,
util::online_cpus,
Ebpf, Pod,
};

@ -1,5 +1,6 @@
use alloc::{
borrow::{Cow, ToOwned as _},
boxed::Box,
format,
string::String,
vec,
@ -157,6 +158,10 @@ pub enum BtfError {
/// unable to get symbol name
#[error("Unable to get symbol name")]
InvalidSymbolName,
/// an irrecoverable error occurred
#[error(transparent)]
Other(#[from] Box<dyn std::error::Error>),
}
/// Available BTF features
@ -889,7 +894,7 @@ impl BtfExt {
);
Ok((name, info))
})
.collect::<Result<HashMap<_, _>, _>>()?,
.collect::<Result<HashMap<_, _>, BtfError>>()?,
);
let line_info_rec_size = ext.line_info_rec_size;
@ -910,7 +915,7 @@ impl BtfExt {
);
Ok((name, info))
})
.collect::<Result<HashMap<_, _>, _>>()?,
.collect::<Result<HashMap<_, _>, BtfError>>()?,
);
let rec_size = ext.core_relo_rec_size;
@ -925,7 +930,7 @@ impl BtfExt {
.collect::<Result<Vec<_>, _>>()?;
Ok((sec.name_offset, relos))
})
.collect::<Result<Vec<_>, _>>()?,
.collect::<Result<Vec<_>, BtfError>>()?,
);
Ok(ext)

@ -1,7 +1,7 @@
use std::{
borrow::Cow,
collections::{HashMap, HashSet},
fs, io,
fs,
os::{
fd::{AsFd as _, AsRawFd as _},
raw::c_int,
@ -11,29 +11,28 @@ use std::{
};
use aya_obj::{
btf::{BtfFeatures, BtfRelocationError},
btf::BtfFeatures,
generated::{BPF_F_SLEEPABLE, BPF_F_XDP_HAS_FRAGS},
relocation::EbpfRelocationError,
EbpfSectionKind, Features,
};
use log::{debug, warn};
use thiserror::Error;
use crate::{
errors::{EbpfError, InternalMapError, MapError, SysError},
generated::{
bpf_map_type::{self, *},
AYA_PERF_EVENT_IOC_DISABLE, AYA_PERF_EVENT_IOC_ENABLE, AYA_PERF_EVENT_IOC_SET_BPF,
},
maps::{Map, MapData, MapError},
maps::{Map, MapData},
obj::{
btf::{Btf, BtfError},
Object, ParseError, ProgramSection,
Object, ProgramSection,
},
programs::{
BtfTracePoint, CgroupDevice, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr,
CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, KProbe, LircMode2, Lsm, PerfEvent,
ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkLookup,
SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
ProbeKind, Program, ProgramData, RawTracePoint, SchedClassifier, SkLookup, SkMsg, SkSkb,
SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
},
sys::{
bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported,
@ -395,11 +394,15 @@ impl<'a> EbpfLoader<'a> {
verifier_log_level,
allow_unsupported_maps,
} = self;
let mut obj = Object::parse(data)?;
obj.patch_map_data(globals.clone())?;
let mut obj = Object::parse(data).map_err(|e| EbpfError::Other(e.into()))?;
obj.patch_map_data(globals.clone())
.map_err(|e| EbpfError::Other(e.into()))?;
let btf_fd = if let Some(features) = &FEATURES.btf() {
if let Some(btf) = obj.fixup_and_sanitize_btf(features)? {
if let Some(btf) = obj
.fixup_and_sanitize_btf(features)
.map_err(|e| EbpfError::Other(e.into()))?
{
match load_btf(btf.to_bytes(), *verifier_log_level) {
Ok(btf_fd) => Some(Arc::new(btf_fd)),
// Only report an error here if the BTF is truly needed, otherwise proceed without.
@ -411,7 +414,7 @@ impl<'a> EbpfLoader<'a> {
| ProgramSection::FExit { sleepable: _ }
| ProgramSection::Lsm { sleepable: _ }
| ProgramSection::BtfTracePoint => {
return Err(EbpfError::BtfError(err))
return Err(EbpfError::Other(err.into()));
}
ProgramSection::KRetProbe
| ProgramSection::KProbe
@ -456,7 +459,8 @@ impl<'a> EbpfLoader<'a> {
};
if let Some(btf) = &btf {
obj.relocate_btf(btf)?;
obj.relocate_btf(btf)
.map_err(|e| EbpfError::Other(e.into()))?;
}
let mut maps = HashMap::new();
for (name, mut obj) in obj.maps.drain() {
@ -471,7 +475,10 @@ impl<'a> EbpfLoader<'a> {
error,
})? as u32)
};
let map_type: bpf_map_type = obj.map_type().try_into().map_err(MapError::from)?;
let map_type: bpf_map_type = obj
.map_type()
.try_into()
.map_err(|e| EbpfError::MapError(MapError::from(e)))?;
if let Some(max_entries) = max_entries_override(
map_type,
max_entries.get(name.as_str()).copied(),
@ -492,7 +499,9 @@ impl<'a> EbpfLoader<'a> {
}
let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd());
let mut map = match obj.pinning() {
PinningType::None => MapData::create(obj, &name, btf_fd)?,
PinningType::None => {
MapData::create(obj, &name, btf_fd).map_err(|e| EbpfError::Other(e.into()))?
}
PinningType::ByName => {
// pin maps in /sys/fs/bpf by default to align with libbpf
// behavior https://github.com/libbpf/libbpf/blob/v1.2.2/src/libbpf.c#L2161.
@ -517,8 +526,10 @@ impl<'a> EbpfLoader<'a> {
maps.iter()
.map(|(s, data)| (s.as_str(), data.fd().as_fd().as_raw_fd(), data.obj())),
&text_sections,
)?;
obj.relocate_calls(&text_sections)?;
)
.map_err(|e| EbpfError::Other(e.into()))?;
obj.relocate_calls(&text_sections)
.map_err(|e| EbpfError::Other(e.into()))?;
obj.sanitize_functions(&FEATURES);
let programs = obj
@ -699,10 +710,10 @@ impl<'a> EbpfLoader<'a> {
.collect::<Result<HashMap<String, Map>, EbpfError>>()?;
if !*allow_unsupported_maps {
maps.iter().try_for_each(|(_, x)| match x {
Map::Unsupported(map) => Err(EbpfError::MapError(MapError::Unsupported {
map_type: map.obj().map_type(),
})),
maps.iter().try_for_each(|(name, x)| match x {
Map::Unsupported(_) => Err(EbpfError::Other(Box::new(
InternalMapError::Unsupported(name.clone()),
))),
_ => Ok(()),
})?;
};
@ -713,7 +724,8 @@ impl<'a> EbpfLoader<'a> {
fn parse_map(data: (String, MapData)) -> Result<(String, Map), EbpfError> {
let (name, map) = data;
let map_type = bpf_map_type::try_from(map.obj().map_type()).map_err(MapError::from)?;
let map_type = bpf_map_type::try_from(map.obj().map_type())
.map_err(|e| EbpfError::Other(Box::new(MapError::from(e))))?;
let map = match map_type {
BPF_MAP_TYPE_ARRAY => Map::Array(map),
BPF_MAP_TYPE_PERCPU_ARRAY => Map::PerCpuArray(map),
@ -1068,55 +1080,6 @@ impl Ebpf {
}
}
/// The error type returned by [`Ebpf::load_file`] and [`Ebpf::load`].
#[derive(Debug, Error)]
pub enum EbpfError {
/// Error loading file
#[error("error loading {path}")]
FileError {
/// The file path
path: PathBuf,
#[source]
/// The original io::Error
error: io::Error,
},
/// Unexpected pinning type
#[error("unexpected pinning type {name}")]
UnexpectedPinningType {
/// The value encountered
name: u32,
},
/// Error parsing BPF object
#[error("error parsing BPF object: {0}")]
ParseError(#[from] ParseError),
/// Error parsing BTF object
#[error("BTF error: {0}")]
BtfError(#[from] BtfError),
/// Error performing relocations
#[error("error relocating function")]
RelocationError(#[from] EbpfRelocationError),
/// Error performing relocations
#[error("error relocating section")]
BtfRelocationError(#[from] BtfRelocationError),
/// No BTF parsed for object
#[error("no BTF parsed for object")]
NoBTF,
#[error("map error: {0}")]
/// A map error
MapError(#[from] MapError),
#[error("program error: {0}")]
/// A program error
ProgramError(#[from] ProgramError),
}
/// The error type returned by [`Bpf::load_file`] and [`Bpf::load`].
#[deprecated(since = "0.13.0", note = "use `EbpfError` instead")]
pub type BpfError = EbpfError;
@ -1128,10 +1091,14 @@ fn load_btf(
let (ret, verifier_log) = retry_with_verifier_logs(10, |logger| {
bpf_load_btf(raw_btf.as_slice(), logger, verifier_log_level)
});
ret.map_err(|(_, io_error)| BtfError::LoadError {
io_error,
verifier_log,
})
match ret {
Ok(fd) => Ok(fd),
Err((_, SysError::Syscall { call: _, io_error })) => Err(BtfError::LoadError {
io_error,
verifier_log,
}),
Err((_, e)) => Err(BtfError::Other(Box::new(e))),
}
}
/// Global data that can be exported to eBPF programs before they are loaded.

@ -0,0 +1,535 @@
//! Aya Error types.
use std::{error::Error, ffi::NulError, io, path::PathBuf};
use aya_obj::{
btf::{BtfError, BtfRelocationError},
relocation::EbpfRelocationError,
InvalidTypeBinding, ParseError, VerifierLog,
};
use thiserror::Error;
/// The error type returned by [`Ebpf::load_file`] and [`Ebpf::load`].
#[derive(Debug, Error)]
#[cfg_attr(not(test), non_exhaustive)]
pub enum EbpfError {
/// Error loading file.
#[error("error loading {path}")]
FileError {
/// The file path.
path: PathBuf,
#[source]
/// The original [`io::Error`].
error: io::Error,
},
#[error("map error: {0}")]
/// A map error.
MapError(#[from] MapError),
#[error("program error: {0}")]
/// A program error.
ProgramError(#[from] ProgramError),
/// An irrecoverable error occurred.
#[error(transparent)]
Other(#[from] Box<dyn Error>),
}
#[derive(Debug, Error)]
pub(crate) enum EbpfInternalError {
#[error("error parsing BPF object: {0}")]
ParseError(#[from] ParseError),
#[error("BTF error: {0}")]
BtfError(#[from] BtfError),
#[error("error relocating function")]
RelocationError(#[from] EbpfRelocationError),
#[error("error relocating section")]
BtfRelocationError(#[from] BtfRelocationError),
#[error("no BTF parsed for object")]
NoBTF,
}
/// Error type returned when working with programs.
#[derive(Debug, Error)]
pub enum ProgramError {
/// The program is already loaded.
#[error("the program is already loaded")]
AlreadyLoaded,
/// The program is not loaded.
#[error("the program is not loaded")]
NotLoaded,
/// Loading the program failed.
#[error("the BPF_PROG_LOAD syscall failed. Verifier output: {verifier_log}")]
LoadError {
/// The [`SysError`] returned by the `BPF_PROG_LOAD` syscall.
#[source]
source: SysError,
/// The error log produced by the kernel verifier.
verifier_log: VerifierLog,
},
/// The program is not of the expected type.
#[error("unexpected program type")]
UnexpectedProgramType,
/// A syscall failed.
#[error("syscall failed")]
Syscall(#[from] SysError),
/// An irrecoverable error occurred.
#[error(transparent)]
Other(#[from] Box<dyn Error>),
}
impl From<EbpfInternalError> for ProgramError {
fn from(e: EbpfInternalError) -> Self {
Self::Other(Box::new(e))
}
}
impl From<(i64, SysError)> for ProgramError {
fn from((_, error): (i64, SysError)) -> Self {
error.into()
}
}
impl From<io::Error> for ProgramError {
fn from(e: io::Error) -> Self {
Self::Other(Box::new(e))
}
}
impl From<BtfError> for ProgramError {
fn from(e: BtfError) -> Self {
Self::Other(Box::new(e))
}
}
impl From<LinkError> for ProgramError {
fn from(e: LinkError) -> Self {
Self::Other(Box::new(e))
}
}
impl From<NulError> for ProgramError {
fn from(e: NulError) -> Self {
Self::Other(Box::new(e))
}
}
/// An error ocurred working with a pinned BPF object.
#[derive(Error, Debug)]
pub(crate) enum PinError {
/// The path for the BPF object is not valid.
#[error("invalid pin path `{}`", path.display())]
InvalidPinPath {
/// The path.
path: std::path::PathBuf,
#[source]
/// The source error.
error: std::ffi::NulError,
},
/// An irrecoverable error occurred.
#[error(transparent)]
Other(#[from] Box<dyn Error>),
}
impl From<InternalMapError> for MapError {
fn from(e: InternalMapError) -> Self {
Self::Other(Box::new(e))
}
}
/// Errors from operations on maps.
#[derive(Error, Debug)]
pub enum MapError {
/// Key not found
#[error("key not found")]
KeyNotFound,
/// Element not found
#[error("element not found")]
ElementNotFound,
/// Index is out of bounds
#[error("the index is {index} but `max_entries` is {max_entries}")]
OutOfBounds {
/// Index accessed
index: u32,
/// Map size
max_entries: u32,
},
/// Chained programs are not supported.
#[error("chained programs are not supported by the current kernel")]
ChainedProgramNotSupported,
/// Invalid map type encontered
#[error("invalid map type {map_type}")]
InvalidMapType {
/// The map type
map_type: u32,
},
/// A syscall failed.
#[error(transparent)]
Syscall(#[from] SysError),
/// An internal and irrecoverable error occurred.
#[error(transparent)]
Other(#[from] Box<dyn Error>),
}
impl From<(i64, SysError)> for MapError {
fn from((_, error): (i64, SysError)) -> Self {
error.into()
}
}
impl From<io::Error> for MapError {
fn from(e: io::Error) -> Self {
Self::Other(Box::new(e))
}
}
impl From<NulError> for MapError {
fn from(e: NulError) -> Self {
Self::Other(Box::new(e))
}
}
impl From<PinError> for MapError {
fn from(e: PinError) -> Self {
Self::Other(Box::new(e))
}
}
#[derive(Error, Debug)]
/// Errors occuring from working with Maps
pub(crate) enum InternalMapError {
/// Invalid map name encountered
#[error("invalid map name `{name}`")]
InvalidName {
/// The map name
name: String,
},
/// Failed to create map
#[error("failed to create map `{name}` with code {code}")]
CreateError {
/// Map name
name: String,
/// Error code
code: i64,
#[source]
/// Original io::Error
source: SysError,
},
/// Invalid key size
#[error("invalid key size {size}, expected {expected}")]
InvalidKeySize {
/// Size encountered
size: usize,
/// Size expected
expected: usize,
},
/// Invalid value size
#[error("invalid value size {size}, expected {expected}")]
InvalidValueSize {
/// Size encountered
size: usize,
/// Size expected
expected: usize,
},
/// An IO error occurred
#[error(transparent)]
IoError(#[from] io::Error),
/// Syscall failed
#[error(transparent)]
SysError(#[from] SysError),
/// Map type not supported
#[error("map {0}: type is not supported")]
Unsupported(String),
}
impl From<InvalidTypeBinding<u32>> for MapError {
fn from(e: InvalidTypeBinding<u32>) -> Self {
let InvalidTypeBinding { value } = e;
Self::InvalidMapType { map_type: value }
}
}
/// Perf buffer error.
#[derive(Error, Debug)]
pub enum PerfBufferError {
/// the page count value passed to [`PerfEventArray::open`](crate::maps::PerfEventArray::open) is invalid.
#[error("invalid page count {page_count}, the value must be a power of two")]
InvalidPageCount {
/// the page count
page_count: usize,
},
/// `perf_event_open` failed.
#[error("perf_event_open failed: {io_error}")]
OpenError {
/// the source of this error
#[source]
io_error: io::Error,
},
/// `mmap`-ping the buffer failed.
#[error("mmap failed: {io_error}")]
MMapError {
/// the source of this error
#[source]
io_error: io::Error,
},
/// The `PERF_EVENT_IOC_ENABLE` ioctl failed
#[error("PERF_EVENT_IOC_ENABLE failed: {io_error}")]
PerfEventEnableError {
#[source]
/// the source of this error
io_error: io::Error,
},
/// `read_events()` was called with no output buffers.
#[error("read_events() was called with no output buffers")]
NoBuffers,
/// `read_events()` was called with a buffer that is not large enough to
/// contain the next event in the perf buffer.
#[deprecated(
since = "0.10.8",
note = "read_events() now calls BytesMut::reserve() internally, so this error is never returned"
)]
#[error("the buffer needs to be of at least {size} bytes")]
MoreSpaceNeeded {
/// expected size
size: usize,
},
/// An IO error occurred.
#[error(transparent)]
IOError(#[from] io::Error),
/// An irrecoverable error occurred.
#[error(transparent)]
Other(#[from] Box<dyn Error>),
}
impl From<SysError> for PerfBufferError {
fn from(e: SysError) -> Self {
match e {
SysError::Syscall { call: _, io_error } => Self::Other(Box::new(io_error)),
SysError::Mmap { io_error } => Self::MMapError { io_error },
_ => Self::Other(Box::new(e)),
}
}
}
impl From<(i64, SysError)> for PerfBufferError {
fn from((_, error): (i64, SysError)) -> Self {
error.into()
}
}
#[derive(Error, Debug)]
/// Errors from operations on links.
pub enum LinkError {
/// Invalid link.
#[error("Invalid link")]
InvalidLink,
/// The program is not attached.
#[error("the program is not attached")]
NotAttached,
/// The program is already attached.
#[error("the program is already attached")]
AlreadyAttached,
/// A file error.
#[error("file error: {path}")]
FileError {
/// The file path.
path: PathBuf,
/// The original [`io::Error`].
#[source]
error: io::Error,
},
/// An irrecoverable error occurred.
#[error(transparent)]
Other(#[from] Box<dyn Error>),
}
impl From<(PathBuf, io::Error)> for LinkError {
fn from((path, error): (PathBuf, io::Error)) -> Self {
Self::FileError { path, error }
}
}
impl From<SysError> for LinkError {
fn from(e: SysError) -> Self {
match e {
SysError::Syscall { call: _, io_error } => Self::Other(Box::new(io_error)),
_ => Self::Other(Box::new(e)),
}
}
}
impl From<(i64, SysError)> for LinkError {
fn from((_, error): (i64, SysError)) -> Self {
error.into()
}
}
impl From<ProgramError> for LinkError {
fn from(e: ProgramError) -> Self {
Self::Other(Box::new(e))
}
}
impl From<io::Error> for LinkError {
fn from(e: io::Error) -> Self {
Self::Other(Box::new(e))
}
}
impl From<NulError> for LinkError {
fn from(e: NulError) -> Self {
Self::Other(Box::new(e))
}
}
impl From<std::num::ParseIntError> for LinkError {
fn from(e: std::num::ParseIntError) -> Self {
Self::Other(Box::new(e))
}
}
impl From<ResolveSymbolError> for LinkError {
fn from(e: ResolveSymbolError) -> Self {
Self::Other(Box::new(e))
}
}
#[derive(Error, Debug)]
pub(crate) enum InternalLinkError {
#[error("the target program is not loaded")]
TargetProgramNotLoaded,
#[error("target program does not have BTF")]
TargetNoBtf,
#[error("error reading ld.so.cache file")]
LdSoCache {
#[source]
io_error: &'static io::Error,
},
#[error("could not resolve uprobe target `{path}`")]
InvalidTarget {
/// path to target
path: PathBuf,
},
/// netlink error while attaching XDP program
#[error("Netlink error")]
NetlinkError {
/// the [`io::Error`] from the netlink call
#[source]
io_error: io::Error,
},
/// operation not supported for programs loaded via tcx
#[error("operation not supported for programs loaded via tcx")]
InvalidLinkOperation,
/// tcx links can only be attached to ingress or egress, custom attachment is not supported
#[error("tcx links can only be attached to ingress or egress, custom attachment: {0} is not supported")]
InvalidTcxAttach(u32),
/// The network interface does not exist.
#[error("unknown network interface {name}")]
UnknownInterface {
/// interface name
name: String,
},
/// Setting the `SO_ATTACH_BPF` socket option failed.
#[error("setsockopt SO_ATTACH_BPF failed")]
SoAttachEbpf {
/// original [`io::Error`]
#[source]
io_error: io::Error,
},
}
impl From<InternalLinkError> for LinkError {
fn from(e: InternalLinkError) -> Self {
Self::Other(Box::new(e))
}
}
#[derive(Error, Debug)]
pub(crate) enum ResolveSymbolError {
#[error(transparent)]
Io(#[from] io::Error),
#[error("error parsing ELF")]
Object(#[from] object::Error),
#[error("unknown symbol `{0}`")]
Unknown(String),
#[error("symbol `{0}` does not appear in section")]
NotInSection(String),
#[error("symbol `{0}` in section `{1:?}` which has no offset")]
SectionFileRangeNone(String, Result<String, object::Error>),
#[error("failed to access debuglink file `{0}`: `{1}`")]
DebuglinkAccessError(String, io::Error),
#[error("symbol `{0}` not found, mismatched build IDs in main and debug files")]
BuildIdMismatch(String),
}
/// Errors from Syscalls.
#[derive(Debug, Error)]
pub enum SysError {
/// A syscall failed.
#[error("{call} failed")]
Syscall {
/// The name of the syscall which failed.
call: String,
/// The [`io::Error`] returned by the syscall.
#[source]
io_error: io::Error,
},
/// A mmap operation failed.
#[error("mmap failed")]
Mmap {
/// The [`io::Error`] returned by the mmap operation.
#[source]
io_error: io::Error,
},
/// An irecoverable error occurred.
#[error(transparent)]
Other(#[from] Box<dyn Error>),
}

@ -78,8 +78,8 @@
)]
mod bpf;
pub mod errors;
pub mod maps;
pub mod pin;
pub mod programs;
pub mod sys;
pub mod util;

@ -5,8 +5,9 @@ use std::{
};
use crate::{
maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError},
sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError},
errors::MapError,
maps::{check_bounds, check_kv_size, IterableMap, MapData},
sys::{bpf_map_lookup_elem, bpf_map_update_elem},
Pod,
};
@ -64,11 +65,7 @@ impl<T: Borrow<MapData>, V: Pod> Array<T, V> {
check_bounds(data, *index)?;
let fd = data.fd().as_fd();
let value =
bpf_map_lookup_elem(fd, index, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?;
let value = bpf_map_lookup_elem(fd, index, flags)?;
value.ok_or(MapError::KeyNotFound)
}
@ -90,12 +87,7 @@ impl<T: BorrowMut<MapData>, V: Pod> Array<T, V> {
let data = self.inner.borrow_mut();
check_bounds(data, index)?;
let fd = data.fd().as_fd();
bpf_map_update_elem(fd, Some(&index), value.borrow(), flags).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_update_elem",
io_error,
}
})?;
bpf_map_update_elem(fd, Some(&index), value.borrow(), flags)?;
Ok(())
}
}

@ -5,8 +5,9 @@ use std::{
};
use crate::{
maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError, PerCpuValues},
sys::{bpf_map_lookup_elem_per_cpu, bpf_map_update_elem_per_cpu, SyscallError},
errors::MapError,
maps::{check_bounds, check_kv_size, IterableMap, MapData, PerCpuValues},
sys::{bpf_map_lookup_elem_per_cpu, bpf_map_update_elem_per_cpu},
Pod,
};
@ -83,12 +84,7 @@ impl<T: Borrow<MapData>, V: Pod> PerCpuArray<T, V> {
check_bounds(data, *index)?;
let fd = data.fd().as_fd();
let value = bpf_map_lookup_elem_per_cpu(fd, index, flags).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_lookup_elem",
io_error,
}
})?;
let value = bpf_map_lookup_elem_per_cpu(fd, index, flags)?;
value.ok_or(MapError::KeyNotFound)
}
@ -104,19 +100,14 @@ impl<T: BorrowMut<MapData>, V: Pod> PerCpuArray<T, V> {
///
/// # Errors
///
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
/// if `bpf_map_update_elem` fails.
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::Other`]
/// if `bpf_map_update_elem` fails for any other reason.
pub fn set(&mut self, index: u32, values: PerCpuValues<V>, flags: u64) -> Result<(), MapError> {
let data = self.inner.borrow_mut();
check_bounds(data, index)?;
let fd = data.fd().as_fd();
bpf_map_update_elem_per_cpu(fd, &index, &values, flags).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_update_elem",
io_error,
}
})?;
bpf_map_update_elem_per_cpu(fd, &index, &values, flags)?;
Ok(())
}
}

@ -6,9 +6,10 @@ use std::{
};
use crate::{
maps::{check_bounds, check_kv_size, MapData, MapError, MapKeys},
errors::MapError,
maps::{check_bounds, check_kv_size, MapData, MapKeys},
programs::ProgramFd,
sys::{bpf_map_delete_elem, bpf_map_update_elem, SyscallError},
sys::{bpf_map_delete_elem, bpf_map_update_elem},
};
/// An array of eBPF program file descriptors used as a jump table.
@ -78,12 +79,7 @@ impl<T: BorrowMut<MapData>> ProgramArray<T> {
let prog_fd = program.as_fd();
let prog_fd = prog_fd.as_raw_fd();
bpf_map_update_elem(fd, Some(&index), &prog_fd, flags).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_update_elem",
io_error,
}
})?;
bpf_map_update_elem(fd, Some(&index), &prog_fd, flags)?;
Ok(())
}
@ -98,12 +94,6 @@ impl<T: BorrowMut<MapData>> ProgramArray<T> {
bpf_map_delete_elem(fd, index)
.map(|_| ())
.map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_delete_elem",
io_error,
}
.into()
})
.map_err(Into::into)
}
}

@ -6,8 +6,9 @@ use std::{
};
use crate::{
maps::{check_v_size, MapData, MapError},
sys::{bpf_map_lookup_elem_ptr, bpf_map_push_elem, SyscallError},
errors::MapError,
maps::{check_v_size, MapData},
sys::{bpf_map_lookup_elem_ptr, bpf_map_push_elem},
Pod,
};
@ -54,11 +55,7 @@ impl<T: Borrow<MapData>, V: Pod> BloomFilter<T, V> {
pub fn contains(&self, mut value: &V, flags: u64) -> Result<(), MapError> {
let fd = self.inner.borrow().fd().as_fd();
bpf_map_lookup_elem_ptr::<u32, _>(fd, None, &mut value, flags)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?
bpf_map_lookup_elem_ptr::<u32, _>(fd, None, &mut value, flags)?
.ok_or(MapError::ElementNotFound)?;
Ok(())
}
@ -68,10 +65,7 @@ impl<T: BorrowMut<MapData>, V: Pod> BloomFilter<T, V> {
/// Inserts a value into the map.
pub fn insert(&mut self, value: impl Borrow<V>, flags: u64) -> Result<(), MapError> {
let fd = self.inner.borrow_mut().fd().as_fd();
bpf_map_push_elem(fd, value.borrow(), flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_push_elem",
io_error,
})?;
bpf_map_push_elem(fd, value.borrow(), flags)?;
Ok(())
}
}
@ -81,40 +75,60 @@ mod tests {
use std::io;
use assert_matches::assert_matches;
use libc::{EFAULT, ENOENT};
use libc::{EFAULT, EINVAL, ENOENT};
use super::*;
use crate::{
generated::{
bpf_cmd,
bpf_map_type::{BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_BLOOM_FILTER},
},
errors::{InternalMapError, SysError},
generated::bpf_map_type::{BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_BLOOM_FILTER},
maps::{
test_utils::{self, new_map},
Map,
},
obj,
sys::{override_syscall, SysResult, Syscall},
sys::{override_syscall, BpfCmd, SysResult, Syscall},
};
fn new_obj_map() -> obj::Map {
test_utils::new_obj_map::<u32>(BPF_MAP_TYPE_BLOOM_FILTER)
}
fn sys_error(value: i32) -> SysResult<i64> {
Err((-1, io::Error::from_raw_os_error(value)))
fn sys_error(call: Syscall<'_>, value: i32) -> SysResult<i64> {
match call {
Syscall::Ebpf { .. } => Err((
-1,
SysError::Syscall {
call: format!("{:?}", call),
io_error: io::Error::from_raw_os_error(value),
},
)),
_ => Err((
-1,
SysError::Syscall {
call: "UNEXPECTED!!!".to_string(),
io_error: io::Error::from_raw_os_error(EINVAL),
},
)),
}
}
#[test]
fn test_wrong_value_size() {
let map = new_map(new_obj_map());
assert_matches!(
BloomFilter::<_, u16>::new(&map),
Err(MapError::InvalidValueSize {
size: 2,
expected: 4
})
);
let res = BloomFilter::<_, u16>::new(&map);
assert!(res.is_err());
let res = res.unwrap_err();
if let MapError::Other(map_err) = res {
assert_matches!(
map_err.downcast_ref::<InternalMapError>().unwrap(),
InternalMapError::InvalidKeySize {
size: 2,
expected: 4
}
);
} else {
panic!("unexpected error: {:?}", res);
}
}
#[test]
@ -149,12 +163,9 @@ mod tests {
let mut map = new_map(new_obj_map());
let mut bloom_filter = BloomFilter::<_, u32>::new(&mut map).unwrap();
override_syscall(|_| sys_error(EFAULT));
override_syscall(|c| sys_error(c, EFAULT));
assert_matches!(
bloom_filter.insert(1, 0),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_push_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
);
assert!(bloom_filter.insert(1, 0).is_err());
}
#[test]
@ -164,10 +175,10 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
cmd: BpfCmd::MapUpdateElem,
..
} => Ok(1),
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
assert!(bloom_filter.insert(0, 42).is_ok());
@ -178,12 +189,9 @@ mod tests {
let map = new_map(new_obj_map());
let bloom_filter = BloomFilter::<_, u32>::new(&map).unwrap();
override_syscall(|_| sys_error(EFAULT));
override_syscall(|c| sys_error(c, EFAULT));
assert_matches!(
bloom_filter.contains(&1, 0),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_lookup_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
);
assert!(bloom_filter.contains(&1, 0).is_err());
}
#[test]
@ -193,10 +201,10 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
cmd: BpfCmd::MapUpdateElem,
..
} => sys_error(ENOENT),
_ => sys_error(EFAULT),
} => sys_error(call, ENOENT),
c => sys_error(c, EFAULT),
});
assert_matches!(bloom_filter.contains(&1, 0), Err(MapError::ElementNotFound));

@ -5,8 +5,9 @@ use std::{
};
use crate::{
maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys},
sys::{bpf_map_lookup_elem, SyscallError},
errors::MapError,
maps::{check_kv_size, hash_map, IterableMap, MapData, MapIter, MapKeys},
sys::bpf_map_lookup_elem,
Pod,
};
@ -54,10 +55,7 @@ impl<T: Borrow<MapData>, K: Pod, V: Pod> HashMap<T, K, V> {
/// Returns a copy of the value associated with the key.
pub fn get(&self, key: &K, flags: u64) -> Result<V, MapError> {
let fd = self.inner.borrow().fd().as_fd();
let value = bpf_map_lookup_elem(fd, key, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?;
let value = bpf_map_lookup_elem(fd, key, flags)?;
value.ok_or(MapError::KeyNotFound)
}
@ -106,12 +104,13 @@ mod tests {
use std::io;
use assert_matches::assert_matches;
use libc::{EFAULT, ENOENT};
use libc::{EFAULT, EINVAL, ENOENT};
use super::*;
use crate::{
errors::{InternalMapError, SysError},
generated::{
bpf_attr, bpf_cmd,
bpf_attr,
bpf_map_type::{BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_LRU_HASH},
},
maps::{
@ -119,39 +118,68 @@ mod tests {
Map,
},
obj,
sys::{override_syscall, SysResult, Syscall},
sys::{override_syscall, BpfCmd, SysResult, Syscall},
};
fn new_obj_map() -> obj::Map {
test_utils::new_obj_map::<u32>(BPF_MAP_TYPE_HASH)
}
fn sys_error(value: i32) -> SysResult<i64> {
Err((-1, io::Error::from_raw_os_error(value)))
fn sys_error(call: Syscall<'_>, value: i32) -> SysResult<i64> {
match call {
Syscall::Ebpf { .. } => Err((
-1,
SysError::Syscall {
call: format!("{:?}", call),
io_error: io::Error::from_raw_os_error(value),
},
)),
_ => Err((
-1,
SysError::Syscall {
call: "UNEXPECTED!!!".to_string(),
io_error: io::Error::from_raw_os_error(EINVAL),
},
)),
}
}
#[test]
fn test_wrong_key_size() {
let map = new_map(new_obj_map());
assert_matches!(
HashMap::<_, u8, u32>::new(&map),
Err(MapError::InvalidKeySize {
size: 1,
expected: 4
})
);
let res = HashMap::<_, u8, u32>::new(&map);
assert!(res.is_err());
let res = res.unwrap_err();
if let MapError::Other(map_err) = res {
assert_matches!(
map_err.downcast_ref::<InternalMapError>().unwrap(),
InternalMapError::InvalidKeySize {
size: 1,
expected: 4
}
);
} else {
panic!("unexpected error: {:?}", res);
}
}
#[test]
fn test_wrong_value_size() {
let map = new_map(new_obj_map());
assert_matches!(
HashMap::<_, u32, u16>::new(&map),
Err(MapError::InvalidValueSize {
size: 2,
expected: 4
})
);
let res = HashMap::<_, u32, u16>::new(&map);
assert!(res.is_err());
let res = res.unwrap_err();
if let MapError::Other(map_err) = res {
assert_matches!(
map_err.downcast_ref::<InternalMapError>().unwrap(),
InternalMapError::InvalidValueSize {
size: 2,
expected: 4
}
);
} else {
panic!("unexpected error: {:?}", res);
}
}
#[test]
@ -160,7 +188,7 @@ mod tests {
let map = Map::Array(map);
assert_matches!(
HashMap::<_, u8, u32>::try_from(&map),
Err(MapError::InvalidMapType { .. })
Err(MapError::Other(_))
);
}
@ -170,10 +198,7 @@ mod tests {
let map = Map::HashMap(map);
assert_matches!(
HashMap::<_, u32, u16>::try_from(&map),
Err(MapError::InvalidValueSize {
size: 2,
expected: 4
})
Err(MapError::Other(_))
);
}
@ -204,12 +229,9 @@ mod tests {
let mut map = new_map(new_obj_map());
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
override_syscall(|_| sys_error(EFAULT));
override_syscall(|c| sys_error(c, EFAULT));
assert_matches!(
hm.insert(1, 42, 0),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_update_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
);
assert!(hm.insert(1, 42, 0).is_err());
}
#[test]
@ -219,10 +241,10 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
cmd: BpfCmd::MapUpdateElem,
..
} => Ok(1),
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
assert!(hm.insert(1, 42, 0).is_ok());
@ -235,10 +257,10 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
cmd: BpfCmd::MapUpdateElem,
..
} => Ok(1),
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
assert!(hm.insert(Box::new(1), Box::new(42), 0).is_ok());
@ -249,12 +271,9 @@ mod tests {
let mut map = new_map(new_obj_map());
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
override_syscall(|_| sys_error(EFAULT));
override_syscall(|c| sys_error(c, EFAULT));
assert_matches!(
hm.remove(&1),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_delete_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
);
assert!(hm.remove(&1).is_err());
}
#[test]
@ -264,10 +283,10 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_DELETE_ELEM,
cmd: BpfCmd::MapDeleteElem,
..
} => Ok(1),
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
assert!(hm.remove(&1).is_ok());
@ -276,13 +295,19 @@ mod tests {
#[test]
fn test_get_syscall_error() {
let map = new_map(new_obj_map());
override_syscall(|_| sys_error(EFAULT));
override_syscall(|c| sys_error(c, EFAULT));
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
assert_matches!(
hm.get(&1, 0),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_lookup_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
);
let res = hm.get(&1, 0);
assert!(res.is_err());
let res = res.unwrap_err();
if let MapError::Other(map_err) = res {
assert_matches!(
map_err.downcast_ref::<SysError>().unwrap(),
SysError::Syscall { call, io_error } if call == "bpf_map_lookup_elem" && io_error.raw_os_error() == Some(EFAULT)
);
} else {
panic!("unexpected error: {:?}", res);
}
}
#[test]
@ -290,10 +315,10 @@ mod tests {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
cmd: BpfCmd::MapLookupElem,
..
} => sys_error(ENOENT),
_ => sys_error(EFAULT),
} => sys_error(call, ENOENT),
c => sys_error(c, EFAULT),
});
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
@ -322,13 +347,13 @@ mod tests {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
cmd: BpfCmd::MapGetNextKey,
..
} => sys_error(ENOENT),
_ => sys_error(EFAULT),
} => sys_error(call, ENOENT),
c => sys_error(c, EFAULT),
});
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let keys = hm.keys().collect::<Result<Vec<_>, _>>();
let keys = hm.keys().collect::<Result<Vec<u32>, MapError>>();
assert_matches!(keys, Ok(ks) if ks.is_empty())
}
@ -337,8 +362,24 @@ mod tests {
None => set_next_key(attr, 10),
Some(10) => set_next_key(attr, 20),
Some(20) => set_next_key(attr, 30),
Some(30) => return sys_error(ENOENT),
Some(_) => return sys_error(EFAULT),
Some(30) => {
return sys_error(
Syscall::Ebpf {
cmd: BpfCmd::MapGetNextKey,
attr: &mut attr.clone(),
},
ENOENT,
)
}
Some(_) => {
return sys_error(
Syscall::Ebpf {
cmd: BpfCmd::MapGetNextKey,
attr: &mut attr.clone(),
},
EFAULT,
)
}
};
Ok(1)
@ -349,8 +390,24 @@ mod tests {
Some(10) => set_ret(attr, 100),
Some(20) => set_ret(attr, 200),
Some(30) => set_ret(attr, 300),
Some(_) => return sys_error(ENOENT),
None => return sys_error(EFAULT),
Some(_) => {
return sys_error(
Syscall::Ebpf {
cmd: BpfCmd::MapLookupElem,
attr: &mut attr.clone(),
},
ENOENT,
)
}
None => {
return sys_error(
Syscall::Ebpf {
cmd: BpfCmd::MapLookupElem,
attr: &mut attr.clone(),
},
EFAULT,
)
}
};
Ok(1)
@ -362,15 +419,15 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
cmd: BpfCmd::MapGetNextKey,
attr,
} => get_next_key(attr),
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let keys = hm.keys().collect::<Result<Vec<_>, _>>().unwrap();
let keys = hm.keys().collect::<Result<Vec<u32>, MapError>>().unwrap();
assert_eq!(&keys, &[10, 20, 30])
}
@ -379,31 +436,33 @@ mod tests {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
cmd: BpfCmd::MapGetNextKey,
attr,
} => {
match bpf_key(attr) {
None => set_next_key(attr, 10),
Some(10) => set_next_key(attr, 20),
Some(_) => return sys_error(EFAULT),
Some(_) => {
return sys_error(
Syscall::Ebpf {
cmd: BpfCmd::MapGetNextKey,
attr: &mut attr.clone(),
},
EFAULT,
)
}
};
Ok(1)
}
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let mut keys = hm.keys();
assert_matches!(keys.next(), Some(Ok(10)));
assert_matches!(keys.next(), Some(Ok(20)));
assert_matches!(
keys.next(),
Some(Err(MapError::SyscallError(SyscallError {
call: "bpf_map_get_next_key",
io_error: _
})))
);
assert_matches!(keys.next(), Some(Err(MapError::Other(_))));
assert_matches!(keys.next(), None);
}
@ -412,17 +471,20 @@ mod tests {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
cmd: BpfCmd::MapGetNextKey,
attr,
} => get_next_key(attr),
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
cmd: BpfCmd::MapLookupElem,
attr,
} => lookup_elem(attr),
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let items = hm.iter().collect::<Result<Vec<_>, _>>().unwrap();
let items = hm
.iter()
.collect::<Result<Vec<(u32, u32)>, MapError>>()
.unwrap();
assert_eq!(&items, &[(10, 100), (20, 200), (30, 300)])
}
@ -431,28 +493,36 @@ mod tests {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
cmd: BpfCmd::MapGetNextKey,
attr,
} => get_next_key(attr),
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
cmd: BpfCmd::MapLookupElem,
attr,
} => {
let call = {
Syscall::Ebpf {
cmd: BpfCmd::MapLookupElem,
attr: &mut attr.clone(),
}
};
match bpf_key(attr) {
Some(10) => set_ret(attr, 100),
Some(20) => return sys_error(ENOENT),
Some(20) => return sys_error(call, ENOENT),
Some(30) => set_ret(attr, 300),
Some(_) => return sys_error(ENOENT),
None => return sys_error(EFAULT),
};
Some(_) => return sys_error(call, ENOENT),
None => return sys_error(call, EFAULT),
}
Ok(1)
}
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let items = hm.iter().collect::<Result<Vec<_>, _>>().unwrap();
let items = hm
.iter()
.collect::<Result<Vec<(u32, u32)>, MapError>>()
.unwrap();
assert_eq!(&items, &[(10, 100), (30, 300)])
}
@ -461,37 +531,37 @@ mod tests {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
cmd: BpfCmd::MapGetNextKey,
attr,
} => {
let call = {
Syscall::Ebpf {
cmd: BpfCmd::MapGetNextKey,
attr: &mut attr.clone(),
}
};
match bpf_key(attr) {
None => set_next_key(attr, 10),
Some(10) => set_next_key(attr, 20),
Some(20) => return sys_error(EFAULT),
Some(30) => return sys_error(ENOENT),
Some(20) => return sys_error(call, EFAULT),
Some(30) => return sys_error(call, ENOENT),
Some(i) => panic!("invalid key {}", i),
};
Ok(1)
}
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
cmd: BpfCmd::MapLookupElem,
attr,
} => lookup_elem(attr),
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let mut iter = hm.iter();
assert_matches!(iter.next(), Some(Ok((10, 100))));
assert_matches!(iter.next(), Some(Ok((20, 200))));
assert_matches!(
iter.next(),
Some(Err(MapError::SyscallError(SyscallError {
call: "bpf_map_get_next_key",
io_error: _
})))
);
assert_matches!(iter.next(), Some(Err(MapError::Other(_))));
assert_matches!(iter.next(), None);
}
@ -500,36 +570,36 @@ mod tests {
let map = new_map(new_obj_map());
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
cmd: BpfCmd::MapGetNextKey,
attr,
} => get_next_key(attr),
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
cmd: BpfCmd::MapLookupElem,
attr,
} => {
let call = {
Syscall::Ebpf {
cmd: BpfCmd::MapLookupElem,
attr: &mut attr.clone(),
}
};
match bpf_key(attr) {
Some(10) => set_ret(attr, 100),
Some(20) => return sys_error(EFAULT),
Some(20) => return sys_error(call, EFAULT),
Some(30) => set_ret(attr, 300),
Some(_) => return sys_error(ENOENT),
None => return sys_error(EFAULT),
Some(_) => return sys_error(call, ENOENT),
None => return sys_error(call, EFAULT),
};
Ok(1)
}
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
let mut iter = hm.iter();
assert_matches!(iter.next(), Some(Ok((10, 100))));
assert_matches!(
iter.next(),
Some(Err(MapError::SyscallError(SyscallError {
call: "bpf_map_lookup_elem",
io_error: _
})))
);
assert_matches!(iter.next(), Some(Err(MapError::Other(_))));
assert_matches!(iter.next(), Some(Ok((30, 300))));
assert_matches!(iter.next(), None);
}

@ -2,8 +2,8 @@
use std::os::fd::AsFd as _;
use crate::{
maps::MapError,
sys::{bpf_map_delete_elem, bpf_map_update_elem, SyscallError},
errors::MapError,
sys::{bpf_map_delete_elem, bpf_map_update_elem},
Pod,
};
@ -23,23 +23,11 @@ pub(crate) fn insert<K: Pod, V: Pod>(
flags: u64,
) -> Result<(), MapError> {
let fd = map.fd().as_fd();
bpf_map_update_elem(fd, Some(key), value, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_update_elem",
io_error,
})?;
bpf_map_update_elem(fd, Some(key), value, flags)?;
Ok(())
}
pub(crate) fn remove<K: Pod>(map: &MapData, key: &K) -> Result<(), MapError> {
let fd = map.fd().as_fd();
bpf_map_delete_elem(fd, key)
.map(|_| ())
.map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_delete_elem",
io_error,
}
.into()
})
bpf_map_delete_elem(fd, key).map(|_| ()).map_err(Into::into)
}

@ -6,10 +6,9 @@ use std::{
};
use crate::{
maps::{
check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys, PerCpuValues,
},
sys::{bpf_map_lookup_elem_per_cpu, bpf_map_update_elem_per_cpu, SyscallError},
errors::MapError,
maps::{check_kv_size, hash_map, IterableMap, MapData, MapIter, MapKeys, PerCpuValues},
sys::{bpf_map_lookup_elem_per_cpu, bpf_map_update_elem_per_cpu},
Pod,
};
@ -63,11 +62,7 @@ impl<T: Borrow<MapData>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
/// Returns a slice of values - one for each CPU - associated with the key.
pub fn get(&self, key: &K, flags: u64) -> Result<PerCpuValues<V>, MapError> {
let fd = self.inner.borrow().fd().as_fd();
let values =
bpf_map_lookup_elem_per_cpu(fd, key, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?;
let values = bpf_map_lookup_elem_per_cpu(fd, key, flags)?;
values.ok_or(MapError::KeyNotFound)
}
@ -121,13 +116,7 @@ impl<T: BorrowMut<MapData>, K: Pod, V: Pod> PerCpuHashMap<T, K, V> {
flags: u64,
) -> Result<(), MapError> {
let fd = self.inner.borrow_mut().fd().as_fd();
bpf_map_update_elem_per_cpu(fd, key.borrow(), &values, flags).map_err(
|(_, io_error)| SyscallError {
call: "bpf_map_update_elem",
io_error,
},
)?;
bpf_map_update_elem_per_cpu(fd, key.borrow(), &values, flags)?;
Ok(())
}

@ -8,11 +8,10 @@ use std::{
use aya_obj::generated::{bpf_map_info, bpf_map_type};
use super::{MapError, MapFd};
use super::MapFd;
use crate::{
sys::{
bpf_get_object, bpf_map_get_fd_by_id, bpf_map_get_info_by_fd, iter_map_ids, SyscallError,
},
errors::MapError,
sys::{bpf_get_object, bpf_map_get_fd_by_id, bpf_map_get_info_by_fd, iter_map_ids},
util::bytes_of_bpf_name,
FEATURES,
};
@ -35,7 +34,7 @@ impl MapInfo {
/// Uses kernel v4.13 features.
pub fn from_id(id: u32) -> Result<Self, MapError> {
bpf_map_get_fd_by_id(id)
.map_err(MapError::from)
.map_err(|e| e.into())
.and_then(|fd| Self::new_from_fd(fd.as_fd()))
}
@ -121,10 +120,7 @@ impl MapInfo {
// TODO: avoid this unwrap by adding a new error variant.
let path_string = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| SyscallError {
call: "BPF_OBJ_GET",
io_error,
})?;
let fd = bpf_get_object(&path_string)?;
Self::new_from_fd(fd.as_fd())
}

@ -6,8 +6,9 @@ use std::{
};
use crate::{
maps::{check_kv_size, IterableMap, MapData, MapError, MapIter, MapKeys},
sys::{bpf_map_delete_elem, bpf_map_lookup_elem, bpf_map_update_elem, SyscallError},
errors::MapError,
maps::{check_kv_size, IterableMap, MapData, MapIter, MapKeys},
sys::{bpf_map_delete_elem, bpf_map_lookup_elem, bpf_map_update_elem},
Pod,
};
@ -127,10 +128,7 @@ impl<T: Borrow<MapData>, K: Pod, V: Pod> LpmTrie<T, K, V> {
/// Returns a copy of the value associated with the longest prefix matching key in the LpmTrie.
pub fn get(&self, key: &Key<K>, flags: u64) -> Result<V, MapError> {
let fd = self.inner.borrow().fd().as_fd();
let value = bpf_map_lookup_elem(fd, key, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?;
let value = bpf_map_lookup_elem(fd, key, flags)?;
value.ok_or(MapError::KeyNotFound)
}
@ -156,12 +154,7 @@ impl<T: BorrowMut<MapData>, K: Pod, V: Pod> LpmTrie<T, K, V> {
flags: u64,
) -> Result<(), MapError> {
let fd = self.inner.borrow().fd().as_fd();
bpf_map_update_elem(fd, Some(key), value.borrow(), flags).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_update_elem",
io_error,
}
})?;
bpf_map_update_elem(fd, Some(key), value.borrow(), flags)?;
Ok(())
}
@ -171,15 +164,7 @@ impl<T: BorrowMut<MapData>, K: Pod, V: Pod> LpmTrie<T, K, V> {
/// Both the prefix and data must match exactly - this method does not do a longest prefix match.
pub fn remove(&mut self, key: &Key<K>) -> Result<(), MapError> {
let fd = self.inner.borrow().fd().as_fd();
bpf_map_delete_elem(fd, key)
.map(|_| ())
.map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_delete_elem",
io_error,
}
.into()
})
bpf_map_delete_elem(fd, key).map(|_| ()).map_err(Into::into)
}
}
@ -198,52 +183,79 @@ mod tests {
use std::{io, net::Ipv4Addr};
use assert_matches::assert_matches;
use libc::{EFAULT, ENOENT};
use libc::{EFAULT, EINVAL, ENOENT};
use super::*;
use crate::{
generated::{
bpf_cmd,
bpf_map_type::{BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_LPM_TRIE},
},
errors::{InternalMapError, SysError},
generated::bpf_map_type::{BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_LPM_TRIE},
maps::{
test_utils::{self, new_map},
Map,
},
obj,
sys::{override_syscall, SysResult, Syscall},
sys::{override_syscall, BpfCmd, SysResult, Syscall},
};
fn new_obj_map() -> obj::Map {
test_utils::new_obj_map::<Key<u32>>(BPF_MAP_TYPE_LPM_TRIE)
}
fn sys_error(value: i32) -> SysResult<i64> {
Err((-1, io::Error::from_raw_os_error(value)))
fn sys_error(call: Syscall<'_>, value: i32) -> SysResult<i64> {
match call {
Syscall::Ebpf { .. } => Err((
-1,
SysError::Syscall {
call: format!("{:?}", call),
io_error: io::Error::from_raw_os_error(value),
},
)),
_ => Err((
-1,
SysError::Syscall {
call: "UNEXPECTED!!!".to_string(),
io_error: io::Error::from_raw_os_error(EINVAL),
},
)),
}
}
#[test]
fn test_wrong_key_size() {
let map = new_map(new_obj_map());
assert_matches!(
LpmTrie::<_, u16, u32>::new(&map),
Err(MapError::InvalidKeySize {
size: 6,
expected: 8 // four bytes for prefixlen and four bytes for data.
})
);
let res = LpmTrie::<_, u16, u32>::new(&map);
assert!(res.is_err());
let res = res.err().unwrap();
if let MapError::Other(map_err) = res {
assert_matches!(
map_err.downcast_ref::<InternalMapError>().unwrap(),
InternalMapError::InvalidKeySize {
size: 2,
expected: 8
}
);
} else {
panic!("unexpected error: {:?}", res);
}
}
#[test]
fn test_wrong_value_size() {
let map = new_map(new_obj_map());
assert_matches!(
LpmTrie::<_, u32, u16>::new(&map),
Err(MapError::InvalidValueSize {
size: 2,
expected: 4
})
);
let res = LpmTrie::<_, u32, u16>::new(&map);
assert!(res.is_err());
let res = res.err().unwrap();
if let MapError::Other(map_err) = res {
assert_matches!(
map_err.downcast_ref::<InternalMapError>().unwrap(),
InternalMapError::InvalidValueSize {
size: 2,
expected: 4
}
);
} else {
panic!("unexpected error: {:?}", res);
}
}
#[test]
@ -280,12 +292,9 @@ mod tests {
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
override_syscall(|_| sys_error(EFAULT));
override_syscall(|c| sys_error(c, EFAULT));
assert_matches!(
trie.insert(&key, 1, 0),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_update_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
);
assert!(trie.insert(&key, 1, 0).is_err());
}
#[test]
@ -297,10 +306,10 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
cmd: BpfCmd::MapUpdateElem,
..
} => Ok(1),
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
assert!(trie.insert(&key, 1, 0).is_ok());
@ -313,12 +322,9 @@ mod tests {
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
override_syscall(|_| sys_error(EFAULT));
override_syscall(|c| sys_error(c, EFAULT));
assert_matches!(
trie.remove(&key),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_delete_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
);
assert!(trie.remove(&key).is_err());
}
#[test]
@ -330,10 +336,10 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_DELETE_ELEM,
cmd: BpfCmd::MapDeleteElem,
..
} => Ok(1),
_ => sys_error(EFAULT),
c => sys_error(c, EFAULT),
});
assert!(trie.remove(&key).is_ok());
@ -346,12 +352,9 @@ mod tests {
let ipaddr = Ipv4Addr::new(8, 8, 8, 8);
let key = Key::new(16, u32::from(ipaddr).to_be());
override_syscall(|_| sys_error(EFAULT));
override_syscall(|c| sys_error(c, EFAULT));
assert_matches!(
trie.get(&key, 0),
Err(MapError::SyscallError(SyscallError { call: "bpf_map_lookup_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT)
);
assert!(trie.get(&key, 0).is_err());
}
#[test]
@ -363,10 +366,10 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
cmd: BpfCmd::MapLookupElem,
..
} => sys_error(ENOENT),
_ => sys_error(EFAULT),
} => sys_error(call, ENOENT),
c => sys_error(c, EFAULT),
});
assert_matches!(trie.get(&key, 0), Err(MapError::KeyNotFound));

@ -59,17 +59,16 @@ use std::{
ptr,
};
use aya_obj::{generated::bpf_map_type, InvalidTypeBinding};
use aya_obj::generated::bpf_map_type;
use libc::{getrlimit, rlim_t, rlimit, RLIMIT_MEMLOCK, RLIM_INFINITY};
use log::warn;
use thiserror::Error;
use crate::{
errors::{InternalMapError, MapError, PinError},
obj::{self, parse_map_info, EbpfSectionKind},
pin::PinError,
sys::{
bpf_create_map, bpf_get_object, bpf_map_freeze, bpf_map_get_fd_by_id, bpf_map_get_next_key,
bpf_map_update_elem_ptr, bpf_pin_object, SyscallError,
bpf_map_update_elem_ptr, bpf_pin_object,
},
util::{nr_cpus, KernelVersion},
PinningType, Pod,
@ -104,111 +103,6 @@ pub use stack::Stack;
pub use stack_trace::StackTraceMap;
pub use xdp::{CpuMap, DevMap, DevMapHash, XskMap};
#[derive(Error, Debug)]
/// Errors occuring from working with Maps
pub enum MapError {
/// Invalid map type encontered
#[error("invalid map type {map_type}")]
InvalidMapType {
/// The map type
map_type: u32,
},
/// Invalid map name encountered
#[error("invalid map name `{name}`")]
InvalidName {
/// The map name
name: String,
},
/// Failed to create map
#[error("failed to create map `{name}` with code {code}")]
CreateError {
/// Map name
name: String,
/// Error code
code: i64,
#[source]
/// Original io::Error
io_error: io::Error,
},
/// Invalid key size
#[error("invalid key size {size}, expected {expected}")]
InvalidKeySize {
/// Size encountered
size: usize,
/// Size expected
expected: usize,
},
/// Invalid value size
#[error("invalid value size {size}, expected {expected}")]
InvalidValueSize {
/// Size encountered
size: usize,
/// Size expected
expected: usize,
},
/// Index is out of bounds
#[error("the index is {index} but `max_entries` is {max_entries}")]
OutOfBounds {
/// Index accessed
index: u32,
/// Map size
max_entries: u32,
},
/// Key not found
#[error("key not found")]
KeyNotFound,
/// Element not found
#[error("element not found")]
ElementNotFound,
/// Progam Not Loaded
#[error("the program is not loaded")]
ProgramNotLoaded,
/// An IO error occurred
#[error(transparent)]
IoError(#[from] io::Error),
/// Syscall failed
#[error(transparent)]
SyscallError(#[from] SyscallError),
/// Could not pin map
#[error("map `{name:?}` requested pinning. pinning failed")]
PinError {
/// The map name
name: Option<String>,
/// The reason for the failure
#[source]
error: PinError,
},
/// Program IDs are not supported
#[error("program ids are not supported by the current kernel")]
ProgIdNotSupported,
/// Unsupported Map type
#[error("Unsupported map type found {map_type}")]
Unsupported {
/// The map type
map_type: u32,
},
}
impl From<InvalidTypeBinding<u32>> for MapError {
fn from(e: InvalidTypeBinding<u32>) -> Self {
let InvalidTypeBinding { value } = e;
Self::InvalidMapType { map_type: value }
}
}
/// A map file descriptor.
#[derive(Debug)]
pub struct MapFd {
@ -347,7 +241,7 @@ impl Map {
///
/// When a map is pinned it will remain loaded until the corresponding file
/// is deleted. All parent directories in the given `path` must already exist.
pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), PinError> {
pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), MapError> {
match self {
Self::Array(map) => map.pin(path),
Self::BloomFilter(map) => map.pin(path),
@ -391,7 +285,7 @@ macro_rules! impl_map_pin {
///
/// When a map is pinned it will remain loaded until the corresponding file
/// is deleted. All parent directories in the given `path` must already exist.
pub fn pin<P: AsRef<Path>>(self, path: P) -> Result<(), PinError> {
pub fn pin<P: AsRef<Path>>(self, path: P) -> Result<(), MapError> {
let data = self.inner.borrow();
data.pin(path)
}
@ -516,25 +410,25 @@ pub(crate) fn check_bounds(map: &MapData, index: u32) -> Result<(), MapError> {
}
}
pub(crate) fn check_kv_size<K, V>(map: &MapData) -> Result<(), MapError> {
pub(crate) fn check_kv_size<K, V>(map: &MapData) -> Result<(), InternalMapError> {
let size = mem::size_of::<K>();
let expected = map.obj.key_size() as usize;
if size != expected {
return Err(MapError::InvalidKeySize { size, expected });
return Err(InternalMapError::InvalidKeySize { size, expected });
}
let size = mem::size_of::<V>();
let expected = map.obj.value_size() as usize;
if size != expected {
return Err(MapError::InvalidValueSize { size, expected });
return Err(InternalMapError::InvalidValueSize { size, expected });
};
Ok(())
}
pub(crate) fn check_v_size<V>(map: &MapData) -> Result<(), MapError> {
pub(crate) fn check_v_size<V>(map: &MapData) -> Result<(), InternalMapError> {
let size = mem::size_of::<V>();
let expected = map.obj.value_size() as usize;
if size != expected {
return Err(MapError::InvalidValueSize { size, expected });
return Err(InternalMapError::InvalidValueSize { size, expected });
};
Ok(())
}
@ -555,7 +449,8 @@ impl MapData {
name: &str,
btf_fd: Option<BorrowedFd<'_>>,
) -> Result<Self, MapError> {
let c_name = CString::new(name).map_err(|_| MapError::InvalidName { name: name.into() })?;
let c_name =
CString::new(name).map_err(|_| InternalMapError::InvalidName { name: name.into() })?;
// BPF_MAP_TYPE_PERF_EVENT_ARRAY's max_entries should not exceed the number of
// CPUs.
@ -569,7 +464,7 @@ impl MapData {
//
// Otherwise, when the value is `0` or too large, we set it to the number of CPUs.
if obj.map_type() == bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY as u32 {
let nr_cpus = nr_cpus().map_err(|(_, error)| MapError::IoError(error))? as u32;
let nr_cpus = nr_cpus().map_err(|(_, error)| InternalMapError::IoError(error))? as u32;
if obj.max_entries() == 0 || obj.max_entries() > nr_cpus {
obj.set_max_entries(nr_cpus);
}
@ -580,15 +475,14 @@ impl MapData {
#[cfg(test)]
let kernel_version = KernelVersion::new(0xff, 0xff, 0xff);
let fd =
bpf_create_map(&c_name, &obj, btf_fd, kernel_version).map_err(|(code, io_error)| {
bpf_create_map(&c_name, &obj, btf_fd, kernel_version).map_err(|(code, source)| {
if kernel_version < KernelVersion::new(5, 11, 0) {
maybe_warn_rlimit();
}
MapError::CreateError {
InternalMapError::CreateError {
name: name.into(),
code,
io_error,
source,
}
})?;
Ok(Self {
@ -610,26 +504,17 @@ impl MapData {
let path_string = match CString::new(path.as_os_str().as_bytes()) {
Ok(path) => path,
Err(error) => {
return Err(MapError::PinError {
name: Some(name.into()),
error: PinError::InvalidPinPath { path, error },
});
return Err(PinError::InvalidPinPath { path, error }.into());
}
};
match bpf_get_object(&path_string).map_err(|(_, io_error)| SyscallError {
call: "BPF_OBJ_GET",
io_error,
}) {
match bpf_get_object(&path_string) {
Ok(fd) => Ok(Self {
obj,
fd: MapFd::from_fd(fd),
}),
Err(_) => {
let map = Self::create(obj, name, btf_fd)?;
map.pin(&path).map_err(|error| MapError::PinError {
name: Some(name.into()),
error,
})?;
map.pin(&path)?;
Ok(map)
}
}
@ -638,20 +523,10 @@ impl MapData {
pub(crate) fn finalize(&mut self) -> Result<(), MapError> {
let Self { obj, fd } = self;
if !obj.data().is_empty() && obj.section_kind() != EbpfSectionKind::Bss {
bpf_map_update_elem_ptr(fd.as_fd(), &0 as *const _, obj.data_mut().as_mut_ptr(), 0)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_map_update_elem",
io_error,
})
.map_err(MapError::from)?;
bpf_map_update_elem_ptr(fd.as_fd(), &0 as *const _, obj.data_mut().as_mut_ptr(), 0)?;
}
if obj.section_kind() == EbpfSectionKind::Rodata {
bpf_map_freeze(fd.as_fd())
.map_err(|(_, io_error)| SyscallError {
call: "bpf_map_freeze",
io_error,
})
.map_err(MapError::from)?;
bpf_map_freeze(fd.as_fd())?;
}
Ok(())
}
@ -661,19 +536,8 @@ impl MapData {
use std::os::unix::ffi::OsStrExt as _;
let path = path.as_ref();
let path_string =
CString::new(path.as_os_str().as_bytes()).map_err(|error| MapError::PinError {
name: None,
error: PinError::InvalidPinPath {
path: path.into(),
error,
},
})?;
let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| SyscallError {
call: "BPF_OBJ_GET",
io_error,
})?;
let path_string = CString::new(path.as_os_str().as_bytes())?;
let fd = bpf_get_object(&path_string)?;
Self::from_fd_inner(fd)
}
@ -726,21 +590,13 @@ impl MapData {
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), PinError> {
pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), MapError> {
use std::os::unix::ffi::OsStrExt as _;
let Self { fd, obj: _ } = self;
let path = path.as_ref();
let path_string = CString::new(path.as_os_str().as_bytes()).map_err(|error| {
PinError::InvalidPinPath {
path: path.to_path_buf(),
error,
}
})?;
bpf_pin_object(fd.as_fd(), &path_string).map_err(|(_, io_error)| SyscallError {
call: "BPF_OBJ_PIN",
io_error,
})?;
let path_string = CString::new(path.as_os_str().as_bytes())?;
bpf_pin_object(fd.as_fd(), &path_string)?;
Ok(())
}
@ -796,11 +652,7 @@ impl<K: Pod> Iterator for MapKeys<'_, K> {
}
let fd = self.map.fd().as_fd();
let key =
bpf_map_get_next_key(fd, self.key.as_ref()).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_get_next_key",
io_error,
});
let key = bpf_map_get_next_key(fd, self.key.as_ref());
match key {
Err(err) => {
self.err = true;
@ -953,16 +805,16 @@ impl<T: Pod> Deref for PerCpuValues<T> {
mod test_utils {
use crate::{
bpf_map_def,
generated::{bpf_cmd, bpf_map_type},
generated::bpf_map_type,
maps::MapData,
obj::{self, maps::LegacyMap, EbpfSectionKind},
sys::{override_syscall, Syscall},
sys::{override_syscall, BpfCmd, Syscall},
};
pub(super) fn new_map(obj: obj::Map) -> MapData {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_CREATE,
cmd: BpfCmd::MapCreate,
..
} => Ok(crate::MockableFd::mock_signed_fd().into()),
call => panic!("unexpected syscall {:?}", call),
@ -1019,15 +871,15 @@ mod tests {
use super::*;
use crate::{
generated::bpf_cmd,
sys::{override_syscall, Syscall},
errors::SysError,
sys::{override_syscall, BpfCmd, Syscall},
};
#[test]
fn test_from_map_id() {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_GET_FD_BY_ID,
cmd: BpfCmd::MapGetFdById,
attr,
} => {
assert_eq!(
@ -1037,7 +889,7 @@ mod tests {
Ok(crate::MockableFd::mock_signed_fd().into())
}
Syscall::Ebpf {
cmd: bpf_cmd::BPF_OBJ_GET_INFO_BY_FD,
cmd: BpfCmd::ObjGetInfoByFd,
attr,
} => {
assert_eq!(
@ -1046,7 +898,13 @@ mod tests {
);
Ok(0)
}
_ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
_ => Err((
-1,
SysError::Syscall {
call: "unexpected".to_string(),
io_error: io::Error::from_raw_os_error(EFAULT),
},
)),
});
assert_matches!(
@ -1062,10 +920,16 @@ mod tests {
fn test_create() {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_CREATE,
cmd: BpfCmd::MapCreate,
..
} => Ok(crate::MockableFd::mock_signed_fd().into()),
_ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
_ => Err((
-1,
SysError::Syscall {
call: "unexpected".to_string(),
io_error: io::Error::from_raw_os_error(EFAULT),
},
)),
});
assert_matches!(
@ -1082,10 +946,16 @@ mod tests {
fn test_create_perf_event_array() {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_CREATE,
cmd: BpfCmd::MapCreate,
..
} => Ok(crate::MockableFd::mock_signed_fd().into()),
_ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
_ => Err((
-1,
SysError::Syscall {
call: "unexpected".to_string(),
io_error: io::Error::from_raw_os_error(EFAULT),
},
)),
});
let nr_cpus = nr_cpus().unwrap();
@ -1148,11 +1018,11 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_CREATE,
cmd: BpfCmd::MapCreate,
..
} => Ok(crate::MockableFd::mock_signed_fd().into()),
Syscall::Ebpf {
cmd: bpf_cmd::BPF_OBJ_GET_INFO_BY_FD,
cmd: BpfCmd::ObjGetInfoByFd,
attr,
} => {
assert_eq!(
@ -1165,7 +1035,13 @@ mod tests {
});
Ok(0)
}
_ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
_ => Err((
-1,
SysError::Syscall {
call: "unexpected".to_string(),
io_error: io::Error::from_raw_os_error(EFAULT),
},
)),
});
let map_data = MapData::create(new_obj_map(), TEST_NAME, None).unwrap();
@ -1182,7 +1058,7 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_GET_NEXT_ID,
cmd: BpfCmd::MapGetNextId,
attr,
} => unsafe {
let id = attr.__bindgen_anon_6.__bindgen_anon_1.start_id;
@ -1190,17 +1066,23 @@ mod tests {
attr.__bindgen_anon_6.next_id = id + 1;
Ok(0)
} else {
Err((-1, io::Error::from_raw_os_error(libc::ENOENT)))
Err((
-1,
SysError::Syscall {
call: "unexpected".to_string(),
io_error: io::Error::from_raw_os_error(EFAULT),
},
))
}
},
Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_GET_FD_BY_ID,
cmd: BpfCmd::MapGetFdById,
attr,
} => Ok((unsafe { attr.__bindgen_anon_6.__bindgen_anon_1.map_id }
+ crate::MockableFd::mock_unsigned_fd())
.into()),
Syscall::Ebpf {
cmd: bpf_cmd::BPF_OBJ_GET_INFO_BY_FD,
cmd: BpfCmd::ObjGetInfoByFd,
attr,
} => {
let map_info = unsafe { &mut *(attr.info.info as *mut bpf_map_info) };
@ -1211,7 +1093,13 @@ mod tests {
map_info.max_entries = 99;
Ok(0)
}
_ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
_ => Err((
-1,
SysError::Syscall {
call: "unexpected".to_string(),
io_error: io::Error::from_raw_os_error(EFAULT),
},
)),
});
assert_eq!(
@ -1243,15 +1131,34 @@ mod tests {
#[test]
fn test_create_failed() {
override_syscall(|_| Err((-42, io::Error::from_raw_os_error(EFAULT))));
override_syscall(|_| {
Err((
-42,
SysError::Syscall {
call: "unexpected".to_string(),
io_error: io::Error::from_raw_os_error(EFAULT),
},
))
});
assert_matches!(
MapData::create(new_obj_map(), "foo", None),
Err(MapError::CreateError { name, code, io_error }) => {
assert_eq!(name, "foo");
assert_eq!(code, -42);
assert_eq!(io_error.raw_os_error(), Some(EFAULT));
}
);
let res = MapData::create(new_obj_map(), "foo", None);
assert!(res.is_err());
let res = res.unwrap_err();
if let MapError::Other(e) = res {
assert_matches!(
e.downcast_ref::<InternalMapError>().unwrap(),
InternalMapError::CreateError { name, code, source} => {
assert_eq!(name, "foo");
assert_eq!(*code, -42);
if let SysError::Syscall { call: _, io_error } = source {
assert_eq!(io_error.raw_os_error(), Some(EFAULT));
} else {
panic!("unexpected source: {:?}", source);
}
}
);
} else {
panic!("unexpected error: {:?}", res);
}
}
}

@ -13,9 +13,12 @@ use bytes::BytesMut;
#[cfg(feature = "async_tokio")]
use tokio::io::unix::AsyncFd;
use crate::maps::{
perf::{Events, PerfBufferError, PerfEventArray, PerfEventArrayBuffer},
MapData, MapError, PinError,
use crate::{
errors::{MapError, PerfBufferError},
maps::{
perf::{Events, PerfEventArray, PerfEventArrayBuffer},
MapData,
},
};
/// A `Future` based map that can be used to receive events from eBPF programs using the linux
@ -113,7 +116,7 @@ impl<T: BorrowMut<MapData>> AsyncPerfEventArray<T> {
///
/// When a map is pinned it will remain loaded until the corresponding file
/// is deleted. All parent directories in the given `path` must already exist.
pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), PinError> {
pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), MapError> {
self.perf_map.pin(path)
}
}

@ -8,9 +8,9 @@ use std::{
use bytes::BytesMut;
use libc::{munmap, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE};
use thiserror::Error;
use crate::{
errors::PerfBufferError,
generated::{
perf_event_header, perf_event_mmap_page,
perf_event_type::{PERF_RECORD_LOST, PERF_RECORD_SAMPLE},
@ -19,61 +19,6 @@ use crate::{
PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE,
};
/// Perf buffer error.
#[derive(Error, Debug)]
pub enum PerfBufferError {
/// the page count value passed to [`PerfEventArray::open`](crate::maps::PerfEventArray::open) is invalid.
#[error("invalid page count {page_count}, the value must be a power of two")]
InvalidPageCount {
/// the page count
page_count: usize,
},
/// `perf_event_open` failed.
#[error("perf_event_open failed: {io_error}")]
OpenError {
/// the source of this error
#[source]
io_error: io::Error,
},
/// `mmap`-ping the buffer failed.
#[error("mmap failed: {io_error}")]
MMapError {
/// the source of this error
#[source]
io_error: io::Error,
},
/// The `PERF_EVENT_IOC_ENABLE` ioctl failed
#[error("PERF_EVENT_IOC_ENABLE failed: {io_error}")]
PerfEventEnableError {
#[source]
/// the source of this error
io_error: io::Error,
},
/// `read_events()` was called with no output buffers.
#[error("read_events() was called with no output buffers")]
NoBuffers,
/// `read_events()` was called with a buffer that is not large enough to
/// contain the next event in the perf buffer.
#[deprecated(
since = "0.10.8",
note = "read_events() now calls BytesMut::reserve() internally, so this error is never returned"
)]
#[error("the buffer needs to be of at least {size} bytes")]
MoreSpaceNeeded {
/// expected size
size: usize,
},
/// An IO error occurred.
#[error(transparent)]
IOError(#[from] io::Error),
}
/// Return type of `read_events()`.
#[derive(Debug, PartialEq, Eq)]
pub struct Events {
@ -101,8 +46,7 @@ impl PerfBuffer {
return Err(PerfBufferError::InvalidPageCount { page_count });
}
let fd = perf_event_open_bpf(cpu_id as i32)
.map_err(|(_, io_error)| PerfBufferError::OpenError { io_error })?;
let fd = perf_event_open_bpf(cpu_id as i32)?;
let size = page_size * page_count;
let buf = unsafe {
mmap(
@ -127,8 +71,7 @@ impl PerfBuffer {
fd,
};
perf_event_ioctl(perf_buf.fd.as_fd(), PERF_EVENT_IOC_ENABLE, 0)
.map_err(|(_, io_error)| PerfBufferError::PerfEventEnableError { io_error })?;
perf_event_ioctl(perf_buf.fd.as_fd(), PERF_EVENT_IOC_ENABLE, 0)?;
Ok(perf_buf)
}

@ -12,9 +12,10 @@ use std::{
use bytes::BytesMut;
use crate::{
errors::{MapError, PerfBufferError},
maps::{
perf::{Events, PerfBuffer, PerfBufferError},
MapData, MapError, PinError,
perf::{Events, PerfBuffer},
MapData,
},
sys::bpf_map_update_elem,
util::page_size,
@ -179,7 +180,7 @@ impl<T: Borrow<MapData>> PerfEventArray<T> {
///
/// When a map is pinned it will remain loaded until the corresponding file
/// is deleted. All parent directories in the given `path` must already exist.
pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), PinError> {
pub fn pin<P: AsRef<Path>>(&self, path: P) -> Result<(), MapError> {
let data: &MapData = self.map.deref().borrow();
data.pin(path)
}
@ -199,8 +200,7 @@ impl<T: BorrowMut<MapData>> PerfEventArray<T> {
let map_data: &MapData = self.map.deref().borrow();
let map_fd = map_data.fd().as_fd();
let buf = PerfBuffer::open(index, self.page_size, page_count.unwrap_or(2))?;
bpf_map_update_elem(map_fd, Some(&index), &buf.as_fd().as_raw_fd(), 0)
.map_err(|(_, io_error)| io_error)?;
bpf_map_update_elem(map_fd, Some(&index), &buf.as_fd().as_raw_fd(), 0)?;
Ok(PerfEventArrayBuffer {
buf,

@ -7,7 +7,7 @@ use std::{
use crate::{
maps::{check_kv_size, MapData, MapError},
sys::{bpf_map_lookup_and_delete_elem, bpf_map_push_elem, SyscallError},
sys::{bpf_map_lookup_and_delete_elem, bpf_map_push_elem},
Pod,
};
@ -63,12 +63,7 @@ impl<T: BorrowMut<MapData>, V: Pod> Queue<T, V> {
pub fn pop(&mut self, flags: u64) -> Result<V, MapError> {
let fd = self.inner.borrow().fd().as_fd();
let value = bpf_map_lookup_and_delete_elem::<u32, _>(fd, None, flags).map_err(
|(_, io_error)| SyscallError {
call: "bpf_map_lookup_and_delete_elem",
io_error,
},
)?;
let value = bpf_map_lookup_and_delete_elem::<u32, _>(fd, None, flags)?;
value.ok_or(MapError::ElementNotFound)
}
@ -79,10 +74,7 @@ impl<T: BorrowMut<MapData>, V: Pod> Queue<T, V> {
/// [`MapError::SyscallError`] if `bpf_map_update_elem` fails.
pub fn push(&mut self, value: impl Borrow<V>, flags: u64) -> Result<(), MapError> {
let fd = self.inner.borrow().fd().as_fd();
bpf_map_push_elem(fd, value.borrow(), flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_push_elem",
io_error,
})?;
bpf_map_push_elem(fd, value.borrow(), flags)?;
Ok(())
}
}

@ -20,9 +20,10 @@ use std::{
use libc::{munmap, off_t, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE};
use crate::{
errors::SysError,
generated::{BPF_RINGBUF_BUSY_BIT, BPF_RINGBUF_DISCARD_BIT, BPF_RINGBUF_HDR_SZ},
maps::{MapData, MapError},
sys::{mmap, SyscallError},
sys::mmap,
util::page_size,
};
@ -426,21 +427,21 @@ impl MMap {
offset: off_t,
) -> Result<Self, MapError> {
match unsafe { mmap(ptr::null_mut(), len, prot, flags, fd, offset) } {
MAP_FAILED => Err(MapError::SyscallError(SyscallError {
call: "mmap",
MAP_FAILED => Err(SysError::Mmap {
io_error: io::Error::last_os_error(),
})),
}
.into()),
ptr => Ok(Self {
ptr: NonNull::new(ptr).ok_or(
ptr: NonNull::new(ptr).ok_or::<MapError>(
// This should never happen, but to be paranoid, and so we never need to talk
// about a null pointer, we check it anyway.
MapError::SyscallError(SyscallError {
call: "mmap",
SysError::Mmap {
io_error: io::Error::new(
io::ErrorKind::Other,
"mmap returned null pointer",
),
}),
}
.into(),
)?,
len,
}),

@ -5,11 +5,11 @@ use std::{
};
use crate::{
errors::MapError,
maps::{
check_kv_size, hash_map, sock::SockMapFd, IterableMap, MapData, MapError, MapFd, MapIter,
MapKeys,
check_kv_size, hash_map, sock::SockMapFd, IterableMap, MapData, MapFd, MapIter, MapKeys,
},
sys::{bpf_map_lookup_elem, SyscallError},
sys::bpf_map_lookup_elem,
Pod,
};
@ -83,10 +83,7 @@ impl<T: Borrow<MapData>, K: Pod> SockHash<T, K> {
/// Returns the fd of the socket stored at the given key.
pub fn get(&self, key: &K, flags: u64) -> Result<RawFd, MapError> {
let fd = self.inner.borrow().fd().as_fd();
let value = bpf_map_lookup_elem(fd, key, flags).map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?;
let value = bpf_map_lookup_elem(fd, key, flags)?;
value.ok_or(MapError::KeyNotFound)
}

@ -6,8 +6,9 @@ use std::{
};
use crate::{
maps::{check_bounds, check_kv_size, sock::SockMapFd, MapData, MapError, MapFd, MapKeys},
sys::{bpf_map_delete_elem, bpf_map_update_elem, SyscallError},
errors::MapError,
maps::{check_bounds, check_kv_size, sock::SockMapFd, MapData, MapFd, MapKeys},
sys::{bpf_map_delete_elem, bpf_map_update_elem},
};
/// An array of TCP or UDP sockets.
@ -87,12 +88,7 @@ impl<T: BorrowMut<MapData>> SockMap<T> {
let data = self.inner.borrow_mut();
let fd = data.fd().as_fd();
check_bounds(data, index)?;
bpf_map_update_elem(fd, Some(&index), &socket.as_raw_fd(), flags).map_err(
|(_, io_error)| SyscallError {
call: "bpf_map_update_elem",
io_error,
},
)?;
bpf_map_update_elem(fd, Some(&index), &socket.as_raw_fd(), flags)?;
Ok(())
}
@ -103,12 +99,6 @@ impl<T: BorrowMut<MapData>> SockMap<T> {
check_bounds(data, *index)?;
bpf_map_delete_elem(fd, index)
.map(|_| ())
.map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_delete_elem",
io_error,
}
.into()
})
.map_err(Into::into)
}
}

@ -7,7 +7,7 @@ use std::{
use crate::{
maps::{check_kv_size, MapData, MapError},
sys::{bpf_map_lookup_and_delete_elem, bpf_map_update_elem, SyscallError},
sys::{bpf_map_lookup_and_delete_elem, bpf_map_update_elem},
Pod,
};
@ -63,12 +63,7 @@ impl<T: BorrowMut<MapData>, V: Pod> Stack<T, V> {
pub fn pop(&mut self, flags: u64) -> Result<V, MapError> {
let fd = self.inner.borrow().fd().as_fd();
let value = bpf_map_lookup_and_delete_elem::<u32, _>(fd, None, flags).map_err(
|(_, io_error)| SyscallError {
call: "bpf_map_lookup_and_delete_elem",
io_error,
},
)?;
let value = bpf_map_lookup_and_delete_elem::<u32, _>(fd, None, flags)?;
value.ok_or(MapError::ElementNotFound)
}
@ -79,12 +74,7 @@ impl<T: BorrowMut<MapData>, V: Pod> Stack<T, V> {
/// [`MapError::SyscallError`] if `bpf_map_update_elem` fails.
pub fn push(&mut self, value: impl Borrow<V>, flags: u64) -> Result<(), MapError> {
let fd = self.inner.borrow().fd().as_fd();
bpf_map_update_elem(fd, None::<&u32>, value.borrow(), flags).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_update_elem",
io_error,
}
})?;
bpf_map_update_elem(fd, None::<&u32>, value.borrow(), flags)?;
Ok(())
}
}

@ -10,8 +10,9 @@ use std::{
};
use crate::{
maps::{IterableMap, MapData, MapError, MapIter, MapKeys},
sys::{bpf_map_delete_elem, bpf_map_lookup_elem_ptr, SyscallError},
errors::{InternalMapError, MapError},
maps::{IterableMap, MapData, MapIter, MapKeys},
sys::{bpf_map_delete_elem, bpf_map_lookup_elem_ptr},
};
/// A hash map of kernel or user space stack traces.
@ -83,17 +84,13 @@ impl<T: Borrow<MapData>> StackTraceMap<T> {
let expected = mem::size_of::<u32>();
let size = data.obj.key_size() as usize;
if size != expected {
return Err(MapError::InvalidKeySize { size, expected });
return Err(InternalMapError::InvalidKeySize { size, expected }.into());
}
let max_stack_depth =
sysctl::<usize>("kernel/perf_event_max_stack").map_err(|io_error| SyscallError {
call: "sysctl",
io_error,
})?;
let max_stack_depth = sysctl::<usize>("kernel/perf_event_max_stack")?;
let size = data.obj.value_size() as usize;
if size > max_stack_depth * mem::size_of::<u64>() {
return Err(MapError::InvalidValueSize { size, expected });
return Err(InternalMapError::InvalidValueSize { size, expected }.into());
}
Ok(Self {
@ -112,11 +109,7 @@ impl<T: Borrow<MapData>> StackTraceMap<T> {
let fd = self.inner.borrow().fd().as_fd();
let mut frames = vec![0; self.max_stack_depth];
bpf_map_lookup_elem_ptr(fd, Some(stack_id), frames.as_mut_ptr(), flags)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?
bpf_map_lookup_elem_ptr(fd, Some(stack_id), frames.as_mut_ptr(), flags)?
.ok_or(MapError::KeyNotFound)?;
let frames = frames
@ -169,13 +162,7 @@ impl<T: BorrowMut<MapData>> StackTraceMap<T> {
let fd = self.inner.borrow().fd().as_fd();
bpf_map_delete_elem(fd, stack_id)
.map(|_| ())
.map_err(|(_, io_error)| {
SyscallError {
call: "bpf_map_delete_elem",
io_error,
}
.into()
})
.map_err(Into::into)
}
}

@ -8,11 +8,10 @@ use std::{
use aya_obj::generated::bpf_cpumap_val;
use super::XdpMapError;
use crate::{
maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError},
programs::ProgramFd,
sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError},
sys::{bpf_map_lookup_elem, bpf_map_update_elem},
Pod, FEATURES,
};
@ -102,12 +101,7 @@ impl<T: Borrow<MapData>> CpuMap<T> {
})
})
};
value
.map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?
.ok_or(MapError::KeyNotFound)
value?.ok_or(MapError::KeyNotFound)
}
/// An iterator over the elements of the map.
@ -142,7 +136,7 @@ impl<T: BorrowMut<MapData>> CpuMap<T> {
queue_size: u32,
program: Option<&ProgramFd>,
flags: u64,
) -> Result<(), XdpMapError> {
) -> Result<(), MapError> {
let data = self.inner.borrow_mut();
check_bounds(data, cpu_index)?;
let fd = data.fd().as_fd();
@ -158,17 +152,12 @@ impl<T: BorrowMut<MapData>> CpuMap<T> {
bpf_map_update_elem(fd, Some(&cpu_index), &value, flags)
} else {
if program.is_some() {
return Err(XdpMapError::ChainedProgramNotSupported);
return Err(MapError::ChainedProgramNotSupported);
}
bpf_map_update_elem(fd, Some(&cpu_index), &queue_size, flags)
};
res.map_err(|(_, io_error)| {
MapError::from(SyscallError {
call: "bpf_map_update_elem",
io_error,
})
})?;
res?;
Ok(())
}
}

@ -8,11 +8,11 @@ use std::{
use aya_obj::generated::bpf_devmap_val;
use super::XdpMapError;
use crate::{
maps::{check_bounds, check_kv_size, IterableMap, MapData, MapError},
errors::MapError,
maps::{check_bounds, check_kv_size, IterableMap, MapData},
programs::ProgramFd,
sys::{bpf_map_lookup_elem, bpf_map_update_elem, SyscallError},
sys::{bpf_map_lookup_elem, bpf_map_update_elem},
Pod, FEATURES,
};
@ -93,12 +93,7 @@ impl<T: Borrow<MapData>> DevMap<T> {
})
})
};
value
.map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?
.ok_or(MapError::KeyNotFound)
value?.ok_or(MapError::KeyNotFound)
}
/// An iterator over the elements of the array.
@ -124,7 +119,7 @@ impl<T: BorrowMut<MapData>> DevMap<T> {
/// # Errors
///
/// Returns [`MapError::OutOfBounds`] if `index` is out of bounds, [`MapError::SyscallError`]
/// if `bpf_map_update_elem` fails, [`MapError::ProgIdNotSupported`] if the kernel does not
/// if `bpf_map_update_elem` fails, [`MapError::ChainedProgramsNotSupported`] if the kernel does not
/// support chained programs and one is provided.
pub fn set(
&mut self,
@ -132,7 +127,7 @@ impl<T: BorrowMut<MapData>> DevMap<T> {
target_if_index: u32,
program: Option<&ProgramFd>,
flags: u64,
) -> Result<(), XdpMapError> {
) -> Result<(), MapError> {
let data = self.inner.borrow_mut();
check_bounds(data, index)?;
let fd = data.fd().as_fd();
@ -149,17 +144,12 @@ impl<T: BorrowMut<MapData>> DevMap<T> {
bpf_map_update_elem(fd, Some(&index), &value, flags)
} else {
if program.is_some() {
return Err(XdpMapError::ChainedProgramNotSupported);
return Err(MapError::ChainedProgramNotSupported);
}
bpf_map_update_elem(fd, Some(&index), &target_if_index, flags)
};
res.map_err(|(_, io_error)| {
MapError::from(SyscallError {
call: "bpf_map_update_elem",
io_error,
})
})?;
res?;
Ok(())
}
}

@ -8,11 +8,13 @@ use std::{
use aya_obj::generated::bpf_devmap_val;
use super::{dev_map::DevMapValue, XdpMapError};
use crate::{
maps::{check_kv_size, hash_map, IterableMap, MapData, MapError, MapIter, MapKeys},
errors::MapError,
maps::{
check_kv_size, hash_map, xdp::dev_map::DevMapValue, IterableMap, MapData, MapIter, MapKeys,
},
programs::ProgramFd,
sys::{bpf_map_lookup_elem, SyscallError},
sys::bpf_map_lookup_elem,
FEATURES,
};
@ -83,12 +85,7 @@ impl<T: Borrow<MapData>> DevMapHash<T> {
})
})
};
value
.map_err(|(_, io_error)| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?
.ok_or(MapError::KeyNotFound)
value?.ok_or(MapError::KeyNotFound)
}
/// An iterator over the elements of the devmap in arbitrary order.
@ -126,7 +123,7 @@ impl<T: BorrowMut<MapData>> DevMapHash<T> {
target_if_index: u32,
program: Option<&ProgramFd>,
flags: u64,
) -> Result<(), XdpMapError> {
) -> Result<(), MapError> {
if FEATURES.devmap_prog_id() {
let mut value = unsafe { std::mem::zeroed::<bpf_devmap_val>() };
value.ifindex = target_if_index;
@ -139,7 +136,7 @@ impl<T: BorrowMut<MapData>> DevMapHash<T> {
hash_map::insert(self.inner.borrow_mut(), &key, &value, flags)?;
} else {
if program.is_some() {
return Err(XdpMapError::ChainedProgramNotSupported);
return Err(MapError::ChainedProgramNotSupported);
}
hash_map::insert(self.inner.borrow_mut(), &key, &target_if_index, flags)?;
}

@ -7,19 +7,4 @@ mod xsk_map;
pub use cpu_map::CpuMap;
pub use dev_map::DevMap;
pub use dev_map_hash::DevMapHash;
use thiserror::Error;
pub use xsk_map::XskMap;
use super::MapError;
#[derive(Error, Debug)]
/// Errors occuring from working with XDP maps.
pub enum XdpMapError {
/// Chained programs are not supported.
#[error("chained programs are not supported by the current kernel")]
ChainedProgramNotSupported,
/// Map operation failed.
#[error(transparent)]
MapError(#[from] MapError),
}

@ -7,7 +7,7 @@ use std::{
use crate::{
maps::{check_bounds, check_kv_size, MapData, MapError},
sys::{bpf_map_update_elem, SyscallError},
sys::bpf_map_update_elem,
};
/// An array of AF_XDP sockets.
@ -70,12 +70,7 @@ impl<T: BorrowMut<MapData>> XskMap<T> {
let data = self.inner.borrow_mut();
check_bounds(data, index)?;
let fd = data.fd().as_fd();
bpf_map_update_elem(fd, Some(&index), &socket_fd.as_raw_fd(), flags).map_err(
|(_, io_error)| SyscallError {
call: "bpf_map_update_elem",
io_error,
},
)?;
bpf_map_update_elem(fd, Some(&index), &socket_fd.as_raw_fd(), flags)?;
Ok(())
}
}

@ -1,29 +0,0 @@
//! Pinning BPF objects to the BPF filesystem.
use thiserror::Error;
use crate::sys::SyscallError;
/// An error ocurred working with a pinned BPF object.
#[derive(Error, Debug)]
pub enum PinError {
/// 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 `{}`", path.display())]
InvalidPinPath {
/// The path.
path: std::path::PathBuf,
#[source]
/// The source error.
error: std::ffi::NulError,
},
/// An error ocurred making a syscall.
#[error(transparent)]
SyscallError(#[from] SyscallError),
}

@ -3,12 +3,13 @@
use std::os::fd::AsFd;
use crate::{
errors::{LinkError, ProgramError},
generated::{bpf_attach_type::BPF_CGROUP_DEVICE, bpf_prog_type::BPF_PROG_TYPE_CGROUP_DEVICE},
programs::{
bpf_prog_get_fd_by_id, define_link_wrapper, load_program, query, CgroupAttachMode, FdLink,
Link, ProgAttachLink, ProgramData, ProgramError, ProgramFd,
Link, ProgAttachLink, ProgramData, ProgramFd,
},
sys::{bpf_link_create, LinkTarget, ProgQueryTarget, SyscallError},
sys::{bpf_link_create, LinkTarget, ProgQueryTarget},
util::KernelVersion,
};
@ -65,7 +66,7 @@ impl CgroupDevice {
&mut self,
cgroup: T,
mode: CgroupAttachMode,
) -> Result<CgroupDeviceLinkId, ProgramError> {
) -> Result<CgroupDeviceLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let cgroup_fd = cgroup.as_fd();
@ -78,11 +79,7 @@ impl CgroupDevice {
None,
mode.into(),
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
self.data
.links
.insert(CgroupDeviceLink::new(CgroupDeviceLinkInner::Fd(
@ -106,14 +103,14 @@ impl CgroupDevice {
pub fn take_link(
&mut self,
link_id: CgroupDeviceLinkId,
) -> Result<CgroupDeviceLink, ProgramError> {
) -> Result<CgroupDeviceLink, LinkError> {
self.data.take_link(link_id)
}
/// Detaches the program
///
/// See [CgroupDevice::attach].
pub fn detach(&mut self, link_id: CgroupDeviceLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: CgroupDeviceLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -164,7 +161,7 @@ impl Link for CgroupDeviceLinkInner {
}
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
match self {
Self::Fd(fd) => fd.detach(),
Self::ProgAttach(p) => p.detach(),

@ -3,15 +3,16 @@
use std::{hash::Hash, os::fd::AsFd, path::Path};
use crate::{
errors::{LinkError, ProgramError},
generated::{
bpf_attach_type::{BPF_CGROUP_INET_EGRESS, BPF_CGROUP_INET_INGRESS},
bpf_prog_type::BPF_PROG_TYPE_CGROUP_SKB,
},
programs::{
define_link_wrapper, load_program, CgroupAttachMode, FdLink, Link, ProgAttachLink,
ProgramData, ProgramError,
ProgramData,
},
sys::{bpf_link_create, LinkTarget, SyscallError},
sys::{bpf_link_create, LinkTarget},
util::KernelVersion,
VerifierLogLevel,
};
@ -89,7 +90,7 @@ impl CgroupSkb {
cgroup: T,
attach_type: CgroupSkbAttachType,
mode: CgroupAttachMode,
) -> Result<CgroupSkbLinkId, ProgramError> {
) -> Result<CgroupSkbLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let cgroup_fd = cgroup.as_fd();
@ -106,11 +107,7 @@ impl CgroupSkb {
None,
mode.into(),
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
self.data
.links
.insert(CgroupSkbLink::new(CgroupSkbLinkInner::Fd(FdLink::new(
@ -129,14 +126,14 @@ impl CgroupSkb {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: CgroupSkbLinkId) -> Result<CgroupSkbLink, ProgramError> {
pub fn take_link(&mut self, link_id: CgroupSkbLinkId) -> Result<CgroupSkbLink, LinkError> {
self.data.take_link(link_id)
}
/// Detaches the program.
///
/// See [CgroupSkb::attach].
pub fn detach(&mut self, link_id: CgroupSkbLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: CgroupSkbLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -180,7 +177,7 @@ impl Link for CgroupSkbLinkInner {
}
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
match self {
Self::Fd(fd) => fd.detach(),
Self::ProgAttach(p) => p.detach(),

@ -5,12 +5,13 @@ use std::{hash::Hash, os::fd::AsFd, path::Path};
pub use aya_obj::programs::CgroupSockAttachType;
use crate::{
errors::{LinkError, ProgramError},
generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK,
programs::{
define_link_wrapper, load_program, CgroupAttachMode, FdLink, Link, ProgAttachLink,
ProgramData, ProgramError,
ProgramData,
},
sys::{bpf_link_create, LinkTarget, SyscallError},
sys::{bpf_link_create, LinkTarget},
util::KernelVersion,
VerifierLogLevel,
};
@ -71,7 +72,7 @@ impl CgroupSock {
&mut self,
cgroup: T,
mode: CgroupAttachMode,
) -> Result<CgroupSockLinkId, ProgramError> {
) -> Result<CgroupSockLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let cgroup_fd = cgroup.as_fd();
@ -84,11 +85,7 @@ impl CgroupSock {
None,
mode.into(),
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
self.data
.links
.insert(CgroupSockLink::new(CgroupSockLinkInner::Fd(FdLink::new(
@ -107,14 +104,14 @@ impl CgroupSock {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: CgroupSockLinkId) -> Result<CgroupSockLink, ProgramError> {
pub fn take_link(&mut self, link_id: CgroupSockLinkId) -> Result<CgroupSockLink, LinkError> {
self.data.take_link(link_id)
}
/// Detaches the program.
///
/// See [CgroupSock::attach].
pub fn detach(&mut self, link_id: CgroupSockLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: CgroupSockLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -155,7 +152,7 @@ impl Link for CgroupSockLinkInner {
}
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
match self {
Self::Fd(fd) => fd.detach(),
Self::ProgAttach(p) => p.detach(),

@ -5,12 +5,13 @@ use std::{hash::Hash, os::fd::AsFd, path::Path};
pub use aya_obj::programs::CgroupSockAddrAttachType;
use crate::{
errors::{LinkError, ProgramError},
generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
programs::{
define_link_wrapper, load_program, CgroupAttachMode, FdLink, Link, ProgAttachLink,
ProgramData, ProgramError,
ProgramData,
},
sys::{bpf_link_create, LinkTarget, SyscallError},
sys::{bpf_link_create, LinkTarget},
util::KernelVersion,
VerifierLogLevel,
};
@ -72,7 +73,7 @@ impl CgroupSockAddr {
&mut self,
cgroup: T,
mode: CgroupAttachMode,
) -> Result<CgroupSockAddrLinkId, ProgramError> {
) -> Result<CgroupSockAddrLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let cgroup_fd = cgroup.as_fd();
@ -85,11 +86,7 @@ impl CgroupSockAddr {
None,
mode.into(),
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
self.data
.links
.insert(CgroupSockAddrLink::new(CgroupSockAddrLinkInner::Fd(
@ -111,14 +108,14 @@ impl CgroupSockAddr {
pub fn take_link(
&mut self,
link_id: CgroupSockAddrLinkId,
) -> Result<CgroupSockAddrLink, ProgramError> {
) -> Result<CgroupSockAddrLink, LinkError> {
self.data.take_link(link_id)
}
/// Detaches the program.
///
/// See [CgroupSockAddr::attach].
pub fn detach(&mut self, link_id: CgroupSockAddrLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: CgroupSockAddrLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -159,7 +156,7 @@ impl Link for CgroupSockAddrLinkInner {
}
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
match self {
Self::Fd(fd) => fd.detach(),
Self::ProgAttach(p) => p.detach(),

@ -5,12 +5,13 @@ use std::{hash::Hash, os::fd::AsFd, path::Path};
pub use aya_obj::programs::CgroupSockoptAttachType;
use crate::{
errors::{LinkError, ProgramError},
generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCKOPT,
programs::{
define_link_wrapper, load_program, CgroupAttachMode, FdLink, Link, ProgAttachLink,
ProgramData, ProgramError,
ProgramData,
},
sys::{bpf_link_create, LinkTarget, SyscallError},
sys::{bpf_link_create, LinkTarget},
util::KernelVersion,
VerifierLogLevel,
};
@ -69,7 +70,7 @@ impl CgroupSockopt {
&mut self,
cgroup: T,
mode: CgroupAttachMode,
) -> Result<CgroupSockoptLinkId, ProgramError> {
) -> Result<CgroupSockoptLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let cgroup_fd = cgroup.as_fd();
@ -82,11 +83,7 @@ impl CgroupSockopt {
None,
mode.into(),
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
self.data
.links
.insert(CgroupSockoptLink::new(CgroupSockoptLinkInner::Fd(
@ -110,14 +107,14 @@ impl CgroupSockopt {
pub fn take_link(
&mut self,
link_id: CgroupSockoptLinkId,
) -> Result<CgroupSockoptLink, ProgramError> {
) -> Result<CgroupSockoptLink, LinkError> {
self.data.take_link(link_id)
}
/// Detaches the program.
///
/// See [CgroupSockopt::attach].
pub fn detach(&mut self, link_id: CgroupSockoptLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: CgroupSockoptLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -158,7 +155,7 @@ impl Link for CgroupSockoptLinkInner {
}
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
match self {
Self::Fd(fd) => fd.detach(),
Self::ProgAttach(p) => p.detach(),

@ -3,12 +3,13 @@
use std::{hash::Hash, os::fd::AsFd};
use crate::{
errors::{LinkError, ProgramError},
generated::{bpf_attach_type::BPF_CGROUP_SYSCTL, bpf_prog_type::BPF_PROG_TYPE_CGROUP_SYSCTL},
programs::{
define_link_wrapper, load_program, CgroupAttachMode, FdLink, Link, ProgAttachLink,
ProgramData, ProgramError,
ProgramData,
},
sys::{bpf_link_create, LinkTarget, SyscallError},
sys::{bpf_link_create, LinkTarget},
util::KernelVersion,
};
@ -64,7 +65,7 @@ impl CgroupSysctl {
&mut self,
cgroup: T,
mode: CgroupAttachMode,
) -> Result<CgroupSysctlLinkId, ProgramError> {
) -> Result<CgroupSysctlLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let cgroup_fd = cgroup.as_fd();
@ -77,11 +78,7 @@ impl CgroupSysctl {
None,
mode.into(),
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
self.data
.links
.insert(CgroupSysctlLink::new(CgroupSysctlLinkInner::Fd(
@ -105,14 +102,14 @@ impl CgroupSysctl {
pub fn take_link(
&mut self,
link_id: CgroupSysctlLinkId,
) -> Result<CgroupSysctlLink, ProgramError> {
) -> Result<CgroupSysctlLink, LinkError> {
self.data.take_link(link_id)
}
/// Detaches the program.
///
/// See [CgroupSysctl::attach].
pub fn detach(&mut self, link_id: CgroupSysctlLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: CgroupSysctlLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
}
@ -139,7 +136,7 @@ impl Link for CgroupSysctlLinkInner {
}
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
match self {
Self::Fd(fd) => fd.detach(),
Self::ProgAttach(p) => p.detach(),

@ -3,26 +3,16 @@
use std::os::fd::{AsFd as _, BorrowedFd};
use object::Endianness;
use thiserror::Error;
use crate::{
errors::{EbpfInternalError, InternalLinkError, LinkError, ProgramError},
generated::{bpf_attach_type::BPF_CGROUP_INET_INGRESS, bpf_prog_type::BPF_PROG_TYPE_EXT},
obj::btf::BtfKind,
programs::{
define_link_wrapper, load_program, FdLink, FdLinkId, ProgramData, ProgramError, ProgramFd,
},
sys::{self, bpf_link_create, LinkTarget, SyscallError},
programs::{define_link_wrapper, load_program, FdLink, FdLinkId, ProgramData, ProgramFd},
sys::{self, bpf_link_create, LinkTarget},
Btf,
};
/// The type returned when loading or attaching an [`Extension`] fails.
#[derive(Debug, Error)]
pub enum ExtensionError {
/// Target BPF program does not have BTF loaded to the kernel.
#[error("target BPF program does not have BTF loaded to the kernel")]
NoBTF,
}
/// A program used to extend existing BPF programs.
///
/// [`Extension`] programs can be loaded to replace a global
@ -86,16 +76,19 @@ impl Extension {
///
/// The returned value can be used to detach the extension and restore the
/// original function, see [Extension::detach].
pub fn attach(&mut self) -> Result<ExtensionLinkId, ProgramError> {
pub fn attach(&mut self) -> Result<ExtensionLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let target_fd = self
.data
.attach_prog_fd
.as_ref()
.ok_or(ProgramError::NotLoaded)?;
.ok_or(InternalLinkError::TargetProgramNotLoaded)?;
let target_fd = target_fd.as_fd();
let btf_id = self.data.attach_btf_id.ok_or(ProgramError::NotLoaded)?;
let btf_id = self
.data
.attach_btf_id
.ok_or(InternalLinkError::TargetNoBtf)?;
// the attach type must be set as 0, which is bpf_attach_type::BPF_CGROUP_INET_INGRESS
let link_fd = bpf_link_create(
prog_fd,
@ -104,11 +97,7 @@ impl Extension {
Some(btf_id),
0,
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
self.data
.links
.insert(ExtensionLink::new(FdLink::new(link_fd)))
@ -129,7 +118,7 @@ impl Extension {
&mut self,
program: &ProgramFd,
func_name: &str,
) -> Result<ExtensionLinkId, ProgramError> {
) -> Result<ExtensionLinkId, LinkError> {
let target_fd = program.as_fd();
let (_, btf_id) = get_btf_info(target_fd, func_name)?;
let prog_fd = self.fd()?;
@ -142,11 +131,7 @@ impl Extension {
Some(btf_id),
0,
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
self.data
.links
.insert(ExtensionLink::new(FdLink::new(link_fd)))
@ -156,7 +141,7 @@ impl Extension {
///
/// Detaching restores the original code overridden by the extension program.
/// See [Extension::attach].
pub fn detach(&mut self, link_id: ExtensionLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: ExtensionLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -164,7 +149,7 @@ impl Extension {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: ExtensionLinkId) -> Result<ExtensionLink, ProgramError> {
pub fn take_link(&mut self, link_id: ExtensionLinkId) -> Result<ExtensionLink, LinkError> {
self.data.take_link(link_id)
}
}
@ -180,7 +165,7 @@ fn get_btf_info(
// btf_id refers to the ID of the program btf that was loaded with bpf(BPF_BTF_LOAD)
if info.btf_id == 0 {
return Err(ProgramError::ExtensionError(ExtensionError::NoBTF));
return Err(EbpfInternalError::NoBTF.into());
}
// the bpf fd of the BTF object
@ -200,11 +185,9 @@ fn get_btf_info(
break;
}
let btf = Btf::parse(&buf, Endianness::default()).map_err(ProgramError::Btf)?;
let btf = Btf::parse(&buf, Endianness::default())?;
let btf_id = btf
.id_by_type_name_kind(func_name, BtfKind::Func)
.map_err(ProgramError::Btf)?;
let btf_id = btf.id_by_type_name_kind(func_name, BtfKind::Func)?;
Ok((btf_fd, btf_id))
}

@ -1,11 +1,12 @@
//! Fentry programs.
use crate::{
errors::{LinkError, ProgramError},
generated::{bpf_attach_type::BPF_TRACE_FENTRY, bpf_prog_type::BPF_PROG_TYPE_TRACING},
obj::btf::{Btf, BtfKind},
programs::{
define_link_wrapper, load_program, utils::attach_raw_tracepoint, FdLink, FdLinkId,
ProgramData, ProgramError,
ProgramData,
},
};
@ -64,14 +65,14 @@ impl FEntry {
/// Attaches the program.
///
/// The returned value can be used to detach, see [FEntry::detach].
pub fn attach(&mut self) -> Result<FEntryLinkId, ProgramError> {
pub fn attach(&mut self) -> Result<FEntryLinkId, LinkError> {
attach_raw_tracepoint(&mut self.data, None)
}
/// Detaches the program.
///
/// See [FEntry::attach].
pub fn detach(&mut self, link_id: FEntryLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: FEntryLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -79,7 +80,7 @@ impl FEntry {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: FEntryLinkId) -> Result<FEntryLink, ProgramError> {
pub fn take_link(&mut self, link_id: FEntryLinkId) -> Result<FEntryLink, LinkError> {
self.data.take_link(link_id)
}
}

@ -1,11 +1,12 @@
//! Fexit programs.
use crate::{
errors::{LinkError, ProgramError},
generated::{bpf_attach_type::BPF_TRACE_FEXIT, bpf_prog_type::BPF_PROG_TYPE_TRACING},
obj::btf::{Btf, BtfKind},
programs::{
define_link_wrapper, load_program, utils::attach_raw_tracepoint, FdLink, FdLinkId,
ProgramData, ProgramError,
ProgramData,
},
};
@ -64,14 +65,14 @@ impl FExit {
/// Attaches the program.
///
/// The returned value can be used to detach, see [FExit::detach].
pub fn attach(&mut self) -> Result<FExitLinkId, ProgramError> {
pub fn attach(&mut self) -> Result<FExitLinkId, LinkError> {
attach_raw_tracepoint(&mut self.data, None)
}
/// Detaches the program.
///
/// See [FExit::attach].
pub fn detach(&mut self, link_id: FExitLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: FExitLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -79,7 +80,7 @@ impl FExit {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: FExitLinkId) -> Result<FExitLink, ProgramError> {
pub fn take_link(&mut self, link_id: FExitLinkId) -> Result<FExitLink, LinkError> {
self.data.take_link(link_id)
}
}

@ -9,14 +9,13 @@ use std::{
use aya_obj::generated::{bpf_prog_info, bpf_prog_type};
use super::{
utils::{boot_time, get_fdinfo},
ProgramError, ProgramFd,
};
use crate::{
sys::{
bpf_get_object, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd, iter_prog_ids, SyscallError,
errors::ProgramError,
programs::{
utils::{boot_time, get_fdinfo},
ProgramFd,
},
sys::{bpf_get_object, bpf_prog_get_fd_by_id, bpf_prog_get_info_by_fd, iter_prog_ids},
util::bytes_of_bpf_name,
FEATURES,
};
@ -214,10 +213,7 @@ impl ProgramInfo {
// TODO: avoid this unwrap by adding a new error variant.
let path_string = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| SyscallError {
call: "BPF_OBJ_GET",
io_error,
})?;
let fd = bpf_get_object(&path_string)?;
Self::new_from_fd(fd.as_fd())
}
@ -235,7 +231,7 @@ macro_rules! impl_info {
/// Returns metadata information of this program.
///
/// Uses kernel v4.13 features.
pub fn info(&self) -> Result<ProgramInfo, ProgramError> {
pub fn info(&self) -> Result<ProgramInfo, crate::errors::ProgramError> {
let ProgramFd(fd) = self.fd()?;
ProgramInfo::new_from_fd(fd.as_fd())
}

@ -1,20 +1,14 @@
//! Kernel space probes.
use std::{
ffi::OsStr,
io,
os::fd::AsFd as _,
path::{Path, PathBuf},
};
use thiserror::Error;
use std::{ffi::OsStr, os::fd::AsFd as _, path::Path};
use crate::{
errors::LinkError,
generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_KPROBE},
programs::{
define_link_wrapper, load_program,
perf_attach::{PerfLinkIdInner, PerfLinkInner},
probe::{attach, ProbeKind},
FdLink, LinkError, ProgramData, ProgramError,
FdLink, ProgramData, ProgramError,
},
sys::bpf_link_get_info_by_fd,
VerifierLogLevel,
@ -77,14 +71,14 @@ impl KProbe {
&mut self,
fn_name: T,
offset: u64,
) -> Result<KProbeLinkId, ProgramError> {
) -> Result<KProbeLinkId, LinkError> {
attach(&mut self.data, self.kind, fn_name.as_ref(), offset, None)
}
/// Detaches the program.
///
/// See [KProbe::attach].
pub fn detach(&mut self, link_id: KProbeLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: KProbeLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -92,7 +86,7 @@ impl KProbe {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: KProbeLinkId) -> Result<KProbeLink, ProgramError> {
pub fn take_link(&mut self, link_id: KProbeLinkId) -> Result<KProbeLink, LinkError> {
self.data.take_link(link_id)
}
@ -117,20 +111,6 @@ define_link_wrapper!(
PerfLinkIdInner
);
/// The type returned when attaching a [`KProbe`] fails.
#[derive(Debug, Error)]
pub enum KProbeError {
/// Error detaching from debugfs
#[error("`{filename}`")]
FileError {
/// The file name
filename: PathBuf,
/// The [`io::Error`] returned from the file operation
#[source]
io_error: io::Error,
},
}
impl TryFrom<KProbeLink> for FdLink {
type Error = LinkError;

@ -7,16 +7,14 @@ use std::{
path::{Path, PathBuf},
};
use thiserror::Error;
use crate::{
errors::LinkError,
generated::{
bpf_attach_type, BPF_F_AFTER, BPF_F_ALLOW_MULTI, BPF_F_ALLOW_OVERRIDE, BPF_F_BEFORE,
BPF_F_ID, BPF_F_LINK, BPF_F_REPLACE,
},
pin::PinError,
programs::{MultiProgLink, MultiProgram, ProgramError, ProgramFd, ProgramId},
sys::{bpf_get_object, bpf_pin_object, bpf_prog_attach, bpf_prog_detach, SyscallError},
sys::{bpf_get_object, bpf_pin_object, bpf_prog_attach, bpf_prog_detach},
};
/// A Link.
@ -28,7 +26,7 @@ pub trait Link: std::fmt::Debug + 'static {
fn id(&self) -> Self::Id;
/// Detaches the LinkOwnedLink is gone... but this doesn't work :(
fn detach(self) -> Result<(), ProgramError>;
fn detach(self) -> Result<(), LinkError>;
}
/// Program attachment mode.
@ -67,33 +65,33 @@ impl<T: Link> LinkMap<T> {
}
}
pub(crate) fn insert(&mut self, link: T) -> Result<T::Id, ProgramError> {
pub(crate) fn insert(&mut self, link: T) -> Result<T::Id, LinkError> {
let id = link.id();
match self.links.entry(link.id()) {
Entry::Occupied(_) => return Err(ProgramError::AlreadyAttached),
Entry::Occupied(_) => return Err(LinkError::AlreadyAttached),
Entry::Vacant(e) => e.insert(link),
};
Ok(id)
}
pub(crate) fn remove(&mut self, link_id: T::Id) -> Result<(), ProgramError> {
pub(crate) fn remove(&mut self, link_id: T::Id) -> Result<(), LinkError> {
self.links
.remove(&link_id)
.ok_or(ProgramError::NotAttached)?
.ok_or(LinkError::NotAttached)?
.detach()
}
pub(crate) fn remove_all(&mut self) -> Result<(), ProgramError> {
pub(crate) fn remove_all(&mut self) -> Result<(), LinkError> {
for (_, link) in self.links.drain() {
link.detach()?;
}
Ok(())
}
pub(crate) fn forget(&mut self, link_id: T::Id) -> Result<T, ProgramError> {
self.links.remove(&link_id).ok_or(ProgramError::NotAttached)
pub(crate) fn forget(&mut self, link_id: T::Id) -> Result<T, LinkError> {
self.links.remove(&link_id).ok_or(LinkError::NotAttached)
}
}
@ -159,8 +157,6 @@ impl FdLink {
/// # #[error(transparent)]
/// # Ebpf(#[from] aya::EbpfError),
/// # #[error(transparent)]
/// # Pin(#[from] aya::pin::PinError),
/// # #[error(transparent)]
/// # Program(#[from] aya::programs::ProgramError)
/// # }
/// # let mut bpf = aya::Ebpf::load(&[])?;
@ -171,20 +167,12 @@ impl FdLink {
/// let pinned_link = fd_link.pin("/sys/fs/bpf/example")?;
/// # Ok::<(), Error>(())
/// ```
pub fn pin<P: AsRef<Path>>(self, path: P) -> Result<PinnedLink, PinError> {
pub fn pin<P: AsRef<Path>>(self, path: P) -> Result<PinnedLink, LinkError> {
use std::os::unix::ffi::OsStrExt as _;
let path = path.as_ref();
let path_string = CString::new(path.as_os_str().as_bytes()).map_err(|error| {
PinError::InvalidPinPath {
path: path.into(),
error,
}
})?;
bpf_pin_object(self.fd.as_fd(), &path_string).map_err(|(_, io_error)| SyscallError {
call: "BPF_OBJ_PIN",
io_error,
})?;
let path_string = CString::new(path.as_os_str().as_bytes())?;
bpf_pin_object(self.fd.as_fd(), &path_string)?;
Ok(PinnedLink::new(path.into(), self))
}
}
@ -196,7 +184,7 @@ impl Link for FdLink {
FdLinkId(self.fd.as_raw_fd())
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
// detach is a noop since it consumes self. once self is consumed, drop will be triggered
// and the link will be detached.
//
@ -234,12 +222,7 @@ impl PinnedLink {
// TODO: avoid this unwrap by adding a new error variant.
let path_string = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| {
LinkError::SyscallError(SyscallError {
call: "BPF_OBJ_GET",
io_error,
})
})?;
let fd = bpf_get_object(&path_string)?;
Ok(Self::new(path.as_ref().to_path_buf(), FdLink::new(fd)))
}
@ -280,7 +263,7 @@ impl ProgAttachLink {
target_fd: BorrowedFd<'_>,
attach_type: bpf_attach_type,
mode: CgroupAttachMode,
) -> Result<Self, ProgramError> {
) -> Result<Self, LinkError> {
// The link is going to own this new file descriptor so we are
// going to need a duplicate whose lifetime we manage. Let's
// duplicate it prior to attaching it so the new file
@ -311,7 +294,7 @@ impl Link for ProgAttachLink {
)
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
bpf_prog_detach(
self.prog_fd.as_fd(),
self.target_fd.as_fd(),
@ -365,7 +348,7 @@ macro_rules! define_link_wrapper {
$wrapper_id(self.0.as_ref().unwrap().id())
}
fn detach(mut self) -> Result<(), ProgramError> {
fn detach(mut self) -> Result<(), crate::errors::LinkError> {
self.0.take().unwrap().detach()
}
}
@ -386,17 +369,6 @@ macro_rules! define_link_wrapper {
pub(crate) use define_link_wrapper;
#[derive(Error, Debug)]
/// Errors from operations on links.
pub enum LinkError {
/// Invalid link.
#[error("Invalid link")]
InvalidLink,
/// Syscall failed.
#[error(transparent)]
SyscallError(#[from] SyscallError),
}
#[derive(Debug)]
pub(crate) enum LinkRef {
Id(u32),
@ -525,8 +497,9 @@ mod tests {
use super::{FdLink, Link, LinkMap};
use crate::{
errors::LinkError,
generated::{BPF_F_ALLOW_MULTI, BPF_F_ALLOW_OVERRIDE},
programs::{CgroupAttachMode, ProgramError},
programs::CgroupAttachMode,
sys::override_syscall,
};
@ -555,7 +528,7 @@ mod tests {
TestLinkId(self.id.0, self.id.1)
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
*self.detached.borrow_mut() += 1;
Ok(())
}
@ -591,7 +564,7 @@ mod tests {
links.insert(TestLink::new(1, 2)).unwrap();
assert_matches!(
links.insert(TestLink::new(1, 2)),
Err(ProgramError::AlreadyAttached)
Err(LinkError::AlreadyAttached)
);
}
@ -604,7 +577,7 @@ mod tests {
let l1_id2 = l1.id();
links.insert(TestLink::new(1, 2)).unwrap();
links.remove(l1_id1).unwrap();
assert_matches!(links.remove(l1_id2), Err(ProgramError::NotAttached));
assert_matches!(links.remove(l1_id2), Err(LinkError::NotAttached));
}
#[test]

@ -2,6 +2,7 @@
use std::os::fd::{AsFd, AsRawFd as _, RawFd};
use crate::{
errors::LinkError,
generated::{bpf_attach_type::BPF_LIRC_MODE2, bpf_prog_type::BPF_PROG_TYPE_LIRC_MODE2},
programs::{
load_program, query, CgroupAttachMode, Link, ProgramData, ProgramError, ProgramFd,
@ -61,7 +62,7 @@ impl LircMode2 {
/// Attaches the program to the given lirc device.
///
/// The returned value can be used to detach, see [LircMode2::detach].
pub fn attach<T: AsFd>(&mut self, lircdev: T) -> Result<LircLinkId, ProgramError> {
pub fn attach<T: AsFd>(&mut self, lircdev: T) -> Result<LircLinkId, LinkError> {
let prog_fd = self.fd()?;
// The link is going to own this new file descriptor so we are
@ -85,7 +86,7 @@ impl LircMode2 {
/// Detaches the program.
///
/// See [LircMode2::attach].
pub fn detach(&mut self, link_id: LircLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: LircLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -93,7 +94,7 @@ impl LircMode2 {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: LircLinkId) -> Result<LircLink, ProgramError> {
pub fn take_link(&mut self, link_id: LircLinkId) -> Result<LircLink, LinkError> {
self.data.take_link(link_id)
}
@ -148,7 +149,7 @@ impl Link for LircLink {
LircLinkId(self.prog_fd.as_fd().as_raw_fd(), self.target_fd.as_raw_fd())
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
bpf_prog_detach(self.prog_fd.as_fd(), self.target_fd.as_fd(), BPF_LIRC_MODE2)
.map_err(Into::into)
}

@ -1,6 +1,7 @@
//! LSM probes.
use crate::{
errors::LinkError,
generated::{bpf_attach_type::BPF_LSM_MAC, bpf_prog_type::BPF_PROG_TYPE_LSM},
obj::btf::{Btf, BtfKind},
programs::{
@ -70,14 +71,14 @@ impl Lsm {
/// Attaches the program.
///
/// The returned value can be used to detach, see [Lsm::detach].
pub fn attach(&mut self) -> Result<LsmLinkId, ProgramError> {
pub fn attach(&mut self) -> Result<LsmLinkId, LinkError> {
attach_raw_tracepoint(&mut self.data, None)
}
/// Detaches the program.
///
/// See [Lsm::attach].
pub fn detach(&mut self, link_id: LsmLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: LsmLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -85,7 +86,7 @@ impl Lsm {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: LsmLinkId) -> Result<LsmLink, ProgramError> {
pub fn take_link(&mut self, link_id: LsmLinkId) -> Result<LsmLink, LinkError> {
self.data.take_link(link_id)
}
}

@ -81,7 +81,6 @@ use info::impl_info;
pub use info::{loaded_programs, ProgramInfo, ProgramType};
use libc::ENOSPC;
use tc::SchedClassifierLink;
use thiserror::Error;
// re-export the main items needed to load and attach
pub use crate::programs::{
@ -91,10 +90,10 @@ pub use crate::programs::{
cgroup_sock_addr::{CgroupSockAddr, CgroupSockAddrAttachType},
cgroup_sockopt::{CgroupSockopt, CgroupSockoptAttachType},
cgroup_sysctl::CgroupSysctl,
extension::{Extension, ExtensionError},
extension::Extension,
fentry::FEntry,
fexit::FExit,
kprobe::{KProbe, KProbeError},
kprobe::KProbe,
links::{CgroupAttachMode, Link, LinkOrder},
lirc_mode2::LircMode2,
lsm::Lsm,
@ -105,120 +104,27 @@ pub use crate::programs::{
sk_msg::SkMsg,
sk_skb::{SkSkb, SkSkbKind},
sock_ops::SockOps,
socket_filter::{SocketFilter, SocketFilterError},
tc::{SchedClassifier, TcAttachType, TcError},
socket_filter::SocketFilter,
tc::{SchedClassifier, TcAttachType},
tp_btf::BtfTracePoint,
trace_point::{TracePoint, TracePointError},
uprobe::{UProbe, UProbeError},
xdp::{Xdp, XdpError, XdpFlags},
trace_point::TracePoint,
uprobe::UProbe,
xdp::{Xdp, XdpFlags},
};
use crate::{
errors::{LinkError, ProgramError, SysError},
generated::{bpf_attach_type, bpf_link_info, bpf_prog_info, bpf_prog_type},
maps::MapError,
obj::{self, btf::BtfError, VerifierLog},
pin::PinError,
obj::{self},
programs::{links::*, perf_attach::*},
sys::{
bpf_btf_get_fd_by_id, bpf_get_object, bpf_link_get_fd_by_id, bpf_link_get_info_by_fd,
bpf_load_program, bpf_pin_object, bpf_prog_get_fd_by_id, bpf_prog_query, iter_link_ids,
retry_with_verifier_logs, EbpfLoadProgramAttrs, ProgQueryTarget, SyscallError,
retry_with_verifier_logs, EbpfLoadProgramAttrs, ProgQueryTarget,
},
util::KernelVersion,
VerifierLogLevel,
};
/// Error type returned when working with programs.
#[derive(Debug, Error)]
pub enum ProgramError {
/// The program is already loaded.
#[error("the program is already loaded")]
AlreadyLoaded,
/// The program is not loaded.
#[error("the program is not loaded")]
NotLoaded,
/// The program is already attached.
#[error("the program was already attached")]
AlreadyAttached,
/// The program is not attached.
#[error("the program is not attached")]
NotAttached,
/// Loading the program failed.
#[error("the BPF_PROG_LOAD syscall failed. Verifier output: {verifier_log}")]
LoadError {
/// The [`io::Error`] returned by the `BPF_PROG_LOAD` syscall.
#[source]
io_error: io::Error,
/// The error log produced by the kernel verifier.
verifier_log: VerifierLog,
},
/// A syscall failed.
#[error(transparent)]
SyscallError(#[from] SyscallError),
/// The network interface does not exist.
#[error("unknown network interface {name}")]
UnknownInterface {
/// interface name
name: String,
},
/// The program is not of the expected type.
#[error("unexpected program type")]
UnexpectedProgramType,
/// A map error occurred while loading or attaching a program.
#[error(transparent)]
MapError(#[from] MapError),
/// An error occurred while working with a [`KProbe`].
#[error(transparent)]
KProbeError(#[from] KProbeError),
/// An error occurred while working with an [`UProbe`].
#[error(transparent)]
UProbeError(#[from] UProbeError),
/// An error occurred while working with a [`TracePoint`].
#[error(transparent)]
TracePointError(#[from] TracePointError),
/// An error occurred while working with a [`SocketFilter`].
#[error(transparent)]
SocketFilterError(#[from] SocketFilterError),
/// An error occurred while working with an [`Xdp`] program.
#[error(transparent)]
XdpError(#[from] XdpError),
/// An error occurred while working with a TC program.
#[error(transparent)]
TcError(#[from] TcError),
/// An error occurred while working with an [`Extension`] program.
#[error(transparent)]
ExtensionError(#[from] ExtensionError),
/// An error occurred while working with BTF.
#[error(transparent)]
Btf(#[from] BtfError),
/// The program is not attached.
#[error("the program name `{name}` is invalid")]
InvalidName {
/// program name
name: String,
},
/// An error occurred while working with IO.
#[error(transparent)]
IOError(#[from] io::Error),
}
/// A [`Program`] file descriptor.
#[derive(Debug)]
pub struct ProgramFd(crate::MockableFd);
@ -334,7 +240,7 @@ impl Program {
}
/// Pin the program to the provided path
pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), PinError> {
pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ProgramError> {
match self {
Self::KProbe(p) => p.pin(path),
Self::UProbe(p) => p.pin(path),
@ -538,10 +444,7 @@ impl<T: Link> ProgramData<T> {
// TODO: avoid this unwrap by adding a new error variant.
let path_string = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let fd = bpf_get_object(&path_string).map_err(|(_, io_error)| SyscallError {
call: "bpf_obj_get",
io_error,
})?;
let fd = bpf_get_object(&path_string)?;
let info = ProgramInfo::new_from_fd(fd.as_fd())?;
let name = info.name_as_str().map(|s| s.to_string());
@ -554,7 +457,7 @@ impl<T: Link> ProgramData<T> {
self.fd.as_ref().ok_or(ProgramError::NotLoaded)
}
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, LinkError> {
self.links.forget(link_id)
}
}
@ -567,26 +470,16 @@ fn unload_program<T: Link>(data: &mut ProgramData<T>) -> Result<(), ProgramError
.map(|ProgramFd { .. }| ())
}
fn pin_program<T: Link, P: AsRef<Path>>(data: &ProgramData<T>, path: P) -> Result<(), PinError> {
fn pin_program<T: Link, P: AsRef<Path>>(
data: &ProgramData<T>,
path: P,
) -> Result<(), ProgramError> {
use std::os::unix::ffi::OsStrExt as _;
let fd = data.fd.as_ref().ok_or(PinError::NoFd {
name: data
.name
.as_deref()
.unwrap_or("<unknown program>")
.to_string(),
})?;
let fd = data.fd.as_ref().ok_or(ProgramError::NotLoaded)?;
let path = path.as_ref();
let path_string =
CString::new(path.as_os_str().as_bytes()).map_err(|error| PinError::InvalidPinPath {
path: path.into(),
error,
})?;
bpf_pin_object(fd.as_fd(), &path_string).map_err(|(_, io_error)| SyscallError {
call: "BPF_OBJ_PIN",
io_error,
})?;
let path_string = CString::new(path.as_os_str().as_bytes())?;
bpf_pin_object(fd.as_fd(), &path_string)?;
Ok(())
}
@ -640,8 +533,7 @@ fn load_program<T: Link>(
if name.len() > 15 {
name.truncate(15);
}
let prog_name = CString::new(name.clone())
.map_err(|_| ProgramError::InvalidName { name: name.clone() })?;
let prog_name = CString::new(name.clone())?;
Some(prog_name)
} else {
None
@ -674,8 +566,8 @@ fn load_program<T: Link>(
*fd = Some(ProgramFd(prog_fd));
Ok(())
}
Err((_, io_error)) => Err(ProgramError::LoadError {
io_error,
Err((_, source)) => Err(ProgramError::LoadError {
source,
verifier_log,
}),
}
@ -707,16 +599,14 @@ pub(crate) fn query(
prog_ids.resize(prog_cnt as usize, 0);
return Ok((revision, prog_ids));
}
Err((_, io_error)) => {
if retries == 0 && io_error.raw_os_error() == Some(ENOSPC) {
prog_ids.resize(prog_cnt as usize, 0);
retries += 1;
} else {
return Err(SyscallError {
call: "bpf_prog_query",
io_error,
Err((_, e)) => {
if let SysError::Syscall { call: _, io_error } = e {
if retries == 0 && io_error.raw_os_error() == Some(ENOSPC) {
prog_ids.resize(prog_cnt as usize, 0);
retries += 1;
}
.into());
} else {
return Err(e.into());
}
}
}
@ -874,7 +764,7 @@ macro_rules! impl_program_pin{
/// 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> {
pub fn pin<P: AsRef<Path>>(&mut self, path: P) -> Result<(), ProgramError> {
self.data.path = Some(path.as_ref().to_path_buf());
pin_program(&self.data, path)
}

@ -2,12 +2,13 @@
use std::os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd};
use crate::{
errors::LinkError,
generated::bpf_attach_type::BPF_PERF_EVENT,
programs::{
probe::{detach_debug_fs, ProbeEvent},
FdLink, Link, ProgramError,
FdLink, Link,
},
sys::{bpf_link_create, perf_event_ioctl, LinkTarget, SysResult, SyscallError},
sys::{bpf_link_create, perf_event_ioctl, LinkTarget, SysResult},
FEATURES, PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_SET_BPF,
};
@ -33,7 +34,7 @@ impl Link for PerfLinkInner {
}
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
match self {
Self::FdLink(link) => link.detach(),
Self::PerfLink(link) => link.detach(),
@ -59,7 +60,7 @@ impl Link for PerfLink {
PerfLinkId(self.perf_fd.as_raw_fd())
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
let Self { perf_fd, event } = self;
let _: SysResult<_> = perf_event_ioctl(perf_fd.as_fd(), PERF_EVENT_IOC_DISABLE, 0);
if let Some(event) = event {
@ -73,7 +74,7 @@ impl Link for PerfLink {
pub(crate) fn perf_attach(
prog_fd: BorrowedFd<'_>,
fd: crate::MockableFd,
) -> Result<PerfLinkInner, ProgramError> {
) -> Result<PerfLinkInner, LinkError> {
if FEATURES.bpf_perf_link() {
let link_fd = bpf_link_create(
prog_fd,
@ -82,11 +83,7 @@ pub(crate) fn perf_attach(
None,
0,
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
Ok(PerfLinkInner::FdLink(FdLink::new(link_fd)))
} else {
perf_attach_either(prog_fd, fd, None)
@ -97,7 +94,7 @@ pub(crate) fn perf_attach_debugfs(
prog_fd: BorrowedFd<'_>,
fd: crate::MockableFd,
event: ProbeEvent,
) -> Result<PerfLinkInner, ProgramError> {
) -> Result<PerfLinkInner, LinkError> {
perf_attach_either(prog_fd, fd, Some(event))
}
@ -105,19 +102,9 @@ fn perf_attach_either(
prog_fd: BorrowedFd<'_>,
fd: crate::MockableFd,
event: Option<ProbeEvent>,
) -> Result<PerfLinkInner, ProgramError> {
perf_event_ioctl(fd.as_fd(), PERF_EVENT_IOC_SET_BPF, prog_fd.as_raw_fd()).map_err(
|(_, io_error)| SyscallError {
call: "PERF_EVENT_IOC_SET_BPF",
io_error,
},
)?;
perf_event_ioctl(fd.as_fd(), PERF_EVENT_IOC_ENABLE, 0).map_err(|(_, io_error)| {
SyscallError {
call: "PERF_EVENT_IOC_ENABLE",
io_error,
}
})?;
) -> Result<PerfLinkInner, LinkError> {
perf_event_ioctl(fd.as_fd(), PERF_EVENT_IOC_SET_BPF, prog_fd.as_raw_fd())?;
perf_event_ioctl(fd.as_fd(), PERF_EVENT_IOC_ENABLE, 0)?;
Ok(PerfLinkInner::PerfLink(PerfLink { perf_fd: fd, event }))
}

@ -6,6 +6,7 @@ pub use crate::generated::{
perf_hw_cache_id, perf_hw_cache_op_id, perf_hw_cache_op_result_id, perf_hw_id, perf_sw_ids,
};
use crate::{
errors::LinkError,
generated::{
bpf_link_type,
bpf_prog_type::BPF_PROG_TYPE_PERF_EVENT,
@ -16,11 +17,11 @@ use crate::{
},
programs::{
links::define_link_wrapper,
load_program, perf_attach,
perf_attach::{PerfLinkIdInner, PerfLinkInner},
FdLink, LinkError, ProgramData, ProgramError,
load_program,
perf_attach::{perf_attach, PerfLinkIdInner, PerfLinkInner},
FdLink, ProgramData, ProgramError,
},
sys::{bpf_link_get_info_by_fd, perf_event_open, SyscallError},
sys::{bpf_link_get_info_by_fd, perf_event_open},
};
/// The type of perf event
@ -150,7 +151,7 @@ impl PerfEvent {
scope: PerfEventScope,
sample_policy: SamplePolicy,
inherit: bool,
) -> Result<PerfEventLinkId, ProgramError> {
) -> Result<PerfEventLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let (sample_period, sample_frequency) = match sample_policy {
@ -174,11 +175,7 @@ impl PerfEvent {
false,
inherit,
0,
)
.map_err(|(_code, io_error)| SyscallError {
call: "perf_event_open",
io_error,
})?;
)?;
let link = perf_attach(prog_fd, fd)?;
self.data.links.insert(PerfEventLink::new(link))
@ -187,7 +184,7 @@ impl PerfEvent {
/// Detaches the program.
///
/// See [PerfEvent::attach].
pub fn detach(&mut self, link_id: PerfEventLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: PerfEventLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -195,7 +192,7 @@ impl PerfEvent {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: PerfEventLinkId) -> Result<PerfEventLink, ProgramError> {
pub fn take_link(&mut self, link_id: PerfEventLinkId) -> Result<PerfEventLink, LinkError> {
self.data.take_link(link_id)
}
}

@ -12,12 +12,15 @@ use std::{
use libc::pid_t;
use crate::{
errors::LinkError,
programs::{
kprobe::KProbeError, perf_attach, perf_attach::PerfLinkInner, perf_attach_debugfs,
trace_point::read_sys_fs_trace_point_id, uprobe::UProbeError, utils::find_tracefs_path,
Link, ProgramData, ProgramError,
perf_attach::{perf_attach, PerfLinkInner},
perf_attach_debugfs,
trace_point::read_sys_fs_trace_point_id,
utils::find_tracefs_path,
Link, ProgramData,
},
sys::{perf_event_open_probe, perf_event_open_trace_point, SyscallError},
sys::{perf_event_open_probe, perf_event_open_trace_point},
util::KernelVersion,
};
@ -112,7 +115,7 @@ pub(crate) fn attach<T: Link + From<PerfLinkInner>>(
fn_name: &OsStr,
offset: u64,
pid: Option<pid_t>,
) -> Result<T::Id, ProgramError> {
) -> Result<T::Id, LinkError> {
// https://github.com/torvalds/linux/commit/e12f03d7031a977356e3d7b75a68c2185ff8d155
// Use debugfs to create probe
let prog_fd = program_data.fd()?;
@ -127,22 +130,10 @@ pub(crate) fn attach<T: Link + From<PerfLinkInner>>(
program_data.links.insert(T::from(link))
}
pub(crate) fn detach_debug_fs(event: ProbeEvent) -> Result<(), ProgramError> {
use ProbeKind::*;
pub(crate) fn detach_debug_fs(event: ProbeEvent) -> Result<(), LinkError> {
let tracefs = find_tracefs_path()?;
let ProbeEvent {
kind,
event_alias: _,
} = &event;
let kind = *kind;
let result = delete_probe_event(tracefs, event);
result.map_err(|(filename, io_error)| match kind {
KProbe | KRetProbe => KProbeError::FileError { filename, io_error }.into(),
UProbe | URetProbe => UProbeError::FileError { filename, io_error }.into(),
})
delete_probe_event(tracefs, event).map_err(Into::into)
}
fn create_as_probe(
@ -150,35 +141,21 @@ fn create_as_probe(
fn_name: &OsStr,
offset: u64,
pid: Option<pid_t>,
) -> Result<crate::MockableFd, ProgramError> {
) -> Result<crate::MockableFd, LinkError> {
use ProbeKind::*;
let perf_ty = match kind {
KProbe | KRetProbe => read_sys_fs_perf_type(kind.pmu())
.map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
UProbe | URetProbe => read_sys_fs_perf_type(kind.pmu())
.map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
KProbe | KRetProbe => read_sys_fs_perf_type(kind.pmu())?,
UProbe | URetProbe => read_sys_fs_perf_type(kind.pmu())?,
};
let ret_bit = match kind {
KRetProbe => Some(
read_sys_fs_perf_ret_probe(kind.pmu())
.map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
),
URetProbe => Some(
read_sys_fs_perf_ret_probe(kind.pmu())
.map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
),
KRetProbe => Some(read_sys_fs_perf_ret_probe(kind.pmu())?),
URetProbe => Some(read_sys_fs_perf_ret_probe(kind.pmu())?),
_ => None,
};
perf_event_open_probe(perf_ty, ret_bit, fn_name, offset, pid).map_err(|(_code, io_error)| {
SyscallError {
call: "perf_event_open",
io_error,
}
.into()
})
perf_event_open_probe(perf_ty, ret_bit, fn_name, offset, pid).map_err(Into::into)
}
fn create_as_trace_point(
@ -186,24 +163,19 @@ fn create_as_trace_point(
name: &OsStr,
offset: u64,
pid: Option<pid_t>,
) -> Result<(crate::MockableFd, OsString), ProgramError> {
) -> Result<(crate::MockableFd, OsString), LinkError> {
use ProbeKind::*;
let tracefs = find_tracefs_path()?;
let event_alias = match kind {
KProbe | KRetProbe => create_probe_event(tracefs, kind, name, offset)
.map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
UProbe | URetProbe => create_probe_event(tracefs, kind, name, offset)
.map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
KProbe | KRetProbe => create_probe_event(tracefs, kind, name, offset)?,
UProbe | URetProbe => create_probe_event(tracefs, kind, name, offset)?,
};
let category = format!("{}s", kind.pmu());
let tpid = read_sys_fs_trace_point_id(tracefs, &category, event_alias.as_ref())?;
let fd = perf_event_open_trace_point(tpid, pid).map_err(|(_code, io_error)| SyscallError {
call: "perf_event_open",
io_error,
})?;
let fd = perf_event_open_trace_point(tpid, pid)?;
Ok((fd, event_alias))
}

@ -2,6 +2,7 @@
use std::ffi::CString;
use crate::{
errors::LinkError,
generated::bpf_prog_type::BPF_PROG_TYPE_RAW_TRACEPOINT,
programs::{
define_link_wrapper, load_program, utils::attach_raw_tracepoint, FdLink, FdLinkId,
@ -48,7 +49,7 @@ impl RawTracePoint {
/// Attaches the program to the given tracepoint.
///
/// The returned value can be used to detach, see [RawTracePoint::detach].
pub fn attach(&mut self, tp_name: &str) -> Result<RawTracePointLinkId, ProgramError> {
pub fn attach(&mut self, tp_name: &str) -> Result<RawTracePointLinkId, LinkError> {
let tp_name_c = CString::new(tp_name).unwrap();
attach_raw_tracepoint(&mut self.data, Some(&tp_name_c))
}
@ -56,7 +57,7 @@ impl RawTracePoint {
/// Detaches from a tracepoint.
///
/// See [RawTracePoint::attach].
pub fn detach(&mut self, link_id: RawTracePointLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: RawTracePointLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -67,7 +68,7 @@ impl RawTracePoint {
pub fn take_link(
&mut self,
link_id: RawTracePointLinkId,
) -> Result<RawTracePointLink, ProgramError> {
) -> Result<RawTracePointLink, LinkError> {
self.data.take_link(link_id)
}
}

@ -3,9 +3,10 @@ use std::os::fd::AsFd;
use super::links::FdLink;
use crate::{
errors::LinkError,
generated::{bpf_attach_type::BPF_SK_LOOKUP, bpf_prog_type::BPF_PROG_TYPE_SK_LOOKUP},
programs::{define_link_wrapper, load_program, FdLinkId, ProgramData, ProgramError},
sys::{bpf_link_create, LinkTarget, SyscallError},
sys::{bpf_link_create, LinkTarget},
};
/// A program used to redirect incoming packets to a local socket.
@ -60,7 +61,7 @@ impl SkLookup {
/// Attaches the program to the given network namespace.
///
/// The returned value can be used to detach, see [SkLookup::detach].
pub fn attach<T: AsFd>(&mut self, netns: T) -> Result<SkLookupLinkId, ProgramError> {
pub fn attach<T: AsFd>(&mut self, netns: T) -> Result<SkLookupLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let netns_fd = netns.as_fd();
@ -72,11 +73,7 @@ impl SkLookup {
None,
0,
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
self.data
.links
.insert(SkLookupLink::new(FdLink::new(link_fd)))
@ -86,14 +83,14 @@ impl SkLookup {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: SkLookupLinkId) -> Result<SkLookupLink, ProgramError> {
pub fn take_link(&mut self, link_id: SkLookupLinkId) -> Result<SkLookupLink, LinkError> {
self.data.take_link(link_id)
}
/// Detaches the program.
///
/// See [SkLookup::attach].
pub fn detach(&mut self, link_id: SkLookupLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: SkLookupLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
}

@ -3,6 +3,7 @@
use std::os::fd::AsFd as _;
use crate::{
errors::LinkError,
generated::{bpf_attach_type::BPF_SK_MSG_VERDICT, bpf_prog_type::BPF_PROG_TYPE_SK_MSG},
maps::sock::SockMapFd,
programs::{
@ -77,7 +78,7 @@ impl SkMsg {
/// Attaches the program to the given sockmap.
///
/// The returned value can be used to detach, see [SkMsg::detach].
pub fn attach(&mut self, map: &SockMapFd) -> Result<SkMsgLinkId, ProgramError> {
pub fn attach(&mut self, map: &SockMapFd) -> Result<SkMsgLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let link = ProgAttachLink::attach(
@ -93,7 +94,7 @@ impl SkMsg {
/// Detaches the program from a sockmap.
///
/// See [SkMsg::attach].
pub fn detach(&mut self, link_id: SkMsgLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: SkMsgLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -101,7 +102,7 @@ impl SkMsg {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: SkMsgLinkId) -> Result<SkMsgLink, ProgramError> {
pub fn take_link(&mut self, link_id: SkMsgLinkId) -> Result<SkMsgLink, LinkError> {
self.data.take_link(link_id)
}
}

@ -3,6 +3,7 @@
use std::{os::fd::AsFd as _, path::Path};
use crate::{
errors::LinkError,
generated::{
bpf_attach_type::{BPF_SK_SKB_STREAM_PARSER, BPF_SK_SKB_STREAM_VERDICT},
bpf_prog_type::BPF_PROG_TYPE_SK_SKB,
@ -81,7 +82,7 @@ impl SkSkb {
/// Attaches the program to the given socket map.
///
/// The returned value can be used to detach, see [SkSkb::detach].
pub fn attach(&mut self, map: &SockMapFd) -> Result<SkSkbLinkId, ProgramError> {
pub fn attach(&mut self, map: &SockMapFd) -> Result<SkSkbLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
@ -99,7 +100,7 @@ impl SkSkb {
/// Detaches the program.
///
/// See [SkSkb::attach].
pub fn detach(&mut self, link_id: SkSkbLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: SkSkbLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -107,7 +108,7 @@ impl SkSkb {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: SkSkbLinkId) -> Result<SkSkbLink, ProgramError> {
pub fn take_link(&mut self, link_id: SkSkbLinkId) -> Result<SkSkbLink, LinkError> {
self.data.take_link(link_id)
}

@ -2,12 +2,13 @@
use std::os::fd::AsFd;
use crate::{
errors::LinkError,
generated::{bpf_attach_type::BPF_CGROUP_SOCK_OPS, bpf_prog_type::BPF_PROG_TYPE_SOCK_OPS},
programs::{
define_link_wrapper, load_program, CgroupAttachMode, FdLink, Link, ProgAttachLink,
ProgramData, ProgramError,
},
sys::{bpf_link_create, LinkTarget, SyscallError},
sys::{bpf_link_create, LinkTarget},
util::KernelVersion,
};
@ -63,7 +64,7 @@ impl SockOps {
&mut self,
cgroup: T,
mode: CgroupAttachMode,
) -> Result<SockOpsLinkId, ProgramError> {
) -> Result<SockOpsLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let cgroup_fd = cgroup.as_fd();
@ -76,11 +77,7 @@ impl SockOps {
None,
mode.into(),
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
self.data
.links
.insert(SockOpsLink::new(SockOpsLinkInner::Fd(FdLink::new(link_fd))))
@ -96,7 +93,7 @@ impl SockOps {
/// Detaches the program.
///
/// See [SockOps::attach].
pub fn detach(&mut self, link_id: SockOpsLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: SockOpsLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -104,7 +101,7 @@ impl SockOps {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: SockOpsLinkId) -> Result<SockOpsLink, ProgramError> {
pub fn take_link(&mut self, link_id: SockOpsLinkId) -> Result<SockOpsLink, LinkError> {
self.data.take_link(link_id)
}
}
@ -131,7 +128,7 @@ impl Link for SockOpsLinkInner {
}
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
match self {
Self::Fd(fd) => fd.detach(),
Self::ProgAttach(p) => p.detach(),

@ -5,25 +5,13 @@ use std::{
};
use libc::{setsockopt, SOL_SOCKET};
use thiserror::Error;
use crate::{
errors::{InternalLinkError, LinkError},
generated::{bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER, SO_ATTACH_BPF, SO_DETACH_BPF},
programs::{load_program, Link, ProgramData, ProgramError},
};
/// The type returned when attaching a [`SocketFilter`] fails.
#[derive(Debug, Error)]
pub enum SocketFilterError {
/// Setting the `SO_ATTACH_BPF` socket option failed.
#[error("setsockopt SO_ATTACH_BPF failed")]
SoAttachEbpfError {
/// original [`io::Error`]
#[source]
io_error: io::Error,
},
}
/// A program used to inspect and filter incoming packets on a socket.
///
/// [`SocketFilter`] programs are attached on sockets and can be used to inspect
@ -72,7 +60,7 @@ impl SocketFilter {
/// Attaches the filter on the given socket.
///
/// The returned value can be used to detach from the socket, see [SocketFilter::detach].
pub fn attach<T: AsFd>(&mut self, socket: T) -> Result<SocketFilterLinkId, ProgramError> {
pub fn attach<T: AsFd>(&mut self, socket: T) -> Result<SocketFilterLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let prog_fd = prog_fd.as_raw_fd();
@ -89,7 +77,7 @@ impl SocketFilter {
)
};
if ret < 0 {
return Err(SocketFilterError::SoAttachEbpfError {
return Err(InternalLinkError::SoAttachEbpf {
io_error: io::Error::last_os_error(),
}
.into());
@ -101,7 +89,7 @@ impl SocketFilter {
/// Detaches the program.
///
/// See [SocketFilter::attach].
pub fn detach(&mut self, link_id: SocketFilterLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: SocketFilterLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -112,7 +100,7 @@ impl SocketFilter {
pub fn take_link(
&mut self,
link_id: SocketFilterLinkId,
) -> Result<SocketFilterLink, ProgramError> {
) -> Result<SocketFilterLink, LinkError> {
self.data.take_link(link_id)
}
}
@ -135,7 +123,7 @@ impl Link for SocketFilterLink {
SocketFilterLinkId(self.socket, self.prog_fd)
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
unsafe {
setsockopt(
self.socket,

@ -6,10 +6,9 @@ use std::{
path::Path,
};
use thiserror::Error;
use super::{FdLink, ProgramInfo};
use crate::{
errors::{InternalLinkError, LinkError},
generated::{
bpf_attach_type::{self, BPF_TCX_EGRESS, BPF_TCX_INGRESS},
bpf_link_type,
@ -17,13 +16,12 @@ use crate::{
TC_H_CLSACT, TC_H_MIN_EGRESS, TC_H_MIN_INGRESS,
},
programs::{
define_link_wrapper, load_program, query, Link, LinkError, LinkOrder, ProgramData,
ProgramError,
define_link_wrapper, load_program, query, Link, LinkOrder, ProgramData, ProgramError,
},
sys::{
bpf_link_create, bpf_link_get_info_by_fd, bpf_link_update, bpf_prog_get_fd_by_id,
netlink_find_filter_with_name, netlink_qdisc_add_clsact, netlink_qdisc_attach,
netlink_qdisc_detach, LinkTarget, ProgQueryTarget, SyscallError,
netlink_qdisc_detach, LinkTarget, ProgQueryTarget,
},
util::{ifindex_from_ifname, tc_handler_make, KernelVersion},
VerifierLogLevel,
@ -84,27 +82,6 @@ pub struct SchedClassifier {
pub(crate) data: ProgramData<SchedClassifierLink>,
}
/// Errors from TC programs
#[derive(Debug, Error)]
pub enum TcError {
/// netlink error while attaching ebpf program
#[error("netlink error while attaching ebpf program to tc")]
NetlinkError {
/// the [`io::Error`] from the netlink call
#[source]
io_error: io::Error,
},
/// the clsact qdisc is already attached
#[error("the clsact qdisc is already attached")]
AlreadyAttached,
/// tcx links can only be attached to ingress or egress, custom attachment is not supported
#[error("tcx links can only be attached to ingress or egress, custom attachment: {0} is not supported")]
InvalidTcxAttach(u32),
/// operation not supported for programs loaded via tcx
#[error("operation not supported for programs loaded via tcx")]
InvalidLinkOperation,
}
impl TcAttachType {
pub(crate) fn tc_parent(&self) -> u32 {
match self {
@ -114,11 +91,13 @@ impl TcAttachType {
}
}
pub(crate) fn tcx_attach_type(&self) -> Result<bpf_attach_type, TcError> {
pub(crate) fn tcx_attach_type(&self) -> Result<bpf_attach_type, InternalLinkError> {
match self {
Self::Ingress => Ok(BPF_TCX_INGRESS),
Self::Egress => Ok(BPF_TCX_EGRESS),
Self::Custom(tcx_attach_type) => Err(TcError::InvalidTcxAttach(*tcx_attach_type)),
Self::Custom(tcx_attach_type) => {
Err(InternalLinkError::InvalidTcxAttach(*tcx_attach_type))
}
}
}
}
@ -175,7 +154,7 @@ impl SchedClassifier {
&mut self,
interface: &str,
attach_type: TcAttachType,
) -> Result<SchedClassifierLinkId, ProgramError> {
) -> Result<SchedClassifierLinkId, LinkError> {
if !matches!(attach_type, TcAttachType::Custom(_))
&& KernelVersion::current().unwrap() >= KernelVersion::new(6, 6, 0)
{
@ -208,9 +187,9 @@ impl SchedClassifier {
interface: &str,
attach_type: TcAttachType,
options: TcAttachOptions,
) -> Result<SchedClassifierLinkId, ProgramError> {
) -> Result<SchedClassifierLinkId, LinkError> {
let if_index = ifindex_from_ifname(interface)
.map_err(|io_error| TcError::NetlinkError { io_error })?;
.map_err(|io_error| InternalLinkError::NetlinkError { io_error })?;
self.do_attach(if_index, attach_type, options, true)
}
@ -220,7 +199,7 @@ impl SchedClassifier {
pub fn attach_to_link(
&mut self,
link: SchedClassifierLink,
) -> Result<SchedClassifierLinkId, ProgramError> {
) -> Result<SchedClassifierLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
match link.into_inner() {
@ -228,12 +207,7 @@ impl SchedClassifier {
let fd = link.fd;
let link_fd = fd.as_fd();
bpf_link_update(link_fd.as_fd(), prog_fd, None, 0).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_link_update",
io_error,
}
})?;
bpf_link_update(link_fd.as_fd(), prog_fd, None, 0).map_err(|(_, e)| e)?;
self.data
.links
@ -261,7 +235,7 @@ impl SchedClassifier {
attach_type: TcAttachType,
options: TcAttachOptions,
create: bool,
) -> Result<SchedClassifierLinkId, ProgramError> {
) -> Result<SchedClassifierLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
@ -281,7 +255,7 @@ impl SchedClassifier {
create,
)
}
.map_err(|io_error| TcError::NetlinkError { io_error })?;
.map_err(|io_error| InternalLinkError::NetlinkError { io_error })?;
self.data
.links
@ -300,11 +274,7 @@ impl SchedClassifier {
None,
options.flags.bits(),
Some(&options.link_ref),
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_mprog_attach",
io_error,
})?;
)?;
self.data
.links
@ -318,7 +288,7 @@ impl SchedClassifier {
/// Detaches the program.
///
/// See [SchedClassifier::attach].
pub fn detach(&mut self, link_id: SchedClassifierLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: SchedClassifierLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -329,7 +299,7 @@ impl SchedClassifier {
pub fn take_link(
&mut self,
link_id: SchedClassifierLinkId,
) -> Result<SchedClassifierLink, ProgramError> {
) -> Result<SchedClassifierLink, LinkError> {
self.data.take_link(link_id)
}
@ -361,9 +331,9 @@ impl SchedClassifier {
pub fn query_tcx(
interface: &str,
attach_type: TcAttachType,
) -> Result<(u64, Vec<ProgramInfo>), ProgramError> {
) -> Result<(u64, Vec<ProgramInfo>), LinkError> {
let if_index = ifindex_from_ifname(interface)
.map_err(|io_error| TcError::NetlinkError { io_error })?;
.map_err(|io_error| InternalLinkError::NetlinkError { io_error })?;
let (revision, prog_ids) = query(
ProgQueryTarget::IfIndex(if_index),
@ -403,7 +373,7 @@ impl Link for NlLink {
NlLinkId(self.if_index, self.attach_type, self.priority, self.handle)
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
unsafe {
netlink_qdisc_detach(
self.if_index as i32,
@ -412,7 +382,7 @@ impl Link for NlLink {
self.handle,
)
}
.map_err(|io_error| TcError::NetlinkError { io_error })?;
.map_err(|io_error| InternalLinkError::NetlinkError { io_error })?;
Ok(())
}
}
@ -439,7 +409,7 @@ impl Link for TcLinkInner {
}
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
match self {
Self::FdLink(link) => link.detach(),
Self::NlLink(link) => link.detach(),
@ -540,29 +510,29 @@ impl SchedClassifierLink {
}
/// Returns the attach type.
pub fn attach_type(&self) -> Result<TcAttachType, ProgramError> {
pub fn attach_type(&self) -> Result<TcAttachType, LinkError> {
if let TcLinkInner::NlLink(n) = self.inner() {
Ok(n.attach_type)
} else {
Err(TcError::InvalidLinkOperation.into())
Err(InternalLinkError::InvalidLinkOperation.into())
}
}
/// Returns the allocated priority. If none was provided at attach time, this was allocated for you.
pub fn priority(&self) -> Result<u16, ProgramError> {
pub fn priority(&self) -> Result<u16, LinkError> {
if let TcLinkInner::NlLink(n) = self.inner() {
Ok(n.priority)
} else {
Err(TcError::InvalidLinkOperation.into())
Err(InternalLinkError::InvalidLinkOperation.into())
}
}
/// Returns the assigned handle. If none was provided at attach time, this was allocated for you.
pub fn handle(&self) -> Result<u32, ProgramError> {
pub fn handle(&self) -> Result<u32, LinkError> {
if let TcLinkInner::NlLink(n) = self.inner() {
Ok(n.handle)
} else {
Err(TcError::InvalidLinkOperation.into())
Err(InternalLinkError::InvalidLinkOperation.into())
}
}
}

@ -1,6 +1,7 @@
//! BTF-enabled raw tracepoints.
use crate::{
errors::LinkError,
generated::{bpf_attach_type::BPF_TRACE_RAW_TP, bpf_prog_type::BPF_PROG_TYPE_TRACING},
obj::btf::{Btf, BtfKind},
programs::{
@ -68,14 +69,14 @@ impl BtfTracePoint {
/// Attaches the program.
///
/// The returned value can be used to detach, see [BtfTracePoint::detach].
pub fn attach(&mut self) -> Result<BtfTracePointLinkId, ProgramError> {
pub fn attach(&mut self) -> Result<BtfTracePointLinkId, LinkError> {
attach_raw_tracepoint(&mut self.data, None)
}
/// Detaches the program.
///
/// See [BtfTracePoint::attach].
pub fn detach(&mut self, link_id: BtfTracePointLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: BtfTracePointLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -86,7 +87,7 @@ impl BtfTracePoint {
pub fn take_link(
&mut self,
link_id: BtfTracePointLinkId,
) -> Result<BtfTracePointLink, ProgramError> {
) -> Result<BtfTracePointLink, LinkError> {
self.data.take_link(link_id)
}
}

@ -1,33 +1,18 @@
//! Tracepoint programs.
use std::{fs, io, os::fd::AsFd as _, path::Path};
use thiserror::Error;
use std::{fs, os::fd::AsFd as _, path::Path};
use crate::{
errors::LinkError,
generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT},
programs::{
define_link_wrapper, load_program,
perf_attach::{perf_attach, PerfLinkIdInner, PerfLinkInner},
utils::find_tracefs_path,
FdLink, LinkError, ProgramData, ProgramError,
FdLink, ProgramData, ProgramError,
},
sys::{bpf_link_get_info_by_fd, perf_event_open_trace_point, SyscallError},
sys::{bpf_link_get_info_by_fd, perf_event_open_trace_point},
};
/// The type returned when attaching a [`TracePoint`] fails.
#[derive(Debug, Error)]
pub enum TracePointError {
/// Error detaching from debugfs
#[error("`{filename}`")]
FileError {
/// The file name
filename: String,
/// The [`io::Error`] returned from the file operation
#[source]
io_error: io::Error,
},
}
/// 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
@ -67,16 +52,12 @@ impl TracePoint {
/// `/sys/kernel/debug/tracing/events`.
///
/// The returned value can be used to detach, see [TracePoint::detach].
pub fn attach(&mut self, category: &str, name: &str) -> Result<TracePointLinkId, ProgramError> {
pub fn attach(&mut self, category: &str, name: &str) -> Result<TracePointLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let tracefs = find_tracefs_path()?;
let id = read_sys_fs_trace_point_id(tracefs, category, name.as_ref())?;
let fd =
perf_event_open_trace_point(id, None).map_err(|(_code, io_error)| SyscallError {
call: "perf_event_open_trace_point",
io_error,
})?;
let fd = perf_event_open_trace_point(id, None)?;
let link = perf_attach(prog_fd, fd)?;
self.data.links.insert(TracePointLink::new(link))
@ -85,7 +66,7 @@ impl TracePoint {
/// Detaches from a trace point.
///
/// See [TracePoint::attach].
pub fn detach(&mut self, link_id: TracePointLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: TracePointLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -93,7 +74,7 @@ impl TracePoint {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: TracePointLinkId) -> Result<TracePointLink, ProgramError> {
pub fn take_link(&mut self, link_id: TracePointLinkId) -> Result<TracePointLink, LinkError> {
self.data.take_link(link_id)
}
}
@ -135,20 +116,11 @@ pub(crate) fn read_sys_fs_trace_point_id(
tracefs: &Path,
category: &str,
name: &Path,
) -> Result<u32, TracePointError> {
) -> Result<u32, LinkError> {
let file = tracefs.join("events").join(category).join(name).join("id");
let id = fs::read_to_string(&file).map_err(|io_error| TracePointError::FileError {
filename: file.display().to_string(),
io_error,
})?;
let id = id
.trim()
.parse::<u32>()
.map_err(|error| TracePointError::FileError {
filename: file.display().to_string(),
io_error: io::Error::new(io::ErrorKind::Other, error),
})?;
let id = fs::read_to_string(&file)?;
let id = id.trim().parse::<u32>()?;
Ok(id)
}

@ -1,7 +1,6 @@
//! User space probes.
use std::{
borrow::Cow,
error::Error,
ffi::{CStr, OsStr, OsString},
fs,
io::{self, BufRead, Cursor, Read},
@ -13,15 +12,15 @@ use std::{
use libc::pid_t;
use object::{Object, ObjectSection, ObjectSymbol, Symbol};
use thiserror::Error;
use crate::{
errors::{InternalLinkError, LinkError, ResolveSymbolError},
generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_KPROBE},
programs::{
define_link_wrapper, load_program,
perf_attach::{PerfLinkIdInner, PerfLinkInner},
probe::{attach, OsStringExt as _, ProbeKind},
FdLink, LinkError, ProgramData, ProgramError,
FdLink, ProgramData, ProgramError,
},
sys::bpf_link_get_info_by_fd,
VerifierLogLevel,
@ -81,14 +80,11 @@ impl UProbe {
offset: u64,
target: T,
pid: Option<pid_t>,
) -> Result<UProbeLinkId, ProgramError> {
) -> Result<UProbeLinkId, LinkError> {
let path = resolve_attach_path(target.as_ref(), pid)?;
let sym_offset = if let Some(fn_name) = fn_name {
resolve_symbol(&path, fn_name).map_err(|error| UProbeError::SymbolError {
symbol: fn_name.to_string(),
error: Box::new(error),
})?
resolve_symbol(&path, fn_name)?
} else {
0
};
@ -100,7 +96,7 @@ impl UProbe {
/// Detaches the program.
///
/// See [UProbe::attach].
pub fn detach(&mut self, link_id: UProbeLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: UProbeLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -108,7 +104,7 @@ impl UProbe {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: UProbeLinkId) -> Result<UProbeLink, ProgramError> {
pub fn take_link(&mut self, link_id: UProbeLinkId) -> Result<UProbeLink, LinkError> {
self.data.take_link(link_id)
}
@ -124,15 +120,12 @@ impl UProbe {
}
}
fn resolve_attach_path(target: &Path, pid: Option<pid_t>) -> Result<Cow<'_, Path>, UProbeError> {
fn resolve_attach_path(target: &Path, pid: Option<pid_t>) -> Result<Cow<'_, Path>, LinkError> {
// Look up the path for the target. If it there is a pid, and the target is a library name
// that is in the process's memory map, use the path of that library. Otherwise, use the target as-is.
pid.and_then(|pid| {
find_lib_in_proc_maps(pid, target)
.map_err(|io_error| UProbeError::FileError {
filename: Path::new("/proc").join(pid.to_string()).join("maps"),
io_error,
})
.map_err(Into::into)
.map(|v| v.map(Cow::Owned))
.transpose()
})
@ -140,14 +133,15 @@ fn resolve_attach_path(target: &Path, pid: Option<pid_t>) -> Result<Cow<'_, Path
.or_else(|| {
LD_SO_CACHE
.as_ref()
.map_err(|io_error| UProbeError::InvalidLdSoCache { io_error })
.map_err(|io_error| InternalLinkError::LdSoCache { io_error }.into())
.map(|cache| cache.resolve(target).map(Cow::Borrowed))
.transpose()
})
.unwrap_or_else(|| {
Err(UProbeError::InvalidTarget {
Err(InternalLinkError::InvalidTarget {
path: target.to_owned(),
})
}
.into())
})
}
@ -205,45 +199,6 @@ impl TryFrom<FdLink> for UProbeLink {
}
}
/// The type returned when attaching an [`UProbe`] fails.
#[derive(Debug, Error)]
pub enum UProbeError {
/// There was an error parsing `/etc/ld.so.cache`.
#[error("error reading `{}` file", LD_SO_CACHE_FILE)]
InvalidLdSoCache {
/// the original [`io::Error`]
#[source]
io_error: &'static io::Error,
},
/// The target program could not be found.
#[error("could not resolve uprobe target `{path}`")]
InvalidTarget {
/// path to target
path: PathBuf,
},
/// There was an error resolving the target symbol.
#[error("error resolving symbol")]
SymbolError {
/// symbol name
symbol: String,
/// the original error
#[source]
error: Box<dyn Error + Send + Sync>,
},
/// There was an error accessing `filename`.
#[error("`{filename}`")]
FileError {
/// The file name
filename: PathBuf,
/// The [`io::Error`] returned from the file operation
#[source]
io_error: io::Error,
},
}
fn proc_maps_libs(pid: pid_t) -> Result<Vec<(OsString, PathBuf)>, io::Error> {
use std::os::unix::ffi::OsStrExt as _;
@ -412,30 +367,6 @@ impl LdSoCache {
}
}
#[derive(Error, Debug)]
enum ResolveSymbolError {
#[error(transparent)]
Io(#[from] io::Error),
#[error("error parsing ELF")]
Object(#[from] object::Error),
#[error("unknown symbol `{0}`")]
Unknown(String),
#[error("symbol `{0}` does not appear in section")]
NotInSection(String),
#[error("symbol `{0}` in section `{1:?}` which has no offset")]
SectionFileRangeNone(String, Result<String, object::Error>),
#[error("failed to access debuglink file `{0}`: `{1}`")]
DebuglinkAccessError(String, io::Error),
#[error("symbol `{0}` not found, mismatched build IDs in main and debug files")]
BuildIdMismatch(String),
}
fn construct_debuglink_path(
filename: &[u8],
main_path: &Path,

@ -10,28 +10,25 @@ use std::{
};
use crate::{
errors::LinkError,
programs::{FdLink, Link, ProgramData, ProgramError},
sys::{bpf_raw_tracepoint_open, SyscallError},
sys::bpf_raw_tracepoint_open,
};
/// Attaches the program to a raw tracepoint.
pub(crate) fn attach_raw_tracepoint<T: Link + From<FdLink>>(
program_data: &mut ProgramData<T>,
tp_name: Option<&CStr>,
) -> Result<T::Id, ProgramError> {
) -> Result<T::Id, LinkError> {
let prog_fd = program_data.fd()?;
let prog_fd = prog_fd.as_fd();
let pfd =
bpf_raw_tracepoint_open(tp_name, prog_fd).map_err(|(_code, io_error)| SyscallError {
call: "bpf_raw_tracepoint_open",
io_error,
})?;
let pfd = bpf_raw_tracepoint_open(tp_name, prog_fd)?;
program_data.links.insert(FdLink::new(pfd).into())
}
/// Find tracefs filesystem path.
pub(crate) fn find_tracefs_path() -> Result<&'static Path, ProgramError> {
pub(crate) fn find_tracefs_path() -> Result<&'static Path, LinkError> {
static TRACE_FS: LazyLock<Option<&'static Path>> = LazyLock::new(|| {
[
Path::new("/sys/kernel/tracing"),

@ -3,43 +3,27 @@
use std::{
ffi::CString,
hash::Hash,
io,
os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd},
path::Path,
};
use libc::if_nametoindex;
use thiserror::Error;
use crate::{
errors::{InternalLinkError, LinkError},
generated::{
bpf_link_type, bpf_prog_type, XDP_FLAGS_DRV_MODE, XDP_FLAGS_HW_MODE, XDP_FLAGS_REPLACE,
XDP_FLAGS_SKB_MODE, XDP_FLAGS_UPDATE_IF_NOEXIST,
},
obj::programs::XdpAttachType,
programs::{
define_link_wrapper, load_program, FdLink, Link, LinkError, ProgramData, ProgramError,
},
programs::{define_link_wrapper, load_program, FdLink, Link, ProgramData, ProgramError},
sys::{
bpf_link_create, bpf_link_get_info_by_fd, bpf_link_update, netlink_set_xdp_fd, LinkTarget,
SyscallError,
},
util::KernelVersion,
VerifierLogLevel,
};
/// The type returned when attaching an [`Xdp`] program fails on kernels `< 5.9`.
#[derive(Debug, Error)]
pub enum XdpError {
/// netlink error while attaching XDP program
#[error("netlink error while attaching XDP program")]
NetlinkError {
/// the [`io::Error`] from the netlink call
#[source]
io_error: io::Error,
},
}
bitflags::bitflags! {
/// Flags passed to [`Xdp::attach()`].
#[derive(Clone, Copy, Debug, Default)]
@ -105,14 +89,15 @@ impl Xdp {
/// kernels `>= 5.9.0`, and instead
/// [`XdpError::NetlinkError`] is returned for older
/// kernels.
pub fn attach(&mut self, interface: &str, flags: XdpFlags) -> Result<XdpLinkId, ProgramError> {
pub fn attach(&mut self, interface: &str, flags: XdpFlags) -> Result<XdpLinkId, LinkError> {
// TODO: avoid this unwrap by adding a new error variant.
let c_interface = CString::new(interface).unwrap();
let if_index = unsafe { if_nametoindex(c_interface.as_ptr()) };
if if_index == 0 {
return Err(ProgramError::UnknownInterface {
return Err(InternalLinkError::UnknownInterface {
name: interface.to_string(),
});
}
.into());
}
self.attach_to_if_index(if_index, flags)
}
@ -131,7 +116,7 @@ impl Xdp {
&mut self,
if_index: u32,
flags: XdpFlags,
) -> Result<XdpLinkId, ProgramError> {
) -> Result<XdpLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
@ -151,18 +136,14 @@ impl Xdp {
None,
flags.bits(),
None,
)
.map_err(|(_, io_error)| SyscallError {
call: "bpf_link_create",
io_error,
})?;
)?;
self.data
.links
.insert(XdpLink::new(XdpLinkInner::FdLink(FdLink::new(link_fd))))
} else {
let if_index = if_index as i32;
unsafe { netlink_set_xdp_fd(if_index, Some(prog_fd), None, flags.bits()) }
.map_err(|io_error| XdpError::NetlinkError { io_error })?;
.map_err(|io_error| InternalLinkError::NetlinkError { io_error })?;
let prog_fd = prog_fd.as_raw_fd();
self.data
@ -193,7 +174,7 @@ impl Xdp {
/// Detaches the program.
///
/// See [Xdp::attach].
pub fn detach(&mut self, link_id: XdpLinkId) -> Result<(), ProgramError> {
pub fn detach(&mut self, link_id: XdpLinkId) -> Result<(), LinkError> {
self.data.links.remove(link_id)
}
@ -201,25 +182,20 @@ impl Xdp {
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: XdpLinkId) -> Result<XdpLink, ProgramError> {
pub fn take_link(&mut self, link_id: XdpLinkId) -> Result<XdpLink, LinkError> {
self.data.take_link(link_id)
}
/// Atomically replaces the program referenced by the provided link.
///
/// Ownership of the link will transfer to this program.
pub fn attach_to_link(&mut self, link: XdpLink) -> Result<XdpLinkId, ProgramError> {
pub fn attach_to_link(&mut self, link: XdpLink) -> Result<XdpLinkId, LinkError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
match link.into_inner() {
XdpLinkInner::FdLink(fd_link) => {
let link_fd = fd_link.fd;
bpf_link_update(link_fd.as_fd(), prog_fd, None, 0).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_link_update",
io_error,
}
})?;
bpf_link_update(link_fd.as_fd(), prog_fd, None, 0)?;
self.data
.links
@ -239,7 +215,7 @@ impl Xdp {
Some(old_prog_fd),
replace_flags.bits(),
)
.map_err(|io_error| XdpError::NetlinkError { io_error })?;
.map_err(|io_error| InternalLinkError::NetlinkError { io_error })?;
}
let prog_fd = prog_fd.as_raw_fd();
@ -269,7 +245,7 @@ impl Link for NlLink {
(self.if_index, self.prog_fd)
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
let flags = if KernelVersion::current().unwrap() >= KernelVersion::new(5, 7, 0) {
self.flags.bits() | XDP_FLAGS_REPLACE
} else {
@ -304,7 +280,7 @@ impl Link for XdpLinkInner {
}
}
fn detach(self) -> Result<(), ProgramError> {
fn detach(self) -> Result<(), LinkError> {
match self {
Self::FdLink(link) => link.detach(),
Self::NlLink(link) => link.detach(),

@ -16,9 +16,11 @@ use obj::{
EbpfSectionKind, VerifierLog,
};
use super::BpfCmd;
use crate::{
errors::SysError,
generated::{
bpf_attach_type, bpf_attr, bpf_btf_info, bpf_cmd, bpf_insn, bpf_link_info, bpf_map_info,
bpf_attach_type, bpf_attr, bpf_btf_info, bpf_insn, bpf_link_info, bpf_map_info,
bpf_map_type, bpf_prog_info, bpf_prog_type, BPF_F_REPLACE,
},
maps::{MapData, PerCpuValues},
@ -31,7 +33,7 @@ use crate::{
copy_instructions,
},
programs::links::LinkRef,
sys::{syscall, SysResult, Syscall, SyscallError},
sys::{syscall, SysResult, Syscall},
util::KernelVersion,
Btf, Pod, VerifierLogLevel, BPF_OBJ_NAME_LEN, FEATURES,
};
@ -95,7 +97,7 @@ pub(crate) fn bpf_create_map(
}
// SAFETY: BPF_MAP_CREATE returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_MAP_CREATE, &mut attr) }
unsafe { fd_sys_bpf(BpfCmd::MapCreate, &mut attr) }
}
pub(crate) fn bpf_pin_object(fd: BorrowedFd<'_>, path: &CStr) -> SysResult<i64> {
@ -103,7 +105,7 @@ pub(crate) fn bpf_pin_object(fd: BorrowedFd<'_>, path: &CStr) -> SysResult<i64>
let u = unsafe { &mut attr.__bindgen_anon_4 };
u.bpf_fd = fd.as_raw_fd() as u32;
u.pathname = path.as_ptr() as u64;
sys_bpf(bpf_cmd::BPF_OBJ_PIN, &mut attr)
sys_bpf(BpfCmd::ObjPin, &mut attr)
}
/// Introduced in kernel v4.4.
@ -112,7 +114,7 @@ pub(crate) fn bpf_get_object(path: &CStr) -> SysResult<crate::MockableFd> {
let u = unsafe { &mut attr.__bindgen_anon_4 };
u.pathname = path.as_ptr() as u64;
// SAFETY: BPF_OBJ_GET returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_OBJ_GET, &mut attr) }
unsafe { fd_sys_bpf(BpfCmd::ObjGet, &mut attr) }
}
pub(crate) struct EbpfLoadProgramAttrs<'a> {
@ -202,7 +204,7 @@ fn lookup<K: Pod, V: Pod>(
fd: BorrowedFd<'_>,
key: Option<&K>,
flags: u64,
cmd: bpf_cmd,
cmd: BpfCmd,
) -> SysResult<Option<V>> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let mut value = MaybeUninit::zeroed();
@ -217,7 +219,9 @@ fn lookup<K: Pod, V: Pod>(
match sys_bpf(cmd, &mut attr) {
Ok(_) => Ok(Some(unsafe { value.assume_init() })),
Err((_, io_error)) if io_error.raw_os_error() == Some(ENOENT) => Ok(None),
Err((_, SysError::Syscall { io_error, .. })) if io_error.raw_os_error() == Some(ENOENT) => {
Ok(None)
}
Err(e) => Err(e),
}
}
@ -227,7 +231,7 @@ pub(crate) fn bpf_map_lookup_elem<K: Pod, V: Pod>(
key: &K,
flags: u64,
) -> SysResult<Option<V>> {
lookup(fd, Some(key), flags, bpf_cmd::BPF_MAP_LOOKUP_ELEM)
lookup(fd, Some(key), flags, BpfCmd::MapLookupElem)
}
pub(crate) fn bpf_map_lookup_and_delete_elem<K: Pod, V: Pod>(
@ -235,7 +239,7 @@ pub(crate) fn bpf_map_lookup_and_delete_elem<K: Pod, V: Pod>(
key: Option<&K>,
flags: u64,
) -> SysResult<Option<V>> {
lookup(fd, key, flags, bpf_cmd::BPF_MAP_LOOKUP_AND_DELETE_ELEM)
lookup(fd, key, flags, BpfCmd::MapLookupAndDeleteElem)
}
pub(crate) fn bpf_map_lookup_elem_per_cpu<K: Pod, V: Pod>(
@ -243,10 +247,13 @@ pub(crate) fn bpf_map_lookup_elem_per_cpu<K: Pod, V: Pod>(
key: &K,
flags: u64,
) -> SysResult<Option<PerCpuValues<V>>> {
let mut mem = PerCpuValues::<V>::alloc_kernel_mem().map_err(|io_error| (-1, io_error))?;
let mut mem = PerCpuValues::<V>::alloc_kernel_mem()
.map_err(|io_error| (-1, SysError::Other(Box::new(io_error))))?;
match bpf_map_lookup_elem_ptr(fd, Some(key), mem.as_mut_ptr(), flags) {
Ok(_) => Ok(Some(unsafe { PerCpuValues::from_kernel_mem(mem) })),
Err((_, io_error)) if io_error.raw_os_error() == Some(ENOENT) => Ok(None),
Err((_, SysError::Syscall { io_error, .. })) if io_error.raw_os_error() == Some(ENOENT) => {
Ok(None)
}
Err(e) => Err(e),
}
}
@ -267,9 +274,11 @@ pub(crate) fn bpf_map_lookup_elem_ptr<K: Pod, V>(
u.__bindgen_anon_1.value = value as u64;
u.flags = flags;
match sys_bpf(bpf_cmd::BPF_MAP_LOOKUP_ELEM, &mut attr) {
match sys_bpf(BpfCmd::MapLookupElem, &mut attr) {
Ok(_) => Ok(Some(())),
Err((_, io_error)) if io_error.raw_os_error() == Some(ENOENT) => Ok(None),
Err((_, SysError::Syscall { io_error, .. })) if io_error.raw_os_error() == Some(ENOENT) => {
Ok(None)
}
Err(e) => Err(e),
}
}
@ -290,7 +299,7 @@ pub(crate) fn bpf_map_update_elem<K: Pod, V: Pod>(
u.__bindgen_anon_1.value = value as *const _ as u64;
u.flags = flags;
sys_bpf(bpf_cmd::BPF_MAP_UPDATE_ELEM, &mut attr)
sys_bpf(BpfCmd::MapUpdateElem, &mut attr)
}
pub(crate) fn bpf_map_push_elem<V: Pod>(
@ -305,7 +314,7 @@ pub(crate) fn bpf_map_push_elem<V: Pod>(
u.__bindgen_anon_1.value = value as *const _ as u64;
u.flags = flags;
sys_bpf(bpf_cmd::BPF_MAP_UPDATE_ELEM, &mut attr)
sys_bpf(BpfCmd::MapUpdateElem, &mut attr)
}
pub(crate) fn bpf_map_update_elem_ptr<K, V>(
@ -322,7 +331,7 @@ pub(crate) fn bpf_map_update_elem_ptr<K, V>(
u.__bindgen_anon_1.value = value as u64;
u.flags = flags;
sys_bpf(bpf_cmd::BPF_MAP_UPDATE_ELEM, &mut attr)
sys_bpf(BpfCmd::MapUpdateElem, &mut attr)
}
pub(crate) fn bpf_map_update_elem_per_cpu<K: Pod, V: Pod>(
@ -331,7 +340,9 @@ pub(crate) fn bpf_map_update_elem_per_cpu<K: Pod, V: Pod>(
values: &PerCpuValues<V>,
flags: u64,
) -> SysResult<i64> {
let mut mem = values.build_kernel_mem().map_err(|e| (-1, e))?;
let mut mem = values
.build_kernel_mem()
.map_err(|e| (-1, SysError::Other(Box::new(e))))?;
bpf_map_update_elem_ptr(fd, key, mem.as_mut_ptr(), flags)
}
@ -342,7 +353,7 @@ pub(crate) fn bpf_map_delete_elem<K: Pod>(fd: BorrowedFd<'_>, key: &K) -> SysRes
u.map_fd = fd.as_raw_fd() as u32;
u.key = key as *const _ as u64;
sys_bpf(bpf_cmd::BPF_MAP_DELETE_ELEM, &mut attr)
sys_bpf(BpfCmd::MapDeleteElem, &mut attr)
}
pub(crate) fn bpf_map_get_next_key<K: Pod>(
@ -359,9 +370,11 @@ pub(crate) fn bpf_map_get_next_key<K: Pod>(
}
u.__bindgen_anon_1.next_key = &mut next_key as *mut _ as u64;
match sys_bpf(bpf_cmd::BPF_MAP_GET_NEXT_KEY, &mut attr) {
match sys_bpf(BpfCmd::MapGetNextKey, &mut attr) {
Ok(_) => Ok(Some(unsafe { next_key.assume_init() })),
Err((_, io_error)) if io_error.raw_os_error() == Some(ENOENT) => Ok(None),
Err((_, SysError::Syscall { io_error, .. })) if io_error.raw_os_error() == Some(ENOENT) => {
Ok(None)
}
Err(e) => Err(e),
}
}
@ -371,7 +384,7 @@ pub(crate) fn bpf_map_freeze(fd: BorrowedFd<'_>) -> SysResult<i64> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_2 };
u.map_fd = fd.as_raw_fd() as u32;
sys_bpf(bpf_cmd::BPF_MAP_FREEZE, &mut attr)
sys_bpf(BpfCmd::MapFreeze, &mut attr)
}
pub(crate) enum LinkTarget<'f> {
@ -428,7 +441,7 @@ pub(crate) fn bpf_link_create(
};
// SAFETY: BPF_LINK_CREATE returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_LINK_CREATE, &mut attr) }
unsafe { fd_sys_bpf(BpfCmd::LinkCreate, &mut attr) }
}
// since kernel 5.7
@ -449,7 +462,7 @@ pub(crate) fn bpf_link_update(
attr.link_update.flags = flags;
}
sys_bpf(bpf_cmd::BPF_LINK_UPDATE, &mut attr)
sys_bpf(BpfCmd::LinkUpdate, &mut attr)
}
pub(crate) fn bpf_prog_attach(
@ -457,7 +470,7 @@ pub(crate) fn bpf_prog_attach(
target_fd: BorrowedFd<'_>,
attach_type: bpf_attach_type,
flags: u32,
) -> Result<(), SyscallError> {
) -> Result<(), SysError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
attr.__bindgen_anon_5.attach_bpf_fd = prog_fd.as_raw_fd() as u32;
@ -465,12 +478,9 @@ pub(crate) fn bpf_prog_attach(
attr.__bindgen_anon_5.attach_type = attach_type as u32;
attr.__bindgen_anon_5.attach_flags = flags;
let ret = sys_bpf(bpf_cmd::BPF_PROG_ATTACH, &mut attr).map_err(|(code, io_error)| {
let ret = sys_bpf(BpfCmd::ProgAttach, &mut attr).map_err(|(code, error)| {
assert_eq!(code, -1);
SyscallError {
call: "bpf_prog_attach",
io_error,
}
error
})?;
assert_eq!(ret, 0);
Ok(())
@ -480,19 +490,16 @@ pub(crate) fn bpf_prog_detach(
prog_fd: BorrowedFd<'_>,
target_fd: BorrowedFd<'_>,
attach_type: bpf_attach_type,
) -> Result<(), SyscallError> {
) -> Result<(), SysError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
attr.__bindgen_anon_5.attach_bpf_fd = prog_fd.as_raw_fd() as u32;
attr.__bindgen_anon_5.__bindgen_anon_1.target_fd = target_fd.as_raw_fd() as u32;
attr.__bindgen_anon_5.attach_type = attach_type as u32;
let ret = sys_bpf(bpf_cmd::BPF_PROG_DETACH, &mut attr).map_err(|(code, io_error)| {
let ret = sys_bpf(BpfCmd::ProgDetach, &mut attr).map_err(|(code, e)| {
assert_eq!(code, -1);
SyscallError {
call: "bpf_prog_detach",
io_error,
}
e
})?;
assert_eq!(ret, 0);
Ok(())
@ -527,7 +534,7 @@ pub(crate) fn bpf_prog_query(
attr.query.query_flags = query_flags;
attr.query.__bindgen_anon_2.prog_cnt = prog_ids.len() as u32;
attr.query.prog_ids = prog_ids.as_mut_ptr() as u64;
let ret = sys_bpf(bpf_cmd::BPF_PROG_QUERY, &mut attr);
let ret = sys_bpf(BpfCmd::ProgQuery, &mut attr);
*prog_cnt = unsafe { attr.query.__bindgen_anon_2.prog_cnt };
*revision = unsafe { attr.query.revision };
@ -540,17 +547,14 @@ pub(crate) fn bpf_prog_query(
}
/// Introduced in kernel v4.13.
pub(crate) fn bpf_prog_get_fd_by_id(prog_id: u32) -> Result<crate::MockableFd, SyscallError> {
pub(crate) fn bpf_prog_get_fd_by_id(prog_id: u32) -> Result<crate::MockableFd, SysError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
attr.__bindgen_anon_6.__bindgen_anon_1.prog_id = prog_id;
// SAFETY: BPF_PROG_GET_FD_BY_ID returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_PROG_GET_FD_BY_ID, &mut attr) }.map_err(|(code, io_error)| {
unsafe { fd_sys_bpf(BpfCmd::ProgGetFdById, &mut attr) }.map_err(|(code, e)| {
assert_eq!(code, -1);
SyscallError {
call: "bpf_prog_get_fd_by_id",
io_error,
}
e
})
}
@ -558,7 +562,7 @@ pub(crate) fn bpf_prog_get_fd_by_id(prog_id: u32) -> Result<crate::MockableFd, S
fn bpf_obj_get_info_by_fd<T, F: FnOnce(&mut T)>(
fd: BorrowedFd<'_>,
init: F,
) -> Result<T, SyscallError> {
) -> Result<T, SysError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let mut info = unsafe { mem::zeroed() };
@ -568,17 +572,14 @@ fn bpf_obj_get_info_by_fd<T, F: FnOnce(&mut T)>(
attr.info.info = &info as *const _ as u64;
attr.info.info_len = mem::size_of_val(&info) as u32;
match sys_bpf(bpf_cmd::BPF_OBJ_GET_INFO_BY_FD, &mut attr) {
match sys_bpf(BpfCmd::ObjGetInfoByFd, &mut attr) {
Ok(code) => {
assert_eq!(code, 0);
Ok(info)
}
Err((code, io_error)) => {
Err((code, e)) => {
assert_eq!(code, -1);
Err(SyscallError {
call: "bpf_obj_get_info_by_fd",
io_error,
})
Err(e)
}
}
}
@ -587,7 +588,7 @@ fn bpf_obj_get_info_by_fd<T, F: FnOnce(&mut T)>(
pub(crate) fn bpf_prog_get_info_by_fd(
fd: BorrowedFd<'_>,
map_ids: &mut [u32],
) -> Result<bpf_prog_info, SyscallError> {
) -> Result<bpf_prog_info, SysError> {
// An `E2BIG` error can occur on kernels below v4.15 when handing over a large struct where the
// extra space is not all-zero bytes.
bpf_obj_get_info_by_fd(fd, |info: &mut bpf_prog_info| {
@ -599,47 +600,41 @@ pub(crate) fn bpf_prog_get_info_by_fd(
}
/// Introduced in kernel v4.13.
pub(crate) fn bpf_map_get_fd_by_id(map_id: u32) -> Result<crate::MockableFd, SyscallError> {
pub(crate) fn bpf_map_get_fd_by_id(map_id: u32) -> Result<crate::MockableFd, SysError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
attr.__bindgen_anon_6.__bindgen_anon_1.map_id = map_id;
// SAFETY: BPF_MAP_GET_FD_BY_ID returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_MAP_GET_FD_BY_ID, &mut attr) }.map_err(|(code, io_error)| {
unsafe { fd_sys_bpf(BpfCmd::MapGetFdById, &mut attr) }.map_err(|(code, e)| {
assert_eq!(code, -1);
SyscallError {
call: "bpf_map_get_fd_by_id",
io_error,
}
e
})
}
pub(crate) fn bpf_map_get_info_by_fd(fd: BorrowedFd<'_>) -> Result<bpf_map_info, SyscallError> {
pub(crate) fn bpf_map_get_info_by_fd(fd: BorrowedFd<'_>) -> Result<bpf_map_info, SysError> {
bpf_obj_get_info_by_fd(fd, |_| {})
}
pub(crate) fn bpf_link_get_fd_by_id(link_id: u32) -> Result<crate::MockableFd, SyscallError> {
pub(crate) fn bpf_link_get_fd_by_id(link_id: u32) -> Result<crate::MockableFd, SysError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
attr.__bindgen_anon_6.__bindgen_anon_1.link_id = link_id;
// SAFETY: BPF_LINK_GET_FD_BY_ID returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_LINK_GET_FD_BY_ID, &mut attr) }.map_err(|(code, io_error)| {
unsafe { fd_sys_bpf(BpfCmd::LinkGetFdById, &mut attr) }.map_err(|(code, e)| {
assert_eq!(code, -1);
SyscallError {
call: "bpf_link_get_fd_by_id",
io_error,
}
e
})
}
pub(crate) fn bpf_link_get_info_by_fd(fd: BorrowedFd<'_>) -> Result<bpf_link_info, SyscallError> {
pub(crate) fn bpf_link_get_info_by_fd(fd: BorrowedFd<'_>) -> Result<bpf_link_info, SysError> {
bpf_obj_get_info_by_fd(fd, |_| {})
}
pub(crate) fn btf_obj_get_info_by_fd(
fd: BorrowedFd<'_>,
buf: &mut [u8],
) -> Result<bpf_btf_info, SyscallError> {
) -> Result<bpf_btf_info, SysError> {
bpf_obj_get_info_by_fd(fd, |info: &mut bpf_btf_info| {
info.btf = buf.as_mut_ptr() as _;
info.btf_size = buf.len() as _;
@ -659,7 +654,7 @@ pub(crate) fn bpf_raw_tracepoint_open(
attr.raw_tracepoint.prog_fd = prog_fd.as_raw_fd() as u32;
// SAFETY: BPF_RAW_TRACEPOINT_OPEN returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_RAW_TRACEPOINT_OPEN, &mut attr) }
unsafe { fd_sys_bpf(BpfCmd::RawTracepointOpen, &mut attr) }
}
pub(crate) fn bpf_load_btf(
@ -677,35 +672,32 @@ pub(crate) fn bpf_load_btf(
u.btf_log_size = log_buf.len() as u32;
}
// SAFETY: `BPF_BTF_LOAD` returns a newly created fd.
unsafe { fd_sys_bpf(bpf_cmd::BPF_BTF_LOAD, &mut attr) }
unsafe { fd_sys_bpf(BpfCmd::BtfLoad, &mut attr) }
}
// SAFETY: only use for bpf_cmd that return a new file descriptor on success.
unsafe fn fd_sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> SysResult<crate::MockableFd> {
unsafe fn fd_sys_bpf(cmd: BpfCmd, attr: &mut bpf_attr) -> SysResult<crate::MockableFd> {
let fd = sys_bpf(cmd, attr)?;
let fd = fd.try_into().map_err(|_| {
(
fd,
io::Error::new(
SysError::Other(Box::new(io::Error::new(
io::ErrorKind::InvalidData,
format!("{cmd:?}: invalid fd returned: {fd}"),
),
))),
)
})?;
Ok(crate::MockableFd::from_raw_fd(fd))
}
pub(crate) fn bpf_btf_get_fd_by_id(id: u32) -> Result<crate::MockableFd, SyscallError> {
pub(crate) fn bpf_btf_get_fd_by_id(id: u32) -> Result<crate::MockableFd, SysError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
attr.__bindgen_anon_6.__bindgen_anon_1.btf_id = id;
// SAFETY: BPF_BTF_GET_FD_BY_ID returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_BTF_GET_FD_BY_ID, &mut attr) }.map_err(|(code, io_error)| {
unsafe { fd_sys_bpf(BpfCmd::BtfGetNextId, &mut attr) }.map_err(|(code, e)| {
assert_eq!(code, -1);
SyscallError {
call: "bpf_btf_get_fd_by_id",
io_error,
}
e
})
}
@ -865,7 +857,7 @@ pub(crate) fn is_perf_link_supported() -> bool {
// Uses an invalid target FD so we get EBADF if supported.
bpf_link_create(fd, LinkTarget::IfIndex(u32::MAX), bpf_attach_type::BPF_PERF_EVENT, None, 0, None),
// Returns EINVAL if unsupported. EBADF if supported.
Err((_, e)) if e.raw_os_error() == Some(libc::EBADF),
Err((_, SysError::Syscall{ call: _, io_error })) if io_error.raw_os_error() == Some(libc::EBADF),
)
} else {
false
@ -983,7 +975,7 @@ pub(crate) fn is_prog_id_supported(map_type: bpf_map_type) -> bool {
u.map_flags = 0;
// SAFETY: BPF_MAP_CREATE returns a new file descriptor.
let fd = unsafe { fd_sys_bpf(bpf_cmd::BPF_MAP_CREATE, &mut attr) };
let fd = unsafe { fd_sys_bpf(BpfCmd::MapCreate, &mut attr) };
fd.is_ok()
}
@ -1145,18 +1137,14 @@ pub(crate) fn is_btf_type_tag_supported() -> bool {
fn bpf_prog_load(attr: &mut bpf_attr) -> SysResult<crate::MockableFd> {
// SAFETY: BPF_PROG_LOAD returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_PROG_LOAD, attr) }
unsafe { fd_sys_bpf(BpfCmd::ProgLoad, attr) }
}
fn sys_bpf(cmd: bpf_cmd, attr: &mut bpf_attr) -> SysResult<i64> {
fn sys_bpf(cmd: BpfCmd, attr: &mut bpf_attr) -> SysResult<i64> {
syscall(Syscall::Ebpf { cmd, attr })
}
fn bpf_obj_get_next_id(
id: u32,
cmd: bpf_cmd,
name: &'static str,
) -> Result<Option<u32>, SyscallError> {
fn bpf_obj_get_next_id(id: u32, cmd: BpfCmd) -> Result<Option<u32>, SysError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_6 };
u.__bindgen_anon_1.start_id = id;
@ -1165,66 +1153,54 @@ fn bpf_obj_get_next_id(
assert_eq!(code, 0);
Ok(Some(unsafe { attr.__bindgen_anon_6.next_id }))
}
Err((code, io_error)) => {
Err((code, SysError::Syscall { call, io_error })) => {
assert_eq!(code, -1);
if io_error.raw_os_error() == Some(ENOENT) {
Ok(None)
} else {
Err(SyscallError {
call: name,
io_error,
})
Err(SysError::Syscall { call, io_error })
}
}
Err((_, e)) => Err(SysError::Other(Box::new(e))),
}
}
fn iter_obj_ids(
cmd: bpf_cmd,
name: &'static str,
) -> impl Iterator<Item = Result<u32, SyscallError>> {
fn iter_obj_ids(cmd: BpfCmd) -> impl Iterator<Item = Result<u32, SysError>> {
let mut current_id = Some(0);
iter::from_fn(move || {
let next_id = {
let current_id = current_id?;
bpf_obj_get_next_id(current_id, cmd, name).transpose()
bpf_obj_get_next_id(current_id, cmd).transpose()
};
current_id = next_id.as_ref().and_then(|next_id| match next_id {
Ok(next_id) => Some(*next_id),
Err(SyscallError { .. }) => None,
Err(_) => None,
});
next_id
})
}
/// Introduced in kernel v4.13.
pub(crate) fn iter_prog_ids() -> impl Iterator<Item = Result<u32, SyscallError>> {
iter_obj_ids(bpf_cmd::BPF_PROG_GET_NEXT_ID, "bpf_prog_get_next_id")
pub(crate) fn iter_prog_ids() -> impl Iterator<Item = Result<u32, SysError>> {
iter_obj_ids(BpfCmd::ProgGetNextId)
}
pub(crate) fn iter_link_ids() -> impl Iterator<Item = Result<u32, SyscallError>> {
iter_obj_ids(bpf_cmd::BPF_LINK_GET_NEXT_ID, "bpf_link_get_next_id")
pub(crate) fn iter_link_ids() -> impl Iterator<Item = Result<u32, SysError>> {
iter_obj_ids(BpfCmd::LinkGetNextId)
}
/// Introduced in kernel v4.13.
pub(crate) fn iter_map_ids() -> impl Iterator<Item = Result<u32, SyscallError>> {
iter_obj_ids(bpf_cmd::BPF_MAP_GET_NEXT_ID, "bpf_map_get_next_id")
pub(crate) fn iter_map_ids() -> impl Iterator<Item = Result<u32, SysError>> {
iter_obj_ids(BpfCmd::MapGetNextId)
}
/// Introduced in kernel v5.8.
pub(crate) fn bpf_enable_stats(
stats_type: bpf_stats_type,
) -> Result<crate::MockableFd, SyscallError> {
pub(crate) fn bpf_enable_stats(stats_type: bpf_stats_type) -> Result<crate::MockableFd, SysError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
attr.enable_stats.type_ = stats_type as u32;
// SAFETY: BPF_ENABLE_STATS returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_ENABLE_STATS, &mut attr) }.map_err(|(_, io_error)| {
SyscallError {
call: "bpf_enable_stats",
io_error,
}
})
unsafe { fd_sys_bpf(BpfCmd::EnableStats, &mut attr) }.map_err(|(_, e)| e)
}
pub(crate) fn retry_with_verifier_logs<T>(
@ -1239,7 +1215,7 @@ pub(crate) fn retry_with_verifier_logs<T>(
loop {
let ret = f(log_buf.as_mut_slice());
if retries != max_retries {
if let Err((_, io_error)) = &ret {
if let Err((_, SysError::Syscall { call: _, io_error })) = &ret {
if retries == 0 || io_error.raw_os_error() == Some(ENOSPC) {
let len = (log_buf.capacity() * 10).clamp(MIN_LOG_BUF_SIZE, MAX_LOG_BUF_SIZE);
log_buf.resize(len, 0);
@ -1275,13 +1251,19 @@ mod tests {
// Test attach flags propagated to system call.
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_PROG_ATTACH,
cmd: BpfCmd::ProgAttach,
attr,
} => {
assert_eq!(unsafe { attr.__bindgen_anon_5.attach_flags }, FAKE_FLAGS);
Ok(0)
}
_ => Err((-1, io::Error::from_raw_os_error(EINVAL))),
_ => Err((
-1,
SysError::Syscall {
call: format!("{:?}", BpfCmd::ProgAttach),
io_error: io::Error::from_raw_os_error(EINVAL),
},
)),
});
let prog_fd = unsafe { BorrowedFd::borrow_raw(FAKE_FD) };
@ -1297,13 +1279,19 @@ mod tests {
// Test with no flags.
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_PROG_ATTACH,
cmd: BpfCmd::ProgAttach,
attr,
} => {
assert_eq!(unsafe { attr.__bindgen_anon_5.attach_flags }, 0);
Ok(0)
}
_ => Err((-1, io::Error::from_raw_os_error(EINVAL))),
_ => Err((
-1,
SysError::Syscall {
call: format!("{:?}", BpfCmd::ProgAttach),
io_error: io::Error::from_raw_os_error(EINVAL),
},
)),
});
result = bpf_prog_attach(prog_fd, tgt_fd, bpf_attach_type::BPF_CGROUP_SOCK_OPS, 0);
@ -1314,9 +1302,15 @@ mod tests {
fn test_perf_link_supported() {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_LINK_CREATE,
cmd: BpfCmd::LinkCreate,
..
} => Err((-1, io::Error::from_raw_os_error(EBADF))),
} => Err((
-1,
SysError::Syscall {
call: format!("{:?}", BpfCmd::LinkCreate),
io_error: io::Error::from_raw_os_error(EBADF),
},
)),
_ => Ok(crate::MockableFd::mock_signed_fd().into()),
});
let supported = is_perf_link_supported();
@ -1324,9 +1318,15 @@ mod tests {
override_syscall(|call| match call {
Syscall::Ebpf {
cmd: bpf_cmd::BPF_LINK_CREATE,
cmd: BpfCmd::LinkCreate,
..
} => Err((-1, io::Error::from_raw_os_error(EINVAL))),
} => Err((
-1,
SysError::Syscall {
call: format!("{:?}", BpfCmd::LinkCreate),
io_error: io::Error::from_raw_os_error(EINVAL),
},
)),
_ => Ok(crate::MockableFd::mock_signed_fd().into()),
});
let supported = is_perf_link_supported();
@ -1345,7 +1345,15 @@ mod tests {
let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_DEVMAP_HASH);
assert!(supported);
override_syscall(|_call| Err((-1, io::Error::from_raw_os_error(EINVAL))));
override_syscall(|_call| {
Err((
-1,
SysError::Syscall {
call: format!("{:?}", BpfCmd::MapCreate),
io_error: io::Error::from_raw_os_error(EINVAL),
},
))
});
let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_CPUMAP);
assert!(!supported);
}

@ -11,8 +11,16 @@ thread_local! {
}
#[cfg(test)]
unsafe fn test_syscall(_call: Syscall<'_>) -> SysResult<i64> {
Err((-1, io::Error::from_raw_os_error(libc::EINVAL)))
unsafe fn test_syscall(call: Syscall<'_>) -> SysResult<i64> {
use crate::errors::SysError;
Err((
-1,
SysError::Syscall {
call: format!("{:?}", call),
io_error: io::Error::from_raw_os_error(libc::EINVAL),
},
))
}
#[cfg(test)]

@ -21,15 +21,17 @@ use libc::{pid_t, SYS_bpf, SYS_perf_event_open};
pub use netlink::netlink_set_link_up;
pub(crate) use netlink::*;
pub(crate) use perf_event::*;
use thiserror::Error;
use crate::generated::{bpf_attr, bpf_cmd, perf_event_attr};
use crate::{
errors::SysError,
generated::{bpf_attr, bpf_cmd, perf_event_attr},
};
pub(crate) type SysResult<T> = Result<T, (i64, io::Error)>;
pub(crate) type SysResult<T> = Result<T, (i64, SysError)>;
pub(crate) enum Syscall<'a> {
Ebpf {
cmd: bpf_cmd,
cmd: BpfCmd,
attr: &'a mut bpf_attr,
},
PerfEventOpen {
@ -46,15 +48,137 @@ pub(crate) enum Syscall<'a> {
},
}
/// A system call error.
#[derive(Debug, Error)]
#[error("`{call}` failed")]
pub struct SyscallError {
/// The name of the syscall which failed.
pub call: &'static str,
/// The [`io::Error`] returned by the syscall.
#[source]
pub io_error: io::Error,
#[derive(Copy, Clone)]
pub(crate) enum BpfCmd {
MapCreate,
MapLookupElem,
MapUpdateElem,
MapDeleteElem,
MapGetNextKey,
ProgLoad,
ObjPin,
ObjGet,
ProgAttach,
ProgDetach,
ProgTestRun,
ProgGetNextId,
MapGetNextId,
ProgGetFdById,
MapGetFdById,
ObjGetInfoByFd,
ProgQuery,
RawTracepointOpen,
BtfLoad,
BtfGetFdById,
TaskFdQuery,
MapLookupAndDeleteElem,
MapFreeze,
BtfGetNextId,
MapLookupBatch,
MapLookupAndDeleteBatch,
MapUpdateBatch,
MapDeleteBatch,
LinkCreate,
LinkUpdate,
LinkGetFdById,
LinkGetNextId,
EnableStats,
IterCreate,
LinkDetach,
ProgBindMap,
TokenCreate,
Max,
}
impl From<bpf_cmd> for BpfCmd {
fn from(value: bpf_cmd) -> Self {
use bpf_cmd::*;
match value {
BPF_MAP_CREATE => Self::MapCreate,
BPF_MAP_LOOKUP_ELEM => Self::MapLookupElem,
BPF_MAP_UPDATE_ELEM => Self::MapUpdateElem,
BPF_MAP_DELETE_ELEM => Self::MapDeleteElem,
BPF_MAP_GET_NEXT_KEY => Self::MapGetNextKey,
BPF_PROG_LOAD => Self::ProgLoad,
BPF_OBJ_PIN => Self::ObjPin,
BPF_OBJ_GET => Self::ObjGet,
BPF_PROG_ATTACH => Self::ProgAttach,
BPF_PROG_DETACH => Self::ProgDetach,
BPF_PROG_TEST_RUN => Self::ProgTestRun,
BPF_PROG_GET_NEXT_ID => Self::ProgGetNextId,
BPF_MAP_GET_NEXT_ID => Self::MapGetNextId,
BPF_PROG_GET_FD_BY_ID => Self::ProgGetFdById,
BPF_MAP_GET_FD_BY_ID => Self::MapGetFdById,
BPF_OBJ_GET_INFO_BY_FD => Self::ObjGetInfoByFd,
BPF_PROG_QUERY => Self::ProgQuery,
BPF_RAW_TRACEPOINT_OPEN => Self::RawTracepointOpen,
BPF_BTF_LOAD => Self::BtfLoad,
BPF_BTF_GET_FD_BY_ID => Self::BtfGetFdById,
BPF_TASK_FD_QUERY => Self::TaskFdQuery,
BPF_MAP_LOOKUP_AND_DELETE_ELEM => Self::MapLookupAndDeleteElem,
BPF_MAP_FREEZE => Self::MapFreeze,
BPF_BTF_GET_NEXT_ID => Self::BtfGetNextId,
BPF_MAP_LOOKUP_BATCH => Self::MapLookupBatch,
BPF_MAP_LOOKUP_AND_DELETE_BATCH => Self::MapLookupAndDeleteBatch,
BPF_MAP_UPDATE_BATCH => Self::MapUpdateBatch,
BPF_MAP_DELETE_BATCH => Self::MapDeleteBatch,
BPF_LINK_CREATE => Self::LinkCreate,
BPF_LINK_UPDATE => Self::LinkUpdate,
BPF_LINK_GET_FD_BY_ID => Self::LinkGetFdById,
BPF_LINK_GET_NEXT_ID => Self::LinkGetNextId,
BPF_ENABLE_STATS => Self::EnableStats,
BPF_ITER_CREATE => Self::IterCreate,
BPF_LINK_DETACH => Self::LinkDetach,
BPF_PROG_BIND_MAP => Self::ProgBindMap,
BPF_TOKEN_CREATE => Self::TokenCreate,
__MAX_BPF_CMD => Self::Max,
}
}
}
impl std::fmt::Debug for BpfCmd {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MapCreate => f.write_str("bpf_map_create"),
Self::MapLookupElem => f.write_str("bpf_map_lookup_elem"),
Self::MapUpdateElem => f.write_str("bpf_map_update_elem"),
Self::MapDeleteElem => f.write_str("bpf_map_delete_elem"),
Self::MapGetNextKey => f.write_str("bpf_map_get_next_key"),
Self::ProgLoad => f.write_str("bpf_prog_load"),
Self::ObjPin => f.write_str("bpf_obj_pin"),
Self::ObjGet => f.write_str("bpf_obj_get"),
Self::ProgAttach => f.write_str("bpf_prog_attach"),
Self::ProgDetach => f.write_str("bpf_prog_detach"),
Self::ProgTestRun => f.write_str("bpf_prog_test_run"),
Self::ProgGetNextId => f.write_str("bpf_prog_get_next_id"),
Self::MapGetNextId => f.write_str("bpf_map_get_next_id"),
Self::ProgGetFdById => f.write_str("bpf_prog_get_fd_by_id"),
Self::MapGetFdById => f.write_str("bpf_map_get_fd_by_id"),
Self::ObjGetInfoByFd => f.write_str("bpf_obj_get_info_by_fd"),
Self::ProgQuery => f.write_str("bpf_prog_query"),
Self::RawTracepointOpen => f.write_str("bpf_raw_tracepoint_open"),
Self::BtfLoad => f.write_str("bpf_btf_load"),
Self::BtfGetFdById => f.write_str("bpf_btf_get_fd_by_id"),
Self::TaskFdQuery => f.write_str("bpf_task_fd_query"),
Self::MapLookupAndDeleteElem => f.write_str("bpf_map_lookup_and_delete_elem"),
Self::MapFreeze => f.write_str("bpf_map_freeze"),
Self::BtfGetNextId => f.write_str("bpf_btf_get_next_id"),
Self::MapLookupBatch => f.write_str("bpf_map_lookup_batch"),
Self::MapLookupAndDeleteBatch => f.write_str("bpf_map_lookup_and_delete_batch"),
Self::MapUpdateBatch => f.write_str("bpf_map_update_batch"),
Self::MapDeleteBatch => f.write_str("bpf_map_delete_batch"),
Self::LinkCreate => f.write_str("bpf_link_create"),
Self::LinkUpdate => f.write_str("bpf_link_update"),
Self::LinkGetFdById => f.write_str("bpf_link_get_fd_by_id"),
Self::LinkGetNextId => f.write_str("bpf_link_get_next_id"),
Self::EnableStats => f.write_str("bpf_enable_stats"),
Self::IterCreate => f.write_str("bpf_iter_create"),
Self::LinkDetach => f.write_str("bpf_link_detach"),
Self::ProgBindMap => f.write_str("bpf_prog_bind_map"),
Self::TokenCreate => f.write_str("bpf_token_create"),
Self::Max => f.write_str("MAX"),
}
}
}
impl std::fmt::Debug for Syscall<'_> {
@ -95,6 +219,7 @@ fn syscall(call: Syscall<'_>) -> SysResult<i64> {
#[cfg_attr(test, allow(unreachable_code))]
{
let call_name = format!("{:?}", call);
let ret = unsafe {
match call {
Syscall::Ebpf { cmd, attr } => {
@ -120,7 +245,13 @@ fn syscall(call: Syscall<'_>) -> SysResult<i64> {
#[allow(clippy::useless_conversion)]
match ret.into() {
ret @ 0.. => Ok(ret),
ret => Err((ret, io::Error::last_os_error())),
ret => Err((
ret,
SysError::Syscall {
call: call_name,
io_error: io::Error::last_os_error(),
},
)),
}
}
}
@ -181,13 +312,15 @@ impl From<Stats> for crate::generated::bpf_stats_type {
/// # Examples
///
/// ```no_run
/// # use aya::sys::{SyscallError};
/// # use aya::errors::{SysError};
/// use aya::sys::{enable_stats, Stats};
///
/// let _fd = enable_stats(Stats::RunTime)?;
/// # Ok::<(), SyscallError>(())
/// # Ok::<(), SysError>(())
/// ```
#[doc(alias = "BPF_ENABLE_STATS")]
pub fn enable_stats(stats_type: Stats) -> Result<OwnedFd, SyscallError> {
bpf_enable_stats(stats_type.into()).map(|fd| fd.into_inner())
pub fn enable_stats(stats_type: Stats) -> Result<OwnedFd, Box<dyn std::error::Error + 'static>> {
Ok(bpf_enable_stats(stats_type.into())
.map(|fd| fd.into_inner())
.map_err(Box::new)?)
}

@ -7,12 +7,15 @@ use std::{
use libc::pid_t;
use super::{syscall, SysResult, Syscall};
use crate::generated::{
perf_event_attr,
perf_event_sample_format::PERF_SAMPLE_RAW,
perf_sw_ids::PERF_COUNT_SW_BPF_OUTPUT,
perf_type_id::{PERF_TYPE_SOFTWARE, PERF_TYPE_TRACEPOINT},
PERF_FLAG_FD_CLOEXEC,
use crate::{
errors::SysError,
generated::{
perf_event_attr,
perf_event_sample_format::PERF_SAMPLE_RAW,
perf_sw_ids::PERF_COUNT_SW_BPF_OUTPUT,
perf_type_id::{PERF_TYPE_SOFTWARE, PERF_TYPE_TRACEPOINT},
PERF_FLAG_FD_CLOEXEC,
},
};
#[allow(clippy::too_many_arguments)]
@ -130,10 +133,10 @@ fn perf_event_sys(
let fd = fd.try_into().map_err(|_| {
(
fd,
io::Error::new(
SysError::Other(Box::new(io::Error::new(
io::ErrorKind::InvalidData,
format!("perf_event_open: invalid fd returned: {fd}"),
),
))),
)
})?;

@ -8,8 +8,9 @@
use std::{fs, panic, path::Path, time::SystemTime};
use aya::{
maps::{loaded_maps, Array, HashMap, IterableMap as _, MapError, MapType},
programs::{loaded_programs, ProgramError, ProgramType, SocketFilter, TracePoint},
errors::{MapError, ProgramError, SysError},
maps::{loaded_maps, Array, HashMap, IterableMap as _, MapType},
programs::{loaded_programs, ProgramType, SocketFilter, TracePoint},
sys::enable_stats,
util::KernelVersion,
Ebpf,
@ -33,13 +34,9 @@ fn test_loaded_programs() {
// Ensure loaded program doesn't panic
let mut programs = loaded_programs().peekable();
if let Err(err) = programs.peek().unwrap() {
if let ProgramError::SyscallError(err) = &err {
if let ProgramError::Syscall(SysError::Syscall { call: _, io_error }) = &err {
// Skip entire test since feature not available
if err
.io_error
.raw_os_error()
.is_some_and(|errno| errno == EINVAL)
{
if io_error.raw_os_error().is_some_and(|errno| errno == EINVAL) {
eprintln!(
"ignoring test completely as `loaded_programs()` is not available on the host"
);
@ -234,12 +231,8 @@ fn list_loaded_maps() {
// Ensure the loaded_maps() api doesn't panic
let mut maps = loaded_maps().peekable();
if let Err(err) = maps.peek().unwrap() {
if let MapError::SyscallError(err) = &err {
if err
.io_error
.raw_os_error()
.is_some_and(|errno| errno == EINVAL)
{
if let MapError::Syscall(SysError::Syscall { call: _, io_error }) = &err {
if io_error.raw_os_error().is_some_and(|errno| errno == EINVAL) {
eprintln!(
"ignoring test completely as `loaded_maps()` is not available on the host"
);

@ -1 +1 @@
Subproject commit 20ea95b4505c477af3b6ff6ce9d19cee868ddc5d
Subproject commit a2258003f21d9d52afd48aa64787b65ef80bd355

@ -1,17 +1,17 @@
pub mod aya_log
pub enum aya_log::Error
pub aya_log::Error::InvalidOnlineCpu(std::io::error::Error)
pub aya_log::Error::MapError(aya::maps::MapError)
pub aya_log::Error::MapError(aya::errors::MapError)
pub aya_log::Error::MapNotFound
pub aya_log::Error::PerfBufferError(aya::maps::perf::perf_buffer::PerfBufferError)
pub aya_log::Error::ProgramError(aya::programs::ProgramError)
pub aya_log::Error::PerfBufferError(aya::errors::PerfBufferError)
pub aya_log::Error::ProgramError(aya::errors::ProgramError)
pub aya_log::Error::ProgramNotFound
impl core::convert::From<aya::maps::MapError> for aya_log::Error
pub fn aya_log::Error::from(source: aya::maps::MapError) -> Self
impl core::convert::From<aya::maps::perf::perf_buffer::PerfBufferError> for aya_log::Error
pub fn aya_log::Error::from(source: aya::maps::perf::perf_buffer::PerfBufferError) -> Self
impl core::convert::From<aya::programs::ProgramError> for aya_log::Error
pub fn aya_log::Error::from(source: aya::programs::ProgramError) -> Self
impl core::convert::From<aya::errors::MapError> for aya_log::Error
pub fn aya_log::Error::from(source: aya::errors::MapError) -> Self
impl core::convert::From<aya::errors::PerfBufferError> for aya_log::Error
pub fn aya_log::Error::from(source: aya::errors::PerfBufferError) -> Self
impl core::convert::From<aya::errors::ProgramError> for aya_log::Error
pub fn aya_log::Error::from(source: aya::errors::ProgramError) -> Self
impl core::error::Error for aya_log::Error
pub fn aya_log::Error::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)>
impl core::fmt::Debug for aya_log::Error
@ -19,8 +19,8 @@ pub fn aya_log::Error::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt
impl core::fmt::Display for aya_log::Error
pub fn aya_log::Error::fmt(&self, __formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Freeze for aya_log::Error
impl core::marker::Send for aya_log::Error
impl core::marker::Sync for aya_log::Error
impl !core::marker::Send for aya_log::Error
impl !core::marker::Sync for aya_log::Error
impl core::marker::Unpin for aya_log::Error
impl !core::panic::unwind_safe::RefUnwindSafe for aya_log::Error
impl !core::panic::unwind_safe::UnwindSafe for aya_log::Error

@ -28,6 +28,7 @@ pub aya_obj::btf::BtfError::LoadError::io_error: std::io::error::Error
pub aya_obj::btf::BtfError::LoadError::verifier_log: aya_obj::VerifierLog
pub aya_obj::btf::BtfError::MaximumTypeDepthReached
pub aya_obj::btf::BtfError::MaximumTypeDepthReached::type_id: u32
pub aya_obj::btf::BtfError::Other(alloc::boxed::Box<dyn core::error::Error>)
pub aya_obj::btf::BtfError::SymbolOffsetNotFound
pub aya_obj::btf::BtfError::SymbolOffsetNotFound::symbol_name: alloc::string::String
pub aya_obj::btf::BtfError::UnexpectedBtfType
@ -38,6 +39,8 @@ pub aya_obj::btf::BtfError::UnknownBtfTypeName
pub aya_obj::btf::BtfError::UnknownBtfTypeName::type_name: alloc::string::String
pub aya_obj::btf::BtfError::UnknownSectionSize
pub aya_obj::btf::BtfError::UnknownSectionSize::section_name: alloc::string::String
impl core::convert::From<alloc::boxed::Box<dyn core::error::Error>> for aya_obj::btf::BtfError
pub fn aya_obj::btf::BtfError::from(source: alloc::boxed::Box<dyn core::error::Error>) -> Self
impl core::convert::From<aya_obj::btf::BtfError> for aya_obj::ParseError
pub fn aya_obj::ParseError::from(source: aya_obj::btf::BtfError) -> Self
impl core::error::Error for aya_obj::btf::BtfError
@ -47,8 +50,8 @@ pub fn aya_obj::btf::BtfError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> c
impl core::fmt::Display for aya_obj::btf::BtfError
pub fn aya_obj::btf::BtfError::fmt(&self, __formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Freeze for aya_obj::btf::BtfError
impl core::marker::Send for aya_obj::btf::BtfError
impl core::marker::Sync for aya_obj::btf::BtfError
impl !core::marker::Send for aya_obj::btf::BtfError
impl !core::marker::Sync for aya_obj::btf::BtfError
impl core::marker::Unpin for aya_obj::btf::BtfError
impl !core::panic::unwind_safe::RefUnwindSafe for aya_obj::btf::BtfError
impl !core::panic::unwind_safe::UnwindSafe for aya_obj::btf::BtfError
@ -582,8 +585,8 @@ pub fn aya_obj::btf::BtfRelocationError::fmt(&self, f: &mut core::fmt::Formatter
impl core::fmt::Display for aya_obj::btf::BtfRelocationError
pub fn aya_obj::btf::BtfRelocationError::fmt(&self, __formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Freeze for aya_obj::btf::BtfRelocationError
impl core::marker::Send for aya_obj::btf::BtfRelocationError
impl core::marker::Sync for aya_obj::btf::BtfRelocationError
impl !core::marker::Send for aya_obj::btf::BtfRelocationError
impl !core::marker::Sync for aya_obj::btf::BtfRelocationError
impl core::marker::Unpin for aya_obj::btf::BtfRelocationError
impl !core::panic::unwind_safe::RefUnwindSafe for aya_obj::btf::BtfRelocationError
impl !core::panic::unwind_safe::UnwindSafe for aya_obj::btf::BtfRelocationError
@ -7149,8 +7152,8 @@ pub fn aya_obj::ParseError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core
impl core::fmt::Display for aya_obj::ParseError
pub fn aya_obj::ParseError::fmt(&self, __formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Freeze for aya_obj::ParseError
impl core::marker::Send for aya_obj::ParseError
impl core::marker::Sync for aya_obj::ParseError
impl !core::marker::Send for aya_obj::ParseError
impl !core::marker::Sync for aya_obj::ParseError
impl core::marker::Unpin for aya_obj::ParseError
impl !core::panic::unwind_safe::RefUnwindSafe for aya_obj::ParseError
impl !core::panic::unwind_safe::UnwindSafe for aya_obj::ParseError
@ -8009,8 +8012,8 @@ pub fn aya_obj::ParseError::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core
impl core::fmt::Display for aya_obj::ParseError
pub fn aya_obj::ParseError::fmt(&self, __formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Freeze for aya_obj::ParseError
impl core::marker::Send for aya_obj::ParseError
impl core::marker::Sync for aya_obj::ParseError
impl !core::marker::Send for aya_obj::ParseError
impl !core::marker::Sync for aya_obj::ParseError
impl core::marker::Unpin for aya_obj::ParseError
impl !core::panic::unwind_safe::RefUnwindSafe for aya_obj::ParseError
impl !core::panic::unwind_safe::UnwindSafe for aya_obj::ParseError

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save