mirror of
https://codeberg.org/icewind/netnsd.git
synced 2026-06-03 17:14:06 +02:00
basic netns management
This commit is contained in:
commit
a4c7b3c1c9
17 changed files with 1555 additions and 0 deletions
55
src/daemon/mod.rs
Normal file
55
src/daemon/mod.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
mod namespace;
|
||||
|
||||
use crate::config::{Config, NamespaceName};
|
||||
use crate::daemon::namespace::{NamespaceError, NetNs};
|
||||
use main_error::MainResult;
|
||||
use sd_notify::{notify, NotifyState};
|
||||
use std::io::Error as IoError;
|
||||
use thiserror::Error;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::signal::ctrl_c;
|
||||
|
||||
pub fn daemon(config: Config) -> MainResult {
|
||||
let rt = Runtime::new()?;
|
||||
Ok(rt.block_on(daemon_async(config))?)
|
||||
}
|
||||
|
||||
async fn daemon_async(config: Config) -> Result<(), DaemonError> {
|
||||
for namespace in &config.namespaces {
|
||||
println!("{}:", namespace.name);
|
||||
for forward in &namespace.forward {
|
||||
println!(" {} => {}", forward.source, forward.destination);
|
||||
}
|
||||
}
|
||||
|
||||
let namespaces = config
|
||||
.namespaces
|
||||
.iter()
|
||||
.map(|ns| ActiveNamespace::new(&ns.name))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// now the namespaces are setup, we can tell systemd to start any service depending on them
|
||||
notify(true, &[NotifyState::Ready]).map_err(DaemonError::Notify)?;
|
||||
|
||||
let _ = ctrl_c().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct ActiveNamespace {
|
||||
ns: NetNs,
|
||||
}
|
||||
|
||||
impl ActiveNamespace {
|
||||
pub fn new(name: &NamespaceName) -> Result<Self, DaemonError> {
|
||||
let ns = NetNs::new(name)?;
|
||||
Ok(ActiveNamespace { ns })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DaemonError {
|
||||
#[error(transparent)]
|
||||
Namespace(#[from] NamespaceError),
|
||||
#[error("Error sending notification to systemd: {0:#}")]
|
||||
Notify(IoError)
|
||||
}
|
||||
87
src/daemon/namespace.rs
Normal file
87
src/daemon/namespace.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
use crate::config::NamespaceName;
|
||||
use nix::errno::Errno;
|
||||
use nix::mount::{MsFlags, mount, umount};
|
||||
use nix::sched::{CloneFlags, unshare};
|
||||
use std::fs::{File, create_dir_all, remove_file};
|
||||
use std::io::{Error as IoError, ErrorKind};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::thread::{JoinHandle, spawn};
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, error};
|
||||
|
||||
pub struct NetNs {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl NetNs {
|
||||
/// Create a new named network namespace that will be removed when dropped
|
||||
pub fn new(name: &NamespaceName) -> Result<Self, NamespaceError> {
|
||||
debug!(%name, "creating network namespace");
|
||||
let parent = Path::new("/var/run/netns");
|
||||
create_dir_all(parent).map_err(NamespaceError::Parent)?;
|
||||
let path = parent.join(name);
|
||||
let mount_path = path.clone();
|
||||
|
||||
let _ =
|
||||
File::create_new(&path).map_err(|error| NamespaceError::from_create(name, error))?;
|
||||
|
||||
let handle: JoinHandle<Result<(), NamespaceError>> = spawn(move || {
|
||||
unshare(CloneFlags::CLONE_NEWNET).map_err(NamespaceError::Unshare)?;
|
||||
mount(
|
||||
Some("/proc/self/ns/net"),
|
||||
&mount_path,
|
||||
Option::<&str>::None,
|
||||
MsFlags::MS_BIND,
|
||||
Option::<&str>::None,
|
||||
)
|
||||
.map_err(NamespaceError::from_mount)?;
|
||||
Ok(())
|
||||
});
|
||||
handle.join().unwrap()?;
|
||||
Ok(NetNs { path })
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for NetNs {
|
||||
fn drop(&mut self) {
|
||||
let name = self.path.file_name().unwrap().to_str().unwrap();
|
||||
debug!(name, "deleting network namespace");
|
||||
if let Err(error) = umount(&self.path) {
|
||||
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("Network namespace {0} already exists")]
|
||||
AlreadyExists(NamespaceName),
|
||||
#[error("Failed to create namespace file {}: {error:#}", path.display())]
|
||||
Create { path: PathBuf, error: IoError },
|
||||
#[error("Unexpected error while creating new network namespace: {0:}")]
|
||||
Unshare(Errno),
|
||||
#[error("Unexpected error while binding new network namespace: {0:}")]
|
||||
Bind(Errno),
|
||||
}
|
||||
|
||||
impl NamespaceError {
|
||||
fn from_mount(errno: Errno) -> Self {
|
||||
// todo more specific errors?
|
||||
NamespaceError::Bind(errno)
|
||||
}
|
||||
|
||||
fn from_create(name: &NamespaceName, error: IoError) -> Self {
|
||||
match error.kind() {
|
||||
ErrorKind::AlreadyExists => NamespaceError::AlreadyExists(name.clone()),
|
||||
_ => NamespaceError::Create {
|
||||
path: Path::new("/var/run/netns").join(name),
|
||||
error,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue