Add support for raw tracepoint and LSM programs

This change adds support for the following program types:

* raw tracepoint
* LSM

Supporting LSM programs involved a necessity of supporting more
load_attrs for the BPF_PROG_LOAD operation, concretely:

* expected_attach_type - for LSM programs, it has always to be set to
  BPF_LSM_MAC
* attach_btf_obj_fd - it's often used to reference the file descriptor of
  program's BTF info, altough in case of LSM programs, it only has to
  contain the value 0, which means the vmlinux object file (usually
  /sys/kernel/btf/vmlinux)
* attach_btf_id - ID of the BTF object, which in case of LSM programs is
  the ID of the function (the LSM hook)

The example of LSM program using that functionality can be found here:

https://github.com/vadorovsky/aya-example-lsm

Fixes: #9
Signed-off-by: William Findlay <william@williamfindlay.com>
Signed-off-by: Michal Rostecki <mrostecki@opensuse.org>
pull/68/head
William Findlay 3 years ago committed by Michal Rostecki
parent a94774755f
commit 169478c863

@ -20,9 +20,9 @@ use crate::{
Object, ParseError, ProgramSection, Object, ParseError, ProgramSection,
}, },
programs::{ programs::{
CgroupSkb, CgroupSkbAttachType, KProbe, LircMode2, PerfEvent, ProbeKind, Program, CgroupSkb, CgroupSkbAttachType, KProbe, LircMode2, Lsm, PerfEvent, ProbeKind, Program,
ProgramData, ProgramError, SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkMsg, SkSkb, SkSkbKind,
TracePoint, UProbe, Xdp, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
}, },
sys::bpf_map_update_elem_ptr, sys::bpf_map_update_elem_ptr,
util::{possible_cpus, POSSIBLE_CPUS}, util::{possible_cpus, POSSIBLE_CPUS},
@ -253,6 +253,9 @@ impl<'a> BpfLoader<'a> {
name: name.clone(), name: name.clone(),
fd: None, fd: None,
links: Vec::new(), links: Vec::new(),
expected_attach_type: None,
attach_btf_obj_fd: None,
attach_btf_id: None,
}; };
let program = match section { let program = match section {
ProgramSection::KProbe { .. } => Program::KProbe(KProbe { ProgramSection::KProbe { .. } => Program::KProbe(KProbe {
@ -299,6 +302,10 @@ impl<'a> BpfLoader<'a> {
}), }),
ProgramSection::LircMode2 { .. } => Program::LircMode2(LircMode2 { data }), ProgramSection::LircMode2 { .. } => Program::LircMode2(LircMode2 { data }),
ProgramSection::PerfEvent { .. } => Program::PerfEvent(PerfEvent { data }), ProgramSection::PerfEvent { .. } => Program::PerfEvent(PerfEvent { data }),
ProgramSection::RawTracePoint { .. } => {
Program::RawTracePoint(RawTracePoint { data })
}
ProgramSection::Lsm { .. } => Program::Lsm(Lsm { data }),
}; };
(name, program) (name, program)

@ -12,7 +12,7 @@ use thiserror::Error;
use crate::{ use crate::{
generated::{btf_ext_header, btf_header}, 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; pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32;
@ -66,6 +66,9 @@ pub enum BtfError {
#[error("Unexpected BTF type id `{type_id}`")] #[error("Unexpected BTF type id `{type_id}`")]
UnexpectedBtfType { type_id: u32 }, UnexpectedBtfType { type_id: u32 },
#[error("Unknown BTF type `{type_name}`")]
UnknownBtfTypeName { type_name: String },
#[error("maximum depth reached resolving BTF type")] #[error("maximum depth reached resolving BTF type")]
MaximumTypeDepthReached { type_id: u32 }, MaximumTypeDepthReached { type_id: u32 },
} }
@ -221,6 +224,33 @@ impl Btf {
.and_then(|off| self.string_at(off).ok().map(String::from)) .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<u32, BtfError> {
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<usize, BtfError> { pub(crate) fn type_size(&self, root_type_id: u32) -> Result<usize, BtfError> {
let mut type_id = root_type_id; let mut type_id = root_type_id;
let mut n_elems = 1; let mut n_elems = 1;

@ -85,6 +85,8 @@ pub enum ProgramSection {
CgroupSkbEgress { name: String }, CgroupSkbEgress { name: String },
LircMode2 { name: String }, LircMode2 { name: String },
PerfEvent { name: String }, PerfEvent { name: String },
RawTracePoint { name: String },
Lsm { name: String },
} }
impl ProgramSection { impl ProgramSection {
@ -106,6 +108,8 @@ impl ProgramSection {
ProgramSection::CgroupSkbEgress { name } => name, ProgramSection::CgroupSkbEgress { name } => name,
ProgramSection::LircMode2 { name } => name, ProgramSection::LircMode2 { name } => name,
ProgramSection::PerfEvent { 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 }, "cgroup_skb/egress" => CgroupSkbEgress { name },
"lirc_mode2" => LircMode2 { name }, "lirc_mode2" => LircMode2 { name },
"perf_event" => PerfEvent { name }, "perf_event" => PerfEvent { name },
"raw_tp" | "raw_tracepoint" => RawTracePoint { name },
"lsm" => Lsm { name },
_ => { _ => {
return Err(ParseError::InvalidProgramSection { return Err(ParseError::InvalidProgramSection {
section: section.to_owned(), section: section.to_owned(),
@ -570,6 +576,9 @@ fn is_program_section(name: &str) -> bool {
"uprobe", "uprobe",
"uretprobe", "uretprobe",
"xdp", "xdp",
"raw_tp",
"raw_tracepoint",
"lsm",
] { ] {
if name.starts_with(prefix) { if name.starts_with(prefix) {
return true; 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 { .. },
..
})
);
}
} }

@ -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<LinkRef, ProgramError> {
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<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) }))
}

@ -39,9 +39,11 @@
mod cgroup_skb; mod cgroup_skb;
mod kprobe; mod kprobe;
mod lirc_mode2; mod lirc_mode2;
mod lsm;
mod perf_attach; mod perf_attach;
pub mod perf_event; pub mod perf_event;
mod probe; mod probe;
mod raw_trace_point;
mod sk_msg; mod sk_msg;
mod sk_skb; mod sk_skb;
mod sock_ops; mod sock_ops;
@ -67,9 +69,11 @@ use thiserror::Error;
pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType}; pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType};
pub use kprobe::{KProbe, KProbeError}; pub use kprobe::{KProbe, KProbeError};
pub use lirc_mode2::LircMode2; pub use lirc_mode2::LircMode2;
pub use lsm::{Lsm, LsmLoadError};
use perf_attach::*; use perf_attach::*;
pub use perf_event::{PerfEvent, PerfEventScope, PerfTypeId, SamplePolicy}; pub use perf_event::{PerfEvent, PerfEventScope, PerfTypeId, SamplePolicy};
pub use probe::ProbeKind; pub use probe::ProbeKind;
pub use raw_trace_point::RawTracePoint;
pub use sk_msg::SkMsg; pub use sk_msg::SkMsg;
pub use sk_skb::{SkSkb, SkSkbKind}; pub use sk_skb::{SkSkb, SkSkbKind};
pub use sock_ops::SockOps; pub use sock_ops::SockOps;
@ -83,7 +87,7 @@ use crate::{
generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type}, generated::{bpf_attach_type, bpf_prog_info, bpf_prog_type},
maps::MapError, maps::MapError,
obj::{self, Function}, 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. /// Error type returned when working with programs.
@ -188,6 +192,8 @@ pub enum Program {
CgroupSkb(CgroupSkb), CgroupSkb(CgroupSkb),
LircMode2(LircMode2), LircMode2(LircMode2),
PerfEvent(PerfEvent), PerfEvent(PerfEvent),
RawTracePoint(RawTracePoint),
Lsm(Lsm),
} }
impl Program { impl Program {
@ -221,6 +227,8 @@ impl Program {
Program::CgroupSkb(_) => BPF_PROG_TYPE_CGROUP_SKB, Program::CgroupSkb(_) => BPF_PROG_TYPE_CGROUP_SKB,
Program::LircMode2(_) => BPF_PROG_TYPE_LIRC_MODE2, Program::LircMode2(_) => BPF_PROG_TYPE_LIRC_MODE2,
Program::PerfEvent(_) => BPF_PROG_TYPE_PERF_EVENT, 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::CgroupSkb(p) => &p.data,
Program::LircMode2(p) => &p.data, Program::LircMode2(p) => &p.data,
Program::PerfEvent(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::CgroupSkb(p) => &mut p.data,
Program::LircMode2(p) => &mut p.data, Program::LircMode2(p) => &mut p.data,
Program::PerfEvent(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) obj: obj::Program,
pub(crate) fd: Option<RawFd>, pub(crate) fd: Option<RawFd>,
pub(crate) links: Vec<Rc<RefCell<dyn Link>>>, pub(crate) links: Vec<Rc<RefCell<dyn Link>>>,
pub(crate) expected_attach_type: Option<bpf_attach_type>,
pub(crate) attach_btf_obj_fd: Option<u32>,
pub(crate) attach_btf_id: Option<u32>,
} }
impl ProgramData { impl ProgramData {
@ -376,13 +391,17 @@ fn load_program(prog_type: bpf_prog_type, data: &mut ProgramData) -> Result<(),
let mut retries = 0; let mut retries = 0;
let mut ret; let mut ret;
loop { loop {
ret = bpf_load_program( let attr = BpfLoadProgramAttrs {
prog_type, ty: prog_type,
instructions, insns: instructions,
license, license,
(*kernel_version).into(), kernel_version: (*kernel_version).into(),
&mut log_buf, 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 { match &ret {
Ok(prog_fd) => { Ok(prog_fd) => {
*fd = Some(*prog_fd as RawFd); *fd = Some(*prog_fd as RawFd);
@ -568,7 +587,9 @@ impl_program_fd!(
SchedClassifier, SchedClassifier,
CgroupSkb, CgroupSkb,
LircMode2, LircMode2,
PerfEvent PerfEvent,
Lsm,
RawTracePoint,
); );
macro_rules! impl_try_from_program { macro_rules! impl_try_from_program {
@ -611,7 +632,9 @@ impl_try_from_program!(
SchedClassifier, SchedClassifier,
CgroupSkb, CgroupSkb,
LircMode2, LircMode2,
PerfEvent PerfEvent,
Lsm,
RawTracePoint,
); );
/// Provides information about a loaded program, like name, id and statistics /// Provides information about a loaded program, like name, id and statistics

@ -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<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) }))
}
}

@ -53,28 +53,41 @@ pub(crate) fn bpf_get_object(path: &CStr) -> SysResult {
sys_bpf(bpf_cmd::BPF_OBJ_GET, &attr) sys_bpf(bpf_cmd::BPF_OBJ_GET, &attr)
} }
pub(crate) fn bpf_load_program( pub(crate) struct BpfLoadProgramAttrs<'a> {
ty: bpf_prog_type, pub(crate) ty: bpf_prog_type,
insns: &[bpf_insn], pub(crate) insns: &'a [bpf_insn],
license: &CStr, pub(crate) license: &'a CStr,
kernel_version: u32, pub(crate) kernel_version: u32,
log: &mut VerifierLog, pub(crate) expected_attach_type: Option<bpf_attach_type>,
) -> SysResult { pub(crate) attach_btf_obj_fd: Option<u32>,
pub(crate) attach_btf_id: Option<u32>,
pub(crate) log: &'a mut VerifierLog,
}
pub(crate) fn bpf_load_program(aya_attr: BpfLoadProgramAttrs) -> SysResult {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() }; let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
let u = unsafe { &mut attr.__bindgen_anon_3 }; let u = unsafe { &mut attr.__bindgen_anon_3 };
u.prog_type = ty as u32; u.prog_type = aya_attr.ty as u32;
u.expected_attach_type = 0; if let Some(v) = aya_attr.expected_attach_type {
u.insns = insns.as_ptr() as u64; u.expected_attach_type = v as u32;
u.insn_cnt = insns.len() as u32; }
u.license = license.as_ptr() as u64; u.insns = aya_attr.insns.as_ptr() as u64;
u.kern_version = kernel_version; u.insn_cnt = aya_attr.insns.len() as u32;
let log_buf = log.buf(); 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 { if log_buf.capacity() > 0 {
u.log_level = 7; u.log_level = 7;
u.log_buf = log_buf.as_mut_ptr() as u64; u.log_buf = log_buf.as_mut_ptr() as u64;
u.log_size = log_buf.capacity() as u32; 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) 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<bpf_prog_info, io
} }
} }
pub(crate) fn bpf_raw_tracepoint_open(name: Option<&CStr>, prog_fd: RawFd) -> SysResult {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
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 { fn sys_bpf(cmd: bpf_cmd, attr: &bpf_attr) -> SysResult {
syscall(Syscall::Bpf { cmd, attr }) syscall(Syscall::Bpf { cmd, attr })
} }

@ -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<RawTracePoint> {
let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
Ok(RawTracePoint { item, name })
}
pub fn expand(&self) -> Result<TokenStream> {
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<Lsm> {
let name = name_arg(&mut args)?.unwrap_or_else(|| item.sig.ident.to_string());
Ok(Lsm { item, name })
}
pub fn expand(&self) -> Result<TokenStream> {
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
}
})
}
}

@ -1,7 +1,8 @@
mod expand; mod expand;
use 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 proc_macro::TokenStream;
use syn::{parse_macro_input, ItemFn, ItemStatic}; 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()) .unwrap_or_else(|err| err.to_compile_error())
.into() .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<i32, i32> {
/// 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<i32, i32> {
/// 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()
}

@ -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
}
}

@ -1,13 +1,17 @@
pub mod lsm;
pub mod perf_event; pub mod perf_event;
pub mod probe; pub mod probe;
pub mod raw_tracepoint;
pub mod sk_msg; pub mod sk_msg;
pub mod sk_skb; pub mod sk_skb;
pub mod sock_ops; pub mod sock_ops;
pub mod tracepoint; pub mod tracepoint;
pub mod xdp; pub mod xdp;
pub use lsm::LsmContext;
pub use perf_event::PerfEventContext; pub use perf_event::PerfEventContext;
pub use probe::ProbeContext; pub use probe::ProbeContext;
pub use raw_tracepoint::RawTracePointContext;
pub use sk_msg::SkMsgContext; pub use sk_msg::SkMsgContext;
pub use sk_skb::SkSkbContext; pub use sk_skb::SkSkbContext;
pub use sock_ops::SockOpsContext; pub use sock_ops::SockOpsContext;

@ -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
}
}
Loading…
Cancel
Save