mirror of https://github.com/aya-rs/aya
Add support for BPF_PROG_TYPE_CGROUP_DEVICE
Kernel 4.15 added a new eBPF program that can be used with cgroup v2 to control & observe device access (e.g. read, write, mknod) - `BPF_PROG_TYPE_CGROUP_DEVICE`. We add the ability to create these programs with the `cgroup_device` proc macro which creates the `cgroup/dev` link section. Device details are available to the eBPF program in `DeviceContext`. The userspace representation is provided with the `CgroupDevice` structure. Fixes: #212 Signed-off-by: Milan <milan@mdaverde.com>pull/466/head
parent
9ce1530695
commit
8f1163a400
@ -0,0 +1,135 @@
|
||||
//! 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
|
||||
/// 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)?;
|
||||
/// ```
|
||||
#[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
|
||||
);
|
@ -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 _
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue