use std::fs::File;
use std::io;
use std::os::fd::{AsFd, BorrowedFd, OwnedFd};
use std::path::Path;
use nix::sched::{CloneFlags, setns};
#[derive(Debug)]
pub struct NetNs {
fd: OwnedFd,
label: String,
}
impl NetNs {
pub fn from_path(path: impl AsRef<Path>, label: impl Into<String>) -> io::Result<Self> {
let file = File::open(path.as_ref())?;
Ok(Self {
fd: OwnedFd::from(file),
label: label.into(),
})
}
pub fn from_name(name: &str) -> io::Result<Self> {
Self::from_path(format!("/run/netns/{name}"), name)
}
pub fn from_pid(pid: u32) -> io::Result<Self> {
Self::from_path(format!("/proc/{pid}/ns/net"), format!("pid:{pid}"))
}
pub fn current() -> io::Result<Self> {
Self::from_path("/proc/self/ns/net", "current")
}
pub fn label(&self) -> &str {
&self.label
}
pub fn inode(&self) -> io::Result<u64> {
use std::os::fd::AsRawFd;
let mut st: libc::stat = unsafe { std::mem::zeroed() };
let rc = unsafe { libc::fstat(self.fd.as_raw_fd(), &mut st) };
if rc != 0 {
return Err(io::Error::last_os_error());
}
Ok(st.st_ino as u64)
}
pub fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
pub fn try_clone(&self) -> io::Result<Self> {
Ok(Self {
fd: self.fd.try_clone()?,
label: self.label.clone(),
})
}
pub fn run_in<T, F>(&self, f: F) -> io::Result<T>
where
F: FnOnce() -> T + Send,
T: Send,
{
std::thread::scope(|scope| {
let handle = scope.spawn(|| -> io::Result<T> {
setns(self.fd.as_fd(), CloneFlags::CLONE_NEWNET)
.map_err(|e| io::Error::from_raw_os_error(e as i32))?;
Ok(f())
});
match handle.join() {
Ok(res) => res,
Err(_) => Err(io::Error::other("netns worker thread panicked")),
}
})
}
}