programs: rework ProgramError a bit

Move type specific errors to XdpError SocketFilterError etc.

Annotate all source errors with #[source]
pull/1/head
Alessandro Decina 4 years ago
parent f88ca1f1f1
commit d326038cf4

@ -164,7 +164,7 @@ pub enum BpfError {
#[error("error relocating BPF program `{program_name}`: {error}")]
RelocationError {
program_name: String,
error: Box<dyn Error>,
error: Box<dyn Error + Send + Sync>,
},
#[error("map error: {0}")]

@ -5,9 +5,7 @@ mod trace_point;
mod xdp;
use libc::{close, ENOSPC};
use std::{
cell::RefCell, cmp, convert::TryFrom, ffi::CStr, io, os::raw::c_uint, path::PathBuf, rc::Rc,
};
use std::{cell::RefCell, cmp, convert::TryFrom, ffi::CStr, io, os::raw::c_uint, rc::Rc};
use thiserror::Error;
use perf_attach::*;
@ -19,65 +17,63 @@ pub use xdp::*;
use crate::{obj, sys::bpf_load_program, RawFd};
#[derive(Debug, Error)]
pub enum ProgramError {
#[error("the program {program} is already loaded")]
AlreadyLoaded { program: String },
#[error("the program is already loaded")]
AlreadyLoaded,
#[error("the program {program} is not loaded")]
NotLoaded { program: String },
#[error("the BPF_PROG_LOAD syscall for `{program}` failed: {io_error}\nVerifier output:\n{verifier_log}")]
LoadError {
program: String,
io_error: io::Error,
verifier_log: String,
},
#[error("the program is not loaded")]
NotLoaded,
#[error("the program was already detached")]
AlreadyDetached,
#[error("the perf_event_open syscall failed: {io_error}")]
PerfEventOpenError { io_error: io::Error },
#[error("PERF_EVENT_IOC_SET_BPF/PERF_EVENT_IOC_ENABLE failed: {io_error}")]
PerfEventAttachError { io_error: io::Error },
#[error("the program is not attached")]
NotAttached,
#[error("the program {program} is not attached")]
NotAttached { program: String },
#[error("the BPF_PROG_LOAD syscall failed: {io_error}\nVerifier output:\n{verifier_log}")]
LoadError {
#[source]
io_error: io::Error,
verifier_log: String,
},
#[error("error attaching {program}: BPF_LINK_CREATE failed with {io_error}")]
BpfLinkCreateError {
program: String,
#[error("the perf_event_open syscall failed")]
PerfEventOpenError {
#[source]
io_error: io::Error,
},
#[error("error attaching XDP program using netlink: {io_error}")]
NetlinkXdpError {
program: String,
#[error("PERF_EVENT_IOC_SET_BPF/PERF_EVENT_IOC_ENABLE failed")]
PerfEventAttachError {
#[source]
io_error: io::Error,
},
#[error("unkown network interface {name}")]
UnkownInterface { name: String },
#[error("unknown network interface {name}")]
UnknownInterface { name: String },
#[error("error reading ld.so.cache file")]
InvalidLdSoCache { error_kind: io::ErrorKind },
#[error("BPF_LINK_CREATE failed")]
BpfLinkCreateError {
#[source]
io_error: io::Error,
},
#[error("could not resolve uprobe target {path}")]
InvalidUprobeTarget { path: PathBuf },
#[error("unexpected program type")]
UnexpectedProgramType,
#[error("error resolving symbol: {error}")]
UprobeSymbolError { symbol: String, error: String },
#[error(transparent)]
KProbeError(#[from] KProbeError),
#[error("setsockopt SO_ATTACH_BPF failed: {io_error}")]
SocketFilterError { io_error: io::Error },
#[error(transparent)]
UProbeError(#[from] UProbeError),
#[error("unexpected program type")]
UnexpectedProgramType,
#[error(transparent)]
TracePointError(#[from] TracePointError),
#[error("{message}")]
Other { message: String },
#[error(transparent)]
SocketFilterError(#[from] SocketFilterError),
#[error(transparent)]
XdpError(#[from] XdpError),
}
pub trait ProgramFd {
@ -140,9 +136,7 @@ pub(crate) struct ProgramData {
impl ProgramData {
fn fd_or_err(&self) -> Result<RawFd, ProgramError> {
self.fd.ok_or(ProgramError::NotLoaded {
program: self.name.clone(),
})
self.fd.ok_or(ProgramError::NotLoaded)
}
pub fn link<T: Link + 'static>(&mut self, link: T) -> LinkRef {
@ -204,11 +198,9 @@ impl VerifierLog {
}
fn load_program(prog_type: c_uint, data: &mut ProgramData) -> Result<(), ProgramError> {
let ProgramData { obj, fd, name, .. } = data;
let ProgramData { obj, fd, .. } = data;
if fd.is_some() {
return Err(ProgramError::AlreadyLoaded {
program: name.to_string(),
});
return Err(ProgramError::AlreadyLoaded);
}
let crate::obj::Program {
instructions,
@ -244,7 +236,6 @@ fn load_program(prog_type: c_uint, data: &mut ProgramData) -> Result<(), Program
if let Err((_, io_error)) = ret {
log_buf.truncate();
return Err(ProgramError::LoadError {
program: name.clone(),
io_error,
verifier_log: log_buf.as_c_str().unwrap().to_string_lossy().to_string(),
});

@ -1,12 +1,14 @@
use libc::pid_t;
use object::{Object, ObjectSymbol};
use std::{
error::Error,
ffi::CStr,
fs,
io::{self, BufRead, Cursor, Read},
mem,
os::raw::c_char,
path::{Path, PathBuf},
sync::Arc,
};
use thiserror::Error;
@ -16,11 +18,50 @@ use crate::{
sys::perf_event_open_probe,
};
const LD_SO_CACHE_FILE: &str = "/etc/ld.so.cache";
lazy_static! {
static ref LD_SO_CACHE: Result<LdSoCache, io::Error> = LdSoCache::load("/etc/ld.so.cache");
static ref LD_SO_CACHE: Result<LdSoCache, Arc<io::Error>> =
LdSoCache::load(LD_SO_CACHE_FILE).map_err(Arc::new);
}
const LD_SO_CACHE_HEADER: &str = "glibc-ld.so.cache1.1";
#[derive(Debug, Error)]
pub enum KProbeError {
#[error("`{filename}`")]
FileError {
filename: String,
#[source]
io_error: io::Error,
},
}
#[derive(Debug, Error)]
pub enum UProbeError {
#[error("error reading `{}` file", LD_SO_CACHE_FILE)]
InvalidLdSoCache {
#[source]
io_error: Arc<io::Error>,
},
#[error("could not resolve uprobe target `{path}`")]
InvalidTarget { path: PathBuf },
#[error("error resolving symbol")]
SymbolError {
symbol: String,
#[source]
error: Box<dyn Error + Send + Sync>,
},
#[error("`{filename}`")]
FileError {
filename: String,
#[source]
io_error: io::Error,
},
}
#[derive(Debug)]
pub struct KProbe {
pub(crate) data: ProgramData,
@ -70,8 +111,9 @@ impl UProbe {
let target_str = &*target.as_os_str().to_string_lossy();
let mut path = if let Some(pid) = pid {
find_lib_in_proc_maps(pid, &target_str).map_err(|io_error| ProgramError::Other {
message: format!("error parsing /proc/{}/maps: {}", pid, io_error),
find_lib_in_proc_maps(pid, &target_str).map_err(|io_error| UProbeError::FileError {
filename: format!("/proc/{}/maps", pid),
io_error,
})?
} else {
None
@ -84,22 +126,22 @@ impl UProbe {
let cache =
LD_SO_CACHE
.as_ref()
.map_err(|io_error| ProgramError::InvalidLdSoCache {
error_kind: io_error.kind(),
.map_err(|error| UProbeError::InvalidLdSoCache {
io_error: error.clone(),
})?;
cache.resolve(target_str)
}
.map(String::from)
};
let path = path.ok_or(ProgramError::InvalidUprobeTarget {
let path = path.ok_or(UProbeError::InvalidTarget {
path: target.to_owned(),
})?;
let sym_offset = if let Some(fn_name) = fn_name {
resolve_symbol(&path, fn_name).map_err(|error| ProgramError::UprobeSymbolError {
resolve_symbol(&path, fn_name).map_err(|error| UProbeError::SymbolError {
symbol: fn_name.to_string(),
error: error.to_string(),
error: Box::new(error),
})?
} else {
0
@ -131,13 +173,22 @@ fn attach(
) -> Result<LinkRef, ProgramError> {
use ProbeKind::*;
let perf_ty = read_sys_fs_perf_type(match kind {
KProbe | KRetProbe => "kprobe",
UProbe | URetProbe => "uprobe",
})?;
let perf_ty = match kind {
KProbe | KRetProbe => read_sys_fs_perf_type("kprobe")
.map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
UProbe | URetProbe => read_sys_fs_perf_type("uprobe")
.map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
};
let ret_bit = match kind {
KRetProbe => Some(read_sys_fs_perf_ret_probe("kprobe")?),
URetProbe => Some(read_sys_fs_perf_ret_probe("uprobe")?),
KRetProbe => Some(
read_sys_fs_perf_ret_probe("kprobe")
.map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
),
URetProbe => Some(
read_sys_fs_perf_ret_probe("uprobe")
.map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
),
_ => None,
};
@ -270,13 +321,13 @@ impl LdSoCache {
#[derive(Error, Debug)]
enum ResolveSymbolError {
#[error("io error {0}")]
#[error(transparent)]
Io(#[from] io::Error),
#[error("error parsing ELF {0}")]
#[error("error parsing ELF")]
Object(#[from] object::Error),
#[error("unknown symbol {0}")]
#[error("unknown symbol `{0}`")]
Unknown(String),
}
@ -291,34 +342,32 @@ fn resolve_symbol(path: &str, symbol: &str) -> Result<u64, ResolveSymbolError> {
.ok_or_else(|| ResolveSymbolError::Unknown(symbol.to_string()))
}
fn read_sys_fs_perf_type(pmu: &str) -> Result<u32, ProgramError> {
fn read_sys_fs_perf_type(pmu: &str) -> Result<u32, (String, io::Error)> {
let file = format!("/sys/bus/event_source/devices/{}/type", pmu);
let perf_ty = fs::read_to_string(&file).map_err(|e| ProgramError::Other {
message: format!("error parsing {}: {}", file, e),
})?;
let perf_ty = fs::read_to_string(&file).map_err(|e| (file.clone(), e))?;
let perf_ty = perf_ty
.trim()
.parse::<u32>()
.map_err(|e| ProgramError::Other {
message: format!("error parsing {}: {}", file, e),
})?;
.map_err(|e| (file, io::Error::new(io::ErrorKind::Other, e)))?;
Ok(perf_ty)
}
fn read_sys_fs_perf_ret_probe(pmu: &str) -> Result<u32, ProgramError> {
fn read_sys_fs_perf_ret_probe(pmu: &str) -> Result<u32, (String, io::Error)> {
let file = format!("/sys/bus/event_source/devices/{}/format/retprobe", pmu);
let data = fs::read_to_string(&file).map_err(|e| ProgramError::Other {
message: format!("error parsing {}: {}", file, e),
})?;
let data = fs::read_to_string(&file).map_err(|e| (file.clone(), e))?;
let mut parts = data.trim().splitn(2, ":").skip(1);
let config = parts.next().ok_or(ProgramError::Other {
message: format!("error parsing {}: `{}'", file, data),
let config = parts.next().ok_or_else(|| {
(
file.clone(),
io::Error::new(io::ErrorKind::Other, "invalid format"),
)
})?;
config.parse::<u32>().map_err(|e| ProgramError::Other {
message: format!("error parsing {}: {}", file, e),
})
config
.parse::<u32>()
.map_err(|e| (file, io::Error::new(io::ErrorKind::Other, e)))
}

@ -1,11 +1,21 @@
use libc::{setsockopt, SOL_SOCKET, SO_ATTACH_BPF, SO_DETACH_BPF};
use std::{io, mem, os::unix::prelude::RawFd};
use thiserror::Error;
use crate::{
generated::bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER,
programs::{load_program, Link, LinkRef, ProgramData, ProgramError},
};
#[derive(Debug, Error)]
pub enum SocketFilterError {
#[error("setsockopt SO_ATTACH_BPF failed")]
SoAttachBpfError {
#[source]
io_error: io::Error,
},
}
#[derive(Debug)]
pub struct SocketFilter {
pub(crate) data: ProgramData,
@ -29,9 +39,9 @@ impl SocketFilter {
)
};
if ret < 0 {
return Err(ProgramError::SocketFilterError {
return Err(SocketFilterError::SoAttachBpfError {
io_error: io::Error::last_os_error(),
});
})?;
}
Ok(self.data.link(SocketFilterLink {

@ -1,9 +1,20 @@
use std::fs;
use std::{fs, io};
use thiserror::Error;
use crate::{generated::bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT, sys::perf_event_open_trace_point};
use super::{load_program, perf_attach, LinkRef, ProgramData, ProgramError};
#[derive(Debug, Error)]
pub enum TracePointError {
#[error("`{filename}`")]
FileError {
filename: String,
#[source]
io_error: io::Error,
},
}
#[derive(Debug)]
pub struct TracePoint {
pub(crate) data: ProgramData,
@ -24,15 +35,20 @@ impl TracePoint {
}
}
fn read_sys_fs_trace_point_id(category: &str, name: &str) -> Result<u32, ProgramError> {
fn read_sys_fs_trace_point_id(category: &str, name: &str) -> Result<u32, TracePointError> {
let file = format!("/sys/kernel/debug/tracing/events/{}/{}/id", category, name);
let id = fs::read_to_string(&file).map_err(|e| ProgramError::Other {
message: format!("error parsing {}: {}", file, e),
})?;
let id = id.trim().parse::<u32>().map_err(|e| ProgramError::Other {
message: format!("error parsing {}: {}", file, e),
let id = fs::read_to_string(&file).map_err(|io_error| TracePointError::FileError {
filename: file.clone(),
io_error,
})?;
let id = id
.trim()
.parse::<u32>()
.map_err(|error| TracePointError::FileError {
filename: file.clone(),
io_error: io::Error::new(io::ErrorKind::Other, error),
})?;
Ok(id)
}

@ -1,16 +1,25 @@
use std::ffi::CString;
use libc::if_nametoindex;
use std::{ffi::CString, io};
use thiserror::Error;
use crate::{generated::XDP_FLAGS_REPLACE, RawFd};
use crate::{
generated::{bpf_attach_type::BPF_XDP, bpf_prog_type::BPF_PROG_TYPE_XDP},
generated::{bpf_attach_type::BPF_XDP, bpf_prog_type::BPF_PROG_TYPE_XDP, XDP_FLAGS_REPLACE},
programs::{load_program, FdLink, Link, LinkRef, ProgramData, ProgramError},
sys::bpf_link_create,
sys::kernel_version,
sys::netlink_set_xdp_fd,
RawFd,
};
#[derive(Debug, Error)]
pub enum XdpError {
#[error("netlink error while attaching XDP program")]
NetlinkError {
#[source]
io_error: io::Error,
},
}
#[derive(Debug)]
pub struct Xdp {
pub(crate) data: ProgramData,
@ -31,30 +40,22 @@ impl Xdp {
let c_interface = CString::new(interface).unwrap();
let if_index = unsafe { if_nametoindex(c_interface.as_ptr()) } as RawFd;
if if_index == 0 {
return Err(ProgramError::UnkownInterface {
return Err(ProgramError::UnknownInterface {
name: interface.to_string(),
})?;
}
let k_ver = kernel_version().unwrap();
if k_ver >= (5, 7, 0) {
let link_fd =
bpf_link_create(prog_fd, if_index, BPF_XDP, 0).map_err(|(_, io_error)| {
ProgramError::BpfLinkCreateError {
program: self.name(),
io_error,
}
})? as RawFd;
let link_fd = bpf_link_create(prog_fd, if_index + 42, BPF_XDP, 0)
.map_err(|(_, io_error)| ProgramError::BpfLinkCreateError { io_error })?
as RawFd;
Ok(self
.data
.link(XdpLink::FdLink(FdLink { fd: Some(link_fd) })))
} else {
unsafe { netlink_set_xdp_fd(if_index, prog_fd, None, 0) }.map_err(|io_error| {
ProgramError::NetlinkXdpError {
program: self.name(),
io_error,
}
})?;
unsafe { netlink_set_xdp_fd(if_index, prog_fd, None, 0) }
.map_err(|io_error| XdpError::NetlinkError { io_error })?;
Ok(self.data.link(XdpLink::NlLink(NlLink {
if_index,

Loading…
Cancel
Save