diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 0d219ac1..9ed72d3a 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -22,9 +22,9 @@ use crate::{ MapKind, Object, ParseError, ProgramSection, }, programs::{ - BtfTracePoint, CgroupSkb, CgroupSkbAttachType, CgroupSockAddr, CgroupSockopt, CgroupSysctl, - Extension, FEntry, FExit, KProbe, LircMode2, Lsm, PerfEvent, ProbeKind, Program, - ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkLookup, SkMsg, SkSkb, + BtfTracePoint, CgroupSkb, CgroupSkbAttachType, CgroupSock, CgroupSockAddr, CgroupSockopt, + CgroupSysctl, Extension, FEntry, FExit, KProbe, LircMode2, Lsm, PerfEvent, ProbeKind, + Program, ProgramData, ProgramError, RawTracePoint, SchedClassifier, SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp, }, sys::{ @@ -524,6 +524,12 @@ impl<'a> BpfLoader<'a> { ProgramSection::SkLookup { .. } => Program::SkLookup(SkLookup { data: ProgramData::new(prog_name, obj, btf_fd), }), + ProgramSection::CgroupSock { attach_type, .. } => { + Program::CgroupSock(CgroupSock { + data: ProgramData::new(prog_name, obj, btf_fd), + attach_type: *attach_type, + }) + } } }; (name, program) diff --git a/aya/src/obj/mod.rs b/aya/src/obj/mod.rs index df69ec30..c7852e45 100644 --- a/aya/src/obj/mod.rs +++ b/aya/src/obj/mod.rs @@ -21,7 +21,7 @@ use crate::{ bpf_map_def, generated::{bpf_insn, bpf_map_type::BPF_MAP_TYPE_ARRAY, BPF_F_RDONLY_PROG}, obj::btf::{Btf, BtfError, BtfExt}, - programs::{CgroupSockAddrAttachType, CgroupSockoptAttachType}, + programs::{CgroupSockAddrAttachType, CgroupSockAttachType, CgroupSockoptAttachType}, BpfError, }; use std::slice::from_raw_parts_mut; @@ -188,6 +188,10 @@ pub enum ProgramSection { SkLookup { name: String, }, + CgroupSock { + name: String, + attach_type: CgroupSockAttachType, + }, } impl ProgramSection { @@ -220,6 +224,7 @@ impl ProgramSection { ProgramSection::FExit { name } => name, ProgramSection::Extension { name } => name, ProgramSection::SkLookup { name } => name, + ProgramSection::CgroupSock { name, .. } => name, } } } @@ -279,6 +284,10 @@ impl FromStr for ProgramSection { "cgroup_skb/ingress" => CgroupSkbIngress { name }, "cgroup_skb/egress" => CgroupSkbEgress { name }, "cgroup/skb" => CgroupSkb { name }, + "cgroup/sock" => CgroupSock { + name, + attach_type: CgroupSockAttachType::default(), + }, "cgroup/sysctl" => CgroupSysctl { name }, "cgroup/getsockopt" => CgroupSockopt { name, @@ -300,6 +309,19 @@ impl FromStr for ProgramSection { }); } } + "sock" => CgroupSock { + name, + attach_type: CgroupSockAttachType::default(), + }, + "post_bind4" | "post_bind6" | "sock_create" | "sock_release" => { + if let Ok(attach_type) = CgroupSockAttachType::try_from(name.as_str()) { + CgroupSock { name, attach_type } + } else { + return Err(ParseError::InvalidProgramSection { + section: section.to_owned(), + }); + } + } _ => { if let Ok(attach_type) = CgroupSockAddrAttachType::try_from(name.as_str()) { CgroupSockAddr { name, attach_type } @@ -310,6 +332,22 @@ impl FromStr for ProgramSection { } } }, + "cgroup/post_bind4" => CgroupSock { + name, + attach_type: CgroupSockAttachType::PostBind4, + }, + "cgroup/post_bind6" => CgroupSock { + name, + attach_type: CgroupSockAttachType::PostBind6, + }, + "cgroup/sock_create" => CgroupSock { + name, + attach_type: CgroupSockAttachType::SockCreate, + }, + "cgroup/sock_release" => CgroupSock { + name, + attach_type: CgroupSockAttachType::SockRelease, + }, "cgroup/bind4" => CgroupSockAddr { name, attach_type: CgroupSockAddrAttachType::Bind4, diff --git a/aya/src/programs/cgroup_sock.rs b/aya/src/programs/cgroup_sock.rs new file mode 100644 index 00000000..08b8bf3c --- /dev/null +++ b/aya/src/programs/cgroup_sock.rs @@ -0,0 +1,209 @@ +//! Cgroup socket programs. +use thiserror::Error; + +use crate::generated::bpf_attach_type; +use std::{ + hash::Hash, + os::unix::prelude::{AsRawFd, RawFd}, +}; + +use crate::{ + generated::bpf_prog_type::BPF_PROG_TYPE_CGROUP_SOCK, + programs::{ + define_link_wrapper, load_program, FdLink, Link, OwnedLink, ProgAttachLink, ProgramData, + ProgramError, + }, + sys::{bpf_link_create, bpf_prog_attach, kernel_version}, +}; + +/// A program that is called on socket creation, bind and release. +/// +/// [`CgroupSock`] programs can be used to allow or deny socket creation from +/// within a [cgroup], or they can be used to monitor and gather statistics. +/// +/// [cgroup]: https://man7.org/linux/man-pages/man7/cgroups.7.html +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 4.10. +/// +/// # Examples +/// +/// ```no_run +/// # #[derive(thiserror::Error, Debug)] +/// # 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::{CgroupSock, CgroupSockAttachType}; +/// +/// let file = File::open("/sys/fs/cgroup/unified")?; +/// let bind: &mut CgroupSock = bpf.program_mut("bind").unwrap().try_into()?; +/// bind.load()?; +/// bind.attach(file)?; +/// # Ok::<(), Error>(()) +/// ``` +#[derive(Debug)] +#[doc(alias = "BPF_PROG_TYPE_CGROUP_SOCK")] +pub struct CgroupSock { + pub(crate) data: ProgramData, + pub(crate) attach_type: CgroupSockAttachType, +} + +impl CgroupSock { + /// Loads the program inside the kernel. + pub fn load(&mut self) -> Result<(), ProgramError> { + self.data.expected_attach_type = Some(self.attach_type.into()); + load_program(BPF_PROG_TYPE_CGROUP_SOCK, &mut self.data) + } + + /// Attaches the program to the given cgroup. + /// + /// The returned value can be used to detach, see [CgroupSock::detach]. + pub fn attach(&mut self, cgroup: T) -> Result { + let prog_fd = self.data.fd_or_err()?; + let cgroup_fd = cgroup.as_raw_fd(); + let attach_type = self.data.expected_attach_type.unwrap(); + let k_ver = kernel_version().unwrap(); + if k_ver >= (5, 7, 0) { + let link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, None, 0).map_err( + |(_, io_error)| ProgramError::SyscallError { + call: "bpf_link_create".to_owned(), + io_error, + }, + )? as RawFd; + self.data + .links + .insert(CgroupSockLink(CgroupSockLinkInner::Fd(FdLink::new( + link_fd, + )))) + } else { + bpf_prog_attach(prog_fd, cgroup_fd, attach_type).map_err(|(_, io_error)| { + ProgramError::SyscallError { + call: "bpf_prog_attach".to_owned(), + io_error, + } + })?; + + self.data + .links + .insert(CgroupSockLink(CgroupSockLinkInner::ProgAttach( + ProgAttachLink::new(prog_fd, cgroup_fd, attach_type), + ))) + } + } + + /// 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: CgroupSockLinkId, + ) -> Result, ProgramError> { + Ok(OwnedLink::new(self.data.take_link(link_id)?)) + } + + /// Detaches the program. + /// + /// See [CgroupSock::attach]. + pub fn detach(&mut self, link_id: CgroupSockLinkId) -> Result<(), ProgramError> { + self.data.links.remove(link_id) + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +enum CgroupSockLinkIdInner { + Fd(::Id), + ProgAttach(::Id), +} + +#[derive(Debug)] +enum CgroupSockLinkInner { + Fd(FdLink), + ProgAttach(ProgAttachLink), +} + +impl Link for CgroupSockLinkInner { + type Id = CgroupSockLinkIdInner; + + fn id(&self) -> Self::Id { + match self { + CgroupSockLinkInner::Fd(fd) => CgroupSockLinkIdInner::Fd(fd.id()), + CgroupSockLinkInner::ProgAttach(p) => CgroupSockLinkIdInner::ProgAttach(p.id()), + } + } + + fn detach(self) -> Result<(), ProgramError> { + match self { + CgroupSockLinkInner::Fd(fd) => fd.detach(), + CgroupSockLinkInner::ProgAttach(p) => p.detach(), + } + } +} + +define_link_wrapper!( + /// The link used by [CgroupSock] programs. + CgroupSockLink, + /// The type returned by [CgroupSock::attach]. Can be passed to [CgroupSock::detach]. + CgroupSockLinkId, + CgroupSockLinkInner, + CgroupSockLinkIdInner +); + +/// Defines where to attach a [`CgroupSock`] program. +#[derive(Copy, Clone, Debug)] +pub enum CgroupSockAttachType { + /// Called after the IPv4 bind events. + PostBind4, + /// Called after the IPv6 bind events. + PostBind6, + /// Attach to IPv4 connect events. + SockCreate, + /// Attach to IPv6 connect events. + SockRelease, +} + +impl Default for CgroupSockAttachType { + // The kernel checks for a 0 attach_type and sets it to sock_create + // We may as well do that here also + fn default() -> Self { + CgroupSockAttachType::SockCreate + } +} + +impl From for bpf_attach_type { + fn from(s: CgroupSockAttachType) -> bpf_attach_type { + match s { + CgroupSockAttachType::PostBind4 => bpf_attach_type::BPF_CGROUP_INET4_POST_BIND, + CgroupSockAttachType::PostBind6 => bpf_attach_type::BPF_CGROUP_INET6_POST_BIND, + CgroupSockAttachType::SockCreate => bpf_attach_type::BPF_CGROUP_INET_SOCK_CREATE, + CgroupSockAttachType::SockRelease => bpf_attach_type::BPF_CGROUP_INET_SOCK_RELEASE, + } + } +} + +#[derive(Debug, Error)] +#[error("{0} is not a valid attach type for a CGROUP_SOCK program")] +pub(crate) struct InvalidAttachType(String); + +impl CgroupSockAttachType { + pub(crate) fn try_from(value: &str) -> Result { + match value { + "post_bind4" => Ok(CgroupSockAttachType::PostBind4), + "post_bind6" => Ok(CgroupSockAttachType::PostBind6), + "sock_create" => Ok(CgroupSockAttachType::SockCreate), + "sock_release" => Ok(CgroupSockAttachType::SockRelease), + _ => Err(InvalidAttachType(value.to_owned())), + } + } +} diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 48ab2d5f..5a7138d6 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -37,6 +37,7 @@ //! [`Bpf::program_mut`]: crate::Bpf::program_mut //! [`maps`]: crate::maps pub mod cgroup_skb; +pub mod cgroup_sock; pub mod cgroup_sock_addr; pub mod cgroup_sockopt; pub mod cgroup_sysctl; @@ -74,6 +75,7 @@ use std::{ use thiserror::Error; pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType}; +pub use cgroup_sock::{CgroupSock, CgroupSockAttachType}; pub use cgroup_sock_addr::{CgroupSockAddr, CgroupSockAddrAttachType}; pub use cgroup_sockopt::{CgroupSockopt, CgroupSockoptAttachType}; pub use cgroup_sysctl::CgroupSysctl; @@ -265,6 +267,8 @@ pub enum Program { Extension(Extension), /// A [`SkLookup`] program SkLookup(SkLookup), + /// A [`CgroupSock`] program + CgroupSock(CgroupSock), } impl Program { @@ -294,6 +298,7 @@ impl Program { Program::Extension(_) => BPF_PROG_TYPE_EXT, Program::CgroupSockAddr(_) => BPF_PROG_TYPE_CGROUP_SOCK_ADDR, Program::SkLookup(_) => BPF_PROG_TYPE_SK_LOOKUP, + Program::CgroupSock(_) => BPF_PROG_TYPE_CGROUP_SOCK, } } @@ -322,6 +327,7 @@ impl Program { Program::Extension(p) => p.data.pin(path), Program::CgroupSockAddr(p) => p.data.pin(path), Program::SkLookup(p) => p.data.pin(path), + Program::CgroupSock(p) => p.data.pin(path), } } @@ -350,6 +356,7 @@ impl Program { Program::Extension(p) => p.unload(), Program::CgroupSockAddr(p) => p.unload(), Program::SkLookup(p) => p.unload(), + Program::CgroupSock(p) => p.unload(), } } } @@ -573,6 +580,7 @@ impl ProgramFd for Program { Program::Extension(p) => p.data.fd, Program::CgroupSockAddr(p) => p.data.fd, Program::SkLookup(p) => p.data.fd, + Program::CgroupSock(p) => p.data.fd, } } } @@ -622,7 +630,8 @@ impl_program_unload!( Extension, CgroupSockAddr, SkLookup, - SockOps + SockOps, + CgroupSock, ); macro_rules! impl_program_fd { @@ -665,6 +674,7 @@ impl_program_fd!( Extension, CgroupSockAddr, SkLookup, + CgroupSock, ); macro_rules! impl_try_from_program { @@ -718,6 +728,7 @@ impl_try_from_program!( Extension, CgroupSockAddr, SkLookup, + CgroupSock, ); /// Provides information about a loaded program, like name, id and statistics