From 56fd6e4ab6d82dbf6cc0a70f4df9b053506c0358 Mon Sep 17 00:00:00 2001 From: "Sijie.Sun" Date: Wed, 17 Sep 2025 23:45:05 +0800 Subject: [PATCH] fix wireguard listener (#1382) * listen both v4 and v6 for wireguard portal * fix panic when getting udp local addr Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- easytier-web/frontend-lib/src/locales/cn.yaml | 1 + easytier-web/frontend-lib/src/locales/en.yaml | 1 + .../frontend-lib/src/types/network.ts | 1 + easytier/src/common/global_ctx.rs | 3 +- easytier/src/instance_manager.rs | 7 +++ easytier/src/tunnel/wireguard.rs | 5 +- easytier/src/vpn_portal/wireguard.rs | 50 +++++++++++++------ 7 files changed, 52 insertions(+), 16 deletions(-) diff --git a/easytier-web/frontend-lib/src/locales/cn.yaml b/easytier-web/frontend-lib/src/locales/cn.yaml index ec9fc31..6d427c8 100644 --- a/easytier-web/frontend-lib/src/locales/cn.yaml +++ b/easytier-web/frontend-lib/src/locales/cn.yaml @@ -212,6 +212,7 @@ event: ConnectionError: 连接错误 Connecting: 正在连接 ConnectError: 连接错误 + VpnPortalStarted: VPN门户已启动 VpnPortalClientConnected: VPN门户客户端已连接 VpnPortalClientDisconnected: VPN门户客户端已断开连接 DhcpIpv4Changed: DHCP IPv4地址更改 diff --git a/easytier-web/frontend-lib/src/locales/en.yaml b/easytier-web/frontend-lib/src/locales/en.yaml index 8e18e79..5f035fd 100644 --- a/easytier-web/frontend-lib/src/locales/en.yaml +++ b/easytier-web/frontend-lib/src/locales/en.yaml @@ -212,6 +212,7 @@ event: ConnectionError: ConnectionError Connecting: Connecting ConnectError: ConnectError + VpnPortalStarted: VpnPortalStarted VpnPortalClientConnected: VpnPortalClientConnected VpnPortalClientDisconnected: VpnPortalClientDisconnected DhcpIpv4Changed: DhcpIpv4Changed diff --git a/easytier-web/frontend-lib/src/types/network.ts b/easytier-web/frontend-lib/src/types/network.ts index 9fb2d13..fdccc7d 100644 --- a/easytier-web/frontend-lib/src/types/network.ts +++ b/easytier-web/frontend-lib/src/types/network.ts @@ -306,6 +306,7 @@ export enum EventType { Connecting = 'Connecting', // any ConnectError = 'ConnectError', // string, string, string + VpnPortalStarted = 'VpnPortalStarted', // string VpnPortalClientConnected = 'VpnPortalClientConnected', // string, string VpnPortalClientDisconnected = 'VpnPortalClientDisconnected', // string, string, string diff --git a/easytier/src/common/global_ctx.rs b/easytier/src/common/global_ctx.rs index ac4cd9f..448be26 100644 --- a/easytier/src/common/global_ctx.rs +++ b/easytier/src/common/global_ctx.rs @@ -44,7 +44,8 @@ pub enum GlobalCtxEvent { Connecting(url::Url), ConnectError(String, String, String), // (dst, ip version, error message) - VpnPortalClientConnected(String, String), // (portal, client ip) + VpnPortalStarted(String), // (portal) + VpnPortalClientConnected(String, String), // (portal, client ip) VpnPortalClientDisconnected(String, String), // (portal, client ip) DhcpIpv4Changed(Option, Option), // (old, new) diff --git a/easytier/src/instance_manager.rs b/easytier/src/instance_manager.rs index 59fe11d..dca8013 100644 --- a/easytier/src/instance_manager.rs +++ b/easytier/src/instance_manager.rs @@ -270,6 +270,13 @@ fn handle_event( ); } + GlobalCtxEvent::VpnPortalStarted(portal) => { + print_event( + instance_id, + format!("vpn portal started. portal: {}", portal), + ); + } + GlobalCtxEvent::VpnPortalClientConnected(portal, client_addr) => { print_event( instance_id, diff --git a/easytier/src/tunnel/wireguard.rs b/easytier/src/tunnel/wireguard.rs index 1d720b1..96705f4 100644 --- a/easytier/src/tunnel/wireguard.rs +++ b/easytier/src/tunnel/wireguard.rs @@ -629,7 +629,10 @@ impl WgTunnelConnector { addr: SocketAddr, ) -> Result, super::TunnelError> { tracing::warn!("wg connect: {:?}", addr); - let local_addr = udp.local_addr().unwrap().to_string(); + let local_addr = udp + .local_addr() + .with_context(|| "Failed to get local addr")? + .to_string(); let mut wg_peer = WgPeer::new(Arc::new(udp), config.clone(), addr); let udp = wg_peer.udp_socket(); diff --git a/easytier/src/vpn_portal/wireguard.rs b/easytier/src/vpn_portal/wireguard.rs index f4d94d7..a871089 100644 --- a/easytier/src/vpn_portal/wireguard.rs +++ b/easytier/src/vpn_portal/wireguard.rs @@ -1,5 +1,5 @@ use std::{ - net::{IpAddr, Ipv4Addr, SocketAddr}, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6}, sync::Arc, }; @@ -49,7 +49,7 @@ struct WireGuardImpl { global_ctx: ArcGlobalCtx, peer_mgr: Arc, wg_config: WgConfig, - listenr_addr: SocketAddr, + listener_addr: SocketAddr, wg_peer_ip_table: WgPeerIpTable, @@ -62,13 +62,13 @@ impl WireGuardImpl { let wg_config = get_wg_config_for_portal(&nid); let vpn_cfg = global_ctx.config.get_vpn_portal_config().unwrap(); - let listenr_addr = vpn_cfg.wireguard_listen; + let listener_addr = vpn_cfg.wireguard_listen; Self { global_ctx, peer_mgr, wg_config, - listenr_addr, + listener_addr, wg_peer_ip_table: Arc::new(DashMap::new()), tasks: Arc::new(std::sync::Mutex::new(JoinSet::new())), } @@ -209,12 +209,11 @@ impl WireGuardImpl { .await; } - #[tracing::instrument(skip(self), err(level = Level::WARN))] - async fn start(&self) -> anyhow::Result<()> { - let mut l = WgTunnelListener::new( - format!("wg://{}", self.listenr_addr).parse().unwrap(), - self.wg_config.clone(), - ); + async fn start_listener(&self, listener_addr: &SocketAddr) -> anyhow::Result<()> { + let mut listener_url = url::Url::parse("wg://0.0.0.0:0").unwrap(); + listener_url.set_port(Some(listener_addr.port())).unwrap(); + listener_url.set_ip_host(listener_addr.ip()).unwrap(); + let mut l = WgTunnelListener::new(listener_url.clone(), self.wg_config.clone()); tracing::info!("Wireguard VPN Portal Starting"); @@ -224,9 +223,6 @@ impl WireGuardImpl { .await .with_context(|| "Failed to start wireguard listener for vpn portal")?; } - - join_joinset_background(self.tasks.clone(), "wireguard".to_string()); - let tasks = Arc::downgrade(&self.tasks.clone()); let peer_mgr = self.peer_mgr.clone(); let wg_peer_ip_table = self.wg_peer_ip_table.clone(); @@ -243,6 +239,32 @@ impl WireGuardImpl { } }); + self.global_ctx + .issue_event(GlobalCtxEvent::VpnPortalStarted(listener_url.to_string())); + + Ok(()) + } + + #[tracing::instrument(skip(self), err(level = Level::WARN))] + async fn start(&self) -> anyhow::Result<()> { + tracing::info!("Wireguard VPN Portal Starting"); + + self.start_listener(&self.listener_addr).await?; + // if binding to v4 unspecified, also start a listener on v6 unspecified + if let SocketAddr::V4(v4) = &self.listener_addr { + if v4.ip().is_unspecified() { + let _ = self + .start_listener(&SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::UNSPECIFIED, + v4.port(), + 0, + 0, + ))) + .await; + } + }; + + join_joinset_background(self.tasks.clone(), "wireguard".to_string()); self.start_pipeline_processor().await; Ok(()) @@ -324,7 +346,7 @@ PersistentKeepalive = 25 "#, peer_secret_key = BASE64_STANDARD.encode(cfg.peer_secret_key()), my_public_key = BASE64_STANDARD.encode(cfg.my_public_key()), - listenr_addr = self.inner.as_ref().unwrap().listenr_addr, + listenr_addr = self.inner.as_ref().unwrap().listener_addr, allow_ips = allow_ips, address = client_cidr.first_address().to_string() + "/32", );