diff --git a/hscontrol/mapper/batcher_concurrency_test.go b/hscontrol/mapper/batcher_concurrency_test.go index 0ced070d2..066d1ac43 100644 --- a/hscontrol/mapper/batcher_concurrency_test.go +++ b/hscontrol/mapper/batcher_concurrency_test.go @@ -273,7 +273,7 @@ func TestAddToBatchDifferential(t *testing.T) { lb.b.nodes.Store(99, nil) } - endpoint := change.EndpointOrDERPUpdate(1, &tailcfg.PeerChange{ + endpoint := change.EndpointOrDERPUpdate(&tailcfg.PeerChange{ NodeID: 1, DERPRegion: 7, Online: new(true), }) ping := change.PingNode(2, &tailcfg.PingRequest{URL: "https://example.com/one"}) diff --git a/hscontrol/servertest/mapstream_compat_test.go b/hscontrol/servertest/mapstream_compat_test.go index 39d3b2fd0..ed7d5bf36 100644 --- a/hscontrol/servertest/mapstream_compat_test.go +++ b/hscontrol/servertest/mapstream_compat_test.go @@ -59,10 +59,6 @@ var mapStreamSkipReasons = map[string]string{ "map-delivery/S3.5-remove/db1/frames": policyResponseToEveryNode, "map-delivery/S3.5-restore/db1/frames": policyResponseToEveryNode, - "map-delivery/S2.1/laptop/frames": ownReportEchoed, - "map-delivery/S2.3/laptop/frames": ownReportEchoed, - "map-delivery/S2.5/laptop/self-endpoints": ownReportEchoed, - "map-delivery/S2.5/laptop/endpoints-gap-bystander": restartEndpointsKept, "map-delivery/S2.5/laptop/endpoints-gap-svc": restartEndpointsKept, @@ -121,7 +117,6 @@ var mapStreamSkipReasons = map[string]string{ const ( policyResponseToEveryNode = "POLICY_RESPONSE_TO_EVERY_NODE: headscale sends every connected node a policy response when the policy, a node's tags or approved routes, or the set of nodes change; SaaS sends only the nodes whose peers or filter change" - ownReportEchoed = "OWN_REPORT_ECHOED: headscale sends a node its own endpoint or DERP report back; SaaS sends it nothing, so its own entry keeps the endpoints of its last full map" restartEndpointsKept = "RESTART_ENDPOINTS_KEPT: headscale keeps a restarted node's last endpoints at its peers until it reports new ones; SaaS sends peers the node without endpoints until then" reconnectAnnounced = "RECONNECT_ANNOUNCED: headscale tells peers a node is online after a stream gap of a few seconds; SaaS sends them nothing" routerReconnectRecompute = "ROUTER_RECONNECT_RECOMPUTE: headscale sends every node a policy response when a subnet router's stream comes back after a few seconds; SaaS sends nothing" diff --git a/hscontrol/state/state.go b/hscontrol/state/state.go index 82554865f..2127e2700 100644 --- a/hscontrol/state/state.go +++ b/hscontrol/state/state.go @@ -4207,7 +4207,7 @@ func buildMapRequestChangeResponse( patch.DERPRegion = currentDERP } - return change.EndpointOrDERPUpdate(id, patch) + return change.EndpointOrDERPUpdate(patch) } return change.Change{} diff --git a/hscontrol/types/change/change.go b/hscontrol/types/change/change.go index d9053b3fd..4e1561ffc 100644 --- a/hscontrol/types/change/change.go +++ b/hscontrol/types/change/change.go @@ -595,13 +595,12 @@ func KeyExpiryFor(id types.NodeID, expiry time.Time) Change { return c } -// EndpointOrDERPUpdate returns a [Change] for when a node's endpoints or DERP region changes. -// The [Change.OriginNode] field enables self-update detection by the mapper. -func EndpointOrDERPUpdate(id types.NodeID, patch *tailcfg.PeerChange) Change { - c := PeerPatched("endpoint/DERP update", patch) - c.OriginNode = id - - return c +// EndpointOrDERPUpdate returns a [Change] for when a node reports new +// endpoints or a new DERP region. It has no [Change.OriginNode]: the node +// already knows what it reported, so, as on Tailscale SaaS, only its peers +// are sent the patch. +func EndpointOrDERPUpdate(patch *tailcfg.PeerChange) Change { + return PeerPatched("endpoint/DERP update", patch) } // NodeKeyRotated returns a [Change] for a node re-logging in: its NodeKey (and