Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for BPF_PROG_TYPE_CGROUP_SYSCTL #256

Merged
merged 3 commits into from May 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 9 additions & 3 deletions aya/src/bpf.rs
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions aya/src/obj/mod.rs
Expand Up @@ -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 },
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(),
Expand Down
153 changes: 153 additions & 0 deletions 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 changes: 9 additions & 0 deletions aya/src/programs/mod.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -540,6 +547,7 @@ impl_program_fd!(
SkSkb,
SchedClassifier,
CgroupSkb,
CgroupSysctl,
LircMode2,
PerfEvent,
Lsm,
Expand Down Expand Up @@ -589,6 +597,7 @@ impl_try_from_program!(
SockOps,
SchedClassifier,
CgroupSkb,
CgroupSysctl,
LircMode2,
PerfEvent,
Lsm,
Expand Down
32 changes: 32 additions & 0 deletions bpf/aya-bpf-macros/src/expand.rs
Expand Up @@ -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>,
Expand Down
17 changes: 14 additions & 3 deletions 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};
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions bpf/aya-bpf/src/programs/mod.rs
Expand Up @@ -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;
Expand All @@ -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 changes: 19 additions & 0 deletions 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 _
}
}