mirror of
https://codeberg.org/icewind/netnsd.git
synced 2026-06-03 09:04:07 +02:00
it seems to be working now
This commit is contained in:
parent
ec6c3a0a8b
commit
b4cf0acb44
11 changed files with 398 additions and 64 deletions
201
Cargo.lock
generated
201
Cargo.lock
generated
|
|
@ -67,6 +67,12 @@ version = "2.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.1"
|
||||
|
|
@ -151,12 +157,84 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diatomic-waker"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
|
|
@ -181,6 +259,12 @@ version = "0.5.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
|
|
@ -325,6 +409,18 @@ dependencies = [
|
|||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getset"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912"
|
||||
dependencies = [
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.0"
|
||||
|
|
@ -346,6 +442,12 @@ dependencies = [
|
|||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.0"
|
||||
|
|
@ -380,6 +482,15 @@ version = "0.2.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
|
|
@ -431,6 +542,35 @@ dependencies = [
|
|||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "neli"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e328dd8a89fa4992c0628ef4ebd9c0b432a5e92522abead20b0f5a8f7bcb812"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"derive_builder",
|
||||
"getset",
|
||||
"libc",
|
||||
"log",
|
||||
"neli-proc-macros",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "neli-proc-macros"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e502fe5db321c6e0ae649ccda600675680125a8e8dee327744fe1910b19332"
|
||||
dependencies = [
|
||||
"either",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "netnsd"
|
||||
version = "0.1.0"
|
||||
|
|
@ -440,6 +580,7 @@ dependencies = [
|
|||
"futures-concurrency",
|
||||
"humansize",
|
||||
"main_error",
|
||||
"neli",
|
||||
"nix",
|
||||
"sd-notify",
|
||||
"serde",
|
||||
|
|
@ -491,6 +632,29 @@ version = "2.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.10"
|
||||
|
|
@ -523,6 +687,28 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
|
|
@ -541,6 +727,15 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.13"
|
||||
|
|
@ -570,6 +765,12 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sd-notify"
|
||||
version = "0.4.5"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ sd-notify = "0.4.5"
|
|||
futures = "0.3.31"
|
||||
futures-concurrency = "7.6.3"
|
||||
humansize = { version = "2.1.3", features = ["no_alloc"] }
|
||||
neli = "0.7.1"
|
||||
|
||||
[dev-dependencies]
|
||||
serde_test = "1.0.177"
|
||||
|
|
@ -53,7 +53,6 @@ in {
|
|||
# symlink instead of passing `configFile` directly to netnsd to allow changing the config without changing the path
|
||||
environment.etc."netnsd/netnsd.toml".source = configFile;
|
||||
|
||||
|
||||
systemd.services.netcsctl = {
|
||||
reloadTriggers = [configFile];
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ impl<'de> Deserialize<'de> for ForwardDestination {
|
|||
|
||||
#[test]
|
||||
fn test_de() {
|
||||
use serde_test::{Token, assert_de_tokens, assert_de_tokens_error};
|
||||
use serde_test::{assert_de_tokens, assert_de_tokens_error, Token};
|
||||
|
||||
let addr_str = "127.0.0.1:80";
|
||||
let addr = SocketAddr::from_str("127.0.0.1:80").unwrap();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use serde::de::{Error, Unexpected, Visitor};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::path::Path;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use serde::de::{Error, Unexpected, Visitor};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct NamespaceName(String);
|
||||
|
|
@ -41,8 +41,7 @@ impl<'de> Deserialize<'de> for NamespaceName {
|
|||
type Value = NamespaceName;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
||||
formatter
|
||||
.write_str("A valid namespace name")
|
||||
formatter.write_str("A valid namespace name")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
|
|
@ -57,7 +56,7 @@ impl<'de> Deserialize<'de> for NamespaceName {
|
|||
|
||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error
|
||||
E: Error,
|
||||
{
|
||||
if !validate_name(&v) {
|
||||
return Err(E::invalid_value(Unexpected::Str(&v), &self));
|
||||
|
|
@ -75,17 +74,18 @@ fn validate_name(name: &str) -> bool {
|
|||
if name.is_empty() {
|
||||
return false;
|
||||
}
|
||||
name.bytes().all(|b| b.is_ascii_alphanumeric() || [b'_', b'.', b'-'].contains(&b))
|
||||
name.bytes()
|
||||
.all(|b| b.is_ascii_alphanumeric() || [b'_', b'.', b'-'].contains(&b))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_de() {
|
||||
use serde_test::{Token, assert_de_tokens, assert_de_tokens_error};
|
||||
use serde_test::{assert_de_tokens, assert_de_tokens_error, Token};
|
||||
|
||||
assert_de_tokens(&NamespaceName("foo".into()), &[Token::String("foo")]);
|
||||
|
||||
assert_de_tokens_error::<NamespaceName>(
|
||||
&[Token::String("foo/bar")],
|
||||
"invalid value: integer `-80`, expected Either a port as integer, or a string containing a socket address",
|
||||
"invalid value: string \"foo/bar\", expected A valid namespace name",
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ impl<'de> Deserialize<'de> for ForwardSource {
|
|||
|
||||
#[test]
|
||||
fn test_de() {
|
||||
use serde_test::{Token, assert_de_tokens, assert_de_tokens_error};
|
||||
use serde_test::{assert_de_tokens, assert_de_tokens_error, Token};
|
||||
|
||||
let addr_str = "127.0.0.1:80";
|
||||
let addr = SocketAddr::from_str("127.0.0.1:80").unwrap();
|
||||
|
|
|
|||
68
src/daemon/link.rs
Normal file
68
src/daemon/link.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
use neli::consts::nl::NlmF;
|
||||
use neli::consts::rtnl::Ifla;
|
||||
use neli::consts::rtnl::RtAddrFamily;
|
||||
use neli::consts::rtnl::Rtm;
|
||||
use neli::consts::socket::NlFamily;
|
||||
use neli::err::RouterError;
|
||||
use neli::nl::NlPayload;
|
||||
use neli::router::synchronous::NlRouter;
|
||||
use neli::rtnl::Ifinfomsg;
|
||||
use neli::rtnl::IfinfomsgBuilder;
|
||||
use neli::utils::Groups;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum LinkError {
|
||||
#[error("failed to communicate with netlink")]
|
||||
Netlink,
|
||||
#[error("failed to code netlink response")]
|
||||
Parse,
|
||||
}
|
||||
|
||||
impl<T, P> From<RouterError<T, P>> for LinkError {
|
||||
fn from(_value: RouterError<T, P>) -> Self {
|
||||
LinkError::Netlink
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a link to UP
|
||||
pub fn up(link_name: &str) -> Result<(), LinkError> {
|
||||
// I honestly don't really know how this code works
|
||||
// It's mostly a copy from one of neli's examples and seems to do what it needs to
|
||||
let (rtnl, _) = NlRouter::connect(NlFamily::Route, None, Groups::empty())?;
|
||||
rtnl.enable_ext_ack(true)?;
|
||||
rtnl.enable_strict_checking(true)?;
|
||||
let ifinfomsg = IfinfomsgBuilder::default()
|
||||
.ifi_family(RtAddrFamily::Inet)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let recv = rtnl.send::<_, _, Rtm, Ifinfomsg>(
|
||||
Rtm::Getlink,
|
||||
NlmF::DUMP | NlmF::ACK,
|
||||
NlPayload::Payload(ifinfomsg),
|
||||
)?;
|
||||
for response in recv {
|
||||
if let Some(payload) = response?.get_payload() {
|
||||
let name = payload
|
||||
.rtattrs()
|
||||
.get_attr_handle()
|
||||
.get_attr_payload_as_with_len::<String>(Ifla::Ifname)
|
||||
.map_err(|_| LinkError::Parse)?;
|
||||
if name == link_name {
|
||||
let up_msg = IfinfomsgBuilder::default()
|
||||
.ifi_family(RtAddrFamily::Inet)
|
||||
.ifi_index(*payload.ifi_index())
|
||||
.up()
|
||||
.build()
|
||||
.unwrap();
|
||||
rtnl.send::<_, _, Rtm, Ifinfomsg>(
|
||||
Rtm::Setlink,
|
||||
NlmF::ACK,
|
||||
NlPayload::Payload(up_msg),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
mod namespace;
|
||||
mod proxy;
|
||||
pub mod link;
|
||||
|
||||
use crate::config::{Config, ForwardConfig, NamespaceConfig, NamespaceName};
|
||||
use crate::daemon::namespace::{NamespaceError, NetNs};
|
||||
|
|
@ -163,7 +164,7 @@ impl ActiveNamespace {
|
|||
|
||||
for new in &config.forward {
|
||||
if !self.has_forward(new) {
|
||||
self.proxies.push(ActiveProxy::new(new)?);
|
||||
self.proxies.push(ActiveProxy::new(new, &config.name)?);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
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 nix::mount::{mount, umount, MsFlags};
|
||||
use nix::sched::{clone, CloneFlags};
|
||||
use nix::sys::signal::Signal;
|
||||
use nix::sys::wait::{waitpid, WaitStatus};
|
||||
use std::fs::{create_dir_all, remove_file, File};
|
||||
use std::io::{Error as IoError, ErrorKind};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::thread::{JoinHandle, spawn};
|
||||
use thiserror::Error;
|
||||
use tracing::{error, info};
|
||||
|
||||
|
|
@ -34,19 +35,48 @@ impl NetNs {
|
|||
Err(e) => return Err(NamespaceError::from_create(name, e)),
|
||||
}
|
||||
|
||||
let handle: JoinHandle<Result<(), NamespaceError>> = spawn(move || {
|
||||
unshare(CloneFlags::CLONE_NEWNET).map_err(NamespaceError::Unshare)?;
|
||||
mount(
|
||||
let mut stack = vec![0; 8 * 1024 * 1024];
|
||||
let pid = unsafe {
|
||||
clone(
|
||||
Box::new(move || {
|
||||
if let Err(e) = mount(
|
||||
Some("/proc/self/ns/net"),
|
||||
&mount_path,
|
||||
Option::<&str>::None,
|
||||
MsFlags::MS_BIND,
|
||||
Option::<&str>::None,
|
||||
) {
|
||||
return e as i32 as isize;
|
||||
}
|
||||
|
||||
if let Err(error) = super::link::up("lo") {
|
||||
error!(%error, "error setting link up");
|
||||
return 1;
|
||||
}
|
||||
|
||||
0
|
||||
}),
|
||||
&mut stack,
|
||||
CloneFlags::CLONE_NEWNET,
|
||||
Some(Signal::SIGCHLD as i32),
|
||||
)
|
||||
.map_err(NamespaceError::from_mount)?;
|
||||
Ok(())
|
||||
});
|
||||
handle.join().unwrap()?;
|
||||
}
|
||||
.map_err(NamespaceError::Clone)?;
|
||||
|
||||
match waitpid(pid, None).map_err(NamespaceError::Wait)? {
|
||||
WaitStatus::Exited(_, exit) => {
|
||||
if exit > 0 {
|
||||
if let Err(error) = remove_file(&path) {
|
||||
error!(%error, path = %path.display(), "Failed to remove namespace file after mount failure");
|
||||
}
|
||||
return Err(NamespaceError::from_mount(Errno::from_raw(exit)));
|
||||
}
|
||||
}
|
||||
status => {
|
||||
error!(?status, "unexpected wait status");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(NetNs {
|
||||
name: name.clone(),
|
||||
path,
|
||||
|
|
@ -79,10 +109,12 @@ pub enum NamespaceError {
|
|||
Parent(IoError),
|
||||
#[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 creating new network namespace with clone: {0:}")]
|
||||
Clone(Errno),
|
||||
#[error("Unexpected error while binding new network namespace: {0:}")]
|
||||
Bind(Errno),
|
||||
#[error("Unexpected error while waiting for network namespace thread: {0:}")]
|
||||
Wait(Errno),
|
||||
}
|
||||
|
||||
impl NamespaceError {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,18 @@
|
|||
mod tcp;
|
||||
|
||||
use std::fs::remove_file;
|
||||
use crate::config::{ForwardConfig, ForwardDestination, ForwardSource};
|
||||
use crate::config::{ForwardConfig, ForwardDestination, ForwardSource, NamespaceName};
|
||||
use crate::daemon::proxy::tcp::Proxy;
|
||||
use futures::future::AbortHandle;
|
||||
use nix::sched::{CloneFlags, setns};
|
||||
use std::fs::{File, remove_file};
|
||||
use std::io::Error as IoError;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::thread::spawn;
|
||||
use thiserror::Error;
|
||||
use tokio::runtime::Builder;
|
||||
use tracing::error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -25,6 +29,8 @@ pub enum ProxyError {
|
|||
destination: SocketAddr,
|
||||
error: IoError,
|
||||
},
|
||||
#[error("Failed to open namespace file {}: {error:#}", path.display())]
|
||||
OpenNamespace { path: PathBuf, error: IoError },
|
||||
}
|
||||
|
||||
pub struct ActiveProxy {
|
||||
|
|
@ -35,9 +41,44 @@ pub struct ActiveProxy {
|
|||
}
|
||||
|
||||
impl ActiveProxy {
|
||||
pub fn new(config: &ForwardConfig) -> Result<ActiveProxy, ProxyError> {
|
||||
pub fn new(
|
||||
config: &ForwardConfig,
|
||||
namespace: &NamespaceName,
|
||||
) -> Result<ActiveProxy, ProxyError> {
|
||||
let proxy = Proxy::listen(config.source.clone())?;
|
||||
Ok(proxy.run(config.destination.clone()))
|
||||
let stats = ProxyStats::default();
|
||||
|
||||
let (abort, abort_reg) = AbortHandle::new_pair();
|
||||
|
||||
let destination = config.destination.clone();
|
||||
let run_stats = stats.clone();
|
||||
let ns_path = PathBuf::from(format!("/var/run/netns/{namespace}"));
|
||||
let ns_handle = File::open(&ns_path).map_err(|error| ProxyError::OpenNamespace {
|
||||
error,
|
||||
path: ns_path,
|
||||
})?;
|
||||
spawn(move || match setns(ns_handle, CloneFlags::CLONE_NEWNET) {
|
||||
Ok(_) => {
|
||||
let rt = match Builder::new_current_thread().enable_io().build() {
|
||||
Ok(rt) => rt,
|
||||
Err(error) => {
|
||||
error!(%error, "Error setting up tokio runtime");
|
||||
return;
|
||||
}
|
||||
};
|
||||
rt.block_on(proxy.run(destination, abort_reg, run_stats));
|
||||
}
|
||||
Err(error) => {
|
||||
error!(%error, "Failed to join network namespace for proxy");
|
||||
}
|
||||
});
|
||||
|
||||
Ok(ActiveProxy {
|
||||
source: config.source.clone(),
|
||||
destination: config.destination.clone(),
|
||||
abort,
|
||||
stats,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use std::fs::remove_file;
|
||||
/// Loosely based on https://github.com/fooker/netns-proxy/blob/main/src/tcp.rs
|
||||
use crate::config::{ForwardDestination, ForwardSource};
|
||||
use crate::daemon::proxy::{ActiveProxy, ProxyError, ProxyStats};
|
||||
use crate::daemon::proxy::{ProxyError, ProxyStats};
|
||||
use futures::TryStreamExt;
|
||||
use futures::stream::{AbortHandle, AbortRegistration, Abortable};
|
||||
use futures::stream::{AbortRegistration, Abortable};
|
||||
use std::fs::{remove_file, set_permissions};
|
||||
use std::io::Error as IoError;
|
||||
use std::net::SocketAddr;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::pin::pin;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, copy_bidirectional};
|
||||
use tokio::net::{TcpListener, TcpSocket, TcpStream, UnixListener};
|
||||
|
|
@ -16,7 +17,6 @@ use tracing::{Level, debug, error, instrument, span, trace, warn};
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Proxy {
|
||||
source: ForwardSource,
|
||||
socket: ProxyListener,
|
||||
}
|
||||
|
||||
|
|
@ -41,30 +41,28 @@ impl Proxy {
|
|||
let socket = match &bind {
|
||||
ForwardSource::Unix(path) => {
|
||||
let _ = remove_file(path);
|
||||
UnixListener::bind(path).map(ProxyListener::Unix)
|
||||
},
|
||||
UnixListener::bind(path).map(|listener| {
|
||||
if let Err(error) = set_permissions(path, PermissionsExt::from_mode(0o666)) {
|
||||
error!(%error, "failed to set socket permissions");
|
||||
}
|
||||
ProxyListener::Unix(listener)
|
||||
})
|
||||
}
|
||||
ForwardSource::Ip(addr) => bind_tcp(*addr).map(ProxyListener::Tcp),
|
||||
}
|
||||
.map_err(|error| ProxyError::Bind {
|
||||
address: bind.clone(),
|
||||
address: bind,
|
||||
error,
|
||||
})?;
|
||||
debug!("Created TCP socket");
|
||||
|
||||
Ok(Self {
|
||||
source: bind,
|
||||
socket,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run(self, target: ForwardDestination) -> ActiveProxy {
|
||||
let (abort_handle, abort) = AbortHandle::new_pair();
|
||||
let destination = target.clone();
|
||||
|
||||
let stats = ProxyStats::default();
|
||||
|
||||
pub async fn run(self, target: ForwardDestination, abort: AbortRegistration, stats: ProxyStats) {
|
||||
let proxy_stats = stats.clone();
|
||||
spawn(async move {
|
||||
match self.socket {
|
||||
ProxyListener::Tcp(socket) => {
|
||||
run_tcp(socket, target.addr, abort, proxy_stats).await
|
||||
|
|
@ -73,13 +71,6 @@ impl Proxy {
|
|||
run_unix(socket, target.addr, abort, proxy_stats).await
|
||||
}
|
||||
}
|
||||
});
|
||||
ActiveProxy {
|
||||
source: self.source,
|
||||
destination,
|
||||
abort: abort_handle,
|
||||
stats,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue