diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index b7070d29..9aec7fe7 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -20,9 +20,9 @@ use crate::{ Object, ParseError, ProgramSection, }, programs::{ - CgroupSkb, CgroupSkbAttachType, KProbe, LircMode2, PerfEvent, ProbeKind, Program, - ProgramData, ProgramError, SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, - TracePoint, UProbe, Xdp, + CgroupSkb, CgroupSkbAttachType, 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}, @@ -253,6 +253,9 @@ impl<'a> BpfLoader<'a> { name: name.clone(), fd: None, links: Vec::new(), + expected_attach_type: None, + attach_btf_obj_fd: None, + attach_btf_id: None, }; let program = match section { ProgramSection::KProbe { .. } => Program::KProbe(KProbe { @@ -299,6 +302,10 @@ impl<'a> BpfLoader<'a> { }), ProgramSection::LircMode2 { .. } => Program::LircMode2(LircMode2 { data }), ProgramSection::PerfEvent { .. } => Program::PerfEvent(PerfEvent { data }), + ProgramSection::RawTracePoint { .. } => { + Program::RawTracePoint(RawTracePoint { data }) + } + ProgramSection::Lsm { .. } => Program::Lsm(Lsm { data }), }; (name, program) diff --git a/aya/src/obj/btf/btf.rs b/aya/src/obj/btf/btf.rs index 32c15520..a6ba2990 100644 --- a/aya/src/obj/btf/btf.rs +++ b/aya/src/obj/btf/btf.rs @@ -12,7 +12,7 @@ use thiserror::Error; use crate::{ generated::{btf_ext_header, btf_header}, - obj::btf::{relocation::Relocation, BtfType}, + obj::btf::{relocation::Relocation, BtfKind, BtfType}, }; pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32; @@ -66,6 +66,9 @@ pub enum BtfError { #[error("Unexpected BTF type id `{type_id}`")] UnexpectedBtfType { type_id: u32 }, + #[error("Unknown BTF type `{type_name}`")] + UnknownBtfTypeName { type_name: String }, + #[error("maximum depth reached resolving BTF type")] MaximumTypeDepthReached { type_id: u32 }, } @@ -221,6 +224,33 @@ impl Btf { .and_then(|off| self.string_at(off).ok().map(String::from)) } + pub(crate) fn id_by_type_name_kind(&self, name: &str, kind: BtfKind) -> Result { + for (type_id, ty) in self.types().enumerate() { + match ty.kind()? { + Some(k) => { + if k != kind { + continue; + } + } + None => continue, + } + + match self.type_name(ty)? { + Some(ty_name) => { + if ty_name == name { + return Ok(type_id as u32); + } + continue; + } + None => continue, + } + } + + Err(BtfError::UnknownBtfTypeName { + type_name: name.to_string(), + }) + } + pub(crate) fn type_size(&self, root_type_id: u32) -> Result { let mut type_id = root_type_id; let mut n_elems = 1; diff --git a/aya/src/obj/mod.rs b/aya/src/obj/mod.rs index edf6341e..8b6c89c4 100644 --- a/aya/src/obj/mod.rs +++ b/aya/src/obj/mod.rs @@ -85,6 +85,8 @@ pub enum ProgramSection { CgroupSkbEgress { name: String }, LircMode2 { name: String }, PerfEvent { name: String }, + RawTracePoint { name: String }, + Lsm { name: String }, } impl ProgramSection { @@ -106,6 +108,8 @@ impl ProgramSection { ProgramSection::CgroupSkbEgress { name } => name, ProgramSection::LircMode2 { name } => name, ProgramSection::PerfEvent { name } => name, + ProgramSection::RawTracePoint { name } => name, + ProgramSection::Lsm { name } => name, } } } @@ -147,6 +151,8 @@ impl FromStr for ProgramSection { "cgroup_skb/egress" => CgroupSkbEgress { name }, "lirc_mode2" => LircMode2 { name }, "perf_event" => PerfEvent { name }, + "raw_tp" | "raw_tracepoint" => RawTracePoint { name }, + "lsm" => Lsm { name }, _ => { return Err(ParseError::InvalidProgramSection { section: section.to_owned(), @@ -570,6 +576,9 @@ fn is_program_section(name: &str) -> bool { "uprobe", "uretprobe", "xdp", + "raw_tp", + "raw_tracepoint", + "lsm", ] { if name.starts_with(prefix) { return true; @@ -991,4 +1000,50 @@ mod tests { }) ); } + + #[test] + fn test_parse_section_raw_tp() { + let mut obj = fake_obj(); + + assert_matches!( + obj.parse_section(fake_section("raw_tp/foo", bytes_of(&fake_ins()))), + Ok(()) + ); + assert_matches!( + obj.programs.get("foo"), + Some(Program { + section: ProgramSection::RawTracePoint { .. }, + .. + }) + ); + + assert_matches!( + obj.parse_section(fake_section("raw_tracepoint/bar", bytes_of(&fake_ins()))), + Ok(()) + ); + assert_matches!( + obj.programs.get("bar"), + Some(Program { + section: ProgramSection::RawTracePoint { .. }, + .. + }) + ); + } + + #[test] + fn test_parse_section_lsm() { + let mut obj = fake_obj(); + + assert_matches!( + obj.parse_section(fake_section("lsm/foo", bytes_of(&fake_ins()))), + Ok(()) + ); + assert_matches!( + obj.programs.get("foo"), + Some(Program { + section: ProgramSection::Lsm { .. }, + .. + }) + ); + } } diff --git a/aya/src/programs/lsm.rs b/aya/src/programs/lsm.rs new file mode 100644 index 00000000..30f21aea --- /dev/null +++ b/aya/src/programs/lsm.rs @@ -0,0 +1,109 @@ +//! 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, +}; + +/// A program that attaches to Linux LSM hooks. Used to implement security policy and +/// audit logging. +/// +/// LSM probes can be attached to the kernel's [security hooks][1] to implement mandatory +/// access control policy and security auditing. +/// +/// LSM probes require a kernel compiled with `CONFIG_BPF_LSM=y` and `CONFIG_DEBUG_INFO_BTF=y`. +/// In order for the probes to fire, you also need the BPF LSM to be enabled through your +/// kernel's boot paramters (like `lsm=lockdown,yama,bpf`). +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.7. +/// +/// # Examples +/// +/// ```no_run +/// # #[derive(thiserror::Error, Debug)] +/// # enum LsmError { +/// # #[error(transparent)] +/// # LsmLoad(#[from] aya::programs::LsmLoadError), +/// # #[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::Lsm}; +/// use std::convert::TryInto; +/// +/// let program: &mut Lsm = bpf.program_mut("lsm_prog")?.try_into()?; +/// program.load("security_bprm_exec")?; +/// program.attach()?; +/// # Ok::<(), LsmError>(()) +/// ``` +/// +/// [1]: https://elixir.bootlin.com/linux/latest/source/include/linux/lsm_hook_defs.h +#[derive(Debug)] +#[doc(alias = "BPF_PROG_TYPE_LSM")] +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. + /// + /// See also [`Program::load`](crate::programs::Program::load). + /// + /// # Arguments + /// + /// * `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) -> Result<(), LsmLoadError> { + let btf = &Btf::from_sys_fs()?; + 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) + } + + /// Returns the name of the program. + pub fn name(&self) -> String { + self.data.name.to_string() + } + + /// Attaches the program. + pub fn attach(&mut self) -> Result { + attach_btf_id(&mut self.data) + } +} + +/// 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 8d39c4e8..fecf0df2 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -39,9 +39,11 @@ mod cgroup_skb; mod kprobe; mod lirc_mode2; +mod lsm; mod perf_attach; pub mod perf_event; mod probe; +mod raw_trace_point; mod sk_msg; mod sk_skb; mod sock_ops; @@ -67,9 +69,11 @@ use thiserror::Error; pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType}; pub use kprobe::{KProbe, KProbeError}; pub use lirc_mode2::LircMode2; +pub use lsm::{Lsm, LsmLoadError}; use perf_attach::*; pub use perf_event::{PerfEvent, PerfEventScope, PerfTypeId, SamplePolicy}; pub use probe::ProbeKind; +pub use raw_trace_point::RawTracePoint; pub use sk_msg::SkMsg; pub use sk_skb::{SkSkb, SkSkbKind}; pub use sock_ops::SockOps; @@ -83,7 +87,7 @@ use crate::{ generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type}, maps::MapError, obj::{self, Function}, - sys::{bpf_load_program, bpf_pin_object, bpf_prog_detach, bpf_prog_query}, + sys::{bpf_load_program, bpf_pin_object, bpf_prog_detach, bpf_prog_query, BpfLoadProgramAttrs}, }; /// Error type returned when working with programs. @@ -188,6 +192,8 @@ pub enum Program { CgroupSkb(CgroupSkb), LircMode2(LircMode2), PerfEvent(PerfEvent), + RawTracePoint(RawTracePoint), + Lsm(Lsm), } impl Program { @@ -221,6 +227,8 @@ impl Program { Program::CgroupSkb(_) => BPF_PROG_TYPE_CGROUP_SKB, Program::LircMode2(_) => BPF_PROG_TYPE_LIRC_MODE2, Program::PerfEvent(_) => BPF_PROG_TYPE_PERF_EVENT, + Program::RawTracePoint(_) => BPF_PROG_TYPE_RAW_TRACEPOINT, + Program::Lsm(_) => BPF_PROG_TYPE_LSM, } } @@ -248,6 +256,8 @@ impl Program { Program::CgroupSkb(p) => &p.data, Program::LircMode2(p) => &p.data, Program::PerfEvent(p) => &p.data, + Program::RawTracePoint(p) => &p.data, + Program::Lsm(p) => &p.data, } } @@ -265,6 +275,8 @@ impl Program { Program::CgroupSkb(p) => &mut p.data, Program::LircMode2(p) => &mut p.data, Program::PerfEvent(p) => &mut p.data, + Program::RawTracePoint(p) => &mut p.data, + Program::Lsm(p) => &mut p.data, } } } @@ -275,6 +287,9 @@ pub(crate) struct ProgramData { pub(crate) obj: obj::Program, pub(crate) fd: Option, pub(crate) links: Vec>>, + pub(crate) expected_attach_type: Option, + pub(crate) attach_btf_obj_fd: Option, + pub(crate) attach_btf_id: Option, } impl ProgramData { @@ -376,13 +391,17 @@ fn load_program(prog_type: bpf_prog_type, data: &mut ProgramData) -> Result<(), let mut retries = 0; let mut ret; loop { - ret = bpf_load_program( - prog_type, - instructions, + let attr = BpfLoadProgramAttrs { + ty: prog_type, + insns: instructions, license, - (*kernel_version).into(), - &mut log_buf, - ); + kernel_version: (*kernel_version).into(), + expected_attach_type: data.expected_attach_type, + attach_btf_obj_fd: data.attach_btf_obj_fd, + attach_btf_id: data.attach_btf_id, + log: &mut log_buf, + }; + ret = bpf_load_program(attr); match &ret { Ok(prog_fd) => { *fd = Some(*prog_fd as RawFd); @@ -568,7 +587,9 @@ impl_program_fd!( SchedClassifier, CgroupSkb, LircMode2, - PerfEvent + PerfEvent, + Lsm, + RawTracePoint, ); macro_rules! impl_try_from_program { @@ -611,7 +632,9 @@ impl_try_from_program!( SchedClassifier, CgroupSkb, LircMode2, - PerfEvent + PerfEvent, + Lsm, + RawTracePoint, ); /// 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 new file mode 100644 index 00000000..2ca95826 --- /dev/null +++ b/aya/src/programs/raw_trace_point.rs @@ -0,0 +1,67 @@ +//! Raw tracepoints. +use std::{ffi::CString, os::unix::io::RawFd}; + +use crate::{ + generated::bpf_prog_type::BPF_PROG_TYPE_RAW_TRACEPOINT, + programs::{load_program, FdLink, LinkRef, ProgramData, ProgramError}, + sys::bpf_raw_tracepoint_open, +}; + +/// A program that can be attached at a pre-defined kernel trace point, but also +/// has an access to kernel internal arguments of trace points, which +/// differentiates them from traditional tracepoint eBPF programs. +/// +/// The kernel provides a set of pre-defined trace points that eBPF programs can +/// be attached to. See`/sys/kernel/debug/tracing/events` for a list of which +/// events can be traced. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.17. +/// +/// # Examples +/// +/// ```no_run +/// # let mut bpf = Bpf::load_file("ebpf_programs.o")?; +/// use aya::{Bpf, programs::RawTracePoint}; +/// use std::convert::TryInto; +/// +/// let program: &mut RawTracePoint = bpf.program_mut("sys_enter")?.try_into()?; +/// program.load()?; +/// program.attach("sys_enter")?; +/// # Ok::<(), aya::BpfError>(()) +/// ``` +#[derive(Debug)] +#[doc(alias = "BPF_PROG_TYPE_RAW_TRACEPOINT")] +pub struct RawTracePoint { + pub(crate) data: ProgramData, +} + +impl RawTracePoint { + /// Loads the program inside the kernel. + /// + /// See also [`Program::load`](crate::programs::Program::load). + pub fn load(&mut self) -> Result<(), ProgramError> { + load_program(BPF_PROG_TYPE_RAW_TRACEPOINT, &mut self.data) + } + + /// Returns the name of the program. + pub fn name(&self) -> String { + self.data.name.to_string() + } + + /// 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) })) + } +} diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index a9902015..80728218 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -53,28 +53,41 @@ pub(crate) fn bpf_get_object(path: &CStr) -> SysResult { sys_bpf(bpf_cmd::BPF_OBJ_GET, &attr) } -pub(crate) fn bpf_load_program( - ty: bpf_prog_type, - insns: &[bpf_insn], - license: &CStr, - kernel_version: u32, - log: &mut VerifierLog, -) -> SysResult { +pub(crate) struct BpfLoadProgramAttrs<'a> { + pub(crate) ty: bpf_prog_type, + pub(crate) insns: &'a [bpf_insn], + pub(crate) license: &'a CStr, + pub(crate) kernel_version: u32, + pub(crate) expected_attach_type: Option, + pub(crate) attach_btf_obj_fd: Option, + pub(crate) attach_btf_id: Option, + pub(crate) log: &'a mut VerifierLog, +} + +pub(crate) fn bpf_load_program(aya_attr: BpfLoadProgramAttrs) -> SysResult { let mut attr = unsafe { mem::zeroed::() }; let u = unsafe { &mut attr.__bindgen_anon_3 }; - u.prog_type = ty as u32; - u.expected_attach_type = 0; - u.insns = insns.as_ptr() as u64; - u.insn_cnt = insns.len() as u32; - u.license = license.as_ptr() as u64; - u.kern_version = kernel_version; - let log_buf = log.buf(); + u.prog_type = aya_attr.ty as u32; + if let Some(v) = aya_attr.expected_attach_type { + u.expected_attach_type = v as u32; + } + u.insns = aya_attr.insns.as_ptr() as u64; + u.insn_cnt = aya_attr.insns.len() as u32; + u.license = aya_attr.license.as_ptr() as u64; + u.kern_version = aya_attr.kernel_version; + let log_buf = aya_attr.log.buf(); if log_buf.capacity() > 0 { u.log_level = 7; u.log_buf = log_buf.as_mut_ptr() as u64; u.log_size = log_buf.capacity() as u32; } + if let Some(v) = aya_attr.attach_btf_obj_fd { + u.__bindgen_anon_1.attach_btf_obj_fd = v; + } + if let Some(v) = aya_attr.attach_btf_id { + u.attach_btf_id = v; + } sys_bpf(bpf_cmd::BPF_PROG_LOAD, &attr) } @@ -332,6 +345,18 @@ pub(crate) fn bpf_obj_get_info_by_fd(prog_fd: RawFd) -> Result, prog_fd: RawFd) -> SysResult { + let mut attr = unsafe { mem::zeroed::() }; + + attr.raw_tracepoint.name = match name { + Some(n) => n.as_ptr() as u64, + None => 0, + }; + attr.raw_tracepoint.prog_fd = prog_fd as u32; + + sys_bpf(bpf_cmd::BPF_RAW_TRACEPOINT_OPEN, &attr) +} + fn sys_bpf(cmd: bpf_cmd, attr: &bpf_attr) -> SysResult { syscall(Syscall::Bpf { cmd, attr }) } diff --git a/bpf/aya-bpf-macros/src/expand.rs b/bpf/aya-bpf-macros/src/expand.rs index 279e9458..45c36b74 100644 --- a/bpf/aya-bpf-macros/src/expand.rs +++ b/bpf/aya-bpf-macros/src/expand.rs @@ -352,3 +352,63 @@ impl PerfEvent { }) } } + +pub struct RawTracePoint { + item: ItemFn, + name: String, +} + +impl RawTracePoint { + 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(RawTracePoint { item, name }) + } + + pub fn expand(&self) -> Result { + let section_name = format!("raw_tp/{}", 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) -> u32 { + let _ = #fn_name(::aya_bpf::programs::RawTracePointContext::new(ctx)); + return 0; + + #item + } + }) + } +} + +pub struct Lsm { + item: ItemFn, + name: String, +} + +impl Lsm { + 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(Lsm { item, name }) + } + + pub fn expand(&self) -> Result { + let section_name = format!("lsm/{}", self.name); + let fn_name = &self.item.sig.ident; + let item = &self.item; + // LSM probes need to return an integer corresponding to the correct + // policy decision. Therefore we do not simply default to a return value + // of 0 as in other program types. + Ok(quote! { + #[no_mangle] + #[link_section = #section_name] + fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 { + return #fn_name(::aya_bpf::programs::LsmContext::new(ctx)); + + #item + } + }) + } +} diff --git a/bpf/aya-bpf-macros/src/lib.rs b/bpf/aya-bpf-macros/src/lib.rs index 1ccfe362..d11ed583 100644 --- a/bpf/aya-bpf-macros/src/lib.rs +++ b/bpf/aya-bpf-macros/src/lib.rs @@ -1,7 +1,8 @@ mod expand; use expand::{ - Args, Map, PerfEvent, Probe, ProbeKind, SchedClassifier, SkMsg, SockOps, TracePoint, Xdp, + Args, Lsm, Map, PerfEvent, Probe, ProbeKind, RawTracePoint, SchedClassifier, SkMsg, SockOps, + TracePoint, Xdp, }; use proc_macro::TokenStream; use syn::{parse_macro_input, ItemFn, ItemStatic}; @@ -125,3 +126,84 @@ pub fn perf_event(attrs: TokenStream, item: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } + +/// Marks a function as a raw tracepoint eBPF 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 +/// be attached to. See `/sys/kernel/debug/tracing/events` for a list of which +/// events can be traced. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.7. +/// +/// # Examples +/// +/// ```no_run +/// use aya_bpf::{macros::raw_tracepoint, programs::RawTracePointContext}; +/// +/// #[raw_tracepoint(name = "sys_enter")] +/// pub fn sys_enter(ctx: RawTracePointContext) -> i32 { +/// match unsafe { try_sys_enter(ctx) } { +/// Ok(ret) => ret, +/// Err(ret) => ret, +/// } +/// } +/// +/// unsafe fn try_sys_enter(_ctx: RawTracePointContext) -> Result { +/// Ok(0) +/// } +/// ``` +#[proc_macro_attribute] +pub fn raw_tracepoint(attrs: TokenStream, item: TokenStream) -> TokenStream { + let args = parse_macro_input!(attrs as Args); + let item = parse_macro_input!(item as ItemFn); + + RawTracePoint::from_syn(args, item) + .and_then(|u| u.expand()) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} + +/// Marks a function as an LSM program that can be attached to Linux LSM hooks. +/// Used to implement security policy and audit logging. +/// +/// LSM probes can be attached to the kernel's [security hooks][1] to implement mandatory +/// access control policy and security auditing. +/// +/// LSM probes require a kernel compiled with `CONFIG_BPF_LSM=y` and `CONFIG_DEBUG_INFO_BTF=y`. +/// In order for the probes to fire, you also need the BPF LSM to be enabled through your +/// kernel's boot paramters (like `lsm=lockdown,yama,bpf`). +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.7. +/// +/// # Examples +/// +/// ```no_run +/// use aya_bpf::{macros::lsm, programs::LsmContext}; +/// +/// #[lsm(name = "file_open")] +/// pub fn file_open(ctx: LsmContext) -> i32 { +/// match unsafe { try_file_open(ctx) } { +/// Ok(ret) => ret, +/// Err(ret) => ret, +/// } +/// } +/// +/// unsafe fn try_file_open(_ctx: LsmContext) -> Result { +/// Ok(0) +/// } +/// ``` +#[proc_macro_attribute] +pub fn lsm(attrs: TokenStream, item: TokenStream) -> TokenStream { + let args = parse_macro_input!(attrs as Args); + let item = parse_macro_input!(item as ItemFn); + + Lsm::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/lsm.rs b/bpf/aya-bpf/src/programs/lsm.rs new file mode 100644 index 00000000..4a36cd51 --- /dev/null +++ b/bpf/aya-bpf/src/programs/lsm.rs @@ -0,0 +1,19 @@ +use core::ffi::c_void; + +use crate::BpfContext; + +pub struct LsmContext { + ctx: *mut c_void, +} + +impl LsmContext { + pub fn new(ctx: *mut c_void) -> LsmContext { + LsmContext { ctx } + } +} + +impl BpfContext for LsmContext { + 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 2e55a4b1..16d53d05 100644 --- a/bpf/aya-bpf/src/programs/mod.rs +++ b/bpf/aya-bpf/src/programs/mod.rs @@ -1,13 +1,17 @@ +pub mod lsm; pub mod perf_event; pub mod probe; +pub mod raw_tracepoint; pub mod sk_msg; pub mod sk_skb; pub mod sock_ops; pub mod tracepoint; pub mod xdp; +pub use lsm::LsmContext; pub use perf_event::PerfEventContext; pub use probe::ProbeContext; +pub use raw_tracepoint::RawTracePointContext; pub use sk_msg::SkMsgContext; pub use sk_skb::SkSkbContext; pub use sock_ops::SockOpsContext; diff --git a/bpf/aya-bpf/src/programs/raw_tracepoint.rs b/bpf/aya-bpf/src/programs/raw_tracepoint.rs new file mode 100644 index 00000000..cc140244 --- /dev/null +++ b/bpf/aya-bpf/src/programs/raw_tracepoint.rs @@ -0,0 +1,19 @@ +use core::ffi::c_void; + +use crate::BpfContext; + +pub struct RawTracePointContext { + ctx: *mut c_void, +} + +impl RawTracePointContext { + pub fn new(ctx: *mut c_void) -> RawTracePointContext { + RawTracePointContext { ctx } + } +} + +impl BpfContext for RawTracePointContext { + fn as_ptr(&self) -> *mut c_void { + self.ctx + } +}