mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-28 19:27:41 +00:00

Fixes #14492 ----- Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. Change-Id: I6dc1068d34bbfa7477e7b7a56a4325b3868c92e1 Signed-off-by: Marc Paquette <marcphilippaquette@gmail.com>
106 lines
3.7 KiB
Go
106 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
|
|
}
|