Skip to content

fix(wind-base): bind direct UDP relay dual-stack for IPv6 targets#48

Merged
Itsusinn merged 2 commits into
mainfrom
fix/udp-relay-dual-stack
Jun 25, 2026
Merged

fix(wind-base): bind direct UDP relay dual-stack for IPv6 targets#48
Itsusinn merged 2 commits into
mainfrom
fix/udp-relay-dual-stack

Conversation

@Itsusinn

Copy link
Copy Markdown
Member

Symptom

The direct UDP relay logs this for every IPv6 target, dropping the packet:

WARN conn: UDP send failed target=[2404:3fc0:2:101::671c:3697]:27019 error=Address family not supported by protocol (os error 97) peer=[::ffff:180.213.128.85]:4332 ...

IPv4 destinations work; every IPv6 destination is silently dropped (logged only).

Root cause

relay_udp_direct bound its socket to 0.0.0.0:0 — an IPv4-only (AF_INET) socket — so send_to an IPv6 address returns EAFNOSUPPORT. The TCP path right next to it already selects the family per target; only the UDP relay was hard-wired to IPv4.

Fix

Bind the relay socket dual-stack (IPV6_V6ONLY=false, with an IPv4-only fallback for hosts without IPv6), mirroring wind-socks's udp_bind_random_port. A single UDP association can target both families over the one socket, so additionally:

  • send: map IPv4 targets to their IPv4-mapped form (::ffff:a.b.c.d) — a bare SocketAddr::V4 on a dual-stack socket hits the same EAFNOSUPPORT inverted, which would otherwise trade the IPv6 failure for an IPv4 regression;
  • recv: unmap IPv4-mapped sources so replies carry the responder's real address family (TargetAddr::from does not canonicalize), letting the client match a reply to the target it sent to.

Tests

  • Unit tests for the map_target_for_socket / unmap_source helpers.
  • tests/udp_relay.rs — end-to-end through the public handle_udp against real loopback echo servers: IPv4, IPv6, and both families on one association.

Verified these actually catch the bug: temporarily reverting the socket to IPv4-only makes the IPv6 tests fail by timeout (send dropped) while the IPv4 test still passes.

🤖 Generated with Claude Code

The direct UDP relay bound its socket to `0.0.0.0:0` (IPv4 only), so any
packet whose target resolved to an IPv6 address failed `send_to` with
EAFNOSUPPORT ("Address family not supported by protocol") and was dropped
— logged only as a warning. IPv4 targets worked; IPv6 targets silently
never reached.

Bind the relay socket dual-stack instead (IPv6 with IPV6_V6ONLY=false,
falling back to IPv4-only on hosts without IPv6), mirroring wind-socks'
`udp_bind_random_port`. Since a single association can target both
families over the one socket, also:

- map IPv4 targets to their IPv4-mapped form (::ffff:a.b.c.d) before
  send_to — a bare V4 address on a dual-stack socket hits the same
  EAFNOSUPPORT inverted; and
- unmap IPv4-mapped sources on the receive path so replies carry the
  responder's real address family (TargetAddr::from does not
  canonicalize), letting the client match replies to the target it sent.

Adds unit tests for the mapping helpers and end-to-end integration tests
(tests/udp_relay.rs) driving handle_udp against real loopback echo servers
over IPv4, IPv6, and both families on a single association.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codspeed-hq

codspeed-hq Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 22 untouched benchmarks
⏩ 11 skipped benchmarks1


Comparing fix/udp-relay-dual-stack (3831a56) with main (af48d0b)

Open in CodSpeed

Footnotes

  1. 11 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Itsusinn Itsusinn merged commit 4003eae into main Jun 25, 2026
17 checks passed
@Itsusinn Itsusinn deleted the fix/udp-relay-dual-stack branch June 25, 2026 02:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant