Update
This commit is contained in:
122
vendor/tailscale.com/net/packet/checksum/checksum.go
generated
vendored
122
vendor/tailscale.com/net/packet/checksum/checksum.go
generated
vendored
@@ -8,8 +8,6 @@ import (
|
||||
"encoding/binary"
|
||||
"net/netip"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"tailscale.com/net/packet"
|
||||
"tailscale.com/types/ipproto"
|
||||
)
|
||||
@@ -88,13 +86,13 @@ func updateV4PacketChecksums(p *packet.Parsed, old, new netip.Addr) {
|
||||
tr := p.Transport()
|
||||
switch p.IPProto {
|
||||
case ipproto.UDP, ipproto.DCCP:
|
||||
if len(tr) < header.UDPMinimumSize {
|
||||
if len(tr) < minUDPSize {
|
||||
// Not enough space for a UDP header.
|
||||
return
|
||||
}
|
||||
updateV4Checksum(tr[6:8], o4[:], n4[:])
|
||||
case ipproto.TCP:
|
||||
if len(tr) < header.TCPMinimumSize {
|
||||
if len(tr) < minTCPSize {
|
||||
// Not enough space for a TCP header.
|
||||
return
|
||||
}
|
||||
@@ -112,34 +110,60 @@ func updateV4PacketChecksums(p *packet.Parsed, old, new netip.Addr) {
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
minUDPSize = 8
|
||||
minTCPSize = 20
|
||||
minICMPv6Size = 8
|
||||
minIPv6Header = 40
|
||||
|
||||
offsetICMPv6Checksum = 2
|
||||
offsetUDPChecksum = 6
|
||||
offsetTCPChecksum = 16
|
||||
)
|
||||
|
||||
// updateV6PacketChecksums updates the checksums in the packet buffer.
|
||||
// p is modified in place.
|
||||
// If p.IPProto is unknown, no checksums are updated.
|
||||
func updateV6PacketChecksums(p *packet.Parsed, old, new netip.Addr) {
|
||||
if len(p.Buffer()) < 40 {
|
||||
if len(p.Buffer()) < minIPv6Header {
|
||||
// Not enough space for an IPv6 header.
|
||||
return
|
||||
}
|
||||
o6, n6 := tcpip.AddrFrom16Slice(old.AsSlice()), tcpip.AddrFrom16Slice(new.AsSlice())
|
||||
o6, n6 := old.As16(), new.As16()
|
||||
|
||||
// Now update the transport layer checksums, where applicable.
|
||||
tr := p.Transport()
|
||||
switch p.IPProto {
|
||||
case ipproto.ICMPv6:
|
||||
if len(tr) < header.ICMPv6MinimumSize {
|
||||
if len(tr) < minICMPv6Size {
|
||||
return
|
||||
}
|
||||
header.ICMPv6(tr).UpdateChecksumPseudoHeaderAddress(o6, n6)
|
||||
|
||||
ss := tr[offsetICMPv6Checksum:]
|
||||
xsum := binary.BigEndian.Uint16(ss)
|
||||
binary.BigEndian.PutUint16(ss,
|
||||
^checksumUpdate2ByteAlignedAddress(^xsum, o6, n6))
|
||||
|
||||
case ipproto.UDP, ipproto.DCCP:
|
||||
if len(tr) < header.UDPMinimumSize {
|
||||
if len(tr) < minUDPSize {
|
||||
return
|
||||
}
|
||||
header.UDP(tr).UpdateChecksumPseudoHeaderAddress(o6, n6, true)
|
||||
ss := tr[offsetUDPChecksum:]
|
||||
xsum := binary.BigEndian.Uint16(ss)
|
||||
xsum = ^xsum
|
||||
xsum = checksumUpdate2ByteAlignedAddress(xsum, o6, n6)
|
||||
xsum = ^xsum
|
||||
binary.BigEndian.PutUint16(ss, xsum)
|
||||
case ipproto.TCP:
|
||||
if len(tr) < header.TCPMinimumSize {
|
||||
if len(tr) < minTCPSize {
|
||||
return
|
||||
}
|
||||
header.TCP(tr).UpdateChecksumPseudoHeaderAddress(o6, n6, true)
|
||||
ss := tr[offsetTCPChecksum:]
|
||||
xsum := binary.BigEndian.Uint16(ss)
|
||||
xsum = ^xsum
|
||||
xsum = checksumUpdate2ByteAlignedAddress(xsum, o6, n6)
|
||||
xsum = ^xsum
|
||||
binary.BigEndian.PutUint16(ss, xsum)
|
||||
case ipproto.SCTP:
|
||||
// No transport layer update required.
|
||||
}
|
||||
@@ -195,3 +219,77 @@ func updateV4Checksum(oldSum, old, new []byte) {
|
||||
hcPrime := ^uint16(cPrime)
|
||||
binary.BigEndian.PutUint16(oldSum, hcPrime)
|
||||
}
|
||||
|
||||
// checksumUpdate2ByteAlignedAddress updates an address in a calculated
|
||||
// checksum.
|
||||
//
|
||||
// The addresses must have the same length and must contain an even number
|
||||
// of bytes. The address MUST begin at a 2-byte boundary in the original buffer.
|
||||
//
|
||||
// This implementation is copied from gVisor, but updated to use [16]byte.
|
||||
func checksumUpdate2ByteAlignedAddress(xsum uint16, old, new [16]byte) uint16 {
|
||||
const uint16Bytes = 2
|
||||
|
||||
oldAddr := old[:]
|
||||
newAddr := new[:]
|
||||
|
||||
// As per RFC 1071 page 4,
|
||||
// (4) Incremental Update
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// To update the checksum, simply add the differences of the
|
||||
// sixteen bit integers that have been changed. To see why this
|
||||
// works, observe that every 16-bit integer has an additive inverse
|
||||
// and that addition is associative. From this it follows that
|
||||
// given the original value m, the new value m', and the old
|
||||
// checksum C, the new checksum C' is:
|
||||
//
|
||||
// C' = C + (-m) + m' = C + (m' - m)
|
||||
for len(oldAddr) != 0 {
|
||||
// Convert the 2 byte sequences to uint16 values then apply the increment
|
||||
// update.
|
||||
xsum = checksumUpdate2ByteAlignedUint16(xsum, (uint16(oldAddr[0])<<8)+uint16(oldAddr[1]), (uint16(newAddr[0])<<8)+uint16(newAddr[1]))
|
||||
oldAddr = oldAddr[uint16Bytes:]
|
||||
newAddr = newAddr[uint16Bytes:]
|
||||
}
|
||||
|
||||
return xsum
|
||||
}
|
||||
|
||||
// checksumUpdate2ByteAlignedUint16 updates a uint16 value in a calculated
|
||||
// checksum.
|
||||
//
|
||||
// The value MUST begin at a 2-byte boundary in the original buffer.
|
||||
//
|
||||
// This implementation is copied from gVisor.
|
||||
func checksumUpdate2ByteAlignedUint16(xsum, old, new uint16) uint16 {
|
||||
// As per RFC 1071 page 4,
|
||||
// (4) Incremental Update
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// To update the checksum, simply add the differences of the
|
||||
// sixteen bit integers that have been changed. To see why this
|
||||
// works, observe that every 16-bit integer has an additive inverse
|
||||
// and that addition is associative. From this it follows that
|
||||
// given the original value m, the new value m', and the old
|
||||
// checksum C, the new checksum C' is:
|
||||
//
|
||||
// C' = C + (-m) + m' = C + (m' - m)
|
||||
if old == new {
|
||||
return xsum
|
||||
}
|
||||
return checksumCombine(xsum, checksumCombine(new, ^old))
|
||||
}
|
||||
|
||||
// checksumCombine combines the two uint16 to form their checksum. This is done
|
||||
// by adding them and the carry.
|
||||
//
|
||||
// Note that checksum a must have been computed on an even number of bytes.
|
||||
//
|
||||
// This implementation is copied from gVisor.
|
||||
func checksumCombine(a, b uint16) uint16 {
|
||||
v := uint32(a) + uint32(b)
|
||||
return uint16(v + v>>16)
|
||||
}
|
||||
|
||||
50
vendor/tailscale.com/net/packet/geneve.go
generated
vendored
50
vendor/tailscale.com/net/packet/geneve.go
generated
vendored
@@ -24,6 +24,33 @@ const (
|
||||
GeneveProtocolWireGuard uint16 = 0x7A12
|
||||
)
|
||||
|
||||
// VirtualNetworkID is a Geneve header (RFC8926) 3-byte virtual network
|
||||
// identifier. Its methods are NOT thread-safe.
|
||||
type VirtualNetworkID struct {
|
||||
_vni uint32
|
||||
}
|
||||
|
||||
const (
|
||||
vniSetMask uint32 = 0xFF000000
|
||||
vniGetMask uint32 = ^vniSetMask
|
||||
)
|
||||
|
||||
// IsSet returns true if Set() had been called previously, otherwise false.
|
||||
func (v *VirtualNetworkID) IsSet() bool {
|
||||
return v._vni&vniSetMask != 0
|
||||
}
|
||||
|
||||
// Set sets the provided VNI. If VNI exceeds the 3-byte storage it will be
|
||||
// clamped.
|
||||
func (v *VirtualNetworkID) Set(vni uint32) {
|
||||
v._vni = vni | vniSetMask
|
||||
}
|
||||
|
||||
// Get returns the VNI value.
|
||||
func (v *VirtualNetworkID) Get() uint32 {
|
||||
return v._vni & vniGetMask
|
||||
}
|
||||
|
||||
// GeneveHeader represents the fixed size Geneve header from RFC8926.
|
||||
// TLVs/options are not implemented/supported.
|
||||
//
|
||||
@@ -51,7 +78,7 @@ type GeneveHeader struct {
|
||||
// decisions or MAY be used as a mechanism to distinguish between
|
||||
// overlapping address spaces contained in the encapsulated packet when load
|
||||
// balancing across CPUs.
|
||||
VNI uint32
|
||||
VNI VirtualNetworkID
|
||||
|
||||
// O (1 bit): Control packet. This packet contains a control message.
|
||||
// Control messages are sent between tunnel endpoints. Tunnel endpoints MUST
|
||||
@@ -65,12 +92,18 @@ type GeneveHeader struct {
|
||||
Control bool
|
||||
}
|
||||
|
||||
// Encode encodes GeneveHeader into b. If len(b) < GeneveFixedHeaderLength an
|
||||
// io.ErrShortBuffer error is returned.
|
||||
var ErrGeneveVNIUnset = errors.New("VNI is unset")
|
||||
|
||||
// Encode encodes GeneveHeader into b. If len(b) < [GeneveFixedHeaderLength] an
|
||||
// [io.ErrShortBuffer] error is returned. If !h.VNI.IsSet() then an
|
||||
// [ErrGeneveVNIUnset] error is returned.
|
||||
func (h *GeneveHeader) Encode(b []byte) error {
|
||||
if len(b) < GeneveFixedHeaderLength {
|
||||
return io.ErrShortBuffer
|
||||
}
|
||||
if !h.VNI.IsSet() {
|
||||
return ErrGeneveVNIUnset
|
||||
}
|
||||
if h.Version > 3 {
|
||||
return errors.New("version must be <= 3")
|
||||
}
|
||||
@@ -81,15 +114,12 @@ func (h *GeneveHeader) Encode(b []byte) error {
|
||||
b[1] |= 0x80
|
||||
}
|
||||
binary.BigEndian.PutUint16(b[2:], h.Protocol)
|
||||
if h.VNI > 1<<24-1 {
|
||||
return errors.New("VNI must be <= 2^24-1")
|
||||
}
|
||||
binary.BigEndian.PutUint32(b[4:], h.VNI<<8)
|
||||
binary.BigEndian.PutUint32(b[4:], h.VNI.Get()<<8)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode decodes GeneveHeader from b. If len(b) < GeneveFixedHeaderLength an
|
||||
// io.ErrShortBuffer error is returned.
|
||||
// Decode decodes GeneveHeader from b. If len(b) < [GeneveFixedHeaderLength] an
|
||||
// [io.ErrShortBuffer] error is returned.
|
||||
func (h *GeneveHeader) Decode(b []byte) error {
|
||||
if len(b) < GeneveFixedHeaderLength {
|
||||
return io.ErrShortBuffer
|
||||
@@ -99,6 +129,6 @@ func (h *GeneveHeader) Decode(b []byte) error {
|
||||
h.Control = true
|
||||
}
|
||||
h.Protocol = binary.BigEndian.Uint16(b[2:])
|
||||
h.VNI = binary.BigEndian.Uint32(b[4:]) >> 8
|
||||
h.VNI.Set(binary.BigEndian.Uint32(b[4:]) >> 8)
|
||||
return nil
|
||||
}
|
||||
|
||||
1
vendor/tailscale.com/net/packet/header.go
generated
vendored
1
vendor/tailscale.com/net/packet/header.go
generated
vendored
@@ -8,6 +8,7 @@ import (
|
||||
"math"
|
||||
)
|
||||
|
||||
const igmpHeaderLength = 8
|
||||
const tcpHeaderLength = 20
|
||||
const sctpHeaderLength = 12
|
||||
|
||||
|
||||
41
vendor/tailscale.com/net/packet/packet.go
generated
vendored
41
vendor/tailscale.com/net/packet/packet.go
generated
vendored
@@ -51,10 +51,11 @@ type Parsed struct {
|
||||
IPVersion uint8
|
||||
// IPProto is the IP subprotocol (UDP, TCP, etc.). Valid iff IPVersion != 0.
|
||||
IPProto ipproto.Proto
|
||||
// SrcIP4 is the source address. Family matches IPVersion. Port is
|
||||
// valid iff IPProto == TCP || IPProto == UDP.
|
||||
// Src is the source address. Family matches IPVersion. Port is
|
||||
// valid iff IPProto == TCP || IPProto == UDP || IPProto == SCTP.
|
||||
Src netip.AddrPort
|
||||
// DstIP4 is the destination address. Family matches IPVersion.
|
||||
// Dst is the destination address. Family matches IPVersion. Port is
|
||||
// valid iff IPProto == TCP || IPProto == UDP || IPProto == SCTP.
|
||||
Dst netip.AddrPort
|
||||
// TCPFlags is the packet's TCP flag bits. Valid iff IPProto == TCP.
|
||||
TCPFlags TCPFlag
|
||||
@@ -160,14 +161,8 @@ func (q *Parsed) decode4(b []byte) {
|
||||
|
||||
if fragOfs == 0 {
|
||||
// This is the first fragment
|
||||
if moreFrags && len(sub) < minFragBlks {
|
||||
// Suspiciously short first fragment, dump it.
|
||||
q.IPProto = unknown
|
||||
return
|
||||
}
|
||||
// otherwise, this is either non-fragmented (the usual case)
|
||||
// or a big enough initial fragment that we can read the
|
||||
// whole subprotocol header.
|
||||
// Every protocol below MUST check that it has at least one entire
|
||||
// transport header in order to protect against fragment confusion.
|
||||
switch q.IPProto {
|
||||
case ipproto.ICMPv4:
|
||||
if len(sub) < icmp4HeaderLength {
|
||||
@@ -179,6 +174,10 @@ func (q *Parsed) decode4(b []byte) {
|
||||
q.dataofs = q.subofs + icmp4HeaderLength
|
||||
return
|
||||
case ipproto.IGMP:
|
||||
if len(sub) < igmpHeaderLength {
|
||||
q.IPProto = unknown
|
||||
return
|
||||
}
|
||||
// Keep IPProto, but don't parse anything else
|
||||
// out.
|
||||
return
|
||||
@@ -211,6 +210,15 @@ func (q *Parsed) decode4(b []byte) {
|
||||
q.Dst = withPort(q.Dst, binary.BigEndian.Uint16(sub[2:4]))
|
||||
return
|
||||
case ipproto.TSMP:
|
||||
// Strictly disallow fragmented TSMP
|
||||
if moreFrags {
|
||||
q.IPProto = unknown
|
||||
return
|
||||
}
|
||||
if len(sub) < minTSMPSize {
|
||||
q.IPProto = unknown
|
||||
return
|
||||
}
|
||||
// Inter-tailscale messages.
|
||||
q.dataofs = q.subofs
|
||||
return
|
||||
@@ -223,8 +231,11 @@ func (q *Parsed) decode4(b []byte) {
|
||||
} else {
|
||||
// This is a fragment other than the first one.
|
||||
if fragOfs < minFragBlks {
|
||||
// First frag was suspiciously short, so we can't
|
||||
// trust the followup either.
|
||||
// disallow fragment offsets that are potentially inside of a
|
||||
// transport header. This is notably asymmetric with the
|
||||
// first-packet limit, that may allow a first-packet that requires a
|
||||
// shorter offset than this limit, but without state to tie this
|
||||
// to the first fragment we can not allow shorter packets.
|
||||
q.IPProto = unknown
|
||||
return
|
||||
}
|
||||
@@ -314,6 +325,10 @@ func (q *Parsed) decode6(b []byte) {
|
||||
q.Dst = withPort(q.Dst, binary.BigEndian.Uint16(sub[2:4]))
|
||||
return
|
||||
case ipproto.TSMP:
|
||||
if len(sub) < minTSMPSize {
|
||||
q.IPProto = unknown
|
||||
return
|
||||
}
|
||||
// Inter-tailscale messages.
|
||||
q.dataofs = q.subofs
|
||||
return
|
||||
|
||||
63
vendor/tailscale.com/net/packet/tsmp.go
generated
vendored
63
vendor/tailscale.com/net/packet/tsmp.go
generated
vendored
@@ -15,10 +15,13 @@ import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
"tailscale.com/net/flowtrack"
|
||||
"go4.org/mem"
|
||||
"tailscale.com/types/ipproto"
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
const minTSMPSize = 7 // the rejected body is 7 bytes
|
||||
|
||||
// TailscaleRejectedHeader is a TSMP message that says that one
|
||||
// Tailscale node has rejected the connection from another. Unlike a
|
||||
// TCP RST, this includes a reason.
|
||||
@@ -56,10 +59,6 @@ type TailscaleRejectedHeader struct {
|
||||
|
||||
const rejectFlagBitMaybeBroken = 0x1
|
||||
|
||||
func (rh TailscaleRejectedHeader) Flow() flowtrack.Tuple {
|
||||
return flowtrack.MakeTuple(rh.Proto, rh.Src, rh.Dst)
|
||||
}
|
||||
|
||||
func (rh TailscaleRejectedHeader) String() string {
|
||||
return fmt.Sprintf("TSMP-reject-flow{%s %s > %s}: %s", rh.Proto, rh.Src, rh.Dst, rh.Reason)
|
||||
}
|
||||
@@ -75,6 +74,9 @@ const (
|
||||
|
||||
// TSMPTypePong is the type byte for a TailscalePongResponse.
|
||||
TSMPTypePong TSMPType = 'o'
|
||||
|
||||
// TSPMTypeDiscoAdvertisement is the type byte for sending disco keys
|
||||
TSMPTypeDiscoAdvertisement TSMPType = 'a'
|
||||
)
|
||||
|
||||
type TailscaleRejectReason byte
|
||||
@@ -262,3 +264,54 @@ func (h TSMPPongReply) Marshal(buf []byte) error {
|
||||
binary.BigEndian.PutUint16(buf[9:11], h.PeerAPIPort)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TSMPDiscoKeyAdvertisement is a TSMP message that's used for distributing Disco Keys.
|
||||
//
|
||||
// On the wire, after the IP header, it's currently 33 bytes:
|
||||
// - 'a' (TSMPTypeDiscoAdvertisement)
|
||||
// - 32 disco key bytes
|
||||
type TSMPDiscoKeyAdvertisement struct {
|
||||
Src, Dst netip.Addr // Src and Dst are set from the parent IP Header when parsing.
|
||||
Key key.DiscoPublic
|
||||
}
|
||||
|
||||
func (ka *TSMPDiscoKeyAdvertisement) Marshal() ([]byte, error) {
|
||||
var iph Header
|
||||
if ka.Src.Is4() {
|
||||
iph = IP4Header{
|
||||
IPProto: ipproto.TSMP,
|
||||
Src: ka.Src,
|
||||
Dst: ka.Dst,
|
||||
}
|
||||
} else {
|
||||
iph = IP6Header{
|
||||
IPProto: ipproto.TSMP,
|
||||
Src: ka.Src,
|
||||
Dst: ka.Dst,
|
||||
}
|
||||
}
|
||||
payload := make([]byte, 0, 33)
|
||||
payload = append(payload, byte(TSMPTypeDiscoAdvertisement))
|
||||
payload = ka.Key.AppendTo(payload)
|
||||
if len(payload) != 33 {
|
||||
// Mostly to safeguard against ourselves changing this in the future.
|
||||
return []byte{}, fmt.Errorf("expected payload length 33, got %d", len(payload))
|
||||
}
|
||||
|
||||
return Generate(iph, payload[:]), nil
|
||||
}
|
||||
|
||||
func (pp *Parsed) AsTSMPDiscoAdvertisement() (tka TSMPDiscoKeyAdvertisement, ok bool) {
|
||||
if pp.IPProto != ipproto.TSMP {
|
||||
return
|
||||
}
|
||||
p := pp.Payload()
|
||||
if len(p) < 33 || p[0] != byte(TSMPTypeDiscoAdvertisement) {
|
||||
return
|
||||
}
|
||||
tka.Src = pp.Src.Addr()
|
||||
tka.Dst = pp.Dst.Addr()
|
||||
tka.Key = key.DiscoPublicFromRaw32(mem.B(p[1:33]))
|
||||
|
||||
return tka, true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user