From 86bf3df0fd64280b40ae95291347d7f72425d783 Mon Sep 17 00:00:00 2001 From: KKRainbow <443152178@qq.com> Date: Thu, 13 Aug 2026 02:51:04 +0800 Subject: [PATCH] fix(shared-tun): restore routed IPv6 replies Forward route-owned packets unchanged when a member has no address suitable for source translation. This preserves public IPv6 provider replies while keeping cross-member source translation when an address is available. Keep the shared DNS route backend available in tun-only builds and use the current error conversion in the FreeBSD IPv4 setup path. --- easytier/src/common/ifcfg/darwin.rs | 4 +-- .../instance/shared_virtual_nic/dispatcher.rs | 25 ++++++++++++++++++- easytier/src/instance/virtual_nic.rs | 1 - 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/easytier/src/common/ifcfg/darwin.rs b/easytier/src/common/ifcfg/darwin.rs index 34dbe0d0..9d38e744 100644 --- a/easytier/src/common/ifcfg/darwin.rs +++ b/easytier/src/common/ifcfg/darwin.rs @@ -100,9 +100,7 @@ impl IfConfiguerTrait for MacIfConfiger { cidr_prefix: u8, ) -> Result<(), Error> { let addr = Ipv4Inet::new(address, cidr_prefix).map_err(|err| { - Error::RouteError(Some(format!( - "invalid IPv4 address {address}/{cidr_prefix}: {err:?}" - ))) + anyhow::anyhow!("invalid IPv4 address {address}/{cidr_prefix}: {err:?}") })?; let mut configured_ipv4 = self.configured_ipv4.lock().await; let has_configured_ipv4 = configured_ipv4 diff --git a/easytier/src/instance/shared_virtual_nic/dispatcher.rs b/easytier/src/instance/shared_virtual_nic/dispatcher.rs index 39b223e1..c277da1a 100644 --- a/easytier/src/instance/shared_virtual_nic/dispatcher.rs +++ b/easytier/src/instance/shared_virtual_nic/dispatcher.rs @@ -1251,7 +1251,7 @@ impl SharedVirtualNicDispatcherState { .source_table .source_for_member_destination(member_id, key.dst) else { - return Err(packet); + return self.send_packet_to_member(member_id, packet).await; }; let original_packet = packet.clone(); let nat_entry = if key.src == translated_src { @@ -2490,6 +2490,29 @@ mod tests { assert!(owner_receiver.try_recv().is_ok()); } + #[tokio::test] + async fn dispatcher_forwards_external_ipv6_to_route_only_owner() { + let route_owner = uuid::Uuid::from_u128(1); + let source = "2001:db8:ffff::2".parse::().unwrap(); + let destination = "2001:db8:100::2".parse::().unwrap(); + let (sender, mut receiver) = mpsc::channel(1); + let mut state = SharedVirtualNicDispatcherState::default(); + + state.register(route_owner, member_entry(sender)); + state.source_table.update_member_sources( + route_owner, + member_sources_with_ipv6_routes(&[], &["2001:db8:100::2/128"]), + ); + state + .forward_tun_packet_to_member(ipv6_packet(source, destination)) + .await; + + let packet = receiver.try_recv().unwrap(); + let ipv6 = pnet_packet::ipv6::Ipv6Packet::new(packet.payload()).unwrap(); + assert_eq!(ipv6.get_source(), source); + assert_eq!(ipv6.get_destination(), destination); + } + #[tokio::test] async fn dispatcher_drops_inactive_source_owner_without_fallback() { let fallback = uuid::Uuid::from_u128(1); diff --git a/easytier/src/instance/virtual_nic.rs b/easytier/src/instance/virtual_nic.rs index d250e168..2207978c 100644 --- a/easytier/src/instance/virtual_nic.rs +++ b/easytier/src/instance/virtual_nic.rs @@ -1197,7 +1197,6 @@ impl NicCtx { ) } - #[cfg(feature = "magic-dns")] pub(crate) fn shared_route_backend_for_dns(&self) -> Option { match self.backend { NicBackend::Dedicated(_) => None,