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

View File

@ -280,7 +280,7 @@ func TestConnMemoryOverhead(t *testing.T) {
growthTotal := int64(ms.HeapAlloc) - int64(ms0.HeapAlloc)
growthEach := float64(growthTotal) / float64(num)
t.Logf("Alloced %v bytes, %.2f B/each", growthTotal, growthEach)
const max = 2000
const max = 2048
if growthEach > max {
t.Errorf("allocated more than expected; want max %v bytes/each", max)
}

View File

@ -7,7 +7,6 @@ import (
"cmp"
"context"
"encoding/json"
"fmt"
"maps"
"net"
"reflect"
@ -166,6 +165,7 @@ func (ms *mapSession) HandleNonKeepAliveMapResponse(ctx context.Context, resp *t
// For responses that mutate the self node, check for updated nodeAttrs.
if resp.Node != nil {
upgradeNode(resp.Node)
if DevKnob.StripCaps() {
resp.Node.Capabilities = nil
resp.Node.CapMap = nil
@ -181,6 +181,13 @@ func (ms *mapSession) HandleNonKeepAliveMapResponse(ctx context.Context, resp *t
ms.controlKnobs.UpdateFromNodeAttributes(resp.Node.CapMap)
}
for _, p := range resp.Peers {
upgradeNode(p)
}
for _, p := range resp.PeersChanged {
upgradeNode(p)
}
// Call Node.InitDisplayNames on any changed nodes.
initDisplayNames(cmp.Or(resp.Node.View(), ms.lastNode), resp)
@ -216,6 +223,26 @@ func (ms *mapSession) HandleNonKeepAliveMapResponse(ctx context.Context, resp *t
return nil
}
// upgradeNode upgrades Node fields from the server into the modern forms
// not using deprecated fields.
func upgradeNode(n *tailcfg.Node) {
if n == nil {
return
}
if n.LegacyDERPString != "" {
if n.HomeDERP == 0 {
ip, portStr, err := net.SplitHostPort(n.LegacyDERPString)
if ip == tailcfg.DerpMagicIP && err == nil {
port, err := strconv.Atoi(portStr)
if err == nil {
n.HomeDERP = port
}
}
}
n.LegacyDERPString = ""
}
}
func (ms *mapSession) tryHandleIncrementally(res *tailcfg.MapResponse) bool {
if ms.controlKnobs != nil && ms.controlKnobs.DisableDeltaUpdates.Load() {
return false
@ -443,7 +470,7 @@ func (ms *mapSession) updatePeersStateFromResponse(resp *tailcfg.MapResponse) (s
stats.changed++
mut := vp.AsStruct()
if pc.DERPRegion != 0 {
mut.DERP = fmt.Sprintf("%s:%v", tailcfg.DerpMagicIP, pc.DERPRegion)
mut.HomeDERP = pc.DERPRegion
patchDERPRegion.Add(1)
}
if pc.Cap != 0 {
@ -631,17 +658,13 @@ func peerChangeDiff(was tailcfg.NodeView, n *tailcfg.Node) (_ *tailcfg.PeerChang
if !views.SliceEqual(was.Endpoints(), views.SliceOf(n.Endpoints)) {
pc().Endpoints = slices.Clone(n.Endpoints)
}
case "DERP":
if was.DERP() != n.DERP {
ip, portStr, err := net.SplitHostPort(n.DERP)
if err != nil || ip != "127.3.3.40" {
return nil, false
}
port, err := strconv.Atoi(portStr)
if err != nil || port < 1 || port > 65535 {
return nil, false
}
pc().DERPRegion = port
case "LegacyDERPString":
if was.LegacyDERPString() != "" || n.LegacyDERPString != "" {
panic("unexpected; caller should've already called upgradeNode")
}
case "HomeDERP":
if was.HomeDERP() != n.HomeDERP {
pc().DERPRegion = n.HomeDERP
}
case "Hostinfo":
if !was.Hostinfo().Valid() && !n.Hostinfo.Valid() {

View File

@ -50,9 +50,9 @@ func TestUpdatePeersStateFromResponse(t *testing.T) {
n.LastSeen = &t
}
}
withDERP := func(d string) func(*tailcfg.Node) {
withDERP := func(regionID int) func(*tailcfg.Node) {
return func(n *tailcfg.Node) {
n.DERP = d
n.HomeDERP = regionID
}
}
withEP := func(ep string) func(*tailcfg.Node) {
@ -189,14 +189,14 @@ func TestUpdatePeersStateFromResponse(t *testing.T) {
},
{
name: "ep_change_derp",
prev: peers(n(1, "foo", withDERP("127.3.3.40:3"))),
prev: peers(n(1, "foo", withDERP(3))),
mapRes: &tailcfg.MapResponse{
PeersChangedPatch: []*tailcfg.PeerChange{{
NodeID: 1,
DERPRegion: 4,
}},
},
want: peers(n(1, "foo", withDERP("127.3.3.40:4"))),
want: peers(n(1, "foo", withDERP(4))),
wantStats: updateStats{changed: 1},
},
{
@ -213,19 +213,19 @@ func TestUpdatePeersStateFromResponse(t *testing.T) {
},
{
name: "ep_change_udp_2",
prev: peers(n(1, "foo", withDERP("127.3.3.40:3"), withEP("1.2.3.4:111"))),
prev: peers(n(1, "foo", withDERP(3), withEP("1.2.3.4:111"))),
mapRes: &tailcfg.MapResponse{
PeersChangedPatch: []*tailcfg.PeerChange{{
NodeID: 1,
Endpoints: eps("1.2.3.4:56"),
}},
},
want: peers(n(1, "foo", withDERP("127.3.3.40:3"), withEP("1.2.3.4:56"))),
want: peers(n(1, "foo", withDERP(3), withEP("1.2.3.4:56"))),
wantStats: updateStats{changed: 1},
},
{
name: "ep_change_both",
prev: peers(n(1, "foo", withDERP("127.3.3.40:3"), withEP("1.2.3.4:111"))),
prev: peers(n(1, "foo", withDERP(3), withEP("1.2.3.4:111"))),
mapRes: &tailcfg.MapResponse{
PeersChangedPatch: []*tailcfg.PeerChange{{
NodeID: 1,
@ -233,7 +233,7 @@ func TestUpdatePeersStateFromResponse(t *testing.T) {
Endpoints: eps("1.2.3.4:56"),
}},
},
want: peers(n(1, "foo", withDERP("127.3.3.40:2"), withEP("1.2.3.4:56"))),
want: peers(n(1, "foo", withDERP(2), withEP("1.2.3.4:56"))),
wantStats: updateStats{changed: 1},
},
{
@ -744,8 +744,8 @@ func TestPeerChangeDiff(t *testing.T) {
},
{
name: "patch-derp",
a: &tailcfg.Node{ID: 1, DERP: "127.3.3.40:1"},
b: &tailcfg.Node{ID: 1, DERP: "127.3.3.40:2"},
a: &tailcfg.Node{ID: 1, HomeDERP: 1},
b: &tailcfg.Node{ID: 1, HomeDERP: 2},
want: &tailcfg.PeerChange{NodeID: 1, DERPRegion: 2},
},
{
@ -929,23 +929,23 @@ func TestPatchifyPeersChanged(t *testing.T) {
mr0: &tailcfg.MapResponse{
Node: &tailcfg.Node{Name: "foo.bar.ts.net."},
Peers: []*tailcfg.Node{
{ID: 1, DERP: "127.3.3.40:1", Hostinfo: hi},
{ID: 2, DERP: "127.3.3.40:2", Hostinfo: hi},
{ID: 3, DERP: "127.3.3.40:3", Hostinfo: hi},
{ID: 1, HomeDERP: 1, Hostinfo: hi},
{ID: 2, HomeDERP: 2, Hostinfo: hi},
{ID: 3, HomeDERP: 3, Hostinfo: hi},
},
},
mr1: &tailcfg.MapResponse{
PeersChanged: []*tailcfg.Node{
{ID: 1, DERP: "127.3.3.40:11", Hostinfo: hi},
{ID: 1, HomeDERP: 11, Hostinfo: hi},
{ID: 2, StableID: "other-change", Hostinfo: hi},
{ID: 3, DERP: "127.3.3.40:33", Hostinfo: hi},
{ID: 4, DERP: "127.3.3.40:4", Hostinfo: hi},
{ID: 3, HomeDERP: 33, Hostinfo: hi},
{ID: 4, HomeDERP: 4, Hostinfo: hi},
},
},
want: &tailcfg.MapResponse{
PeersChanged: []*tailcfg.Node{
{ID: 2, StableID: "other-change", Hostinfo: hi},
{ID: 4, DERP: "127.3.3.40:4", Hostinfo: hi},
{ID: 4, HomeDERP: 4, Hostinfo: hi},
},
PeersChangedPatch: []*tailcfg.PeerChange{
{NodeID: 1, DERPRegion: 11},
@ -1006,6 +1006,53 @@ func TestPatchifyPeersChanged(t *testing.T) {
}
}
func TestUpgradeNode(t *testing.T) {
tests := []struct {
name string
in *tailcfg.Node
want *tailcfg.Node
}{
{
name: "nil",
in: nil,
want: nil,
},
{
name: "empty",
in: new(tailcfg.Node),
want: new(tailcfg.Node),
},
{
name: "derp-both",
in: &tailcfg.Node{HomeDERP: 1, LegacyDERPString: tailcfg.DerpMagicIP + ":2"},
want: &tailcfg.Node{HomeDERP: 1},
},
{
name: "derp-str-only",
in: &tailcfg.Node{LegacyDERPString: tailcfg.DerpMagicIP + ":2"},
want: &tailcfg.Node{HomeDERP: 2},
},
{
name: "derp-int-only",
in: &tailcfg.Node{HomeDERP: 2},
want: &tailcfg.Node{HomeDERP: 2},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var got *tailcfg.Node
if tt.in != nil {
got = ptr.To(*tt.in) // shallow clone
}
upgradeNode(got)
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Errorf("wrong result (-want +got):\n%s", diff)
}
})
}
}
func BenchmarkMapSessionDelta(b *testing.B) {
for _, size := range []int{10, 100, 1_000, 10_000} {
b.Run(fmt.Sprintf("size_%d", size), func(b *testing.B) {
@ -1022,7 +1069,7 @@ func BenchmarkMapSessionDelta(b *testing.B) {
res.Peers = append(res.Peers, &tailcfg.Node{
ID: tailcfg.NodeID(i + 2),
Name: fmt.Sprintf("peer%d.bar.ts.net.", i),
DERP: "127.3.3.40:10",
HomeDERP: 10,
Addresses: []netip.Prefix{netip.MustParsePrefix("100.100.2.3/32"), netip.MustParsePrefix("fd7a:115c:a1e0::123/128")},
AllowedIPs: []netip.Prefix{netip.MustParsePrefix("100.100.2.3/32"), netip.MustParsePrefix("fd7a:115c:a1e0::123/128")},
Endpoints: eps("192.168.1.2:345", "192.168.1.3:678"),

View File

@ -116,7 +116,7 @@ func (em *expiryManager) flagExpiredPeers(netmap *netmap.NetworkMap, localNow ti
// since we discover endpoints via DERP, and due to DERP return
// path optimization.
mut.Endpoints = nil
mut.DERP = ""
mut.HomeDERP = 0
// Defense-in-depth: break the node's public key as well, in
// case something tries to communicate.

View File

@ -7381,15 +7381,7 @@ func suggestExitNode(report *netcheck.Report, netMap *netmap.NetworkMap, prevSug
}
distances := make([]nodeDistance, 0, len(candidates))
for _, c := range candidates {
if c.DERP() != "" {
ipp, err := netip.ParseAddrPort(c.DERP())
if err != nil {
continue
}
if ipp.Addr() != tailcfg.DerpMagicIPAddr {
continue
}
regionID := int(ipp.Port())
if regionID := c.HomeDERP(); regionID != 0 {
candidatesByRegion[regionID] = append(candidatesByRegion[regionID], c)
continue
}

View File

@ -1007,8 +1007,8 @@ func TestUpdateNetmapDelta(t *testing.T) {
wants := []*tailcfg.Node{
{
ID: 1,
DERP: "127.3.3.40:1",
ID: 1,
HomeDERP: 1,
},
{
ID: 2,
@ -2021,7 +2021,7 @@ func TestAutoExitNodeSetNetInfoCallback(t *testing.T) {
netip.MustParsePrefix("100.64.1.1/32"),
netip.MustParsePrefix("fe70::1/128"),
},
DERP: "127.3.3.40:2",
HomeDERP: 2,
}
defaultDERPMap := &tailcfg.DERPMap{
Regions: map[int]*tailcfg.DERPRegion{
@ -2985,7 +2985,7 @@ func makePeer(id tailcfg.NodeID, opts ...peerOptFunc) tailcfg.NodeView {
ID: id,
StableID: tailcfg.StableNodeID(fmt.Sprintf("stable%d", id)),
Name: fmt.Sprintf("peer%d", id),
DERP: fmt.Sprintf("127.3.3.40:%d", id),
HomeDERP: int(id),
}
for _, opt := range opts {
opt(node)
@ -3001,13 +3001,13 @@ func withName(name string) peerOptFunc {
func withDERP(region int) peerOptFunc {
return func(n *tailcfg.Node) {
n.DERP = fmt.Sprintf("127.3.3.40:%d", region)
n.HomeDERP = region
}
}
func withoutDERP() peerOptFunc {
return func(n *tailcfg.Node) {
n.DERP = ""
n.HomeDERP = 0
}
}

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) &&

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

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,
},
{

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

View File

@ -805,7 +805,7 @@ func (s *Server) serveMap(w http.ResponseWriter, r *http.Request, mkey key.Machi
node.Hostinfo = req.Hostinfo.View()
if ni := node.Hostinfo.NetInfo(); ni.Valid() {
if ni.PreferredDERP() != 0 {
node.DERP = fmt.Sprintf("127.3.3.40:%d", ni.PreferredDERP())
node.HomeDERP = ni.PreferredDERP()
}
}
}

View File

@ -287,11 +287,8 @@ func printPeerConcise(buf *strings.Builder, p tailcfg.NodeView) {
epStrs[i] = fmt.Sprintf("%21v", e+strings.Repeat(" ", spaces))
}
derp := p.DERP()
const derpPrefix = "127.3.3.40:"
if strings.HasPrefix(derp, derpPrefix) {
derp = "D" + derp[len(derpPrefix):]
}
derp := fmt.Sprintf("D%d", p.HomeDERP())
var discoShort string
if !p.DiscoKey().IsZero() {
discoShort = p.DiscoKey().ShortString() + " "
@ -311,7 +308,7 @@ func printPeerConcise(buf *strings.Builder, p tailcfg.NodeView) {
// nodeConciseEqual reports whether a and b are equal for the fields accessed by printPeerConcise.
func nodeConciseEqual(a, b tailcfg.NodeView) bool {
return a.Key() == b.Key() &&
a.DERP() == b.DERP() &&
a.HomeDERP() == b.HomeDERP() &&
a.DiscoKey() == b.DiscoKey() &&
views.SliceEqual(a.AllowedIPs(), b.AllowedIPs()) &&
views.SliceEqual(a.Endpoints(), b.Endpoints())

View File

@ -63,12 +63,12 @@ func TestNetworkMapConcise(t *testing.T) {
Peers: nodeViews([]*tailcfg.Node{
{
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
{
Key: testNodeKey(3),
DERP: "127.3.3.40:4",
HomeDERP: 4,
Endpoints: eps("10.2.0.100:12", "10.1.0.100:12345"),
},
}),
@ -102,7 +102,7 @@ func TestConciseDiffFrom(t *testing.T) {
Peers: nodeViews([]*tailcfg.Node{
{
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
}),
@ -112,7 +112,7 @@ func TestConciseDiffFrom(t *testing.T) {
Peers: nodeViews([]*tailcfg.Node{
{
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
}),
@ -126,7 +126,7 @@ func TestConciseDiffFrom(t *testing.T) {
Peers: nodeViews([]*tailcfg.Node{
{
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
}),
@ -136,7 +136,7 @@ func TestConciseDiffFrom(t *testing.T) {
Peers: nodeViews([]*tailcfg.Node{
{
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
}),
@ -151,7 +151,7 @@ func TestConciseDiffFrom(t *testing.T) {
{
ID: 2,
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
}),
@ -162,19 +162,19 @@ func TestConciseDiffFrom(t *testing.T) {
{
ID: 1,
Key: testNodeKey(1),
DERP: "127.3.3.40:1",
HomeDERP: 1,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
{
ID: 2,
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
{
ID: 3,
Key: testNodeKey(3),
DERP: "127.3.3.40:3",
HomeDERP: 3,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
}),
@ -189,19 +189,19 @@ func TestConciseDiffFrom(t *testing.T) {
{
ID: 1,
Key: testNodeKey(1),
DERP: "127.3.3.40:1",
HomeDERP: 1,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
{
ID: 2,
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
{
ID: 3,
Key: testNodeKey(3),
DERP: "127.3.3.40:3",
HomeDERP: 3,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
}),
@ -212,7 +212,7 @@ func TestConciseDiffFrom(t *testing.T) {
{
ID: 2,
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:12", "192.168.0.100:12354"),
},
}),
@ -227,7 +227,7 @@ func TestConciseDiffFrom(t *testing.T) {
{
ID: 2,
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:12", "1.1.1.1:1"),
},
}),
@ -238,7 +238,7 @@ func TestConciseDiffFrom(t *testing.T) {
{
ID: 2,
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:12", "1.1.1.1:2"),
},
}),
@ -253,7 +253,7 @@ func TestConciseDiffFrom(t *testing.T) {
{
ID: 2,
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:41641", "1.1.1.1:41641"),
DiscoKey: testDiscoKey("f00f00f00f"),
AllowedIPs: []netip.Prefix{netip.PrefixFrom(netaddr.IPv4(100, 102, 103, 104), 32)},
@ -266,7 +266,7 @@ func TestConciseDiffFrom(t *testing.T) {
{
ID: 2,
Key: testNodeKey(2),
DERP: "127.3.3.40:2",
HomeDERP: 2,
Endpoints: eps("192.168.0.100:41641", "1.1.1.1:41641"),
DiscoKey: testDiscoKey("ba4ba4ba4b"),
AllowedIPs: []netip.Prefix{netip.PrefixFrom(netaddr.IPv4(100, 102, 103, 104), 32)},

View File

@ -5,7 +5,6 @@ package netmap
import (
"cmp"
"fmt"
"net/netip"
"reflect"
"slices"
@ -35,7 +34,7 @@ type NodeMutationDERPHome struct {
}
func (m NodeMutationDERPHome) Apply(n *tailcfg.Node) {
n.DERP = fmt.Sprintf("127.3.3.40:%v", m.DERPRegion)
n.HomeDERP = m.DERPRegion
}
// NodeMutation is a NodeMutation that says a node's endpoints have changed.

View File

@ -1359,7 +1359,7 @@ func (de *endpoint) updateFromNode(n tailcfg.NodeView, heartbeatDisabled bool, p
})
de.resetLocked()
}
if n.DERP() == "" {
if n.HomeDERP() == 0 {
if de.derpAddr.IsValid() {
de.debugUpdates.Add(EndpointChange{
When: time.Now(),
@ -1369,7 +1369,7 @@ func (de *endpoint) updateFromNode(n tailcfg.NodeView, heartbeatDisabled bool, p
}
de.derpAddr = netip.AddrPort{}
} else {
newDerp, _ := netip.ParseAddrPort(n.DERP())
newDerp := netip.AddrPortFrom(tailcfg.DerpMagicIPAddr, uint16(n.HomeDERP()))
if de.derpAddr != newDerp {
de.debugUpdates.Add(EndpointChange{
When: time.Now(),

View File

@ -2337,10 +2337,7 @@ func devPanicf(format string, a ...any) {
func (c *Conn) logEndpointCreated(n tailcfg.NodeView) {
c.logf("magicsock: created endpoint key=%s: disco=%s; %v", n.Key().ShortString(), n.DiscoKey().ShortString(), logger.ArgWriter(func(w *bufio.Writer) {
const derpPrefix = "127.3.3.40:"
if strings.HasPrefix(n.DERP(), derpPrefix) {
ipp, _ := netip.ParseAddrPort(n.DERP())
regionID := int(ipp.Port())
if regionID := n.HomeDERP(); regionID != 0 {
code := c.derpRegionCodeLocked(regionID)
if code != "" {
code = "(" + code + ")"

View File

@ -314,7 +314,7 @@ func meshStacks(logf logger.Logf, mutateNetmap func(idx int, nm *netmap.NetworkM
Addresses: addrs,
AllowedIPs: addrs,
Endpoints: epFromTyped(eps[i]),
DERP: "127.3.3.40:1",
HomeDERP: 1,
}
nm.Peers = append(nm.Peers, peer.View())
}

View File

@ -198,7 +198,7 @@ func (e *userspaceEngine) onOpenTimeout(flow flowtrack.Tuple) {
e.logf("open-conn-track: timeout opening %v; peer node %v running pre-0.100", flow, n.Key().ShortString())
return
}
if n.DERP() == "" {
if n.HomeDERP() == 0 {
e.logf("open-conn-track: timeout opening %v; peer node %v not connected to any DERP relay", flow, n.Key().ShortString())
return
}

View File

@ -85,7 +85,7 @@ func WGCfg(nm *netmap.NetworkMap, logf logger.Logf, flags netmap.WGConfigFlags,
skippedSubnets := new(bytes.Buffer)
for _, peer := range nm.Peers {
if peer.DiscoKey().IsZero() && peer.DERP() == "" && !peer.IsWireGuardOnly() {
if peer.DiscoKey().IsZero() && peer.HomeDERP() == 0 && !peer.IsWireGuardOnly() {
// Peer predates both DERP and active discovery, we cannot
// communicate with it.
logf("[v1] wgcfg: skipped peer %s, doesn't offer DERP or disco", peer.Key().ShortString())