prevent EasyTier-managed IPv6 from being used as underlay connections (#2181)

When a node has public IPv6 addresses allocated by EasyTier, those addresses
are installed on the host's network interfaces. The system would then pick
them up as candidate source/destination addresses for underlay connections
(direct peer, UDP hole punch, bind addresses), causing overlay traffic to
loop back into the overlay itself.

Add a central predicate is_ip_easytier_managed_ipv6() and apply it at every
point where IPv6 addresses are selected for underlay use:
- Filter managed IPv6 from DNS-resolved connector addresses, including a
  UDP socket getsockname check to detect whether the OS would route through
  the overlay to reach a destination
- Skip managed IPv6 in bind address selection and STUN candidate filtering
- Strip managed IPv6 from GetIpListResponse RPC so peers never learn them
- Pass pre-resolved addresses to tunnel connectors to avoid re-resolution

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
KKRainbow
2026-04-29 12:17:22 +08:00
committed by GitHub
parent f66010e6f9
commit ed8df2d58f
14 changed files with 478 additions and 87 deletions
+10 -1
View File
@@ -598,6 +598,7 @@ pub struct WgTunnelConnector {
bind_addrs: Vec<SocketAddr>,
ip_version: IpVersion,
resolved_addr: Option<SocketAddr>,
}
impl Debug for WgTunnelConnector {
@@ -617,6 +618,7 @@ impl WgTunnelConnector {
udp: None,
bind_addrs: vec![],
ip_version: IpVersion::Both,
resolved_addr: None,
}
}
@@ -702,7 +704,10 @@ impl WgTunnelConnector {
impl super::TunnelConnector for WgTunnelConnector {
#[tracing::instrument]
async fn connect(&mut self) -> Result<Box<dyn Tunnel>, TunnelError> {
let addr = SocketAddr::from_url(self.addr.clone(), self.ip_version).await?;
let addr = match self.resolved_addr {
Some(addr) => addr,
None => SocketAddr::from_url(self.addr.clone(), self.ip_version).await?,
};
if addr.is_ipv6() {
return self.connect_with_ipv6(addr).await;
@@ -744,6 +749,10 @@ impl super::TunnelConnector for WgTunnelConnector {
fn set_ip_version(&mut self, ip_version: IpVersion) {
self.ip_version = ip_version;
}
fn set_resolved_addr(&mut self, addr: SocketAddr) {
self.resolved_addr = Some(addr);
}
}
#[cfg(test)]