avoid udp hole punch go through tun (#1155)

This commit is contained in:
Sijie.Sun
2025-07-26 14:39:03 +08:00
committed by GitHub
parent 354a4e1d7b
commit 7dc5988620
4 changed files with 61 additions and 10 deletions

View File

@@ -314,8 +314,12 @@ impl PunchBothEasySymHoleClient {
); );
for _ in 0..2 { for _ in 0..2 {
match try_connect_with_socket(socket.socket.clone(), remote_mapped_addr.into()) match try_connect_with_socket(
.await global_ctx.clone(),
socket.socket.clone(),
remote_mapped_addr.into(),
)
.await
{ {
Ok(tunnel) => { Ok(tunnel) => {
return Ok(Some(tunnel)); return Ok(Some(tunnel));

View File

@@ -1,5 +1,5 @@
use std::{ use std::{
net::{Ipv4Addr, SocketAddr, SocketAddrV4}, net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4},
sync::Arc, sync::Arc,
time::Duration, time::Duration,
}; };
@@ -582,7 +582,33 @@ pub(crate) async fn send_symmetric_hole_punch_packet(
Ok(cur_port_idx % ports.len()) 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( pub(crate) async fn try_connect_with_socket(
global_ctx: ArcGlobalCtx,
socket: Arc<UdpSocket>, socket: Arc<UdpSocket>,
remote_mapped_addr: SocketAddr, remote_mapped_addr: SocketAddr,
) -> Result<Box<dyn Tunnel>, Error> { ) -> Result<Box<dyn Tunnel>, Error> {
@@ -596,6 +622,9 @@ pub(crate) async fn try_connect_with_socket(
.parse() .parse()
.unwrap(), .unwrap(),
); );
check_udp_socket_local_addr(global_ctx, remote_mapped_addr).await?;
connector connector
.try_connect_with_socket(socket, remote_mapped_addr) .try_connect_with_socket(socket, remote_mapped_addr)
.await .await

View File

@@ -223,8 +223,12 @@ impl PunchConeHoleClient {
tracing::debug!(?socket, ?tid, "punched socket found, try connect with it"); tracing::debug!(?socket, ?tid, "punched socket found, try connect with it");
for _ in 0..2 { for _ in 0..2 {
match try_connect_with_socket(socket.socket.clone(), remote_mapped_addr.into()) match try_connect_with_socket(
.await global_ctx.clone(),
socket.socket.clone(),
remote_mapped_addr.into(),
)
.await
{ {
Ok(tunnel) => { Ok(tunnel) => {
tracing::info!(?tunnel, "hole punched"); tracing::info!(?tunnel, "hole punched");

View File

@@ -14,11 +14,15 @@ use tokio::{net::UdpSocket, sync::RwLock};
use tracing::Level; use tracing::Level;
use crate::{ use crate::{
common::{scoped_task::ScopedTask, stun::StunInfoCollectorTrait, PeerId}, common::{
connector::udp_hole_punch::common::{ global_ctx::ArcGlobalCtx, scoped_task::ScopedTask, stun::StunInfoCollectorTrait, PeerId,
send_symmetric_hole_punch_packet, try_connect_with_socket, HOLE_PUNCH_PACKET_BODY_LEN, },
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, defer,
peers::peer_manager::PeerManager, peers::peer_manager::PeerManager,
proto::{ proto::{
@@ -350,6 +354,7 @@ impl PunchSymToConeHoleClient {
} }
async fn check_hole_punch_result<T>( async fn check_hole_punch_result<T>(
global_ctx: ArcGlobalCtx,
udp_array: &Arc<UdpSocketArray>, udp_array: &Arc<UdpSocketArray>,
packet: &[u8], packet: &[u8],
tid: u32, tid: u32,
@@ -376,7 +381,13 @@ impl PunchSymToConeHoleClient {
}; };
// if hole punched but tunnel creation failed, need to retry entire process. // 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) => { Ok(tunnel) => {
ret_tunnel.replace(tunnel); ret_tunnel.replace(tunnel);
break; break;
@@ -435,6 +446,7 @@ impl PunchSymToConeHoleClient {
// try direct connect first // try direct connect first
if self.try_direct_connect.load(Ordering::Relaxed) { if self.try_direct_connect.load(Ordering::Relaxed) {
if let Ok(tunnel) = try_connect_with_socket( if let Ok(tunnel) = try_connect_with_socket(
global_ctx.clone(),
Arc::new(UdpSocket::bind("0.0.0.0:0").await?), Arc::new(UdpSocket::bind("0.0.0.0:0").await?),
remote_mapped_addr.into(), remote_mapped_addr.into(),
) )
@@ -478,6 +490,7 @@ impl PunchSymToConeHoleClient {
)) ))
.into(); .into();
let ret_tunnel = Self::check_hole_punch_result( let ret_tunnel = Self::check_hole_punch_result(
global_ctx.clone(),
&udp_array, &udp_array,
&packet, &packet,
tid, tid,
@@ -505,6 +518,7 @@ impl PunchSymToConeHoleClient {
)) ))
.into(); .into();
let ret_tunnel = Self::check_hole_punch_result( let ret_tunnel = Self::check_hole_punch_result(
global_ctx,
&udp_array, &udp_array,
&packet, &packet,
tid, tid,