Support for fentry and fexit programs

fentry and fexit programs are similar to kprobe and kretprobe, but they
are newer and they have practically zero overhead to call before or
after kernel function. Also, fexit programs are focused on access to
arguments rather than the return value.

Those kind of programs were introduced in the following patchset:

https://lwn.net/Articles/804112/

Signed-off-by: Michal Rostecki <mrostecki@opensuse.org>
pull/143/head
Michal Rostecki 3 years ago committed by Alessandro Decina
parent 54b0c67795
commit 7e2fcd1d6d

@ -21,9 +21,9 @@ use crate::{
Object, ParseError, ProgramSection,
},
programs::{
BtfTracePoint, CgroupSkb, CgroupSkbAttachType, KProbe, LircMode2, Lsm, PerfEvent,
ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkMsg,
SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
BtfTracePoint, CgroupSkb, CgroupSkbAttachType, FEntry, FExit, KProbe, LircMode2, Lsm,
PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier,
SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
},
sys::bpf_map_update_elem_ptr,
util::{possible_cpus, POSSIBLE_CPUS},
@ -314,6 +314,8 @@ impl<'a> BpfLoader<'a> {
ProgramSection::BtfTracePoint { .. } => {
Program::BtfTracePoint(BtfTracePoint { data })
}
ProgramSection::FEntry { .. } => Program::FEntry(FEntry { data }),
ProgramSection::FExit { .. } => Program::FExit(FExit { data }),
};
(name, program)

@ -88,6 +88,8 @@ pub enum ProgramSection {
RawTracePoint { name: String },
Lsm { name: String },
BtfTracePoint { name: String },
FEntry { name: String },
FExit { name: String },
}
impl ProgramSection {
@ -112,6 +114,8 @@ impl ProgramSection {
ProgramSection::RawTracePoint { name } => name,
ProgramSection::Lsm { name } => name,
ProgramSection::BtfTracePoint { name } => name,
ProgramSection::FEntry { name } => name,
ProgramSection::FExit { name } => name,
}
}
}
@ -165,6 +169,8 @@ impl FromStr for ProgramSection {
"perf_event" => PerfEvent { name },
"raw_tp" | "raw_tracepoint" => RawTracePoint { name },
"lsm" => Lsm { name },
"fentry" => FEntry { name },
"fexit" => FExit { name },
_ => {
return Err(ParseError::InvalidProgramSection {
section: section.to_owned(),
@ -1192,4 +1198,46 @@ mod tests {
})
);
}
#[test]
fn test_parse_section_fentry() {
let mut obj = fake_obj();
assert_matches!(
obj.parse_section(fake_section(
BpfSectionKind::Program,
"fentry/foo",
bytes_of(&fake_ins())
)),
Ok(())
);
assert_matches!(
obj.programs.get("foo"),
Some(Program {
section: ProgramSection::FEntry { .. },
..
})
);
}
#[test]
fn test_parse_section_fexit() {
let mut obj = fake_obj();
assert_matches!(
obj.parse_section(fake_section(
BpfSectionKind::Program,
"fexit/foo",
bytes_of(&fake_ins())
)),
Ok(())
);
assert_matches!(
obj.programs.get("foo"),
Some(Program {
section: ProgramSection::FExit { .. },
..
})
);
}
}

@ -0,0 +1,67 @@
//! fentry programs.
use crate::{
generated::{bpf_attach_type::BPF_TRACE_FENTRY, bpf_prog_type::BPF_PROG_TYPE_TRACING},
obj::btf::{Btf, BtfKind},
programs::{load_program, utils::attach_raw_tracepoint, LinkRef, ProgramData, ProgramError},
};
/// A program that can be attached to the entry point of (almost) any kernel
/// function.
///
/// [`FEntry`] programs are similar to [kprobes](crate::programs::KProbe), but
/// the difference is that fentry has practically zero overhead to call before
/// kernel function. Fentry programs can be also attaached to other eBPF
/// programs.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 5.5.
///
/// # Examples
///
/// ```no_run
/// # #[derive(thiserror::Error, Debug)]
/// # enum Error {
/// # #[error(transparent)]
/// # BtfError(#[from] aya::BtfError),
/// # #[error(transparent)]
/// # Program(#[from] aya::programs::ProgramError),
/// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError),
/// # }
/// # let mut bpf = Bpf::load_file("ebpf_programs.o")?;
/// use aya::{Bpf, programs::FEntry, BtfError, Btf};
/// use std::convert::TryInto;
///
/// let btf = Btf::from_sys_fs()?;
/// let program: &mut FEntry = bpf.program_mut("filename_lookup").unwrap().try_into()?;
/// program.load("filename_lookup", &btf)?;
/// program.attach()?;
/// # Ok::<(), Error>(())
/// ```
#[derive(Debug)]
#[doc(alias = "BPF_TRACE_FENTRY")]
#[doc(alias = "BPF_PROG_TYPE_TRACING")]
pub struct FEntry {
pub(crate) data: ProgramData,
}
impl FEntry {
/// Loads the program inside the kernel.
///
/// See also [`Program::load`](crate::programs::Program::load).
///
/// Loads the program so it's executed when the kernel function `fn_name`
/// is entered. The `btf` argument must contain the BTF info for the
/// running kernel.
pub fn load(&mut self, fn_name: &str, btf: &Btf) -> Result<(), ProgramError> {
self.data.expected_attach_type = Some(BPF_TRACE_FENTRY);
self.data.attach_btf_id = Some(btf.id_by_type_name_kind(fn_name, BtfKind::Func)?);
load_program(BPF_PROG_TYPE_TRACING, &mut self.data)
}
/// Attaches the program
pub fn attach(&mut self) -> Result<LinkRef, ProgramError> {
attach_raw_tracepoint(&mut self.data, None)
}
}

@ -0,0 +1,67 @@
//! fexit programs.
use crate::{
generated::{bpf_attach_type::BPF_TRACE_FEXIT, bpf_prog_type::BPF_PROG_TYPE_TRACING},
obj::btf::{Btf, BtfKind},
programs::{load_program, utils::attach_raw_tracepoint, LinkRef, ProgramData, ProgramError},
};
/// A program that can be attached to the exit point of (almost) anny kernel
/// function.
///
/// [`FExit`] programs are similar to [kretprobes](crate::programs::KProbe),
/// but the difference is that fexit has practically zero overhead to call
/// before kernel function. Fexit programs can be also attached to other eBPF
/// programs.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 5.5.
///
/// # Examples
///
/// ```no_run
/// # #[derive(thiserror::Error, Debug)]
/// # enum Error {
/// # #[error(transparent)]
/// # BtfError(#[from] aya::BtfError),
/// # #[error(transparent)]
/// # Program(#[from] aya::programs::ProgramError),
/// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError),
/// # }
/// # let mut bpf = Bpf::load_file("ebpf_programs.o")?;
/// use aya::{Bpf, programs::FExit, BtfError, Btf};
/// use std::convert::TryInto;
///
/// let btf = Btf::from_sys_fs()?;
/// let program: &mut FExit = bpf.program_mut("filename_lookup").unwrap().try_into()?;
/// program.load("filename_lookup", &btf)?;
/// program.attach()?;
/// # Ok::<(), Error>(())
/// ```
#[derive(Debug)]
#[doc(alias = "BPF_TRACE_FEXIT")]
#[doc(alias = "BPF_PROG_TYPE_TRACING")]
pub struct FExit {
pub(crate) data: ProgramData,
}
impl FExit {
/// Loads the program inside the kernel.
///
/// See also [`Program::load`](crate::programs::Program::load).
///
/// Loads the program so it's executed when the kernel function `fn_name`
/// is exited. The `btf` argument must contain the BTF info for the running
/// kernel.
pub fn load(&mut self, fn_name: &str, btf: &Btf) -> Result<(), ProgramError> {
self.data.expected_attach_type = Some(BPF_TRACE_FEXIT);
self.data.attach_btf_id = Some(btf.id_by_type_name_kind(fn_name, BtfKind::Func)?);
load_program(BPF_PROG_TYPE_TRACING, &mut self.data)
}
/// Attaches the program
pub fn attach(&mut self) -> Result<LinkRef, ProgramError> {
attach_raw_tracepoint(&mut self.data, None)
}
}

@ -1,13 +1,8 @@
//! LSM probes.
use std::os::unix::io::RawFd;
use thiserror::Error;
use crate::{
generated::{bpf_attach_type::BPF_LSM_MAC, bpf_prog_type::BPF_PROG_TYPE_LSM},
obj::btf::{Btf, BtfError, BtfKind},
programs::{load_program, FdLink, LinkRef, ProgramData, ProgramError},
sys::bpf_raw_tracepoint_open,
obj::btf::{Btf, BtfKind},
programs::{load_program, utils::attach_raw_tracepoint, LinkRef, ProgramData, ProgramError},
};
/// A program that attaches to Linux LSM hooks. Used to implement security policy and
@ -30,8 +25,6 @@ use crate::{
/// # #[derive(thiserror::Error, Debug)]
/// # enum LsmError {
/// # #[error(transparent)]
/// # LsmLoad(#[from] aya::programs::LsmLoadError),
/// # #[error(transparent)]
/// # BtfError(#[from] aya::BtfError),
/// # #[error(transparent)]
/// # Program(#[from] aya::programs::ProgramError),
@ -56,16 +49,6 @@ pub struct Lsm {
pub(crate) data: ProgramData,
}
/// Error type returned when loading LSM programs.
#[derive(Debug, Error)]
pub enum LsmLoadError {
#[error(transparent)]
Btf(#[from] BtfError),
#[error(transparent)]
Program(#[from] ProgramError),
}
impl Lsm {
/// Loads the program inside the kernel.
///
@ -75,32 +58,16 @@ impl Lsm {
///
/// * `lsm_hook_name` - full name of the LSM hook that the program should
/// be attached to
pub fn load(&mut self, lsm_hook_name: &str, btf: &Btf) -> Result<(), LsmLoadError> {
pub fn load(&mut self, lsm_hook_name: &str, btf: &Btf) -> Result<(), ProgramError> {
self.data.expected_attach_type = Some(BPF_LSM_MAC);
let type_name = format!("bpf_lsm_{}", lsm_hook_name);
self.data.attach_btf_id =
Some(btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Func)?);
load_program(BPF_PROG_TYPE_LSM, &mut self.data).map_err(LsmLoadError::from)
load_program(BPF_PROG_TYPE_LSM, &mut self.data)
}
/// Attaches the program.
pub fn attach(&mut self) -> Result<LinkRef, ProgramError> {
attach_btf_id(&mut self.data)
attach_raw_tracepoint(&mut self.data, None)
}
}
/// Common logic for all BPF program types that attach to a BTF id.
pub(crate) fn attach_btf_id(program_data: &mut ProgramData) -> Result<LinkRef, ProgramError> {
let prog_fd = program_data.fd_or_err()?;
// Attaching LSM programs doesn't require providing attach name. LSM
// programs are attached to LSM hook which is specified in the program.
let pfd = bpf_raw_tracepoint_open(None, prog_fd).map_err(|(_code, io_error)| {
ProgramError::SyscallError {
call: "bpf_raw_tracepoint_open".to_owned(),
io_error,
}
})? as RawFd;
Ok(program_data.link(FdLink { fd: Some(pfd) }))
}

@ -37,6 +37,8 @@
//! [`Bpf::program_mut`]: crate::Bpf::program_mut
//! [`maps`]: crate::maps
mod cgroup_skb;
mod fentry;
mod fexit;
mod kprobe;
mod lirc_mode2;
mod lsm;
@ -52,6 +54,7 @@ pub mod tc;
mod tp_btf;
mod trace_point;
mod uprobe;
mod utils;
mod xdp;
use libc::{close, dup, ENOSPC};
@ -68,9 +71,11 @@ use std::{
use thiserror::Error;
pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType};
pub use fentry::FEntry;
pub use fexit::FExit;
pub use kprobe::{KProbe, KProbeError};
pub use lirc_mode2::LircMode2;
pub use lsm::{Lsm, LsmLoadError};
pub use lsm::Lsm;
use perf_attach::*;
pub use perf_event::{PerfEvent, PerfEventScope, PerfTypeId, SamplePolicy};
pub use probe::ProbeKind;
@ -80,7 +85,7 @@ pub use sk_skb::{SkSkb, SkSkbKind};
pub use sock_ops::SockOps;
pub use socket_filter::{SocketFilter, SocketFilterError};
pub use tc::{SchedClassifier, TcAttachType, TcError};
pub use tp_btf::{BtfTracePoint, BtfTracePointError};
pub use tp_btf::BtfTracePoint;
pub use trace_point::{TracePoint, TracePointError};
pub use uprobe::{UProbe, UProbeError};
pub use xdp::{Xdp, XdpError, XdpFlags};
@ -88,7 +93,7 @@ pub use xdp::{Xdp, XdpError, XdpFlags};
use crate::{
generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type},
maps::MapError,
obj::{self, Function, KernelVersion},
obj::{self, btf::BtfError, Function, KernelVersion},
sys::{bpf_load_program, bpf_pin_object, bpf_prog_detach, bpf_prog_query, BpfLoadProgramAttrs},
};
@ -170,9 +175,9 @@ pub enum ProgramError {
#[error(transparent)]
TcError(#[from] TcError),
/// An error occurred while working with a BTF raw tracepoint program.
/// An error occurred while working with BTF.
#[error(transparent)]
BtfTracePointError(#[from] BtfTracePointError),
Btf(#[from] BtfError),
}
pub trait ProgramFd {
@ -197,6 +202,8 @@ pub enum Program {
RawTracePoint(RawTracePoint),
Lsm(Lsm),
BtfTracePoint(BtfTracePoint),
FEntry(FEntry),
FExit(FExit),
}
impl Program {
@ -233,6 +240,8 @@ impl Program {
Program::RawTracePoint(_) => BPF_PROG_TYPE_RAW_TRACEPOINT,
Program::Lsm(_) => BPF_PROG_TYPE_LSM,
Program::BtfTracePoint(_) => BPF_PROG_TYPE_TRACING,
Program::FEntry(_) => BPF_PROG_TYPE_TRACING,
Program::FExit(_) => BPF_PROG_TYPE_TRACING,
}
}
@ -258,6 +267,8 @@ impl Program {
Program::RawTracePoint(p) => &p.data,
Program::Lsm(p) => &p.data,
Program::BtfTracePoint(p) => &p.data,
Program::FEntry(p) => &p.data,
Program::FExit(p) => &p.data,
}
}
@ -278,6 +289,8 @@ impl Program {
Program::RawTracePoint(p) => &mut p.data,
Program::Lsm(p) => &mut p.data,
Program::BtfTracePoint(p) => &mut p.data,
Program::FEntry(p) => &mut p.data,
Program::FExit(p) => &mut p.data,
}
}
}
@ -605,6 +618,8 @@ impl_program_fd!(
Lsm,
RawTracePoint,
BtfTracePoint,
FEntry,
FExit,
);
macro_rules! impl_try_from_program {
@ -651,6 +666,8 @@ impl_try_from_program!(
Lsm,
RawTracePoint,
BtfTracePoint,
FEntry,
FExit,
);
/// Provides information about a loaded program, like name, id and statistics

@ -1,10 +1,9 @@
//! Raw tracepoints.
use std::{ffi::CString, os::unix::io::RawFd};
use std::ffi::CString;
use crate::{
generated::bpf_prog_type::BPF_PROG_TYPE_RAW_TRACEPOINT,
programs::{load_program, FdLink, LinkRef, ProgramData, ProgramError},
sys::bpf_raw_tracepoint_open,
programs::{load_program, utils::attach_raw_tracepoint, LinkRef, ProgramData, ProgramError},
};
/// A program that can be attached at a pre-defined kernel trace point, but also
@ -47,16 +46,7 @@ impl RawTracePoint {
/// Attaches the program to the given tracepoint.
pub fn attach(&mut self, tp_name: &str) -> Result<LinkRef, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
let name = CString::new(tp_name).unwrap();
let pfd = bpf_raw_tracepoint_open(Some(&name), prog_fd).map_err(|(_code, io_error)| {
ProgramError::SyscallError {
call: "bpf_raw_tracepoint_open".to_owned(),
io_error,
}
})? as RawFd;
Ok(self.data.link(FdLink { fd: Some(pfd) }))
let tp_name_c = CString::new(tp_name).unwrap();
attach_raw_tracepoint(&mut self.data, Some(&tp_name_c))
}
}

@ -1,13 +1,8 @@
//! BTF-enabled raw tracepoints.
use std::os::unix::io::RawFd;
use thiserror::Error;
use crate::{
generated::{bpf_attach_type::BPF_TRACE_RAW_TP, bpf_prog_type::BPF_PROG_TYPE_TRACING},
obj::btf::{Btf, BtfError, BtfKind},
programs::{load_program, FdLink, LinkRef, ProgramData, ProgramError},
sys::bpf_raw_tracepoint_open,
obj::btf::{Btf, BtfKind},
programs::{load_program, utils::attach_raw_tracepoint, LinkRef, ProgramData, ProgramError},
};
/// Marks a function as a [BTF-enabled raw tracepoint][1] eBPF program that can be attached at
@ -27,8 +22,6 @@ use crate::{
/// # #[derive(thiserror::Error, Debug)]
/// # enum Error {
/// # #[error(transparent)]
/// # BtfTracePointError(#[from] aya::programs::BtfTracePointError),
/// # #[error(transparent)]
/// # BtfError(#[from] aya::BtfError),
/// # #[error(transparent)]
/// # Program(#[from] aya::programs::ProgramError),
@ -54,14 +47,6 @@ pub struct BtfTracePoint {
pub(crate) data: ProgramData,
}
/// Error type returned when loading LSM programs.
#[derive(Debug, Error)]
pub enum BtfTracePointError {
/// An error occured while working with BTF.
#[error(transparent)]
Btf(#[from] BtfError),
}
impl BtfTracePoint {
/// Loads the program inside the kernel.
///
@ -74,25 +59,13 @@ impl BtfTracePoint {
pub fn load(&mut self, tracepoint: &str, btf: &Btf) -> Result<(), ProgramError> {
self.data.expected_attach_type = Some(BPF_TRACE_RAW_TP);
let type_name = format!("btf_trace_{}", tracepoint);
self.data.attach_btf_id = Some(
btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Typedef)
.map_err(BtfTracePointError::from)?,
);
self.data.attach_btf_id =
Some(btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Typedef)?);
load_program(BPF_PROG_TYPE_TRACING, &mut self.data)
}
/// Attaches the program.
pub fn attach(&mut self) -> Result<LinkRef, ProgramError> {
let prog_fd = self.data.fd_or_err()?;
// BTF programs specify their attach name at program load time
let pfd = bpf_raw_tracepoint_open(None, prog_fd).map_err(|(_code, io_error)| {
ProgramError::SyscallError {
call: "bpf_raw_tracepoint_open".to_owned(),
io_error,
}
})? as RawFd;
Ok(self.data.link(FdLink { fd: Some(pfd) }))
attach_raw_tracepoint(&mut self.data, None)
}
}

@ -0,0 +1,24 @@
//! Common functions shared between multiple eBPF program types.
use std::{ffi::CStr, os::unix::io::RawFd};
use crate::{
programs::{FdLink, LinkRef, ProgramData, ProgramError},
sys::bpf_raw_tracepoint_open,
};
/// Attaches the program to a raw tracepoint.
pub(crate) fn attach_raw_tracepoint(
program_data: &mut ProgramData,
tp_name: Option<&CStr>,
) -> Result<LinkRef, ProgramError> {
let prog_fd = program_data.fd_or_err()?;
let pfd = bpf_raw_tracepoint_open(tp_name, prog_fd).map_err(|(_code, io_error)| {
ProgramError::SyscallError {
call: "bpf_raw_tracepoint_open".to_owned(),
io_error,
}
})? as RawFd;
Ok(program_data.link(FdLink { fd: Some(pfd) }))
}

@ -529,6 +529,64 @@ impl SocketFilter {
}
}
pub struct FEntry {
item: ItemFn,
name: String,
}
impl FEntry {
pub fn from_syn(mut args: Args, item: ItemFn) -> Result<FEntry> {
let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
Ok(FEntry { item, name })
}
pub fn expand(&self) -> Result<TokenStream> {
let section_name = format!("fentry/{}", self.name);
let fn_name = &self.item.sig.ident;
let item = &self.item;
Ok(quote! {
#[no_mangle]
#[link_section = #section_name]
fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 {
let _ = #fn_name(::aya_bpf::programs::FEntryContext::new(ctx));
return 0;
#item
}
})
}
}
pub struct FExit {
item: ItemFn,
name: String,
}
impl FExit {
pub fn from_syn(mut args: Args, item: ItemFn) -> Result<FExit> {
let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
Ok(FExit { item, name })
}
pub fn expand(&self) -> Result<TokenStream> {
let section_name = format!("fexit/{}", self.name);
let fn_name = &self.item.sig.ident;
let item = &self.item;
Ok(quote! {
#[no_mangle]
#[link_section = #section_name]
fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 {
let _ = #fn_name(::aya_bpf::programs::FExitContext::new(ctx));
return 0;
#item
}
})
}
}
#[cfg(test)]
mod tests {
use syn::parse_quote;

@ -1,8 +1,9 @@
mod expand;
use expand::{
Args, BtfTracePoint, CgroupSkb, Lsm, Map, PerfEvent, Probe, ProbeKind, RawTracePoint,
SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, Xdp,
Args, BtfTracePoint, CgroupSkb, FEntry, FExit, Lsm, Map, PerfEvent, Probe, ProbeKind,
RawTracePoint, SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint,
Xdp,
};
use proc_macro::TokenStream;
use syn::{parse_macro_input, ItemFn, ItemStatic};
@ -344,3 +345,86 @@ pub fn socket_filter(attrs: TokenStream, item: TokenStream) -> TokenStream {
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
/// Marks a function as a fentry eBPF program that can be attached to almost
/// any function inside the kernel. The difference between fentry and kprobe
/// is that fexit has practically zero overhead to call before kernel function.
/// fentry programs can be also attached to other eBPF programs.
///
/// # Minimumm kernel version
///
/// The minimum kernel version required to use this feature is 5.5.
///
/// # Examples
///
/// ```no_run
/// use aya_bpf::{macros::fentry, programs::FEntryContext};
/// use vmlinux::{filename, path};
///
/// #[fentry(name = "filename_lookup")]
/// fn filename_lookup(ctx: FEntryContext) -> i32 {
/// match { try_filename_lookup(ctx) } {
/// Ok(ret) => ret,
/// Err(ret) => ret,
/// }
/// }
///
/// unsafe fn try_filename_lookup(ctx: FEntryContext) -> Result<u32, u32> {
/// let _f: *const filename = ctx.arg(1);
/// let _p: *const path = ctx.arg(3);
///
/// Ok(0)
/// }
/// ```
#[proc_macro_attribute]
pub fn fentry(attrs: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attrs as Args);
let item = parse_macro_input!(item as ItemFn);
FEntry::from_syn(args, item)
.and_then(|u| u.expand())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
/// Marks a function as a fexit eBPF program that can be attached to almost
/// any function inside the kernel. The difference between fexit and kretprobe
/// is that fexit has practically zero overhead to call after kernel function
/// and it focuses on access to arguments rather than the return value. fexit
/// programs can be also attached to other eBPF programs
///
/// # Minimumm kernel version
///
/// The minimum kernel version required to use this feature is 5.5.
///
/// # Examples
///
/// ```no_run
/// use aya_bpf::{macros::fexit, programs::FExitContext};
/// use vmlinux::{filename, path};
///
/// #[fexit(name = "filename_lookup")]
/// fn filename_lookup(ctx: FExitContext) -> i32 {
/// match { try_filename_lookup(ctx) } {
/// Ok(ret) => ret,
/// Err(ret) => ret,
/// }
/// }
///
/// unsafe fn try_filename_lookup(ctx: FExitContext) -> Result<u32, u32> {
/// let _f: *const filename = ctx.arg(1);
/// let _p: *const path = ctx.arg(3);
///
/// Ok(0)
/// }
/// ```
#[proc_macro_attribute]
pub fn fexit(attrs: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attrs as Args);
let item = parse_macro_input!(item as ItemFn);
FExit::from_syn(args, item)
.and_then(|u| u.expand())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

@ -0,0 +1,43 @@
use core::ffi::c_void;
use crate::{args::FromBtfArgument, BpfContext};
pub struct FEntryContext {
ctx: *mut c_void,
}
impl FEntryContext {
pub fn new(ctx: *mut c_void) -> FEntryContext {
FEntryContext { ctx }
}
/// Returns the `n`th argument to passed to the probe function, starting from 0.
///
/// # Examples
///
/// ```no_run
/// # #![allow(non_camel_case_types)]
/// # #![allow(dead_code)]
/// # use aya_bpf::{cty::c_int, programs::FEntryContext};
/// # type pid_t = c_int;
/// # struct task_struct {
/// # pid: pid_t,
/// # }
/// unsafe fn try_fentry_try_to_wake_up(ctx: FEntryContext) -> Result<u32, u32> {
/// let tp: *const task_struct = ctx.arg(0);
///
/// // Do something with tp
///
/// Ok(0)
/// }
/// ```
pub unsafe fn arg<T: FromBtfArgument>(&self, n: usize) -> T {
T::from_argument(self.ctx as *const _, n)
}
}
impl BpfContext for FEntryContext {
fn as_ptr(&self) -> *mut c_void {
self.ctx
}
}

@ -0,0 +1,43 @@
use core::ffi::c_void;
use crate::{args::FromBtfArgument, BpfContext};
pub struct FExitContext {
ctx: *mut c_void,
}
impl FExitContext {
pub fn new(ctx: *mut c_void) -> FExitContext {
FExitContext { ctx }
}
/// Returns the `n`th argument to passed to the probe function, starting from 0.
///
/// # Examples
///
/// ```no_run
/// # #![allow(non_camel_case_types)]
/// # #![allow(dead_code)]
/// # use aya_bpf::{cty::c_int, programs::FExitContext};
/// # type pid_t = c_int;
/// # struct task_struct {
/// # pid: pid_t,
/// # }
/// unsafe fn try_filename_lookup(ctx: FExitContext) -> Result<u32, u32> {
/// let tp: *const task_struct = ctx.arg(0);
///
/// // Do something with tp
///
/// Ok(0)
/// }
/// ```
pub unsafe fn arg<T: FromBtfArgument>(&self, n: usize) -> T {
T::from_argument(self.ctx as *const _, n)
}
}
impl BpfContext for FExitContext {
fn as_ptr(&self) -> *mut c_void {
self.ctx
}
}

@ -1,3 +1,5 @@
pub mod fentry;
pub mod fexit;
pub mod lsm;
pub mod perf_event;
pub mod probe;
@ -9,6 +11,8 @@ pub mod tp_btf;
pub mod tracepoint;
pub mod xdp;
pub use fentry::FEntryContext;
pub use fexit::FExitContext;
pub use lsm::LsmContext;
pub use perf_event::PerfEventContext;
pub use probe::ProbeContext;

Loading…
Cancel
Save