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
pull/259/head
Kenjiro Nakayama 3 years ago committed by GitHub
parent 2fca4aee4e
commit f721021a0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

@ -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(),

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

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

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

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

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

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