From 139f3826383daba9a10dc7aacc079f31d28980fc Mon Sep 17 00:00:00 2001 From: Tuetuopay Date: Sat, 5 Aug 2023 00:15:49 +0200 Subject: [PATCH] aya: add support for map-bound XDP programs Such programs are to be bound to cpumap or devmap instead of the usual network interfaces. --- aya-obj/src/obj.rs | 23 +++++++++++----- aya-obj/src/programs/mod.rs | 2 ++ aya-obj/src/programs/xdp.rs | 24 +++++++++++++++++ aya/src/bpf.rs | 14 +++++++--- aya/src/programs/mod.rs | 1 - aya/src/programs/xdp.rs | 35 ++++++++++++++++++++++--- test/integration-test/src/tests/load.rs | 6 +++-- test/integration-test/src/tests/rbpf.rs | 7 +++-- test/integration-test/src/tests/xdp.rs | 13 +++++++++ 9 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 aya-obj/src/programs/xdp.rs diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index deadeeaf..ce599d3c 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -19,6 +19,7 @@ use crate::{ btf::BtfFeatures, generated::{BPF_CALL, BPF_JMP, BPF_K}, maps::{BtfMap, LegacyMap, Map, MINIMUM_MAP_SIZE}, + programs::XdpAttachType, relocation::*, util::HashMap, }; @@ -204,8 +205,6 @@ pub struct Function { /// - `struct_ops+` /// - `fmod_ret+`, `fmod_ret.s+` /// - `iter+`, `iter.s+` -/// - `xdp.frags/cpumap`, `xdp/cpumap` -/// - `xdp.frags/devmap`, `xdp/devmap` #[derive(Debug, Clone)] #[allow(missing_docs)] pub enum ProgramSection { @@ -221,6 +220,7 @@ pub enum ProgramSection { SocketFilter, Xdp { frags: bool, + attach_type: XdpAttachType, }, SkMsg, SkSkbStreamParser, @@ -283,8 +283,19 @@ impl FromStr for ProgramSection { "uprobe.s" => UProbe { sleepable: true }, "uretprobe" => URetProbe { sleepable: false }, "uretprobe.s" => URetProbe { sleepable: true }, - "xdp" => Xdp { frags: false }, - "xdp.frags" => Xdp { frags: true }, + "xdp" | "xdp.frags" => Xdp { + frags: kind == "xdp.frags", + attach_type: match pieces.next() { + None => XdpAttachType::Interface, + Some("cpumap") => XdpAttachType::CpuMap, + Some("devmap") => XdpAttachType::DevMap, + Some(_) => { + return Err(ParseError::InvalidProgramSection { + section: section.to_owned(), + }) + } + }, + }, "tp_btf" => BtfTracePoint, "tracepoint" | "tp" => TracePoint, "socket" => SocketFilter, @@ -2012,7 +2023,7 @@ mod tests { assert_matches!( obj.parse_section(fake_section( BpfSectionKind::Program, - "xdp/foo", + "xdp", bytes_of(&fake_ins()), None )), @@ -2035,7 +2046,7 @@ mod tests { assert_matches!( obj.parse_section(fake_section( BpfSectionKind::Program, - "xdp.frags/foo", + "xdp.frags", bytes_of(&fake_ins()), None )), diff --git a/aya-obj/src/programs/mod.rs b/aya-obj/src/programs/mod.rs index 4f76211a..6b66b005 100644 --- a/aya-obj/src/programs/mod.rs +++ b/aya-obj/src/programs/mod.rs @@ -3,7 +3,9 @@ pub mod cgroup_sock; pub mod cgroup_sock_addr; pub mod cgroup_sockopt; +pub mod xdp; pub use cgroup_sock::CgroupSockAttachType; pub use cgroup_sock_addr::CgroupSockAddrAttachType; pub use cgroup_sockopt::CgroupSockoptAttachType; +pub use xdp::XdpAttachType; diff --git a/aya-obj/src/programs/xdp.rs b/aya-obj/src/programs/xdp.rs new file mode 100644 index 00000000..17fab6ab --- /dev/null +++ b/aya-obj/src/programs/xdp.rs @@ -0,0 +1,24 @@ +//! XDP programs. + +use crate::generated::bpf_attach_type; + +/// Defines where to attach an `XDP` program. +#[derive(Copy, Clone, Debug)] +pub enum XdpAttachType { + /// Attach to a network interface. + Interface, + /// Attach to a cpumap. Requires kernel 5.9 or later. + CpuMap, + /// Attach to a devmap. Requires kernel 5.8 or later. + DevMap, +} + +impl From for bpf_attach_type { + fn from(value: XdpAttachType) -> Self { + match value { + XdpAttachType::Interface => bpf_attach_type::BPF_XDP, + XdpAttachType::CpuMap => bpf_attach_type::BPF_XDP_CPUMAP, + XdpAttachType::DevMap => bpf_attach_type::BPF_XDP_DEVMAP, + } + } +} diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index 999271ec..0140886d 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -412,7 +412,10 @@ impl<'a> BpfLoader<'a> { | ProgramSection::URetProbe { sleepable: _ } | ProgramSection::TracePoint | ProgramSection::SocketFilter - | ProgramSection::Xdp { frags: _ } + | ProgramSection::Xdp { + frags: _, + attach_type: _, + } | ProgramSection::SkMsg | ProgramSection::SkSkbStreamParser | ProgramSection::SkSkbStreamVerdict @@ -556,13 +559,18 @@ impl<'a> BpfLoader<'a> { ProgramSection::SocketFilter => Program::SocketFilter(SocketFilter { data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), }), - ProgramSection::Xdp { frags, .. } => { + ProgramSection::Xdp { + frags, attach_type, .. + } => { let mut data = ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); if *frags { data.flags = BPF_F_XDP_HAS_FRAGS; } - Program::Xdp(Xdp { data }) + Program::Xdp(Xdp { + data, + attach_type: *attach_type, + }) } ProgramSection::SkMsg => Program::SkMsg(SkMsg { data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 0952a348..bb9622fe 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -879,7 +879,6 @@ macro_rules! impl_from_pin { impl_from_pin!( TracePoint, SocketFilter, - Xdp, SkMsg, CgroupSysctl, LircMode2, diff --git a/aya/src/programs/xdp.rs b/aya/src/programs/xdp.rs index 89574328..481d95d9 100644 --- a/aya/src/programs/xdp.rs +++ b/aya/src/programs/xdp.rs @@ -12,18 +12,21 @@ use std::{ hash::Hash, io, os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd}, + path::Path, }; use thiserror::Error; use crate::{ generated::{ - bpf_attach_type, bpf_link_type, bpf_prog_type, XDP_FLAGS_DRV_MODE, XDP_FLAGS_HW_MODE, - XDP_FLAGS_REPLACE, XDP_FLAGS_SKB_MODE, XDP_FLAGS_UPDATE_IF_NOEXIST, + bpf_link_type, bpf_prog_type, XDP_FLAGS_DRV_MODE, XDP_FLAGS_HW_MODE, XDP_FLAGS_REPLACE, + XDP_FLAGS_SKB_MODE, XDP_FLAGS_UPDATE_IF_NOEXIST, }, + obj::programs::XdpAttachType, programs::{ define_link_wrapper, load_program, FdLink, Link, LinkError, ProgramData, ProgramError, }, sys::{bpf_link_create, bpf_link_get_info_by_fd, bpf_link_update, netlink_set_xdp_fd}, + VerifierLogLevel, }; /// The type returned when attaching an [`Xdp`] program fails on kernels `< 5.9`. @@ -80,12 +83,13 @@ bitflags::bitflags! { #[doc(alias = "BPF_PROG_TYPE_XDP")] pub struct Xdp { pub(crate) data: ProgramData, + pub(crate) attach_type: XdpAttachType, } impl Xdp { /// Loads the program inside the kernel. pub fn load(&mut self) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(bpf_attach_type::BPF_XDP); + self.data.expected_attach_type = Some(self.attach_type.into()); load_program(bpf_prog_type::BPF_PROG_TYPE_XDP, &mut self.data) } @@ -133,10 +137,18 @@ impl Xdp { let prog_fd = prog_fd.as_fd(); if KernelVersion::current().unwrap() >= KernelVersion::new(5, 9, 0) { + // Unwrap safety: the function starts with `self.fd()?` that will succeed if and only + // if the program has been loaded, i.e. there is an fd. We get one by: + // - Using `Xdp::from_pin` that sets `expected_attach_type` + // - Calling `Xdp::attach` that sets `expected_attach_type`, as geting an `Xdp` + // instance trhough `Xdp:try_from(Program)` does not set any fd. + // So, in all cases where we have an fd, we have an expected_attach_type. Thus, if we + // reach this point, expected_attach_type is guaranteed to be Some(_). + let attach_type = self.data.expected_attach_type.unwrap(); let link_fd = bpf_link_create( prog_fd, LinkTarget::IfIndex(if_index), - bpf_attach_type::BPF_XDP, + attach_type, None, flags.bits(), ) @@ -163,6 +175,21 @@ impl Xdp { } } + /// Creates a program from a pinned entry on a bpffs. + /// + /// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + pub fn from_pin>( + path: P, + attach_type: XdpAttachType, + ) -> Result { + let mut data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + data.expected_attach_type = Some(attach_type.into()); + Ok(Self { data, attach_type }) + } + /// Detaches the program. /// /// See [Xdp::attach]. diff --git a/test/integration-test/src/tests/load.rs b/test/integration-test/src/tests/load.rs index 4c99a008..d444b95d 100644 --- a/test/integration-test/src/tests/load.rs +++ b/test/integration-test/src/tests/load.rs @@ -9,6 +9,7 @@ use aya::{ util::KernelVersion, Bpf, }; +use aya_obj::programs::XdpAttachType; const MAX_RETRIES: usize = 100; const RETRY_DURATION: time::Duration = time::Duration::from_millis(10); @@ -283,7 +284,7 @@ fn pin_lifecycle() { // 2. Load program from bpffs but don't attach it { - let _ = Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog").unwrap(); + let _ = Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog", XdpAttachType::Interface).unwrap(); } // should still be loaded since prog was pinned @@ -291,7 +292,8 @@ fn pin_lifecycle() { // 3. Load program from bpffs and attach { - let mut prog = Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog").unwrap(); + let mut prog = + Xdp::from_pin("/sys/fs/bpf/aya-xdp-test-prog", XdpAttachType::Interface).unwrap(); let link_id = prog.attach("lo", XdpFlags::default()).unwrap(); let link = prog.take_link(link_id).unwrap(); let fd_link: FdLink = link.try_into().unwrap(); diff --git a/test/integration-test/src/tests/rbpf.rs b/test/integration-test/src/tests/rbpf.rs index 9caab94c..3d2c0c97 100644 --- a/test/integration-test/src/tests/rbpf.rs +++ b/test/integration-test/src/tests/rbpf.rs @@ -2,7 +2,7 @@ use core::{mem::size_of, ptr::null_mut, slice::from_raw_parts}; use std::collections::HashMap; use assert_matches::assert_matches; -use aya_obj::{generated::bpf_insn, Object, ProgramSection}; +use aya_obj::{generated::bpf_insn, programs::XdpAttachType, Object, ProgramSection}; #[test] fn run_with_rbpf() { @@ -11,7 +11,10 @@ fn run_with_rbpf() { assert_eq!(object.programs.len(), 1); assert_matches!( object.programs["pass"].section, - ProgramSection::Xdp { frags: true } + ProgramSection::Xdp { + frags: true, + attach_type: XdpAttachType::Interface + } ); let instructions = &object diff --git a/test/integration-test/src/tests/xdp.rs b/test/integration-test/src/tests/xdp.rs index 0c2f56da..566ca5e5 100644 --- a/test/integration-test/src/tests/xdp.rs +++ b/test/integration-test/src/tests/xdp.rs @@ -1,3 +1,4 @@ +use aya::Bpf; use object::{Object, ObjectSection, ObjectSymbol, SymbolSection}; #[test] @@ -33,3 +34,15 @@ fn ensure_symbol(obj_file: &object::File, sec_name: &str, sym_name: &str) { "symbol not found. available symbols in section: {syms:?}" ); } + +#[test] +fn map_load() { + let bpf = Bpf::load(crate::XDP_SEC).unwrap(); + + bpf.program("xdp_plain").unwrap(); + bpf.program("xdp_frags").unwrap(); + bpf.program("xdp_cpumap").unwrap(); + bpf.program("xdp_devmap").unwrap(); + bpf.program("xdp_frags_cpumap").unwrap(); + bpf.program("xdp_frags_devmap").unwrap(); +}