From 7dc5988620dc34c24a80e44eb61efbab52ae5d93 Mon Sep 17 00:00:00 2001 From: "Sijie.Sun" Date: Sat, 26 Jul 2025 14:39:03 +0800 Subject: [PATCH] avoid udp hole punch go through tun (#1155) --- .../connector/udp_hole_punch/both_easy_sym.rs | 8 +++-- .../src/connector/udp_hole_punch/common.rs | 31 ++++++++++++++++++- easytier/src/connector/udp_hole_punch/cone.rs | 8 +++-- .../connector/udp_hole_punch/sym_to_cone.rs | 24 +++++++++++--- 4 files changed, 61 insertions(+), 10 deletions(-) diff --git a/easytier/src/connector/udp_hole_punch/both_easy_sym.rs b/easytier/src/connector/udp_hole_punch/both_easy_sym.rs index 39d80cd..1678436 100644 --- a/easytier/src/connector/udp_hole_punch/both_easy_sym.rs +++ b/easytier/src/connector/udp_hole_punch/both_easy_sym.rs @@ -314,8 +314,12 @@ impl PunchBothEasySymHoleClient { ); for _ in 0..2 { - match try_connect_with_socket(socket.socket.clone(), remote_mapped_addr.into()) - .await + match try_connect_with_socket( + global_ctx.clone(), + socket.socket.clone(), + remote_mapped_addr.into(), + ) + .await { Ok(tunnel) => { return Ok(Some(tunnel)); diff --git a/easytier/src/connector/udp_hole_punch/common.rs b/easytier/src/connector/udp_hole_punch/common.rs index e89808d..2840223 100644 --- a/easytier/src/connector/udp_hole_punch/common.rs +++ b/easytier/src/connector/udp_hole_punch/common.rs @@ -1,5 +1,5 @@ use std::{ - net::{Ipv4Addr, SocketAddr, SocketAddrV4}, + net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}, sync::Arc, time::Duration, }; @@ -582,7 +582,33 @@ pub(crate) async fn send_symmetric_hole_punch_packet( Ok(cur_port_idx % ports.len()) } +async fn check_udp_socket_local_addr( + global_ctx: ArcGlobalCtx, + remote_mapped_addr: SocketAddr, +) -> Result<(), Error> { + let socket = UdpSocket::bind("0.0.0.0:0").await?; + socket.connect(remote_mapped_addr).await?; + if let Ok(local_addr) = socket.local_addr() { + // local_addr should not be equal to virtual ipv4 or virtual ipv6 + match local_addr.ip() { + IpAddr::V4(ip) => { + if global_ctx.get_ipv4().map(|ip| ip.address()) == Some(ip) { + return Err(anyhow::anyhow!("local address is virtual ipv4").into()); + } + } + IpAddr::V6(ip) => { + if global_ctx.get_ipv6().map(|ip| ip.address()) == Some(ip) { + return Err(anyhow::anyhow!("local address is virtual ipv6").into()); + } + } + } + } + + Ok(()) +} + pub(crate) async fn try_connect_with_socket( + global_ctx: ArcGlobalCtx, socket: Arc, remote_mapped_addr: SocketAddr, ) -> Result, Error> { @@ -596,6 +622,9 @@ pub(crate) async fn try_connect_with_socket( .parse() .unwrap(), ); + + check_udp_socket_local_addr(global_ctx, remote_mapped_addr).await?; + connector .try_connect_with_socket(socket, remote_mapped_addr) .await diff --git a/easytier/src/connector/udp_hole_punch/cone.rs b/easytier/src/connector/udp_hole_punch/cone.rs index 70cffb8..d59adeb 100644 --- a/easytier/src/connector/udp_hole_punch/cone.rs +++ b/easytier/src/connector/udp_hole_punch/cone.rs @@ -223,8 +223,12 @@ impl PunchConeHoleClient { tracing::debug!(?socket, ?tid, "punched socket found, try connect with it"); for _ in 0..2 { - match try_connect_with_socket(socket.socket.clone(), remote_mapped_addr.into()) - .await + match try_connect_with_socket( + global_ctx.clone(), + socket.socket.clone(), + remote_mapped_addr.into(), + ) + .await { Ok(tunnel) => { tracing::info!(?tunnel, "hole punched"); diff --git a/easytier/src/connector/udp_hole_punch/sym_to_cone.rs b/easytier/src/connector/udp_hole_punch/sym_to_cone.rs index c265898..95f7a0b 100644 --- a/easytier/src/connector/udp_hole_punch/sym_to_cone.rs +++ b/easytier/src/connector/udp_hole_punch/sym_to_cone.rs @@ -14,11 +14,15 @@ use tokio::{net::UdpSocket, sync::RwLock}; use tracing::Level; use crate::{ - common::{scoped_task::ScopedTask, stun::StunInfoCollectorTrait, PeerId}, - connector::udp_hole_punch::common::{ - send_symmetric_hole_punch_packet, try_connect_with_socket, HOLE_PUNCH_PACKET_BODY_LEN, + common::{ + global_ctx::ArcGlobalCtx, scoped_task::ScopedTask, stun::StunInfoCollectorTrait, PeerId, + }, + connector::udp_hole_punch::{ + common::{ + send_symmetric_hole_punch_packet, try_connect_with_socket, HOLE_PUNCH_PACKET_BODY_LEN, + }, + handle_rpc_result, }, - connector::udp_hole_punch::handle_rpc_result, defer, peers::peer_manager::PeerManager, proto::{ @@ -350,6 +354,7 @@ impl PunchSymToConeHoleClient { } async fn check_hole_punch_result( + global_ctx: ArcGlobalCtx, udp_array: &Arc, packet: &[u8], tid: u32, @@ -376,7 +381,13 @@ impl PunchSymToConeHoleClient { }; // if hole punched but tunnel creation failed, need to retry entire process. - match try_connect_with_socket(socket.socket.clone(), remote_mapped_addr.into()).await { + match try_connect_with_socket( + global_ctx.clone(), + socket.socket.clone(), + remote_mapped_addr.into(), + ) + .await + { Ok(tunnel) => { ret_tunnel.replace(tunnel); break; @@ -435,6 +446,7 @@ impl PunchSymToConeHoleClient { // try direct connect first if self.try_direct_connect.load(Ordering::Relaxed) { if let Ok(tunnel) = try_connect_with_socket( + global_ctx.clone(), Arc::new(UdpSocket::bind("0.0.0.0:0").await?), remote_mapped_addr.into(), ) @@ -478,6 +490,7 @@ impl PunchSymToConeHoleClient { )) .into(); let ret_tunnel = Self::check_hole_punch_result( + global_ctx.clone(), &udp_array, &packet, tid, @@ -505,6 +518,7 @@ impl PunchSymToConeHoleClient { )) .into(); let ret_tunnel = Self::check_hole_punch_result( + global_ctx, &udp_array, &packet, tid,