aya: add support for map-bound XDP programs

Such programs are to be bound to cpumap or devmap instead of the usual
network interfaces.
reviewable/pr527/r18
Tuetuopay 1 year ago
parent 4452364c41
commit 139f382638

@ -19,6 +19,7 @@ use crate::{
btf::BtfFeatures, btf::BtfFeatures,
generated::{BPF_CALL, BPF_JMP, BPF_K}, generated::{BPF_CALL, BPF_JMP, BPF_K},
maps::{BtfMap, LegacyMap, Map, MINIMUM_MAP_SIZE}, maps::{BtfMap, LegacyMap, Map, MINIMUM_MAP_SIZE},
programs::XdpAttachType,
relocation::*, relocation::*,
util::HashMap, util::HashMap,
}; };
@ -204,8 +205,6 @@ pub struct Function {
/// - `struct_ops+` /// - `struct_ops+`
/// - `fmod_ret+`, `fmod_ret.s+` /// - `fmod_ret+`, `fmod_ret.s+`
/// - `iter+`, `iter.s+` /// - `iter+`, `iter.s+`
/// - `xdp.frags/cpumap`, `xdp/cpumap`
/// - `xdp.frags/devmap`, `xdp/devmap`
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum ProgramSection { pub enum ProgramSection {
@ -221,6 +220,7 @@ pub enum ProgramSection {
SocketFilter, SocketFilter,
Xdp { Xdp {
frags: bool, frags: bool,
attach_type: XdpAttachType,
}, },
SkMsg, SkMsg,
SkSkbStreamParser, SkSkbStreamParser,
@ -283,8 +283,19 @@ impl FromStr for ProgramSection {
"uprobe.s" => UProbe { sleepable: true }, "uprobe.s" => UProbe { sleepable: true },
"uretprobe" => URetProbe { sleepable: false }, "uretprobe" => URetProbe { sleepable: false },
"uretprobe.s" => URetProbe { sleepable: true }, "uretprobe.s" => URetProbe { sleepable: true },
"xdp" => Xdp { frags: false }, "xdp" | "xdp.frags" => Xdp {
"xdp.frags" => Xdp { frags: true }, 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, "tp_btf" => BtfTracePoint,
"tracepoint" | "tp" => TracePoint, "tracepoint" | "tp" => TracePoint,
"socket" => SocketFilter, "socket" => SocketFilter,
@ -2012,7 +2023,7 @@ mod tests {
assert_matches!( assert_matches!(
obj.parse_section(fake_section( obj.parse_section(fake_section(
BpfSectionKind::Program, BpfSectionKind::Program,
"xdp/foo", "xdp",
bytes_of(&fake_ins()), bytes_of(&fake_ins()),
None None
)), )),
@ -2035,7 +2046,7 @@ mod tests {
assert_matches!( assert_matches!(
obj.parse_section(fake_section( obj.parse_section(fake_section(
BpfSectionKind::Program, BpfSectionKind::Program,
"xdp.frags/foo", "xdp.frags",
bytes_of(&fake_ins()), bytes_of(&fake_ins()),
None None
)), )),

@ -3,7 +3,9 @@
pub mod cgroup_sock; pub mod cgroup_sock;
pub mod cgroup_sock_addr; pub mod cgroup_sock_addr;
pub mod cgroup_sockopt; pub mod cgroup_sockopt;
pub mod xdp;
pub use cgroup_sock::CgroupSockAttachType; pub use cgroup_sock::CgroupSockAttachType;
pub use cgroup_sock_addr::CgroupSockAddrAttachType; pub use cgroup_sock_addr::CgroupSockAddrAttachType;
pub use cgroup_sockopt::CgroupSockoptAttachType; pub use cgroup_sockopt::CgroupSockoptAttachType;
pub use xdp::XdpAttachType;

@ -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<XdpAttachType> 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,
}
}
}

@ -412,7 +412,10 @@ impl<'a> BpfLoader<'a> {
| ProgramSection::URetProbe { sleepable: _ } | ProgramSection::URetProbe { sleepable: _ }
| ProgramSection::TracePoint | ProgramSection::TracePoint
| ProgramSection::SocketFilter | ProgramSection::SocketFilter
| ProgramSection::Xdp { frags: _ } | ProgramSection::Xdp {
frags: _,
attach_type: _,
}
| ProgramSection::SkMsg | ProgramSection::SkMsg
| ProgramSection::SkSkbStreamParser | ProgramSection::SkSkbStreamParser
| ProgramSection::SkSkbStreamVerdict | ProgramSection::SkSkbStreamVerdict
@ -556,13 +559,18 @@ impl<'a> BpfLoader<'a> {
ProgramSection::SocketFilter => Program::SocketFilter(SocketFilter { ProgramSection::SocketFilter => Program::SocketFilter(SocketFilter {
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
}), }),
ProgramSection::Xdp { frags, .. } => { ProgramSection::Xdp {
frags, attach_type, ..
} => {
let mut data = let mut data =
ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
if *frags { if *frags {
data.flags = BPF_F_XDP_HAS_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 { ProgramSection::SkMsg => Program::SkMsg(SkMsg {
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),

@ -879,7 +879,6 @@ macro_rules! impl_from_pin {
impl_from_pin!( impl_from_pin!(
TracePoint, TracePoint,
SocketFilter, SocketFilter,
Xdp,
SkMsg, SkMsg,
CgroupSysctl, CgroupSysctl,
LircMode2, LircMode2,

@ -12,18 +12,21 @@ use std::{
hash::Hash, hash::Hash,
io, io,
os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd}, os::fd::{AsFd as _, AsRawFd as _, BorrowedFd, RawFd},
path::Path,
}; };
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
generated::{ generated::{
bpf_attach_type, bpf_link_type, bpf_prog_type, XDP_FLAGS_DRV_MODE, XDP_FLAGS_HW_MODE, bpf_link_type, bpf_prog_type, XDP_FLAGS_DRV_MODE, XDP_FLAGS_HW_MODE, XDP_FLAGS_REPLACE,
XDP_FLAGS_REPLACE, XDP_FLAGS_SKB_MODE, XDP_FLAGS_UPDATE_IF_NOEXIST, XDP_FLAGS_SKB_MODE, XDP_FLAGS_UPDATE_IF_NOEXIST,
}, },
obj::programs::XdpAttachType,
programs::{ programs::{
define_link_wrapper, load_program, FdLink, Link, LinkError, ProgramData, ProgramError, 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}, 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`. /// 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")] #[doc(alias = "BPF_PROG_TYPE_XDP")]
pub struct Xdp { pub struct Xdp {
pub(crate) data: ProgramData<XdpLink>, pub(crate) data: ProgramData<XdpLink>,
pub(crate) attach_type: XdpAttachType,
} }
impl Xdp { impl Xdp {
/// Loads the program inside the kernel. /// Loads the program inside the kernel.
pub fn load(&mut self) -> Result<(), ProgramError> { 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) 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(); let prog_fd = prog_fd.as_fd();
if KernelVersion::current().unwrap() >= KernelVersion::new(5, 9, 0) { 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( let link_fd = bpf_link_create(
prog_fd, prog_fd,
LinkTarget::IfIndex(if_index), LinkTarget::IfIndex(if_index),
bpf_attach_type::BPF_XDP, attach_type,
None, None,
flags.bits(), 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<P: AsRef<Path>>(
path: P,
attach_type: XdpAttachType,
) -> Result<Self, ProgramError> {
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. /// Detaches the program.
/// ///
/// See [Xdp::attach]. /// See [Xdp::attach].

@ -9,6 +9,7 @@ use aya::{
util::KernelVersion, util::KernelVersion,
Bpf, Bpf,
}; };
use aya_obj::programs::XdpAttachType;
const MAX_RETRIES: usize = 100; const MAX_RETRIES: usize = 100;
const RETRY_DURATION: time::Duration = time::Duration::from_millis(10); 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 // 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 // should still be loaded since prog was pinned
@ -291,7 +292,8 @@ fn pin_lifecycle() {
// 3. Load program from bpffs and attach // 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_id = prog.attach("lo", XdpFlags::default()).unwrap();
let link = prog.take_link(link_id).unwrap(); let link = prog.take_link(link_id).unwrap();
let fd_link: FdLink = link.try_into().unwrap(); let fd_link: FdLink = link.try_into().unwrap();

@ -2,7 +2,7 @@ use core::{mem::size_of, ptr::null_mut, slice::from_raw_parts};
use std::collections::HashMap; use std::collections::HashMap;
use assert_matches::assert_matches; use assert_matches::assert_matches;
use aya_obj::{generated::bpf_insn, Object, ProgramSection}; use aya_obj::{generated::bpf_insn, programs::XdpAttachType, Object, ProgramSection};
#[test] #[test]
fn run_with_rbpf() { fn run_with_rbpf() {
@ -11,7 +11,10 @@ fn run_with_rbpf() {
assert_eq!(object.programs.len(), 1); assert_eq!(object.programs.len(), 1);
assert_matches!( assert_matches!(
object.programs["pass"].section, object.programs["pass"].section,
ProgramSection::Xdp { frags: true } ProgramSection::Xdp {
frags: true,
attach_type: XdpAttachType::Interface
}
); );
let instructions = &object let instructions = &object

@ -1,3 +1,4 @@
use aya::Bpf;
use object::{Object, ObjectSection, ObjectSymbol, SymbolSection}; use object::{Object, ObjectSection, ObjectSymbol, SymbolSection};
#[test] #[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:?}" "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();
}

Loading…
Cancel
Save