ソースを参照

Merge pull request #68 from vadorovsky/lsm

Add support for raw tracepoint and LSM programs
Alessandro Decina 3 年 前
コミット
140005d9e3

+ 10 - 3
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)

+ 31 - 1
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<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> {
         let mut type_id = root_type_id;
         let mut n_elems = 1;

+ 55 - 0
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 { .. },
+                ..
+            })
+        );
+    }
 }

+ 109 - 0
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<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) }))
+}

+ 32 - 9
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<RawFd>,
     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 {
@@ -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

+ 67 - 0
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<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) }))
+    }
+}

+ 39 - 14
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<bpf_attach_type>,
+    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 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<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 {
     syscall(Syscall::Bpf { cmd, attr })
 }

+ 60 - 0
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<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
+            }
+        })
+    }
+}

+ 83 - 1
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<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()
+}

+ 19 - 0
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
+    }
+}

+ 4 - 0
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;

+ 19 - 0
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
+    }
+}