From 6a750f4522c943fbe96f2c5ce15f5998e96fee86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 30 Dec 2025 22:36:19 +0800 Subject: [PATCH] Fix missing relay support for Tailscale --- docs/configuration/endpoint/tailscale.md | 15 ++++++ docs/configuration/endpoint/tailscale.zh.md | 15 ++++++ go.mod | 2 +- go.sum | 4 +- option/tailscale.go | 24 +++++----- protocol/tailscale/endpoint.go | 51 +++++++++++++-------- 6 files changed, 78 insertions(+), 33 deletions(-) diff --git a/docs/configuration/endpoint/tailscale.md b/docs/configuration/endpoint/tailscale.md index 612a86e63..9ac6caf09 100644 --- a/docs/configuration/endpoint/tailscale.md +++ b/docs/configuration/endpoint/tailscale.md @@ -2,6 +2,11 @@ icon: material/new-box --- +!!! quote "Changes in sing-box 1.13.0" + + :material-plus: [relay_server_port](#relay_server_port) + :material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints) + !!! question "Since sing-box 1.12.0" ### Structure @@ -20,6 +25,8 @@ icon: material/new-box "exit_node_allow_lan_access": false, "advertise_routes": [], "advertise_exit_node": false, + "relay_server_port": 0, + "relay_server_static_endpoints": [], "udp_timeout": "5m", ... // Dial Fields @@ -89,6 +96,14 @@ Example: `["192.168.1.1/24"]` Indicates whether the node should advertise itself as an exit node. +#### relay_server_port + +The port to listen on for incoming relay connections from other Tailscale nodes. + +#### relay_server_static_endpoints + +Static endpoints to advertise for the relay server. + #### udp_timeout UDP NAT expiration time. diff --git a/docs/configuration/endpoint/tailscale.zh.md b/docs/configuration/endpoint/tailscale.zh.md index 395ecbdef..44eb6284f 100644 --- a/docs/configuration/endpoint/tailscale.zh.md +++ b/docs/configuration/endpoint/tailscale.zh.md @@ -2,6 +2,11 @@ icon: material/new-box --- +!!! quote "sing-box 1.13.0 中的更改" + + :material-plus: [relay_server_port](#relay_server_port) + :material-plus: [relay_server_static_endpoints](#relay_server_static_endpoints) + !!! question "自 sing-box 1.12.0 起" ### 结构 @@ -20,6 +25,8 @@ icon: material/new-box "exit_node_allow_lan_access": false, "advertise_routes": [], "advertise_exit_node": false, + "relay_server_port": 0, + "relay_server_static_endpoints": [], "udp_timeout": "5m", ... // 拨号字段 @@ -88,6 +95,14 @@ icon: material/new-box 指示节点是否应将自己通告为出口节点。 +#### relay_server_port + +监听来自其他 Tailscale 节点的中继连接的端口。 + +#### relay_server_static_endpoints + +为中继服务器通告的静态端点。 + #### udp_timeout UDP NAT 过期时间。 diff --git a/go.mod b/go.mod index 4da4a8086..06fc3e35d 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,7 @@ require ( github.com/sagernet/sing-tun v0.8.0-beta.11.0.20251226064455-a850c4f8a1c8 github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 github.com/sagernet/smux v1.5.50-sing-box-mod.1 - github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.3.0.20251225080651-3b25379a5bf8 + github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.4 github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 github.com/spf13/cobra v1.10.2 diff --git a/go.sum b/go.sum index 3dd6f9055..0b6493349 100644 --- a/go.sum +++ b/go.sum @@ -222,8 +222,8 @@ github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkV github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY= github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478= github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8= -github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.3.0.20251225080651-3b25379a5bf8 h1:+rb3fIFwFxhCkIt8B/V3bXWZmiNwDWBd22jQMxqY92w= -github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.3.0.20251225080651-3b25379a5bf8/go.mod h1:HZxL3asFIkcIJtHdnqsdcXsY6d+1iMtq0SPUlX17TGM= +github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.4 h1:p+9JllOL5Q2pj6bmP9gu+LdjyRg/XxHLTpMfuhuQsY4= +github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.4/go.mod h1:HZxL3asFIkcIJtHdnqsdcXsY6d+1iMtq0SPUlX17TGM= github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288 h1:E2tZFeg9mGYGQ7E7BbxMv1cU35HxwgRm6tPKI2Pp7DA= github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20250917110311-16510ac47288/go.mod h1:WUxgxUDZoCF2sxVmW+STSxatP02Qn3FcafTiI2BLtE0= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc= diff --git a/option/tailscale.go b/option/tailscale.go index 1a4318870..f8220df32 100644 --- a/option/tailscale.go +++ b/option/tailscale.go @@ -12,17 +12,19 @@ import ( type TailscaleEndpointOptions struct { DialerOptions - StateDirectory string `json:"state_directory,omitempty"` - AuthKey string `json:"auth_key,omitempty"` - ControlURL string `json:"control_url,omitempty"` - Ephemeral bool `json:"ephemeral,omitempty"` - Hostname string `json:"hostname,omitempty"` - AcceptRoutes bool `json:"accept_routes,omitempty"` - ExitNode string `json:"exit_node,omitempty"` - ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"` - AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"` - AdvertiseExitNode bool `json:"advertise_exit_node,omitempty"` - UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"` + StateDirectory string `json:"state_directory,omitempty"` + AuthKey string `json:"auth_key,omitempty"` + ControlURL string `json:"control_url,omitempty"` + Ephemeral bool `json:"ephemeral,omitempty"` + Hostname string `json:"hostname,omitempty"` + AcceptRoutes bool `json:"accept_routes,omitempty"` + ExitNode string `json:"exit_node,omitempty"` + ExitNodeAllowLANAccess bool `json:"exit_node_allow_lan_access,omitempty"` + AdvertiseRoutes []netip.Prefix `json:"advertise_routes,omitempty"` + AdvertiseExitNode bool `json:"advertise_exit_node,omitempty"` + RelayServerPort *uint16 `json:"relay_server_port,omitempty"` + RelayServerStaticEndpoints []netip.AddrPort `json:"relay_server_static_endpoints,omitempty"` + UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"` } type TailscaleDNSServerOptions struct { diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index 30cdd718a..3bdef142a 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -44,6 +44,7 @@ import ( "github.com/sagernet/sing/common/ntp" "github.com/sagernet/sing/service" "github.com/sagernet/sing/service/filemanager" + _ "github.com/sagernet/tailscale/feature/relayserver" "github.com/sagernet/tailscale/ipn" tsDNS "github.com/sagernet/tailscale/net/dns" "github.com/sagernet/tailscale/net/netmon" @@ -91,11 +92,13 @@ type Endpoint struct { routeDomains common.TypedValue[map[string]bool] routePrefixes atomic.Pointer[netipx.IPSet] - acceptRoutes bool - exitNode string - exitNodeAllowLANAccess bool - advertiseRoutes []netip.Prefix - advertiseExitNode bool + acceptRoutes bool + exitNode string + exitNodeAllowLANAccess bool + advertiseRoutes []netip.Prefix + advertiseExitNode bool + relayServerPort *uint16 + relayServerStaticEndpoints []netip.AddrPort udpTimeout time.Duration } @@ -183,20 +186,22 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL }, } return &Endpoint{ - Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, nil), - ctx: ctx, - router: router, - logger: logger, - dnsRouter: dnsRouter, - network: service.FromContext[adapter.NetworkManager](ctx), - platformInterface: service.FromContext[adapter.PlatformInterface](ctx), - server: server, - acceptRoutes: options.AcceptRoutes, - exitNode: options.ExitNode, - exitNodeAllowLANAccess: options.ExitNodeAllowLANAccess, - advertiseRoutes: options.AdvertiseRoutes, - advertiseExitNode: options.AdvertiseExitNode, - udpTimeout: udpTimeout, + Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, nil), + ctx: ctx, + router: router, + logger: logger, + dnsRouter: dnsRouter, + network: service.FromContext[adapter.NetworkManager](ctx), + platformInterface: service.FromContext[adapter.PlatformInterface](ctx), + server: server, + acceptRoutes: options.AcceptRoutes, + exitNode: options.ExitNode, + exitNodeAllowLANAccess: options.ExitNodeAllowLANAccess, + advertiseRoutes: options.AdvertiseRoutes, + advertiseExitNode: options.AdvertiseExitNode, + relayServerPort: options.RelayServerPort, + relayServerStaticEndpoints: options.RelayServerStaticEndpoints, + udpTimeout: udpTimeout, }, nil } @@ -270,6 +275,14 @@ func (t *Endpoint) Start(stage adapter.StartStage) error { if t.advertiseExitNode { perfs.AdvertiseRoutes = append(perfs.AdvertiseRoutes, tsaddr.ExitRoutes()...) } + if t.relayServerPort != nil { + perfs.RelayServerPort = t.relayServerPort + perfs.RelayServerPortSet = true + } + if len(t.relayServerStaticEndpoints) > 0 { + perfs.RelayServerStaticEndpoints = t.relayServerStaticEndpoints + perfs.RelayServerStaticEndpointsSet = true + } _, err = localBackend.EditPrefs(perfs) if err != nil { return E.Cause(err, "update prefs")