mirror of
https://github.com/tailscale/tailscale.git
synced 2025-03-28 12:02:23 +00:00
net/packet: implement Geneve header serialization (#15301)
Updates tailscale/corp#27100 Signed-off-by: Jordan Whited <jordan@tailscale.com>
This commit is contained in:
parent
f0b395d851
commit
8b1e7f646e
104
net/packet/geneve.go
Normal file
104
net/packet/geneve.go
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
// GeneveFixedHeaderLength is the length of the fixed size portion of the
|
||||
// Geneve header, in bytes.
|
||||
GeneveFixedHeaderLength = 8
|
||||
)
|
||||
|
||||
const (
|
||||
// GeneveProtocolDisco is the IEEE 802 Ethertype number used to represent
|
||||
// the Tailscale Disco protocol in a Geneve header.
|
||||
GeneveProtocolDisco uint16 = 0x7A11
|
||||
// GeneveProtocolWireGuard is the IEEE 802 Ethertype number used to represent the
|
||||
// WireGuard protocol in a Geneve header.
|
||||
GeneveProtocolWireGuard uint16 = 0x7A12
|
||||
)
|
||||
|
||||
// GeneveHeader represents the fixed size Geneve header from RFC8926.
|
||||
// TLVs/options are not implemented/supported.
|
||||
//
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |Ver| Opt Len |O|C| Rsvd. | Protocol Type |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Virtual Network Identifier (VNI) | Reserved |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type GeneveHeader struct {
|
||||
// Ver (2 bits): The current version number is 0. Packets received by a
|
||||
// tunnel endpoint with an unknown version MUST be dropped. Transit devices
|
||||
// interpreting Geneve packets with an unknown version number MUST treat
|
||||
// them as UDP packets with an unknown payload.
|
||||
Version uint8
|
||||
|
||||
// Protocol Type (16 bits): The type of protocol data unit appearing after
|
||||
// the Geneve header. This follows the Ethertype [ETYPES] convention, with
|
||||
// Ethernet itself being represented by the value 0x6558.
|
||||
Protocol uint16
|
||||
|
||||
// Virtual Network Identifier (VNI) (24 bits): An identifier for a unique
|
||||
// element of a virtual network. In many situations, this may represent an
|
||||
// L2 segment; however, the control plane defines the forwarding semantics
|
||||
// of decapsulated packets. The VNI MAY be used as part of ECMP forwarding
|
||||
// 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
|
||||
|
||||
// O (1 bit): Control packet. This packet contains a control message.
|
||||
// Control messages are sent between tunnel endpoints. Tunnel endpoints MUST
|
||||
// NOT forward the payload, and transit devices MUST NOT attempt to
|
||||
// interpret it. Since control messages are less frequent, it is RECOMMENDED
|
||||
// that tunnel endpoints direct these packets to a high-priority control
|
||||
// queue (for example, to direct the packet to a general purpose CPU from a
|
||||
// forwarding Application-Specific Integrated Circuit (ASIC) or to separate
|
||||
// out control traffic on a NIC). Transit devices MUST NOT alter forwarding
|
||||
// behavior on the basis of this bit, such as ECMP link selection.
|
||||
Control bool
|
||||
}
|
||||
|
||||
// Encode encodes GeneveHeader into b. If len(b) < GeneveFixedHeaderLength an
|
||||
// io.ErrShortBuffer error is returned.
|
||||
func (h *GeneveHeader) Encode(b []byte) error {
|
||||
if len(b) < GeneveFixedHeaderLength {
|
||||
return io.ErrShortBuffer
|
||||
}
|
||||
if h.Version > 3 {
|
||||
return errors.New("version must be <= 3")
|
||||
}
|
||||
b[0] = 0
|
||||
b[1] = 0
|
||||
b[0] |= h.Version << 6
|
||||
if h.Control {
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
h.Version = b[0] >> 6
|
||||
if b[1]&0x80 != 0 {
|
||||
h.Control = true
|
||||
}
|
||||
h.Protocol = binary.BigEndian.Uint16(b[2:])
|
||||
h.VNI = binary.BigEndian.Uint32(b[4:]) >> 8
|
||||
return nil
|
||||
}
|
32
net/packet/geneve_test.go
Normal file
32
net/packet/geneve_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package packet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestGeneveHeader(t *testing.T) {
|
||||
in := GeneveHeader{
|
||||
Version: 3,
|
||||
Protocol: GeneveProtocolDisco,
|
||||
VNI: 1<<24 - 1,
|
||||
Control: true,
|
||||
}
|
||||
b := make([]byte, GeneveFixedHeaderLength)
|
||||
err := in.Encode(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
out := GeneveHeader{}
|
||||
err = out.Decode(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if diff := cmp.Diff(out, in); diff != "" {
|
||||
t.Fatalf("wrong results (-got +want)\n%s", diff)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user