diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 8e102dea..eea4bd55 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -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) diff --git a/aya/src/obj/mod.rs b/aya/src/obj/mod.rs index b309b408..2eee874f 100644 --- a/aya/src/obj/mod.rs +++ b/aya/src/obj/mod.rs @@ -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 { .. }, + .. + }) + ); + } } diff --git a/aya/src/programs/fentry.rs b/aya/src/programs/fentry.rs new file mode 100644 index 00000000..f94cb439 --- /dev/null +++ b/aya/src/programs/fentry.rs @@ -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 { + attach_raw_tracepoint(&mut self.data, None) + } +} diff --git a/aya/src/programs/fexit.rs b/aya/src/programs/fexit.rs new file mode 100644 index 00000000..85d3d2e9 --- /dev/null +++ b/aya/src/programs/fexit.rs @@ -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 { + attach_raw_tracepoint(&mut self.data, None) + } +} diff --git a/aya/src/programs/lsm.rs b/aya/src/programs/lsm.rs index ef057d9b..03c0a778 100644 --- a/aya/src/programs/lsm.rs +++ b/aya/src/programs/lsm.rs @@ -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 { - 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 { - 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) })) -} diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 6d32187d..5b10e3e8 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -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 diff --git a/aya/src/programs/raw_trace_point.rs b/aya/src/programs/raw_trace_point.rs index 81b0e393..66794870 100644 --- a/aya/src/programs/raw_trace_point.rs +++ b/aya/src/programs/raw_trace_point.rs @@ -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 { - 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)) } } diff --git a/aya/src/programs/tp_btf.rs b/aya/src/programs/tp_btf.rs index 966414df..d0631498 100644 --- a/aya/src/programs/tp_btf.rs +++ b/aya/src/programs/tp_btf.rs @@ -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 { - 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) } } diff --git a/aya/src/programs/utils.rs b/aya/src/programs/utils.rs new file mode 100644 index 00000000..c864da93 --- /dev/null +++ b/aya/src/programs/utils.rs @@ -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 { + 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) })) +} diff --git a/bpf/aya-bpf-macros/src/expand.rs b/bpf/aya-bpf-macros/src/expand.rs index 39791e7a..ed10ca3f 100644 --- a/bpf/aya-bpf-macros/src/expand.rs +++ b/bpf/aya-bpf-macros/src/expand.rs @@ -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 { + let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string()); + + Ok(FEntry { item, name }) + } + + pub fn expand(&self) -> Result { + 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 { + let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string()); + + Ok(FExit { item, name }) + } + + pub fn expand(&self) -> Result { + 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; diff --git a/bpf/aya-bpf-macros/src/lib.rs b/bpf/aya-bpf-macros/src/lib.rs index 08d2748a..4ae01f83 100644 --- a/bpf/aya-bpf-macros/src/lib.rs +++ b/bpf/aya-bpf-macros/src/lib.rs @@ -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 { +/// 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 { +/// 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() +} diff --git a/bpf/aya-bpf/src/programs/fentry.rs b/bpf/aya-bpf/src/programs/fentry.rs new file mode 100644 index 00000000..56687539 --- /dev/null +++ b/bpf/aya-bpf/src/programs/fentry.rs @@ -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 { + /// let tp: *const task_struct = ctx.arg(0); + /// + /// // Do something with tp + /// + /// Ok(0) + /// } + /// ``` + pub unsafe fn arg(&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 + } +} diff --git a/bpf/aya-bpf/src/programs/fexit.rs b/bpf/aya-bpf/src/programs/fexit.rs new file mode 100644 index 00000000..1e52d733 --- /dev/null +++ b/bpf/aya-bpf/src/programs/fexit.rs @@ -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 { + /// let tp: *const task_struct = ctx.arg(0); + /// + /// // Do something with tp + /// + /// Ok(0) + /// } + /// ``` + pub unsafe fn arg(&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 + } +} diff --git a/bpf/aya-bpf/src/programs/mod.rs b/bpf/aya-bpf/src/programs/mod.rs index 097d428d..8a022c18 100644 --- a/bpf/aya-bpf/src/programs/mod.rs +++ b/bpf/aya-bpf/src/programs/mod.rs @@ -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;