mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-16 19:51:41 +00:00
Revert "go.mod,net/tstun,wgengine/netstack: implement gVisor TCP GRO for Linux (#12921)"
This reverts commit f0230ce0b596a49735d7cac6be6a1f7eb22e0fa5. It also reverts commit 25f0a3fc8f6f9cf681bb5afda8e1762816c67a8b which depended on f0230ce0b596a49735d7cac6be6a1f7eb22e0fa5. Updates tailscale/corp#22348 Signed-off-by: Jordan Whited <jordan@tailscale.com>
This commit is contained in:
parent
db4247f705
commit
657f17a805
@ -314,7 +314,6 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
|||||||
gvisor.dev/gvisor/pkg/tcpip/ports from gvisor.dev/gvisor/pkg/tcpip/stack+
|
gvisor.dev/gvisor/pkg/tcpip/ports from gvisor.dev/gvisor/pkg/tcpip/stack+
|
||||||
gvisor.dev/gvisor/pkg/tcpip/seqnum from gvisor.dev/gvisor/pkg/tcpip/header+
|
gvisor.dev/gvisor/pkg/tcpip/seqnum from gvisor.dev/gvisor/pkg/tcpip/header+
|
||||||
💣 gvisor.dev/gvisor/pkg/tcpip/stack from gvisor.dev/gvisor/pkg/tcpip/adapters/gonet+
|
💣 gvisor.dev/gvisor/pkg/tcpip/stack from gvisor.dev/gvisor/pkg/tcpip/adapters/gonet+
|
||||||
gvisor.dev/gvisor/pkg/tcpip/stack/gro from tailscale.com/wgengine/netstack
|
|
||||||
gvisor.dev/gvisor/pkg/tcpip/transport from gvisor.dev/gvisor/pkg/tcpip/transport/icmp+
|
gvisor.dev/gvisor/pkg/tcpip/transport from gvisor.dev/gvisor/pkg/tcpip/transport/icmp+
|
||||||
gvisor.dev/gvisor/pkg/tcpip/transport/icmp from tailscale.com/wgengine/netstack
|
gvisor.dev/gvisor/pkg/tcpip/transport/icmp from tailscale.com/wgengine/netstack
|
||||||
gvisor.dev/gvisor/pkg/tcpip/transport/internal/network from gvisor.dev/gvisor/pkg/tcpip/transport/icmp+
|
gvisor.dev/gvisor/pkg/tcpip/transport/internal/network from gvisor.dev/gvisor/pkg/tcpip/transport/icmp+
|
||||||
|
@ -225,7 +225,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
gvisor.dev/gvisor/pkg/tcpip/ports from gvisor.dev/gvisor/pkg/tcpip/stack+
|
gvisor.dev/gvisor/pkg/tcpip/ports from gvisor.dev/gvisor/pkg/tcpip/stack+
|
||||||
gvisor.dev/gvisor/pkg/tcpip/seqnum from gvisor.dev/gvisor/pkg/tcpip/header+
|
gvisor.dev/gvisor/pkg/tcpip/seqnum from gvisor.dev/gvisor/pkg/tcpip/header+
|
||||||
💣 gvisor.dev/gvisor/pkg/tcpip/stack from gvisor.dev/gvisor/pkg/tcpip/adapters/gonet+
|
💣 gvisor.dev/gvisor/pkg/tcpip/stack from gvisor.dev/gvisor/pkg/tcpip/adapters/gonet+
|
||||||
gvisor.dev/gvisor/pkg/tcpip/stack/gro from tailscale.com/wgengine/netstack
|
|
||||||
gvisor.dev/gvisor/pkg/tcpip/transport from gvisor.dev/gvisor/pkg/tcpip/transport/icmp+
|
gvisor.dev/gvisor/pkg/tcpip/transport from gvisor.dev/gvisor/pkg/tcpip/transport/icmp+
|
||||||
gvisor.dev/gvisor/pkg/tcpip/transport/icmp from tailscale.com/wgengine/netstack
|
gvisor.dev/gvisor/pkg/tcpip/transport/icmp from tailscale.com/wgengine/netstack
|
||||||
gvisor.dev/gvisor/pkg/tcpip/transport/internal/network from gvisor.dev/gvisor/pkg/tcpip/transport/icmp+
|
gvisor.dev/gvisor/pkg/tcpip/transport/internal/network from gvisor.dev/gvisor/pkg/tcpip/transport/icmp+
|
||||||
|
4
go.mod
4
go.mod
@ -82,7 +82,7 @@ require (
|
|||||||
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4
|
github.com/tailscale/peercred v0.0.0-20240214030740-b535050b2aa4
|
||||||
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1
|
github.com/tailscale/web-client-prebuilt v0.0.0-20240226180453-5db17b287bf1
|
||||||
github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6
|
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-20240724015428-60eeedfd624b
|
||||||
github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e
|
github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e
|
||||||
github.com/tc-hib/winres v0.2.1
|
github.com/tc-hib/winres v0.2.1
|
||||||
github.com/tcnksm/go-httpstat v0.2.0
|
github.com/tcnksm/go-httpstat v0.2.0
|
||||||
@ -106,7 +106,7 @@ require (
|
|||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987
|
gvisor.dev/gvisor v0.0.0-20240306221502-ee1e1f6070e3
|
||||||
honnef.co/go/tools v0.4.6
|
honnef.co/go/tools v0.4.6
|
||||||
k8s.io/api v0.30.3
|
k8s.io/api v0.30.3
|
||||||
k8s.io/apimachinery v0.30.3
|
k8s.io/apimachinery v0.30.3
|
||||||
|
8
go.sum
8
go.sum
@ -938,8 +938,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/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 h1:l10Gi6w9jxvinoiq15g8OToDdASBni4CyJOdHY1Hr8M=
|
||||||
github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6/go.mod h1:ZXRML051h7o4OcI0d3AaILDIad/Xw0IkXaHM17dic1Y=
|
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-20240724015428-60eeedfd624b h1:8U9NaPB32iFoNjJ+H/yPkAVqXw/dudtj+fLTE4edF+Q=
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20240731203015-71393c576b98/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4=
|
github.com/tailscale/wireguard-go v0.0.0-20240724015428-60eeedfd624b/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 h1:zOGKqN5D5hHhiYUp091JqK7DPCqSARyUfduhGUY8Bek=
|
||||||
github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg=
|
github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg=
|
||||||
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
||||||
@ -1495,8 +1495,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||||
gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987 h1:TU8z2Lh3Bbq77w0t1eG8yRlLcNHzZu3x6mhoH2Mk0c8=
|
gvisor.dev/gvisor v0.0.0-20240306221502-ee1e1f6070e3 h1:/8/t5pz/mgdRXhYOIeqqYhFAQLE4DDGegc0Y4ZjyFJM=
|
||||||
gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU=
|
gvisor.dev/gvisor v0.0.0-20240306221502-ee1e1f6070e3/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
@ -162,10 +162,6 @@ type Wrapper struct {
|
|||||||
PreFilterPacketInboundFromWireGuard FilterFunc
|
PreFilterPacketInboundFromWireGuard FilterFunc
|
||||||
// PostFilterPacketInboundFromWireGuard is the inbound filter function that runs after the main filter.
|
// PostFilterPacketInboundFromWireGuard is the inbound filter function that runs after the main filter.
|
||||||
PostFilterPacketInboundFromWireGuard FilterFunc
|
PostFilterPacketInboundFromWireGuard FilterFunc
|
||||||
// EndPacketVectorInboundFromWireGuardFlush is a function that runs after all packets in a given vector
|
|
||||||
// have been handled by all filters. Filters may queue packets for the purposes of GRO, requiring an
|
|
||||||
// explicit flush.
|
|
||||||
EndPacketVectorInboundFromWireGuardFlush func()
|
|
||||||
// PreFilterPacketOutboundToWireGuardNetstackIntercept is a filter function that runs before the main filter
|
// PreFilterPacketOutboundToWireGuardNetstackIntercept is a filter function that runs before the main filter
|
||||||
// for packets from the local system. This filter is populated by netstack to hook
|
// for packets from the local system. This filter is populated by netstack to hook
|
||||||
// packets that should be handled by netstack. If set, this filter runs before
|
// packets that should be handled by netstack. If set, this filter runs before
|
||||||
@ -1183,9 +1179,6 @@ func (t *Wrapper) Write(buffs [][]byte, offset int) (int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.EndPacketVectorInboundFromWireGuardFlush != nil {
|
|
||||||
t.EndPacketVectorInboundFromWireGuardFlush()
|
|
||||||
}
|
|
||||||
if t.disableFilter {
|
if t.disableFilter {
|
||||||
i = len(buffs)
|
i = len(buffs)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
//go:build !ios
|
|
||||||
|
|
||||||
package netstack
|
|
||||||
|
|
||||||
import (
|
|
||||||
nsgro "gvisor.dev/gvisor/pkg/tcpip/stack/gro"
|
|
||||||
)
|
|
||||||
|
|
||||||
// gro wraps a gVisor GRO implementation. It exists solely to prevent iOS from
|
|
||||||
// importing said package (see _ios.go).
|
|
||||||
type gro struct {
|
|
||||||
nsgro.GRO
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
//go:build ios
|
|
||||||
|
|
||||||
package netstack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
||||||
)
|
|
||||||
|
|
||||||
// gro on iOS delivers packets to its Dispatcher, immediately. This type exists
|
|
||||||
// to prevent importation of the gVisor GRO implementation as said package
|
|
||||||
// increases binary size. This is a penalty we do not wish to pay since we
|
|
||||||
// currently do not leverage GRO on iOS.
|
|
||||||
type gro struct {
|
|
||||||
Dispatcher stack.NetworkDispatcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *gro) Init(v bool) {
|
|
||||||
if v {
|
|
||||||
panic("GRO is not supported on this platform")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *gro) Flush() {}
|
|
||||||
|
|
||||||
func (g *gro) Enqueue(pkt *stack.PacketBuffer) {
|
|
||||||
g.Dispatcher.DeliverNetworkPacket(pkt.NetworkProtocolNumber, pkt)
|
|
||||||
}
|
|
@ -4,18 +4,12 @@
|
|||||||
package netstack
|
package netstack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/tailscale/wireguard-go/tun"
|
|
||||||
"gvisor.dev/gvisor/pkg/buffer"
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/header/parse"
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
"tailscale.com/net/packet"
|
|
||||||
"tailscale.com/types/ipproto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type queue struct {
|
type queue struct {
|
||||||
@ -85,53 +79,34 @@ var _ stack.GSOEndpoint = (*linkEndpoint)(nil)
|
|||||||
|
|
||||||
// linkEndpoint implements stack.LinkEndpoint and stack.GSOEndpoint. Outbound
|
// linkEndpoint implements stack.LinkEndpoint and stack.GSOEndpoint. Outbound
|
||||||
// packets written by gVisor towards Tailscale are stored in a channel.
|
// packets written by gVisor towards Tailscale are stored in a channel.
|
||||||
// Inbound is fed to gVisor via injectInbound or enqueueGRO. This is loosely
|
// Inbound is fed to gVisor via InjectInbound. This is loosely modeled after
|
||||||
// modeled after gvisor.dev/pkg/tcpip/link/channel.Endpoint.
|
// gvisor.dev/pkg/tcpip/link/channel.Endpoint.
|
||||||
type linkEndpoint struct {
|
type linkEndpoint struct {
|
||||||
SupportedGSOKind stack.SupportedGSO
|
LinkEPCapabilities stack.LinkEndpointCapabilities
|
||||||
initGRO initGRO
|
SupportedGSOKind stack.SupportedGSO
|
||||||
|
|
||||||
mu sync.RWMutex // mu guards the following fields
|
mu sync.RWMutex // mu guards the following fields
|
||||||
dispatcher stack.NetworkDispatcher
|
dispatcher stack.NetworkDispatcher
|
||||||
linkAddr tcpip.LinkAddress
|
linkAddr tcpip.LinkAddress
|
||||||
mtu uint32
|
mtu uint32
|
||||||
gro gro // mu only guards access to gro.Dispatcher
|
|
||||||
|
|
||||||
q *queue // outbound
|
q *queue // outbound
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(jwhited): move to linkEndpointOpts struct or similar.
|
func newLinkEndpoint(size int, mtu uint32, linkAddr tcpip.LinkAddress) *linkEndpoint {
|
||||||
type initGRO bool
|
return &linkEndpoint{
|
||||||
|
|
||||||
const (
|
|
||||||
disableGRO initGRO = false
|
|
||||||
enableGRO initGRO = true
|
|
||||||
)
|
|
||||||
|
|
||||||
func newLinkEndpoint(size int, mtu uint32, linkAddr tcpip.LinkAddress, gro initGRO) *linkEndpoint {
|
|
||||||
le := &linkEndpoint{
|
|
||||||
q: &queue{
|
q: &queue{
|
||||||
c: make(chan *stack.PacketBuffer, size),
|
c: make(chan *stack.PacketBuffer, size),
|
||||||
},
|
},
|
||||||
mtu: mtu,
|
mtu: mtu,
|
||||||
linkAddr: linkAddr,
|
linkAddr: linkAddr,
|
||||||
}
|
}
|
||||||
le.initGRO = gro
|
|
||||||
le.gro.Init(bool(gro))
|
|
||||||
return le
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes l. Further packet injections will return an error, and all
|
// Close closes l. Further packet injections will return an error, and all
|
||||||
// pending packets are discarded. Close may be called concurrently with
|
// pending packets are discarded. Close may be called concurrently with
|
||||||
// WritePackets.
|
// WritePackets.
|
||||||
func (l *linkEndpoint) Close() {
|
func (l *linkEndpoint) Close() {
|
||||||
l.mu.Lock()
|
|
||||||
if l.gro.Dispatcher != nil {
|
|
||||||
l.gro.Flush()
|
|
||||||
}
|
|
||||||
l.dispatcher = nil
|
|
||||||
l.gro.Dispatcher = nil
|
|
||||||
l.mu.Unlock()
|
|
||||||
l.q.Close()
|
l.q.Close()
|
||||||
l.Drain()
|
l.Drain()
|
||||||
}
|
}
|
||||||
@ -157,149 +132,19 @@ func (l *linkEndpoint) Drain() int {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumQueued returns the number of packets queued for outbound.
|
// NumQueued returns the number of packet queued for outbound.
|
||||||
func (l *linkEndpoint) NumQueued() int {
|
func (l *linkEndpoint) NumQueued() int {
|
||||||
return l.q.Num()
|
return l.q.Num()
|
||||||
}
|
}
|
||||||
|
|
||||||
// rxChecksumOffload validates IPv4, TCP, and UDP header checksums in p,
|
// InjectInbound injects an inbound packet. If the endpoint is not attached, the
|
||||||
// returning an equivalent *stack.PacketBuffer if they are valid, otherwise nil.
|
// packet is not delivered.
|
||||||
// The set of headers validated covers where gVisor would perform validation if
|
func (l *linkEndpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) {
|
||||||
// !stack.PacketBuffer.RXChecksumValidated, i.e. it satisfies
|
|
||||||
// stack.CapabilityRXChecksumOffload. Other protocols with checksum fields,
|
|
||||||
// e.g. ICMP{v6}, are still validated by gVisor regardless of rx checksum
|
|
||||||
// offloading capabilities.
|
|
||||||
func rxChecksumOffload(p *packet.Parsed) *stack.PacketBuffer {
|
|
||||||
var (
|
|
||||||
pn tcpip.NetworkProtocolNumber
|
|
||||||
csumStart int
|
|
||||||
)
|
|
||||||
buf := p.Buffer()
|
|
||||||
|
|
||||||
switch p.IPVersion {
|
|
||||||
case 4:
|
|
||||||
if len(buf) < header.IPv4MinimumSize {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
csumStart = int((buf[0] & 0x0F) * 4)
|
|
||||||
if csumStart < header.IPv4MinimumSize || csumStart > header.IPv4MaximumHeaderSize || len(buf) < csumStart {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if ^tun.Checksum(buf[:csumStart], 0) != 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pn = header.IPv4ProtocolNumber
|
|
||||||
case 6:
|
|
||||||
if len(buf) < header.IPv6FixedHeaderSize {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
csumStart = header.IPv6FixedHeaderSize
|
|
||||||
pn = header.IPv6ProtocolNumber
|
|
||||||
if p.IPProto != ipproto.ICMPv6 && p.IPProto != ipproto.TCP && p.IPProto != ipproto.UDP {
|
|
||||||
// buf could have extension headers before a UDP or TCP header, but
|
|
||||||
// packet.Parsed.IPProto will be set to the ext header type, so we
|
|
||||||
// have to look deeper. We are still responsible for validating the
|
|
||||||
// L4 checksum in this case. So, make use of gVisor's existing
|
|
||||||
// extension header parsing via parse.IPv6() in order to unpack the
|
|
||||||
// L4 csumStart index. This is not particularly efficient as we have
|
|
||||||
// to allocate a short-lived stack.PacketBuffer that cannot be
|
|
||||||
// re-used. parse.IPv6() "consumes" the IPv6 headers, so we can't
|
|
||||||
// inject this stack.PacketBuffer into the stack at a later point.
|
|
||||||
packetBuf := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
|
||||||
Payload: buffer.MakeWithData(bytes.Clone(buf)),
|
|
||||||
})
|
|
||||||
defer packetBuf.DecRef()
|
|
||||||
// The rightmost bool returns false only if packetBuf is too short,
|
|
||||||
// which we've already accounted for above.
|
|
||||||
transportProto, _, _, _, _ := parse.IPv6(packetBuf)
|
|
||||||
if transportProto == header.TCPProtocolNumber || transportProto == header.UDPProtocolNumber {
|
|
||||||
csumLen := packetBuf.Data().Size()
|
|
||||||
if len(buf) < csumLen {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
csumStart = len(buf) - csumLen
|
|
||||||
p.IPProto = ipproto.Proto(transportProto)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.IPProto == ipproto.TCP || p.IPProto == ipproto.UDP {
|
|
||||||
lenForPseudo := len(buf) - csumStart
|
|
||||||
csum := tun.PseudoHeaderChecksum(
|
|
||||||
uint8(p.IPProto),
|
|
||||||
p.Src.Addr().AsSlice(),
|
|
||||||
p.Dst.Addr().AsSlice(),
|
|
||||||
uint16(lenForPseudo))
|
|
||||||
csum = tun.Checksum(buf[csumStart:], csum)
|
|
||||||
if ^csum != 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
packetBuf := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
|
||||||
Payload: buffer.MakeWithData(bytes.Clone(buf)),
|
|
||||||
})
|
|
||||||
packetBuf.NetworkProtocolNumber = pn
|
|
||||||
// Setting this is not technically required. gVisor overrides where
|
|
||||||
// stack.CapabilityRXChecksumOffload is advertised from Capabilities().
|
|
||||||
// https://github.com/google/gvisor/blob/64c016c92987cc04dfd4c7b091ddd21bdad875f8/pkg/tcpip/stack/nic.go#L763
|
|
||||||
// This is also why we offload for all packets since we cannot signal this
|
|
||||||
// per-packet.
|
|
||||||
packetBuf.RXChecksumValidated = true
|
|
||||||
return packetBuf
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *linkEndpoint) injectInbound(p *packet.Parsed) {
|
|
||||||
l.mu.RLock()
|
l.mu.RLock()
|
||||||
d := l.dispatcher
|
d := l.dispatcher
|
||||||
l.mu.RUnlock()
|
l.mu.RUnlock()
|
||||||
if d == nil {
|
if d != nil {
|
||||||
return
|
d.DeliverNetworkPacket(protocol, pkt)
|
||||||
}
|
|
||||||
pkt := rxChecksumOffload(p)
|
|
||||||
if pkt == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d.DeliverNetworkPacket(pkt.NetworkProtocolNumber, pkt)
|
|
||||||
pkt.DecRef()
|
|
||||||
}
|
|
||||||
|
|
||||||
// enqueueGRO enqueues the provided packet for GRO. It may immediately deliver
|
|
||||||
// it to the underlying stack.NetworkDispatcher depending on its contents and if
|
|
||||||
// GRO was initialized via newLinkEndpoint. To explicitly flush previously
|
|
||||||
// enqueued packets see flushGRO. enqueueGRO is not thread-safe and must not
|
|
||||||
// be called concurrently with flushGRO.
|
|
||||||
func (l *linkEndpoint) enqueueGRO(p *packet.Parsed) {
|
|
||||||
l.mu.RLock()
|
|
||||||
defer l.mu.RUnlock()
|
|
||||||
if l.gro.Dispatcher == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pkt := rxChecksumOffload(p)
|
|
||||||
if pkt == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO(jwhited): gro.Enqueue() duplicates a lot of p.Decode().
|
|
||||||
// We may want to push stack.PacketBuffer further up as a
|
|
||||||
// replacement for packet.Parsed, or inversely push packet.Parsed
|
|
||||||
// down into refactored GRO logic.
|
|
||||||
l.gro.Enqueue(pkt)
|
|
||||||
pkt.DecRef()
|
|
||||||
}
|
|
||||||
|
|
||||||
// flushGRO flushes previously enqueueGRO'd packets to the underlying
|
|
||||||
// stack.NetworkDispatcher. flushGRO is not thread-safe, and must not be
|
|
||||||
// called concurrently with enqueueGRO.
|
|
||||||
func (l *linkEndpoint) flushGRO() {
|
|
||||||
if !l.initGRO {
|
|
||||||
// If GRO was not initialized fast path return to avoid scanning GRO
|
|
||||||
// buckets (see l.gro.Flush()) that will always be empty.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
l.mu.RLock()
|
|
||||||
defer l.mu.RUnlock()
|
|
||||||
if l.gro.Dispatcher != nil {
|
|
||||||
l.gro.Flush()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,7 +154,6 @@ func (l *linkEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
|||||||
l.mu.Lock()
|
l.mu.Lock()
|
||||||
defer l.mu.Unlock()
|
defer l.mu.Unlock()
|
||||||
l.dispatcher = dispatcher
|
l.dispatcher = dispatcher
|
||||||
l.gro.Dispatcher = dispatcher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAttached implements stack.LinkEndpoint.IsAttached.
|
// IsAttached implements stack.LinkEndpoint.IsAttached.
|
||||||
@ -335,9 +179,7 @@ func (l *linkEndpoint) SetMTU(mtu uint32) {
|
|||||||
|
|
||||||
// Capabilities implements stack.LinkEndpoint.Capabilities.
|
// Capabilities implements stack.LinkEndpoint.Capabilities.
|
||||||
func (l *linkEndpoint) Capabilities() stack.LinkEndpointCapabilities {
|
func (l *linkEndpoint) Capabilities() stack.LinkEndpointCapabilities {
|
||||||
// We are required to offload RX checksum validation for the purposes of
|
return l.LinkEPCapabilities
|
||||||
// GRO.
|
|
||||||
return stack.CapabilityRXChecksumOffload
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GSOMaxSize implements stack.GSOEndpoint.
|
// GSOMaxSize implements stack.GSOEndpoint.
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
package netstack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"net/netip"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
|
||||||
"tailscale.com/net/packet"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_rxChecksumOffload(t *testing.T) {
|
|
||||||
payloadLen := 100
|
|
||||||
|
|
||||||
tcpFields := &header.TCPFields{
|
|
||||||
SrcPort: 1,
|
|
||||||
DstPort: 1,
|
|
||||||
SeqNum: 1,
|
|
||||||
AckNum: 1,
|
|
||||||
DataOffset: 20,
|
|
||||||
Flags: header.TCPFlagAck | header.TCPFlagPsh,
|
|
||||||
WindowSize: 3000,
|
|
||||||
}
|
|
||||||
tcp4 := make([]byte, 20+20+payloadLen)
|
|
||||||
ipv4H := header.IPv4(tcp4)
|
|
||||||
ipv4H.Encode(&header.IPv4Fields{
|
|
||||||
SrcAddr: tcpip.AddrFromSlice(netip.MustParseAddr("192.0.2.1").AsSlice()),
|
|
||||||
DstAddr: tcpip.AddrFromSlice(netip.MustParseAddr("192.0.2.2").AsSlice()),
|
|
||||||
Protocol: uint8(header.TCPProtocolNumber),
|
|
||||||
TTL: 64,
|
|
||||||
TotalLength: uint16(len(tcp4)),
|
|
||||||
})
|
|
||||||
ipv4H.SetChecksum(^ipv4H.CalculateChecksum())
|
|
||||||
tcpH := header.TCP(tcp4[20:])
|
|
||||||
tcpH.Encode(tcpFields)
|
|
||||||
pseudoCsum := header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipv4H.SourceAddress(), ipv4H.DestinationAddress(), uint16(20+payloadLen))
|
|
||||||
tcpH.SetChecksum(^tcpH.CalculateChecksum(pseudoCsum))
|
|
||||||
|
|
||||||
tcp6ExtHeader := make([]byte, 40+8+20+payloadLen)
|
|
||||||
ipv6H := header.IPv6(tcp6ExtHeader)
|
|
||||||
ipv6H.Encode(&header.IPv6Fields{
|
|
||||||
SrcAddr: tcpip.AddrFromSlice(netip.MustParseAddr("2001:db8::1").AsSlice()),
|
|
||||||
DstAddr: tcpip.AddrFromSlice(netip.MustParseAddr("2001:db8::2").AsSlice()),
|
|
||||||
TransportProtocol: 60, // really next header; destination options ext header
|
|
||||||
HopLimit: 64,
|
|
||||||
PayloadLength: uint16(8 + 20 + payloadLen),
|
|
||||||
})
|
|
||||||
tcp6ExtHeader[40] = uint8(header.TCPProtocolNumber) // next header
|
|
||||||
tcp6ExtHeader[41] = 0 // length of ext header in 8-octet units, exclusive of first 8 octets.
|
|
||||||
// 42-47 options and padding
|
|
||||||
tcpH = header.TCP(tcp6ExtHeader[48:])
|
|
||||||
tcpH.Encode(tcpFields)
|
|
||||||
pseudoCsum = header.PseudoHeaderChecksum(header.TCPProtocolNumber, ipv6H.SourceAddress(), ipv6H.DestinationAddress(), uint16(20+payloadLen))
|
|
||||||
tcpH.SetChecksum(^tcpH.CalculateChecksum(pseudoCsum))
|
|
||||||
|
|
||||||
tcp4InvalidCsum := make([]byte, len(tcp4))
|
|
||||||
copy(tcp4InvalidCsum, tcp4)
|
|
||||||
at := 20 + 16
|
|
||||||
tcp4InvalidCsum[at] = ^tcp4InvalidCsum[at]
|
|
||||||
|
|
||||||
tcp6ExtHeaderInvalidCsum := make([]byte, len(tcp6ExtHeader))
|
|
||||||
copy(tcp6ExtHeaderInvalidCsum, tcp6ExtHeader)
|
|
||||||
at = 40 + 8 + 16
|
|
||||||
tcp6ExtHeaderInvalidCsum[at] = ^tcp6ExtHeaderInvalidCsum[at]
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input []byte
|
|
||||||
wantPB bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"tcp4 packet valid csum",
|
|
||||||
tcp4,
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tcp6 with ext header valid csum",
|
|
||||||
tcp6ExtHeader,
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tcp4 packet invalid csum",
|
|
||||||
tcp4InvalidCsum,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tcp6 with ext header invalid csum",
|
|
||||||
tcp6ExtHeaderInvalidCsum,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
p := &packet.Parsed{}
|
|
||||||
p.Decode(tt.input)
|
|
||||||
got := rxChecksumOffload(p)
|
|
||||||
if tt.wantPB != (got != nil) {
|
|
||||||
t.Fatalf("wantPB = %v != (got != nil): %v", tt.wantPB, got != nil)
|
|
||||||
}
|
|
||||||
if tt.wantPB {
|
|
||||||
gotBuf := got.ToBuffer()
|
|
||||||
if !bytes.Equal(tt.input, gotBuf.Flatten()) {
|
|
||||||
t.Fatal("output packet unequal to input")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,6 +5,7 @@
|
|||||||
package netstack
|
package netstack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"expvar"
|
"expvar"
|
||||||
@ -20,6 +21,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gvisor.dev/gvisor/pkg/buffer"
|
||||||
"gvisor.dev/gvisor/pkg/refs"
|
"gvisor.dev/gvisor/pkg/refs"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||||
@ -324,14 +326,10 @@ func Create(logf logger.Logf, tundev *tstun.Wrapper, e wgengine.Engine, mc *magi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var linkEP *linkEndpoint
|
linkEP := newLinkEndpoint(512, uint32(tstun.DefaultTUNMTU()), "")
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
// TODO(jwhited): add Windows GSO support https://github.com/tailscale/corp/issues/21874
|
// TODO(jwhited): add Windows support https://github.com/tailscale/corp/issues/21874
|
||||||
// TODO(jwhited): exercise enableGRO in relation to https://github.com/tailscale/corp/issues/22353
|
|
||||||
linkEP = newLinkEndpoint(512, uint32(tstun.DefaultTUNMTU()), "", disableGRO)
|
|
||||||
linkEP.SupportedGSOKind = stack.HostGSOSupported
|
linkEP.SupportedGSOKind = stack.HostGSOSupported
|
||||||
} else {
|
|
||||||
linkEP = newLinkEndpoint(512, uint32(tstun.DefaultTUNMTU()), "", disableGRO)
|
|
||||||
}
|
}
|
||||||
if tcpipProblem := ipstack.CreateNIC(nicID, linkEP); tcpipProblem != nil {
|
if tcpipProblem := ipstack.CreateNIC(nicID, linkEP); tcpipProblem != nil {
|
||||||
return nil, fmt.Errorf("could not create netstack NIC: %v", tcpipProblem)
|
return nil, fmt.Errorf("could not create netstack NIC: %v", tcpipProblem)
|
||||||
@ -380,7 +378,6 @@ func Create(logf logger.Logf, tundev *tstun.Wrapper, e wgengine.Engine, mc *magi
|
|||||||
ns.ctx, ns.ctxCancel = context.WithCancel(context.Background())
|
ns.ctx, ns.ctxCancel = context.WithCancel(context.Background())
|
||||||
ns.atomicIsLocalIPFunc.Store(ipset.FalseContainsIPFunc())
|
ns.atomicIsLocalIPFunc.Store(ipset.FalseContainsIPFunc())
|
||||||
ns.tundev.PostFilterPacketInboundFromWireGuard = ns.injectInbound
|
ns.tundev.PostFilterPacketInboundFromWireGuard = ns.injectInbound
|
||||||
ns.tundev.EndPacketVectorInboundFromWireGuardFlush = linkEP.flushGRO
|
|
||||||
ns.tundev.PreFilterPacketOutboundToWireGuardNetstackIntercept = ns.handleLocalPackets
|
ns.tundev.PreFilterPacketOutboundToWireGuardNetstackIntercept = ns.handleLocalPackets
|
||||||
stacksForMetrics.Store(ns, struct{}{})
|
stacksForMetrics.Store(ns, struct{}{})
|
||||||
return ns, nil
|
return ns, nil
|
||||||
@ -780,11 +777,23 @@ func (ns *Impl) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper) filter.Re
|
|||||||
// care about the packet; resume processing.
|
// care about the packet; resume processing.
|
||||||
return filter.Accept
|
return filter.Accept
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pn tcpip.NetworkProtocolNumber
|
||||||
|
switch p.IPVersion {
|
||||||
|
case 4:
|
||||||
|
pn = header.IPv4ProtocolNumber
|
||||||
|
case 6:
|
||||||
|
pn = header.IPv6ProtocolNumber
|
||||||
|
}
|
||||||
if debugPackets {
|
if debugPackets {
|
||||||
ns.logf("[v2] service packet in (from %v): % x", p.Src, p.Buffer())
|
ns.logf("[v2] service packet in (from %v): % x", p.Src, p.Buffer())
|
||||||
}
|
}
|
||||||
|
|
||||||
ns.linkEP.injectInbound(p)
|
packetBuf := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||||
|
Payload: buffer.MakeWithData(bytes.Clone(p.Buffer())),
|
||||||
|
})
|
||||||
|
ns.linkEP.InjectInbound(pn, packetBuf)
|
||||||
|
packetBuf.DecRef()
|
||||||
return filter.DropSilently
|
return filter.DropSilently
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -825,7 +834,7 @@ func (ns *Impl) DialContextUDP(ctx context.Context, ipp netip.AddrPort) (*gonet.
|
|||||||
func (ns *Impl) inject() {
|
func (ns *Impl) inject() {
|
||||||
for {
|
for {
|
||||||
pkt := ns.linkEP.ReadContext(ns.ctx)
|
pkt := ns.linkEP.ReadContext(ns.ctx)
|
||||||
if pkt == nil {
|
if pkt.IsNil() {
|
||||||
if ns.ctx.Err() != nil {
|
if ns.ctx.Err() != nil {
|
||||||
// Return without logging.
|
// Return without logging.
|
||||||
return
|
return
|
||||||
@ -1069,10 +1078,21 @@ func (ns *Impl) injectInbound(p *packet.Parsed, t *tstun.Wrapper) filter.Respons
|
|||||||
return filter.DropSilently
|
return filter.DropSilently
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pn tcpip.NetworkProtocolNumber
|
||||||
|
switch p.IPVersion {
|
||||||
|
case 4:
|
||||||
|
pn = header.IPv4ProtocolNumber
|
||||||
|
case 6:
|
||||||
|
pn = header.IPv6ProtocolNumber
|
||||||
|
}
|
||||||
if debugPackets {
|
if debugPackets {
|
||||||
ns.logf("[v2] packet in (from %v): % x", p.Src, p.Buffer())
|
ns.logf("[v2] packet in (from %v): % x", p.Src, p.Buffer())
|
||||||
}
|
}
|
||||||
ns.linkEP.enqueueGRO(p)
|
packetBuf := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||||
|
Payload: buffer.MakeWithData(bytes.Clone(p.Buffer())),
|
||||||
|
})
|
||||||
|
ns.linkEP.InjectInbound(pn, packetBuf)
|
||||||
|
packetBuf.DecRef()
|
||||||
|
|
||||||
// We've now delivered this to netstack, so we're done.
|
// We've now delivered this to netstack, so we're done.
|
||||||
// Instead of returning a filter.Accept here (which would also
|
// Instead of returning a filter.Accept here (which would also
|
||||||
|
Loading…
x
Reference in New Issue
Block a user