mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-29 12:11:02 +00:00
disco: implement CallMeMaybeVia serialization (#15779)
This message type is currently unused and considered experimental. Updates tailscale/corp#27502 Signed-off-by: Jordan Whited <jordan@tailscale.com>
This commit is contained in:
parent
dbf13976d3
commit
dae2319e11
@ -25,6 +25,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"time"
|
||||||
|
|
||||||
"go4.org/mem"
|
"go4.org/mem"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
@ -47,6 +48,7 @@ const (
|
|||||||
TypeBindUDPRelayEndpoint = MessageType(0x04)
|
TypeBindUDPRelayEndpoint = MessageType(0x04)
|
||||||
TypeBindUDPRelayEndpointChallenge = MessageType(0x05)
|
TypeBindUDPRelayEndpointChallenge = MessageType(0x05)
|
||||||
TypeBindUDPRelayEndpointAnswer = MessageType(0x06)
|
TypeBindUDPRelayEndpointAnswer = MessageType(0x06)
|
||||||
|
TypeCallMeMaybeVia = MessageType(0x07)
|
||||||
)
|
)
|
||||||
|
|
||||||
const v0 = byte(0)
|
const v0 = byte(0)
|
||||||
@ -93,6 +95,8 @@ func Parse(p []byte) (Message, error) {
|
|||||||
return parseBindUDPRelayEndpointChallenge(ver, p)
|
return parseBindUDPRelayEndpointChallenge(ver, p)
|
||||||
case TypeBindUDPRelayEndpointAnswer:
|
case TypeBindUDPRelayEndpointAnswer:
|
||||||
return parseBindUDPRelayEndpointAnswer(ver, p)
|
return parseBindUDPRelayEndpointAnswer(ver, p)
|
||||||
|
case TypeCallMeMaybeVia:
|
||||||
|
return parseCallMeMaybeVia(ver, p)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown message type 0x%02x", byte(t))
|
return nil, fmt.Errorf("unknown message type 0x%02x", byte(t))
|
||||||
}
|
}
|
||||||
@ -392,3 +396,94 @@ func parseBindUDPRelayEndpointAnswer(ver uint8, p []byte) (m *BindUDPRelayEndpoi
|
|||||||
copy(m.Answer[:], p[:])
|
copy(m.Answer[:], p[:])
|
||||||
return m, nil
|
return m, 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.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.
|
||||||
|
//
|
||||||
|
// This message type is currently considered experimental and is not yet tied to
|
||||||
|
// a [tailscale.com/tailcfg.CapabilityVersion].
|
||||||
|
type CallMeMaybeVia struct {
|
||||||
|
// ServerDisco is [tailscale.com/net/udprelay.ServerEndpoint.ServerDisco]
|
||||||
|
ServerDisco key.DiscoPublic
|
||||||
|
// LamportID is [tailscale.com/net/udprelay.ServerEndpoint.LamportID]
|
||||||
|
LamportID uint64
|
||||||
|
// VNI is [tailscale.com/net/udprelay.ServerEndpoint.VNI]
|
||||||
|
VNI uint32
|
||||||
|
// BindLifetime is [tailscale.com/net/udprelay.ServerEndpoint.BindLifetime]
|
||||||
|
BindLifetime time.Duration
|
||||||
|
// SteadyStateLifetime is [tailscale.com/net/udprelay.ServerEndpoint.SteadyStateLifetime]
|
||||||
|
SteadyStateLifetime time.Duration
|
||||||
|
// AddrPorts is [tailscale.com/net/udprelay.ServerEndpoint.AddrPorts]
|
||||||
|
AddrPorts []netip.AddrPort
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmmvDataLenMinusEndpoints = key.DiscoPublicRawLen + // ServerDisco
|
||||||
|
8 + // LamportID
|
||||||
|
4 + // VNI
|
||||||
|
8 + // BindLifetime
|
||||||
|
8 // SteadyStateLifetime
|
||||||
|
|
||||||
|
func (m *CallMeMaybeVia) AppendMarshal(b []byte) []byte {
|
||||||
|
endpointsLen := epLength * len(m.AddrPorts)
|
||||||
|
ret, p := appendMsgHeader(b, TypeCallMeMaybeVia, v0, cmmvDataLenMinusEndpoints+endpointsLen)
|
||||||
|
disco := m.ServerDisco.AppendTo(nil)
|
||||||
|
copy(p, disco)
|
||||||
|
p = p[key.DiscoPublicRawLen:]
|
||||||
|
binary.BigEndian.PutUint64(p[:8], m.LamportID)
|
||||||
|
p = p[8:]
|
||||||
|
binary.BigEndian.PutUint32(p[:4], m.VNI)
|
||||||
|
p = p[4:]
|
||||||
|
binary.BigEndian.PutUint64(p[:8], uint64(m.BindLifetime))
|
||||||
|
p = p[8:]
|
||||||
|
binary.BigEndian.PutUint64(p[:8], uint64(m.SteadyStateLifetime))
|
||||||
|
p = p[8:]
|
||||||
|
for _, ipp := range m.AddrPorts {
|
||||||
|
a := ipp.Addr().As16()
|
||||||
|
copy(p, a[:])
|
||||||
|
binary.BigEndian.PutUint16(p[16:18], ipp.Port())
|
||||||
|
p = p[epLength:]
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCallMeMaybeVia(ver uint8, p []byte) (m *CallMeMaybeVia, err error) {
|
||||||
|
m = new(CallMeMaybeVia)
|
||||||
|
if len(p) < cmmvDataLenMinusEndpoints+epLength ||
|
||||||
|
(len(p)-cmmvDataLenMinusEndpoints)%epLength != 0 ||
|
||||||
|
ver != 0 {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
m.ServerDisco = key.DiscoPublicFromRaw32(mem.B(p[:key.DiscoPublicRawLen]))
|
||||||
|
p = p[key.DiscoPublicRawLen:]
|
||||||
|
m.LamportID = binary.BigEndian.Uint64(p[:8])
|
||||||
|
p = p[8:]
|
||||||
|
m.VNI = binary.BigEndian.Uint32(p[:4])
|
||||||
|
p = p[4:]
|
||||||
|
m.BindLifetime = time.Duration(binary.BigEndian.Uint64(p[:8]))
|
||||||
|
p = p[8:]
|
||||||
|
m.SteadyStateLifetime = time.Duration(binary.BigEndian.Uint64(p[:8]))
|
||||||
|
p = p[8:]
|
||||||
|
m.AddrPorts = make([]netip.AddrPort, 0, len(p)-cmmvDataLenMinusEndpoints/epLength)
|
||||||
|
for len(p) > 0 {
|
||||||
|
var a [16]byte
|
||||||
|
copy(a[:], p)
|
||||||
|
m.AddrPorts = append(m.AddrPorts, netip.AddrPortFrom(
|
||||||
|
netip.AddrFrom16(a).Unmap(),
|
||||||
|
binary.BigEndian.Uint16(p[16:18])))
|
||||||
|
p = p[epLength:]
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"go4.org/mem"
|
"go4.org/mem"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
@ -106,6 +107,21 @@ func TestMarshalAndParse(t *testing.T) {
|
|||||||
},
|
},
|
||||||
want: "06 00 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f",
|
want: "06 00 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "call_me_maybe_via",
|
||||||
|
m: &CallMeMaybeVia{
|
||||||
|
ServerDisco: key.DiscoPublicFromRaw32(mem.B([]byte{1: 1, 2: 2, 30: 30, 31: 31})),
|
||||||
|
LamportID: 123,
|
||||||
|
VNI: 456,
|
||||||
|
BindLifetime: time.Second,
|
||||||
|
SteadyStateLifetime: time.Minute,
|
||||||
|
AddrPorts: []netip.AddrPort{
|
||||||
|
netip.MustParseAddrPort("1.2.3.4:567"),
|
||||||
|
netip.MustParseAddrPort("[2001::3456]:789"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: "07 00 00 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1e 1f 00 00 00 00 00 00 00 7b 00 00 01 c8 00 00 00 00 3b 9a ca 00 00 00 00 0d f8 47 58 00 00 00 00 00 00 00 00 00 00 00 ff ff 01 02 03 04 02 37 20 01 00 00 00 00 00 00 00 00 00 00 00 00 34 56 03 15",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user