diff --git a/Cargo.lock b/Cargo.lock index 07c5d75..f29ddea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1979,7 +1979,7 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "easytier" -version = "2.4.0" +version = "2.4.1" dependencies = [ "aes-gcm", "anyhow", @@ -2112,7 +2112,7 @@ dependencies = [ [[package]] name = "easytier-gui" -version = "2.4.0" +version = "2.4.1" dependencies = [ "anyhow", "chrono", @@ -2162,7 +2162,7 @@ dependencies = [ [[package]] name = "easytier-web" -version = "2.4.0" +version = "2.4.1" dependencies = [ "anyhow", "async-trait", diff --git a/easytier/src/common/config.rs b/easytier/src/common/config.rs index e9eeedb..c607a46 100644 --- a/easytier/src/common/config.rs +++ b/easytier/src/common/config.rs @@ -1,5 +1,5 @@ use std::{ - net::{Ipv4Addr, SocketAddr}, + net::{IpAddr, SocketAddr}, path::PathBuf, sync::{Arc, Mutex}, u64, @@ -107,8 +107,8 @@ pub trait ConfigLoader: Send + Sync { fn get_flags(&self) -> Flags; fn set_flags(&self, flags: Flags); - fn get_exit_nodes(&self) -> Vec; - fn set_exit_nodes(&self, nodes: Vec); + fn get_exit_nodes(&self) -> Vec; + fn set_exit_nodes(&self, nodes: Vec); fn get_routes(&self) -> Option>; fn set_routes(&self, routes: Option>); @@ -283,7 +283,7 @@ struct Config { network_identity: Option, listeners: Option>, mapped_listeners: Option>, - exit_nodes: Option>, + exit_nodes: Option>, peer: Option>, proxy_network: Option>, @@ -624,7 +624,7 @@ impl ConfigLoader for TomlConfigLoader { self.config.lock().unwrap().flags_struct = Some(flags); } - fn get_exit_nodes(&self) -> Vec { + fn get_exit_nodes(&self) -> Vec { self.config .lock() .unwrap() @@ -633,7 +633,7 @@ impl ConfigLoader for TomlConfigLoader { .unwrap_or_default() } - fn set_exit_nodes(&self, nodes: Vec) { + fn set_exit_nodes(&self, nodes: Vec) { self.config.lock().unwrap().exit_nodes = Some(nodes); } diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index a2ab471..c64bcf2 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -4,7 +4,7 @@ extern crate rust_i18n; use std::{ - net::{Ipv4Addr, SocketAddr}, + net::{IpAddr, SocketAddr}, path::PathBuf, process::ExitCode, sync::Arc, @@ -333,7 +333,7 @@ struct NetworkOptions { help = t!("core_clap.exit_nodes").to_string(), num_args = 0.. )] - exit_nodes: Vec, + exit_nodes: Vec, #[arg( long, diff --git a/easytier/src/instance/virtual_nic.rs b/easytier/src/instance/virtual_nic.rs index cb5722c..8e7a8ed 100644 --- a/easytier/src/instance/virtual_nic.rs +++ b/easytier/src/instance/virtual_nic.rs @@ -711,12 +711,20 @@ impl NicCtx { tracing::info!("[USER_PACKET] not ipv6 packet: {:?}", ipv6); return; } + let src_ipv6 = ipv6.get_source(); let dst_ipv6 = ipv6.get_destination(); tracing::trace!( ?ret, "[USER_PACKET] recv new packet from tun device and forward to peers." ); + if src_ipv6.is_unicast_link_local() + && Some(src_ipv6) != mgr.get_global_ctx().get_ipv6().map(|x| x.address()) + { + // do not route link local packet to other nodes unless the address is assigned by user + return; + } + // TODO: use zero-copy let send_ret = mgr.send_msg_by_ip(ret, IpAddr::V6(dst_ipv6)).await; if send_ret.is_err() { diff --git a/easytier/src/launcher.rs b/easytier/src/launcher.rs index 5f2f1ca..aed6b61 100644 --- a/easytier/src/launcher.rs +++ b/easytier/src/launcher.rs @@ -627,7 +627,7 @@ impl NetworkConfig { } if self.exit_nodes.len() > 0 { - let mut exit_nodes = Vec::::with_capacity(self.exit_nodes.len()); + let mut exit_nodes = Vec::::with_capacity(self.exit_nodes.len()); for node in self.exit_nodes.iter() { exit_nodes.push( node.parse() @@ -891,7 +891,7 @@ impl NetworkConfig { mod tests { use crate::common::config::ConfigLoader; use rand::Rng; - use std::net::Ipv4Addr; + use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; fn gen_default_config() -> crate::common::config::TomlConfigLoader { let config = crate::common::config::TomlConfigLoader::default(); @@ -1073,7 +1073,19 @@ mod tests { rng.gen_range(0..255), rng.gen_range(1..254), ); - nodes.push(ip); + nodes.push(IpAddr::V4(ip)); + // gen ipv6 + let ip = Ipv6Addr::new( + rng.gen_range(0..65535), + rng.gen_range(0..65535), + rng.gen_range(0..65535), + rng.gen_range(0..65535), + rng.gen_range(0..65535), + rng.gen_range(0..65535), + rng.gen_range(0..65535), + rng.gen_range(0..65535), + ); + nodes.push(IpAddr::V6(ip)); } config.set_exit_nodes(nodes); } diff --git a/easytier/src/peers/peer_manager.rs b/easytier/src/peers/peer_manager.rs index 9507aeb..438c3f9 100644 --- a/easytier/src/peers/peer_manager.rs +++ b/easytier/src/peers/peer_manager.rs @@ -142,7 +142,7 @@ pub struct PeerManager { encryptor: Arc>, data_compress_algo: CompressorAlgo, - exit_nodes: Vec, + exit_nodes: Vec, reserved_my_peer_id_map: DashMap, @@ -948,6 +948,9 @@ impl PeerManager { dst_peers.push(peer_id); } else { for exit_node in &self.exit_nodes { + let IpAddr::V4(exit_node) = exit_node else { + continue; + }; if let Some(peer_id) = self.peers.get_peer_id_by_ipv4(exit_node).await { dst_peers.push(peer_id); is_exit_node = true; @@ -985,18 +988,17 @@ impl PeerManager { ); } else if let Some(peer_id) = self.peers.get_peer_id_by_ipv6(&ipv6_addr).await { dst_peers.push(peer_id); - } else { - // For IPv6, we'll need to implement exit node support later - // For now, just try to find any available peer for routing - if dst_peers.is_empty() { - dst_peers.extend( - self.peers - .list_routes() - .await - .iter() - .map(|x| x.key().clone()), - ); - is_exit_node = true; + } else if !ipv6_addr.is_unicast_link_local() { + // NOTE: never route link local address to exit node. + for exit_node in &self.exit_nodes { + let IpAddr::V6(exit_node) = exit_node else { + continue; + }; + if let Some(peer_id) = self.peers.get_peer_id_by_ipv6(exit_node).await { + dst_peers.push(peer_id); + is_exit_node = true; + break; + } } }