Skip to content

cmd/tailcat: forward UDP through --serve=exit-node - #107

Merged
bradfitz merged 1 commit into
tailscale:mainfrom
zhaoyswd:exit-node-udp
Sep 13, 2026
Merged

cmd/tailcat: forward UDP through --serve=exit-node#107
bradfitz merged 1 commit into
tailscale:mainfrom
zhaoyswd:exit-node-udp

Conversation

@zhaoyswd

Copy link
Copy Markdown
Contributor

tailcat serve exit-node registers only OnTCPForward, so the UDP flows a client sends through the tunnel — DNS, QUIC, anything datagram-based — are dropped. From the client side it looks like a half-working tunnel: TCP connects fine, UDP never comes back.

Reproduced against an exit-node server built from main, with an in-process tailcat.Client and a UDP echo server on the server side:

  • before: DialUDP succeeds and the write goes out, but no reply ever arrives (the new test fails with read udp ...: i/o timeout)
  • after: the echo comes back

For a real-world report: I run a full-tunnel VPN client (HarmonyOS) whose only path out is the tunnel. With the official exit-node binary, DNS-over-UDP was silently dropped (102 packets up, 0 down) while TCP on the same channel worked; adding these few lines to a local build made UDP work end to end.

The README already documents the intended wiring:

Use OnUDPForward and DialUDP for exit-node traffic; ProxyPacketConns provides datagram-safe bidirectional forwarding.

So this just adds the missing hook: an udpForwardTo helper next to tcpForwardTo that dials the destination with net.DialUDP and hands the pair to tailcat.ProxyPacketConns. Like the TCP path it forwards to whatever IP:port the client asked for, and server-side flow lifetime/idle handling is the library's (DefaultUDPIdleTimeout).

Testing:

  • new TestServeExitNodeUDP — UDP echo through an exit-node server; fails on main, passes with this change
  • go test ./... -count=1 -timeout 600s passes
  • go vet ./... clean
  • no dependency changes (go.mod/go.sum untouched)

A --serve=exit-node server only registered OnTCPForward, so UDP flows
sent through the tunnel (DNS, QUIC, ...) were dropped: the tunnel came
up and TCP worked, but every UDP flow went nowhere. The README already
documents OnUDPForward + ProxyPacketConns as the exit-node UDP channel;
only the CLI was missing the hook.

Register OnUDPForward and proxy each flow to its destination with
ProxyPacketConns, mirroring how tcpForwardTo handles TCP.

TestServeExitNodeUDP dials a UDP echo server through an exit-node server
and checks the echo comes back. On the previous commit it fails with
"i/o timeout" (no reply through the exit node); with this change it
passes.
@bradfitz
bradfitz merged commit d0d5991 into tailscale:main Sep 13, 2026
14 checks passed
zhaoyswd added a commit to zhaoyswd/tailcat that referenced this pull request Sep 13, 2026
上游 main 现在自带 exit-node UDP 转发(bradfitz 合入,merge commit d0d5991),
但尚未进任何发行版(最新 release 仍是 v0.6.0),所以本 fork 继续带着这段补丁;
等下一个上游 tag 之后再从这里去掉。
zhaoyswd added a commit to zhaoyswd/tailcat that referenced this pull request Sep 13, 2026
上游自上次同步以来的 4 个提交:
  fd10188 tailcat: add Server.Listen for net.Listener-style TCP and UDP serving
  5ceb166 cmd/tailcat, internal/localhostdns: fix serving local ports from Windows
  d0d5991 cmd/tailcat: forward UDP through --serve=exit-node(我们的 PR tailscale#107,已由维护者合入)
  57b8ce5 CHANGELOG.md: add unreleased entries for changes since v0.6.0

无冲突。fork 侧保留自己的 workflow 取舍(只留 binaries.yml,删掉继承来的
release/nix/webdemo-pages/test workflow)与 FORK-NOTES.md。
zhaoyswd added a commit to zhaoyswd/tailcat that referenced this pull request Sep 13, 2026
上游 main 现在自带 exit-node UDP 转发(bradfitz 合入,merge commit d0d5991),
但尚未进任何发行版(最新 release 仍是 v0.6.0),所以本 fork 继续带着这段补丁;
等下一个上游 tag 之后再从这里去掉。
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.

2 participants