cmd/tailcat: forward UDP through --serve=exit-node - #107
Merged
Conversation
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.
zhaoyswd
force-pushed
the
exit-node-udp
branch
from
September 10, 2026 17:12
14fb824 to
6d04ebc
Compare
bradfitz
approved these changes
Sep 12, 2026
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 之后再从这里去掉。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
tailcat serve exit-noderegisters onlyOnTCPForward, 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.Clientand a UDP echo server on the server side:DialUDPsucceeds and the write goes out, but no reply ever arrives (the new test fails withread udp ...: i/o timeout)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:
So this just adds the missing hook: an
udpForwardTohelper next totcpForwardTothat dials the destination withnet.DialUDPand hands the pair totailcat.ProxyPacketConns. Like the TCP path it forwards to whateverIP:portthe client asked for, and server-side flow lifetime/idle handling is the library's (DefaultUDPIdleTimeout).Testing:
TestServeExitNodeUDP— UDP echo through an exit-node server; fails on main, passes with this changego test ./... -count=1 -timeout 600spassesgo vet ./...cleango.mod/go.sumuntouched)