Skip to content

Commit

Permalink
Exercise all new TCX APIs in the TCX integration test
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
anfredette committed Sep 17, 2024
1 parent 5f03569 commit fa32aa5
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 107 deletions.
60 changes: 4 additions & 56 deletions test/integration-ebpf/src/tcx.rs
Original file line number Diff line number Diff line change
@@ -1,74 +1,22 @@
#![no_std]
#![no_main]

use core::mem;

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]
pub fn tcx_order(ctx: TcContext) -> i32 {
pub fn tcx_next(ctx: TcContext) -> i32 {
match try_tcxtest(ctx) {
Ok(ret) => ret,
Err(_ret) => TCX_PASS,
Err(_) => 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),
}
fn try_tcxtest(_ctx: TcContext) -> Result<i32, u32> {
Ok(TCX_NEXT)
}

#[cfg(not(test))]
Expand Down
156 changes: 105 additions & 51 deletions test/integration-test/src/tests/tcx.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
use aya::{
programs::{tc::TcAttachOptions, LinkOrder, SchedClassifier, TcAttachType},
programs::{
tc::{SchedClassifierLink, TcAttachOptions},
Link, LinkOrder, ProgramId, SchedClassifier, TcAttachType,
},
util::KernelVersion,
EbpfLoader,
Ebpf, EbpfLoader,
};
use test_log::test;

use crate::utils::NetNsGuard;

#[test(tokio::test)]
async fn tcx_attach() {
async fn tcx() {
let kernel_version = KernelVersion::current().unwrap();
if kernel_version < KernelVersion::new(6, 6, 0) {
eprintln!("skipping tcx_attach test on kernel {kernel_version:?}");
Expand All @@ -17,77 +20,128 @@ async fn tcx_attach() {

let _netns = NetNsGuard::new();

let mut program0 = EbpfLoader::new()
.set_global("ORDER", &0, true)
.load(crate::TCX)
.unwrap();
let mut program1 = EbpfLoader::new()
.set_global("ORDER", &1, true)
.load(crate::TCX)
.unwrap();
let mut program2 = EbpfLoader::new()
.set_global("ORDER", &2, true)
.load(crate::TCX)
.unwrap();
let mut program3 = EbpfLoader::new()
.set_global("ORDER", &3, true)
.load(crate::TCX)
let mut programs: Vec<Ebpf> = vec![];
for _ in 0..9 {
let program = EbpfLoader::new().load(crate::TCX).unwrap();
programs.push(program);
}

let mut tcx_programs: Vec<&mut SchedClassifier> = vec![];
for program in programs.iter_mut() {
let prog: &mut SchedClassifier =
program.program_mut("tcx_next").unwrap().try_into().unwrap();
prog.load().unwrap();
tcx_programs.push(prog);
}

let mut tcx_links: Vec<SchedClassifierLink> = vec![];

// Test LinkOrder::default()
// Should end up in position 4 at the end of the test.
let order = LinkOrder::default();
let options = TcAttachOptions::TcxOrder(order);
let link_id = tcx_programs[0]
.attach_with_options("lo", TcAttachType::Ingress, options)
.unwrap();
let link = tcx_programs[0].take_link(link_id).unwrap();
tcx_links.push(link);

let prog0: &mut SchedClassifier = program0
.program_mut("tcx_order")
.unwrap()
.try_into()
// Test LinkOrder::first()
// Should end up in position 1 at the end of the test.
let order: LinkOrder = LinkOrder::first();
let options = TcAttachOptions::TcxOrder(order);
let link_id = tcx_programs[1]
.attach_with_options("lo", TcAttachType::Ingress, options)
.unwrap();
prog0.load().unwrap();
let link = tcx_programs[1].take_link(link_id).unwrap();
tcx_links.push(link);

let prog1: &mut SchedClassifier = program1
.program_mut("tcx_order")
.unwrap()
.try_into()
// Test LinkOrder::last()
// Should end up in position 7 at the end of the test.
let order: LinkOrder = LinkOrder::last();
let options = TcAttachOptions::TcxOrder(order);
let link_id = tcx_programs[2]
.attach_with_options("lo", TcAttachType::Ingress, options)
.unwrap();
prog1.load().unwrap();
let link = tcx_programs[2].take_link(link_id).unwrap();
tcx_links.push(link);

let prog2: &mut SchedClassifier = program2
.program_mut("tcx_order")
.unwrap()
.try_into()
// Test LinkOrder::before_link()
// Should end up in position 6 at the end of the test.
let order = LinkOrder::before_link(&tcx_links[2]).unwrap();
let options = TcAttachOptions::TcxOrder(order);
let link_id = tcx_programs[3]
.attach_with_options("lo", TcAttachType::Ingress, options)
.unwrap();
prog2.load().unwrap();
let link = tcx_programs[3].take_link(link_id).unwrap();
tcx_links.push(link);

let prog3: &mut SchedClassifier = program3
.program_mut("tcx_order")
.unwrap()
.try_into()
// Test LinkOrder::after_link()
// Should end up in position 8 at the end of the test.
let order = LinkOrder::after_link(&tcx_links[2]).unwrap();
let options = TcAttachOptions::TcxOrder(order);
let link_id = tcx_programs[4]
.attach_with_options("lo", TcAttachType::Ingress, options)
.unwrap();
prog3.load().unwrap();
let link = tcx_programs[4].take_link(link_id).unwrap();
tcx_links.push(link);

// Test LinkOrder::last()
let order: LinkOrder = LinkOrder::last();
// Test LinkOrder::before_program()
// Should end up in position 3 at the end of the test.
let order = LinkOrder::before_program(tcx_programs[0]).unwrap();
let options = TcAttachOptions::TcxOrder(order);
prog0
let link_id = tcx_programs[5]
.attach_with_options("lo", TcAttachType::Ingress, options)
.unwrap();
let link = tcx_programs[5].take_link(link_id).unwrap();
tcx_links.push(link);

// Test LinkOrder::after_program()
let order = LinkOrder::after_program(prog0).unwrap();
// Should end up in position 5 at the end of the test.
let order = LinkOrder::after_program(tcx_programs[0]).unwrap();
let options = TcAttachOptions::TcxOrder(order);
let prog1_link_id = prog1
let link_id = tcx_programs[6]
.attach_with_options("lo", TcAttachType::Ingress, options)
.unwrap();
let link = tcx_programs[6].take_link(link_id).unwrap();
tcx_links.push(link);

let prog1_link = prog1.take_link(prog1_link_id).unwrap();

// Test LinkOrder::after_link()
let order = LinkOrder::after_link(&prog1_link).unwrap();
// Test LinkOrder::before_program_id
// Should end up in position 0 at the end of the test.
let prog_1_id = unsafe { ProgramId::new(tcx_programs[1].info().unwrap().id()) };
let order = LinkOrder::before_program_id(prog_1_id);
let options = TcAttachOptions::TcxOrder(order);
prog2
let link_id = tcx_programs[7]
.attach_with_options("lo", TcAttachType::Ingress, options)
.unwrap();
let link = tcx_programs[7].take_link(link_id).unwrap();
tcx_links.push(link);

// Test LinkOrder::last()
let options = TcAttachOptions::TcxOrder(LinkOrder::last());
prog3
// Test LinkOrder::after_program_id
// Should end up in position 2 at the end of the test.
let prog_1_id = unsafe { ProgramId::new(tcx_programs[1].info().unwrap().id()) };
let order = LinkOrder::after_program_id(prog_1_id);
let options = TcAttachOptions::TcxOrder(order);
let link_id = tcx_programs[8]
.attach_with_options("lo", TcAttachType::Ingress, options)
.unwrap();
let link = tcx_programs[8].take_link(link_id).unwrap();
tcx_links.push(link);

// It as been manually verified that all the programs are attached in the
// correct order.
// TODO: Add code here to automatically verify the order after the API based
// on the BPF_PROG_QUERY syscall is implemented.

// Detach all links
while let Some(link) = tcx_links.pop() {
link.detach().unwrap();
}

// Unload all programs
for program in tcx_programs {
program.unload().unwrap();
}

panic!("test complete");
}

0 comments on commit fa32aa5

Please sign in to comment.