mirror of
https://github.com/slackhq/nebula.git
synced 2026-11-15 16:06:52 +01:00
122 lines
3.7 KiB
Go
122 lines
3.7 KiB
Go
// Package fragment carries handshake messages that do not fit in one UDP
|
|
// packet. A message is Reed-Solomon coded into equally sized symbols (see
|
|
// handshake/fec) and each symbol travels in its own packet:
|
|
//
|
|
// Nebula header (16) | Fragment header (8) | [cookie (16)] | symbol
|
|
//
|
|
// The fragment header is FragID u32 | MsgLen u16 | Index u8 | Flags u8, all big
|
|
// endian. Both ends derive the symbol layout from MsgLen, so any MsgLen-derived
|
|
// value never has to be sent. FlagCookie marks a cookie a responder under load
|
|
// asked for; room for it is always reserved, so a fragment never outgrows
|
|
// MaxPacketLen.
|
|
package fragment
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
|
|
"github.com/slackhq/nebula/handshake/fec"
|
|
"github.com/slackhq/nebula/header"
|
|
)
|
|
|
|
const (
|
|
// HeaderLen is the size of the fragment header that follows the Nebula header.
|
|
HeaderLen = 8
|
|
|
|
// CookieLen is the size of the cookie a fragment carries when FlagCookie is set.
|
|
CookieLen = 16
|
|
|
|
// FlagCookie marks a fragment that carries a cookie after its header.
|
|
FlagCookie uint8 = 0x01
|
|
|
|
// MaxPacketLen bounds every fragment packet, Nebula header included. A relayed
|
|
// fragment gains a 16 byte header and a 16 byte tag, which lands exactly on
|
|
// the 1232 byte UDP payload available on an IPv6 minimum MTU path.
|
|
MaxPacketLen = 1200
|
|
|
|
// MaxSymbolSize is the largest symbol that fits in MaxPacketLen alongside a cookie.
|
|
MaxSymbolSize = MaxPacketLen - header.Len - HeaderLen - CookieLen
|
|
)
|
|
|
|
var (
|
|
ErrShortFragment = errors.New("fragment is too short")
|
|
ErrFragID = errors.New("fragment has a zero FragID")
|
|
ErrMsgLen = errors.New("fragment has a zero MsgLen")
|
|
ErrFlags = errors.New("fragment has reserved flags set")
|
|
ErrNoCookie = errors.New("fragment is too short for its cookie")
|
|
ErrSymbolSize = errors.New("fragment symbol size does not match its MsgLen")
|
|
)
|
|
|
|
// Cookie proves a sender received a responder's reply at its address.
|
|
type Cookie [CookieLen]byte
|
|
|
|
// Header is the fragment header.
|
|
type Header struct {
|
|
FragID uint32 // the sender's local index for this handshake, never zero
|
|
MsgLen uint16 // length of the whole message
|
|
Index uint8 // symbol index, data below K and repair from K
|
|
Flags uint8 // FlagCookie, other bits reserved and zero
|
|
Cookie Cookie // valid when Flags has FlagCookie
|
|
}
|
|
|
|
func (h Header) HasCookie() bool {
|
|
return h.Flags&FlagCookie != 0
|
|
}
|
|
|
|
// Encode appends the header, and its cookie if it has one, to b.
|
|
func (h Header) Encode(b []byte) []byte {
|
|
b = binary.BigEndian.AppendUint32(b, h.FragID)
|
|
b = binary.BigEndian.AppendUint16(b, h.MsgLen)
|
|
b = append(b, h.Index, h.Flags)
|
|
if h.HasCookie() {
|
|
b = append(b, h.Cookie[:]...)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Layout returns the symbol layout the header's MsgLen implies.
|
|
func (h Header) Layout() (fec.Layout, error) {
|
|
return fec.NewLayout(int(h.MsgLen), MaxSymbolSize)
|
|
}
|
|
|
|
// Parse validates the bytes following the Nebula header and splits them into
|
|
// the fragment header and the symbol. The symbol aliases b.
|
|
func Parse(b []byte) (Header, []byte, error) {
|
|
if len(b) < HeaderLen {
|
|
return Header{}, nil, ErrShortFragment
|
|
}
|
|
h := Header{
|
|
FragID: binary.BigEndian.Uint32(b[0:4]),
|
|
MsgLen: binary.BigEndian.Uint16(b[4:6]),
|
|
Index: b[6],
|
|
Flags: b[7],
|
|
}
|
|
if h.FragID == 0 {
|
|
return Header{}, nil, ErrFragID
|
|
}
|
|
if h.MsgLen == 0 {
|
|
return Header{}, nil, ErrMsgLen
|
|
}
|
|
if h.Flags&^FlagCookie != 0 {
|
|
return Header{}, nil, ErrFlags
|
|
}
|
|
b = b[HeaderLen:]
|
|
if h.HasCookie() {
|
|
if len(b) < CookieLen {
|
|
return Header{}, nil, ErrNoCookie
|
|
}
|
|
copy(h.Cookie[:], b)
|
|
b = b[CookieLen:]
|
|
}
|
|
|
|
l, err := h.Layout()
|
|
if err != nil {
|
|
return Header{}, nil, err
|
|
}
|
|
sym := b
|
|
if len(sym) != l.SymbolSize {
|
|
return Header{}, nil, ErrSymbolSize
|
|
}
|
|
return h, sym, nil
|
|
}
|