v6 hole punch (#873)

Some devices have ipv6 but don't allow input connection, this patch add hole punching for these devices.

- **add v6 hole punch msg to udp tunnel**
- **send hole punch packet when do ipv6 direct connect**
This commit is contained in:
Sijie.Sun
2025-05-24 22:57:33 +08:00
committed by GitHub
parent fc397c35c5
commit 29994b663a
15 changed files with 499 additions and 198 deletions
+18 -15
View File
@@ -61,11 +61,11 @@ pub struct GlobalCtx {
cached_ipv4: AtomicCell<Option<cidr::Ipv4Inet>>,
cached_proxy_cidrs: AtomicCell<Option<Vec<cidr::IpCidr>>>,
ip_collector: Arc<IPCollector>,
ip_collector: Mutex<Option<Arc<IPCollector>>>,
hostname: Mutex<String>,
stun_info_collection: Box<dyn StunInfoCollectorTrait>,
stun_info_collection: Mutex<Arc<dyn StunInfoCollectorTrait>>,
running_listeners: Mutex<Vec<url::Url>>,
@@ -120,11 +120,14 @@ impl GlobalCtx {
cached_ipv4: AtomicCell::new(None),
cached_proxy_cidrs: AtomicCell::new(None),
ip_collector: Arc::new(IPCollector::new(net_ns, stun_info_collection.clone())),
ip_collector: Mutex::new(Some(Arc::new(IPCollector::new(
net_ns,
stun_info_collection.clone(),
)))),
hostname: Mutex::new(hostname),
stun_info_collection: Box::new(stun_info_collection),
stun_info_collection: Mutex::new(stun_info_collection),
running_listeners: Mutex::new(Vec::new()),
@@ -215,7 +218,7 @@ impl GlobalCtx {
}
pub fn get_ip_collector(&self) -> Arc<IPCollector> {
self.ip_collector.clone()
self.ip_collector.lock().unwrap().as_ref().unwrap().clone()
}
pub fn get_hostname(&self) -> String {
@@ -226,19 +229,19 @@ impl GlobalCtx {
*self.hostname.lock().unwrap() = hostname;
}
pub fn get_stun_info_collector(&self) -> impl StunInfoCollectorTrait + '_ {
self.stun_info_collection.as_ref()
pub fn get_stun_info_collector(&self) -> Arc<dyn StunInfoCollectorTrait> {
self.stun_info_collection.lock().unwrap().clone()
}
pub fn replace_stun_info_collector(&self, collector: Box<dyn StunInfoCollectorTrait>) {
// force replace the stun_info_collection without mut and drop the old one
let ptr = &self.stun_info_collection as *const Box<dyn StunInfoCollectorTrait>;
let ptr = ptr as *mut Box<dyn StunInfoCollectorTrait>;
unsafe {
std::ptr::drop_in_place(ptr);
#[allow(invalid_reference_casting)]
std::ptr::write(ptr, collector);
}
let arc_collector: Arc<dyn StunInfoCollectorTrait> = Arc::new(collector);
*self.stun_info_collection.lock().unwrap() = arc_collector.clone();
// rebuild the ip collector
*self.ip_collector.lock().unwrap() = Some(Arc::new(IPCollector::new(
self.net_ns.clone(),
arc_collector,
)));
}
pub fn get_running_listeners(&self) -> Vec<url::Url> {
+16 -12
View File
@@ -179,18 +179,16 @@ impl IPCollector {
Self::do_collect_local_ip_addrs(self.net_ns.clone()).await;
let net_ns = self.net_ns.clone();
let stun_info_collector = self.stun_info_collector.clone();
task.spawn(async move {
loop {
let ip_addrs = Self::do_collect_local_ip_addrs(net_ns.clone()).await;
*cached_ip_list.write().await = ip_addrs;
tokio::time::sleep(std::time::Duration::from_secs(CACHED_IP_LIST_TIMEOUT_SEC))
.await;
}
});
let cached_ip_list = self.cached_ip_list.clone();
task.spawn(async move {
let mut last_fetch_iface_time = std::time::Instant::now();
loop {
if last_fetch_iface_time.elapsed().as_secs() > CACHED_IP_LIST_TIMEOUT_SEC {
let ifaces = Self::do_collect_local_ip_addrs(net_ns.clone()).await;
*cached_ip_list.write().await = ifaces;
last_fetch_iface_time = std::time::Instant::now();
}
let stun_info = stun_info_collector.get_stun_info();
for ip in stun_info.public_ip.iter() {
let Ok(ip_addr) = ip.parse::<IpAddr>() else {
@@ -199,14 +197,20 @@ impl IPCollector {
match ip_addr {
IpAddr::V4(v) => {
cached_ip_list.write().await.public_ipv4 = Some(v.into())
cached_ip_list.write().await.public_ipv4.replace(v.into());
}
IpAddr::V6(v) => {
cached_ip_list.write().await.public_ipv6 = Some(v.into())
cached_ip_list.write().await.public_ipv6.replace(v.into());
}
}
}
tracing::debug!(
"got public ip: {:?}, {:?}",
cached_ip_list.read().await.public_ipv4,
cached_ip_list.read().await.public_ipv6
);
let sleep_sec = if !cached_ip_list.read().await.public_ipv4.is_none() {
CACHED_IP_LIST_TIMEOUT_SEC
} else {
@@ -217,7 +221,7 @@ impl IPCollector {
});
}
return self.cached_ip_list.read().await.deref().clone();
self.cached_ip_list.read().await.deref().clone()
}
pub async fn collect_interfaces(net_ns: NetNS, filter: bool) -> Vec<NetworkInterface> {
+1 -1
View File
@@ -890,7 +890,7 @@ impl StunInfoCollectorTrait for MockStunInfoCollector {
last_update_time: std::time::Instant::now().elapsed().as_secs() as i64,
min_port: 100,
max_port: 200,
public_ip: vec!["127.0.0.1".to_string()],
public_ip: vec!["127.0.0.1".to_string(), "::1".to_string()],
}
}