diff --git a/Cargo.toml b/Cargo.toml index 048494b9..b8edb0b5 100644 --- a/Cargo.toml +++ b/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 } diff --git a/test/integration-test/Cargo.toml b/test/integration-test/Cargo.toml index 916db163..d18e6fe9 100644 --- a/test/integration-test/Cargo.toml +++ b/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"] } diff --git a/test/integration-test/src/utils.rs b/test/integration-test/src/utils.rs index 6a53775e..bad1f4af 100644 --- a/test/integration-test/src/utils.rs +++ b/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, + 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(¤t_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}"); } }