Skip to main content

aya_friday/programs/
cgroup_sysctl.rs

1//! Cgroup sysctl programs.
2
3use std::{hash::Hash, os::fd::AsFd};
4
5use aya_obj::generated::{
6    bpf_attach_type::BPF_CGROUP_SYSCTL, bpf_prog_type::BPF_PROG_TYPE_CGROUP_SYSCTL,
7};
8
9use crate::{
10    programs::{
11        CgroupAttachMode, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, ProgramType,
12        define_link_wrapper, id_as_key, load_program_with_attach_type,
13    },
14    sys::{LinkTarget, SyscallError, bpf_link_create},
15    util::KernelVersion,
16};
17
18/// A program used to watch for sysctl changes.
19///
20/// [`CgroupSysctl`] programs can be attached to a cgroup and will be called every
21/// time a process inside that cgroup tries to read from or write to a sysctl knob in proc.
22///
23/// # Minimum kernel version
24///
25/// The minimum kernel version required to use this feature is 5.2.
26///
27/// # Examples
28///
29/// ```no_run
30/// # #[derive(Debug, thiserror::Error)]
31/// # enum Error {
32/// #     #[error(transparent)]
33/// #     IO(#[from] std::io::Error),
34/// #     #[error(transparent)]
35/// #     Map(#[from] aya::maps::MapError),
36/// #     #[error(transparent)]
37/// #     Program(#[from] aya::programs::ProgramError),
38/// #     #[error(transparent)]
39/// #     Ebpf(#[from] aya::EbpfError)
40/// # }
41/// # let mut bpf = aya::Ebpf::load(&[])?;
42/// use std::fs::File;
43/// use aya::programs::{CgroupAttachMode, CgroupSysctl};
44///
45/// let file = File::open("/sys/fs/cgroup/unified")?;
46/// let program: &mut CgroupSysctl = bpf.program_mut("cgroup_sysctl").unwrap().try_into()?;
47/// program.load()?;
48/// program.attach(file, CgroupAttachMode::Single)?;
49/// # Ok::<(), Error>(())
50/// ```
51#[derive(Debug)]
52#[doc(alias = "BPF_PROG_TYPE_CGROUP_SYSCTL")]
53pub struct CgroupSysctl {
54    pub(crate) data: ProgramData<CgroupSysctlLink>,
55}
56
57impl CgroupSysctl {
58    /// The type of the program according to the kernel.
59    pub const PROGRAM_TYPE: ProgramType = ProgramType::CgroupSysctl;
60
61    /// Loads the program inside the kernel.
62    pub fn load(&mut self) -> Result<(), ProgramError> {
63        let Self { data } = self;
64        load_program_with_attach_type(BPF_PROG_TYPE_CGROUP_SYSCTL, BPF_CGROUP_SYSCTL, data)
65    }
66
67    /// Attaches the program to the given cgroup.
68    ///
69    /// The returned value can be used to detach, see [`CgroupSysctl::detach`].
70    pub fn attach<T: AsFd>(
71        &mut self,
72        cgroup: T,
73        mode: CgroupAttachMode,
74    ) -> Result<CgroupSysctlLinkId, ProgramError> {
75        let prog_fd = self.fd()?;
76        let prog_fd = prog_fd.as_fd();
77        let cgroup_fd = cgroup.as_fd();
78        let attach_type = BPF_CGROUP_SYSCTL;
79        if KernelVersion::at_least(5, 7, 0) {
80            let link_fd = bpf_link_create(
81                prog_fd,
82                LinkTarget::Fd(cgroup_fd),
83                attach_type,
84                mode.into(),
85                None,
86            )
87            .map_err(|io_error| SyscallError {
88                call: "bpf_link_create",
89                io_error,
90            })?;
91            self.data
92                .links
93                .insert(CgroupSysctlLink::new(CgroupSysctlLinkInner::Fd(
94                    FdLink::new(link_fd),
95                )))
96        } else {
97            let link = ProgAttachLink::attach(prog_fd, cgroup_fd, attach_type, mode)?;
98
99            self.data
100                .links
101                .insert(CgroupSysctlLink::new(CgroupSysctlLinkInner::ProgAttach(
102                    link,
103                )))
104        }
105    }
106}
107
108#[derive(Debug, Hash, Eq, PartialEq)]
109enum CgroupSysctlLinkIdInner {
110    Fd(<FdLink as Link>::Id),
111    ProgAttach(<ProgAttachLink as Link>::Id),
112}
113
114#[derive(Debug)]
115enum CgroupSysctlLinkInner {
116    Fd(FdLink),
117    ProgAttach(ProgAttachLink),
118}
119
120impl Link for CgroupSysctlLinkInner {
121    type Id = CgroupSysctlLinkIdInner;
122
123    fn id(&self) -> Self::Id {
124        match self {
125            Self::Fd(fd) => CgroupSysctlLinkIdInner::Fd(fd.id()),
126            Self::ProgAttach(p) => CgroupSysctlLinkIdInner::ProgAttach(p.id()),
127        }
128    }
129
130    fn detach(self) -> Result<(), ProgramError> {
131        match self {
132            Self::Fd(fd) => fd.detach(),
133            Self::ProgAttach(p) => p.detach(),
134        }
135    }
136}
137
138id_as_key!(CgroupSysctlLinkInner, CgroupSysctlLinkIdInner);
139
140define_link_wrapper!(
141    CgroupSysctlLink,
142    CgroupSysctlLinkId,
143    CgroupSysctlLinkInner,
144    CgroupSysctlLinkIdInner,
145    CgroupSysctl,
146);