integration-test: Use veth pair instead of lo

pull/915/head
Kevin Ji 1 year ago
parent 9f28e3edfd
commit 6270e6aa50

@ -18,11 +18,15 @@ use aya::{
use aya_obj::programs::XdpAttachType;
use test_log::test;
use crate::utils::NetNsGuard;
const MAX_RETRIES: usize = 100;
const RETRY_DURATION: Duration = Duration::from_millis(10);
#[test]
fn long_name() {
let netns = NetNsGuard::new();
let mut bpf = Ebpf::load(crate::NAME_TEST).unwrap();
let name_prog: &mut Xdp = bpf
.program_mut("ihaveaverylongname")
@ -30,7 +34,9 @@ fn long_name() {
.try_into()
.unwrap();
name_prog.load().unwrap();
name_prog.attach("lo", XdpFlags::default()).unwrap();
name_prog
.attach_to_if_index(netns.if_idx2, XdpFlags::default())
.unwrap();
// We used to be able to assert with bpftool that the program name was short.
// It seem though that it now uses the name from the ELF symbol table instead.
@ -198,11 +204,15 @@ fn assert_unloaded(name: &str) {
#[test]
fn unload_xdp() {
let netns = NetNsGuard::new();
let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
prog.load().unwrap();
assert_loaded("pass");
let link = prog.attach("lo", XdpFlags::default()).unwrap();
let link = prog
.attach_to_if_index(netns.if_idx2, XdpFlags::default())
.unwrap();
{
let _link_owned = prog.take_link(link).unwrap();
prog.unload().unwrap();
@ -213,7 +223,8 @@ fn unload_xdp() {
prog.load().unwrap();
assert_loaded("pass");
prog.attach("lo", XdpFlags::default()).unwrap();
prog.attach_to_if_index(netns.if_idx2, XdpFlags::default())
.unwrap();
assert_loaded("pass");
prog.unload().unwrap();
@ -351,15 +362,19 @@ fn pin_link() {
return;
}
let netns = NetNsGuard::new();
let mut bpf = Ebpf::load(crate::TEST).unwrap();
let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
prog.load().unwrap();
let link_id = prog.attach("lo", XdpFlags::default()).unwrap();
let link_id = prog
.attach_to_if_index(netns.if_idx2, XdpFlags::default())
.unwrap();
let link = prog.take_link(link_id).unwrap();
assert_loaded("pass");
let fd_link: FdLink = link.try_into().unwrap();
let pinned = fd_link.pin("/sys/fs/bpf/aya-xdp-test-lo").unwrap();
let pinned = fd_link.pin("/sys/fs/bpf/aya-xdp-test-veth-pair").unwrap();
// because of the pin, the program is still attached
prog.unload().unwrap();
@ -382,6 +397,8 @@ fn pin_lifecycle() {
return;
}
let netns = NetNsGuard::new();
// 1. Load Program and Pin
{
let mut bpf = Ebpf::load(crate::PASS).unwrap();
@ -405,10 +422,12 @@ fn pin_lifecycle() {
{
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_to_if_index(netns.if_idx2, XdpFlags::default())
.unwrap();
let link = prog.take_link(link_id).unwrap();
let fd_link: FdLink = link.try_into().unwrap();
fd_link.pin("/sys/fs/bpf/aya-xdp-test-lo").unwrap();
fd_link.pin("/sys/fs/bpf/aya-xdp-test-veth-pair").unwrap();
// Unpin the program. It will stay attached since its links were pinned.
prog.unpin().unwrap();
@ -423,7 +442,7 @@ fn pin_lifecycle() {
let prog: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
prog.load().unwrap();
let link = PinnedLink::from_pin("/sys/fs/bpf/aya-xdp-test-lo")
let link = PinnedLink::from_pin("/sys/fs/bpf/aya-xdp-test-veth-pair")
.unwrap()
.unpin()
.unwrap();

@ -16,12 +16,14 @@ fn xdp() {
return;
}
let _netns = NetNsGuard::new();
let netns = NetNsGuard::new();
let mut bpf = Ebpf::load(crate::PASS).unwrap();
let dispatcher: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
dispatcher.load().unwrap();
dispatcher.attach("lo", XdpFlags::default()).unwrap();
dispatcher
.attach_to_if_index(netns.if_idx2, XdpFlags::default())
.unwrap();
}
#[test]
@ -54,12 +56,13 @@ fn extension() {
return;
}
let _netns = NetNsGuard::new();
let netns = NetNsGuard::new();
let mut bpf = Ebpf::load(crate::MAIN).unwrap();
let pass: &mut Xdp = bpf.program_mut("xdp_pass").unwrap().try_into().unwrap();
pass.load().unwrap();
pass.attach("lo", XdpFlags::default()).unwrap();
pass.attach_to_if_index(netns.if_idx2, XdpFlags::default())
.unwrap();
let mut bpf = EbpfLoader::new()
.extension("xdp_drop")
@ -73,11 +76,15 @@ fn extension() {
#[test]
fn list_loaded_programs() {
let netns = NetNsGuard::new();
// Load a program.
let mut bpf = Ebpf::load(crate::PASS).unwrap();
let dispatcher: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
dispatcher.load().unwrap();
dispatcher.attach("lo", XdpFlags::default()).unwrap();
dispatcher
.attach_to_if_index(netns.if_idx2, XdpFlags::default())
.unwrap();
// Ensure the loaded_programs() api doesn't panic.
let prog = loaded_programs()
@ -102,11 +109,15 @@ fn list_loaded_programs() {
#[test]
fn list_loaded_maps() {
let netns = NetNsGuard::new();
// Load a program with maps.
let mut bpf = Ebpf::load(crate::MAP_TEST).unwrap();
let dispatcher: &mut Xdp = bpf.program_mut("pass").unwrap().try_into().unwrap();
dispatcher.load().unwrap();
dispatcher.attach("lo", XdpFlags::default()).unwrap();
dispatcher
.attach_to_if_index(netns.if_idx2, XdpFlags::default())
.unwrap();
// Ensure the loaded_maps() api doesn't panic and retrieve a map.
let map = loaded_maps()

@ -1,4 +1,4 @@
use std::{ffi::CStr, mem::MaybeUninit, net::UdpSocket, num::NonZeroU32, time::Duration};
use std::{mem::MaybeUninit, net::UdpSocket, num::NonZeroU32, time::Duration};
use aya::{
maps::{Array, CpuMap, XskMap},
@ -9,11 +9,11 @@ use object::{Object, ObjectSection, ObjectSymbol, SymbolSection};
use test_log::test;
use xdpilone::{BufIdx, IfInfo, Socket, SocketConfig, Umem, UmemConfig};
use crate::utils::NetNsGuard;
use crate::utils::{NetNsGuard, IP_ADDR_1, IP_ADDR_2};
#[test]
fn af_xdp() {
let _netns = NetNsGuard::new();
let netns = NetNsGuard::new();
let mut bpf = Ebpf::load(crate::REDIRECT).unwrap();
let mut socks: XskMap<_> = bpf.take_map("SOCKS").unwrap().try_into().unwrap();
@ -24,7 +24,8 @@ fn af_xdp() {
.try_into()
.unwrap();
xdp.load().unwrap();
xdp.attach("lo", XdpFlags::default()).unwrap();
xdp.attach_to_if_index(netns.if_idx2, XdpFlags::default())
.unwrap();
// So this needs to be page aligned. Pages are 4k on all mainstream architectures except for
// Apple Silicon which uses 16k pages. So let's align on that for tests to run natively there.
@ -41,9 +42,7 @@ fn af_xdp() {
};
let mut iface = IfInfo::invalid();
iface
.from_name(CStr::from_bytes_with_nul(b"lo\0").unwrap())
.unwrap();
iface.from_ifindex(netns.if_idx1).unwrap();
let sock = Socket::with_shared(&iface, &umem).unwrap();
let mut fq_cq = umem.fq_cq(&sock).unwrap(); // Fill Queue / Completion Queue
@ -66,9 +65,12 @@ fn af_xdp() {
writer.insert_once(frame.offset);
writer.commit();
let sock = UdpSocket::bind("127.0.0.1:0").unwrap();
let port = sock.local_addr().unwrap().port();
sock.send_to(b"hello AF_XDP", "127.0.0.1:1777").unwrap();
let sock = UdpSocket::bind((IP_ADDR_1, 0)).unwrap();
let src_port = sock.local_addr().unwrap().port();
const DST_PORT: u16 = 1777;
sock.send_to(b"hello AF_XDP", (IP_ADDR_2, DST_PORT))
.unwrap();
assert_eq!(rx.available(), 1);
let desc = rx.receive(1).read().unwrap();
@ -81,8 +83,8 @@ fn af_xdp() {
let (ip, buf) = buf.split_at(20);
assert_eq!(ip[9], 17); // UDP
let (udp, payload) = buf.split_at(8);
assert_eq!(&udp[0..2], port.to_be_bytes().as_slice()); // Source
assert_eq!(&udp[2..4], 1777u16.to_be_bytes().as_slice()); // Dest
assert_eq!(&udp[0..2], src_port.to_be_bytes().as_slice()); // Source
assert_eq!(&udp[2..4], DST_PORT.to_be_bytes().as_slice()); // Dest
assert_eq!(payload, b"hello AF_XDP");
}
@ -134,7 +136,7 @@ fn map_load() {
#[test]
fn cpumap_chain() {
let _netns = NetNsGuard::new();
let netns = NetNsGuard::new();
let mut bpf = Ebpf::load(crate::REDIRECT).unwrap();
@ -157,20 +159,24 @@ fn cpumap_chain() {
// Load the main program
let xdp: &mut Xdp = bpf.program_mut("redirect_cpu").unwrap().try_into().unwrap();
xdp.load().unwrap();
xdp.attach("lo", XdpFlags::default()).unwrap();
xdp.attach_to_if_index(netns.if_idx2, XdpFlags::default())
.unwrap();
const PAYLOAD: &str = "hello cpumap";
let sock = UdpSocket::bind("127.0.0.1:0").unwrap();
let addr = sock.local_addr().unwrap();
sock.set_read_timeout(Some(Duration::from_secs(60)))
let sock1 = UdpSocket::bind((IP_ADDR_1, 0)).unwrap();
let sock2 = UdpSocket::bind((IP_ADDR_2, 0)).unwrap();
sock2
.set_read_timeout(Some(Duration::from_secs(60)))
.unwrap();
sock1
.send_to(PAYLOAD.as_bytes(), sock2.local_addr().unwrap())
.unwrap();
sock.send_to(PAYLOAD.as_bytes(), addr).unwrap();
// Read back the packet to ensure it went through the entire network stack, including our two
// probes.
let mut buf = [0u8; PAYLOAD.len() + 1];
let n = sock.recv(&mut buf).unwrap();
let n = sock2.recv(&mut buf).unwrap();
assert_eq!(&buf[..n], PAYLOAD.as_bytes());
assert_eq!(hits.get(&0, 0).unwrap(), 1);

@ -1,19 +1,42 @@
//! Utilities to run tests
use std::{
ffi::CString,
io, process,
ffi::CStr,
io,
net::Ipv4Addr,
process,
sync::atomic::{AtomicU64, Ordering},
};
use aya::netlink_set_link_up;
use aya::{
netlink_add_ip_addr, netlink_add_veth_pair, netlink_delete_link, netlink_set_link_down,
netlink_set_link_up,
};
use libc::if_nametoindex;
use netns_rs::{get_from_current_thread, NetNs};
pub const IF_NAME_1: &CStr = c"aya-test-1";
pub const IF_NAME_2: &CStr = c"aya-test-2";
pub const IP_ADDR_1: Ipv4Addr = Ipv4Addr::new(10, 0, 0, 1);
pub const IP_ADDR_2: Ipv4Addr = Ipv4Addr::new(10, 0, 0, 2);
pub const IP_PREFIX: u8 = 24;
pub fn setup_test_veth_pair() {
unsafe { netlink_add_veth_pair(IF_NAME_1, IF_NAME_2) }.unwrap_or_else(|e| {
panic!(
"Failed to set up veth pair ({}, {}): {e}",
IF_NAME_1.to_string_lossy(),
IF_NAME_2.to_string_lossy()
)
})
}
pub struct NetNsGuard {
name: String,
old_ns: NetNs,
ns: Option<NetNs>,
pub if_idx1: u32,
pub if_idx2: u32,
}
impl NetNsGuard {
@ -26,39 +49,116 @@ impl NetNsGuard {
// Create and enter netns
let ns = NetNs::new(&name).unwrap_or_else(|e| panic!("Failed to create netns {name}: {e}"));
let netns = Self {
old_ns,
ns: Some(ns),
name,
};
let ns = netns.ns.as_ref().unwrap();
ns.enter()
.unwrap_or_else(|e| panic!("Failed to enter network namespace {}: {e}", netns.name));
println!("Entered network namespace {}", netns.name);
.unwrap_or_else(|e| panic!("Failed to enter network namespace {}: {e}", name));
println!("Entered network namespace {}", name);
// By default, the loopback in a new netns is down. Set it up.
let lo = CString::new("lo").unwrap();
unsafe {
let idx = if_nametoindex(lo.as_ptr());
if idx == 0 {
let lo_idx = unsafe { if_nametoindex(c"lo".as_ptr()) };
if lo_idx == 0 {
panic!(
"Interface `lo` not found in netns {}: {}",
netns.name,
name,
io::Error::last_os_error()
);
}
netlink_set_link_up(idx as i32)
.unwrap_or_else(|e| panic!("Failed to set `lo` up in netns {}: {e}", netns.name));
unsafe { netlink_set_link_up(lo_idx as i32) }
.unwrap_or_else(|e| panic!("Failed to set `lo` up in netns {}: {e}", name));
setup_test_veth_pair();
let if_idx1 = unsafe { if_nametoindex(IF_NAME_1.as_ptr()) };
if if_idx1 == 0 {
panic!(
"Interface `{}` not found in netns {}: {}",
IF_NAME_1.to_string_lossy(),
name,
io::Error::last_os_error()
);
}
let if_idx2 = unsafe { if_nametoindex(IF_NAME_2.as_ptr()) };
if if_idx2 == 0 {
panic!(
"Interface `{}` not found in netns {}: {}",
IF_NAME_2.to_string_lossy(),
name,
io::Error::last_os_error()
);
}
netns
unsafe { netlink_add_ip_addr(if_idx1, IP_ADDR_1, IP_PREFIX) }.unwrap_or_else(|e| {
panic!(
"Failed to add IP `{}` to `{}` in netns {}: {e}",
IP_ADDR_1,
IF_NAME_1.to_string_lossy(),
name
)
});
unsafe { netlink_add_ip_addr(if_idx2, IP_ADDR_2, IP_PREFIX) }.unwrap_or_else(|e| {
panic!(
"Failed to add IP `{}` to `{}` in netns {}: {e}",
IP_ADDR_2,
IF_NAME_2.to_string_lossy(),
name
)
});
unsafe { netlink_set_link_up(if_idx1 as i32) }.unwrap_or_else(|e| {
panic!(
"Failed to set `{}` up in netns {}: {e}",
IF_NAME_1.to_string_lossy(),
name
)
});
unsafe { netlink_set_link_up(if_idx2 as i32) }.unwrap_or_else(|e| {
panic!(
"Failed to set `{}` up in netns {}: {e}",
IF_NAME_2.to_string_lossy(),
name
)
});
Self {
old_ns,
ns: Some(ns),
name,
if_idx1,
if_idx2,
}
}
}
impl Drop for NetNsGuard {
fn drop(&mut self) {
// Avoid panic in panic
if let Err(e) = unsafe { netlink_set_link_down(self.if_idx1 as i32) } {
eprintln!(
"Failed to set `{}` down in netns {}: {e}",
IF_NAME_1.to_string_lossy(),
self.name
)
}
if let Err(e) = unsafe { netlink_set_link_down(self.if_idx2 as i32) } {
eprintln!(
"Failed to set `{}` down in netns {}: {e}",
IF_NAME_2.to_string_lossy(),
self.name
)
}
if let Err(e) = unsafe { netlink_delete_link(self.if_idx1 as i32) } {
eprintln!(
"Failed to delete `{}` in netns {}: {e}",
IF_NAME_1.to_string_lossy(),
self.name
)
}
if let Err(e) = self.old_ns.enter() {
eprintln!("Failed to return to original netns: {e}");
}

Loading…
Cancel
Save