Skip to content

Redesign UDP relay for endpoint-independent mapping (hole punching support) #498

@appcypher

Description

@appcypher

Summary

The current UDP relay (crates/network/lib/udp_relay.rs) creates a separate host-side UDP socket per (guest_src, guest_dst) pair. This results in address-dependent mapping — different destinations see different source ports from the guest. This breaks UDP hole punching (STUN/TURN, WebRTC, Tailscale direct connections).

Current behavior

  • Guest sends to 1.2.3.4:3478 → host socket A on port X
  • Guest sends to 5.6.7.8:3478 → host socket B on port Y

Different destinations see different external source ports.

Desired behavior

Endpoint-independent mapping: the same guest source port always maps to the same external port regardless of destination.

  • Guest sends to 1.2.3.4:3478 → host socket A on port X
  • Guest sends to 5.6.7.8:3478 → same host socket A on port X

Implementation notes

The relay would need to:

  • Key sessions on guest_src only, not (guest_src, guest_dst)
  • Use a single unconnected host socket per guest source port
  • Use send_to() / recv_from() instead of connect() + send() / recv()
  • Filter responses by source address manually (currently handled by the connected socket)

Trade-off: connected sockets currently provide free source-address filtering and prevent spoofed datagrams. An unconnected design needs explicit filtering.

Context

Requested by a user trying to use Tailscale with direct peer-to-peer connectivity inside microsandbox.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions