all: add Node.HomeDERP int, phase out "127.3.3.40:$region" hack [capver 111]

This deprecates the old "DERP string" packing a DERP region ID into an
IP:port of 127.3.3.40:$REGION_ID and just uses an integer, like
PeerChange.DERPRegion does.

We still support servers sending the old form; they're converted to
the new form internally right when they're read off the network.

Updates #14636

Change-Id: I9427ec071f02a2c6d75ccb0fcbf0ecff9f19f26f
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2025-01-14 10:19:52 -08:00
committed by Brad Fitzpatrick
parent 66269dc934
commit 2fc4455e6d
19 changed files with 171 additions and 97 deletions
+18 -7
View File
@@ -153,7 +153,8 @@ type CapabilityVersion int
// - 108: 2024-11-08: Client sends ServicesHash in Hostinfo, understands c2n GET /vip-services.
// - 109: 2024-11-18: Client supports filtertype.Match.SrcCaps (issue #12542)
// - 110: 2024-12-12: removed never-before-used Tailscale SSH public key support (#14373)
const CurrentCapabilityVersion CapabilityVersion = 110
// - 111: 2025-01-14: Client supports a peer having Node.HomeDERP (issue #14636)
const CurrentCapabilityVersion CapabilityVersion = 111
// ID is an integer ID for a user, node, or login allocated by the
// control plane.
@@ -346,15 +347,24 @@ type Node struct {
AllowedIPs []netip.Prefix // range of IP addresses to route to this node
Endpoints []netip.AddrPort `json:",omitempty"` // IP+port (public via STUN, and local LANs)
// DERP is this node's home DERP region ID integer, but shoved into an
// LegacyDERPString is this node's home LegacyDERPString region ID integer, but shoved into an
// IP:port string for legacy reasons. The IP address is always "127.3.3.40"
// (a loopback address (127) followed by the digits over the letters DERP on
// a QWERTY keyboard (3.3.40)). The "port number" is the home DERP region ID
// a QWERTY keyboard (3.3.40)). The "port number" is the home LegacyDERPString region ID
// integer.
//
// TODO(bradfitz): simplify this legacy mess; add a new HomeDERPRegionID int
// field behind a new capver bump.
DERP string `json:",omitempty"` // DERP-in-IP:port ("127.3.3.40:N") endpoint
// Deprecated: HomeDERP has replaced this, but old servers might still send
// this field. See tailscale/tailscale#14636. Do not use this field in code
// other than in the upgradeNode func, which canonicalizes it to HomeDERP
// if it arrives as a LegacyDERPString string on the wire.
LegacyDERPString string `json:"DERP,omitempty"` // DERP-in-IP:port ("127.3.3.40:N") endpoint
// HomeDERP is the modern version of the DERP string field, with just an
// integer. The client advertises support for this as of capver 111.
//
// HomeDERP may be zero if not (yet) known, but ideally always be non-zero
// for magicsock connectivity to function normally.
HomeDERP int `json:",omitempty"` // DERP region ID of the node's home DERP
Hostinfo HostinfoView
Created time.Time
@@ -2162,7 +2172,8 @@ func (n *Node) Equal(n2 *Node) bool {
slicesx.EqualSameNil(n.AllowedIPs, n2.AllowedIPs) &&
slicesx.EqualSameNil(n.PrimaryRoutes, n2.PrimaryRoutes) &&
slicesx.EqualSameNil(n.Endpoints, n2.Endpoints) &&
n.DERP == n2.DERP &&
n.LegacyDERPString == n2.LegacyDERPString &&
n.HomeDERP == n2.HomeDERP &&
n.Cap == n2.Cap &&
n.Hostinfo.Equal(n2.Hostinfo) &&
n.Created.Equal(n2.Created) &&
+2 -1
View File
@@ -99,7 +99,8 @@ var _NodeCloneNeedsRegeneration = Node(struct {
Addresses []netip.Prefix
AllowedIPs []netip.Prefix
Endpoints []netip.AddrPort
DERP string
LegacyDERPString string
HomeDERP int
Hostinfo HostinfoView
Created time.Time
Cap CapabilityVersion
+8 -3
View File
@@ -367,7 +367,7 @@ func TestNodeEqual(t *testing.T) {
nodeHandles := []string{
"ID", "StableID", "Name", "User", "Sharer",
"Key", "KeyExpiry", "KeySignature", "Machine", "DiscoKey",
"Addresses", "AllowedIPs", "Endpoints", "DERP", "Hostinfo",
"Addresses", "AllowedIPs", "Endpoints", "LegacyDERPString", "HomeDERP", "Hostinfo",
"Created", "Cap", "Tags", "PrimaryRoutes",
"LastSeen", "Online", "MachineAuthorized",
"Capabilities", "CapMap",
@@ -530,8 +530,13 @@ func TestNodeEqual(t *testing.T) {
true,
},
{
&Node{DERP: "foo"},
&Node{DERP: "bar"},
&Node{LegacyDERPString: "foo"},
&Node{LegacyDERPString: "bar"},
false,
},
{
&Node{HomeDERP: 1},
&Node{HomeDERP: 2},
false,
},
{
+4 -2
View File
@@ -139,7 +139,8 @@ func (v NodeView) DiscoKey() key.DiscoPublic { return v.ж.DiscoK
func (v NodeView) Addresses() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.Addresses) }
func (v NodeView) AllowedIPs() views.Slice[netip.Prefix] { return views.SliceOf(v.ж.AllowedIPs) }
func (v NodeView) Endpoints() views.Slice[netip.AddrPort] { return views.SliceOf(v.ж.Endpoints) }
func (v NodeView) DERP() string { return v.ж.DERP }
func (v NodeView) LegacyDERPString() string { return v.ж.LegacyDERPString }
func (v NodeView) HomeDERP() int { return v.ж.HomeDERP }
func (v NodeView) Hostinfo() HostinfoView { return v.ж.Hostinfo }
func (v NodeView) Created() time.Time { return v.ж.Created }
func (v NodeView) Cap() CapabilityVersion { return v.ж.Cap }
@@ -192,7 +193,8 @@ var _NodeViewNeedsRegeneration = Node(struct {
Addresses []netip.Prefix
AllowedIPs []netip.Prefix
Endpoints []netip.AddrPort
DERP string
LegacyDERPString string
HomeDERP int
Hostinfo HostinfoView
Created time.Time
Cap CapabilityVersion