Movatterモバイル変換


[0]ホーム

URL:


derp

package
v1.92.2Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 10, 2025 License:BSD-3-ClauseImports:16Imported by:50

Details

Repository

github.com/tailscale/tailscale

Links

README

DERP

This directory (and subdirectories) contain the DERP code. The server itself isin../cmd/derper.

DERP is a packet relay system (client and servers) where peers are addressedusing WireGuard public keys instead of IP addresses.

It relays two types of packets:

  • "Disco" discovery messages (see../disco) as the a side channel duringNATtraversal.

  • Encrypted WireGuard packets as the fallback of last resort when UDP is blockedor NAT traversal fails.

DERP Map

Each client receives a "DERPMap" from the coordinationserver describing the DERP servers the client should try to use.

The client picks its home "DERP home" based on latency. This is done to keepcosts low by avoid using cloud load balancers (pricey) or anycast, which wouldnecessarily require server-side routing between DERP regions.

Clients pick their DERP home and report it to the coordination server whichshares it to all the peers in the tailnet. When a peer wants to send a packetand it doesn't already have a WireGuard session open, it sends disco messages(some direct, and some over DERP), trying to do the NAT traversal. The clientwill make connections to multiple DERP regions as needed. Only the DERP homeregion connection needs to be alive forever.

DERP Regions

Tailscale runs 1 or more DERP nodes (instances ofcmd/derper) in variousgeographic regions to make sure users have low latency to their DERP home.

Regions generally have multiple nodes per region "meshed" (routing to eachother) together for redundancy: it allows for cloud failures or upgrades withoutkicking users out to a higher latency region. Instead, clients will reconnect tothe next node in the region. Each node in the region is required to to be meshedwith every other node in the region and forward packets to the other nodes inthe region. Packets are forwarded only one hop within the region. There is norouting between regions. The assumption is that the mesh TCP connections areover a VPC that's very fast, low latency, and not charged per byte. Thecoordination server assigns the list of nodes in a region as a function of thetailnet, so all nodes within a tailnet should generally be on the same node andnot require forwarding. Only after a failure do clients of a particular tailnetget split between nodes in a region and require inter-node forwarding. But overtime it balances back out. There's also an admin-only DERP frame type to forceclose the TCP connection of a particular client to force them to reconnect totheir primary if the operator wants to force things to balance out sooner.(Using the(*derphttp.Client).ClosePeer method, as used by Tailscale'sinternal rarely-usedcmd/derpprune maintenance tool)

We generally run a minimum of three nodes in a region not for quorum reasons(there's no voting) but just because two is too uncomfortably few for cascadingfailure reasons: if you're running two nodes at 51% load (CPU, memory, etc) andthen one fails, that makes the second one fail. With three or more nodes, youcan run each node a bit hotter.

Documentation

Overview

Package derp implements the Designated Encrypted Relay for Packets (DERP)protocol.

DERP routes packets to clients using curve25519 keys as addresses.

DERP is used by Tailscale nodes to proxy encrypted WireGuardpackets through the Tailscale cloud servers when a direct pathcannot be found or opened. DERP is a last resort. Both sidesbetween very aggressive NATs, firewalls, no IPv6, etc? Well, DERP.

Index

Constants

View Source
const (NonceLen       = 24FrameHeaderLen = 1 + 4// frameType byte + 4 byte lengthKeyLen         = 32MaxInfoLen     = 1 << 20)
View Source
const (FrameServerKey     =FrameType(0x01)// 8B magic + 32B public key + (0+ bytes future use)FrameClientInfo    =FrameType(0x02)// 32B pub key + 24B nonce + naclbox(json)FrameServerInfo    =FrameType(0x03)// 24B nonce + naclbox(json)FrameSendPacket    =FrameType(0x04)// 32B dest pub key + packet bytesFrameForwardPacket =FrameType(0x0a)// 32B src pub key + 32B dst pub key + packet bytesFrameRecvPacket    =FrameType(0x05)// v0/1: packet bytes, v2: 32B src pub key + packet bytesFrameKeepAlive     =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// 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// 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 BFramePeerGone =FrameType(0x08)// 32B pub key of peer that's gone + 1 byte reason// 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.FramePeerPresent =FrameType(0x09)// 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.FrameWatchConns =FrameType(0x10)// 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)FrameClosePeer =FrameType(0x11)// 32B pub key of peer to close.FramePing =FrameType(0x12)// 8 byte ping payload, to be echoed back in framePongFramePong =FrameType(0x13)// 8 byte payload, the contents of the ping being replied to// 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// 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// more details on how the client should interpret them.FrameRestarting =FrameType(0x15))

Protocol flow:

Login:* client connects* 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

View Source
const (PeerGoneReasonDisconnected  =PeerGoneReasonType(0x00)// is only sent when a peer disconnects from this serverPeerGoneReasonNotHere       =PeerGoneReasonType(0x01)// server doesn't know about this peerPeerGoneReasonMeshConnBroke =PeerGoneReasonType(0xf0)// invented by Client.RunWatchConnectionLoop on disconnect; not sent on the wire)
View Source
const (PeerPresentIsRegular  = 1 << 0PeerPresentIsMeshPeer = 1 << 1PeerPresentIsProber   = 1 << 2PeerPresentNotIdeal   = 1 << 3// client said derp server is not its Region.Nodes[0] ideal node)

PeerPresentFlags bits.

View Source
const FastStartHeader = "Derp-Fast-Start"

FastStartHeader is the header (with value "1") that signals to the HTTPserver that the DERP HTTP client does not want the HTTP 101 responseheaders and it will begin writing & reading the DERP protocol immediatelyfollowing its HTTP request.

View Source
const IdealNodeHeader = "Ideal-Node"

IdealNodeHeader is the HTTP request header sent on DERP HTTP client requeststo indicate that they're connecting to their ideal (Region.Nodes[0]) node.The HTTP header value is the name of the node they wish they were connectedto. This is an optional header.

View Source
const KeepAlive = 60 *time.Second

KeepAlive is the minimum frequency at which the DERP server sendskeep alive frames. The server adds some jitter, so this timing is notexact, but 2x this value can be considered a missed keep alive.

View Source
const Magic = "DERP🔑"// 8 bytes: 0x44 45 52 50 f0 9f 94 91

Magic is the DERP Magic number, sent in the frameServerKey frameupon initial connection.

View Source
const MaxPacketSize = 64 << 10

MaxPacketSize is the maximum size of a packet sent over DERP.(This only includes the data bytes visible to magicsock, notincluding its on-wire framing overhead)

View Source
const ProtocolVersion = 2

ProtocolVersion is bumped whenever there's a wire-incompatible change.

  • version 1 (zero on wire): consistent box headers, in use by employee dev nodes a bit
  • version 2: received packets have src addrs in frameRecvPacket at beginning

Variables

This section is empty.

Functions

funcReadFrameTypeHeaderadded inv1.90.0

func ReadFrameTypeHeader(br *bufio.Reader, wantTypeFrameType) (frameLenuint32, errerror)

ReadFrameTypeHeader reads a frame header from br andverifies that the frame type matches wantType.

If it does, it returns the frame length (not includingthe 5 byte header) and a nil error.

If it doesn't, it returns an error and a zero length.

funcWriteFrameadded inv1.90.0

func WriteFrame(bw *bufio.Writer, tFrameType, b []byte)error

WriteFrame writes a complete frame & flushes it.

funcWriteFrameHeaderadded inv1.90.0

func WriteFrameHeader(bw *bufio.Writer, tFrameType, frameLenuint32)error

WriteFrameHeader writes a frame header to bw.

The frame header is 5 bytes: a one byte frame typefollowed by a big-endian uint32 length of theremaining frame (not including the 5 byte header).

It does not flush bw.

Types

typeClient

type Client struct {// contains filtered or unexported fields}

Client is a DERP client.

funcNewClient

func NewClient(privateKeykey.NodePrivate, ncConn, brw *bufio.ReadWriter, logflogger.Logf, opts ...ClientOpt) (*Client,error)

func (*Client)ClosePeeradded inv0.100.0

func (c *Client) ClosePeer(targetkey.NodePublic)error

ClosePeer asks the server to close target's TCP connection.It's a fatal error if the client wasn't created using MeshKey.

func (*Client)ForwardPacketadded inv0.100.0

func (c *Client) ForwardPacket(srcKey, dstKeykey.NodePublic, pkt []byte) (errerror)

func (*Client)LocalAddradded inv1.20.0

func (c *Client) LocalAddr() (netip.AddrPort,error)

LocalAddr returns the TCP connection's local address.

If the client is broken in some previously detectable way, itreturns an error.

func (*Client)NotePreferred

func (c *Client) NotePreferred(preferredbool) (errerror)

NotePreferred sends a packet that tells the server whether thisclient is the user's preferred server. This is only used in theserver for stats.

func (*Client)PublicKeyadded inv1.76.0

func (c *Client) PublicKey()key.NodePublic

func (*Client)Recv

func (c *Client) Recv() (mReceivedMessage, errerror)

Recv reads a message from the DERP server.

The returned message may alias memory owned by the Client; itshould only be accessed until the next call to Client.

Once Recv returns an error, the Client is dead forever.

func (*Client)Send

func (c *Client) Send(dstKeykey.NodePublic, pkt []byte)error

Send sends a packet to the Tailscale node identified by dstKey.

It is an error if the packet is larger than 64KB.

func (*Client)SendPingadded inv1.20.0

func (c *Client) SendPing(data [8]byte)error

func (*Client)SendPongadded inv1.6.0

func (c *Client) SendPong(data [8]byte)error

func (*Client)ServerPublicKeyadded inv0.100.0

func (c *Client) ServerPublicKey()key.NodePublic

ServerPublicKey returns the server's public key.

func (*Client)WatchConnectionChangesadded inv0.99.1

func (c *Client) WatchConnectionChanges()error

WatchConnectionChanges sends a request to subscribe to the peer's connection list.It's a fatal error if the client wasn't created using MeshKey.

typeClientInfoadded inv1.90.0

type ClientInfo struct {// MeshKey optionally specifies a pre-shared key used by// trusted clients.  It's required to subscribe to the// connection list & forward packets. It's empty for regular// users.MeshKeykey.DERPMesh `json:"meshKey,omitempty,omitzero"`// Version is the DERP protocol version that the client was built with.// See the ProtocolVersion const.Versionint `json:"version,omitempty"`// CanAckPings is whether the client declares it's able to ack// pings.CanAckPingsbool// IsProber is whether this client is a prober.IsProberbool `json:",omitempty"`}

ClientInfo is the information a DERP client sends to the serverabout itself when it connects.

func (*ClientInfo)Equaladded inv1.90.0

func (c *ClientInfo) Equal(other *ClientInfo)bool

Equal reports if two clientInfo values are equal.

typeClientOptadded inv0.100.0

type ClientOpt interface {// contains filtered or unexported methods}

ClientOpt is an option passed to NewClient.

funcCanAckPingsadded inv1.6.0

func CanAckPings(vbool)ClientOpt

CanAckPings returns a ClientOpt to set whether it advertises to theserver that it's capable of acknowledging ping requests.

funcIsProberadded inv1.12.0

func IsProber(vbool)ClientOpt

IsProber returns a ClientOpt to pass to the DERP server during connect todeclare that this client is a a prober.

funcMeshKeyadded inv0.100.0

func MeshKey(kkey.DERPMesh)ClientOpt

MeshKey returns a ClientOpt to pass to the DERP server during connect to getaccess to join the mesh.

An empty key means to not use a mesh key.

funcServerPublicKeyadded inv1.2.0

func ServerPublicKey(keykey.NodePublic)ClientOpt

ServerPublicKey returns a ClientOpt to declare that the server's DERP public key is known.If key is the zero value, the returned ClientOpt is a no-op.

typeConn

type Conn interface {io.WriteCloserLocalAddr()net.Addr// The *Deadline methods follow the semantics of net.Conn.SetDeadline(time.Time)errorSetReadDeadline(time.Time)errorSetWriteDeadline(time.Time)error}

Conn is the subset of the underlying net.Conn the DERP Server needs.It is a defined type so that non-net connections can be used.

typeFrameTypeadded inv1.90.0

type FrameTypebyte

FrameType is the one byte frame type at the beginning of the frameheader. The second field is a big-endian uint32 describing thelength of the remaining frame (not including the initial 5 bytes).

funcReadFrameHeaderadded inv1.90.0

func ReadFrameHeader(br *bufio.Reader) (tFrameType, frameLenuint32, errerror)

ReadFrameHeader reads the header of a DERP frame,reading 5 bytes from br.

typeHealthMessageadded inv1.16.0

type HealthMessage struct {// Problem, if non-empty, is a description of why the connection// is unhealthy.//// The empty string means the connection is healthy again.//// The default condition is healthy, so the server doesn't// broadcast a HealthMessage until a problem exists.Problemstring}

HealthMessage is a one-way message from server to client, declaring theconnection health state.

typeKeepAliveMessageadded inv1.6.0

type KeepAliveMessage struct{}

KeepAliveMessage is a one-way empty message from server to client, just tokeep the connection alive. It's like a PingMessage, but doesn't solicita reply from the client.

typePeerGoneMessageadded inv0.98.0

type PeerGoneMessage struct {Peerkey.NodePublicReasonPeerGoneReasonType}

PeerGoneMessage is a ReceivedMessage that indicates that the clientidentified by the underlying public key is not connected to thisserver.

It has only historically been sent by the server when the clientconnection count decremented from 1 to 0 and not from e.g. 2 to 1.Seehttps://github.com/tailscale/tailscale/issues/13566 for details.

typePeerGoneReasonTypeadded inv1.40.0

type PeerGoneReasonTypebyte

PeerGoneReasonType is a one byte reason code explaining why aserver does not have a path to the requested destination.

typePeerPresentFlagsadded inv1.70.0

type PeerPresentFlagsbyte

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.

typePeerPresentMessageadded inv0.99.1

type PeerPresentMessage struct {// Key is the public key of the client.Keykey.NodePublic// IPPort is the remote IP and port of the client.IPPortnetip.AddrPort// Flags is a bitmask of info about the client.FlagsPeerPresentFlags}

PeerPresentMessage is a ReceivedMessage that indicates that the client isconnected to the server. (Only used by trusted mesh clients)

It will be sent to client watchers for every new connection from a client,even if the client's already connected with that public key.Seehttps://github.com/tailscale/tailscale/issues/13566 for PeerPresentMessageand PeerGoneMessage not being 1:1.

typePingMessageadded inv1.6.0

type PingMessage [8]byte

PingMessage is a request from a client or server to reply to theother side with a PongMessage with the given payload.

typePongMessageadded inv1.20.0

type PongMessage [8]byte

PongMessage is a reply to a PingMessage from a client or serverwith the payload sent previously in a PingMessage.

typeReceivedMessage

type ReceivedMessage interface {// contains filtered or unexported methods}

ReceivedMessage represents a type returned by Client.Recv. Unlessotherwise documented, the returned message aliases the byte sliceprovided to Recv and thus the message is only as good as thatbuffer, which is up to the caller.

typeReceivedPacket

type ReceivedPacket struct {Sourcekey.NodePublic// Data is the received packet bytes. It aliases the memory// passed to Client.Recv.Data []byte}

ReceivedPacket is a ReceivedMessage representing an incoming packet.

typeServerInfoadded inv1.90.0

type ServerInfo struct {Versionint `json:"version,omitempty"`TokenBucketBytesPerSecondint `json:",omitempty"`TokenBucketBytesBurstint `json:",omitempty"`}

ServerInfo is the message sent from the server to clients duringthe connection setup.

typeServerInfoMessageadded inv1.2.0

type ServerInfoMessage struct {// TokenBucketBytesPerSecond is how many bytes per second the// server says it will accept, including all framing bytes.//// Zero means unspecified. There might be a limit, but the// client need not try to respect it.TokenBucketBytesPerSecondint// TokenBucketBytesBurst is how many bytes the server will// allow to burst, temporarily violating// TokenBucketBytesPerSecond.//// Zero means unspecified. There might be a limit, but the// client need not try to respect it.TokenBucketBytesBurstint}

ServerInfoMessage is sent by the server upon first connect.

typeServerRestartingMessageadded inv1.16.0

type ServerRestartingMessage struct {// ReconnectIn is an advisory duration that the client should wait// before attempting to reconnect. It might be zero.// It exists for the server to smear out the reconnects.ReconnectIntime.Duration// TryFor is an advisory duration for how long the client// should attempt to reconnect before giving up and proceeding// with its normal connection failure logic. The interval// between retries is undefined for now.// A server should not send a TryFor duration more than a few// seconds.TryFortime.Duration}

ServerRestartingMessage is a one-way message from server to client,advertising that the server is restarting.

Source Files

View all Source files

Directories

PathSynopsis
Package derpconst contains constants used by the DERP client and server.
Package derpconst contains constants used by the DERP client and server.
Package derphttp implements DERP-over-HTTP.
Package derphttp implements DERP-over-HTTP.
Package derpserver implements a DERP server.
Package derpserver implements a DERP server.
Package xdp contains the XDP STUN program.
Package xdp contains the XDP STUN program.
headerscommand
The update program fetches the libbpf headers from the libbpf GitHub repository and writes them to disk.
The update program fetches the libbpf headers from the libbpf GitHub repository and writes them to disk.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f orF : Jump to
y orY : Canonical URL
go.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.Learn more.

[8]ページ先頭

©2009-2025 Movatter.jp