netnsd/src/namespace/raw.rs
2026-05-01 15:25:30 +02:00

48 lines
1.5 KiB
Rust

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<T, E: From<NamespaceSetupError>>(
handler: impl FnOnce(PathBuf) -> Result<T, E>,
) -> Result<T, E> {
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
}