mirror of
https://codeberg.org/icewind/netnsd.git
synced 2026-06-03 09:04:07 +02:00
track which namespaces we control
This commit is contained in:
parent
d168b4bf4d
commit
32fc90debe
5 changed files with 59 additions and 14 deletions
|
|
@ -1,11 +1,25 @@
|
|||
use serde::de::{Error, Unexpected, Visitor};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::ffi::OsString;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct NamespaceName(String);
|
||||
|
||||
impl TryFrom<OsString> for NamespaceName {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: OsString) -> Result<Self, Self::Error> {
|
||||
let str = value.into_string().map_err(|_| ())?;
|
||||
if validate_name(&str) {
|
||||
Ok(NamespaceName(str))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NamespaceName {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
|
|
@ -80,7 +94,7 @@ fn validate_name(name: &str) -> bool {
|
|||
|
||||
#[test]
|
||||
fn test_de() {
|
||||
use serde_test::{assert_de_tokens, assert_de_tokens_error, Token};
|
||||
use serde_test::{Token, assert_de_tokens, assert_de_tokens_error};
|
||||
|
||||
assert_de_tokens(&NamespaceName("foo".into()), &[Token::String("foo")]);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub fn daemon(config: Config) -> MainResult {
|
|||
}
|
||||
|
||||
async fn daemon_async(mut config: Config) -> Result<(), DaemonError> {
|
||||
let mut state = State::default();
|
||||
let mut state = State::new()?;
|
||||
state.update(&config)?;
|
||||
|
||||
// now the namespaces are setup, we can tell systemd to start any service depending on them
|
||||
|
|
@ -104,12 +104,18 @@ enum Event {
|
|||
Info,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct State {
|
||||
namespaces: Vec<ActiveNamespace>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Result<Self, DaemonError> {
|
||||
let namespaces = NetNs::existing()?.map(ActiveNamespace::new).collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(State {
|
||||
namespaces
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update(&mut self, config: &Config) -> Result<(), DaemonError> {
|
||||
for removed in self.namespaces.extract_if(.., |namespace| {
|
||||
config.get_namespace(namespace.name()).is_none()
|
||||
|
|
@ -119,7 +125,7 @@ impl State {
|
|||
|
||||
for new in &config.namespaces {
|
||||
if !self.has_namespace(&new.name) {
|
||||
self.namespaces.push(ActiveNamespace::new(new)?);
|
||||
self.namespaces.push(ActiveNamespace::new(new.name.clone())?);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,16 +150,13 @@ struct ActiveNamespace {
|
|||
}
|
||||
|
||||
impl ActiveNamespace {
|
||||
pub fn new(config: &NamespaceConfig) -> Result<Self, DaemonError> {
|
||||
let ns = NetNs::new(config.name.clone())?;
|
||||
pub fn new(name: NamespaceName) -> Result<Self, DaemonError> {
|
||||
let ns = NetNs::new(name)?;
|
||||
|
||||
let mut namespace = ActiveNamespace {
|
||||
Ok(ActiveNamespace {
|
||||
ns,
|
||||
proxies: Vec::default(),
|
||||
};
|
||||
namespace.update_proxies(config)?;
|
||||
|
||||
Ok(namespace)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_proxies(&mut self, config: &NamespaceConfig) -> Result<(), DaemonError> {
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ mod raw;
|
|||
use crate::config::NamespaceName;
|
||||
use crate::link::{LinkError, link_up_ns};
|
||||
use crate::namespace::raw::{NamespaceSetupError, create_network_namespace};
|
||||
use either::Either;
|
||||
use nix::errno::Errno;
|
||||
use nix::mount::{MntFlags, MsFlags, mount, umount2};
|
||||
use std::fs::{File, create_dir, remove_file};
|
||||
use std::fs::{File, create_dir, read_dir, remove_file};
|
||||
use std::io::{Error as IoError, ErrorKind};
|
||||
use std::iter::empty;
|
||||
use std::os::unix::fs::symlink;
|
||||
use std::path::{Path, PathBuf};
|
||||
use thiserror::Error;
|
||||
|
|
@ -15,9 +17,26 @@ use tracing::{debug, error, info};
|
|||
pub struct NetNs {
|
||||
name: NamespaceName,
|
||||
path: PathBuf,
|
||||
nsd_path: PathBuf,
|
||||
}
|
||||
|
||||
impl NetNs {
|
||||
pub fn existing() -> Result<impl Iterator<Item = NamespaceName>, NamespaceError> {
|
||||
let dir = match read_dir("/var/run/netnsd") {
|
||||
Ok(dir) => Ok(dir),
|
||||
Err(error) if error.kind() == ErrorKind::NotFound => {
|
||||
return Ok(Either::Left(empty()));
|
||||
}
|
||||
Err(error) => Err(NamespaceError::Scan {
|
||||
path: "/var/run/netnsd".into(),
|
||||
error,
|
||||
}),
|
||||
}?;
|
||||
Ok(Either::Right(dir.flatten().flat_map(|entry| {
|
||||
NamespaceName::try_from(entry.file_name()).ok()
|
||||
})))
|
||||
}
|
||||
|
||||
/// 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");
|
||||
|
|
@ -41,7 +60,8 @@ impl NetNs {
|
|||
}
|
||||
|
||||
return Ok(NetNs {
|
||||
name: name.clone(),
|
||||
name,
|
||||
nsd_path,
|
||||
path,
|
||||
});
|
||||
}
|
||||
|
|
@ -57,7 +77,7 @@ impl NetNs {
|
|||
path: nsd_path.clone(),
|
||||
})?;
|
||||
}
|
||||
Result::<_, NamespaceError>::Ok(NetNs { name, path })
|
||||
Result::<_, NamespaceError>::Ok(NetNs { name, path, nsd_path })
|
||||
})?;
|
||||
|
||||
ns.setup_interfaces()?;
|
||||
|
|
@ -121,6 +141,10 @@ impl NetNs {
|
|||
error,
|
||||
path: self.path,
|
||||
})?;
|
||||
remove_file(&self.nsd_path).map_err(|error| NamespaceError::Delete {
|
||||
error,
|
||||
path: self.nsd_path,
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -145,6 +169,8 @@ pub enum NamespaceError {
|
|||
UnMount(Errno),
|
||||
#[error("Failed to setup loopback inside namespace: {0:#}")]
|
||||
Link(#[from] LinkError),
|
||||
#[error("Failed to scan {} for namespaces: {error:#}", path.display())]
|
||||
Scan { path: PathBuf, error: IoError },
|
||||
}
|
||||
|
||||
impl NamespaceError {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue