mirror of
https://codeberg.org/icewind/netnsd.git
synced 2026-06-03 09:04:07 +02:00
move code around
This commit is contained in:
parent
3b7d53f693
commit
9af09c8669
7 changed files with 9 additions and 10 deletions
112
src/namespace/mod.rs
Normal file
112
src/namespace/mod.rs
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
mod raw;
|
||||
|
||||
use crate::config::NamespaceName;
|
||||
use crate::link::{LinkError, link_up_ns};
|
||||
use crate::namespace::raw::{
|
||||
NamespaceSetupError, create_network_namespace,
|
||||
};
|
||||
use nix::errno::Errno;
|
||||
use nix::mount::{MsFlags, mount, umount2, MntFlags};
|
||||
use std::fs::{File, create_dir_all, remove_file};
|
||||
use std::io::{Error as IoError, ErrorKind};
|
||||
use std::path::{Path, PathBuf};
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
pub struct NetNs {
|
||||
name: NamespaceName,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl NetNs {
|
||||
/// Create a new named network namespace that will be removed when dropped
|
||||
pub fn new(name: NamespaceName) -> Result<Self, NamespaceError> {
|
||||
let parent = Path::new("/var/run/netns");
|
||||
create_dir_all(parent).map_err(NamespaceError::Parent)?;
|
||||
let path = parent.join(&name);
|
||||
|
||||
match File::create_new(&path) {
|
||||
Ok(_) => {}
|
||||
Err(e) if e.kind() == ErrorKind::AlreadyExists => {
|
||||
info!(%name, "using existing network namespace");
|
||||
return Ok(NetNs {
|
||||
name: name.clone(),
|
||||
path,
|
||||
});
|
||||
}
|
||||
Err(e) => return Err(NamespaceError::from_create(path.clone(), e)),
|
||||
}
|
||||
info!(%name, "creating network namespace");
|
||||
|
||||
let ns = create_network_namespace(move |ns| {
|
||||
bind_namespace(&ns, &path)?;
|
||||
Result::<_, NamespaceError>::Ok(NetNs {
|
||||
name,
|
||||
path,
|
||||
})
|
||||
})?;
|
||||
|
||||
ns.setup_interfaces()?;
|
||||
|
||||
Ok(ns)
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_namespace(namespace: &Path, path: &Path) -> Result<(), NamespaceError> {
|
||||
debug!(namespace = %namespace.display(), path = %path.display(), "mounting namespace");
|
||||
mount(
|
||||
Some(namespace.as_os_str()),
|
||||
path.as_os_str(),
|
||||
Option::<&str>::None,
|
||||
MsFlags::MS_BIND,
|
||||
Option::<&str>::None,
|
||||
)
|
||||
.map_err(NamespaceError::Mount)
|
||||
}
|
||||
|
||||
impl NetNs {
|
||||
pub fn name(&self) -> &NamespaceName {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn setup_interfaces(&self) -> Result<(), NamespaceError> {
|
||||
link_up_ns(&self.path, "lo")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for NetNs {
|
||||
fn drop(&mut self) {
|
||||
let name = self.path.file_name().unwrap().to_str().unwrap();
|
||||
info!(name, "deleting network namespace");
|
||||
if let Err(error) = umount2(&self.path, MntFlags::MNT_DETACH) {
|
||||
error!(%error, path = %self.path.display(), "Failed to unmount network namespace");
|
||||
}
|
||||
if let Err(error) = remove_file(&self.path) {
|
||||
error!(%error, path = %self.path.display(), "Failed to remove namespace file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum NamespaceError {
|
||||
#[error("Failed to create parent directory for namespaces (/var/run/netns): {0:#}")]
|
||||
Parent(IoError),
|
||||
#[error("Failed to create namespace file {}: {error:#}", path.display())]
|
||||
Create { path: PathBuf, error: IoError },
|
||||
#[error("Failed to setup namespace: {0:#}")]
|
||||
Setup(#[from] NamespaceSetupError),
|
||||
#[error("Failed to bind-mount netns handle: {0:?}")]
|
||||
Mount(Errno),
|
||||
#[error("Failed to setup loopback inside namespace: {0:#}")]
|
||||
Link(#[from] LinkError),
|
||||
}
|
||||
|
||||
impl NamespaceError {
|
||||
fn from_create(path: PathBuf, error: IoError) -> Self {
|
||||
NamespaceError::Create {
|
||||
path,
|
||||
error,
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/namespace/raw.rs
Normal file
49
src/namespace/raw.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use nix::errno::Errno;
|
||||
use nix::sched::{CloneFlags, clone};
|
||||
use nix::sys::signal::Signal;
|
||||
use nix::sys::wait::{WaitStatus, waitpid};
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
use tracing::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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue