Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+50
-50
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package derp implements the Designated Encrypted Relay for Packets (DERP)
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
// including its on-wire framing overhead)
|
||||
const MaxPacketSize = 64 << 10
|
||||
|
||||
// Magic is the DERP Magic number, sent in the frameServerKey frame
|
||||
// Magic is the DERP Magic number, sent in the FrameServerKey frame
|
||||
// upon initial connection.
|
||||
const Magic = "DERP🔑" // 8 bytes: 0x44 45 52 50 f0 9f 94 91
|
||||
|
||||
@@ -58,15 +58,15 @@ Protocol flow:
|
||||
|
||||
Login:
|
||||
* client connects
|
||||
* server sends frameServerKey
|
||||
* client sends frameClientInfo
|
||||
* server sends frameServerInfo
|
||||
* server sends FrameServerKey
|
||||
* client sends FrameClientInfo
|
||||
* server sends FrameServerInfo
|
||||
|
||||
Steady state:
|
||||
* server occasionally sends frameKeepAlive (or framePing)
|
||||
* client responds to any framePing with a framePong
|
||||
* client sends frameSendPacket
|
||||
* server then sends frameRecvPacket to recipient
|
||||
* server occasionally sends FrameKeepAlive (or FramePing)
|
||||
* client responds to any FramePing with a FramePong
|
||||
* client sends FrameSendPacket
|
||||
* server then sends FrameRecvPacket to recipient
|
||||
*/
|
||||
const (
|
||||
FrameServerKey = FrameType(0x01) // 8B magic + 32B public key + (0+ bytes future use)
|
||||
@@ -78,35 +78,38 @@ const (
|
||||
FrameKeepAlive = FrameType(0x06) // no payload, no-op (to be replaced with ping/pong)
|
||||
FrameNotePreferred = FrameType(0x07) // 1 byte payload: 0x01 or 0x00 for whether this is client's home node
|
||||
|
||||
// framePeerGone is sent from server to client to signal that
|
||||
// FramePeerGone is sent from server to client to signal that
|
||||
// a previous sender is no longer connected. That is, if A
|
||||
// sent to B, and then if A disconnects, the server sends
|
||||
// framePeerGone to B so B can forget that a reverse path
|
||||
// FramePeerGone to B so B can forget that a reverse path
|
||||
// exists on that connection to get back to A. It is also sent
|
||||
// if A tries to send a CallMeMaybe to B and the server has no
|
||||
// record of B
|
||||
FramePeerGone = FrameType(0x08) // 32B pub key of peer that's gone + 1 byte reason
|
||||
|
||||
// framePeerPresent is like framePeerGone, but for other members of the DERP
|
||||
// FramePeerPresent is like FramePeerGone, but for other members of the DERP
|
||||
// region when they're meshed up together.
|
||||
//
|
||||
// The message is at least 32 bytes (the public key of the peer that's
|
||||
// connected). If there are at least 18 bytes remaining after that, it's the
|
||||
// 16 byte IP + 2 byte BE uint16 port of the client. If there's another byte
|
||||
// remaining after that, it's a PeerPresentFlags byte.
|
||||
// While current servers send 41 bytes, old servers will send fewer, and newer
|
||||
// servers might send more.
|
||||
// remaining after that, it's a PeerPresentFlags byte. If there's another
|
||||
// byte remaining after that, it's the length of the client's advertised
|
||||
// app name (see ClientInfo.AppName), followed by that many bytes of app
|
||||
// name.
|
||||
// While current servers send at least 42 bytes, old servers will send
|
||||
// fewer, and newer servers might send more.
|
||||
FramePeerPresent = FrameType(0x09)
|
||||
|
||||
// frameWatchConns is how one DERP node in a regional mesh
|
||||
// FrameWatchConns is how one DERP node in a regional mesh
|
||||
// subscribes to the others in the region.
|
||||
// There's no payload. If the sender doesn't have permission, the connection
|
||||
// is closed. Otherwise, the client is initially flooded with
|
||||
// framePeerPresent for all connected nodes, and then a stream of
|
||||
// framePeerPresent & framePeerGone has peers connect and disconnect.
|
||||
// FramePeerPresent for all connected nodes, and then a stream of
|
||||
// FramePeerPresent & FramePeerGone has peers connect and disconnect.
|
||||
FrameWatchConns = FrameType(0x10)
|
||||
|
||||
// frameClosePeer is a privileged frame type (requires the
|
||||
// FrameClosePeer is a privileged frame type (requires the
|
||||
// mesh key for now) that closes the provided peer's
|
||||
// connection. (To be used for cluster load balancing
|
||||
// purposes, when clients end up on a non-ideal node)
|
||||
@@ -115,14 +118,14 @@ const (
|
||||
FramePing = FrameType(0x12) // 8 byte ping payload, to be echoed back in framePong
|
||||
FramePong = FrameType(0x13) // 8 byte payload, the contents of the ping being replied to
|
||||
|
||||
// frameHealth is sent from server to client to tell the client
|
||||
// FrameHealth is sent from server to client to tell the client
|
||||
// if their connection is unhealthy somehow. Currently the only unhealthy state
|
||||
// is whether the connection is detected as a duplicate.
|
||||
// The entire frame body is the text of the error message. An empty message
|
||||
// clears the error state.
|
||||
FrameHealth = FrameType(0x14)
|
||||
|
||||
// frameRestarting is sent from server to client for the
|
||||
// FrameRestarting is sent from server to client for the
|
||||
// server to declare that it's restarting. Payload is two big
|
||||
// endian uint32 durations in milliseconds: when to reconnect,
|
||||
// and how long to try total. See ServerRestartingMessage docs for
|
||||
@@ -140,7 +143,7 @@ const (
|
||||
PeerGoneReasonMeshConnBroke = PeerGoneReasonType(0xf0) // invented by Client.RunWatchConnectionLoop on disconnect; not sent on the wire
|
||||
)
|
||||
|
||||
// PeerPresentFlags is an optional byte of bit flags sent after a framePeerPresent message.
|
||||
// PeerPresentFlags is an optional byte of bit flags sent after a FramePeerPresent message.
|
||||
//
|
||||
// For a modern server, the value should always be non-zero. If the value is zero,
|
||||
// that means the server doesn't support this field.
|
||||
@@ -168,6 +171,8 @@ const FastStartHeader = "Derp-Fast-Start"
|
||||
|
||||
var bin = binary.BigEndian
|
||||
|
||||
// writeUint32 writes v to bw one byte at a time
|
||||
// as a big-endian uint32.
|
||||
func writeUint32(bw *bufio.Writer, v uint32) error {
|
||||
var b [4]byte
|
||||
bin.PutUint32(b[:], v)
|
||||
@@ -183,21 +188,6 @@ func writeUint32(bw *bufio.Writer, v uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func readUint32(br *bufio.Reader) (uint32, error) {
|
||||
var b [4]byte
|
||||
// Reading a byte at a time is a bit silly,
|
||||
// but it causes b not to escape,
|
||||
// which more than pays for the silliness.
|
||||
for i := range &b {
|
||||
c, err := br.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
b[i] = c
|
||||
}
|
||||
return bin.Uint32(b[:]), nil
|
||||
}
|
||||
|
||||
// ReadFrameTypeHeader reads a frame header from br and
|
||||
// verifies that the frame type matches wantType.
|
||||
//
|
||||
@@ -213,18 +203,16 @@ func ReadFrameTypeHeader(br *bufio.Reader, wantType FrameType) (frameLen uint32,
|
||||
return frameLen, err
|
||||
}
|
||||
|
||||
// ReadFrameHeader reads the header of a DERP frame,
|
||||
// reading 5 bytes from br.
|
||||
// ReadFrameHeader reads a DERP frame header ([FrameHeaderLen] bytes) from br.
|
||||
// It uses Peek+Discard to read directly from bufio's internal buffer
|
||||
// without copying or allocating.
|
||||
func ReadFrameHeader(br *bufio.Reader) (t FrameType, frameLen uint32, err error) {
|
||||
tb, err := br.ReadByte()
|
||||
hdr, err := br.Peek(FrameHeaderLen)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
frameLen, err = readUint32(br)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return FrameType(tb), frameLen, nil
|
||||
defer br.Discard(FrameHeaderLen)
|
||||
return FrameType(hdr[0]), bin.Uint32(hdr[1:FrameHeaderLen]), nil
|
||||
}
|
||||
|
||||
// readFrame reads a frame header and then reads its payload into
|
||||
@@ -260,14 +248,26 @@ func readFrame(br *bufio.Reader, maxSize uint32, b []byte) (t FrameType, frameLe
|
||||
return t, frameLen, err
|
||||
}
|
||||
|
||||
// WriteFrameHeader writes a frame header to bw.
|
||||
//
|
||||
// The frame header is 5 bytes: a one byte frame type
|
||||
// followed by a big-endian uint32 length of the
|
||||
// remaining frame (not including the 5 byte header).
|
||||
// WriteFrameHeader writes a DERP frame header to bw: a one-byte frame
|
||||
// type followed by a big-endian uint32 frame length.
|
||||
//
|
||||
// It uses AvailableBuffer to append the header directly into bufio's
|
||||
// internal buffer without allocation, falling back to WriteByte when
|
||||
// the buffer has insufficient space.
|
||||
// It does not flush bw.
|
||||
func WriteFrameHeader(bw *bufio.Writer, t FrameType, frameLen uint32) error {
|
||||
// Fast path: enough space in the buffer to append the header
|
||||
// directly without allocation via AvailableBuffer.
|
||||
if bw.Available() >= FrameHeaderLen {
|
||||
buf := bw.AvailableBuffer()
|
||||
buf = append(buf, byte(t))
|
||||
buf = bin.AppendUint32(buf, frameLen)
|
||||
_, err := bw.Write(buf)
|
||||
return err
|
||||
}
|
||||
// Slow path: buffer nearly full. Write byte-at-a-time to let
|
||||
// bufio flush as needed, avoiding a heap allocation from append
|
||||
// growing past AvailableBuffer's capacity.
|
||||
if err := bw.WriteByte(byte(t)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user