From 7aa766ee65f024af2df2499a350c0a88b2a1399d Mon Sep 17 00:00:00 2001 From: Jordan Whited Date: Thu, 5 Sep 2024 09:59:31 -0700 Subject: [PATCH] net/tstun: probe TCP GRO (#13376) Disable TCP & UDP GRO if the probe fails. torvalds/linux@e269d79c7d35aa3808b1f3c1737d63dab504ddc8 broke virtio_net TCP & UDP GRO causing GRO writes to return EINVAL. The bug was then resolved later in torvalds/linux@89add40066f9ed9abe5f7f886fe5789ff7e0c50e. The offending commit was pulled into various LTS releases. Updates #13041 Signed-off-by: Jordan Whited --- go.mod | 2 +- go.sum | 4 +- net/tstun/tun.go | 3 - net/tstun/tun_features_linux.go | 19 ----- net/tstun/wrap_linux.go | 82 +++++++++++++++++++ ...{tun_features_notlinux.go => wrap_noop.go} | 8 +- wgengine/userspace.go | 1 + 7 files changed, 87 insertions(+), 32 deletions(-) delete mode 100644 net/tstun/tun_features_linux.go create mode 100644 net/tstun/wrap_linux.go rename net/tstun/{tun_features_notlinux.go => wrap_noop.go} (51%) diff --git a/go.mod b/go.mod index 04ab3c414..79ea0cb54 100644 --- a/go.mod +++ b/go.mod @@ -85,7 +85,7 @@ require ( github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4 github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6 - github.com/tailscale/wireguard-go v0.0.0-20240731203015-71393c576b98 + github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e github.com/tc-hib/winres v0.2.1 github.com/tcnksm/go-httpstat v0.2.0 diff --git a/go.sum b/go.sum index dfe0b0e54..f032582af 100644 --- a/go.sum +++ b/go.sum @@ -946,8 +946,8 @@ github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1 h1:t github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ= github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6 h1:l10Gi6w9jxvinoiq15g8OToDdASBni4CyJOdHY1Hr8M= github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6/go.mod h1:ZXRML051h7o4OcI0d3AaILDIad/Xw0IkXaHM17dic1Y= -github.com/tailscale/wireguard-go v0.0.0-20240731203015-71393c576b98 h1:RNpJrXfI5u6e+uzyIzvmnXbhmhdRkVf//90sMBH3lso= -github.com/tailscale/wireguard-go v0.0.0-20240731203015-71393c576b98/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4= +github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc h1:cezaQN9pvKVaw56Ma5qr/G646uKIYP0yQf+OyWN/okc= +github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4= github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e h1:zOGKqN5D5hHhiYUp091JqK7DPCqSARyUfduhGUY8Bek= github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg= github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= diff --git a/net/tstun/tun.go b/net/tstun/tun.go index f2c034fc3..66e209d1a 100644 --- a/net/tstun/tun.go +++ b/net/tstun/tun.go @@ -53,9 +53,6 @@ func New(logf logger.Logf, tunName string) (tun.Device, string, error) { dev.Close() return nil, "", err } - if err := setLinkFeatures(dev); err != nil { - logf("setting link features: %v", err) - } if err := setLinkAttrs(dev); err != nil { logf("setting link attributes: %v", err) } diff --git a/net/tstun/tun_features_linux.go b/net/tstun/tun_features_linux.go deleted file mode 100644 index 424088115..000000000 --- a/net/tstun/tun_features_linux.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Tailscale Inc & AUTHORS -// SPDX-License-Identifier: BSD-3-Clause - -package tstun - -import ( - "github.com/tailscale/wireguard-go/tun" - "tailscale.com/envknob" -) - -func setLinkFeatures(dev tun.Device) error { - if envknob.Bool("TS_TUN_DISABLE_UDP_GRO") { - linuxDev, ok := dev.(tun.LinuxDevice) - if ok { - linuxDev.DisableUDPGRO() - } - } - return nil -} diff --git a/net/tstun/wrap_linux.go b/net/tstun/wrap_linux.go new file mode 100644 index 000000000..136ddfe1e --- /dev/null +++ b/net/tstun/wrap_linux.go @@ -0,0 +1,82 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package tstun + +import ( + "errors" + "net/netip" + "runtime" + + "github.com/tailscale/wireguard-go/tun" + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/checksum" + "gvisor.dev/gvisor/pkg/tcpip/header" + "tailscale.com/envknob" + "tailscale.com/net/tsaddr" +) + +// SetLinkFeaturesPostUp configures link features on t based on select TS_TUN_ +// environment variables and OS feature tests. Callers should ensure t is +// up prior to calling, otherwise OS feature tests may be inconclusive. +func (t *Wrapper) SetLinkFeaturesPostUp() { + if t.isTAP || runtime.GOOS == "android" { + return + } + if groDev, ok := t.tdev.(tun.GRODevice); ok { + if envknob.Bool("TS_TUN_DISABLE_UDP_GRO") { + groDev.DisableUDPGRO() + } + if envknob.Bool("TS_TUN_DISABLE_TCP_GRO") { + groDev.DisableTCPGRO() + } + err := probeTCPGRO(groDev) + if errors.Is(err, unix.EINVAL) { + groDev.DisableTCPGRO() + groDev.DisableUDPGRO() + t.logf("disabled TUN TCP & UDP GRO due to GRO probe error: %v", err) + } + } +} + +func probeTCPGRO(dev tun.GRODevice) error { + ipPort := netip.MustParseAddrPort(tsaddr.TailscaleServiceIPString + ":0") + fingerprint := []byte("tailscale-probe-tun-gro") + segmentSize := len(fingerprint) + iphLen := 20 + tcphLen := 20 + totalLen := iphLen + tcphLen + segmentSize + ipAs4 := ipPort.Addr().As4() + bufs := make([][]byte, 2) + for i := range bufs { + bufs[i] = make([]byte, PacketStartOffset+totalLen, PacketStartOffset+(totalLen*2)) + ipv4H := header.IPv4(bufs[i][PacketStartOffset:]) + ipv4H.Encode(&header.IPv4Fields{ + SrcAddr: tcpip.AddrFromSlice(ipAs4[:]), + DstAddr: tcpip.AddrFromSlice(ipAs4[:]), + Protocol: unix.IPPROTO_TCP, + // Use a zero value TTL as best effort means to reduce chance of + // probe packet leaking further than it needs to. + TTL: 0, + TotalLength: uint16(totalLen), + }) + tcpH := header.TCP(bufs[i][PacketStartOffset+iphLen:]) + tcpH.Encode(&header.TCPFields{ + SrcPort: ipPort.Port(), + DstPort: ipPort.Port(), + SeqNum: 1 + uint32(i*segmentSize), + AckNum: 1, + DataOffset: 20, + Flags: header.TCPFlagAck, + WindowSize: 3000, + }) + copy(bufs[i][PacketStartOffset+iphLen+tcphLen:], fingerprint) + ipv4H.SetChecksum(^ipv4H.CalculateChecksum()) + pseudoCsum := header.PseudoHeaderChecksum(unix.IPPROTO_TCP, ipv4H.SourceAddress(), ipv4H.DestinationAddress(), uint16(tcphLen+segmentSize)) + pseudoCsum = checksum.Checksum(bufs[i][PacketStartOffset+iphLen+tcphLen:], pseudoCsum) + tcpH.SetChecksum(^tcpH.CalculateChecksum(pseudoCsum)) + } + _, err := dev.Write(bufs, PacketStartOffset) + return err +} diff --git a/net/tstun/tun_features_notlinux.go b/net/tstun/wrap_noop.go similarity index 51% rename from net/tstun/tun_features_notlinux.go rename to net/tstun/wrap_noop.go index 85285fd05..c743072ca 100644 --- a/net/tstun/tun_features_notlinux.go +++ b/net/tstun/wrap_noop.go @@ -5,10 +5,4 @@ package tstun -import ( - "github.com/tailscale/wireguard-go/tun" -) - -func setLinkFeatures(dev tun.Device) error { - return nil -} +func (t *Wrapper) SetLinkFeaturesPostUp() {} diff --git a/wgengine/userspace.go b/wgengine/userspace.go index f23fe2569..f6b4586cb 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -492,6 +492,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error) if err := e.router.Up(); err != nil { return nil, fmt.Errorf("router.Up: %w", err) } + tsTUNDev.SetLinkFeaturesPostUp() // It's a little pointless to apply no-op settings here (they // should already be empty?), but it at least exercises the