Merge pull request #466 from bpfdeploy-io/ml/cgroup-device

Add support for BPF_PROG_TYPE_CGROUP_DEVICE
pull/470/head
Alessandro Decina 2 years ago committed by GitHub
commit d1919a83ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -820,6 +820,37 @@ impl SkLookup {
} }
} }
pub struct CgroupDevice {
item: ItemFn,
name: Option<String>,
}
impl CgroupDevice {
pub fn from_syn(mut args: Args, item: ItemFn) -> Result<Self> {
let name = name_arg(&mut args)?;
Ok(CgroupDevice { item, name })
}
pub fn expand(&self) -> Result<TokenStream> {
let section_name = if let Some(name) = &self.name {
format!("cgroup/dev/{name}")
} else {
("cgroup/dev").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_cgroup_dev_ctx) -> i32 {
return #fn_name(::aya_bpf::programs::DeviceContext::new(ctx));
#item
}
})
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use syn::parse_quote; use syn::parse_quote;
@ -893,4 +924,21 @@ mod tests {
.to_string() .to_string()
.contains("[link_section = \"cgroup_skb/egress\"]")); .contains("[link_section = \"cgroup_skb/egress\"]"));
} }
#[test]
fn cgroup_device_no_name() {
let prog = CgroupDevice::from_syn(
parse_quote!(),
parse_quote!(
fn foo(ctx: DeviceContext) -> i32 {
0
}
),
)
.unwrap();
let stream = prog.expand().unwrap();
assert!(stream
.to_string()
.contains("[link_section = \"cgroup/dev\"]"));
}
} }

@ -1,9 +1,10 @@
mod expand; mod expand;
use expand::{ use expand::{
Args, BtfTracePoint, CgroupSkb, CgroupSock, CgroupSockAddr, CgroupSockopt, CgroupSysctl, Args, BtfTracePoint, CgroupDevice, CgroupSkb, CgroupSock, CgroupSockAddr, CgroupSockopt,
FEntry, FExit, Lsm, Map, PerfEvent, Probe, ProbeKind, RawTracePoint, SchedClassifier, SkLookup, CgroupSysctl, FEntry, FExit, Lsm, Map, PerfEvent, Probe, ProbeKind, RawTracePoint,
SkMsg, SkSkb, SkSkbKind, SockAddrArgs, SockOps, SocketFilter, SockoptArgs, TracePoint, Xdp, SchedClassifier, SkLookup, SkMsg, SkSkb, SkSkbKind, SockAddrArgs, SockOps, SocketFilter,
SockoptArgs, TracePoint, Xdp,
}; };
use proc_macro::TokenStream; use proc_macro::TokenStream;
use syn::{parse_macro_input, ItemFn, ItemStatic}; use syn::{parse_macro_input, ItemFn, ItemStatic};
@ -507,3 +508,35 @@ pub fn sk_lookup(attrs: TokenStream, item: TokenStream) -> TokenStream {
.unwrap_or_else(|err| err.to_compile_error()) .unwrap_or_else(|err| err.to_compile_error())
.into() .into()
} }
/// Marks a function as a cgroup device eBPF program that can be attached to a
/// cgroup.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 4.15.
///
/// # Examples
///
/// ```no_run
/// use aya_bpf::{
/// macros::cgroup_device,
/// programs::DeviceContext,
/// };
///
/// #[cgroup_device(name="cgroup_dev")]
/// pub fn cgroup_dev(ctx: DeviceContext) -> i32 {
/// // Reject all device access
/// return 0;
/// }
/// ```
#[proc_macro_attribute]
pub fn cgroup_device(attrs: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attrs as Args);
let item = parse_macro_input!(item as ItemFn);
CgroupDevice::from_syn(args, item)
.and_then(|u| u.expand())
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

@ -22,10 +22,10 @@ use crate::{
MapKind, Object, ParseError, ProgramSection, MapKind, Object, ParseError, ProgramSection,
}, },
programs::{ programs::{
BtfTracePoint, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr, CgroupSockopt, BtfTracePoint, CgroupDevice, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr,
CgroupSysctl, Extension, FEntry, FExit, KProbe, LircMode2, Lsm, PerfEvent, ProbeKind, CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, KProbe, LircMode2, Lsm, PerfEvent,
Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkLookup, SkMsg, SkSkb, ProbeKind, Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkLookup,
SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp,
}, },
sys::{ sys::{
bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_btf_datasec_supported, bpf_load_btf, bpf_map_freeze, bpf_map_update_elem_ptr, is_btf_datasec_supported,
@ -633,6 +633,11 @@ impl<'a> BpfLoader<'a> {
attach_type: *attach_type, attach_type: *attach_type,
}) })
} }
ProgramSection::CgroupDevice { .. } => {
Program::CgroupDevice(CgroupDevice {
data: ProgramData::new(prog_name, obj, btf_fd, verifier_log_level),
})
}
} }
}; };
(name, program) (name, program)

@ -293,6 +293,9 @@ pub enum ProgramSection {
name: String, name: String,
attach_type: CgroupSockAttachType, attach_type: CgroupSockAttachType,
}, },
CgroupDevice {
name: String,
},
} }
impl ProgramSection { impl ProgramSection {
@ -326,6 +329,7 @@ impl ProgramSection {
ProgramSection::Extension { name } => name, ProgramSection::Extension { name } => name,
ProgramSection::SkLookup { name } => name, ProgramSection::SkLookup { name } => name,
ProgramSection::CgroupSock { name, .. } => name, ProgramSection::CgroupSock { name, .. } => name,
ProgramSection::CgroupDevice { name } => name,
} }
} }
} }
@ -390,6 +394,7 @@ impl FromStr for ProgramSection {
attach_type: CgroupSockAttachType::default(), attach_type: CgroupSockAttachType::default(),
}, },
"cgroup/sysctl" => CgroupSysctl { name }, "cgroup/sysctl" => CgroupSysctl { name },
"cgroup/dev" => CgroupDevice { name },
"cgroup/getsockopt" => CgroupSockopt { "cgroup/getsockopt" => CgroupSockopt {
name, name,
attach_type: CgroupSockoptAttachType::Get, attach_type: CgroupSockoptAttachType::Get,
@ -401,6 +406,7 @@ impl FromStr for ProgramSection {
"cgroup" => match &*name { "cgroup" => match &*name {
"skb" => CgroupSkb { name }, "skb" => CgroupSkb { name },
"sysctl" => CgroupSysctl { name }, "sysctl" => CgroupSysctl { name },
"dev" => CgroupDevice { name },
"getsockopt" | "setsockopt" => { "getsockopt" | "setsockopt" => {
if let Ok(attach_type) = CgroupSockoptAttachType::try_from(name.as_str()) { if let Ok(attach_type) = CgroupSockoptAttachType::try_from(name.as_str()) {
CgroupSockopt { name, attach_type } CgroupSockopt { name, attach_type }

@ -0,0 +1,149 @@
//! Cgroup device programs.
use std::os::unix::prelude::{AsRawFd, RawFd};
use crate::{
generated::{bpf_attach_type::BPF_CGROUP_DEVICE, bpf_prog_type::BPF_PROG_TYPE_CGROUP_DEVICE},
programs::{
define_link_wrapper, load_program, FdLink, Link, ProgAttachLink, ProgramData, ProgramError,
},
sys::{bpf_link_create, bpf_prog_attach, kernel_version},
};
/// A program used to watch or prevent device interaction from a cgroup.
///
/// [`CgroupDevice`] programs can be attached to a cgroup and will be called every
/// time a process inside that cgroup tries to access (e.g. read, write, mknod)
/// a device (identified through its major and minor number). See
/// [mknod](https://man7.org/linux/man-pages/man2/mknod.2.html) as a starting point.
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is [4.15](https://github.com/torvalds/linux/commit/ebc614f687369f9df99828572b1d85a7c2de3d92).
///
/// # 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 aya::programs::CgroupDevice;
///
/// let cgroup = std::fs::File::open("/sys/fs/cgroup/unified")?;
/// let program: &mut CgroupDevice = bpf.program_mut("cgroup_dev").unwrap().try_into()?;
/// program.load()?;
/// program.attach(cgroup)?;
/// # Ok::<(), Error>(())
/// ```
#[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_CGROUP_DEVICE")]
pub struct CgroupDevice {
pub(crate) data: ProgramData<CgroupDeviceLink>,
}
impl CgroupDevice {
/// Loads the program inside the kernel
pub fn load(&mut self) -> Result<(), ProgramError> {
load_program(BPF_PROG_TYPE_CGROUP_DEVICE, &mut self.data)
}
/// Attaches the program to the given cgroup.
///
/// The returned value can be used to detach, see [CgroupDevice::detach]
pub fn attach<T: AsRawFd>(&mut self, cgroup: T) -> Result<CgroupDeviceLinkId, 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_DEVICE, None, 0).map_err(
|(_, io_error)| ProgramError::SyscallError {
call: "bpf_link_create".to_owned(),
io_error,
},
)? as RawFd;
self.data
.links
.insert(CgroupDeviceLink(CgroupDeviceLinkInner::Fd(FdLink::new(
link_fd,
))))
} else {
bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_DEVICE).map_err(|(_, io_error)| {
ProgramError::SyscallError {
call: "bpf_prog_attach".to_owned(),
io_error,
}
})?;
self.data
.links
.insert(CgroupDeviceLink(CgroupDeviceLinkInner::ProgAttach(
ProgAttachLink::new(prog_fd, cgroup_fd, BPF_CGROUP_DEVICE),
)))
}
}
/// 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 take_link(
&mut self,
link_id: CgroupDeviceLinkId,
) -> Result<CgroupDeviceLink, ProgramError> {
self.data.take_link(link_id)
}
/// Detaches the program
///
/// See [CgroupDevice::attach].
pub fn detach(&mut self, link_id: CgroupDeviceLinkId) -> Result<(), ProgramError> {
self.data.links.remove(link_id)
}
}
#[derive(Debug, Hash, Eq, PartialEq)]
enum CgroupDeviceLinkIdInner {
Fd(<FdLink as Link>::Id),
ProgAttach(<ProgAttachLink as Link>::Id),
}
#[derive(Debug)]
enum CgroupDeviceLinkInner {
Fd(FdLink),
ProgAttach(ProgAttachLink),
}
impl Link for CgroupDeviceLinkInner {
type Id = CgroupDeviceLinkIdInner;
fn id(&self) -> Self::Id {
match self {
CgroupDeviceLinkInner::Fd(fd) => CgroupDeviceLinkIdInner::Fd(fd.id()),
CgroupDeviceLinkInner::ProgAttach(p) => CgroupDeviceLinkIdInner::ProgAttach(p.id()),
}
}
fn detach(self) -> Result<(), ProgramError> {
match self {
CgroupDeviceLinkInner::Fd(fd) => fd.detach(),
CgroupDeviceLinkInner::ProgAttach(p) => p.detach(),
}
}
}
define_link_wrapper!(
/// The link used by [CgroupDevice] programs.
CgroupDeviceLink,
/// The type returned by [CgroupDevice::attach]. Can be passed to [CgroupDevice::detach].
CgroupDeviceLinkId,
CgroupDeviceLinkInner,
CgroupDeviceLinkIdInner
);

@ -35,6 +35,7 @@
//! [`Bpf::program`]: crate::Bpf::program //! [`Bpf::program`]: crate::Bpf::program
//! [`Bpf::program_mut`]: crate::Bpf::program_mut //! [`Bpf::program_mut`]: crate::Bpf::program_mut
//! [`maps`]: crate::maps //! [`maps`]: crate::maps
pub mod cgroup_device;
pub mod cgroup_skb; pub mod cgroup_skb;
pub mod cgroup_sock; pub mod cgroup_sock;
pub mod cgroup_sock_addr; pub mod cgroup_sock_addr;
@ -72,6 +73,7 @@ use std::{
}; };
use thiserror::Error; use thiserror::Error;
pub use cgroup_device::CgroupDevice;
pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType}; pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType};
pub use cgroup_sock::{CgroupSock, CgroupSockAttachType}; pub use cgroup_sock::{CgroupSock, CgroupSockAttachType};
pub use cgroup_sock_addr::{CgroupSockAddr, CgroupSockAddrAttachType}; pub use cgroup_sock_addr::{CgroupSockAddr, CgroupSockAddrAttachType};
@ -265,6 +267,8 @@ pub enum Program {
SkLookup(SkLookup), SkLookup(SkLookup),
/// A [`CgroupSock`] program /// A [`CgroupSock`] program
CgroupSock(CgroupSock), CgroupSock(CgroupSock),
/// A [`CgroupDevice`] program
CgroupDevice(CgroupDevice),
} }
impl Program { impl Program {
@ -295,6 +299,7 @@ impl Program {
Program::CgroupSockAddr(_) => BPF_PROG_TYPE_CGROUP_SOCK_ADDR, Program::CgroupSockAddr(_) => BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
Program::SkLookup(_) => BPF_PROG_TYPE_SK_LOOKUP, Program::SkLookup(_) => BPF_PROG_TYPE_SK_LOOKUP,
Program::CgroupSock(_) => BPF_PROG_TYPE_CGROUP_SOCK, Program::CgroupSock(_) => BPF_PROG_TYPE_CGROUP_SOCK,
Program::CgroupDevice(_) => BPF_PROG_TYPE_CGROUP_DEVICE,
} }
} }
@ -324,6 +329,7 @@ impl Program {
Program::CgroupSockAddr(p) => p.pin(path), Program::CgroupSockAddr(p) => p.pin(path),
Program::SkLookup(p) => p.pin(path), Program::SkLookup(p) => p.pin(path),
Program::CgroupSock(p) => p.pin(path), Program::CgroupSock(p) => p.pin(path),
Program::CgroupDevice(p) => p.pin(path),
} }
} }
@ -353,6 +359,7 @@ impl Program {
Program::CgroupSockAddr(p) => p.unload(), Program::CgroupSockAddr(p) => p.unload(),
Program::SkLookup(p) => p.unload(), Program::SkLookup(p) => p.unload(),
Program::CgroupSock(p) => p.unload(), Program::CgroupSock(p) => p.unload(),
Program::CgroupDevice(p) => p.unload(),
} }
} }
@ -385,6 +392,7 @@ impl Program {
Program::CgroupSockAddr(p) => p.fd(), Program::CgroupSockAddr(p) => p.fd(),
Program::SkLookup(p) => p.fd(), Program::SkLookup(p) => p.fd(),
Program::CgroupSock(p) => p.fd(), Program::CgroupSock(p) => p.fd(),
Program::CgroupDevice(p) => p.fd(),
} }
} }
} }
@ -637,6 +645,7 @@ impl_program_unload!(
SkLookup, SkLookup,
SockOps, SockOps,
CgroupSock, CgroupSock,
CgroupDevice,
); );
macro_rules! impl_fd { macro_rules! impl_fd {
@ -676,6 +685,7 @@ impl_fd!(
SkLookup, SkLookup,
SockOps, SockOps,
CgroupSock, CgroupSock,
CgroupDevice,
); );
macro_rules! impl_program_pin{ macro_rules! impl_program_pin{
@ -720,6 +730,7 @@ impl_program_pin!(
SkLookup, SkLookup,
SockOps, SockOps,
CgroupSock, CgroupSock,
CgroupDevice,
); );
macro_rules! impl_try_from_program { macro_rules! impl_try_from_program {
@ -774,6 +785,7 @@ impl_try_from_program!(
CgroupSockAddr, CgroupSockAddr,
SkLookup, SkLookup,
CgroupSock, CgroupSock,
CgroupDevice,
); );
/// Provides information about a loaded program, like name, id and statistics /// Provides information about a loaded program, like name, id and statistics

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

@ -1,3 +1,4 @@
pub mod device;
pub mod fentry; pub mod fentry;
pub mod fexit; pub mod fexit;
pub mod lsm; pub mod lsm;
@ -17,6 +18,7 @@ pub mod tp_btf;
pub mod tracepoint; pub mod tracepoint;
pub mod xdp; pub mod xdp;
pub use device::DeviceContext;
pub use fentry::FEntryContext; pub use fentry::FEntryContext;
pub use fexit::FExitContext; pub use fexit::FExitContext;
pub use lsm::LsmContext; pub use lsm::LsmContext;

Loading…
Cancel
Save