fix peer establish direct conn with subnet proxy to one of local interface (#1782)

* fix peer establish direct conn with subnet proxy to one of local interface

* fix peer mgr ref loop
This commit is contained in:
KKRainbow
2026-01-15 01:00:32 +08:00
committed by GitHub
parent f8b34e3c86
commit 53264f67bf
21 changed files with 354 additions and 170 deletions
+48 -13
View File
@@ -1,5 +1,5 @@
use std::collections::hash_map::DefaultHasher;
use std::net::IpAddr;
use std::net::{IpAddr, SocketAddr};
use std::{
hash::Hasher,
sync::{Arc, Mutex},
@@ -257,6 +257,13 @@ impl GlobalCtx {
}
}
pub fn is_ip_local_virtual_ip(&self, ip: &IpAddr) -> bool {
match ip {
IpAddr::V4(v4) => self.get_ipv4().map(|x| x.address() == *v4).unwrap_or(false),
IpAddr::V6(v6) => self.get_ipv6().map(|x| x.address() == *v6).unwrap_or(false),
}
}
pub fn get_network_identity(&self) -> NetworkIdentity {
self.config.get_network_identity()
}
@@ -303,18 +310,6 @@ impl GlobalCtx {
}
}
pub fn is_port_in_running_listeners(&self, port: u16, is_udp: bool) -> bool {
let check_proto = |listener_proto: &str| {
let listener_is_udp = matches!(listener_proto, "udp" | "wg");
listener_is_udp == is_udp
};
self.running_listeners
.lock()
.unwrap()
.iter()
.any(|x| x.port() == Some(port) && check_proto(x.scheme()))
}
pub fn get_vpn_portal_cidr(&self) -> Option<cidr::Ipv4Cidr> {
self.config.get_vpn_portal_config().map(|x| x.client_cidr)
}
@@ -447,6 +442,46 @@ impl GlobalCtx {
// NOTICE: p2p only is conflict with latency first
self.config.get_flags().latency_first && !self.p2p_only
}
fn is_port_in_running_listeners(&self, port: u16, is_udp: bool) -> bool {
let check_proto = |listener_proto: &str| {
let listener_is_udp = matches!(listener_proto, "udp" | "wg");
listener_is_udp == is_udp
};
self.running_listeners
.lock()
.unwrap()
.iter()
.any(|x| x.port() == Some(port) && check_proto(x.scheme()))
}
#[tracing::instrument(ret, skip(self))]
pub fn should_deny_proxy(&self, dst_addr: &SocketAddr, is_udp: bool) -> bool {
let _g = self.net_ns.guard();
let ip = dst_addr.ip();
// first check if ip is virtual ip
// then try bind this ip, if succ means it is local ip
let dst_is_local_virtual_ip = self.is_ip_local_virtual_ip(&ip);
// this is an expensive operation, should be called sparingly
// 1. tcp/kcp/quic call this only after proxy conn is established
// 2. udp cache the result in nat entry
let dst_is_local_phy_ip = std::net::UdpSocket::bind(format!("{}:0", ip)).is_ok();
tracing::trace!(
"check should_deny_proxy: dst_addr={}, dst_is_local_virtual_ip={}, dst_is_local_phy_ip={}, is_udp={}",
dst_addr,
dst_is_local_virtual_ip,
dst_is_local_phy_ip,
is_udp
);
if dst_is_local_virtual_ip || dst_is_local_phy_ip {
// if is local ip, make sure the port is not one of the listening ports
self.is_port_in_running_listeners(dst_addr.port(), is_udp)
} else {
false
}
}
}
#[cfg(test)]
+1 -1
View File
@@ -74,7 +74,7 @@ impl NetNSGuard {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct NetNS {
name: Option<String>,
}
+11 -8
View File
@@ -1321,7 +1321,10 @@ impl StunInfoCollectorTrait for MockStunInfoCollector {
#[cfg(test)]
mod tests {
use crate::tunnel::{udp::UdpTunnelListener, TunnelListener};
use crate::{
common::scoped_task::ScopedTask,
tunnel::{udp::UdpTunnelListener, TunnelListener},
};
use super::*;
@@ -1406,11 +1409,11 @@ mod tests {
use stun_codec::rfc5389::attributes::XorMappedAddress;
use tokio::net::TcpListener;
async fn spawn_tcp_stun_server() -> SocketAddr {
async fn spawn_tcp_stun_server() -> (SocketAddr, ScopedTask<()>) {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let server_addr = listener.local_addr().unwrap();
tokio::spawn(async move {
let task = tokio::spawn(async move {
let (mut stream, peer_addr) = listener.accept().await.unwrap();
let req = TcpStunClient::tcp_read_stun_message(&mut stream, Duration::from_secs(2))
@@ -1430,11 +1433,11 @@ mod tests {
stream.write_all(rsp_buf.as_slice()).await.unwrap();
});
server_addr
(server_addr, task.into())
}
let server1 = spawn_tcp_stun_server().await;
let server2 = spawn_tcp_stun_server().await;
let (server1, _t1) = spawn_tcp_stun_server().await;
let (server2, _t2) = spawn_tcp_stun_server().await;
let stun_servers = vec![server1.to_string(), server2.to_string()];
let detector = TcpNatTypeDetector::new(stun_servers, 1);
@@ -1469,7 +1472,7 @@ mod tests {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let server_addr = listener.local_addr().unwrap();
tokio::spawn(async move {
let _t = ScopedTask::from(tokio::spawn(async move {
for _ in 0..8 {
let Ok((mut stream, peer_addr)) = listener.accept().await else {
break;
@@ -1491,7 +1494,7 @@ mod tests {
let rsp_buf = encoder.encode_into_bytes(resp_msg).unwrap();
stream.write_all(rsp_buf.as_slice()).await.unwrap();
}
});
}));
let collector = StunInfoCollector::new(vec![], vec![server_addr.to_string()], vec![]);
collector.set_tcp_stun_servers(vec![server_addr.to_string()]);