Exercise all new TCX APIs in the TCX integration test

Also removed unused code in the tcx eBPF program.

I've manually verified that all the programs get attached in the correct
order.

TODO: Add code to the integration test to automatically verify that the
programs are attached in the correct order after the API based on the
BPF_PROG_QUERY syscall has been implemented.

Signed-off-by: Andre Fredette <afredette@redhat.com>
reviewable/pr921/r17
Andre Fredette 7 months ago
parent 5408f17b7e
commit f5d4fdedba

@ -1,74 +1,11 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use core::mem; use aya_ebpf::{bindings::tcx_action_base::TCX_NEXT, macros::classifier, programs::TcContext};
use aya_ebpf::{
bindings::tcx_action_base::{TCX_NEXT, TCX_PASS},
macros::classifier,
programs::TcContext,
};
use aya_log_ebpf::info;
use network_types::{
eth::{EthHdr, EtherType},
ip::{IpProto, Ipv4Hdr},
udp::UdpHdr,
};
#[no_mangle]
static ORDER: i32 = 0;
// Gives us raw pointers to a specific offset in the packet
unsafe fn ptr_at<T>(ctx: &TcContext, offset: usize) -> Result<*mut T, i64> {
let start = ctx.data();
let end = ctx.data_end();
let len = mem::size_of::<T>();
if start + offset + len > end {
return Err(TCX_PASS.into());
}
Ok((start + offset) as *mut T)
}
#[classifier] #[classifier]
pub fn tcx_order(ctx: TcContext) -> i32 { pub fn tcx_next(_ctx: TcContext) -> i32 {
match try_tcxtest(ctx) { TCX_NEXT
Ok(ret) => ret,
Err(_ret) => TCX_PASS,
}
}
fn try_tcxtest(ctx: TcContext) -> Result<i32, i64> {
let eth_hdr: *const EthHdr = unsafe { ptr_at(&ctx, 0) }?;
let order = unsafe { core::ptr::read_volatile(&ORDER) };
match unsafe { *eth_hdr }.ether_type {
EtherType::Ipv4 => {
let ipv4_hdr: *const Ipv4Hdr = unsafe { ptr_at(&ctx, EthHdr::LEN)? };
let saddr = u32::from_be(unsafe { (*ipv4_hdr).src_addr });
let daddr = u32::from_be(unsafe { (*ipv4_hdr).dst_addr });
match unsafe { (*ipv4_hdr).proto } {
IpProto::Udp => {
let udphdr: *const UdpHdr =
unsafe { ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN) }?;
let dport = u16::from_be(unsafe { (*udphdr).dest });
let sport = u16::from_be(unsafe { (*udphdr).source });
info!(
&ctx,
"order: {}, cookie: ({:i}, {:i}, {}, {})",
order,
daddr,
saddr,
dport,
sport
);
Ok(TCX_NEXT)
}
_ => Ok(TCX_PASS),
}
}
_ => Ok(TCX_PASS),
}
} }
#[cfg(not(test))] #[cfg(not(test))]

@ -1,14 +1,19 @@
use std::collections::HashMap;
use aya::{ use aya::{
programs::{tc::TcAttachOptions, LinkOrder, SchedClassifier, TcAttachType}, programs::{
tc::{SchedClassifierLink, TcAttachOptions},
LinkOrder, ProgramId, SchedClassifier, TcAttachType,
},
util::KernelVersion, util::KernelVersion,
EbpfLoader, Ebpf, EbpfLoader,
}; };
use test_log::test; use test_log::test;
use crate::utils::NetNsGuard; use crate::utils::NetNsGuard;
#[test(tokio::test)] #[test(tokio::test)]
async fn tcx_attach() { async fn tcx() {
let kernel_version = KernelVersion::current().unwrap(); let kernel_version = KernelVersion::current().unwrap();
if kernel_version < KernelVersion::new(6, 6, 0) { if kernel_version < KernelVersion::new(6, 6, 0) {
eprintln!("skipping tcx_attach test on kernel {kernel_version:?}"); eprintln!("skipping tcx_attach test on kernel {kernel_version:?}");
@ -17,77 +22,113 @@ async fn tcx_attach() {
let _netns = NetNsGuard::new(); let _netns = NetNsGuard::new();
let mut program0 = EbpfLoader::new() // We need a dedicated `Ebpf` instance for each program that we load
.set_global("ORDER", &0, true) // since TCX does not allow the same program ID to be attached multiple
.load(crate::TCX) // times to the same interface/direction.
.unwrap(); let mut attached_programs: HashMap<&str, (Ebpf, SchedClassifierLink)> = HashMap::new();
let mut program1 = EbpfLoader::new() macro_rules! attach_program_with_linkorder {
.set_global("ORDER", &1, true) ($name:literal,$link_order:expr) => {{
.load(crate::TCX) let mut loader = EbpfLoader::new().load(crate::TCX).unwrap();
.unwrap(); let program: &mut SchedClassifier =
let mut program2 = EbpfLoader::new() loader.program_mut("tcx_next").unwrap().try_into().unwrap();
.set_global("ORDER", &2, true) program.load().unwrap();
.load(crate::TCX) let options = TcAttachOptions::TcxOrder($link_order);
.unwrap(); let link_id = program
let mut program3 = EbpfLoader::new() .attach_with_options("lo", TcAttachType::Ingress, options)
.set_global("ORDER", &3, true) .unwrap();
.load(crate::TCX) let link = program.take_link(link_id).unwrap();
.unwrap(); attached_programs.insert($name, (loader, link));
}};
let prog0: &mut SchedClassifier = program0 }
.program_mut("tcx_order")
.unwrap()
.try_into()
.unwrap();
prog0.load().unwrap();
let prog1: &mut SchedClassifier = program1
.program_mut("tcx_order")
.unwrap()
.try_into()
.unwrap();
prog1.load().unwrap();
let prog2: &mut SchedClassifier = program2 // TODO: Assert in position 4 at the end of the test.
.program_mut("tcx_order") attach_program_with_linkorder!("default", LinkOrder::default());
// TODO: Assert in position 1 at the end of the test.
attach_program_with_linkorder!("first", LinkOrder::first());
// TODO: Assert in position 7 at the end of the test.
attach_program_with_linkorder!("last", LinkOrder::last());
// TODO: Assert in position 6 at the end of the test.
attach_program_with_linkorder!(
"before_last",
LinkOrder::before_link(&attached_programs.get("last").unwrap().1).unwrap()
);
// TODO: Assert in position 8 at the end of the test.
attach_program_with_linkorder!(
"after_last",
LinkOrder::after_link(&attached_programs.get("last").unwrap().1).unwrap()
);
// TODO: Assert in position 3 at the end of the test.
attach_program_with_linkorder!(
"before_default",
LinkOrder::before_program(
TryInto::<&SchedClassifier>::try_into(
attached_programs
.get("default")
.unwrap()
.0
.program("tcx_next")
.unwrap(),
)
.unwrap()
)
.unwrap() .unwrap()
.try_into() );
.unwrap(); // TODO: Assert in position 5 at the end of the test.
prog2.load().unwrap(); attach_program_with_linkorder!(
"after_default",
let prog3: &mut SchedClassifier = program3 LinkOrder::after_program(
.program_mut("tcx_order") TryInto::<&SchedClassifier>::try_into(
attached_programs
.get("default")
.unwrap()
.0
.program("tcx_next")
.unwrap(),
)
.unwrap()
)
.unwrap() .unwrap()
.try_into() );
.unwrap(); // TODO: Assert in position 0 at the end of the test.
prog3.load().unwrap(); attach_program_with_linkorder!(
"before_first",
// Test LinkOrder::last() LinkOrder::before_program_id(unsafe {
let order: LinkOrder = LinkOrder::last(); ProgramId::new(
let options = TcAttachOptions::TcxOrder(order); TryInto::<&SchedClassifier>::try_into(
prog0 attached_programs
.attach_with_options("lo", TcAttachType::Ingress, options) .get("first")
.unwrap(); .unwrap()
.0
// Test LinkOrder::after_program() .program("tcx_next")
let order = LinkOrder::after_program(prog0).unwrap(); .unwrap(),
let options = TcAttachOptions::TcxOrder(order); )
let prog1_link_id = prog1 .unwrap()
.attach_with_options("lo", TcAttachType::Ingress, options) .info()
.unwrap(); .unwrap()
.id(),
let prog1_link = prog1.take_link(prog1_link_id).unwrap(); )
})
// Test LinkOrder::after_link() );
let order = LinkOrder::after_link(&prog1_link).unwrap(); // TODO: Assert in position 2 at the end of the test.
let options = TcAttachOptions::TcxOrder(order); attach_program_with_linkorder!(
prog2 "after_first",
.attach_with_options("lo", TcAttachType::Ingress, options) LinkOrder::after_program_id(unsafe {
.unwrap(); ProgramId::new(
TryInto::<&SchedClassifier>::try_into(
// Test LinkOrder::last() attached_programs
let options = TcAttachOptions::TcxOrder(LinkOrder::last()); .get("first")
prog3 .unwrap()
.attach_with_options("lo", TcAttachType::Ingress, options) .0
.unwrap(); .program("tcx_next")
.unwrap(),
)
.unwrap()
.info()
.unwrap()
.id(),
)
})
);
// TODO: Add code here to automatically verify the order after the API based
// on the BPF_PROG_QUERY syscall is implemented.
} }

Loading…
Cancel
Save