|
|
|
//! Utilities to run tests
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
ffi::CString,
|
|
|
|
io, process,
|
|
|
|
sync::atomic::{AtomicU64, Ordering},
|
|
|
|
};
|
|
|
|
|
|
|
|
use aya::netlink_set_link_up;
|
|
|
|
use libc::if_nametoindex;
|
|
|
|
use netns_rs::{get_from_current_thread, NetNs};
|
|
|
|
|
|
|
|
pub struct NetNsGuard {
|
|
|
|
name: String,
|
|
|
|
old_ns: NetNs,
|
|
|
|
ns: Option<NetNs>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl NetNsGuard {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
let old_ns = get_from_current_thread().expect("Failed to get current netns");
|
|
|
|
|
|
|
|
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
|
|
|
let pid = process::id();
|
|
|
|
let name = format!("aya-test-{pid}-{}", COUNTER.fetch_add(1, Ordering::Relaxed));
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
// 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 {
|
|
|
|
panic!(
|
|
|
|
"Interface `lo` not found in netns {}: {}",
|
|
|
|
netns.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));
|
|
|
|
}
|
|
|
|
|
|
|
|
netns
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for NetNsGuard {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
// Avoid panic in panic
|
|
|
|
if let Err(e) = self.old_ns.enter() {
|
|
|
|
eprintln!("Failed to return to original netns: {e}");
|
|
|
|
}
|
|
|
|
if let Some(ns) = self.ns.take() {
|
|
|
|
if let Err(e) = ns.remove() {
|
|
|
|
eprintln!("Failed to remove netns {}: {e}", self.name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
println!("Exited network namespace {}", self.name);
|
|
|
|
}
|
|
|
|
}
|
aya,integration-test: improve integration tests for info API
Improves the existing integraiton tests for `loaded_programs()` and
`loaded_maps()` in consideration for older kernels:
- Opt for `SocketFilter` program in tests since XDP requires v4.8 and
fragments requires v5.18.
- For assertion tests, first perform the assertion, if the assertion
fails, then it checks the host kernel version to see if it is above
the minimum version requirement. If not, then continue with test,
otherwise fail.
For assertions that are skipped, they're logged in stderr which can
be observed with `-- --nocapture`.
This also fixes the `bpf_prog_get_info_by_fd()` call for kernels below
v4.15. If calling syscall on kernels below v4.15, it can produce an
`E2BIG` error because `check_uarg_tail_zero()` expects the entire
struct to all-zero bytes (which is caused from the map info).
Instead, we first attempt the syscall with the map info filled, if it
returns `E2BIG`, then perform syscall again with empty closure.
Also adds doc for which version a kernel feature was introduced for
better awareness.
The tests have been verified kernel versions:
- 4.13.0
- 4.15.0
- 6.1.0
6 months ago
|
|
|
|
|
|
|
/// Performs `assert!` macro. If the assertion fails and host kernel version
|
|
|
|
/// is above feature version, then fail test.
|
|
|
|
macro_rules! kernel_assert {
|
|
|
|
($cond:expr, $version:expr $(,)?) => {
|
|
|
|
let pass: bool = $cond;
|
|
|
|
if !pass {
|
|
|
|
let feat_version: aya::util::KernelVersion = $version;
|
|
|
|
let current = aya::util::KernelVersion::current().unwrap();
|
|
|
|
let cond_literal = stringify!($cond);
|
|
|
|
if current >= feat_version {
|
|
|
|
// Host kernel is expected to have the feat but does not
|
|
|
|
panic!(
|
|
|
|
r#" assertion `{cond_literal}` failed: expected host kernel v{current} to have v{feat_version} feature"#,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
// Continue with tests since host is not expected to have feat
|
|
|
|
eprintln!(
|
|
|
|
r#"ignoring assertion at {}:{}
|
|
|
|
assertion `{cond_literal}` failed: continuing since host kernel v{current} is not expected to have v{feat_version} feature"#,
|
|
|
|
file!(), line!(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) use kernel_assert;
|
|
|
|
|
|
|
|
/// Performs `assert_eq!` macro. If the assertion fails and host kernel version
|
|
|
|
/// is above feature version, then fail test.
|
|
|
|
macro_rules! kernel_assert_eq {
|
|
|
|
($left:expr, $right:expr, $version:expr $(,)?) => {
|
|
|
|
if $left != $right {
|
|
|
|
let feat_version: aya::util::KernelVersion = $version;
|
|
|
|
let current = aya::util::KernelVersion::current().unwrap();
|
|
|
|
if current >= feat_version {
|
|
|
|
// Host kernel is expected to have the feat but does not
|
|
|
|
panic!(
|
|
|
|
r#" assertion `left == right` failed: expected host kernel v{current} to have v{feat_version} feature
|
|
|
|
left: {:?}
|
|
|
|
right: {:?}"#,
|
|
|
|
$left, $right,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
// Continue with tests since host is not expected to have feat
|
|
|
|
eprintln!(
|
|
|
|
r#"ignoring assertion at {}:{}
|
|
|
|
assertion `left == right` failed: continuing since host kernel v{current} is not expected to have v{feat_version} feature
|
|
|
|
left: {:?}
|
|
|
|
right: {:?}"#,
|
|
|
|
file!(), line!(), $left, $right,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) use kernel_assert_eq;
|