use nix::errno::Errno; use nix::sched::{clone, CloneFlags}; use nix::sys::signal::Signal; use nix::sys::wait::{waitpid, WaitStatus}; use std::path::PathBuf; use thiserror::Error; #[derive(Debug, Error)] pub enum NamespaceSetupError { #[error("Failed to spawn netns thread: {0}")] Spawn(Errno), #[error("Failed to wait for netns thread exit: {0}")] Wait(Errno), #[error("Unexpected status for netns thread: {0:?}")] Status(WaitStatus), } /// Create a new network namespace and call the provided handler with it's path /// /// the namespace will be cleaned up after the handler finishes unless the handler bind mounts the path pub fn create_network_namespace>( handler: impl FnOnce(PathBuf) -> Result, ) -> Result { let mut stack = vec![0; 8 * 1024]; // Safety: we don't execute any code in the new thread // so we use no stack space and don't call non signal-safe syscalls. let pid = unsafe { clone( Box::new(|| 0), &mut stack, CloneFlags::CLONE_NEWNET, Some(Signal::SIGCHLD as i32), ) } .map_err(NamespaceSetupError::Spawn)?; let path = PathBuf::from(format!("/proc/{}/ns/net", pid)); let result = handler(path); match waitpid(pid, None) { Err(error) => Err(NamespaceSetupError::Wait(error)), Ok(WaitStatus::Exited(_, _)) => Ok(()), Ok(status) => Err(NamespaceSetupError::Status(status)), }?; result }