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