map.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  1. // Copyright (c) Tailscale Inc & contributors
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package controlclient
  4. import (
  5. "cmp"
  6. "context"
  7. "crypto/sha256"
  8. "encoding/hex"
  9. "encoding/json"
  10. "io"
  11. "maps"
  12. "net"
  13. "reflect"
  14. "runtime"
  15. "runtime/debug"
  16. "slices"
  17. "strconv"
  18. "sync"
  19. "time"
  20. "tailscale.com/control/controlknobs"
  21. "tailscale.com/envknob"
  22. "tailscale.com/hostinfo"
  23. "tailscale.com/tailcfg"
  24. "tailscale.com/tstime"
  25. "tailscale.com/types/key"
  26. "tailscale.com/types/logger"
  27. "tailscale.com/types/netmap"
  28. "tailscale.com/types/ptr"
  29. "tailscale.com/types/views"
  30. "tailscale.com/util/clientmetric"
  31. "tailscale.com/util/mak"
  32. "tailscale.com/util/set"
  33. "tailscale.com/util/slicesx"
  34. "tailscale.com/wgengine/filter"
  35. )
  36. // mapSession holds the state over a long-polled "map" request to the
  37. // control plane.
  38. //
  39. // It accepts incremental tailcfg.MapResponse values to
  40. // netMapForResponse and returns fully inflated NetworkMaps, filling
  41. // in the omitted data implicit from prior MapResponse values from
  42. // within the same session (the same long-poll HTTP response to the
  43. // one MapRequest).
  44. type mapSession struct {
  45. // Immutable fields.
  46. netmapUpdater NetmapUpdater // called on changes (in addition to the optional hooks below)
  47. controlKnobs *controlknobs.Knobs // or nil
  48. privateNodeKey key.NodePrivate
  49. publicNodeKey key.NodePublic
  50. logf logger.Logf
  51. vlogf logger.Logf
  52. machinePubKey key.MachinePublic
  53. altClock tstime.Clock // if nil, regular time is used
  54. cancel context.CancelFunc // always non-nil, shuts down caller's base long poll context
  55. keepAliveZ []byte // if non-nil, the learned zstd encoding of the just-KeepAlive message for this session
  56. ztdDecodesForTest int // for testing
  57. // sessionAliveCtx is a Background-based context that's alive for the
  58. // duration of the mapSession that we own the lifetime of. It's closed by
  59. // sessionAliveCtxClose.
  60. sessionAliveCtx context.Context
  61. sessionAliveCtxClose context.CancelFunc // closes sessionAliveCtx
  62. // Optional hooks, guaranteed non-nil (set to no-op funcs) by the
  63. // newMapSession constructor. They must be overridden if desired
  64. // before the mapSession is used.
  65. // onDebug specifies what to do with a *tailcfg.Debug message.
  66. onDebug func(context.Context, *tailcfg.Debug) error
  67. // onSelfNodeChanged is called before the NetmapUpdater if the self node was
  68. // changed.
  69. onSelfNodeChanged func(*netmap.NetworkMap)
  70. // Fields storing state over the course of multiple MapResponses.
  71. lastPrintMap time.Time
  72. lastNode tailcfg.NodeView
  73. lastCapSet set.Set[tailcfg.NodeCapability]
  74. peers map[tailcfg.NodeID]tailcfg.NodeView
  75. lastDNSConfig *tailcfg.DNSConfig
  76. lastDERPMap *tailcfg.DERPMap
  77. lastUserProfile map[tailcfg.UserID]tailcfg.UserProfileView
  78. lastPacketFilterRules views.Slice[tailcfg.FilterRule] // concatenation of all namedPacketFilters
  79. namedPacketFilters map[string]views.Slice[tailcfg.FilterRule]
  80. lastParsedPacketFilter []filter.Match
  81. lastSSHPolicy *tailcfg.SSHPolicy
  82. collectServices bool
  83. lastDomain string
  84. lastDomainAuditLogID string
  85. lastHealth []string
  86. lastDisplayMessages map[tailcfg.DisplayMessageID]tailcfg.DisplayMessage
  87. lastPopBrowserURL string
  88. lastTKAInfo *tailcfg.TKAInfo
  89. lastNetmapSummary string // from NetworkMap.VeryConcise
  90. }
  91. // newMapSession returns a mostly unconfigured new mapSession.
  92. //
  93. // Modify its optional fields on the returned value before use.
  94. //
  95. // It must have its Close method called to release resources.
  96. func newMapSession(privateNodeKey key.NodePrivate, nu NetmapUpdater, controlKnobs *controlknobs.Knobs) *mapSession {
  97. ms := &mapSession{
  98. netmapUpdater: nu,
  99. controlKnobs: controlKnobs,
  100. privateNodeKey: privateNodeKey,
  101. publicNodeKey: privateNodeKey.Public(),
  102. lastDNSConfig: new(tailcfg.DNSConfig),
  103. lastUserProfile: map[tailcfg.UserID]tailcfg.UserProfileView{},
  104. // Non-nil no-op defaults, to be optionally overridden by the caller.
  105. logf: logger.Discard,
  106. vlogf: logger.Discard,
  107. cancel: func() {},
  108. onDebug: func(context.Context, *tailcfg.Debug) error { return nil },
  109. onSelfNodeChanged: func(*netmap.NetworkMap) {},
  110. }
  111. ms.sessionAliveCtx, ms.sessionAliveCtxClose = context.WithCancel(context.Background())
  112. return ms
  113. }
  114. // occasionallyPrintSummary logs summary at most once very 5 minutes. The
  115. // summary is the Netmap.VeryConcise result from the last received map response.
  116. func (ms *mapSession) occasionallyPrintSummary(summary string) {
  117. // Occasionally print the netmap header.
  118. // This is handy for debugging, and our logs processing
  119. // pipeline depends on it. (TODO: Remove this dependency.)
  120. now := ms.clock().Now()
  121. if now.Sub(ms.lastPrintMap) < 5*time.Minute {
  122. return
  123. }
  124. ms.lastPrintMap = now
  125. ms.logf("[v1] new network map (periodic):\n%s", summary)
  126. }
  127. func (ms *mapSession) clock() tstime.Clock {
  128. return cmp.Or[tstime.Clock](ms.altClock, tstime.StdClock{})
  129. }
  130. func (ms *mapSession) Close() {
  131. ms.sessionAliveCtxClose()
  132. }
  133. // HandleNonKeepAliveMapResponse handles a non-KeepAlive MapResponse (full or
  134. // incremental).
  135. //
  136. // All fields that are valid on a KeepAlive MapResponse have already been
  137. // handled.
  138. //
  139. // TODO(bradfitz): make this handle all fields later. For now (2023-08-20) this
  140. // is [re]factoring progress enough.
  141. func (ms *mapSession) HandleNonKeepAliveMapResponse(ctx context.Context, resp *tailcfg.MapResponse) error {
  142. if debug := resp.Debug; debug != nil {
  143. if err := ms.onDebug(ctx, debug); err != nil {
  144. return err
  145. }
  146. }
  147. if DevKnob.StripEndpoints() {
  148. for _, p := range resp.Peers {
  149. p.Endpoints = nil
  150. }
  151. for _, p := range resp.PeersChanged {
  152. p.Endpoints = nil
  153. }
  154. }
  155. // For responses that mutate the self node, check for updated nodeAttrs.
  156. if resp.Node != nil {
  157. upgradeNode(resp.Node)
  158. if DevKnob.StripCaps() {
  159. resp.Node.Capabilities = nil
  160. resp.Node.CapMap = nil
  161. }
  162. // If the server is old and is still sending us Capabilities instead of
  163. // CapMap, convert it to CapMap early so the rest of the client code can
  164. // work only in terms of CapMap.
  165. for _, c := range resp.Node.Capabilities {
  166. if _, ok := resp.Node.CapMap[c]; !ok {
  167. mak.Set(&resp.Node.CapMap, c, nil)
  168. }
  169. }
  170. ms.controlKnobs.UpdateFromNodeAttributes(resp.Node.CapMap)
  171. }
  172. for _, p := range resp.Peers {
  173. upgradeNode(p)
  174. }
  175. for _, p := range resp.PeersChanged {
  176. upgradeNode(p)
  177. }
  178. // Call Node.InitDisplayNames on any changed nodes.
  179. initDisplayNames(cmp.Or(resp.Node.View(), ms.lastNode), resp)
  180. ms.patchifyPeersChanged(resp)
  181. ms.updateStateFromResponse(resp)
  182. if ms.tryHandleIncrementally(resp) {
  183. ms.occasionallyPrintSummary(ms.lastNetmapSummary)
  184. return nil
  185. }
  186. // We have to rebuild the whole netmap (lots of garbage & work downstream of
  187. // our UpdateFullNetmap call). This is the part we tried to avoid but
  188. // some field mutations (especially rare ones) aren't yet handled.
  189. if runtime.GOOS == "ios" {
  190. // Memory is tight on iOS. Free what we can while we
  191. // can before this potential burst of in-use memory.
  192. debug.FreeOSMemory()
  193. }
  194. nm := ms.netmap()
  195. ms.lastNetmapSummary = nm.VeryConcise()
  196. ms.occasionallyPrintSummary(ms.lastNetmapSummary)
  197. // If the self node changed, we might need to update persist.
  198. if resp.Node != nil {
  199. ms.onSelfNodeChanged(nm)
  200. }
  201. ms.netmapUpdater.UpdateFullNetmap(nm)
  202. return nil
  203. }
  204. // upgradeNode upgrades Node fields from the server into the modern forms
  205. // not using deprecated fields.
  206. func upgradeNode(n *tailcfg.Node) {
  207. if n == nil {
  208. return
  209. }
  210. if n.LegacyDERPString != "" {
  211. if n.HomeDERP == 0 {
  212. ip, portStr, err := net.SplitHostPort(n.LegacyDERPString)
  213. if ip == tailcfg.DerpMagicIP && err == nil {
  214. port, err := strconv.Atoi(portStr)
  215. if err == nil {
  216. n.HomeDERP = port
  217. }
  218. }
  219. }
  220. n.LegacyDERPString = ""
  221. }
  222. if DevKnob.StripHomeDERP() {
  223. n.HomeDERP = 0
  224. }
  225. if n.AllowedIPs == nil {
  226. n.AllowedIPs = slices.Clone(n.Addresses)
  227. }
  228. }
  229. func (ms *mapSession) tryHandleIncrementally(res *tailcfg.MapResponse) bool {
  230. if ms.controlKnobs != nil && ms.controlKnobs.DisableDeltaUpdates.Load() {
  231. return false
  232. }
  233. nud, ok := ms.netmapUpdater.(NetmapDeltaUpdater)
  234. if !ok {
  235. return false
  236. }
  237. mutations, ok := netmap.MutationsFromMapResponse(res, time.Now())
  238. if ok && len(mutations) > 0 {
  239. return nud.UpdateNetmapDelta(mutations)
  240. }
  241. return ok
  242. }
  243. // updateStats are some stats from updateStateFromResponse, primarily for
  244. // testing. It's meant to be cheap enough to always compute, though. It doesn't
  245. // allocate.
  246. type updateStats struct {
  247. allNew bool
  248. added int
  249. removed int
  250. changed int
  251. }
  252. // updateStateFromResponse updates ms from res. It takes ownership of res.
  253. func (ms *mapSession) updateStateFromResponse(resp *tailcfg.MapResponse) {
  254. ms.updatePeersStateFromResponse(resp)
  255. if resp.Node != nil {
  256. ms.lastNode = resp.Node.View()
  257. capSet := set.Set[tailcfg.NodeCapability]{}
  258. for _, c := range resp.Node.Capabilities {
  259. capSet.Add(c)
  260. }
  261. for c := range resp.Node.CapMap {
  262. capSet.Add(c)
  263. }
  264. ms.lastCapSet = capSet
  265. }
  266. for _, up := range resp.UserProfiles {
  267. ms.lastUserProfile[up.ID] = up.View()
  268. }
  269. // TODO(bradfitz): clean up old user profiles? maybe not worth it.
  270. if dm := resp.DERPMap; dm != nil {
  271. ms.vlogf("netmap: new map contains DERP map")
  272. // Guard against the control server accidentally sending
  273. // a nil region definition, which at least Headscale was
  274. // observed to send.
  275. for rid, r := range dm.Regions {
  276. if r == nil {
  277. delete(dm.Regions, rid)
  278. }
  279. }
  280. // In the copy/v86 wasm environment with limited networking, if the
  281. // control plane didn't pick our DERP home for us, do it ourselves and
  282. // mark all but the lowest region as NoMeasureNoHome. For prod, this
  283. // will be Region 1, NYC, a compromise between the US and Europe. But
  284. // really the control plane should pick this. This is only a fallback.
  285. if hostinfo.IsInVM86() {
  286. numCanMeasure := 0
  287. lowest := 0
  288. for rid, r := range dm.Regions {
  289. if !r.NoMeasureNoHome {
  290. numCanMeasure++
  291. if lowest == 0 || rid < lowest {
  292. lowest = rid
  293. }
  294. }
  295. }
  296. if numCanMeasure > 1 {
  297. for rid, r := range dm.Regions {
  298. if rid != lowest {
  299. r.NoMeasureNoHome = true
  300. }
  301. }
  302. }
  303. }
  304. // Zero-valued fields in a DERPMap mean that we're not changing
  305. // anything and are using the previous value(s).
  306. if ldm := ms.lastDERPMap; ldm != nil {
  307. if dm.Regions == nil {
  308. dm.Regions = ldm.Regions
  309. dm.OmitDefaultRegions = ldm.OmitDefaultRegions
  310. }
  311. if dm.HomeParams == nil {
  312. dm.HomeParams = ldm.HomeParams
  313. } else if oldhh := ldm.HomeParams; oldhh != nil {
  314. // Propagate sub-fields of HomeParams
  315. hh := dm.HomeParams
  316. if hh.RegionScore == nil {
  317. hh.RegionScore = oldhh.RegionScore
  318. }
  319. }
  320. }
  321. ms.lastDERPMap = dm
  322. }
  323. var packetFilterChanged bool
  324. // Older way, one big blob:
  325. if pf := resp.PacketFilter; pf != nil {
  326. packetFilterChanged = true
  327. mak.Set(&ms.namedPacketFilters, "base", views.SliceOf(pf))
  328. }
  329. // Newer way, named chunks:
  330. if m := resp.PacketFilters; m != nil {
  331. packetFilterChanged = true
  332. if v, ok := m["*"]; ok && v == nil {
  333. ms.namedPacketFilters = nil
  334. }
  335. for k, v := range m {
  336. if k == "*" {
  337. continue
  338. }
  339. if v != nil {
  340. mak.Set(&ms.namedPacketFilters, k, views.SliceOf(v))
  341. } else {
  342. delete(ms.namedPacketFilters, k)
  343. }
  344. }
  345. }
  346. if packetFilterChanged {
  347. var concat []tailcfg.FilterRule
  348. for _, v := range slices.Sorted(maps.Keys(ms.namedPacketFilters)) {
  349. concat = ms.namedPacketFilters[v].AppendTo(concat)
  350. }
  351. ms.lastPacketFilterRules = views.SliceOf(concat)
  352. var err error
  353. ms.lastParsedPacketFilter, err = filter.MatchesFromFilterRules(concat)
  354. if err != nil {
  355. ms.logf("parsePacketFilter: %v", err)
  356. }
  357. }
  358. if c := resp.DNSConfig; c != nil {
  359. ms.lastDNSConfig = c
  360. }
  361. if p := resp.SSHPolicy; p != nil {
  362. ms.lastSSHPolicy = p
  363. }
  364. if v, ok := resp.CollectServices.Get(); ok {
  365. ms.collectServices = v
  366. }
  367. if resp.Domain != "" {
  368. ms.lastDomain = resp.Domain
  369. }
  370. if resp.DomainDataPlaneAuditLogID != "" {
  371. ms.lastDomainAuditLogID = resp.DomainDataPlaneAuditLogID
  372. }
  373. if resp.Health != nil {
  374. ms.lastHealth = resp.Health
  375. }
  376. if resp.DisplayMessages != nil {
  377. if v, ok := resp.DisplayMessages["*"]; ok && v == nil {
  378. ms.lastDisplayMessages = nil
  379. }
  380. for k, v := range resp.DisplayMessages {
  381. if k == "*" {
  382. continue
  383. }
  384. if v != nil {
  385. mak.Set(&ms.lastDisplayMessages, k, *v)
  386. } else {
  387. delete(ms.lastDisplayMessages, k)
  388. }
  389. }
  390. }
  391. if resp.TKAInfo != nil {
  392. ms.lastTKAInfo = resp.TKAInfo
  393. }
  394. }
  395. var (
  396. patchDERPRegion = clientmetric.NewCounter("controlclient_patch_derp")
  397. patchEndpoints = clientmetric.NewCounter("controlclient_patch_endpoints")
  398. patchCap = clientmetric.NewCounter("controlclient_patch_capver")
  399. patchKey = clientmetric.NewCounter("controlclient_patch_key")
  400. patchDiscoKey = clientmetric.NewCounter("controlclient_patch_discokey")
  401. patchOnline = clientmetric.NewCounter("controlclient_patch_online")
  402. patchLastSeen = clientmetric.NewCounter("controlclient_patch_lastseen")
  403. patchKeyExpiry = clientmetric.NewCounter("controlclient_patch_keyexpiry")
  404. patchCapMap = clientmetric.NewCounter("controlclient_patch_capmap")
  405. patchKeySignature = clientmetric.NewCounter("controlclient_patch_keysig")
  406. patchifiedPeer = clientmetric.NewCounter("controlclient_patchified_peer")
  407. patchifiedPeerEqual = clientmetric.NewCounter("controlclient_patchified_peer_equal")
  408. )
  409. // updatePeersStateFromResponseres updates ms.peers from resp.
  410. // It takes ownership of resp.
  411. func (ms *mapSession) updatePeersStateFromResponse(resp *tailcfg.MapResponse) (stats updateStats) {
  412. if ms.peers == nil {
  413. ms.peers = make(map[tailcfg.NodeID]tailcfg.NodeView)
  414. }
  415. if len(resp.Peers) > 0 {
  416. // Not delta encoded.
  417. stats.allNew = true
  418. keep := make(map[tailcfg.NodeID]bool, len(resp.Peers))
  419. for _, n := range resp.Peers {
  420. keep[n.ID] = true
  421. lenBefore := len(ms.peers)
  422. ms.peers[n.ID] = n.View()
  423. if len(ms.peers) == lenBefore {
  424. stats.changed++
  425. } else {
  426. stats.added++
  427. }
  428. }
  429. for id := range ms.peers {
  430. if !keep[id] {
  431. stats.removed++
  432. delete(ms.peers, id)
  433. }
  434. }
  435. // Peers precludes all other delta operations so just return.
  436. return
  437. }
  438. for _, id := range resp.PeersRemoved {
  439. if _, ok := ms.peers[id]; ok {
  440. delete(ms.peers, id)
  441. stats.removed++
  442. }
  443. }
  444. for _, n := range resp.PeersChanged {
  445. lenBefore := len(ms.peers)
  446. ms.peers[n.ID] = n.View()
  447. if len(ms.peers) == lenBefore {
  448. stats.changed++
  449. } else {
  450. stats.added++
  451. }
  452. }
  453. for nodeID, seen := range resp.PeerSeenChange {
  454. if vp, ok := ms.peers[nodeID]; ok {
  455. mut := vp.AsStruct()
  456. if seen {
  457. mut.LastSeen = ptr.To(clock.Now())
  458. } else {
  459. mut.LastSeen = nil
  460. }
  461. ms.peers[nodeID] = mut.View()
  462. stats.changed++
  463. }
  464. }
  465. for nodeID, online := range resp.OnlineChange {
  466. if vp, ok := ms.peers[nodeID]; ok {
  467. mut := vp.AsStruct()
  468. mut.Online = ptr.To(online)
  469. ms.peers[nodeID] = mut.View()
  470. stats.changed++
  471. }
  472. }
  473. for _, pc := range resp.PeersChangedPatch {
  474. vp, ok := ms.peers[pc.NodeID]
  475. if !ok {
  476. continue
  477. }
  478. stats.changed++
  479. mut := vp.AsStruct()
  480. if pc.DERPRegion != 0 {
  481. mut.HomeDERP = pc.DERPRegion
  482. patchDERPRegion.Add(1)
  483. }
  484. if pc.Cap != 0 {
  485. mut.Cap = pc.Cap
  486. patchCap.Add(1)
  487. }
  488. if pc.Endpoints != nil {
  489. mut.Endpoints = pc.Endpoints
  490. patchEndpoints.Add(1)
  491. }
  492. if pc.Key != nil {
  493. mut.Key = *pc.Key
  494. patchKey.Add(1)
  495. }
  496. if pc.DiscoKey != nil {
  497. mut.DiscoKey = *pc.DiscoKey
  498. patchDiscoKey.Add(1)
  499. }
  500. if v := pc.Online; v != nil {
  501. mut.Online = ptr.To(*v)
  502. patchOnline.Add(1)
  503. }
  504. if v := pc.LastSeen; v != nil {
  505. mut.LastSeen = ptr.To(*v)
  506. patchLastSeen.Add(1)
  507. }
  508. if v := pc.KeyExpiry; v != nil {
  509. mut.KeyExpiry = *v
  510. patchKeyExpiry.Add(1)
  511. }
  512. if v := pc.KeySignature; v != nil {
  513. mut.KeySignature = v
  514. patchKeySignature.Add(1)
  515. }
  516. if v := pc.CapMap; v != nil {
  517. mut.CapMap = v
  518. patchCapMap.Add(1)
  519. }
  520. ms.peers[pc.NodeID] = mut.View()
  521. }
  522. return
  523. }
  524. func (ms *mapSession) addUserProfile(nm *netmap.NetworkMap, userID tailcfg.UserID) {
  525. if userID == 0 {
  526. return
  527. }
  528. if _, dup := nm.UserProfiles[userID]; dup {
  529. // Already populated it from a previous peer.
  530. return
  531. }
  532. if up, ok := ms.lastUserProfile[userID]; ok {
  533. nm.UserProfiles[userID] = up
  534. }
  535. }
  536. var debugPatchifyPeer = envknob.RegisterBool("TS_DEBUG_PATCHIFY_PEER")
  537. // patchifyPeersChanged mutates resp to promote PeersChanged entries to PeersChangedPatch
  538. // when possible.
  539. func (ms *mapSession) patchifyPeersChanged(resp *tailcfg.MapResponse) {
  540. filtered := resp.PeersChanged[:0]
  541. for _, n := range resp.PeersChanged {
  542. if p, ok := ms.patchifyPeer(n); ok {
  543. patchifiedPeer.Add(1)
  544. if debugPatchifyPeer() {
  545. patchj, _ := json.Marshal(p)
  546. ms.logf("debug: patchifyPeer[ID=%v]: %s", n.ID, patchj)
  547. }
  548. if p != nil {
  549. resp.PeersChangedPatch = append(resp.PeersChangedPatch, p)
  550. } else {
  551. patchifiedPeerEqual.Add(1)
  552. }
  553. } else {
  554. filtered = append(filtered, n)
  555. }
  556. }
  557. resp.PeersChanged = filtered
  558. if len(resp.PeersChanged) == 0 {
  559. resp.PeersChanged = nil
  560. }
  561. }
  562. var nodeFields = sync.OnceValue(getNodeFields)
  563. // getNodeFields returns the fails of tailcfg.Node.
  564. func getNodeFields() []string {
  565. rt := reflect.TypeFor[tailcfg.Node]()
  566. ret := make([]string, rt.NumField())
  567. for i := range rt.NumField() {
  568. ret[i] = rt.Field(i).Name
  569. }
  570. return ret
  571. }
  572. // patchifyPeer returns a *tailcfg.PeerChange of the session's existing copy of
  573. // the n.ID Node to n.
  574. //
  575. // It returns ok=false if a patch can't be made, (V, ok) on a delta, or (nil,
  576. // true) if all the fields were identical (a zero change).
  577. func (ms *mapSession) patchifyPeer(n *tailcfg.Node) (_ *tailcfg.PeerChange, ok bool) {
  578. was, ok := ms.peers[n.ID]
  579. if !ok {
  580. return nil, false
  581. }
  582. return peerChangeDiff(was, n)
  583. }
  584. // peerChangeDiff returns the difference from 'was' to 'n', if possible.
  585. //
  586. // It returns (nil, true) if the fields were identical.
  587. func peerChangeDiff(was tailcfg.NodeView, n *tailcfg.Node) (_ *tailcfg.PeerChange, ok bool) {
  588. var ret *tailcfg.PeerChange
  589. pc := func() *tailcfg.PeerChange {
  590. if ret == nil {
  591. ret = new(tailcfg.PeerChange)
  592. }
  593. return ret
  594. }
  595. for _, field := range nodeFields() {
  596. switch field {
  597. default:
  598. // The whole point of using reflect in this function is to panic
  599. // here in tests if we forget to handle a new field.
  600. panic("unhandled field: " + field)
  601. case "computedHostIfDifferent", "ComputedName", "ComputedNameWithHost":
  602. // Caller's responsibility to have populated these.
  603. continue
  604. case "DataPlaneAuditLogID":
  605. // Not sent for peers.
  606. case "Capabilities":
  607. // Deprecated; see https://github.com/tailscale/tailscale/issues/11508
  608. // And it was never sent by any known control server.
  609. case "ID":
  610. if was.ID() != n.ID {
  611. return nil, false
  612. }
  613. case "StableID":
  614. if was.StableID() != n.StableID {
  615. return nil, false
  616. }
  617. case "Name":
  618. if was.Name() != n.Name {
  619. return nil, false
  620. }
  621. case "User":
  622. if was.User() != n.User {
  623. return nil, false
  624. }
  625. case "Sharer":
  626. if was.Sharer() != n.Sharer {
  627. return nil, false
  628. }
  629. case "Key":
  630. if was.Key() != n.Key {
  631. pc().Key = ptr.To(n.Key)
  632. }
  633. case "KeyExpiry":
  634. if !was.KeyExpiry().Equal(n.KeyExpiry) {
  635. pc().KeyExpiry = ptr.To(n.KeyExpiry)
  636. }
  637. case "KeySignature":
  638. if !was.KeySignature().Equal(n.KeySignature) {
  639. pc().KeySignature = slices.Clone(n.KeySignature)
  640. }
  641. case "Machine":
  642. if was.Machine() != n.Machine {
  643. return nil, false
  644. }
  645. case "DiscoKey":
  646. if was.DiscoKey() != n.DiscoKey {
  647. pc().DiscoKey = ptr.To(n.DiscoKey)
  648. }
  649. case "Addresses":
  650. if !views.SliceEqual(was.Addresses(), views.SliceOf(n.Addresses)) {
  651. return nil, false
  652. }
  653. case "AllowedIPs":
  654. if !views.SliceEqual(was.AllowedIPs(), views.SliceOf(n.AllowedIPs)) {
  655. return nil, false
  656. }
  657. case "Endpoints":
  658. if !views.SliceEqual(was.Endpoints(), views.SliceOf(n.Endpoints)) {
  659. pc().Endpoints = slices.Clone(n.Endpoints)
  660. }
  661. case "LegacyDERPString":
  662. if was.LegacyDERPString() != "" || n.LegacyDERPString != "" {
  663. panic("unexpected; caller should've already called upgradeNode")
  664. }
  665. case "HomeDERP":
  666. if was.HomeDERP() != n.HomeDERP {
  667. pc().DERPRegion = n.HomeDERP
  668. }
  669. case "Hostinfo":
  670. if !was.Hostinfo().Valid() && !n.Hostinfo.Valid() {
  671. continue
  672. }
  673. if !was.Hostinfo().Valid() || !n.Hostinfo.Valid() {
  674. return nil, false
  675. }
  676. if !was.Hostinfo().Equal(n.Hostinfo) {
  677. return nil, false
  678. }
  679. case "Created":
  680. if !was.Created().Equal(n.Created) {
  681. return nil, false
  682. }
  683. case "Cap":
  684. if was.Cap() != n.Cap {
  685. pc().Cap = n.Cap
  686. }
  687. case "CapMap":
  688. if len(n.CapMap) != was.CapMap().Len() {
  689. // If they have different lengths, they're different.
  690. if n.CapMap == nil {
  691. pc().CapMap = make(tailcfg.NodeCapMap)
  692. } else {
  693. pc().CapMap = maps.Clone(n.CapMap)
  694. }
  695. } else {
  696. // If they have the same length, check that all their keys
  697. // have the same values.
  698. for k, v := range was.CapMap().All() {
  699. nv, ok := n.CapMap[k]
  700. if !ok || !views.SliceEqual(v, views.SliceOf(nv)) {
  701. pc().CapMap = maps.Clone(n.CapMap)
  702. break
  703. }
  704. }
  705. }
  706. case "Tags":
  707. if !views.SliceEqual(was.Tags(), views.SliceOf(n.Tags)) {
  708. return nil, false
  709. }
  710. case "PrimaryRoutes":
  711. if !views.SliceEqual(was.PrimaryRoutes(), views.SliceOf(n.PrimaryRoutes)) {
  712. return nil, false
  713. }
  714. case "Online":
  715. if wasOnline, ok := was.Online().GetOk(); ok && n.Online != nil && *n.Online != wasOnline {
  716. pc().Online = ptr.To(*n.Online)
  717. }
  718. case "LastSeen":
  719. if wasSeen, ok := was.LastSeen().GetOk(); ok && n.LastSeen != nil && !wasSeen.Equal(*n.LastSeen) {
  720. pc().LastSeen = ptr.To(*n.LastSeen)
  721. }
  722. case "MachineAuthorized":
  723. if was.MachineAuthorized() != n.MachineAuthorized {
  724. return nil, false
  725. }
  726. case "UnsignedPeerAPIOnly":
  727. if was.UnsignedPeerAPIOnly() != n.UnsignedPeerAPIOnly {
  728. return nil, false
  729. }
  730. case "IsWireGuardOnly":
  731. if was.IsWireGuardOnly() != n.IsWireGuardOnly {
  732. return nil, false
  733. }
  734. case "IsJailed":
  735. if was.IsJailed() != n.IsJailed {
  736. return nil, false
  737. }
  738. case "Expired":
  739. if was.Expired() != n.Expired {
  740. return nil, false
  741. }
  742. case "SelfNodeV4MasqAddrForThisPeer":
  743. va, vb := was.SelfNodeV4MasqAddrForThisPeer(), n.SelfNodeV4MasqAddrForThisPeer
  744. if !va.Valid() && vb == nil {
  745. continue
  746. }
  747. if va, ok := va.GetOk(); !ok || vb == nil || va != *vb {
  748. return nil, false
  749. }
  750. case "SelfNodeV6MasqAddrForThisPeer":
  751. va, vb := was.SelfNodeV6MasqAddrForThisPeer(), n.SelfNodeV6MasqAddrForThisPeer
  752. if !va.Valid() && vb == nil {
  753. continue
  754. }
  755. if va, ok := va.GetOk(); !ok || vb == nil || va != *vb {
  756. return nil, false
  757. }
  758. case "ExitNodeDNSResolvers":
  759. va, vb := was.ExitNodeDNSResolvers(), views.SliceOfViews(n.ExitNodeDNSResolvers)
  760. if va.Len() != vb.Len() {
  761. return nil, false
  762. }
  763. for i := range va.Len() {
  764. if !va.At(i).Equal(vb.At(i)) {
  765. return nil, false
  766. }
  767. }
  768. }
  769. }
  770. if ret != nil {
  771. ret.NodeID = n.ID
  772. }
  773. return ret, true
  774. }
  775. func (ms *mapSession) sortedPeers() []tailcfg.NodeView {
  776. ret := slicesx.MapValues(ms.peers)
  777. slices.SortFunc(ret, func(a, b tailcfg.NodeView) int {
  778. return cmp.Compare(a.ID(), b.ID())
  779. })
  780. return ret
  781. }
  782. // netmap returns a fully populated NetworkMap from the last state seen from
  783. // a call to updateStateFromResponse, filling in omitted
  784. // information from prior MapResponse values.
  785. func (ms *mapSession) netmap() *netmap.NetworkMap {
  786. peerViews := ms.sortedPeers()
  787. var msgs map[tailcfg.DisplayMessageID]tailcfg.DisplayMessage
  788. if len(ms.lastDisplayMessages) != 0 {
  789. msgs = ms.lastDisplayMessages
  790. } else if len(ms.lastHealth) > 0 {
  791. // Convert all ms.lastHealth to the new [netmap.NetworkMap.DisplayMessages]
  792. for _, h := range ms.lastHealth {
  793. id := "health-" + strhash(h) // Unique ID in case there is more than one health message
  794. mak.Set(&msgs, tailcfg.DisplayMessageID(id), tailcfg.DisplayMessage{
  795. Title: "Coordination server reports an issue",
  796. Severity: tailcfg.SeverityMedium,
  797. Text: "The coordination server is reporting a health issue: " + h,
  798. })
  799. }
  800. }
  801. nm := &netmap.NetworkMap{
  802. NodeKey: ms.publicNodeKey,
  803. MachineKey: ms.machinePubKey,
  804. Peers: peerViews,
  805. UserProfiles: make(map[tailcfg.UserID]tailcfg.UserProfileView),
  806. Domain: ms.lastDomain,
  807. DomainAuditLogID: ms.lastDomainAuditLogID,
  808. DNS: *ms.lastDNSConfig,
  809. PacketFilter: ms.lastParsedPacketFilter,
  810. PacketFilterRules: ms.lastPacketFilterRules,
  811. SSHPolicy: ms.lastSSHPolicy,
  812. CollectServices: ms.collectServices,
  813. DERPMap: ms.lastDERPMap,
  814. DisplayMessages: msgs,
  815. TKAEnabled: ms.lastTKAInfo != nil && !ms.lastTKAInfo.Disabled,
  816. }
  817. if ms.lastTKAInfo != nil && ms.lastTKAInfo.Head != "" {
  818. if err := nm.TKAHead.UnmarshalText([]byte(ms.lastTKAInfo.Head)); err != nil {
  819. ms.logf("error unmarshalling TKAHead: %v", err)
  820. nm.TKAEnabled = false
  821. }
  822. }
  823. if node := ms.lastNode; node.Valid() {
  824. nm.SelfNode = node
  825. nm.AllCaps = ms.lastCapSet
  826. }
  827. ms.addUserProfile(nm, nm.User())
  828. for _, peer := range peerViews {
  829. ms.addUserProfile(nm, peer.Sharer())
  830. ms.addUserProfile(nm, peer.User())
  831. }
  832. if DevKnob.ForceProxyDNS() {
  833. nm.DNS.Proxied = true
  834. }
  835. return nm
  836. }
  837. func strhash(h string) string {
  838. s := sha256.New()
  839. io.WriteString(s, h)
  840. return hex.EncodeToString(s.Sum(nil))
  841. }