Browse Source

integration-test: remove `netns-rs`

This crate uses a very old version of `nix` which is preventing us from
adding support for loongarch64.

Make panic-in-panic more uniform while I'm here.
Tamir Duberstein 1 week ago
parent
commit
9e577f60b5
3 changed files with 77 additions and 33 deletions
  1. 0 1
      Cargo.toml
  2. 1 1
      test/integration-test/Cargo.toml
  3. 76 31
      test/integration-test/src/utils.rs

+ 0 - 1
Cargo.toml

@@ -81,7 +81,6 @@ hashbrown = { version = "0.16.0", default-features = false }
 indoc = { version = "2.0", default-features = false }
 libc = { version = "0.2.105", default-features = false }
 log = { version = "0.4", default-features = false }
-netns-rs = { version = "0.1", default-features = false }
 network-types = { version = "0.1.0", default-features = false }
 nix = { version = "0.30.1", default-features = false }
 num_enum = { version = "0.7", default-features = false }

+ 1 - 1
test/integration-test/Cargo.toml

@@ -26,7 +26,7 @@ futures = { workspace = true, features = ["alloc"] }
 integration-common = { path = "../integration-common", features = ["user"] }
 libc = { workspace = true }
 log = { workspace = true }
-netns-rs = { workspace = true }
+nix = { workspace = true, features = ["mount", "sched"] }
 object = { workspace = true, features = ["elf", "read_core", "std"] }
 procfs = { workspace = true, features = ["flate2"] }
 rand = { workspace = true, features = ["thread_rng"] }

+ 76 - 31
test/integration-test/src/utils.rs

@@ -11,9 +11,9 @@ use std::{
     sync::atomic::{AtomicU64, Ordering},
 };
 
+use anyhow::{Context as _, Result};
 use aya::netlink_set_link_up;
 use libc::if_nametoindex;
-use netns_rs::{NetNs, get_from_current_thread};
 
 const CGROUP_ROOT: &str = "/sys/fs/cgroup";
 const CGROUP_PROCS: &str = "cgroup.procs";
@@ -91,46 +91,80 @@ impl Drop for ChildCgroup<'_> {
             fd: _,
         } = self;
 
-        let pids = fs::read_to_string(path.as_ref().join(CGROUP_PROCS)).unwrap();
-        let mut dst = fs::OpenOptions::new()
-            .append(true)
-            .open(parent.path().join(CGROUP_PROCS))
-            .unwrap();
-        for pid in pids.split_inclusive('\n') {
-            dst.write_all(pid.as_bytes()).unwrap();
-        }
+        match (|| -> Result<()> {
+            let dst = parent.path().join(CGROUP_PROCS);
+            let mut dst = fs::OpenOptions::new()
+                .append(true)
+                .open(&dst)
+                .with_context(|| {
+                    format!(
+                        "fs::OpenOptions::new().append(true).open(\"{}\")",
+                        dst.display()
+                    )
+                })?;
+            let pids = path.as_ref().join(CGROUP_PROCS);
+            let pids = fs::read_to_string(&pids)
+                .with_context(|| format!("fs::read_to_string(\"{}\")", pids.display()))?;
+            for pid in pids.split_inclusive('\n') {
+                dst.write_all(pid.as_bytes())
+                    .with_context(|| format!("dst.write_all(\"{}\")", pid))?;
+            }
 
-        if let Err(e) = fs::remove_dir(&path) {
-            eprintln!("failed to remove {}: {e}", path.display());
+            fs::remove_dir(&path)
+                .with_context(|| format!("fs::remove_dir(\"{}\")", path.display()))?;
+            Ok(())
+        })() {
+            Ok(()) => (),
+            Err(err) => {
+                // Avoid panic in panic.
+                if std::thread::panicking() {
+                    eprintln!("{err:?}");
+                } else {
+                    panic!("{err:?}");
+                }
+            }
         }
     }
 }
 
 pub(crate) struct NetNsGuard {
     name: String,
-    old_ns: NetNs,
-    ns: Option<NetNs>,
+    old_ns: fs::File,
 }
 
 impl NetNsGuard {
+    const PERSIST_DIR: &str = "/var/run/netns/";
+
     pub(crate) fn new() -> Self {
-        let old_ns = get_from_current_thread().expect("Failed to get current netns");
+        let current_thread_netns_path = format!("/proc/self/task/{}/ns/net", nix::unistd::gettid());
+        let old_ns = fs::File::open(&current_thread_netns_path).unwrap_or_else(|err| {
+            panic!("fs::File::open(\"{current_thread_netns_path}\"): {err:?}")
+        });
 
         static COUNTER: AtomicU64 = AtomicU64::new(0);
         let pid = process::id();
         let name = format!("aya-test-{pid}-{}", COUNTER.fetch_add(1, Ordering::Relaxed));
 
-        let ns = NetNs::new(&name).unwrap_or_else(|e| panic!("failed to create netns {name}: {e}"));
+        fs::create_dir_all(Self::PERSIST_DIR)
+            .unwrap_or_else(|err| panic!("fs::create_dir_all(\"{}\"): {err:?}", Self::PERSIST_DIR));
+        let ns_path = Path::new(Self::PERSIST_DIR).join(&name);
+        let _: fs::File = fs::File::create(&ns_path)
+            .unwrap_or_else(|err| panic!("fs::File::create(\"{}\"): {err:?}", ns_path.display()));
+        nix::sched::unshare(nix::sched::CloneFlags::CLONE_NEWNET)
+            .expect("nix::sched::unshare(CLONE_NEWNET)");
+
+        nix::mount::mount(
+            Some(current_thread_netns_path.as_str()),
+            &ns_path,
+            Some("none"),
+            nix::mount::MsFlags::MS_BIND,
+            None::<&str>,
+        )
+        .expect("nix::mount::mount");
 
-        ns.enter()
-            .unwrap_or_else(|e| panic!("failed to enter network namespace {name}: {e}"));
         println!("entered network namespace {name}");
 
-        let ns = Self {
-            old_ns,
-            ns: Some(ns),
-            name,
-        };
+        let ns = Self { old_ns, name };
 
         // By default, the loopback in a new netns is down. Set it up.
         let lo = CString::new("lo").unwrap();
@@ -153,17 +187,28 @@ impl NetNsGuard {
 
 impl Drop for NetNsGuard {
     fn drop(&mut self) {
-        let Self { old_ns, ns, name } = self;
-        // Avoid panic in panic.
-        if let Err(e) = old_ns.enter() {
-            eprintln!("failed to return to original netns: {e}");
-        }
-        if let Some(ns) = ns.take() {
-            if let Err(e) = ns.remove() {
-                eprintln!("failed to remove netns {name}: {e}");
+        let Self { old_ns, name } = self;
+        match (|| -> Result<()> {
+            nix::sched::setns(old_ns, nix::sched::CloneFlags::CLONE_NEWNET)
+                .context("nix::sched::setns(_, CLONE_NEWNET)")?;
+            let ns_path = Path::new(Self::PERSIST_DIR).join(&name);
+            nix::mount::umount2(&ns_path, nix::mount::MntFlags::MNT_DETACH).with_context(|| {
+                format!("nix::mount::umount2(\"{}\", MNT_DETACH)", ns_path.display())
+            })?;
+            fs::remove_file(&ns_path)
+                .with_context(|| format!("fs::remove_file(\"{}\")", ns_path.display()))?;
+            Ok(())
+        })() {
+            Ok(()) => (),
+            Err(err) => {
+                // Avoid panic in panic.
+                if std::thread::panicking() {
+                    eprintln!("{err:?}");
+                } else {
+                    panic!("{err:?}");
+                }
             }
         }
-        println!("exited network namespace {name}");
     }
 }