mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-26 19:45:35 +00:00
7675c3ebf2
Indf6014f1d7
we removed build tag gating preventing importation, which tripped a NetworkExtension limit test in corp. This was a reversal of25f0a3fc8f
which actually made the situation worse, hence the simplification. This commit goes back to the strategy in25f0a3fc8f
, and gets us back under the limit in my local testing. Admittedly, we don't fully understand the effects of importing or excluding importation of this package, and have seen mixed results, but this commit allows us to move forward again. Updates tailscale/corp#22125 Signed-off-by: Jordan Whited <jordan@tailscale.com>
105 lines
3.7 KiB
Go
105 lines
3.7 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package gro implements GRO for the receive (write) path into gVisor.
|
|
package gro
|
|
|
|
import (
|
|
"bytes"
|
|
"github.com/tailscale/wireguard-go/tun"
|
|
"gvisor.dev/gvisor/pkg/buffer"
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
|
"gvisor.dev/gvisor/pkg/tcpip/header/parse"
|
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
|
"tailscale.com/net/packet"
|
|
"tailscale.com/types/ipproto"
|
|
)
|
|
|
|
// RXChecksumOffload validates IPv4, TCP, and UDP header checksums in p,
|
|
// returning an equivalent *stack.PacketBuffer if they are valid, otherwise nil.
|
|
// The set of headers validated covers where gVisor would perform validation if
|
|
// !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
|
|
}
|