Pārlūkot izejas kodu

Add support for BPF_PROG_TYPE_CGROUP_SYSCTL (#256)

* Add support for BPF_PROG_TYPE_CGROUP_SYSCTL

This patch adds support for `BPF_PROG_TYPE_CGROUP_SYSCTL`.

* Parse unnamed macro

* Fix docs
Kenjiro Nakayama 2 gadi atpakaļ
vecāks
revīzija
f721021a0a

+ 9 - 3
aya/src/bpf.rs

@@ -22,9 +22,10 @@ use crate::{
         MapKind, Object, ParseError, ProgramSection,
     },
     programs::{
-        BtfTracePoint, CgroupSkb, CgroupSkbAttachType, Extension, FEntry, FExit, KProbe, LircMode2,
-        Lsm, PerfEvent, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint,
-        SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
+        BtfTracePoint, CgroupSkb, CgroupSkbAttachType, CgroupSysctl, Extension, FEntry, FExit,
+        KProbe, LircMode2, Lsm, PerfEvent, ProbeKind, Program, ProgramData, ProgramError,
+        RawTracePoint, SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint,
+        UProbe, Xdp,
     },
     sys::{
         bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_btf_datasec_supported,
@@ -443,6 +444,11 @@ impl<'a> BpfLoader<'a> {
                         ProgramSection::SkMsg { .. } => Program::SkMsg(SkMsg {
                             data: ProgramData::new(prog_name, obj, btf_fd),
                         }),
+                        ProgramSection::CgroupSysctl { .. } => {
+                            Program::CgroupSysctl(CgroupSysctl {
+                                data: ProgramData::new(prog_name, obj, btf_fd),
+                            })
+                        }
                         ProgramSection::SkSkbStreamParser { .. } => Program::SkSkb(SkSkb {
                             data: ProgramData::new(prog_name, obj, btf_fd),
                             kind: SkSkbKind::StreamParser,

+ 4 - 0
aya/src/obj/mod.rs

@@ -119,6 +119,7 @@ pub enum ProgramSection {
     CgroupSkb { name: String },
     CgroupSkbIngress { name: String },
     CgroupSkbEgress { name: String },
+    CgroupSysctl { name: String },
     LircMode2 { name: String },
     PerfEvent { name: String },
     RawTracePoint { name: String },
@@ -147,6 +148,7 @@ impl ProgramSection {
             ProgramSection::CgroupSkb { name } => name,
             ProgramSection::CgroupSkbIngress { name } => name,
             ProgramSection::CgroupSkbEgress { name } => name,
+            ProgramSection::CgroupSysctl { name } => name,
             ProgramSection::LircMode2 { name } => name,
             ProgramSection::PerfEvent { name } => name,
             ProgramSection::RawTracePoint { name } => name,
@@ -214,8 +216,10 @@ impl FromStr for ProgramSection {
             "cgroup_skb/ingress" => CgroupSkbIngress { name },
             "cgroup_skb/egress" => CgroupSkbEgress { name },
             "cgroup/skb" => CgroupSkb { name },
+            "cgroup/sysctl" => CgroupSysctl { name },
             "cgroup" => match &*name {
                 "skb" => CgroupSkb { name },
+                "sysctl" => CgroupSysctl { name },
                 _ => {
                     return Err(ParseError::InvalidProgramSection {
                         section: section.to_owned(),

+ 153 - 0
aya/src/programs/cgroup_sysctl.rs

@@ -0,0 +1,153 @@
+use std::{
+    hash::Hash,
+    os::unix::prelude::{AsRawFd, RawFd},
+};
+
+use crate::{
+    generated::{bpf_attach_type::BPF_CGROUP_SYSCTL, bpf_prog_type::BPF_PROG_TYPE_CGROUP_SYSCTL},
+    programs::{
+        define_link_wrapper, load_program, FdLink, Link, OwnedLink, ProgAttachLink, ProgramData,
+        ProgramError,
+    },
+    sys::{bpf_link_create, bpf_prog_attach, kernel_version},
+};
+
+/// A program used to watch for sysctl changes.
+///
+/// [`CgroupSysctl`] programs can be attached to a cgroup and will be called every
+/// time a process inside that cgroup tries to read from or write to a sysctl knob in proc.
+///
+/// # Minimum kernel version
+///
+/// The minimum kernel version required to use this feature is 5.2.
+///
+/// # Examples
+///
+/// ```no_run
+/// # #[derive(Debug, thiserror::Error)]
+/// # enum Error {
+/// #     #[error(transparent)]
+/// #     IO(#[from] std::io::Error),
+/// #     #[error(transparent)]
+/// #     Map(#[from] aya::maps::MapError),
+/// #     #[error(transparent)]
+/// #     Program(#[from] aya::programs::ProgramError),
+/// #     #[error(transparent)]
+/// #     Bpf(#[from] aya::BpfError)
+/// # }
+/// # let mut bpf = aya::Bpf::load(&[])?;
+/// use std::fs::File;
+/// use std::convert::TryInto;
+/// use aya::programs::CgroupSysctl;
+///
+/// let file = File::open("/sys/fs/cgroup/unified")?;
+/// let program: &mut CgroupSysctl = bpf.program_mut("cgroup_sysctl").unwrap().try_into()?;
+/// program.load()?;
+/// program.attach(file)?;
+/// # Ok::<(), Error>(())
+/// ```
+#[derive(Debug)]
+#[doc(alias = "BPF_PROG_TYPE_CGROUP_SYSCTL")]
+pub struct CgroupSysctl {
+    pub(crate) data: ProgramData<CgroupSysctlLink>,
+}
+
+impl CgroupSysctl {
+    /// Loads the program inside the kernel.
+    pub fn load(&mut self) -> Result<(), ProgramError> {
+        load_program(BPF_PROG_TYPE_CGROUP_SYSCTL, &mut self.data)
+    }
+
+    /// Attaches the program to the given cgroup.
+    ///
+    /// The returned value can be used to detach, see [CgroupSysctl::detach].
+    pub fn attach<T: AsRawFd>(&mut self, cgroup: T) -> Result<CgroupSysctlLinkId, ProgramError> {
+        let prog_fd = self.data.fd_or_err()?;
+        let cgroup_fd = cgroup.as_raw_fd();
+
+        let k_ver = kernel_version().unwrap();
+        if k_ver >= (5, 7, 0) {
+            let link_fd = bpf_link_create(prog_fd, cgroup_fd, BPF_CGROUP_SYSCTL, None, 0).map_err(
+                |(_, io_error)| ProgramError::SyscallError {
+                    call: "bpf_link_create".to_owned(),
+                    io_error,
+                },
+            )? as RawFd;
+            self.data
+                .links
+                .insert(CgroupSysctlLink(CgroupSysctlLinkInner::Fd(FdLink::new(
+                    link_fd,
+                ))))
+        } else {
+            bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_SYSCTL).map_err(|(_, io_error)| {
+                ProgramError::SyscallError {
+                    call: "bpf_prog_attach".to_owned(),
+                    io_error,
+                }
+            })?;
+
+            self.data
+                .links
+                .insert(CgroupSysctlLink(CgroupSysctlLinkInner::ProgAttach(
+                    ProgAttachLink::new(prog_fd, cgroup_fd, BPF_CGROUP_SYSCTL),
+                )))
+        }
+    }
+
+    /// Takes ownership of the link referenced by the provided link_id.
+    ///
+    /// The link will be detached on `Drop` and the caller is now responsible
+    /// for managing its lifetime.
+    pub fn forget_link(
+        &mut self,
+        link_id: CgroupSysctlLinkId,
+    ) -> Result<OwnedLink<CgroupSysctlLink>, ProgramError> {
+        Ok(OwnedLink::new(self.data.forget_link(link_id)?))
+    }
+
+    /// Detaches the program.
+    ///
+    /// See [CgroupSysctl::attach].
+    pub fn detach(&mut self, link_id: CgroupSysctlLinkId) -> Result<(), ProgramError> {
+        self.data.links.remove(link_id)
+    }
+}
+
+#[derive(Debug, Hash, Eq, PartialEq)]
+enum CgroupSysctlLinkIdInner {
+    Fd(<FdLink as Link>::Id),
+    ProgAttach(<ProgAttachLink as Link>::Id),
+}
+
+#[derive(Debug)]
+enum CgroupSysctlLinkInner {
+    Fd(FdLink),
+    ProgAttach(ProgAttachLink),
+}
+
+impl Link for CgroupSysctlLinkInner {
+    type Id = CgroupSysctlLinkIdInner;
+
+    fn id(&self) -> Self::Id {
+        match self {
+            CgroupSysctlLinkInner::Fd(fd) => CgroupSysctlLinkIdInner::Fd(fd.id()),
+            CgroupSysctlLinkInner::ProgAttach(p) => CgroupSysctlLinkIdInner::ProgAttach(p.id()),
+        }
+    }
+
+    fn detach(self) -> Result<(), ProgramError> {
+        match self {
+            CgroupSysctlLinkInner::Fd(fd) => fd.detach(),
+            CgroupSysctlLinkInner::ProgAttach(p) => p.detach(),
+        }
+    }
+}
+
+define_link_wrapper!(
+    /// The link used by [CgroupSysctl] programs.
+    CgroupSysctlLink,
+    /// The type returned by [CgroupSysctl::attach]. Can be passed to [CgroupSysctl::detach].
+    CgroupSysctlLinkId,
+    CgroupSysctlLinkInner,
+    CgroupSysctlLinkIdInner
+);

+ 9 - 0
aya/src/programs/mod.rs

@@ -37,6 +37,7 @@
 //! [`Bpf::program_mut`]: crate::Bpf::program_mut
 //! [`maps`]: crate::maps
 mod cgroup_skb;
+mod cgroup_sysctl;
 mod extension;
 mod fentry;
 mod fexit;
@@ -70,6 +71,7 @@ use std::{
 use thiserror::Error;
 
 pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType};
+pub use cgroup_sysctl::CgroupSysctl;
 pub use extension::{Extension, ExtensionError};
 pub use fentry::FEntry;
 pub use fexit::FExit;
@@ -233,6 +235,8 @@ pub enum Program {
     SchedClassifier(SchedClassifier),
     /// A [`CgroupSkb`] program
     CgroupSkb(CgroupSkb),
+    /// A [`CgroupSysctl`] program
+    CgroupSysctl(CgroupSysctl),
     /// A [`LircMode2`] program
     LircMode2(LircMode2),
     /// A [`PerfEvent`] program
@@ -266,6 +270,7 @@ impl Program {
             Program::SockOps(_) => BPF_PROG_TYPE_SOCK_OPS,
             Program::SchedClassifier(_) => BPF_PROG_TYPE_SCHED_CLS,
             Program::CgroupSkb(_) => BPF_PROG_TYPE_CGROUP_SKB,
+            Program::CgroupSysctl(_) => BPF_PROG_TYPE_CGROUP_SYSCTL,
             Program::LircMode2(_) => BPF_PROG_TYPE_LIRC_MODE2,
             Program::PerfEvent(_) => BPF_PROG_TYPE_PERF_EVENT,
             Program::RawTracePoint(_) => BPF_PROG_TYPE_RAW_TRACEPOINT,
@@ -290,6 +295,7 @@ impl Program {
             Program::SockOps(p) => p.data.pin(path),
             Program::SchedClassifier(p) => p.data.pin(path),
             Program::CgroupSkb(p) => p.data.pin(path),
+            Program::CgroupSysctl(p) => p.data.pin(path),
             Program::LircMode2(p) => p.data.pin(path),
             Program::PerfEvent(p) => p.data.pin(path),
             Program::RawTracePoint(p) => p.data.pin(path),
@@ -494,6 +500,7 @@ impl ProgramFd for Program {
             Program::SockOps(p) => p.data.fd,
             Program::SchedClassifier(p) => p.data.fd,
             Program::CgroupSkb(p) => p.data.fd,
+            Program::CgroupSysctl(p) => p.data.fd,
             Program::LircMode2(p) => p.data.fd,
             Program::PerfEvent(p) => p.data.fd,
             Program::RawTracePoint(p) => p.data.fd,
@@ -540,6 +547,7 @@ impl_program_fd!(
     SkSkb,
     SchedClassifier,
     CgroupSkb,
+    CgroupSysctl,
     LircMode2,
     PerfEvent,
     Lsm,
@@ -589,6 +597,7 @@ impl_try_from_program!(
     SockOps,
     SchedClassifier,
     CgroupSkb,
+    CgroupSysctl,
     LircMode2,
     PerfEvent,
     Lsm,

+ 32 - 0
bpf/aya-bpf-macros/src/expand.rs

@@ -215,6 +215,38 @@ impl SchedClassifier {
     }
 }
 
+pub struct CgroupSysctl {
+    item: ItemFn,
+    name: Option<String>,
+}
+
+impl CgroupSysctl {
+    pub fn from_syn(mut args: Args, item: ItemFn) -> Result<CgroupSysctl> {
+        let name = name_arg(&mut args)?;
+
+        Ok(CgroupSysctl { item, name })
+    }
+
+    pub fn expand(&self) -> Result<TokenStream> {
+        let section_name = if let Some(name) = &self.name {
+            format!("cgroup/sysctl/{}", name)
+        } else {
+            ("cgroup/sysctl").to_owned()
+        };
+        let fn_name = &self.item.sig.ident;
+        let item = &self.item;
+        Ok(quote! {
+            #[no_mangle]
+            #[link_section = #section_name]
+            fn #fn_name(ctx: *mut ::aya_bpf::bindings::bpf_sysctl) -> i32 {
+                return #fn_name(::aya_bpf::programs::SysctlContext::new(ctx));
+
+                #item
+            }
+        })
+    }
+}
+
 pub struct CgroupSkb {
     item: ItemFn,
     expected_attach_type: Option<String>,

+ 14 - 3
bpf/aya-bpf-macros/src/lib.rs

@@ -1,9 +1,9 @@
 mod expand;
 
 use expand::{
-    Args, BtfTracePoint, CgroupSkb, FEntry, FExit, Lsm, Map, PerfEvent, Probe, ProbeKind,
-    RawTracePoint, SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint,
-    Xdp,
+    Args, BtfTracePoint, CgroupSkb, CgroupSysctl, FEntry, FExit, Lsm, Map, PerfEvent, Probe,
+    ProbeKind, RawTracePoint, SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter,
+    TracePoint, Xdp,
 };
 use proc_macro::TokenStream;
 use syn::{parse_macro_input, ItemFn, ItemStatic};
@@ -83,6 +83,17 @@ pub fn classifier(attrs: TokenStream, item: TokenStream) -> TokenStream {
         .into()
 }
 
+#[proc_macro_attribute]
+pub fn cgroup_sysctl(attrs: TokenStream, item: TokenStream) -> TokenStream {
+    let args = parse_macro_input!(attrs as Args);
+    let item = parse_macro_input!(item as ItemFn);
+
+    CgroupSysctl::from_syn(args, item)
+        .and_then(|u| u.expand())
+        .unwrap_or_else(|err| err.to_compile_error())
+        .into()
+}
+
 #[proc_macro_attribute]
 pub fn cgroup_skb(attrs: TokenStream, item: TokenStream) -> TokenStream {
     let args = parse_macro_input!(attrs as Args);

+ 2 - 0
bpf/aya-bpf/src/programs/mod.rs

@@ -7,6 +7,7 @@ pub mod raw_tracepoint;
 pub mod sk_buff;
 pub mod sk_msg;
 pub mod sock_ops;
+pub mod sysctl;
 pub mod tp_btf;
 pub mod tracepoint;
 pub mod xdp;
@@ -20,6 +21,7 @@ pub use raw_tracepoint::RawTracePointContext;
 pub use sk_buff::SkBuffContext;
 pub use sk_msg::SkMsgContext;
 pub use sock_ops::SockOpsContext;
+pub use sysctl::SysctlContext;
 pub use tp_btf::BtfTracePointContext;
 pub use tracepoint::TracePointContext;
 pub use xdp::XdpContext;

+ 19 - 0
bpf/aya-bpf/src/programs/sysctl.rs

@@ -0,0 +1,19 @@
+use core::ffi::c_void;
+
+use crate::{bindings::bpf_sysctl, BpfContext};
+
+pub struct SysctlContext {
+    pub sysctl: *mut bpf_sysctl,
+}
+
+impl SysctlContext {
+    pub fn new(sysctl: *mut bpf_sysctl) -> SysctlContext {
+        SysctlContext { sysctl }
+    }
+}
+
+impl BpfContext for SysctlContext {
+    fn as_ptr(&self) -> *mut c_void {
+        self.sysctl as *mut _
+    }
+}