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
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 88 additions and 8 deletions

View File

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

View File

@ -3,7 +3,12 @@
#[cfg(test)]
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 clap::Parser;
@ -179,6 +184,20 @@ and the vpn client is in network of 10.14.14.0/24"
default_value = "false"
)]
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 {
@ -394,8 +413,11 @@ impl From<Cli> for TomlConfigLoader {
if let Some(mtu) = cli.mtu {
f.mtu = mtu;
}
f.enable_exit_node = cli.enable_exit_node;
cfg.set_flags(f);
cfg.set_exit_nodes(cli.exit_nodes.clone());
cfg
}
}

View File

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

View File

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

View File

@ -233,6 +233,7 @@ impl UdpProxy {
let _ = self.global_ctx.get_ipv4()?;
let hdr = packet.peer_manager_header().unwrap();
let is_exit_node = hdr.is_exit_node();
if hdr.packet_type != PacketType::Data as u8 {
return None;
};
@ -242,7 +243,7 @@ impl UdpProxy {
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;
}

View File

@ -68,7 +68,9 @@ impl IpProxy {
}
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(());
}

View File

@ -155,6 +155,8 @@ pub struct PeerManager {
foreign_network_client: Arc<ForeignNetworkClient>,
encryptor: Arc<Box<dyn Encryptor>>,
exit_nodes: Vec<Ipv4Addr>,
}
impl Debug for PeerManager {
@ -238,6 +240,8 @@ impl PeerManager {
my_peer_id,
));
let exit_nodes = global_ctx.config.get_exit_nodes();
PeerManager {
my_peer_id,
@ -262,6 +266,7 @@ impl PeerManager {
foreign_network_client,
encryptor,
exit_nodes,
}
}
@ -573,6 +578,7 @@ impl PeerManager {
ipv4_addr
);
let mut is_exit_node = false;
let mut dst_peers = vec![];
// NOTE: currently we only support ipv4 and cidr is 24
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 {
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() {
@ -605,7 +619,8 @@ impl PeerManager {
let is_latency_first = self.global_ctx.get_flags().latency_first;
msg.mut_peer_manager_header()
.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 mut errs: Vec<Error> = vec![];

View File

@ -60,6 +60,7 @@ bitflags::bitflags! {
struct PeerManagerHeaderFlags: u8 {
const ENCRYPTED = 0b0000_0001;
const LATENCY_FIRST = 0b0000_0010;
const EXIT_NODE = 0b0000_0100;
const _ = !0;
}
@ -101,7 +102,13 @@ impl PeerManagerHeader {
.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();
if latency_first {
flags.insert(PeerManagerHeaderFlags::LATENCY_FIRST);
@ -109,6 +116,18 @@ impl PeerManagerHeader {
flags.remove(PeerManagerHeaderFlags::LATENCY_FIRST);
}
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
}
}