4
0
Эх сурвалжийг харах

lsm: cgroup attachment type support

Altug Bozkurt 9 сар өмнө
parent
commit
fc5387c806

+ 46 - 1
aya-ebpf-macros/src/lib.rs

@@ -14,6 +14,7 @@ mod fexit;
 mod flow_dissector;
 mod kprobe;
 mod lsm;
+mod lsm_cgroup;
 mod map;
 mod perf_event;
 mod raw_tracepoint;
@@ -40,6 +41,7 @@ use fexit::FExit;
 use flow_dissector::FlowDissector;
 use kprobe::{KProbe, KProbeKind};
 use lsm::Lsm;
+use lsm_cgroup::LsmCgroup;
 use map::Map;
 use perf_event::PerfEvent;
 use proc_macro::TokenStream;
@@ -309,7 +311,7 @@ pub fn raw_tracepoint(attrs: TokenStream, item: TokenStream) -> TokenStream {
 ///
 /// 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`).
+/// kernel's boot parameters (like `lsm=lockdown,yama,bpf`).
 ///
 /// # Minimum kernel version
 ///
@@ -341,6 +343,49 @@ pub fn lsm(attrs: TokenStream, item: TokenStream) -> TokenStream {
     .into()
 }
 
+/// Marks a function as an LSM program that can be attached to cgroups.
+/// This program will only trigger for workloads in the attached cgroups.
+/// Used to implement security policy and audit logging.
+///
+/// The hook name is the first and only argument to the macro.
+///
+/// LSM probes can be attached to the kernel's security hooks 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 parameters (like `lsm=lockdown,yama,bpf`).
+///
+/// # Minimum kernel version
+///
+/// The minimum kernel version required to use this feature is 6.0.
+///
+/// # Examples
+///
+/// ```no_run
+/// use aya_ebpf::{macros::lsm_cgroup, programs::LsmContext};
+///
+/// #[lsm_cgroup(hook = "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> {
+///     Err(0)
+/// }
+/// ```
+#[proc_macro_attribute]
+pub fn lsm_cgroup(attrs: TokenStream, item: TokenStream) -> TokenStream {
+    match LsmCgroup::parse(attrs.into(), item.into()) {
+        Ok(prog) => prog.expand(),
+        Err(err) => err.into_compile_error(),
+    }
+    .into()
+}
+
 /// Marks a function as a [BTF-enabled raw tracepoint][1] eBPF program that can be attached at
 /// a pre-defined kernel trace point.
 ///

+ 1 - 1
aya-ebpf-macros/src/lsm.rs

@@ -44,10 +44,10 @@ impl Lsm {
         } else {
             section_prefix.into()
         };
+        let fn_name = &sig.ident;
         // 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.
-        let fn_name = &sig.ident;
         quote! {
             #[unsafe(no_mangle)]
             #[unsafe(link_section = #section_name)]

+ 87 - 0
aya-ebpf-macros/src/lsm_cgroup.rs

@@ -0,0 +1,87 @@
+use std::borrow::Cow;
+
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{ItemFn, Result};
+
+use crate::args::{err_on_unknown_args, pop_string_arg};
+
+pub(crate) struct LsmCgroup {
+    item: ItemFn,
+    hook: Option<String>,
+}
+
+impl LsmCgroup {
+    pub(crate) fn parse(attrs: TokenStream, item: TokenStream) -> Result<Self> {
+        let item = syn::parse2(item)?;
+        let mut args = syn::parse2(attrs)?;
+        let hook = pop_string_arg(&mut args, "hook");
+        err_on_unknown_args(&args)?;
+
+        Ok(Self { item, hook })
+    }
+
+    pub(crate) fn expand(&self) -> TokenStream {
+        let Self { item, hook } = self;
+        let ItemFn {
+            attrs: _,
+            vis,
+            sig,
+            block: _,
+        } = item;
+        let section_prefix = "lsm_cgroup";
+        let section_name: Cow<'_, _> = if let Some(name) = hook {
+            format!("{section_prefix}/{name}").into()
+        } else {
+            section_prefix.into()
+        };
+        let fn_name = &sig.ident;
+        // 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.
+        quote! {
+            #[unsafe(no_mangle)]
+            #[unsafe(link_section = #section_name)]
+            #vis fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 {
+                return #fn_name(::aya_ebpf::programs::LsmContext::new(ctx));
+
+                #item
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use syn::parse_quote;
+
+    use super::*;
+
+    #[test]
+    fn test_lsm_cgroup() {
+        let prog = LsmCgroup::parse(
+            parse_quote! {
+                hook = "bprm_committed_creds",
+            },
+            parse_quote! {
+                fn bprm_committed_creds(ctx: &mut ::aya_ebpf::programs::LsmContext) -> i32 {
+                    0
+                }
+            },
+        )
+        .unwrap();
+        let expanded = prog.expand();
+        let expected = quote! {
+            #[unsafe(no_mangle)]
+            #[unsafe(link_section = "lsm_cgroup/bprm_committed_creds")]
+            fn bprm_committed_creds(ctx: *mut ::core::ffi::c_void) -> i32 {
+                return bprm_committed_creds(::aya_ebpf::programs::LsmContext::new(ctx));
+
+                fn bprm_committed_creds(ctx: &mut ::aya_ebpf::programs::LsmContext) -> i32 {
+                    0
+                }
+            }
+        };
+        assert_eq!(expected.to_string(), expanded.to_string());
+    }
+}

+ 26 - 4
aya-obj/src/obj.rs

@@ -258,6 +258,7 @@ pub enum ProgramSection {
     Lsm {
         sleepable: bool,
     },
+    LsmCgroup,
     BtfTracePoint,
     FEntry {
         sleepable: bool,
@@ -418,6 +419,7 @@ impl FromStr for ProgramSection {
             "raw_tp" | "raw_tracepoint" => Self::RawTracePoint,
             "lsm" => Self::Lsm { sleepable: false },
             "lsm.s" => Self::Lsm { sleepable: true },
+            "lsm_cgroup" => Self::LsmCgroup,
             "fentry" => Self::FEntry { sleepable: false },
             "fentry.s" => Self::FEntry { sleepable: true },
             "fexit" => Self::FExit { sleepable: false },
@@ -2208,10 +2210,7 @@ mod tests {
         assert_matches!(
             obj.programs.get("foo"),
             Some(Program {
-                section: ProgramSection::Lsm {
-                    sleepable: false,
-                    ..
-                },
+                section: ProgramSection::Lsm { sleepable: false },
                 ..
             })
         );
@@ -2243,6 +2242,29 @@ mod tests {
         );
     }
 
+    #[test]
+    fn test_parse_section_lsm_cgroup() {
+        let mut obj = fake_obj();
+        fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN);
+
+        assert_matches!(
+            obj.parse_section(fake_section(
+                EbpfSectionKind::Program,
+                "lsm_cgroup/foo",
+                bytes_of(&fake_ins()),
+                None
+            )),
+            Ok(())
+        );
+        assert_matches!(
+            obj.programs.get("foo"),
+            Some(Program {
+                section: ProgramSection::LsmCgroup,
+                ..
+            })
+        );
+    }
+
     #[test]
     fn test_parse_section_btf_tracepoint() {
         let mut obj = fake_obj();

+ 7 - 3
aya/src/bpf.rs

@@ -24,9 +24,9 @@ use crate::{
     programs::{
         BtfTracePoint, CgroupDevice, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr,
         CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, FlowDissector, Iter, KProbe,
-        LircMode2, Lsm, PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint,
-        SchedClassifier, SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint,
-        UProbe, Xdp,
+        LircMode2, Lsm, LsmCgroup, PerfEvent, ProbeKind, Program, ProgramData, ProgramError,
+        RawTracePoint, SchedClassifier, SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter,
+        TracePoint, UProbe, Xdp,
     },
     sys::{
         bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported,
@@ -400,6 +400,7 @@ impl<'a> EbpfLoader<'a> {
                                 | ProgramSection::FEntry { sleepable: _ }
                                 | ProgramSection::FExit { sleepable: _ }
                                 | ProgramSection::Lsm { sleepable: _ }
+                                | ProgramSection::LsmCgroup
                                 | ProgramSection::BtfTracePoint
                                 | ProgramSection::Iter { sleepable: _ } => {
                                     return Err(EbpfError::BtfError(err));
@@ -642,6 +643,9 @@ impl<'a> EbpfLoader<'a> {
                             }
                             Program::Lsm(Lsm { data })
                         }
+                        ProgramSection::LsmCgroup => Program::LsmCgroup(LsmCgroup {
+                            data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
+                        }),
                         ProgramSection::BtfTracePoint => Program::BtfTracePoint(BtfTracePoint {
                             data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
                         }),

+ 1 - 1
aya/src/programs/lsm.rs

@@ -18,7 +18,7 @@ use crate::programs::{
 ///
 /// 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`).
+/// kernel's boot parameters (like `lsm=lockdown,yama,bpf`).
 ///
 /// # Minimum kernel version
 ///

+ 119 - 0
aya/src/programs/lsm_cgroup.rs

@@ -0,0 +1,119 @@
+//! LSM Cgroup probes.
+
+use std::{os::fd::AsFd, path::Path};
+
+use aya_obj::{
+    btf::{Btf, BtfKind},
+    generated::{bpf_attach_type::BPF_LSM_CGROUP, bpf_prog_type::BPF_PROG_TYPE_LSM},
+};
+
+use crate::{
+    VerifierLogLevel,
+    programs::{FdLink, FdLinkId, ProgramData, ProgramError, define_link_wrapper, load_program},
+    sys::{BpfLinkCreateArgs, LinkTarget, SyscallError, bpf_link_create},
+};
+
+/// A program that attaches to Linux LSM hooks with per-cgroup attachment type. 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 parameters (like `lsm=lockdown,yama,bpf`).
+///
+/// # Minimum kernel version
+///
+/// The minimum kernel version required to use this feature is 6.0.
+///
+/// # Examples
+///
+/// ```no_run
+/// # #[derive(thiserror::Error, Debug)]
+/// # enum LsmError {
+/// #     #[error(transparent)]
+/// #     BtfError(#[from] aya::BtfError),
+/// #     #[error(transparent)]
+/// #     Program(#[from] aya::programs::ProgramError),
+/// #     #[error(transparent)]
+/// #     Ebpf(#[from] aya::EbpfError),
+/// # }
+/// # let mut bpf = Ebpf::load_file("ebpf_programs.o")?;
+/// use aya::{Ebpf, programs::LsmCgroup, BtfError, Btf};
+/// use std::fs::File;
+///
+/// let btf = Btf::from_sys_fs()?;
+/// let file = File::open("/sys/fs/cgroup/unified").unwrap();
+/// let program: &mut LsmCgroup = bpf.program_mut("lsm_prog").unwrap().try_into()?;
+/// program.load("security_bprm_exec", &btf)?;
+/// program.attach(file)?;
+/// # 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 LsmCgroup {
+    pub(crate) data: ProgramData<LsmLink>,
+}
+
+impl LsmCgroup {
+    /// Loads the program inside the kernel.
+    ///
+    /// # 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, btf: &Btf) -> Result<(), ProgramError> {
+        self.data.expected_attach_type = Some(BPF_LSM_CGROUP);
+        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)
+    }
+
+    /// Creates a program from a pinned entry on a bpffs.
+    ///
+    /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`].
+    ///
+    /// On drop, any managed links are detached and the program is unloaded. This will not result in
+    /// the program being unloaded from the kernel if it is still pinned.
+    pub fn from_pin<P: AsRef<Path>>(path: P) -> Result<Self, ProgramError> {
+        let mut data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?;
+        data.expected_attach_type = Some(BPF_LSM_CGROUP);
+        Ok(Self { data })
+    }
+
+    /// Attaches the program.
+    ///
+    /// The returned value can be used to detach, see [LsmCgroup::detach].
+    pub fn attach<T: AsFd>(&mut self, cgroup: T) -> Result<LsmLinkId, ProgramError> {
+        let prog_fd = self.fd()?;
+        let prog_fd = prog_fd.as_fd();
+        let cgroup_fd = cgroup.as_fd();
+        // Unwrap safety: the function starts with `self.fd()?` that will succeed if and only
+        // if the program has been loaded, i.e. there is an fd. That happens if:
+        // - LsmCgroup::load has been called
+        // - LsmCgroup::from_pin has been called
+        //
+        // In all cases, expected_attach_type is guaranteed to be Some(_).
+        let attach_type = self.data.expected_attach_type.unwrap();
+        let btf_id = self.data.attach_btf_id.ok_or(ProgramError::NotLoaded)?;
+        let link_fd = bpf_link_create(
+            prog_fd,
+            LinkTarget::Fd(cgroup_fd),
+            attach_type,
+            0,
+            Some(BpfLinkCreateArgs::TargetBtfId(btf_id)),
+        )
+        .map_err(|io_error| SyscallError {
+            call: "bpf_link_create",
+            io_error,
+        })?;
+
+        self.data.links.insert(LsmLink::new(FdLink::new(link_fd)))
+    }
+}
+
+define_link_wrapper!(LsmLink, LsmLinkId, FdLink, FdLinkId, LsmCgroup,);

+ 48 - 27
aya/src/programs/mod.rs

@@ -57,6 +57,7 @@ pub mod kprobe;
 pub mod links;
 pub mod lirc_mode2;
 pub mod lsm;
+pub mod lsm_cgroup;
 pub mod perf_attach;
 pub mod perf_event;
 pub mod raw_trace_point;
@@ -109,6 +110,7 @@ pub use crate::programs::{
     links::{CgroupAttachMode, Link, LinkOrder},
     lirc_mode2::LircMode2,
     lsm::Lsm,
+    lsm_cgroup::LsmCgroup,
     perf_event::{PerfEvent, PerfEventScope, PerfTypeId, SamplePolicy},
     probe::ProbeKind,
     raw_trace_point::RawTracePoint,
@@ -307,6 +309,8 @@ pub enum Program {
     RawTracePoint(RawTracePoint),
     /// A [`Lsm`] program
     Lsm(Lsm),
+    /// A [`LsmCgroup`] program
+    LsmCgroup(LsmCgroup),
     /// A [`BtfTracePoint`] program
     BtfTracePoint(BtfTracePoint),
     /// A [`FEntry`] program
@@ -331,32 +335,40 @@ impl Program {
     /// Returns the program type.
     pub fn prog_type(&self) -> ProgramType {
         match self {
-            Self::KProbe(_) => KProbe::PROGRAM_TYPE,
-            Self::UProbe(_) => UProbe::PROGRAM_TYPE,
-            Self::TracePoint(_) => TracePoint::PROGRAM_TYPE,
-            Self::SocketFilter(_) => SocketFilter::PROGRAM_TYPE,
-            Self::Xdp(_) => Xdp::PROGRAM_TYPE,
-            Self::SkMsg(_) => SkMsg::PROGRAM_TYPE,
-            Self::SkSkb(_) => SkSkb::PROGRAM_TYPE,
-            Self::SockOps(_) => SockOps::PROGRAM_TYPE,
-            Self::SchedClassifier(_) => SchedClassifier::PROGRAM_TYPE,
-            Self::CgroupSkb(_) => CgroupSkb::PROGRAM_TYPE,
-            Self::CgroupSysctl(_) => CgroupSysctl::PROGRAM_TYPE,
-            Self::CgroupSockopt(_) => CgroupSockopt::PROGRAM_TYPE,
-            Self::LircMode2(_) => LircMode2::PROGRAM_TYPE,
-            Self::PerfEvent(_) => PerfEvent::PROGRAM_TYPE,
-            Self::RawTracePoint(_) => RawTracePoint::PROGRAM_TYPE,
-            Self::Lsm(_) => Lsm::PROGRAM_TYPE,
-            Self::BtfTracePoint(_) => BtfTracePoint::PROGRAM_TYPE,
-            Self::FEntry(_) => FEntry::PROGRAM_TYPE,
-            Self::FExit(_) => FExit::PROGRAM_TYPE,
-            Self::Extension(_) => Extension::PROGRAM_TYPE,
-            Self::CgroupSockAddr(_) => CgroupSockAddr::PROGRAM_TYPE,
-            Self::SkLookup(_) => SkLookup::PROGRAM_TYPE,
-            Self::CgroupSock(_) => CgroupSock::PROGRAM_TYPE,
-            Self::CgroupDevice(_) => CgroupDevice::PROGRAM_TYPE,
-            Self::Iter(_) => Iter::PROGRAM_TYPE,
-            Self::FlowDissector(_) => FlowDissector::PROGRAM_TYPE,
+            Self::KProbe(_) | Self::UProbe(_) => ProgramType::KProbe,
+            Self::TracePoint(_) => ProgramType::TracePoint,
+            Self::SocketFilter(_) => ProgramType::SocketFilter,
+            Self::Xdp(_) => ProgramType::Xdp,
+            Self::SkMsg(_) => ProgramType::SkMsg,
+            Self::SkSkb(_) => ProgramType::SkSkb,
+            Self::SockOps(_) => ProgramType::SockOps,
+            Self::SchedClassifier(_) => ProgramType::SchedClassifier,
+            Self::CgroupSkb(_) => ProgramType::CgroupSkb,
+            Self::CgroupSysctl(_) => ProgramType::CgroupSysctl,
+            Self::CgroupSockopt(_) => ProgramType::CgroupSockopt,
+            Self::LircMode2(_) => ProgramType::LircMode2,
+            Self::PerfEvent(_) => ProgramType::PerfEvent,
+            Self::RawTracePoint(_) => ProgramType::RawTracePoint,
+            Self::Lsm(_) => ProgramType::Lsm,
+            Self::LsmCgroup(_) => ProgramType::Lsm,
+            // The following program types are a subset of `TRACING` programs:
+            //
+            // - `BPF_TRACE_RAW_TP` (`BtfTracePoint`)
+            // - `BTF_TRACE_FENTRY` (`FEntry`)
+            // - `BPF_MODIFY_RETURN` (not supported yet in Aya)
+            // - `BPF_TRACE_FEXIT` (`FExit`)
+            // - `BPF_TRACE_ITER` (`Iter`)
+            //
+            // https://github.com/torvalds/linux/blob/v6.12/kernel/bpf/syscall.c#L3935-L3940
+            Self::BtfTracePoint(_) | Self::FEntry(_) | Self::FExit(_) | Self::Iter(_) => {
+                ProgramType::Tracing
+            }
+            Self::Extension(_) => ProgramType::Extension,
+            Self::CgroupSockAddr(_) => ProgramType::CgroupSockAddr,
+            Self::SkLookup(_) => ProgramType::SkLookup,
+            Self::CgroupSock(_) => ProgramType::CgroupSock,
+            Self::CgroupDevice(_) => ProgramType::CgroupDevice,
+            Self::FlowDissector(_) => ProgramType::FlowDissector,
         }
     }
 
@@ -379,6 +391,7 @@ impl Program {
             Self::PerfEvent(p) => p.pin(path),
             Self::RawTracePoint(p) => p.pin(path),
             Self::Lsm(p) => p.pin(path),
+            Self::LsmCgroup(p) => p.pin(path),
             Self::BtfTracePoint(p) => p.pin(path),
             Self::FEntry(p) => p.pin(path),
             Self::FExit(p) => p.pin(path),
@@ -411,6 +424,7 @@ impl Program {
             Self::PerfEvent(mut p) => p.unload(),
             Self::RawTracePoint(mut p) => p.unload(),
             Self::Lsm(mut p) => p.unload(),
+            Self::LsmCgroup(mut p) => p.unload(),
             Self::BtfTracePoint(mut p) => p.unload(),
             Self::FEntry(mut p) => p.unload(),
             Self::FExit(mut p) => p.unload(),
@@ -445,6 +459,7 @@ impl Program {
             Self::PerfEvent(p) => p.fd(),
             Self::RawTracePoint(p) => p.fd(),
             Self::Lsm(p) => p.fd(),
+            Self::LsmCgroup(p) => p.fd(),
             Self::BtfTracePoint(p) => p.fd(),
             Self::FEntry(p) => p.fd(),
             Self::FExit(p) => p.fd(),
@@ -480,6 +495,7 @@ impl Program {
             Self::PerfEvent(p) => p.info(),
             Self::RawTracePoint(p) => p.info(),
             Self::Lsm(p) => p.info(),
+            Self::LsmCgroup(p) => p.info(),
             Self::BtfTracePoint(p) => p.info(),
             Self::FEntry(p) => p.info(),
             Self::FExit(p) => p.info(),
@@ -794,6 +810,7 @@ impl_program_unload!(
     LircMode2,
     PerfEvent,
     Lsm,
+    LsmCgroup,
     RawTracePoint,
     BtfTracePoint,
     FEntry,
@@ -836,6 +853,7 @@ impl_fd!(
     LircMode2,
     PerfEvent,
     Lsm,
+    LsmCgroup,
     RawTracePoint,
     BtfTracePoint,
     FEntry,
@@ -943,6 +961,7 @@ impl_program_pin!(
     LircMode2,
     PerfEvent,
     Lsm,
+    LsmCgroup,
     RawTracePoint,
     BtfTracePoint,
     FEntry,
@@ -983,8 +1002,8 @@ impl_from_pin!(
     SkMsg,
     CgroupSysctl,
     LircMode2,
-    PerfEvent,
     Lsm,
+    PerfEvent,
     RawTracePoint,
     BtfTracePoint,
     FEntry,
@@ -1149,6 +1168,7 @@ impl_try_from_program!(
     LircMode2,
     PerfEvent,
     Lsm,
+    LsmCgroup,
     RawTracePoint,
     BtfTracePoint,
     FEntry,
@@ -1177,6 +1197,7 @@ impl_info!(
     LircMode2,
     PerfEvent,
     Lsm,
+    LsmCgroup,
     RawTracePoint,
     BtfTracePoint,
     FEntry,

+ 8 - 1
test/integration-ebpf/src/test.rs

@@ -4,7 +4,9 @@
 
 use aya_ebpf::{
     bindings::{bpf_ret_code, xdp_action},
-    macros::{flow_dissector, kprobe, kretprobe, lsm, tracepoint, uprobe, uretprobe, xdp},
+    macros::{
+        flow_dissector, kprobe, kretprobe, lsm, lsm_cgroup, tracepoint, uprobe, uretprobe, xdp,
+    },
     programs::{
         FlowDissectorContext, LsmContext, ProbeContext, RetProbeContext, TracePointContext,
         XdpContext,
@@ -54,3 +56,8 @@ fn test_flow(_ctx: FlowDissectorContext) -> u32 {
 fn test_lsm(_ctx: LsmContext) -> i32 {
     -1 // Disallow.
 }
+
+#[lsm_cgroup(hook = "socket_bind")]
+fn test_lsm_cgroup(_ctx: LsmContext) -> i32 {
+    0 // Disallow.
+}

+ 50 - 1
test/integration-test/src/tests/lsm.rs

@@ -1,10 +1,12 @@
 use assert_matches::assert_matches;
 use aya::{
     Btf, Ebpf,
-    programs::{Lsm, ProgramError, ProgramType},
+    programs::{Lsm, LsmCgroup, ProgramError, ProgramType},
     sys::{SyscallError, is_program_supported},
 };
 
+use crate::utils::Cgroup;
+
 macro_rules! expect_permission_denied {
     ($result:expr) => {
         let result = $result;
@@ -48,3 +50,50 @@ fn lsm() {
 
     assert_matches!(std::net::TcpListener::bind("127.0.0.1:0"), Ok(_));
 }
+
+#[test]
+fn lsm_cgroup() {
+    let mut bpf: Ebpf = Ebpf::load(crate::TEST).unwrap();
+    let prog = bpf.program_mut("test_lsm_cgroup").unwrap();
+    let prog: &mut LsmCgroup = prog.try_into().unwrap();
+    let btf = Btf::from_sys_fs().expect("could not get btf from sys");
+    prog.load("socket_bind", &btf).unwrap();
+
+    assert_matches!(std::net::TcpListener::bind("127.0.0.1:0"), Ok(_));
+
+    let pid = std::process::id();
+    let root = Cgroup::root();
+    let cgroup = root.create_child("aya-test-lsm-cgroup");
+
+    let link_id = {
+        let result = prog.attach(cgroup.fd());
+
+        if !is_program_supported(ProgramType::Lsm).unwrap() {
+            assert_matches!(result, Err(ProgramError::SyscallError(SyscallError { call, io_error })) => {
+                assert_eq!(call, "bpf_link_create");
+                assert_eq!(io_error.raw_os_error(), Some(524));
+            });
+            eprintln!("skipping test - LSM programs not supported");
+            return;
+        }
+        result.unwrap()
+    };
+
+    let cgroup = cgroup.into_cgroup();
+
+    cgroup.write_pid(pid);
+
+    expect_permission_denied!(std::net::TcpListener::bind("127.0.0.1:0"));
+
+    root.write_pid(pid);
+
+    assert_matches!(std::net::TcpListener::bind("127.0.0.1:0"), Ok(_));
+
+    cgroup.write_pid(pid);
+
+    expect_permission_denied!(std::net::TcpListener::bind("127.0.0.1:0"));
+
+    prog.detach(link_id).unwrap();
+
+    assert_matches!(std::net::TcpListener::bind("127.0.0.1:0"), Ok(_));
+}

+ 1 - 0
xtask/public-api/aya-ebpf-macros.txt

@@ -14,6 +14,7 @@ pub proc macro aya_ebpf_macros::#[flow_dissector]
 pub proc macro aya_ebpf_macros::#[kprobe]
 pub proc macro aya_ebpf_macros::#[kretprobe]
 pub proc macro aya_ebpf_macros::#[lsm]
+pub proc macro aya_ebpf_macros::#[lsm_cgroup]
 pub proc macro aya_ebpf_macros::#[map]
 pub proc macro aya_ebpf_macros::#[perf_event]
 pub proc macro aya_ebpf_macros::#[raw_tracepoint]

+ 2 - 0
xtask/public-api/aya-obj.txt

@@ -8617,6 +8617,7 @@ pub aya_obj::obj::ProgramSection::KRetProbe
 pub aya_obj::obj::ProgramSection::LircMode2
 pub aya_obj::obj::ProgramSection::Lsm
 pub aya_obj::obj::ProgramSection::Lsm::sleepable: bool
+pub aya_obj::obj::ProgramSection::LsmCgroup
 pub aya_obj::obj::ProgramSection::PerfEvent
 pub aya_obj::obj::ProgramSection::RawTracePoint
 pub aya_obj::obj::ProgramSection::SchedClassifier
@@ -9478,6 +9479,7 @@ pub aya_obj::ProgramSection::KRetProbe
 pub aya_obj::ProgramSection::LircMode2
 pub aya_obj::ProgramSection::Lsm
 pub aya_obj::ProgramSection::Lsm::sleepable: bool
+pub aya_obj::ProgramSection::LsmCgroup
 pub aya_obj::ProgramSection::PerfEvent
 pub aya_obj::ProgramSection::RawTracePoint
 pub aya_obj::ProgramSection::SchedClassifier

+ 197 - 0
xtask/public-api/aya.txt

@@ -4387,6 +4387,8 @@ impl core::convert::From<aya::programs::links::FdLink> for aya::programs::fexit:
 pub fn aya::programs::fexit::FExitLink::from(b: aya::programs::links::FdLink) -> aya::programs::fexit::FExitLink
 impl core::convert::From<aya::programs::links::FdLink> for aya::programs::lsm::LsmLink
 pub fn aya::programs::lsm::LsmLink::from(b: aya::programs::links::FdLink) -> aya::programs::lsm::LsmLink
+impl core::convert::From<aya::programs::links::FdLink> for aya::programs::lsm_cgroup::LsmLink
+pub fn aya::programs::lsm_cgroup::LsmLink::from(b: aya::programs::links::FdLink) -> aya::programs::lsm_cgroup::LsmLink
 impl core::convert::From<aya::programs::links::FdLink> for aya::programs::raw_trace_point::RawTracePointLink
 pub fn aya::programs::raw_trace_point::RawTracePointLink::from(b: aya::programs::links::FdLink) -> aya::programs::raw_trace_point::RawTracePointLink
 impl core::convert::From<aya::programs::links::FdLink> for aya::programs::sk_lookup::SkLookupLink
@@ -4397,6 +4399,8 @@ impl core::convert::From<aya::programs::links::PinnedLink> for aya::programs::li
 pub fn aya::programs::links::FdLink::from(p: aya::programs::links::PinnedLink) -> Self
 impl core::convert::From<aya::programs::lsm::LsmLink> for aya::programs::links::FdLink
 pub fn aya::programs::links::FdLink::from(w: aya::programs::lsm::LsmLink) -> aya::programs::links::FdLink
+impl core::convert::From<aya::programs::lsm_cgroup::LsmLink> for aya::programs::links::FdLink
+pub fn aya::programs::links::FdLink::from(w: aya::programs::lsm_cgroup::LsmLink) -> aya::programs::links::FdLink
 impl core::convert::From<aya::programs::raw_trace_point::RawTracePointLink> for aya::programs::links::FdLink
 pub fn aya::programs::links::FdLink::from(w: aya::programs::raw_trace_point::RawTracePointLink) -> aya::programs::links::FdLink
 impl core::convert::From<aya::programs::sk_lookup::SkLookupLink> for aya::programs::links::FdLink
@@ -4767,6 +4771,10 @@ impl aya::programs::links::Link for aya::programs::lsm::LsmLink
 pub type aya::programs::lsm::LsmLink::Id = aya::programs::lsm::LsmLinkId
 pub fn aya::programs::lsm::LsmLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
 pub fn aya::programs::lsm::LsmLink::id(&self) -> Self::Id
+impl aya::programs::links::Link for aya::programs::lsm_cgroup::LsmLink
+pub type aya::programs::lsm_cgroup::LsmLink::Id = aya::programs::lsm_cgroup::LsmLinkId
+pub fn aya::programs::lsm_cgroup::LsmLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::lsm_cgroup::LsmLink::id(&self) -> Self::Id
 impl aya::programs::links::Link for aya::programs::perf_attach::PerfLink
 pub type aya::programs::perf_attach::PerfLink::Id = aya::programs::perf_attach::PerfLinkId
 pub fn aya::programs::perf_attach::PerfLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
@@ -5081,6 +5089,135 @@ impl<T> core::borrow::BorrowMut<T> for aya::programs::lsm::LsmLinkId where T: ?c
 pub fn aya::programs::lsm::LsmLinkId::borrow_mut(&mut self) -> &mut T
 impl<T> core::convert::From<T> for aya::programs::lsm::LsmLinkId
 pub fn aya::programs::lsm::LsmLinkId::from(t: T) -> T
+pub mod aya::programs::lsm_cgroup
+pub struct aya::programs::lsm_cgroup::LsmCgroup
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::attach<T: std::os::fd::owned::AsFd>(&mut self, cgroup: T) -> core::result::Result<aya::programs::lsm_cgroup::LsmLinkId, aya::programs::ProgramError>
+pub fn aya::programs::lsm_cgroup::LsmCgroup::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
+pub fn aya::programs::lsm_cgroup::LsmCgroup::load(&mut self, lsm_hook_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::detach(&mut self, link_id: aya::programs::lsm_cgroup::LsmLinkId) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::lsm_cgroup::LsmCgroup::take_link(&mut self, link_id: aya::programs::lsm_cgroup::LsmLinkId) -> core::result::Result<aya::programs::lsm_cgroup::LsmLink, aya::programs::ProgramError>
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
+pub fn aya::programs::lsm_cgroup::LsmCgroup::unpin(self) -> core::result::Result<(), std::io::error::Error>
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
+impl core::fmt::Debug for aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::ops::drop::Drop for aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::drop(&mut self)
+impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::lsm_cgroup::LsmCgroup
+pub type &'a aya::programs::lsm_cgroup::LsmCgroup::Error = aya::programs::ProgramError
+pub fn &'a aya::programs::lsm_cgroup::LsmCgroup::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::lsm_cgroup::LsmCgroup, aya::programs::ProgramError>
+impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::lsm_cgroup::LsmCgroup
+pub type &'a mut aya::programs::lsm_cgroup::LsmCgroup::Error = aya::programs::ProgramError
+pub fn &'a mut aya::programs::lsm_cgroup::LsmCgroup::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::lsm_cgroup::LsmCgroup, aya::programs::ProgramError>
+impl core::marker::Freeze for aya::programs::lsm_cgroup::LsmCgroup
+impl core::marker::Send for aya::programs::lsm_cgroup::LsmCgroup
+impl core::marker::Sync for aya::programs::lsm_cgroup::LsmCgroup
+impl core::marker::Unpin for aya::programs::lsm_cgroup::LsmCgroup
+impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::lsm_cgroup::LsmCgroup
+impl core::panic::unwind_safe::UnwindSafe for aya::programs::lsm_cgroup::LsmCgroup
+impl<T, U> core::convert::Into<U> for aya::programs::lsm_cgroup::LsmCgroup where U: core::convert::From<T>
+pub fn aya::programs::lsm_cgroup::LsmCgroup::into(self) -> U
+impl<T, U> core::convert::TryFrom<U> for aya::programs::lsm_cgroup::LsmCgroup where U: core::convert::Into<T>
+pub type aya::programs::lsm_cgroup::LsmCgroup::Error = core::convert::Infallible
+pub fn aya::programs::lsm_cgroup::LsmCgroup::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
+impl<T, U> core::convert::TryInto<U> for aya::programs::lsm_cgroup::LsmCgroup where U: core::convert::TryFrom<T>
+pub type aya::programs::lsm_cgroup::LsmCgroup::Error = <U as core::convert::TryFrom<T>>::Error
+pub fn aya::programs::lsm_cgroup::LsmCgroup::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
+impl<T> core::any::Any for aya::programs::lsm_cgroup::LsmCgroup where T: 'static + ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmCgroup::type_id(&self) -> core::any::TypeId
+impl<T> core::borrow::Borrow<T> for aya::programs::lsm_cgroup::LsmCgroup where T: ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmCgroup::borrow(&self) -> &T
+impl<T> core::borrow::BorrowMut<T> for aya::programs::lsm_cgroup::LsmCgroup where T: ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmCgroup::borrow_mut(&mut self) -> &mut T
+impl<T> core::convert::From<T> for aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::from(t: T) -> T
+pub struct aya::programs::lsm_cgroup::LsmLink(_)
+impl aya::programs::links::Link for aya::programs::lsm_cgroup::LsmLink
+pub type aya::programs::lsm_cgroup::LsmLink::Id = aya::programs::lsm_cgroup::LsmLinkId
+pub fn aya::programs::lsm_cgroup::LsmLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::lsm_cgroup::LsmLink::id(&self) -> Self::Id
+impl core::cmp::Eq for aya::programs::lsm_cgroup::LsmLink
+impl core::cmp::PartialEq for aya::programs::lsm_cgroup::LsmLink
+pub fn aya::programs::lsm_cgroup::LsmLink::eq(&self, other: &Self) -> bool
+impl core::convert::From<aya::programs::links::FdLink> for aya::programs::lsm_cgroup::LsmLink
+pub fn aya::programs::lsm_cgroup::LsmLink::from(b: aya::programs::links::FdLink) -> aya::programs::lsm_cgroup::LsmLink
+impl core::convert::From<aya::programs::lsm_cgroup::LsmLink> for aya::programs::links::FdLink
+pub fn aya::programs::links::FdLink::from(w: aya::programs::lsm_cgroup::LsmLink) -> aya::programs::links::FdLink
+impl core::fmt::Debug for aya::programs::lsm_cgroup::LsmLink
+pub fn aya::programs::lsm_cgroup::LsmLink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::hash::Hash for aya::programs::lsm_cgroup::LsmLink
+pub fn aya::programs::lsm_cgroup::LsmLink::hash<H: core::hash::Hasher>(&self, state: &mut H)
+impl core::ops::drop::Drop for aya::programs::lsm_cgroup::LsmLink
+pub fn aya::programs::lsm_cgroup::LsmLink::drop(&mut self)
+impl equivalent::Equivalent<aya::programs::lsm_cgroup::LsmLink> for aya::programs::lsm_cgroup::LsmLinkId
+pub fn aya::programs::lsm_cgroup::LsmLinkId::equivalent(&self, key: &aya::programs::lsm_cgroup::LsmLink) -> bool
+impl core::marker::Freeze for aya::programs::lsm_cgroup::LsmLink
+impl core::marker::Send for aya::programs::lsm_cgroup::LsmLink
+impl core::marker::Sync for aya::programs::lsm_cgroup::LsmLink
+impl core::marker::Unpin for aya::programs::lsm_cgroup::LsmLink
+impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::lsm_cgroup::LsmLink
+impl core::panic::unwind_safe::UnwindSafe for aya::programs::lsm_cgroup::LsmLink
+impl<Q, K> equivalent::Equivalent<K> for aya::programs::lsm_cgroup::LsmLink where Q: core::cmp::Eq + ?core::marker::Sized, K: core::borrow::Borrow<Q> + ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmLink::equivalent(&self, key: &K) -> bool
+impl<T, U> core::convert::Into<U> for aya::programs::lsm_cgroup::LsmLink where U: core::convert::From<T>
+pub fn aya::programs::lsm_cgroup::LsmLink::into(self) -> U
+impl<T, U> core::convert::TryFrom<U> for aya::programs::lsm_cgroup::LsmLink where U: core::convert::Into<T>
+pub type aya::programs::lsm_cgroup::LsmLink::Error = core::convert::Infallible
+pub fn aya::programs::lsm_cgroup::LsmLink::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
+impl<T, U> core::convert::TryInto<U> for aya::programs::lsm_cgroup::LsmLink where U: core::convert::TryFrom<T>
+pub type aya::programs::lsm_cgroup::LsmLink::Error = <U as core::convert::TryFrom<T>>::Error
+pub fn aya::programs::lsm_cgroup::LsmLink::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
+impl<T> core::any::Any for aya::programs::lsm_cgroup::LsmLink where T: 'static + ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmLink::type_id(&self) -> core::any::TypeId
+impl<T> core::borrow::Borrow<T> for aya::programs::lsm_cgroup::LsmLink where T: ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmLink::borrow(&self) -> &T
+impl<T> core::borrow::BorrowMut<T> for aya::programs::lsm_cgroup::LsmLink where T: ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmLink::borrow_mut(&mut self) -> &mut T
+impl<T> core::convert::From<T> for aya::programs::lsm_cgroup::LsmLink
+pub fn aya::programs::lsm_cgroup::LsmLink::from(t: T) -> T
+pub struct aya::programs::lsm_cgroup::LsmLinkId(_)
+impl core::cmp::Eq for aya::programs::lsm_cgroup::LsmLinkId
+impl core::cmp::PartialEq for aya::programs::lsm_cgroup::LsmLinkId
+pub fn aya::programs::lsm_cgroup::LsmLinkId::eq(&self, other: &aya::programs::lsm_cgroup::LsmLinkId) -> bool
+impl core::fmt::Debug for aya::programs::lsm_cgroup::LsmLinkId
+pub fn aya::programs::lsm_cgroup::LsmLinkId::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::hash::Hash for aya::programs::lsm_cgroup::LsmLinkId
+pub fn aya::programs::lsm_cgroup::LsmLinkId::hash<__H: core::hash::Hasher>(&self, state: &mut __H)
+impl core::marker::StructuralPartialEq for aya::programs::lsm_cgroup::LsmLinkId
+impl equivalent::Equivalent<aya::programs::lsm_cgroup::LsmLink> for aya::programs::lsm_cgroup::LsmLinkId
+pub fn aya::programs::lsm_cgroup::LsmLinkId::equivalent(&self, key: &aya::programs::lsm_cgroup::LsmLink) -> bool
+impl core::marker::Freeze for aya::programs::lsm_cgroup::LsmLinkId
+impl core::marker::Send for aya::programs::lsm_cgroup::LsmLinkId
+impl core::marker::Sync for aya::programs::lsm_cgroup::LsmLinkId
+impl core::marker::Unpin for aya::programs::lsm_cgroup::LsmLinkId
+impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::lsm_cgroup::LsmLinkId
+impl core::panic::unwind_safe::UnwindSafe for aya::programs::lsm_cgroup::LsmLinkId
+impl<Q, K> equivalent::Equivalent<K> for aya::programs::lsm_cgroup::LsmLinkId where Q: core::cmp::Eq + ?core::marker::Sized, K: core::borrow::Borrow<Q> + ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmLinkId::equivalent(&self, key: &K) -> bool
+impl<T, U> core::convert::Into<U> for aya::programs::lsm_cgroup::LsmLinkId where U: core::convert::From<T>
+pub fn aya::programs::lsm_cgroup::LsmLinkId::into(self) -> U
+impl<T, U> core::convert::TryFrom<U> for aya::programs::lsm_cgroup::LsmLinkId where U: core::convert::Into<T>
+pub type aya::programs::lsm_cgroup::LsmLinkId::Error = core::convert::Infallible
+pub fn aya::programs::lsm_cgroup::LsmLinkId::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
+impl<T, U> core::convert::TryInto<U> for aya::programs::lsm_cgroup::LsmLinkId where U: core::convert::TryFrom<T>
+pub type aya::programs::lsm_cgroup::LsmLinkId::Error = <U as core::convert::TryFrom<T>>::Error
+pub fn aya::programs::lsm_cgroup::LsmLinkId::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
+impl<T> core::any::Any for aya::programs::lsm_cgroup::LsmLinkId where T: 'static + ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmLinkId::type_id(&self) -> core::any::TypeId
+impl<T> core::borrow::Borrow<T> for aya::programs::lsm_cgroup::LsmLinkId where T: ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmLinkId::borrow(&self) -> &T
+impl<T> core::borrow::BorrowMut<T> for aya::programs::lsm_cgroup::LsmLinkId where T: ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmLinkId::borrow_mut(&mut self) -> &mut T
+impl<T> core::convert::From<T> for aya::programs::lsm_cgroup::LsmLinkId
+pub fn aya::programs::lsm_cgroup::LsmLinkId::from(t: T) -> T
 pub mod aya::programs::perf_attach
 pub struct aya::programs::perf_attach::PerfLink
 impl aya::programs::links::Link for aya::programs::perf_attach::PerfLink
@@ -7689,6 +7826,7 @@ pub aya::programs::Program::Iter(aya::programs::iter::Iter)
 pub aya::programs::Program::KProbe(aya::programs::kprobe::KProbe)
 pub aya::programs::Program::LircMode2(aya::programs::lirc_mode2::LircMode2)
 pub aya::programs::Program::Lsm(aya::programs::lsm::Lsm)
+pub aya::programs::Program::LsmCgroup(aya::programs::lsm_cgroup::LsmCgroup)
 pub aya::programs::Program::PerfEvent(aya::programs::perf_event::PerfEvent)
 pub aya::programs::Program::RawTracePoint(aya::programs::raw_trace_point::RawTracePoint)
 pub aya::programs::Program::SchedClassifier(aya::programs::tc::SchedClassifier)
@@ -7750,6 +7888,9 @@ pub fn &'a aya::programs::lirc_mode2::LircMode2::try_from(program: &'a aya::prog
 impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::lsm::Lsm
 pub type &'a aya::programs::lsm::Lsm::Error = aya::programs::ProgramError
 pub fn &'a aya::programs::lsm::Lsm::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::lsm::Lsm, aya::programs::ProgramError>
+impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::lsm_cgroup::LsmCgroup
+pub type &'a aya::programs::lsm_cgroup::LsmCgroup::Error = aya::programs::ProgramError
+pub fn &'a aya::programs::lsm_cgroup::LsmCgroup::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::lsm_cgroup::LsmCgroup, aya::programs::ProgramError>
 impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::perf_event::PerfEvent
 pub type &'a aya::programs::perf_event::PerfEvent::Error = aya::programs::ProgramError
 pub fn &'a aya::programs::perf_event::PerfEvent::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::perf_event::PerfEvent, aya::programs::ProgramError>
@@ -7828,6 +7969,9 @@ pub fn &'a mut aya::programs::lirc_mode2::LircMode2::try_from(program: &'a mut a
 impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::lsm::Lsm
 pub type &'a mut aya::programs::lsm::Lsm::Error = aya::programs::ProgramError
 pub fn &'a mut aya::programs::lsm::Lsm::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::lsm::Lsm, aya::programs::ProgramError>
+impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::lsm_cgroup::LsmCgroup
+pub type &'a mut aya::programs::lsm_cgroup::LsmCgroup::Error = aya::programs::ProgramError
+pub fn &'a mut aya::programs::lsm_cgroup::LsmCgroup::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::lsm_cgroup::LsmCgroup, aya::programs::ProgramError>
 impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::perf_event::PerfEvent
 pub type &'a mut aya::programs::perf_event::PerfEvent::Error = aya::programs::ProgramError
 pub fn &'a mut aya::programs::perf_event::PerfEvent::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::perf_event::PerfEvent, aya::programs::ProgramError>
@@ -9173,6 +9317,55 @@ impl<T> core::borrow::BorrowMut<T> for aya::programs::lsm::Lsm where T: ?core::m
 pub fn aya::programs::lsm::Lsm::borrow_mut(&mut self) -> &mut T
 impl<T> core::convert::From<T> for aya::programs::lsm::Lsm
 pub fn aya::programs::lsm::Lsm::from(t: T) -> T
+pub struct aya::programs::LsmCgroup
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::attach<T: std::os::fd::owned::AsFd>(&mut self, cgroup: T) -> core::result::Result<aya::programs::lsm_cgroup::LsmLinkId, aya::programs::ProgramError>
+pub fn aya::programs::lsm_cgroup::LsmCgroup::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P) -> core::result::Result<Self, aya::programs::ProgramError>
+pub fn aya::programs::lsm_cgroup::LsmCgroup::load(&mut self, lsm_hook_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError>
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::detach(&mut self, link_id: aya::programs::lsm_cgroup::LsmLinkId) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::lsm_cgroup::LsmCgroup::take_link(&mut self, link_id: aya::programs::lsm_cgroup::LsmLinkId) -> core::result::Result<aya::programs::lsm_cgroup::LsmLink, aya::programs::ProgramError>
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
+pub fn aya::programs::lsm_cgroup::LsmCgroup::unpin(self) -> core::result::Result<(), std::io::error::Error>
+impl aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
+impl core::fmt::Debug for aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
+impl core::ops::drop::Drop for aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::drop(&mut self)
+impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::lsm_cgroup::LsmCgroup
+pub type &'a aya::programs::lsm_cgroup::LsmCgroup::Error = aya::programs::ProgramError
+pub fn &'a aya::programs::lsm_cgroup::LsmCgroup::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::lsm_cgroup::LsmCgroup, aya::programs::ProgramError>
+impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::lsm_cgroup::LsmCgroup
+pub type &'a mut aya::programs::lsm_cgroup::LsmCgroup::Error = aya::programs::ProgramError
+pub fn &'a mut aya::programs::lsm_cgroup::LsmCgroup::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::lsm_cgroup::LsmCgroup, aya::programs::ProgramError>
+impl core::marker::Freeze for aya::programs::lsm_cgroup::LsmCgroup
+impl core::marker::Send for aya::programs::lsm_cgroup::LsmCgroup
+impl core::marker::Sync for aya::programs::lsm_cgroup::LsmCgroup
+impl core::marker::Unpin for aya::programs::lsm_cgroup::LsmCgroup
+impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::lsm_cgroup::LsmCgroup
+impl core::panic::unwind_safe::UnwindSafe for aya::programs::lsm_cgroup::LsmCgroup
+impl<T, U> core::convert::Into<U> for aya::programs::lsm_cgroup::LsmCgroup where U: core::convert::From<T>
+pub fn aya::programs::lsm_cgroup::LsmCgroup::into(self) -> U
+impl<T, U> core::convert::TryFrom<U> for aya::programs::lsm_cgroup::LsmCgroup where U: core::convert::Into<T>
+pub type aya::programs::lsm_cgroup::LsmCgroup::Error = core::convert::Infallible
+pub fn aya::programs::lsm_cgroup::LsmCgroup::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
+impl<T, U> core::convert::TryInto<U> for aya::programs::lsm_cgroup::LsmCgroup where U: core::convert::TryFrom<T>
+pub type aya::programs::lsm_cgroup::LsmCgroup::Error = <U as core::convert::TryFrom<T>>::Error
+pub fn aya::programs::lsm_cgroup::LsmCgroup::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
+impl<T> core::any::Any for aya::programs::lsm_cgroup::LsmCgroup where T: 'static + ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmCgroup::type_id(&self) -> core::any::TypeId
+impl<T> core::borrow::Borrow<T> for aya::programs::lsm_cgroup::LsmCgroup where T: ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmCgroup::borrow(&self) -> &T
+impl<T> core::borrow::BorrowMut<T> for aya::programs::lsm_cgroup::LsmCgroup where T: ?core::marker::Sized
+pub fn aya::programs::lsm_cgroup::LsmCgroup::borrow_mut(&mut self) -> &mut T
+impl<T> core::convert::From<T> for aya::programs::lsm_cgroup::LsmCgroup
+pub fn aya::programs::lsm_cgroup::LsmCgroup::from(t: T) -> T
 pub struct aya::programs::PerfEvent
 impl aya::programs::perf_event::PerfEvent
 pub const aya::programs::perf_event::PerfEvent::PROGRAM_TYPE: aya::programs::ProgramType
@@ -10036,6 +10229,10 @@ impl aya::programs::links::Link for aya::programs::lsm::LsmLink
 pub type aya::programs::lsm::LsmLink::Id = aya::programs::lsm::LsmLinkId
 pub fn aya::programs::lsm::LsmLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
 pub fn aya::programs::lsm::LsmLink::id(&self) -> Self::Id
+impl aya::programs::links::Link for aya::programs::lsm_cgroup::LsmLink
+pub type aya::programs::lsm_cgroup::LsmLink::Id = aya::programs::lsm_cgroup::LsmLinkId
+pub fn aya::programs::lsm_cgroup::LsmLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>
+pub fn aya::programs::lsm_cgroup::LsmLink::id(&self) -> Self::Id
 impl aya::programs::links::Link for aya::programs::perf_attach::PerfLink
 pub type aya::programs::perf_attach::PerfLink::Id = aya::programs::perf_attach::PerfLinkId
 pub fn aya::programs::perf_attach::PerfLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError>