ipn/ipnlocal: evict stale node indexes when a delta upsert replaces a peer
When a node is renamed in the admin console, control sends peers a
single MapResponse delta: a PeersChanged entry carrying the full
updated node with its new Name, and no new DNSConfig (MagicDNS
records are computed client-side from peer names). That arrives as a
NodeMutationUpsert, but nodeBackend's upsert path only added the new
node's index entries and never removed the replaced node's, so
nodeByName retained the old name, and nodeByAddr, nodeByKey, and
nodeByStableID could likewise go stale if those fields changed.
Since 7e609b258 the quad-100 resolver serves MagicDNS answers on
demand from those live indexes, so a renamed peer's old name kept
resolving until something rebuilt the indexes from a full netmap,
such as toggling Tailscale off and on.
Evict the replaced node's index entries before adding the new ones.
Also consolidate the natlab DNS coverage into a single TestMagicDNS
that boots one VM and exercises extra records, search domains, and
peer add/rename/remove end to end, injecting the same MapResponse
shapes that production control sends.
Updates tailscale/corp#45631
Change-Id: I8a418317d930ec8ce112f7bd19bfd5778117a65e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
d0b4d44963
commit
c0c453334a
@@ -1100,6 +1100,22 @@ func (nb *nodeBackend) UpdateNetmapDelta(muts []netmap.NodeMutation) (res netmap
|
||||
} else {
|
||||
delete(nb.tsmpLearnedDisco, old.Key())
|
||||
}
|
||||
// Evict index entries derived from the old node value
|
||||
// before re-adding them from the new one below, so a
|
||||
// changed name, address, or key doesn't leave a stale
|
||||
// entry behind. Notably, a node rename in the admin
|
||||
// console arrives as an upsert with a new Name, and a
|
||||
// stale nodeByName entry would keep serving MagicDNS
|
||||
// answers for the old name (tailscale/corp#45631).
|
||||
for _, ipp := range old.Addresses().All() {
|
||||
if ipp.IsSingleIP() {
|
||||
delete(nb.nodeByAddr, ipp.Addr())
|
||||
}
|
||||
}
|
||||
delete(nb.nodeByKey, old.Key())
|
||||
delete(nb.nodeByWGString, old.Key().WireGuardGoString())
|
||||
delete(nb.nodeByStableID, old.StableID())
|
||||
nb.removeNodeNameLocked(old.Name())
|
||||
}
|
||||
mak.Set(&nb.peers, nid, m.Node)
|
||||
for _, ipp := range m.Node.Addresses().All() {
|
||||
|
||||
@@ -641,4 +641,18 @@ func TestNodeBackendMagicDNSHosts(t *testing.T) {
|
||||
t.Fatal("UpdateNetmapDelta not handled")
|
||||
}
|
||||
wantHost("p3.example.ts.net.", netip.MustParseAddr("100.64.0.3"))
|
||||
|
||||
// Renaming a peer arrives as an upsert of the full node with a
|
||||
// new Name. The old name must stop resolving and the new one
|
||||
// must start (tailscale/corp#45631).
|
||||
p3renamed := p3.Clone()
|
||||
p3renamed.Name = "p3-renamed.example.ts.net."
|
||||
if _, handled := nb.UpdateNetmapDelta([]netmap.NodeMutation{netmap.NodeMutationUpsert{Node: p3renamed.View()}}); !handled {
|
||||
t.Fatal("UpdateNetmapDelta not handled")
|
||||
}
|
||||
wantHost("p3-renamed.example.ts.net.", netip.MustParseAddr("100.64.0.3"))
|
||||
wantHost("p3.example.ts.net.")
|
||||
if fqdn, ok := nb.magicDNSPTR(netip.MustParseAddr("100.64.0.3")); !ok || fqdn != "p3-renamed.example.ts.net." {
|
||||
t.Errorf("magicDNSPTR(100.64.0.3) after rename = %q, %v; want p3's new name", fqdn, ok)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user