mirror of
https://codeberg.org/icewind/netnsd.git
synced 2026-06-03 09:04:07 +02:00
add support for setting up routing inside the netns
This commit is contained in:
parent
35c8f5cc6c
commit
7588b5db00
18 changed files with 272 additions and 53 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -82,6 +82,15 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "cidr"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "579504560394e388085d0c080ea587dfa5c15f7e251b4d5247d1e1a61d1d6928"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.58"
|
||||
|
|
@ -509,6 +518,7 @@ dependencies = [
|
|||
name = "netnsd"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"cidr",
|
||||
"clap",
|
||||
"either",
|
||||
"futures",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ either = "1.15.0"
|
|||
uzers = "0.12.2"
|
||||
sysinfo = "0.38.1"
|
||||
landlock = "0.4.4"
|
||||
cidr = { version = "0.3.2", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_test = "1.0.177"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ A declarative manager for Linux network namespaces.
|
|||
- Hot reloading of configuration
|
||||
- Port forwarding into or out of the namespace
|
||||
- Moving network devices to the namespace
|
||||
- Setting up routing inside the namespace
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
@ -57,6 +58,11 @@ name = "test"
|
|||
# move existing devices into the namespace
|
||||
devices = ["somelink"]
|
||||
|
||||
# create a route inside the namespace
|
||||
[[namespace.route]]
|
||||
destination = "default" # either "default" or an ip range in CIDR notation
|
||||
device = "somelink"
|
||||
|
||||
# You can define any number of port forwards to setup into the namespace
|
||||
[[namespace.forward]]
|
||||
# port, address or socket outside the namespace to listen on
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ name = "test"
|
|||
# move existing devices into the namespace
|
||||
devices = ["somelink"]
|
||||
|
||||
# create a route inside the namespace
|
||||
[[namespace.route]]
|
||||
destination = "default" # either "default" or an ip range in CIDR notation
|
||||
device = "somelink"
|
||||
|
||||
# You can define any number of port forwards to setup into the namespace
|
||||
[[namespace.forward]]
|
||||
# port, address or socket outside the namespace to listen on
|
||||
|
|
|
|||
|
|
@ -60,6 +60,22 @@ in {
|
|||
default = [];
|
||||
description = "devices to move into the namespace";
|
||||
};
|
||||
route = mkOption {
|
||||
type = types.listOf (types.submodule ({config, ...}: {
|
||||
options = {
|
||||
device = mkOption {
|
||||
type = types.str;
|
||||
description = "device to route the traffic trough";
|
||||
};
|
||||
destination = mkOption {
|
||||
type = types.str;
|
||||
description = "What traffic to route. Either \"default\" or an ip range in CIDR notation";
|
||||
};
|
||||
};
|
||||
}));
|
||||
description = "routes to setup inside the namespace";
|
||||
default = [];
|
||||
};
|
||||
};
|
||||
}));
|
||||
description = "namespaces to setup";
|
||||
|
|
|
|||
|
|
@ -5,10 +5,15 @@ mod target;
|
|||
pub use crate::config::name::{DeviceName, NamespaceName};
|
||||
pub use crate::config::source::ForwardSource;
|
||||
pub use crate::config::target::ForwardTarget;
|
||||
use serde::Deserialize;
|
||||
use cidr::AnyIpCidr;
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs::read_to_string;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
use toml::from_str;
|
||||
|
||||
|
|
@ -84,6 +89,8 @@ pub struct NamespaceConfig {
|
|||
pub forward: Vec<ForwardConfig>,
|
||||
#[serde(default)]
|
||||
pub devices: Vec<DeviceName>,
|
||||
#[serde(default, rename = "route")]
|
||||
pub routes: Vec<RouteConfig>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
|
|
@ -94,6 +101,27 @@ pub struct ForwardConfig {
|
|||
pub reverse: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq, Clone)]
|
||||
pub struct RouteConfig {
|
||||
#[serde(deserialize_with = "parse_cidr")]
|
||||
pub destination: AnyIpCidr,
|
||||
pub device: DeviceName,
|
||||
}
|
||||
|
||||
impl Display for RouteConfig {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} dev {}", self.destination, self.device)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_cidr<'de, D: Deserializer<'de>>(deserializer: D) -> Result<AnyIpCidr, D::Error> {
|
||||
let str = Cow::<'de, str>::deserialize(deserializer)?;
|
||||
match str.as_ref() {
|
||||
"default" => Ok(AnyIpCidr::Any),
|
||||
str => AnyIpCidr::from_str(str).map_err(D::Error::custom),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ConfigError {
|
||||
#[error("Error while reading config from {}: {error:#}", path.display())]
|
||||
|
|
|
|||
|
|
@ -186,7 +186,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")]);
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ pub struct InvalidForwardSource {
|
|||
|
||||
#[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};
|
||||
|
||||
let addr_str = "127.0.0.1:80";
|
||||
let addr = SocketAddr::from_str("127.0.0.1:80").unwrap();
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ pub struct InvalidForwardTarget {
|
|||
|
||||
#[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};
|
||||
|
||||
let addr_str = "127.0.0.1:80";
|
||||
let addr = SocketAddr::from_str("127.0.0.1:80").unwrap();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use crate::config::{Config, DeviceName, ForwardConfig, NamespaceConfig, NamespaceName};
|
||||
use crate::config::{
|
||||
Config, DeviceName, ForwardConfig, NamespaceConfig, NamespaceName, RouteConfig,
|
||||
};
|
||||
use crate::link::{LinkError, LinkManager};
|
||||
use crate::namespace::{
|
||||
NamespaceEnterError, NamespaceError, NamespaceHandle, NamespaceHandleError, NetNs,
|
||||
|
|
@ -128,6 +130,7 @@ impl State {
|
|||
let config = config.get_namespace(namespace.name()).unwrap();
|
||||
namespace.update_proxies(config)?;
|
||||
namespace.update_devices(config)?;
|
||||
namespace.update_links(config)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -144,6 +147,7 @@ struct ActiveNamespace {
|
|||
ns: NetNs,
|
||||
proxies: Vec<ActiveProxy>,
|
||||
devices: Vec<DeviceName>,
|
||||
routes: Vec<RouteConfig>,
|
||||
}
|
||||
|
||||
impl ActiveNamespace {
|
||||
|
|
@ -154,6 +158,7 @@ impl ActiveNamespace {
|
|||
ns,
|
||||
proxies: Vec::default(),
|
||||
devices: Vec::default(),
|
||||
routes: Vec::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -212,6 +217,52 @@ impl ActiveNamespace {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_links(&mut self, config: &NamespaceConfig) -> Result<(), DaemonError> {
|
||||
let removed: Vec<_> = self
|
||||
.routes
|
||||
.extract_if(.., |existing| {
|
||||
!config.routes.iter().any(|new| existing == new)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut added = Vec::new();
|
||||
for new in &config.routes {
|
||||
if !self.has_route(new) {
|
||||
added.push(new.clone());
|
||||
}
|
||||
}
|
||||
|
||||
self.ns.handle().run_in(|| {
|
||||
let link_manager = LinkManager::new()?;
|
||||
for link in link_manager.get_links()?.flatten() {
|
||||
if let Some(route) = removed
|
||||
.iter()
|
||||
.find(|route| route.device == link.name.as_str())
|
||||
{
|
||||
info!(namespace = %config.name, %route, "deleting route");
|
||||
link_manager.delete_route(&link, route.destination)?;
|
||||
}
|
||||
}
|
||||
|
||||
for link in link_manager.get_links()?.flatten() {
|
||||
if let Some(route) = added
|
||||
.iter()
|
||||
.find(|route| route.device == link.name.as_str())
|
||||
{
|
||||
info!(namespace = %config.name, %route, "adding route");
|
||||
link_manager.add_route(&link, route.destination)?;
|
||||
}
|
||||
}
|
||||
Ok::<_, DaemonError>(())
|
||||
})??;
|
||||
|
||||
for new in added {
|
||||
self.routes.push(new);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_forward(&self, config: &ForwardConfig) -> bool {
|
||||
self.proxies.iter().any(|existing| existing == config)
|
||||
}
|
||||
|
|
@ -220,6 +271,10 @@ impl ActiveNamespace {
|
|||
self.devices.iter().any(|existing| existing == name)
|
||||
}
|
||||
|
||||
fn has_route(&self, route: &RouteConfig) -> bool {
|
||||
self.routes.iter().any(|existing| existing == route)
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &NamespaceName {
|
||||
self.ns.name()
|
||||
}
|
||||
|
|
|
|||
118
src/link.rs
118
src/link.rs
|
|
@ -1,26 +1,27 @@
|
|||
use cidr::{AnyIpCidr, Family};
|
||||
use neli::consts::nl::NlmF;
|
||||
use neli::consts::rtnl::Ifla;
|
||||
use neli::consts::rtnl::RtAddrFamily;
|
||||
use neli::consts::rtnl::Rtm;
|
||||
use neli::consts::rtnl::{Ifla, RtScope, RtTable, Rta};
|
||||
use neli::consts::rtnl::{RtAddrFamily, Rtn, Rtprot};
|
||||
use neli::consts::socket::NlFamily;
|
||||
use neli::err::RouterError;
|
||||
use neli::nl::NlPayload;
|
||||
use neli::router::synchronous::NlRouter;
|
||||
use neli::rtnl::IfinfomsgBuilder;
|
||||
use neli::rtnl::{Ifinfomsg, RtattrBuilder};
|
||||
use neli::rtnl::{Ifinfomsg, RtattrBuilder, Rtmsg};
|
||||
use neli::rtnl::{IfinfomsgBuilder, RtmsgBuilder};
|
||||
use neli::types::{Buffer, RtBuffer};
|
||||
use neli::utils::Groups;
|
||||
use nix::libc::c_int;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::os::fd::AsRawFd;
|
||||
use thiserror::Error;
|
||||
use tracing::info;
|
||||
use tracing::{info, instrument};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum LinkError {
|
||||
#[error("Failed to communicate with netlink:")]
|
||||
#[error("Failed to communicate with netlink: {0}")]
|
||||
Netlink(String),
|
||||
#[error("Failed to parse netlink response")]
|
||||
#[error("Failed to parse netlink response: {0}")]
|
||||
Parse(String),
|
||||
#[error("Link not found: {0}")]
|
||||
NotFound(String),
|
||||
|
|
@ -46,6 +47,12 @@ pub struct Link {
|
|||
pub name: String,
|
||||
}
|
||||
|
||||
impl Display for Link {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Link {
|
||||
fn msg_builder(&self) -> IfinfomsgBuilder {
|
||||
IfinfomsgBuilder::default()
|
||||
|
|
@ -112,7 +119,7 @@ impl LinkManager {
|
|||
info_attrs.push(
|
||||
RtattrBuilder::default()
|
||||
.rta_type(Ifla::NetNsFd)
|
||||
.rta_payload(Buffer::from(ns_handle.to_ne_bytes().as_slice()))
|
||||
.rta_payload(ns_handle)
|
||||
.build()
|
||||
.expect("invalid rtattr"),
|
||||
);
|
||||
|
|
@ -135,6 +142,47 @@ impl LinkManager {
|
|||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(link = %link, destination = %destination))]
|
||||
pub fn add_route(&self, link: &Link, destination: AnyIpCidr) -> Result<(), LinkError> {
|
||||
let rt_msg = route_message_for(link, destination);
|
||||
|
||||
let res = self.router.send::<_, _, Rtm, Rtmsg>(
|
||||
Rtm::Newroute,
|
||||
NlmF::CREATE | NlmF::EXCL | NlmF::REQUEST | NlmF::ACK,
|
||||
NlPayload::Payload(rt_msg),
|
||||
)?;
|
||||
|
||||
for msg in res {
|
||||
match msg {
|
||||
Err(RouterError::Nlmsgerr(err)) if *err.error() == -17 => {
|
||||
info!("route already exists");
|
||||
// already exists
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err.into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(link = %link, destination = %destination))]
|
||||
pub fn delete_route(&self, link: &Link, destination: AnyIpCidr) -> Result<(), LinkError> {
|
||||
let rt_msg = route_message_for(link, destination);
|
||||
|
||||
let res = self.router.send::<_, _, Rtm, Rtmsg>(
|
||||
Rtm::Delroute,
|
||||
NlmF::REQUEST | NlmF::ACK,
|
||||
NlPayload::Payload(rt_msg),
|
||||
)?;
|
||||
|
||||
for msg in res {
|
||||
msg?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Set a link to UP
|
||||
|
|
@ -147,6 +195,7 @@ pub fn link_up(link_name: &str) -> Result<(), LinkError> {
|
|||
/// Move a link into a namespace
|
||||
pub fn move_link_into<Fd: AsRawFd>(link_name: &str, namespace: &Fd) -> Result<(), LinkError> {
|
||||
let manager = LinkManager::new()?;
|
||||
// todo, might already be in target ns
|
||||
let link = manager.get_link(link_name)?;
|
||||
info!(name = &link.name, "moving link into namespace");
|
||||
manager.move_link(&link, namespace)
|
||||
|
|
@ -163,3 +212,54 @@ pub fn move_all_links<Fd: AsRawFd>(namespace: &Fd) -> Result<(), LinkError> {
|
|||
}
|
||||
Ok::<_, LinkError>(())
|
||||
}
|
||||
|
||||
fn route_message_for(link: &Link, destination: AnyIpCidr) -> Rtmsg {
|
||||
let mut info_attrs = RtBuffer::<Rta, Buffer>::new();
|
||||
match &destination {
|
||||
AnyIpCidr::V4(addr) => {
|
||||
info_attrs.push(
|
||||
RtattrBuilder::default()
|
||||
.rta_type(Rta::Dst)
|
||||
.rta_payload(addr.first_address().octets())
|
||||
.build()
|
||||
.expect("invalid rtattr"),
|
||||
);
|
||||
}
|
||||
AnyIpCidr::V6(addr) => {
|
||||
info_attrs.push(
|
||||
RtattrBuilder::default()
|
||||
.rta_type(Rta::Dst)
|
||||
.rta_payload(addr.first_address().octets())
|
||||
.build()
|
||||
.expect("invalid rtattr"),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
info_attrs.push(
|
||||
RtattrBuilder::default()
|
||||
.rta_type(Rta::Oif)
|
||||
.rta_payload(link.index)
|
||||
.build()
|
||||
.expect("invalid rtattr"),
|
||||
);
|
||||
|
||||
let family = match &destination.family() {
|
||||
None => RtAddrFamily::Inet,
|
||||
Some(Family::Ipv4) => RtAddrFamily::Inet,
|
||||
Some(Family::Ipv6) => RtAddrFamily::Inet6,
|
||||
};
|
||||
|
||||
RtmsgBuilder::default()
|
||||
.rtm_table(RtTable::Main)
|
||||
.rtm_scope(RtScope::Universe)
|
||||
.rtm_family(family)
|
||||
.rtattrs(info_attrs)
|
||||
.rtm_src_len(0)
|
||||
.rtm_tos(0)
|
||||
.rtm_protocol(Rtprot::Boot)
|
||||
.rtm_type(Rtn::Unicast)
|
||||
.rtm_dst_len(destination.network_length().unwrap_or_default())
|
||||
.build()
|
||||
.expect("rt msg")
|
||||
}
|
||||
|
|
|
|||
12
src/main.rs
12
src/main.rs
|
|
@ -5,10 +5,10 @@ use crate::proxy::proxy;
|
|||
use crate::up::up;
|
||||
use clap::{Parser, Subcommand};
|
||||
use main_error::MainResult;
|
||||
use std::path::PathBuf;
|
||||
use nix::errno::Errno;
|
||||
use nix::sys::signal::{kill, Signal};
|
||||
use nix::sys::signal::{Signal, kill};
|
||||
use nix::unistd::Pid;
|
||||
use std::path::PathBuf;
|
||||
use sysinfo::{ProcessRefreshKind, RefreshKind, System, UpdateKind};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
|
|
@ -94,10 +94,12 @@ fn reload() -> MainResult {
|
|||
match kill(Pid::from_raw(proc.pid().as_u32() as i32), Signal::SIGHUP) {
|
||||
Ok(_) => {
|
||||
info!("Sent reload command to daemon")
|
||||
},
|
||||
}
|
||||
Err(Errno::EPERM) => {
|
||||
error!("Sending signal not permitted, try are you running the command as root?");
|
||||
},
|
||||
error!(
|
||||
"Sending signal not permitted, try are you running the command as root?"
|
||||
);
|
||||
}
|
||||
Err(error) => {
|
||||
error!(%error, "Unexpected error");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::namespace::NamespaceEnterError;
|
||||
use nix::errno::Errno;
|
||||
use nix::sched::{setns, CloneFlags};
|
||||
use nix::sched::{CloneFlags, setns};
|
||||
use std::fs::File;
|
||||
use std::io::Error as IoError;
|
||||
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ mod handle;
|
|||
mod raw;
|
||||
|
||||
use crate::config::{DeviceName, NamespaceName};
|
||||
use crate::link::{link_up, move_all_links, move_link_into, LinkError};
|
||||
use crate::link::{LinkError, link_up, move_all_links, move_link_into};
|
||||
pub use crate::namespace::handle::{NamespaceHandle, NamespaceHandleError};
|
||||
use crate::namespace::raw::{create_network_namespace, NamespaceSetupError};
|
||||
use crate::namespace::raw::{NamespaceSetupError, create_network_namespace};
|
||||
use either::Either;
|
||||
use nix::errno::Errno;
|
||||
use nix::mount::{mount, umount2, MntFlags, MsFlags};
|
||||
use std::fs::{create_dir, read_dir, remove_file, File};
|
||||
use nix::mount::{MntFlags, MsFlags, mount, umount2};
|
||||
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;
|
||||
|
|
@ -162,9 +162,7 @@ impl NetNs {
|
|||
pub fn delete(self) -> Result<(), NamespaceError> {
|
||||
let parent_namespace = NamespaceHandle::parent()?;
|
||||
|
||||
self.handle.run_in(|| {
|
||||
move_all_links(&parent_namespace)
|
||||
})??;
|
||||
self.handle.run_in(|| move_all_links(&parent_namespace))??;
|
||||
let name = self.path.file_name().unwrap().to_str().unwrap();
|
||||
info!(name, "deleting network namespace");
|
||||
match umount2(&self.path, MntFlags::MNT_DETACH) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use nix::errno::Errno;
|
||||
use nix::sched::{clone, CloneFlags};
|
||||
use nix::sched::{CloneFlags, clone};
|
||||
use nix::sys::signal::Signal;
|
||||
use nix::sys::wait::{waitpid, WaitStatus};
|
||||
use nix::sys::wait::{WaitStatus, waitpid};
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,12 @@ mod tcp;
|
|||
use crate::config::{ForwardConfig, ForwardSource, ForwardTarget, NamespaceName};
|
||||
use crate::proxy::tcp::Proxy;
|
||||
use futures::future::AbortHandle;
|
||||
use landlock::{
|
||||
ABI, Access, AccessFs, AccessNet, NetPort, Ruleset, RulesetAttr, RulesetCreatedAttr,
|
||||
RulesetError, RulesetStatus,
|
||||
};
|
||||
use main_error::MainResult;
|
||||
use nix::errno::Errno;
|
||||
use nix::sched::{CloneFlags, setns};
|
||||
use nix::sys::signal::{SIGINT, kill};
|
||||
use nix::unistd::{Gid, Pid, Uid, setgid, setuid};
|
||||
|
|
@ -13,8 +18,6 @@ use std::net::SocketAddr;
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Child, Command};
|
||||
use std::thread::spawn;
|
||||
use landlock::{Access, AccessFs, AccessNet, NetPort, Ruleset, RulesetAttr, RulesetCreatedAttr, RulesetError, RulesetStatus, ABI};
|
||||
use nix::errno::Errno;
|
||||
use thiserror::Error;
|
||||
use tokio::runtime::Builder;
|
||||
use tokio::signal::ctrl_c;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/// Loosely based on https://github.com/fooker/netns-proxy/blob/main/src/tcp.rs
|
||||
use crate::config::{ForwardTarget, ForwardSource};
|
||||
use crate::proxy::{ProxyError};
|
||||
use crate::config::{ForwardSource, ForwardTarget};
|
||||
use crate::proxy::ProxyError;
|
||||
use futures::TryStreamExt;
|
||||
use futures::stream::{AbortRegistration, Abortable};
|
||||
use std::fs::{remove_file, set_permissions};
|
||||
|
|
@ -56,28 +56,18 @@ impl Proxy {
|
|||
})?;
|
||||
debug!("Created TCP socket");
|
||||
|
||||
Ok(Self {
|
||||
socket,
|
||||
})
|
||||
Ok(Self { socket })
|
||||
}
|
||||
|
||||
pub async fn run(self, target: ForwardTarget, abort: AbortRegistration) {
|
||||
match self.socket {
|
||||
ProxyListener::Tcp(socket) => {
|
||||
run_tcp(socket, target.addr, abort).await
|
||||
}
|
||||
ProxyListener::Unix(socket) => {
|
||||
run_unix(socket, target.addr, abort).await
|
||||
}
|
||||
ProxyListener::Tcp(socket) => run_tcp(socket, target.addr, abort).await,
|
||||
ProxyListener::Unix(socket) => run_unix(socket, target.addr, abort).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_tcp(
|
||||
socket: TcpListener,
|
||||
target: SocketAddr,
|
||||
abort: AbortRegistration,
|
||||
) {
|
||||
async fn run_tcp(socket: TcpListener, target: SocketAddr, abort: AbortRegistration) {
|
||||
let accepts = TcpListenerStream::new(socket).map_err(|error| ProxyError::Accept { error });
|
||||
let mut accepts = pin!(Abortable::new(accepts, abort));
|
||||
while let Some(client) = accepts.next().await {
|
||||
|
|
@ -94,11 +84,7 @@ async fn run_tcp(
|
|||
}
|
||||
}
|
||||
|
||||
async fn run_unix(
|
||||
socket: UnixListener,
|
||||
target: SocketAddr,
|
||||
abort: AbortRegistration,
|
||||
) {
|
||||
async fn run_unix(socket: UnixListener, target: SocketAddr, abort: AbortRegistration) {
|
||||
let accepts = UnixListenerStream::new(socket).map_err(|error| ProxyError::Accept { error });
|
||||
let mut accepts = pin!(Abortable::new(accepts, abort));
|
||||
while let Some(client) = accepts.next().await {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::config::{Config, NamespaceName};
|
||||
use crate::link::{LinkError, LinkManager};
|
||||
use crate::namespace::NetNs;
|
||||
use main_error::MainResult;
|
||||
use tracing::error;
|
||||
|
|
@ -24,6 +25,14 @@ pub fn up(config: Config) -> MainResult {
|
|||
error!(%error, "failed to move device into namespace");
|
||||
}
|
||||
}
|
||||
for route in new.routes {
|
||||
namespace.handle().run_in(|| {
|
||||
let manager = LinkManager::new()?;
|
||||
let link = manager.get_link(&route.device)?;
|
||||
manager.add_route(&link, route.destination)?;
|
||||
Ok::<_, LinkError>(())
|
||||
})??;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue