mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-16 02:45:41 +00:00
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:
@@ -129,6 +129,7 @@ pub struct TcpTunnelConnector {
|
||||
|
||||
bind_addrs: Vec<SocketAddr>,
|
||||
ip_version: IpVersion,
|
||||
resolved_addr: Option<SocketAddr>,
|
||||
}
|
||||
|
||||
impl TcpTunnelConnector {
|
||||
@@ -137,6 +138,7 @@ impl TcpTunnelConnector {
|
||||
addr,
|
||||
bind_addrs: vec![],
|
||||
ip_version: IpVersion::Both,
|
||||
resolved_addr: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +177,10 @@ impl TcpTunnelConnector {
|
||||
#[async_trait]
|
||||
impl super::TunnelConnector for TcpTunnelConnector {
|
||||
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 self.bind_addrs.is_empty() {
|
||||
self.connect_with_default_bind(addr).await
|
||||
} else {
|
||||
@@ -194,6 +199,10 @@ impl super::TunnelConnector for TcpTunnelConnector {
|
||||
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)]
|
||||
@@ -294,6 +303,31 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn connector_uses_pre_resolved_addr_without_resolving_url() {
|
||||
let mut listener = TcpTunnelListener::new("tcp://127.0.0.1:0".parse().unwrap());
|
||||
listener.listen().await.unwrap();
|
||||
|
||||
let port = listener.local_url().port().unwrap();
|
||||
let source_url: url::Url = format!("tcp://unresolvable.invalid:{port}")
|
||||
.parse()
|
||||
.unwrap();
|
||||
let resolved_addr: SocketAddr = format!("127.0.0.1:{port}").parse().unwrap();
|
||||
let mut connector = TcpTunnelConnector::new(source_url.clone());
|
||||
connector.set_resolved_addr(resolved_addr);
|
||||
|
||||
let accept_task = tokio::spawn(async move { listener.accept().await.unwrap() });
|
||||
let tunnel = connector.connect().await.unwrap();
|
||||
let _accepted_tunnel = accept_task.await.unwrap();
|
||||
|
||||
let info = tunnel.info().unwrap();
|
||||
assert_eq!(info.remote_addr.unwrap().url, source_url.to_string());
|
||||
|
||||
let resolved_remote_addr: url::Url = info.resolved_remote_addr.unwrap().into();
|
||||
assert_eq!(resolved_remote_addr.host_str(), Some("127.0.0.1"));
|
||||
assert_eq!(resolved_remote_addr.port(), Some(port));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_alloc_port() {
|
||||
// v4
|
||||
|
||||
Reference in New Issue
Block a user