auto.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. // Copyright (c) Tailscale Inc & contributors
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package controlclient
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "net/http"
  9. "sync"
  10. "sync/atomic"
  11. "time"
  12. "tailscale.com/net/sockstats"
  13. "tailscale.com/tailcfg"
  14. "tailscale.com/tstime"
  15. "tailscale.com/types/key"
  16. "tailscale.com/types/logger"
  17. "tailscale.com/types/netmap"
  18. "tailscale.com/types/persist"
  19. "tailscale.com/types/structs"
  20. "tailscale.com/util/backoff"
  21. "tailscale.com/util/clientmetric"
  22. "tailscale.com/util/execqueue"
  23. "tailscale.com/util/testenv"
  24. )
  25. type LoginGoal struct {
  26. _ structs.Incomparable
  27. flags LoginFlags // flags to use when logging in
  28. url string // auth url that needs to be visited
  29. }
  30. var _ Client = (*Auto)(nil)
  31. // waitUnpause waits until either the client is unpaused or the Auto client is
  32. // shut down. It reports whether the client should keep running (i.e. it's not
  33. // closed).
  34. func (c *Auto) waitUnpause(routineLogName string) (keepRunning bool) {
  35. c.mu.Lock()
  36. if !c.paused || c.closed {
  37. defer c.mu.Unlock()
  38. return !c.closed
  39. }
  40. unpaused := c.unpausedChanLocked()
  41. c.mu.Unlock()
  42. c.logf("%s: awaiting unpause", routineLogName)
  43. return <-unpaused
  44. }
  45. // updateRoutine is responsible for informing the server of worthy changes to
  46. // our local state. It runs in its own goroutine.
  47. func (c *Auto) updateRoutine() {
  48. defer close(c.updateDone)
  49. bo := backoff.NewBackoff("updateRoutine", c.logf, 30*time.Second)
  50. // lastUpdateGenInformed is the value of lastUpdateAt that we've successfully
  51. // informed the server of.
  52. var lastUpdateGenInformed updateGen
  53. for {
  54. if !c.waitUnpause("updateRoutine") {
  55. c.logf("updateRoutine: exiting")
  56. return
  57. }
  58. c.mu.Lock()
  59. gen := c.lastUpdateGen
  60. ctx := c.mapCtx
  61. needUpdate := gen > 0 && gen != lastUpdateGenInformed && c.loggedIn
  62. c.mu.Unlock()
  63. if !needUpdate {
  64. // Nothing to do, wait for a signal.
  65. select {
  66. case <-ctx.Done():
  67. continue
  68. case <-c.updateCh:
  69. continue
  70. }
  71. }
  72. t0 := c.clock.Now()
  73. err := c.direct.SendUpdate(ctx)
  74. d := time.Since(t0).Round(time.Millisecond)
  75. if err != nil {
  76. if ctx.Err() == nil {
  77. c.direct.logf("lite map update error after %v: %v", d, err)
  78. }
  79. bo.BackOff(ctx, err)
  80. continue
  81. }
  82. bo.BackOff(ctx, nil)
  83. c.direct.logf("[v1] successful lite map update in %v", d)
  84. lastUpdateGenInformed = gen
  85. }
  86. }
  87. // atomicGen is an atomic int64 generator. It is used to generate monotonically
  88. // increasing numbers for updateGen.
  89. var atomicGen atomic.Int64
  90. func nextUpdateGen() updateGen {
  91. return updateGen(atomicGen.Add(1))
  92. }
  93. // updateGen is a monotonically increasing number that represents a particular
  94. // update to the local state.
  95. type updateGen int64
  96. // Auto connects to a tailcontrol server for a node.
  97. // It's a concrete implementation of the Client interface.
  98. type Auto struct {
  99. direct *Direct // our interface to the server APIs
  100. clock tstime.Clock
  101. logf logger.Logf
  102. closed bool
  103. updateCh chan struct{} // readable when we should inform the server of a change
  104. observer Observer // if non-nil, called to update Client status
  105. observerQueue execqueue.ExecQueue
  106. shutdownFn func() // to be called prior to shutdown or nil
  107. mu sync.Mutex // mutex guards the following fields
  108. started bool // whether [Auto.Start] has been called
  109. wantLoggedIn bool // whether the user wants to be logged in per last method call
  110. urlToVisit string // the last url we were told to visit
  111. expiry time.Time
  112. // lastUpdateGen is the gen of last update we had an update worth sending to
  113. // the server.
  114. lastUpdateGen updateGen
  115. lastStatus atomic.Pointer[Status]
  116. paused bool // whether we should stop making HTTP requests
  117. unpauseWaiters []chan bool // chans that gets sent true (once) on wake, or false on Shutdown
  118. loggedIn bool // true if currently logged in
  119. loginGoal *LoginGoal // non-nil if some login activity is desired
  120. inMapPoll bool // true once we get the first MapResponse in a stream; false when HTTP response ends
  121. authCtx context.Context // context used for auth requests
  122. mapCtx context.Context // context used for netmap and update requests
  123. authCancel func() // cancel authCtx
  124. mapCancel func() // cancel mapCtx
  125. authDone chan struct{} // when closed, authRoutine is done
  126. mapDone chan struct{} // when closed, mapRoutine is done
  127. updateDone chan struct{} // when closed, updateRoutine is done
  128. }
  129. // New creates and starts a new Auto.
  130. func New(opts Options) (*Auto, error) {
  131. c, err := newNoStart(opts)
  132. if err != nil {
  133. return nil, err
  134. }
  135. if opts.StartPaused {
  136. c.SetPaused(true)
  137. }
  138. if !opts.SkipStartForTests {
  139. c.start()
  140. }
  141. return c, err
  142. }
  143. // newNoStart creates a new Auto, but without calling Start on it.
  144. func newNoStart(opts Options) (_ *Auto, err error) {
  145. direct, err := NewDirect(opts)
  146. if err != nil {
  147. return nil, err
  148. }
  149. defer func() {
  150. if err != nil {
  151. direct.Close()
  152. }
  153. }()
  154. if opts.Logf == nil {
  155. opts.Logf = func(fmt string, args ...any) {}
  156. }
  157. if opts.Clock == nil {
  158. opts.Clock = tstime.StdClock{}
  159. }
  160. c := &Auto{
  161. direct: direct,
  162. clock: opts.Clock,
  163. logf: opts.Logf,
  164. updateCh: make(chan struct{}, 1),
  165. authDone: make(chan struct{}),
  166. mapDone: make(chan struct{}),
  167. updateDone: make(chan struct{}),
  168. observer: opts.Observer,
  169. shutdownFn: opts.Shutdown,
  170. }
  171. c.authCtx, c.authCancel = context.WithCancel(context.Background())
  172. c.authCtx = sockstats.WithSockStats(c.authCtx, sockstats.LabelControlClientAuto, opts.Logf)
  173. c.mapCtx, c.mapCancel = context.WithCancel(context.Background())
  174. c.mapCtx = sockstats.WithSockStats(c.mapCtx, sockstats.LabelControlClientAuto, opts.Logf)
  175. return c, nil
  176. }
  177. // SetPaused controls whether HTTP activity should be paused.
  178. //
  179. // The client can be paused and unpaused repeatedly, unlike Start and Shutdown, which can only be used once.
  180. func (c *Auto) SetPaused(paused bool) {
  181. c.mu.Lock()
  182. defer c.mu.Unlock()
  183. if paused == c.paused || c.closed {
  184. return
  185. }
  186. c.logf("setPaused(%v)", paused)
  187. c.paused = paused
  188. if paused {
  189. c.cancelMapCtxLocked()
  190. c.cancelAuthCtxLocked()
  191. return
  192. }
  193. for _, ch := range c.unpauseWaiters {
  194. ch <- true
  195. }
  196. c.unpauseWaiters = nil
  197. }
  198. // StartForTest starts the client's goroutines.
  199. //
  200. // It should only be called for clients created with [Options.SkipStartForTests].
  201. func (c *Auto) StartForTest() {
  202. testenv.AssertInTest()
  203. c.start()
  204. }
  205. func (c *Auto) start() {
  206. c.mu.Lock()
  207. defer c.mu.Unlock()
  208. if c.started {
  209. return
  210. }
  211. c.started = true
  212. go c.authRoutine()
  213. go c.mapRoutine()
  214. go c.updateRoutine()
  215. }
  216. // updateControl sends a new OmitPeers, non-streaming map request (to just send
  217. // Hostinfo/Netinfo/Endpoints info, while keeping an existing streaming response
  218. // open).
  219. //
  220. // It should be called whenever there's something new to tell the server.
  221. func (c *Auto) updateControl() {
  222. gen := nextUpdateGen()
  223. c.mu.Lock()
  224. if gen < c.lastUpdateGen {
  225. // This update is out of date.
  226. c.mu.Unlock()
  227. return
  228. }
  229. c.lastUpdateGen = gen
  230. c.mu.Unlock()
  231. select {
  232. case c.updateCh <- struct{}{}:
  233. default:
  234. }
  235. }
  236. // cancelAuthCtxLocked is like cancelAuthCtx, but assumes the caller holds c.mu.
  237. func (c *Auto) cancelAuthCtxLocked() {
  238. if c.authCancel != nil {
  239. c.authCancel()
  240. }
  241. if !c.closed {
  242. c.authCtx, c.authCancel = context.WithCancel(context.Background())
  243. c.authCtx = sockstats.WithSockStats(c.authCtx, sockstats.LabelControlClientAuto, c.logf)
  244. }
  245. }
  246. // cancelMapCtxLocked is like cancelMapCtx, but assumes the caller holds c.mu.
  247. func (c *Auto) cancelMapCtxLocked() {
  248. if c.mapCancel != nil {
  249. c.mapCancel()
  250. }
  251. if !c.closed {
  252. c.mapCtx, c.mapCancel = context.WithCancel(context.Background())
  253. c.mapCtx = sockstats.WithSockStats(c.mapCtx, sockstats.LabelControlClientAuto, c.logf)
  254. }
  255. }
  256. // restartMap cancels the existing mapPoll and liteUpdates, and then starts a
  257. // new one.
  258. func (c *Auto) restartMap() {
  259. c.mu.Lock()
  260. c.cancelMapCtxLocked()
  261. synced := c.inMapPoll
  262. c.mu.Unlock()
  263. c.logf("[v1] restartMap: synced=%v", synced)
  264. c.updateControl()
  265. }
  266. func (c *Auto) authRoutine() {
  267. defer close(c.authDone)
  268. bo := backoff.NewBackoff("authRoutine", c.logf, 30*time.Second)
  269. for {
  270. if !c.waitUnpause("authRoutine") {
  271. c.logf("authRoutine: exiting")
  272. return
  273. }
  274. c.mu.Lock()
  275. goal := c.loginGoal
  276. ctx := c.authCtx
  277. loggedIn := c.loggedIn
  278. if goal != nil {
  279. c.logf("[v1] authRoutine: loggedIn=%v; wantLoggedIn=%v", loggedIn, true)
  280. } else {
  281. c.logf("[v1] authRoutine: loggedIn=%v; goal=nil paused=%v", loggedIn, c.paused)
  282. }
  283. c.mu.Unlock()
  284. report := func(err error, msg string) {
  285. c.logf("[v1] %s: %v", msg, err)
  286. // don't send status updates for context errors,
  287. // since context cancelation is always on purpose.
  288. if ctx.Err() == nil {
  289. c.sendStatus("authRoutine-report", err, "", nil)
  290. }
  291. }
  292. if goal == nil {
  293. c.direct.health.SetAuthRoutineInError(nil)
  294. // Wait for user to Login or Logout.
  295. <-ctx.Done()
  296. c.logf("[v1] authRoutine: context done.")
  297. continue
  298. }
  299. c.mu.Lock()
  300. c.urlToVisit = goal.url
  301. c.mu.Unlock()
  302. var url string
  303. var err error
  304. var f string
  305. if goal.url != "" {
  306. url, err = c.direct.WaitLoginURL(ctx, goal.url)
  307. f = "WaitLoginURL"
  308. } else {
  309. url, err = c.direct.TryLogin(ctx, goal.flags)
  310. f = "TryLogin"
  311. }
  312. if err != nil {
  313. c.direct.health.SetAuthRoutineInError(err)
  314. report(err, f)
  315. bo.BackOff(ctx, err)
  316. continue
  317. }
  318. if url != "" {
  319. // goal.url ought to be empty here. However, not all control servers
  320. // get this right, and logging about it here just generates noise.
  321. //
  322. // TODO(bradfitz): I don't follow that comment. Our own testcontrol
  323. // used by tstest/integration hits this path, in fact.
  324. if c.direct.panicOnUse {
  325. panic("tainted client")
  326. }
  327. c.mu.Lock()
  328. c.urlToVisit = url
  329. c.loginGoal = &LoginGoal{
  330. flags: LoginDefault,
  331. url: url,
  332. }
  333. c.mu.Unlock()
  334. c.sendStatus("authRoutine-url", err, url, nil)
  335. if goal.url == url {
  336. // The server sent us the same URL we already tried,
  337. // backoff to avoid a busy loop.
  338. bo.BackOff(ctx, errors.New("login URL not changing"))
  339. } else {
  340. bo.BackOff(ctx, nil)
  341. }
  342. continue
  343. }
  344. // success
  345. c.direct.health.SetAuthRoutineInError(nil)
  346. c.mu.Lock()
  347. c.urlToVisit = ""
  348. c.loggedIn = true
  349. c.loginGoal = nil
  350. c.mu.Unlock()
  351. c.sendStatus("authRoutine-success", nil, "", nil)
  352. c.restartMap()
  353. bo.BackOff(ctx, nil)
  354. }
  355. }
  356. // ExpiryForTests returns the credential expiration time, or the zero value if
  357. // the expiration time isn't known. It's used in tests only.
  358. func (c *Auto) ExpiryForTests() time.Time {
  359. c.mu.Lock()
  360. defer c.mu.Unlock()
  361. return c.expiry
  362. }
  363. // DirectForTest returns the underlying direct client object.
  364. // It's used in tests only.
  365. func (c *Auto) DirectForTest() *Direct {
  366. return c.direct
  367. }
  368. // unpausedChanLocked returns a new channel that gets sent
  369. // either a true when unpaused or false on Auto.Shutdown.
  370. //
  371. // c.mu must be held
  372. func (c *Auto) unpausedChanLocked() <-chan bool {
  373. unpaused := make(chan bool, 1)
  374. c.unpauseWaiters = append(c.unpauseWaiters, unpaused)
  375. return unpaused
  376. }
  377. // ClientID returns the ClientID of the direct controlClient
  378. func (c *Auto) ClientID() int64 {
  379. return c.direct.ClientID()
  380. }
  381. // mapRoutineState is the state of Auto.mapRoutine while it's running.
  382. type mapRoutineState struct {
  383. c *Auto
  384. bo *backoff.Backoff
  385. }
  386. var _ NetmapDeltaUpdater = mapRoutineState{}
  387. func (mrs mapRoutineState) UpdateFullNetmap(nm *netmap.NetworkMap) {
  388. c := mrs.c
  389. c.mu.Lock()
  390. c.inMapPoll = true
  391. c.expiry = nm.SelfKeyExpiry()
  392. stillAuthed := c.loggedIn
  393. c.logf("[v1] mapRoutine: netmap received: loggedIn=%v inMapPoll=true", stillAuthed)
  394. c.mu.Unlock()
  395. if stillAuthed {
  396. c.sendStatus("mapRoutine-got-netmap", nil, "", nm)
  397. }
  398. // Reset the backoff timer if we got a netmap.
  399. mrs.bo.Reset()
  400. }
  401. func (mrs mapRoutineState) UpdateNetmapDelta(muts []netmap.NodeMutation) bool {
  402. c := mrs.c
  403. c.mu.Lock()
  404. goodState := c.loggedIn && c.inMapPoll
  405. ndu, canDelta := c.observer.(NetmapDeltaUpdater)
  406. c.mu.Unlock()
  407. if !goodState || !canDelta {
  408. return false
  409. }
  410. ctx, cancel := context.WithTimeout(c.mapCtx, 2*time.Second)
  411. defer cancel()
  412. var ok bool
  413. err := c.observerQueue.RunSync(ctx, func() {
  414. ok = ndu.UpdateNetmapDelta(muts)
  415. })
  416. return err == nil && ok
  417. }
  418. // mapRoutine is responsible for keeping a read-only streaming connection to the
  419. // control server, and keeping the netmap up to date.
  420. func (c *Auto) mapRoutine() {
  421. defer close(c.mapDone)
  422. mrs := mapRoutineState{
  423. c: c,
  424. bo: backoff.NewBackoff("mapRoutine", c.logf, 30*time.Second),
  425. }
  426. for {
  427. if !c.waitUnpause("mapRoutine") {
  428. c.logf("mapRoutine: exiting")
  429. return
  430. }
  431. c.mu.Lock()
  432. loggedIn := c.loggedIn
  433. c.logf("[v1] mapRoutine: loggedIn=%v", loggedIn)
  434. ctx := c.mapCtx
  435. c.mu.Unlock()
  436. report := func(err error, msg string) {
  437. c.logf("[v1] %s: %v", msg, err)
  438. err = fmt.Errorf("%s: %w", msg, err)
  439. // don't send status updates for context errors,
  440. // since context cancelation is always on purpose.
  441. if ctx.Err() == nil {
  442. c.sendStatus("mapRoutine1", err, "", nil)
  443. }
  444. }
  445. if !loggedIn {
  446. // Wait for something interesting to happen
  447. c.mu.Lock()
  448. c.inMapPoll = false
  449. c.mu.Unlock()
  450. <-ctx.Done()
  451. c.logf("[v1] mapRoutine: context done.")
  452. continue
  453. }
  454. c.direct.health.SetOutOfPollNetMap()
  455. err := c.direct.PollNetMap(ctx, mrs)
  456. c.direct.health.SetOutOfPollNetMap()
  457. c.mu.Lock()
  458. c.inMapPoll = false
  459. paused := c.paused
  460. c.mu.Unlock()
  461. if paused {
  462. mrs.bo.BackOff(ctx, nil)
  463. c.logf("mapRoutine: paused")
  464. } else {
  465. mrs.bo.BackOff(ctx, err)
  466. report(err, "PollNetMap")
  467. }
  468. }
  469. }
  470. func (c *Auto) AuthCantContinue() bool {
  471. if c == nil {
  472. return true
  473. }
  474. c.mu.Lock()
  475. defer c.mu.Unlock()
  476. return !c.loggedIn && (c.loginGoal == nil || c.loginGoal.url != "")
  477. }
  478. func (c *Auto) SetHostinfo(hi *tailcfg.Hostinfo) {
  479. if hi == nil {
  480. panic("nil Hostinfo")
  481. }
  482. if !c.direct.SetHostinfo(hi) {
  483. // No changes. Don't log.
  484. return
  485. }
  486. // Send new Hostinfo to server
  487. c.updateControl()
  488. }
  489. func (c *Auto) SetNetInfo(ni *tailcfg.NetInfo) {
  490. if ni == nil {
  491. panic("nil NetInfo")
  492. }
  493. if !c.direct.SetNetInfo(ni) {
  494. return
  495. }
  496. // Send new NetInfo to server
  497. c.updateControl()
  498. }
  499. // SetTKAHead updates the TKA head hash that map-request infrastructure sends.
  500. func (c *Auto) SetTKAHead(headHash string) {
  501. if !c.direct.SetTKAHead(headHash) {
  502. return
  503. }
  504. // Send new TKAHead to server
  505. c.updateControl()
  506. }
  507. // sendStatus can not be called with the c.mu held.
  508. func (c *Auto) sendStatus(who string, err error, url string, nm *netmap.NetworkMap) {
  509. c.mu.Lock()
  510. if c.closed {
  511. c.mu.Unlock()
  512. return
  513. }
  514. loggedIn := c.loggedIn
  515. inMapPoll := c.inMapPoll
  516. loginGoal := c.loginGoal
  517. c.mu.Unlock()
  518. c.logf("[v1] sendStatus: %s: loggedIn=%v inMapPoll=%v", who, loggedIn, inMapPoll)
  519. var p persist.PersistView
  520. if nm != nil && loggedIn && inMapPoll {
  521. p = c.direct.GetPersist()
  522. } else {
  523. // don't send netmap status, as it's misleading when we're
  524. // not logged in.
  525. nm = nil
  526. }
  527. newSt := &Status{
  528. URL: url,
  529. Persist: p,
  530. NetMap: nm,
  531. Err: err,
  532. LoggedIn: loggedIn && loginGoal == nil,
  533. InMapPoll: inMapPoll,
  534. }
  535. if c.observer == nil {
  536. return
  537. }
  538. c.lastStatus.Store(newSt)
  539. // Launch a new goroutine to avoid blocking the caller while the observer
  540. // does its thing, which may result in a call back into the client.
  541. metricQueued.Add(1)
  542. c.observerQueue.Add(func() {
  543. c.mu.Lock()
  544. closed := c.closed
  545. c.mu.Unlock()
  546. if closed {
  547. return
  548. }
  549. if canSkipStatus(newSt, c.lastStatus.Load()) {
  550. metricSkippable.Add(1)
  551. if !c.direct.controlKnobs.DisableSkipStatusQueue.Load() {
  552. metricSkipped.Add(1)
  553. return
  554. }
  555. }
  556. c.observer.SetControlClientStatus(c, *newSt)
  557. // Best effort stop retaining the memory now that we've sent it to the
  558. // observer (LocalBackend). We CAS here because the caller goroutine is
  559. // doing a Store which we want to win a race. This is only a memory
  560. // optimization and is not for correctness.
  561. //
  562. // If the CAS fails, that means somebody else's Store replaced our
  563. // pointer (so mission accomplished: our netmap is no longer retained in
  564. // any case) and that Store caller will be responsible for removing
  565. // their own netmap (or losing their race too, down the chain).
  566. // Eventually the last caller will win this CAS and zero lastStatus.
  567. c.lastStatus.CompareAndSwap(newSt, nil)
  568. })
  569. }
  570. var (
  571. metricQueued = clientmetric.NewCounter("controlclient_auto_status_queued")
  572. metricSkippable = clientmetric.NewCounter("controlclient_auto_status_queue_skippable")
  573. metricSkipped = clientmetric.NewCounter("controlclient_auto_status_queue_skipped")
  574. )
  575. // canSkipStatus reports whether we can skip sending s1, knowing
  576. // that s2 is enqueued sometime in the future after s1.
  577. //
  578. // s1 must be non-nil. s2 may be nil.
  579. func canSkipStatus(s1, s2 *Status) bool {
  580. if s2 == nil {
  581. // Nothing in the future.
  582. return false
  583. }
  584. if s1 == s2 {
  585. // If the last item in the queue is the same as s1,
  586. // we can't skip it.
  587. return false
  588. }
  589. if s1.Err != nil || s1.URL != "" {
  590. // If s1 has an error or an URL, we shouldn't skip it, lest the error go
  591. // away in s2 or in-between. We want to make sure all the subsystems see
  592. // it. Plus there aren't many of these, so not worth skipping.
  593. return false
  594. }
  595. if !s1.Persist.Equals(s2.Persist) || s1.LoggedIn != s2.LoggedIn || s1.InMapPoll != s2.InMapPoll || s1.URL != s2.URL {
  596. // If s1 has a different Persist, has changed login state, changed map
  597. // poll state, or has a new login URL, don't skip it. We only care about
  598. // skipping the typical entries where the only difference is the NetMap.
  599. return false
  600. }
  601. // If nothing above precludes it, and both s1 and s2 have NetMaps, then
  602. // we can skip it, because s2's NetMap is a newer version and we can
  603. // jump straight from whatever state we had before to s2's state,
  604. // without passing through s1's state first. A NetMap is regrettably a
  605. // full snapshot of the state, not an incremental delta. We're slowly
  606. // moving towards passing around only deltas around internally at all
  607. // layers, but this is explicitly the case where we didn't have a delta
  608. // path for the message we received over the wire and had to resort
  609. // to the legacy full NetMap path. And then we can get behind processing
  610. // these full NetMap snapshots in LocalBackend/wgengine/magicsock/netstack
  611. // and this path (when it returns true) lets us skip over useless work
  612. // and not get behind in the queue. This matters in particular for tailnets
  613. // that are both very large + very churny.
  614. return s1.NetMap != nil && s2.NetMap != nil
  615. }
  616. func (c *Auto) Login(flags LoginFlags) {
  617. c.logf("client.Login(%v)", flags)
  618. c.mu.Lock()
  619. defer c.mu.Unlock()
  620. if c.closed {
  621. return
  622. }
  623. if c.direct != nil && c.direct.panicOnUse {
  624. panic("tainted client")
  625. }
  626. c.wantLoggedIn = true
  627. c.loginGoal = &LoginGoal{
  628. flags: flags,
  629. }
  630. c.cancelMapCtxLocked()
  631. c.cancelAuthCtxLocked()
  632. }
  633. var ErrClientClosed = errors.New("client closed")
  634. func (c *Auto) Logout(ctx context.Context) error {
  635. c.logf("client.Logout()")
  636. c.mu.Lock()
  637. c.wantLoggedIn = false
  638. c.loginGoal = nil
  639. closed := c.closed
  640. if c.direct != nil && c.direct.panicOnUse {
  641. panic("tainted client")
  642. }
  643. c.mu.Unlock()
  644. if closed {
  645. return ErrClientClosed
  646. }
  647. if err := c.direct.TryLogout(ctx); err != nil {
  648. return err
  649. }
  650. c.mu.Lock()
  651. c.loggedIn = false
  652. c.cancelAuthCtxLocked()
  653. c.cancelMapCtxLocked()
  654. c.mu.Unlock()
  655. c.sendStatus("authRoutine-wantout", nil, "", nil)
  656. return nil
  657. }
  658. func (c *Auto) SetExpirySooner(ctx context.Context, expiry time.Time) error {
  659. return c.direct.SetExpirySooner(ctx, expiry)
  660. }
  661. // UpdateEndpoints sets the client's discovered endpoints and sends
  662. // them to the control server if they've changed.
  663. //
  664. // It does not retain the provided slice.
  665. func (c *Auto) UpdateEndpoints(endpoints []tailcfg.Endpoint) {
  666. changed := c.direct.SetEndpoints(endpoints)
  667. if changed {
  668. c.updateControl()
  669. }
  670. }
  671. // SetDiscoPublicKey sets the client's Disco public to key and sends the change
  672. // to the control server.
  673. func (c *Auto) SetDiscoPublicKey(key key.DiscoPublic) {
  674. c.direct.SetDiscoPublicKey(key)
  675. c.updateControl()
  676. }
  677. func (c *Auto) Shutdown() {
  678. c.mu.Lock()
  679. if c.closed {
  680. c.mu.Unlock()
  681. return
  682. }
  683. c.logf("client.Shutdown ...")
  684. shutdownFn := c.shutdownFn
  685. direct := c.direct
  686. c.closed = true
  687. c.observerQueue.Shutdown()
  688. c.cancelAuthCtxLocked()
  689. c.cancelMapCtxLocked()
  690. for _, w := range c.unpauseWaiters {
  691. w <- false
  692. }
  693. c.unpauseWaiters = nil
  694. c.mu.Unlock()
  695. if shutdownFn != nil {
  696. shutdownFn()
  697. }
  698. <-c.authDone
  699. <-c.mapDone
  700. <-c.updateDone
  701. if direct != nil {
  702. direct.Close()
  703. }
  704. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  705. defer cancel()
  706. c.observerQueue.Wait(ctx)
  707. c.logf("Client.Shutdown done.")
  708. }
  709. // NodePublicKey returns the node public key currently in use. This is
  710. // used exclusively in tests.
  711. func (c *Auto) TestOnlyNodePublicKey() key.NodePublic {
  712. priv := c.direct.GetPersist()
  713. return priv.PrivateNodeKey().Public()
  714. }
  715. func (c *Auto) TestOnlySetAuthKey(authkey string) {
  716. c.direct.mu.Lock()
  717. defer c.direct.mu.Unlock()
  718. c.direct.authKey = authkey
  719. }
  720. func (c *Auto) TestOnlyTimeNow() time.Time {
  721. return c.clock.Now()
  722. }
  723. // SetDNS sends the SetDNSRequest request to the control plane server,
  724. // requesting a DNS record be created or updated.
  725. func (c *Auto) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) error {
  726. return c.direct.SetDNS(ctx, req)
  727. }
  728. func (c *Auto) DoNoiseRequest(req *http.Request) (*http.Response, error) {
  729. return c.direct.DoNoiseRequest(req)
  730. }