Update
This commit is contained in:
388
vendor/tailscale.com/disco/disco.go
generated
vendored
388
vendor/tailscale.com/disco/disco.go
generated
vendored
@@ -25,6 +25,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"go4.org/mem"
|
||||
"tailscale.com/types/key"
|
||||
@@ -41,9 +42,15 @@ const NonceLen = 24
|
||||
type MessageType byte
|
||||
|
||||
const (
|
||||
TypePing = MessageType(0x01)
|
||||
TypePong = MessageType(0x02)
|
||||
TypeCallMeMaybe = MessageType(0x03)
|
||||
TypePing = MessageType(0x01)
|
||||
TypePong = MessageType(0x02)
|
||||
TypeCallMeMaybe = MessageType(0x03)
|
||||
TypeBindUDPRelayEndpoint = MessageType(0x04)
|
||||
TypeBindUDPRelayEndpointChallenge = MessageType(0x05)
|
||||
TypeBindUDPRelayEndpointAnswer = MessageType(0x06)
|
||||
TypeCallMeMaybeVia = MessageType(0x07)
|
||||
TypeAllocateUDPRelayEndpointRequest = MessageType(0x08)
|
||||
TypeAllocateUDPRelayEndpointResponse = MessageType(0x09)
|
||||
)
|
||||
|
||||
const v0 = byte(0)
|
||||
@@ -77,12 +84,25 @@ func Parse(p []byte) (Message, error) {
|
||||
}
|
||||
t, ver, p := MessageType(p[0]), p[1], p[2:]
|
||||
switch t {
|
||||
// TODO(jwhited): consider using a signature matching encoding.BinaryUnmarshaler
|
||||
case TypePing:
|
||||
return parsePing(ver, p)
|
||||
case TypePong:
|
||||
return parsePong(ver, p)
|
||||
case TypeCallMeMaybe:
|
||||
return parseCallMeMaybe(ver, p)
|
||||
case TypeBindUDPRelayEndpoint:
|
||||
return parseBindUDPRelayEndpoint(ver, p)
|
||||
case TypeBindUDPRelayEndpointChallenge:
|
||||
return parseBindUDPRelayEndpointChallenge(ver, p)
|
||||
case TypeBindUDPRelayEndpointAnswer:
|
||||
return parseBindUDPRelayEndpointAnswer(ver, p)
|
||||
case TypeCallMeMaybeVia:
|
||||
return parseCallMeMaybeVia(ver, p)
|
||||
case TypeAllocateUDPRelayEndpointRequest:
|
||||
return parseAllocateUDPRelayEndpointRequest(ver, p)
|
||||
case TypeAllocateUDPRelayEndpointResponse:
|
||||
return parseAllocateUDPRelayEndpointResponse(ver, p)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown message type 0x%02x", byte(t))
|
||||
}
|
||||
@@ -91,6 +111,7 @@ func Parse(p []byte) (Message, error) {
|
||||
// Message a discovery message.
|
||||
type Message interface {
|
||||
// AppendMarshal appends the message's marshaled representation.
|
||||
// TODO(jwhited): consider using a signature matching encoding.BinaryAppender
|
||||
AppendMarshal([]byte) []byte
|
||||
}
|
||||
|
||||
@@ -266,7 +287,368 @@ func MessageSummary(m Message) string {
|
||||
return fmt.Sprintf("pong tx=%x", m.TxID[:6])
|
||||
case *CallMeMaybe:
|
||||
return "call-me-maybe"
|
||||
case *CallMeMaybeVia:
|
||||
return "call-me-maybe-via"
|
||||
case *BindUDPRelayEndpoint:
|
||||
return "bind-udp-relay-endpoint"
|
||||
case *BindUDPRelayEndpointChallenge:
|
||||
return "bind-udp-relay-endpoint-challenge"
|
||||
case *BindUDPRelayEndpointAnswer:
|
||||
return "bind-udp-relay-endpoint-answer"
|
||||
case *AllocateUDPRelayEndpointRequest:
|
||||
return "allocate-udp-relay-endpoint-request"
|
||||
case *AllocateUDPRelayEndpointResponse:
|
||||
return "allocate-udp-relay-endpoint-response"
|
||||
default:
|
||||
return fmt.Sprintf("%#v", m)
|
||||
}
|
||||
}
|
||||
|
||||
// BindUDPRelayHandshakeState represents the state of the 3-way bind handshake
|
||||
// between UDP relay client and UDP relay server. Its potential values include
|
||||
// those for both participants, UDP relay client and UDP relay server. A UDP
|
||||
// relay server implementation can be found in net/udprelay. This is currently
|
||||
// considered experimental.
|
||||
type BindUDPRelayHandshakeState int
|
||||
|
||||
const (
|
||||
// BindUDPRelayHandshakeStateInit represents the initial state prior to any
|
||||
// message being transmitted.
|
||||
BindUDPRelayHandshakeStateInit BindUDPRelayHandshakeState = iota
|
||||
// BindUDPRelayHandshakeStateBindSent is the first client state after
|
||||
// transmitting a BindUDPRelayEndpoint message to a UDP relay server.
|
||||
BindUDPRelayHandshakeStateBindSent
|
||||
// BindUDPRelayHandshakeStateChallengeSent is the first server state after
|
||||
// receiving a BindUDPRelayEndpoint message from a UDP relay client and
|
||||
// replying with a BindUDPRelayEndpointChallenge.
|
||||
BindUDPRelayHandshakeStateChallengeSent
|
||||
// BindUDPRelayHandshakeStateAnswerSent is a client state that is entered
|
||||
// after transmitting a BindUDPRelayEndpointAnswer message towards a UDP
|
||||
// relay server in response to a BindUDPRelayEndpointChallenge message.
|
||||
BindUDPRelayHandshakeStateAnswerSent
|
||||
// BindUDPRelayHandshakeStateAnswerReceived is a server state that is
|
||||
// entered after it has received a correct BindUDPRelayEndpointAnswer
|
||||
// message from a UDP relay client in response to a
|
||||
// BindUDPRelayEndpointChallenge message.
|
||||
BindUDPRelayHandshakeStateAnswerReceived
|
||||
)
|
||||
|
||||
// bindUDPRelayEndpointCommonLen is the length of a marshalled
|
||||
// [BindUDPRelayEndpointCommon], without the message header.
|
||||
const bindUDPRelayEndpointCommonLen = 72
|
||||
|
||||
// BindUDPRelayChallengeLen is the length of the Challenge field carried in
|
||||
// [BindUDPRelayEndpointChallenge] & [BindUDPRelayEndpointAnswer] messages.
|
||||
const BindUDPRelayChallengeLen = 32
|
||||
|
||||
// BindUDPRelayEndpointCommon contains fields that are common across all 3
|
||||
// UDP relay handshake message types. All 4 field values are expected to be
|
||||
// consistent for the lifetime of a handshake besides Challenge, which is
|
||||
// irrelevant in a [BindUDPRelayEndpoint] message.
|
||||
type BindUDPRelayEndpointCommon struct {
|
||||
// VNI is the Geneve header Virtual Network Identifier field value, which
|
||||
// must match this disco-sealed value upon reception. If they are
|
||||
// non-matching it indicates the cleartext Geneve header was tampered with
|
||||
// and/or mangled.
|
||||
VNI uint32
|
||||
// Generation represents the handshake generation. Clients must set a new,
|
||||
// nonzero value at the start of every handshake.
|
||||
Generation uint32
|
||||
// RemoteKey is the disco key of the remote peer participating over this
|
||||
// relay endpoint.
|
||||
RemoteKey key.DiscoPublic
|
||||
// Challenge is set by the server in a [BindUDPRelayEndpointChallenge]
|
||||
// message, and expected to be echoed back by the client in a
|
||||
// [BindUDPRelayEndpointAnswer] message. Its value is irrelevant in a
|
||||
// [BindUDPRelayEndpoint] message, where it simply serves a padding purpose
|
||||
// ensuring all handshake messages are equal in size.
|
||||
Challenge [BindUDPRelayChallengeLen]byte
|
||||
}
|
||||
|
||||
// encode encodes m in b. b must be at least bindUDPRelayEndpointCommonLen bytes
|
||||
// long.
|
||||
func (m *BindUDPRelayEndpointCommon) encode(b []byte) {
|
||||
binary.BigEndian.PutUint32(b, m.VNI)
|
||||
b = b[4:]
|
||||
binary.BigEndian.PutUint32(b, m.Generation)
|
||||
b = b[4:]
|
||||
m.RemoteKey.AppendTo(b[:0])
|
||||
b = b[key.DiscoPublicRawLen:]
|
||||
copy(b, m.Challenge[:])
|
||||
}
|
||||
|
||||
// decode decodes m from b.
|
||||
func (m *BindUDPRelayEndpointCommon) decode(b []byte) error {
|
||||
if len(b) < bindUDPRelayEndpointCommonLen {
|
||||
return errShort
|
||||
}
|
||||
m.VNI = binary.BigEndian.Uint32(b)
|
||||
b = b[4:]
|
||||
m.Generation = binary.BigEndian.Uint32(b)
|
||||
b = b[4:]
|
||||
m.RemoteKey = key.DiscoPublicFromRaw32(mem.B(b[:key.DiscoPublicRawLen]))
|
||||
b = b[key.DiscoPublicRawLen:]
|
||||
copy(m.Challenge[:], b[:BindUDPRelayChallengeLen])
|
||||
return nil
|
||||
}
|
||||
|
||||
// BindUDPRelayEndpoint is the first messaged transmitted from UDP relay client
|
||||
// towards UDP relay server as part of the 3-way bind handshake.
|
||||
type BindUDPRelayEndpoint struct {
|
||||
BindUDPRelayEndpointCommon
|
||||
}
|
||||
|
||||
func (m *BindUDPRelayEndpoint) AppendMarshal(b []byte) []byte {
|
||||
ret, d := appendMsgHeader(b, TypeBindUDPRelayEndpoint, v0, bindUDPRelayEndpointCommonLen)
|
||||
m.BindUDPRelayEndpointCommon.encode(d)
|
||||
return ret
|
||||
}
|
||||
|
||||
func parseBindUDPRelayEndpoint(ver uint8, p []byte) (m *BindUDPRelayEndpoint, err error) {
|
||||
m = new(BindUDPRelayEndpoint)
|
||||
err = m.BindUDPRelayEndpointCommon.decode(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// BindUDPRelayEndpointChallenge is transmitted from UDP relay server towards
|
||||
// UDP relay client in response to a BindUDPRelayEndpoint message as part of the
|
||||
// 3-way bind handshake.
|
||||
type BindUDPRelayEndpointChallenge struct {
|
||||
BindUDPRelayEndpointCommon
|
||||
}
|
||||
|
||||
func (m *BindUDPRelayEndpointChallenge) AppendMarshal(b []byte) []byte {
|
||||
ret, d := appendMsgHeader(b, TypeBindUDPRelayEndpointChallenge, v0, bindUDPRelayEndpointCommonLen)
|
||||
m.BindUDPRelayEndpointCommon.encode(d)
|
||||
return ret
|
||||
}
|
||||
|
||||
func parseBindUDPRelayEndpointChallenge(ver uint8, p []byte) (m *BindUDPRelayEndpointChallenge, err error) {
|
||||
m = new(BindUDPRelayEndpointChallenge)
|
||||
err = m.BindUDPRelayEndpointCommon.decode(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// BindUDPRelayEndpointAnswer is transmitted from UDP relay client to UDP relay
|
||||
// server in response to a BindUDPRelayEndpointChallenge message.
|
||||
type BindUDPRelayEndpointAnswer struct {
|
||||
BindUDPRelayEndpointCommon
|
||||
}
|
||||
|
||||
func (m *BindUDPRelayEndpointAnswer) AppendMarshal(b []byte) []byte {
|
||||
ret, d := appendMsgHeader(b, TypeBindUDPRelayEndpointAnswer, v0, bindUDPRelayEndpointCommonLen)
|
||||
m.BindUDPRelayEndpointCommon.encode(d)
|
||||
return ret
|
||||
}
|
||||
|
||||
func parseBindUDPRelayEndpointAnswer(ver uint8, p []byte) (m *BindUDPRelayEndpointAnswer, err error) {
|
||||
m = new(BindUDPRelayEndpointAnswer)
|
||||
err = m.BindUDPRelayEndpointCommon.decode(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// AllocateUDPRelayEndpointRequest is a message sent only over DERP to request
|
||||
// allocation of a relay endpoint on a [tailscale.com/net/udprelay.Server]
|
||||
type AllocateUDPRelayEndpointRequest struct {
|
||||
// ClientDisco are the Disco public keys of the clients that should be
|
||||
// permitted to handshake with the endpoint.
|
||||
ClientDisco [2]key.DiscoPublic
|
||||
// Generation represents the allocation request generation. The server must
|
||||
// echo it back in the [AllocateUDPRelayEndpointResponse] to enable request
|
||||
// and response alignment client-side.
|
||||
Generation uint32
|
||||
}
|
||||
|
||||
// allocateUDPRelayEndpointRequestLen is the length of a marshaled
|
||||
// [AllocateUDPRelayEndpointRequest] message without the message header.
|
||||
const allocateUDPRelayEndpointRequestLen = key.DiscoPublicRawLen*2 + // ClientDisco
|
||||
4 // Generation
|
||||
|
||||
func (m *AllocateUDPRelayEndpointRequest) AppendMarshal(b []byte) []byte {
|
||||
ret, p := appendMsgHeader(b, TypeAllocateUDPRelayEndpointRequest, v0, allocateUDPRelayEndpointRequestLen)
|
||||
for i := 0; i < len(m.ClientDisco); i++ {
|
||||
disco := m.ClientDisco[i].AppendTo(nil)
|
||||
copy(p, disco)
|
||||
p = p[key.DiscoPublicRawLen:]
|
||||
}
|
||||
binary.BigEndian.PutUint32(p, m.Generation)
|
||||
return ret
|
||||
}
|
||||
|
||||
func parseAllocateUDPRelayEndpointRequest(ver uint8, p []byte) (m *AllocateUDPRelayEndpointRequest, err error) {
|
||||
m = new(AllocateUDPRelayEndpointRequest)
|
||||
if ver != 0 {
|
||||
return
|
||||
}
|
||||
if len(p) < allocateUDPRelayEndpointRequestLen {
|
||||
return m, errShort
|
||||
}
|
||||
for i := 0; i < len(m.ClientDisco); i++ {
|
||||
m.ClientDisco[i] = key.DiscoPublicFromRaw32(mem.B(p[:key.DiscoPublicRawLen]))
|
||||
p = p[key.DiscoPublicRawLen:]
|
||||
}
|
||||
m.Generation = binary.BigEndian.Uint32(p)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// AllocateUDPRelayEndpointResponse is a message sent only over DERP in response
|
||||
// to a [AllocateUDPRelayEndpointRequest].
|
||||
type AllocateUDPRelayEndpointResponse struct {
|
||||
// Generation represents the allocation request generation. The server must
|
||||
// echo back the [AllocateUDPRelayEndpointRequest.Generation] here to enable
|
||||
// request and response alignment client-side.
|
||||
Generation uint32
|
||||
UDPRelayEndpoint
|
||||
}
|
||||
|
||||
func (m *AllocateUDPRelayEndpointResponse) AppendMarshal(b []byte) []byte {
|
||||
endpointsLen := epLength * len(m.AddrPorts)
|
||||
generationLen := 4
|
||||
ret, d := appendMsgHeader(b, TypeAllocateUDPRelayEndpointResponse, v0, generationLen+udpRelayEndpointLenMinusAddrPorts+endpointsLen)
|
||||
binary.BigEndian.PutUint32(d, m.Generation)
|
||||
m.encode(d[4:])
|
||||
return ret
|
||||
}
|
||||
|
||||
func parseAllocateUDPRelayEndpointResponse(ver uint8, p []byte) (m *AllocateUDPRelayEndpointResponse, err error) {
|
||||
m = new(AllocateUDPRelayEndpointResponse)
|
||||
if ver != 0 {
|
||||
return m, nil
|
||||
}
|
||||
if len(p) < 4 {
|
||||
return m, errShort
|
||||
}
|
||||
m.Generation = binary.BigEndian.Uint32(p)
|
||||
err = m.decode(p[4:])
|
||||
return m, err
|
||||
}
|
||||
|
||||
const udpRelayEndpointLenMinusAddrPorts = key.DiscoPublicRawLen + // ServerDisco
|
||||
(key.DiscoPublicRawLen * 2) + // ClientDisco
|
||||
8 + // LamportID
|
||||
4 + // VNI
|
||||
8 + // BindLifetime
|
||||
8 // SteadyStateLifetime
|
||||
|
||||
// UDPRelayEndpoint is a mirror of [tailscale.com/net/udprelay/endpoint.ServerEndpoint],
|
||||
// refer to it for field documentation. [UDPRelayEndpoint] is carried in both
|
||||
// [CallMeMaybeVia] and [AllocateUDPRelayEndpointResponse] messages.
|
||||
type UDPRelayEndpoint struct {
|
||||
// ServerDisco is [tailscale.com/net/udprelay/endpoint.ServerEndpoint.ServerDisco]
|
||||
ServerDisco key.DiscoPublic
|
||||
// ClientDisco is [tailscale.com/net/udprelay/endpoint.ServerEndpoint.ClientDisco]
|
||||
ClientDisco [2]key.DiscoPublic
|
||||
// LamportID is [tailscale.com/net/udprelay/endpoint.ServerEndpoint.LamportID]
|
||||
LamportID uint64
|
||||
// VNI is [tailscale.com/net/udprelay/endpoint.ServerEndpoint.VNI]
|
||||
VNI uint32
|
||||
// BindLifetime is [tailscale.com/net/udprelay/endpoint.ServerEndpoint.BindLifetime]
|
||||
BindLifetime time.Duration
|
||||
// SteadyStateLifetime is [tailscale.com/net/udprelay/endpoint.ServerEndpoint.SteadyStateLifetime]
|
||||
SteadyStateLifetime time.Duration
|
||||
// AddrPorts is [tailscale.com/net/udprelay/endpoint.ServerEndpoint.AddrPorts]
|
||||
AddrPorts []netip.AddrPort
|
||||
}
|
||||
|
||||
// encode encodes m in b. b must be at least [udpRelayEndpointLenMinusAddrPorts]
|
||||
// + [epLength] * len(m.AddrPorts) bytes long.
|
||||
func (m *UDPRelayEndpoint) encode(b []byte) {
|
||||
disco := m.ServerDisco.AppendTo(nil)
|
||||
copy(b, disco)
|
||||
b = b[key.DiscoPublicRawLen:]
|
||||
for i := 0; i < len(m.ClientDisco); i++ {
|
||||
disco = m.ClientDisco[i].AppendTo(nil)
|
||||
copy(b, disco)
|
||||
b = b[key.DiscoPublicRawLen:]
|
||||
}
|
||||
binary.BigEndian.PutUint64(b[:8], m.LamportID)
|
||||
b = b[8:]
|
||||
binary.BigEndian.PutUint32(b[:4], m.VNI)
|
||||
b = b[4:]
|
||||
binary.BigEndian.PutUint64(b[:8], uint64(m.BindLifetime))
|
||||
b = b[8:]
|
||||
binary.BigEndian.PutUint64(b[:8], uint64(m.SteadyStateLifetime))
|
||||
b = b[8:]
|
||||
for _, ipp := range m.AddrPorts {
|
||||
a := ipp.Addr().As16()
|
||||
copy(b, a[:])
|
||||
binary.BigEndian.PutUint16(b[16:18], ipp.Port())
|
||||
b = b[epLength:]
|
||||
}
|
||||
}
|
||||
|
||||
// decode decodes m from b.
|
||||
func (m *UDPRelayEndpoint) decode(b []byte) error {
|
||||
if len(b) < udpRelayEndpointLenMinusAddrPorts+epLength ||
|
||||
(len(b)-udpRelayEndpointLenMinusAddrPorts)%epLength != 0 {
|
||||
return errShort
|
||||
}
|
||||
m.ServerDisco = key.DiscoPublicFromRaw32(mem.B(b[:key.DiscoPublicRawLen]))
|
||||
b = b[key.DiscoPublicRawLen:]
|
||||
for i := 0; i < len(m.ClientDisco); i++ {
|
||||
m.ClientDisco[i] = key.DiscoPublicFromRaw32(mem.B(b[:key.DiscoPublicRawLen]))
|
||||
b = b[key.DiscoPublicRawLen:]
|
||||
}
|
||||
m.LamportID = binary.BigEndian.Uint64(b[:8])
|
||||
b = b[8:]
|
||||
m.VNI = binary.BigEndian.Uint32(b[:4])
|
||||
b = b[4:]
|
||||
m.BindLifetime = time.Duration(binary.BigEndian.Uint64(b[:8]))
|
||||
b = b[8:]
|
||||
m.SteadyStateLifetime = time.Duration(binary.BigEndian.Uint64(b[:8]))
|
||||
b = b[8:]
|
||||
m.AddrPorts = make([]netip.AddrPort, 0, len(b)-udpRelayEndpointLenMinusAddrPorts/epLength)
|
||||
for len(b) > 0 {
|
||||
var a [16]byte
|
||||
copy(a[:], b)
|
||||
m.AddrPorts = append(m.AddrPorts, netip.AddrPortFrom(
|
||||
netip.AddrFrom16(a).Unmap(),
|
||||
binary.BigEndian.Uint16(b[16:18])))
|
||||
b = b[epLength:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CallMeMaybeVia is a message sent only over DERP to request that the recipient
|
||||
// try to open up a magicsock path back to the sender. The 'Via' in
|
||||
// CallMeMaybeVia highlights that candidate paths are served through an
|
||||
// intermediate relay, likely a [tailscale.com/net/udprelay.Server].
|
||||
//
|
||||
// Usage of the candidate paths in magicsock requires a 3-way handshake
|
||||
// involving [BindUDPRelayEndpoint], [BindUDPRelayEndpointChallenge], and
|
||||
// [BindUDPRelayEndpointAnswer].
|
||||
//
|
||||
// CallMeMaybeVia mirrors [tailscale.com/net/udprelay/endpoint.ServerEndpoint],
|
||||
// which contains field documentation.
|
||||
//
|
||||
// The recipient may choose to not open a path back if it's already happy with
|
||||
// its path. Direct connections, e.g. [CallMeMaybe]-signaled, take priority over
|
||||
// CallMeMaybeVia paths.
|
||||
type CallMeMaybeVia struct {
|
||||
UDPRelayEndpoint
|
||||
}
|
||||
|
||||
func (m *CallMeMaybeVia) AppendMarshal(b []byte) []byte {
|
||||
endpointsLen := epLength * len(m.AddrPorts)
|
||||
ret, p := appendMsgHeader(b, TypeCallMeMaybeVia, v0, udpRelayEndpointLenMinusAddrPorts+endpointsLen)
|
||||
m.encode(p)
|
||||
return ret
|
||||
}
|
||||
|
||||
func parseCallMeMaybeVia(ver uint8, p []byte) (m *CallMeMaybeVia, err error) {
|
||||
m = new(CallMeMaybeVia)
|
||||
if ver != 0 {
|
||||
return m, nil
|
||||
}
|
||||
err = m.decode(p)
|
||||
return m, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user