fix(proto): canonicalize off-path probe destinations to IPv4

Off-path NAT traversal probes had IPv4-mapped IPv6 destinations
(e.g. [::ffff:10.0.0.4]) because map_to_local_socket_family maps
to IPv6 when the connection has any IPv6 path. The transport layer
routes V6 addresses to the IPv6 socket, which has a different port
than the IPv4 socket. This breaks NAT hole punching because the
probe arrives from the wrong source port.

Canonicalize the destination in the Transmit so IPv4-mapped IPv6
addresses become plain IPv4, routing to the correct socket.
This commit is contained in:
dignifiedquire
2026-03-20 15:34:21 +01:00
parent 91773d2379
commit 885e792aeb
+11 -2
View File
@@ -2034,7 +2034,10 @@ impl Connection {
.udp_tx
.on_sent(1, size);
Some(Transmit {
destination: network_path.remote,
destination: SocketAddr::new(
network_path.remote.ip().to_canonical(),
network_path.remote.port(),
),
size,
ecn: None,
segment_size: None,
@@ -2117,8 +2120,14 @@ impl Connection {
.udp_tx
.on_sent(1, size);
// Canonicalize the destination: IPv4-mapped IPv6 addresses (e.g.
// [::ffff:10.0.0.4]) must be converted to plain IPv4 so the transport
// layer routes them to the IPv4 socket. Without this, the packet goes
// out from the IPv6 socket with a different source port, breaking NAT
// mappings needed for hole punching.
let destination = SocketAddr::new(remote.ip().to_canonical(), remote.port());
Some(Transmit {
destination: remote,
destination,
size,
ecn: None,
segment_size: None,