From 65875e6dac1d6380b4dc4788ff22201958d213c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 10 Mar 2026 16:56:09 +0800 Subject: [PATCH] tailscale: Use system dialer for system interface * Revert "Fix netstack TCP connections with system interface --- go.mod | 2 +- go.sum | 4 +- protocol/tailscale/endpoint.go | 70 ++++++++++++++++++++++++++++------ 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 99fdaff5b..a77d9d3db 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/sagernet/sing-tun v0.8.2 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.6.0.20260303140313-3bcf9a4b9349 + github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6.0.20260310072802-158edadd59bd github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20260224074747-506b7631853c 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 09d4fb08d..1d3eb5856 100644 --- a/go.sum +++ b/go.sum @@ -254,8 +254,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.6.0.20260303140313-3bcf9a4b9349 h1:ju7aTbndj2sqK4NplE97ynLdhuCtel5OS4e0NrT71nk= -github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6.0.20260303140313-3bcf9a4b9349/go.mod h1:m87GAn4UcesHQF3leaPFEINZETO5za1LGn1GJdNDgNc= +github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6.0.20260310072802-158edadd59bd h1:WUVQsTUCr0OEWXoB6uPXaqup7SjMUFOkOHe0XBcpLn4= +github.com/sagernet/tailscale v1.92.4-sing-box-1.13-mod.6.0.20260310072802-158edadd59bd/go.mod h1:m87GAn4UcesHQF3leaPFEINZETO5za1LGn1GJdNDgNc= github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20260224074747-506b7631853c h1:f9cXNB+IOOPnR8DOLMTpr42jf7naxh5Un5Y09BBf5Cg= github.com/sagernet/wireguard-go v0.0.2-beta.1.0.20260224074747-506b7631853c/go.mod h1:WUxgxUDZoCF2sxVmW+STSxatP02Qn3FcafTiI2BLtE0= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc= diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index 46d363953..49d7428c6 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -110,6 +110,7 @@ type Endpoint struct { systemInterfaceName string systemInterfaceMTU uint32 systemTun tun.Tun + systemDialer *dialer.DefaultDialer fallbackTCPCloser func() } @@ -324,8 +325,19 @@ func (t *Endpoint) Start(stage adapter.StartStage) error { _ = systemTun.Close() return err } + systemDialer, err := dialer.NewDefault(t.ctx, option.DialerOptions{ + BindInterface: tunName, + }) + if err != nil { + _ = systemTun.Close() + return err + } t.systemTun = systemTun + t.systemDialer = systemDialer t.server.TunDevice = wgTunDevice + t.server.RouterWrapper = func(inner router.Router) router.Router { + return &exitRouteFilteringRouter{Router: inner} + } } err := t.server.Start() if err != nil { @@ -459,6 +471,10 @@ func (t *Endpoint) Close() error { t.fallbackTCPCloser() t.fallbackTCPCloser = nil } + if t.systemTun != nil { + _ = t.systemTun.Close() + t.systemTun = nil + } return common.Close(common.PtrOrNil(t.server)) } @@ -476,6 +492,9 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination } return N.DialSerial(ctx, t, network, destination, destinationAddresses) } + if t.systemDialer != nil { + return t.systemDialer.DialContext(ctx, network, destination) + } addr4, addr6 := t.server.TailscaleIPs() remoteAddr := tcpip.FullAddress{ NIC: 1, @@ -522,6 +541,9 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination } func (t *Endpoint) listenPacketWithAddress(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + if t.systemDialer != nil { + return t.systemDialer.ListenPacket(ctx, destination) + } addr4, addr6 := t.server.TailscaleIPs() bind := tcpip.FullAddress{ NIC: 1, @@ -679,19 +701,29 @@ func (t *Endpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, } func (t *Endpoint) NewDirectRouteConnection(metadata adapter.InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) { - inet4Address, inet6Address := t.server.TailscaleIPs() - if metadata.Destination.Addr.Is4() && !inet4Address.IsValid() || metadata.Destination.Addr.Is6() && !inet6Address.IsValid() { - return nil, E.New("Tailscale is not ready yet") - } ctx := log.ContextWithNewID(t.ctx) - destination, err := ping.ConnectGVisor( - ctx, t.logger, - metadata.Source.Addr, metadata.Destination.Addr, - routeContext, - t.stack, - inet4Address, inet6Address, - timeout, - ) + var destination tun.DirectRouteDestination + var err error + if t.systemDialer != nil { + destination, err = ping.ConnectDestination( + ctx, t.logger, + t.systemDialer.DialerForICMPDestination(metadata.Destination.Addr).Control, + metadata.Destination.Addr, routeContext, timeout, + ) + } else { + inet4Address, inet6Address := t.server.TailscaleIPs() + if metadata.Destination.Addr.Is4() && !inet4Address.IsValid() || metadata.Destination.Addr.Is6() && !inet6Address.IsValid() { + return nil, E.New("Tailscale is not ready yet") + } + destination, err = ping.ConnectGVisor( + ctx, t.logger, + metadata.Source.Addr, metadata.Destination.Addr, + routeContext, + t.stack, + inet4Address, inet6Address, + timeout, + ) + } if err != nil { return nil, err } @@ -808,3 +840,17 @@ func (c *dnsConfigurtor) GetBaseConfig() (tsDNS.OSConfig, error) { func (c *dnsConfigurtor) Close() error { return nil } + +type exitRouteFilteringRouter struct { + router.Router +} + +func (r *exitRouteFilteringRouter) Set(config *router.Config) error { + if config != nil { + config = config.Clone() + config.Routes = common.Filter(config.Routes, func(prefix netip.Prefix) bool { + return !tsaddr.IsExitRoute(prefix) + }) + } + return r.Router.Set(config) +}