support exit node (#121)

support exit node, proxy all traffic via one of node
NOTE: this patch has not implemented automatically route management.
This commit is contained in:
Sijie.Sun
2024-05-18 20:32:42 +08:00
committed by GitHub
parent 6efbb5cb3d
commit f64f58e2ae
8 changed files with 88 additions and 8 deletions

View File

@@ -1,5 +1,5 @@
use std::{ use std::{
net::SocketAddr, net::{Ipv4Addr, SocketAddr},
path::PathBuf, path::PathBuf,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
@@ -58,6 +58,9 @@ pub trait ConfigLoader: Send + Sync {
fn get_flags(&self) -> Flags; fn get_flags(&self) -> Flags;
fn set_flags(&self, flags: Flags); fn set_flags(&self, flags: Flags);
fn get_exit_nodes(&self) -> Vec<Ipv4Addr>;
fn set_exit_nodes(&self, nodes: Vec<Ipv4Addr>);
fn dump(&self) -> String; fn dump(&self) -> String;
} }
@@ -155,6 +158,8 @@ pub struct Flags {
pub mtu: u16, pub mtu: u16,
#[derivative(Default(value = "true"))] #[derivative(Default(value = "true"))]
pub latency_first: bool, pub latency_first: bool,
#[derivative(Default(value = "false"))]
pub enable_exit_node: bool,
} }
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
@@ -167,6 +172,7 @@ struct Config {
dhcp: Option<bool>, dhcp: Option<bool>,
network_identity: Option<NetworkIdentity>, network_identity: Option<NetworkIdentity>,
listeners: Option<Vec<url::Url>>, listeners: Option<Vec<url::Url>>,
exit_nodes: Option<Vec<Ipv4Addr>>,
peer: Option<Vec<PeerConfig>>, peer: Option<Vec<PeerConfig>>,
proxy_network: Option<Vec<NetworkConfig>>, proxy_network: Option<Vec<NetworkConfig>>,
@@ -459,6 +465,19 @@ impl ConfigLoader for TomlConfigLoader {
self.config.lock().unwrap().flags = Some(flags); self.config.lock().unwrap().flags = Some(flags);
} }
fn get_exit_nodes(&self) -> Vec<Ipv4Addr> {
self.config
.lock()
.unwrap()
.exit_nodes
.clone()
.unwrap_or_default()
}
fn set_exit_nodes(&self, nodes: Vec<Ipv4Addr>) {
self.config.lock().unwrap().exit_nodes = Some(nodes);
}
fn dump(&self) -> String { fn dump(&self) -> String {
toml::to_string_pretty(&*self.config.lock().unwrap()).unwrap() toml::to_string_pretty(&*self.config.lock().unwrap()).unwrap()
} }

View File

@@ -3,7 +3,12 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use std::{backtrace, io::Write as _, net::SocketAddr, path::PathBuf}; use std::{
backtrace,
io::Write as _,
net::{Ipv4Addr, SocketAddr},
path::PathBuf,
};
use anyhow::Context; use anyhow::Context;
use clap::Parser; use clap::Parser;
@@ -179,6 +184,20 @@ and the vpn client is in network of 10.14.14.0/24"
default_value = "false" default_value = "false"
)] )]
latency_first: bool, latency_first: bool,
#[arg(
long,
help = "exit nodes to forward all traffic to, a virtual ipv4 address, priority is determined by the order of the list",
num_args = 0..
)]
exit_nodes: Vec<Ipv4Addr>,
#[arg(
long,
help = "allow this node to be an exit node, default is false",
default_value = "false"
)]
enable_exit_node: bool,
} }
impl Cli { impl Cli {
@@ -394,8 +413,11 @@ impl From<Cli> for TomlConfigLoader {
if let Some(mtu) = cli.mtu { if let Some(mtu) = cli.mtu {
f.mtu = mtu; f.mtu = mtu;
} }
f.enable_exit_node = cli.enable_exit_node;
cfg.set_flags(f); cfg.set_flags(f);
cfg.set_exit_nodes(cli.exit_nodes.clone());
cfg cfg
} }
} }

View File

@@ -248,6 +248,7 @@ impl IcmpProxy {
async fn try_handle_peer_packet(&self, packet: &ZCPacket) -> Option<()> { async fn try_handle_peer_packet(&self, packet: &ZCPacket) -> Option<()> {
let _ = self.global_ctx.get_ipv4()?; let _ = self.global_ctx.get_ipv4()?;
let hdr = packet.peer_manager_header().unwrap(); let hdr = packet.peer_manager_header().unwrap();
let is_exit_node = hdr.is_exit_node();
if hdr.packet_type != PacketType::Data as u8 { if hdr.packet_type != PacketType::Data as u8 {
return None; return None;
@@ -260,7 +261,7 @@ impl IcmpProxy {
return None; return None;
} }
if !self.cidr_set.contains_v4(ipv4.get_destination()) { if !self.cidr_set.contains_v4(ipv4.get_destination()) && !is_exit_node {
return None; return None;
} }

View File

@@ -358,6 +358,7 @@ impl TcpProxy {
async fn try_handle_peer_packet(&self, packet: &mut ZCPacket) -> Option<()> { async fn try_handle_peer_packet(&self, packet: &mut ZCPacket) -> Option<()> {
let ipv4_addr = self.global_ctx.get_ipv4()?; let ipv4_addr = self.global_ctx.get_ipv4()?;
let hdr = packet.peer_manager_header().unwrap(); let hdr = packet.peer_manager_header().unwrap();
let is_exit_node = hdr.is_exit_node();
if hdr.packet_type != PacketType::Data as u8 { if hdr.packet_type != PacketType::Data as u8 {
return None; return None;
@@ -370,7 +371,7 @@ impl TcpProxy {
return None; return None;
} }
if !self.cidr_set.contains_v4(ipv4.get_destination()) { if !self.cidr_set.contains_v4(ipv4.get_destination()) && !is_exit_node {
return None; return None;
} }

View File

@@ -233,6 +233,7 @@ impl UdpProxy {
let _ = self.global_ctx.get_ipv4()?; let _ = self.global_ctx.get_ipv4()?;
let hdr = packet.peer_manager_header().unwrap(); let hdr = packet.peer_manager_header().unwrap();
let is_exit_node = hdr.is_exit_node();
if hdr.packet_type != PacketType::Data as u8 { if hdr.packet_type != PacketType::Data as u8 {
return None; return None;
}; };
@@ -242,7 +243,7 @@ impl UdpProxy {
return None; return None;
} }
if !self.cidr_set.contains_v4(ipv4.get_destination()) { if !self.cidr_set.contains_v4(ipv4.get_destination()) && !is_exit_node {
return None; return None;
} }

View File

@@ -68,7 +68,9 @@ impl IpProxy {
} }
async fn start(&self) -> Result<(), Error> { async fn start(&self) -> Result<(), Error> {
if self.global_ctx.get_proxy_cidrs().is_empty() || self.started.load(Ordering::Relaxed) { if (self.global_ctx.get_proxy_cidrs().is_empty() || self.started.load(Ordering::Relaxed))
&& !self.global_ctx.config.get_flags().enable_exit_node
{
return Ok(()); return Ok(());
} }

View File

@@ -155,6 +155,8 @@ pub struct PeerManager {
foreign_network_client: Arc<ForeignNetworkClient>, foreign_network_client: Arc<ForeignNetworkClient>,
encryptor: Arc<Box<dyn Encryptor>>, encryptor: Arc<Box<dyn Encryptor>>,
exit_nodes: Vec<Ipv4Addr>,
} }
impl Debug for PeerManager { impl Debug for PeerManager {
@@ -238,6 +240,8 @@ impl PeerManager {
my_peer_id, my_peer_id,
)); ));
let exit_nodes = global_ctx.config.get_exit_nodes();
PeerManager { PeerManager {
my_peer_id, my_peer_id,
@@ -262,6 +266,7 @@ impl PeerManager {
foreign_network_client, foreign_network_client,
encryptor, encryptor,
exit_nodes,
} }
} }
@@ -573,6 +578,7 @@ impl PeerManager {
ipv4_addr ipv4_addr
); );
let mut is_exit_node = false;
let mut dst_peers = vec![]; let mut dst_peers = vec![];
// NOTE: currently we only support ipv4 and cidr is 24 // NOTE: currently we only support ipv4 and cidr is 24
if ipv4_addr.is_broadcast() || ipv4_addr.is_multicast() || ipv4_addr.octets()[3] == 255 { if ipv4_addr.is_broadcast() || ipv4_addr.is_multicast() || ipv4_addr.octets()[3] == 255 {
@@ -585,6 +591,14 @@ impl PeerManager {
); );
} else if let Some(peer_id) = self.peers.get_peer_id_by_ipv4(&ipv4_addr).await { } else if let Some(peer_id) = self.peers.get_peer_id_by_ipv4(&ipv4_addr).await {
dst_peers.push(peer_id); dst_peers.push(peer_id);
} else {
for exit_node in &self.exit_nodes {
if let Some(peer_id) = self.peers.get_peer_id_by_ipv4(exit_node).await {
dst_peers.push(peer_id);
is_exit_node = true;
break;
}
}
} }
if dst_peers.is_empty() { if dst_peers.is_empty() {
@@ -605,7 +619,8 @@ impl PeerManager {
let is_latency_first = self.global_ctx.get_flags().latency_first; let is_latency_first = self.global_ctx.get_flags().latency_first;
msg.mut_peer_manager_header() msg.mut_peer_manager_header()
.unwrap() .unwrap()
.set_latency_first(is_latency_first); .set_latency_first(is_latency_first)
.set_exit_node(is_exit_node);
let next_hop_policy = Self::get_next_hop_policy(is_latency_first); let next_hop_policy = Self::get_next_hop_policy(is_latency_first);
let mut errs: Vec<Error> = vec![]; let mut errs: Vec<Error> = vec![];

View File

@@ -60,6 +60,7 @@ bitflags::bitflags! {
struct PeerManagerHeaderFlags: u8 { struct PeerManagerHeaderFlags: u8 {
const ENCRYPTED = 0b0000_0001; const ENCRYPTED = 0b0000_0001;
const LATENCY_FIRST = 0b0000_0010; const LATENCY_FIRST = 0b0000_0010;
const EXIT_NODE = 0b0000_0100;
const _ = !0; const _ = !0;
} }
@@ -101,7 +102,13 @@ impl PeerManagerHeader {
.contains(PeerManagerHeaderFlags::LATENCY_FIRST) .contains(PeerManagerHeaderFlags::LATENCY_FIRST)
} }
pub fn set_latency_first(&mut self, latency_first: bool) { pub fn is_exit_node(&self) -> bool {
PeerManagerHeaderFlags::from_bits(self.flags)
.unwrap()
.contains(PeerManagerHeaderFlags::EXIT_NODE)
}
pub fn set_latency_first(&mut self, latency_first: bool) -> &mut Self {
let mut flags = PeerManagerHeaderFlags::from_bits(self.flags).unwrap(); let mut flags = PeerManagerHeaderFlags::from_bits(self.flags).unwrap();
if latency_first { if latency_first {
flags.insert(PeerManagerHeaderFlags::LATENCY_FIRST); flags.insert(PeerManagerHeaderFlags::LATENCY_FIRST);
@@ -109,6 +116,18 @@ impl PeerManagerHeader {
flags.remove(PeerManagerHeaderFlags::LATENCY_FIRST); flags.remove(PeerManagerHeaderFlags::LATENCY_FIRST);
} }
self.flags = flags.bits(); self.flags = flags.bits();
self
}
pub fn set_exit_node(&mut self, exit_node: bool) -> &mut Self {
let mut flags = PeerManagerHeaderFlags::from_bits(self.flags).unwrap();
if exit_node {
flags.insert(PeerManagerHeaderFlags::EXIT_NODE);
} else {
flags.remove(PeerManagerHeaderFlags::EXIT_NODE);
}
self.flags = flags.bits();
self
} }
} }