fix(proxy): do not wrap TCP packets destined to own virtual IP (#2572)

The wrapped TCP proxy (KCP/QUIC) claims every SYN emitted by the local host,
including packets addressed to the node's own virtual IP. Those packets were
marked for the wrapped path and self-delivered, then the wrapped destination
lookup failed ("no peer found for wrapped TCP dst"). This broke local access
to the node's own virtual IP on systems that route own-address traffic through
the tun (macOS), and, with the proxy enabled on both sides, also inbound
wrapped flows to a macOS node, because the receiving side dials its own
virtual IP locally.

Skip the wrapped path when the SYN destination equals the local virtual IPv4.
Feed the NIC filter context the instance virtual IPv4 (snapshot.virtual_ipv4)
instead of the smoltcp fake inet so the check works with smoltcp enabled too;
the net-to-net guard is unaffected because it only runs when smoltcp is off,
where both values are identical.

Add a regression test for the KCP and QUIC transports.
This commit is contained in:
MRK authored and GitHub committed 2026-09-15 14:37:47 +08:00
1 parent dd013e6a2a
commit 286e0f4a0d
2 files changed
+34 -1

No files matched your search

@@ -159,6 +159,10 @@ where
let is_syn = tcp_packet.syn() && !tcp_packet.ack();
if is_syn {
// Own virtual IP traffic must stay local; it can never reach a peer.
if ctx.local_ipv4 == Some(dst_ip) {
return false;
}
if !check_dst_allowed(dst_ip).await {
tracing::warn!(
?ctx.transport,
@@ -447,4 +451,33 @@ mod tests {
.await
);
}
#[tokio::test]
async fn own_virtual_ip_syn_is_not_marked() {
let own_ip = "10.144.144.204".parse().unwrap();
let src = SocketAddrV4::new(own_ip, 50000);
let dst = SocketAddrV4::new(own_ip, 80);
for transport in [
WrappedTcpProxyTransport::Kcp,
WrappedTcpProxyTransport::Quic,
] {
let mut packet = build_tcp_packet(src, dst, true, false);
assert!(
!try_process_wrapped_tcp_packet_from_nic(
&mut packet,
context(transport),
|_| false,
|_| async { true },
)
.await
);
assert_eq!(
packet.peer_manager_header().unwrap().packet_type,
PacketType::Data as u8
);
}
}
}
@@ -257,7 +257,7 @@ where
WrappedTransportKind::Quic => WrappedTcpProxyTransport::Quic,
},
my_peer_id: self.peer_manager.my_peer_id(),
local_ipv4: snapshot.local_inet.map(|inet| inet.address()),
local_ipv4: snapshot.virtual_ipv4,
smoltcp_enabled: snapshot.smoltcp_enabled,
},
move |src| connection_engine.is_tcp_proxy_connection(src),