From 08a68faf8a8baa344dd6fee64529ad2dcc0a0846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=C2=A0Decina?= Date: Wed, 9 Jun 2021 12:24:31 +0000 Subject: [PATCH] aya: programs: add support for BPF_PROG_TYPE_CGROUP_SKB programs --- aya/src/bpf.rs | 12 +++- aya/src/obj/mod.rs | 9 ++- aya/src/programs/cgroup_skb.rs | 114 +++++++++++++++++++++++++++++++++ aya/src/programs/mod.rs | 13 +++- 4 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 aya/src/programs/cgroup_skb.rs diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 80b1084b..6004c207 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -19,8 +19,8 @@ use crate::{ Object, ParseError, ProgramKind, }, programs::{ - KProbe, ProbeKind, Program, ProgramData, ProgramError, SchedClassifier, SkMsg, SkSkb, - SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp, + CgroupSkb, CgroupSkbAttachType, KProbe, ProbeKind, Program, ProgramData, ProgramError, + SchedClassifier, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter, TracePoint, UProbe, Xdp, }, sys::bpf_map_update_elem_ptr, util::{possible_cpus, POSSIBLE_CPUS}, @@ -187,6 +187,14 @@ impl Bpf { ProgramKind::SchedClassifier => { Program::SchedClassifier(SchedClassifier { data }) } + ProgramKind::CgroupSkbIngress => Program::CgroupSkb(CgroupSkb { + data, + expected_attach_type: Some(CgroupSkbAttachType::Ingress), + }), + ProgramKind::CgroupSkbEgress => Program::CgroupSkb(CgroupSkb { + data, + expected_attach_type: Some(CgroupSkbAttachType::Egress), + }), }; (name, program) diff --git a/aya/src/obj/mod.rs b/aya/src/obj/mod.rs index dda11582..72441019 100644 --- a/aya/src/obj/mod.rs +++ b/aya/src/obj/mod.rs @@ -78,6 +78,8 @@ pub enum ProgramKind { SkSkbStreamVerdict, SockOps, SchedClassifier, + CgroupSkbIngress, + CgroupSkbEgress, } impl FromStr for ProgramKind { @@ -98,6 +100,8 @@ impl FromStr for ProgramKind { "sk_skb/stream_verdict" => SkSkbStreamVerdict, "sockops" => SockOps, "classifier" => SchedClassifier, + "cgroup_skb/ingress" => CgroupSkbIngress, + "cgroup_skb/egress" => CgroupSkbEgress, _ => { return Err(ParseError::InvalidProgramKind { kind: kind.to_string(), @@ -295,7 +299,10 @@ impl Object { | &[ty @ "sk_skb/stream_parser", name] | &[ty @ "sk_skb/stream_verdict", name] | &[ty @ "sockops", name] - | &[ty @ "classifier", name] => { + | &[ty @ "classifier", name] + | &[ty @ "cgroup_skb/ingress", name] + | &[ty @ "cgroup_skb/egress", name] + | &[ty @ "cgroup/skb", name] => { self.programs .insert(name.to_string(), self.parse_program(§ion, ty, name)?); if !section.relocations.is_empty() { diff --git a/aya/src/programs/cgroup_skb.rs b/aya/src/programs/cgroup_skb.rs new file mode 100644 index 00000000..39007658 --- /dev/null +++ b/aya/src/programs/cgroup_skb.rs @@ -0,0 +1,114 @@ +use std::os::unix::prelude::{AsRawFd, RawFd}; + +use crate::{ + generated::{ + bpf_attach_type::{BPF_CGROUP_INET_EGRESS, BPF_CGROUP_INET_INGRESS}, + bpf_prog_type::BPF_PROG_TYPE_CGROUP_SKB, + }, + programs::{load_program, LinkRef, ProgAttachLink, ProgramData, ProgramError}, + sys::{bpf_link_create, bpf_prog_attach, kernel_version}, +}; + +use super::FdLink; + +/// A program used to inspect or filter network activity for a given cgroup. +/// +/// [`CgroupSkb`] programs can be used to inspect or filter network activity +/// generated on all the sockets belonging to a given [cgroup]. +/// +/// [cgroup]: https://man7.org/linux/man-pages/man7/cgroups.7.html +#[derive(Debug)] +pub struct CgroupSkb { + pub(crate) data: ProgramData, + pub(crate) expected_attach_type: Option, +} + +impl CgroupSkb { + /// Loads the program inside the kernel. + /// + /// See also [`Program::load`](crate::programs::Program::load). + pub fn load(&mut self) -> Result<(), ProgramError> { + load_program(BPF_PROG_TYPE_CGROUP_SKB, &mut self.data) + } + + /// Returns the name of the program. + pub fn name(&self) -> String { + self.data.name.to_string() + } + + /// Returns the expected attach type of the program. + /// + /// [`CgroupSkb`] programs can specify the expected attach type in their ELF + /// section name, eg `cgroup_skb/ingress` or `cgroup_skb/egress`. This + /// method returns `None` for programs defined with the generic section + /// `cgroup/skb`. + pub fn expected_attach_type(&self) -> &Option { + &self.expected_attach_type + } + + /// Attaches the program to the given cgroup. + /// + /// # Example + /// + /// ```no_run + /// # #[derive(thiserror::Error, Debug)] + /// # enum Error { + /// # #[error(transparent)] + /// # IO(#[from] std::io::Error), + /// # #[error(transparent)] + /// # Map(#[from] aya::maps::MapError), + /// # #[error(transparent)] + /// # Bpf(#[from] aya::BpfError) + /// # } + /// # let bpf = aya::Bpf::load(&[], None)?; + /// use aya::programs::{CgroupSkb, CgroupSkbAttachType}; + /// + /// let file = File::open("/sys/fs/cgroup/unified")?; + /// let egress: &mut CgroupSkb = + /// self.bpf.program_mut("egress_filter")?.try_into()?; + /// egress.load()?; + /// egress.attach(file, CgroupSkbAttachType::Egress)?; + /// # Ok::<(), Error>(()) + /// ``` + pub fn attach( + &mut self, + cgroup: T, + attach_type: CgroupSkbAttachType, + ) -> Result { + let prog_fd = self.data.fd_or_err()?; + let cgroup_fd = cgroup.as_raw_fd(); + + let attach_type = match attach_type { + CgroupSkbAttachType::Ingress => BPF_CGROUP_INET_INGRESS, + CgroupSkbAttachType::Egress => BPF_CGROUP_INET_EGRESS, + }; + let k_ver = kernel_version().unwrap(); + if k_ver >= (5, 7, 0) { + let link_fd = + bpf_link_create(prog_fd, cgroup_fd, attach_type, 0).map_err(|(_, io_error)| { + ProgramError::SyscallError { + call: "bpf_link_create".to_owned(), + io_error, + } + })? as RawFd; + Ok(self.data.link(FdLink { fd: Some(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, + } + })?; + + Ok(self + .data + .link(ProgAttachLink::new(prog_fd, cgroup_fd, attach_type))) + } + } +} + +#[derive(Copy, Clone, Debug)] +pub enum CgroupSkbAttachType { + Ingress, + Egress, +} diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 9a6efd36..cfb1f8fd 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -44,7 +44,7 @@ //! [`Bpf::program`]: crate::Bpf::program //! [`Bpf::program_mut`]: crate::Bpf::program_mut //! [maps]: crate::maps -mod tc; +mod cgroup_skb; mod kprobe; mod perf_attach; mod probe; @@ -52,6 +52,7 @@ mod sk_msg; mod sk_skb; mod sock_ops; mod socket_filter; +mod tc; mod trace_point; mod uprobe; mod xdp; @@ -60,7 +61,7 @@ use libc::{close, dup, ENOSPC}; use std::{cell::RefCell, cmp, convert::TryFrom, ffi::CStr, io, os::unix::io::RawFd, rc::Rc}; use thiserror::Error; -pub use tc::{SchedClassifier, TcError, TcAttachPoint}; +pub use cgroup_skb::{CgroupSkb, CgroupSkbAttachType}; pub use kprobe::{KProbe, KProbeError}; use perf_attach::*; pub use probe::ProbeKind; @@ -68,6 +69,7 @@ pub use sk_msg::SkMsg; pub use sk_skb::{SkSkb, SkSkbKind}; pub use sock_ops::SockOps; pub use socket_filter::{SocketFilter, SocketFilterError}; +pub use tc::{SchedClassifier, TcAttachPoint, TcError}; pub use trace_point::{TracePoint, TracePointError}; pub use uprobe::{UProbe, UProbeError}; pub use xdp::{Xdp, XdpError, XdpFlags}; @@ -153,6 +155,7 @@ pub enum Program { SkSkb(SkSkb), SockOps(SockOps), SchedClassifier(SchedClassifier), + CgroupSkb(CgroupSkb), } impl Program { @@ -183,6 +186,7 @@ impl Program { Program::SkSkb(_) => BPF_PROG_TYPE_SK_SKB, Program::SockOps(_) => BPF_PROG_TYPE_SOCK_OPS, Program::SchedClassifier(_) => BPF_PROG_TYPE_SCHED_CLS, + Program::CgroupSkb(_) => BPF_PROG_TYPE_CGROUP_SKB, } } @@ -202,6 +206,7 @@ impl Program { Program::SkSkb(p) => &p.data, Program::SockOps(p) => &p.data, Program::SchedClassifier(p) => &p.data, + Program::CgroupSkb(p) => &p.data, } } @@ -216,6 +221,7 @@ impl Program { Program::SkSkb(p) => &mut p.data, Program::SockOps(p) => &mut p.data, Program::SchedClassifier(p) => &mut p.data, + Program::CgroupSkb(p) => &mut p.data, } } } @@ -496,5 +502,6 @@ impl_try_from_program!( SkMsg, SkSkb, SockOps, - SchedClassifier + SchedClassifier, + CgroupSkb );