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,
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
)),

@ -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;

@ -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::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),

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

@ -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<XdpLink>,
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<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.
///
/// See [Xdp::attach].

@ -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();

@ -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

@ -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();
}

Loading…
Cancel
Save