Commit Graph
70 Commits
Author SHA1 Message Date
M. J. FrombergerandGitHub 4f1406f05a ipn/ipnlocal/netmapcache: include packet filters in the cache (#18715)
Store packet filter rules in the cache. The match expressions are derived
from the filter rules, so these do not need to be stored explicitly, but ensure
they are properly reconstructed when the cache is read back.

Update the tests to include these fields, and provide representative values.

Updates #12639

Change-Id: I9bdb972a86d2c6387177d393ada1f54805a2448b
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2026-02-13 10:59:43 -08:00
M. J. FrombergerandGitHub 14322713a5 ipn/ipnlocal/netmapcache: ensure cache updates preserve unchanged data (#18590)
Found by @cmol. When rewriting the same value into the cache, we were dropping
the unchanged keys, resulting in the cache being pruned incorrectly.
Also update the tests to catch this.

Updates #12639

Change-Id: Iab67e444eb7ddc22ccc680baa2f6a741a00eb325
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2026-02-03 07:55:41 -08:00
M. J. FrombergerandGitHub 99584b26ae ipn/ipnlocal/netmapcache: report the correct error for a missing column (#18547)
The file-based cache implementation was not reporting the correct error when
attempting to load a missing column key. Make it do so, and update the tests to
cover that case.

Updates #12639

Change-Id: Ie2c45a0a7e528d4125f857859c92df807116a56e
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2026-01-28 14:32:40 -08:00
M. J. FrombergerandGitHub 9385dfe7f6 ipn/ipnlocal/netmapcache: add a package to split and cache network maps (#18497)
This commit is based on part of #17925, reworked as a separate package.

Add a package that can store and load netmap.NetworkMap values in persistent
storage, using a basic columnar representation. This commit includes a default
storage interface based on plain files, but the interface can be implemented
with more structured storage if we want to later.

The tests are set up to require that all the fields of the NetworkMap are
handled, except those explicitly designated as not-cached, and check that a
fully-populated value can round-trip correctly through the cache.  Adding or
removing fields, either in the NetworkMap or in the cached representation, will
trigger either build failures (e.g., for type mismatch) or test failures (e.g.,
for representation changes or missing fields). This isn't quite as nice as
automatically updating the representation, which I also prototyped, but is much
simpler to maintain and less code.

This commit does not yet hook up the cache to the backend, that will be a
subsequent change.

Updates #12639

Change-Id: Icb48639e1d61f2aec59904ecd172c73e05ba7bf9
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2026-01-26 14:55:30 -08:00
M. J. FrombergerandGitHub ce12863ee5 ipn/ipnlocal: manage per-profile subdirectories in TailscaleVarRoot (#18485)
In order to better manage per-profile data resources on the client, add methods
to the LocalBackend to support creation of per-profile directory structures in
local storage. These methods build on the existing TailscaleVarRoot config, and
have the same limitation (i.e., if no local storage is available, it will
report an error when used).

The immediate motivation is to support netmap caching, but we can also use this
mechanism for other per-profile resources including pending taildrop files and
Tailnet Lock authority caches.

This commit only adds the directory-management plumbing; later commits will
handle migrating taildrop, TKA, etc. to this mechanism, as well as caching
network maps.

Updates #12639

Change-Id: Ia75741955c7bf885e49c1ad99f856f669a754169
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2026-01-23 10:09:46 -08:00
M. J. FrombergerandGitHub ab4b990d51 net/netmon: do not abandon a subscriber when exiting early (#17899)
LinkChangeLogLimiter keeps a subscription to track rate limits for log
messages.  But when its context ended, it would exit the subscription loop,
leaving the subscriber still alive. Ensure the subscriber gets cleaned up
when the context ends, so we don't stall event processing.

Updates tailscale/corp#34311

Change-Id: I82749e482e9a00dfc47f04afbc69dd0237537cb2
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-11-14 11:57:41 -08:00
M. J. FrombergerandGitHub 4c856078e4 util/eventbus: block for the subscriber during SubscribeFunc close (#17642)
Prior to this change a SubscriberFunc treated the call to the subscriber's
function as the completion of delivery. But that means when we are closing the
subscriber, that callback could continue to execute for some time after the
close returns.

For channel-based subscribers that works OK because the close takes effect
before the subscriber ever sees the event. To make the two subscriber types
symmetric, we should also wait for the callback to finish before returning.
This ensures that a Close of the client means the same thing with both kinds of
subscriber.

Updates #17638

Change-Id: I82fd31bcaa4e92fab07981ac0e57e6e3a7d9d60b
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-31 09:58:09 -07:00
M. J. FrombergerandGitHub 061e6266cf util/eventbus: allow logging of slow subscribers (#17705)
Add options to the eventbus.Bus to plumb in a logger.

Route that logger in to the subscriber machinery, and trigger a log message to
it when a subscriber fails to respond to its delivered events for 5s or more.

The log message includes the package, filename, and line number of the call
site that created the subscription.

Add tests that verify this works.

Updates #17680

Change-Id: I0546516476b1e13e6a9cf79f19db2fe55e56c698
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-30 14:40:57 -07:00
M. J. FrombergerandGitHub 95426b79a9 logtail: avoid racing eventbus subscriptions with shutdown (#17695)
In #17639 we moved the subscription into NewLogger to ensure we would not race
subscribing with shutdown of the eventbus client. Doing so fixed that problem,
but exposed another: As we were only servicing events occasionally when waiting
for the network to come up, we could leave the eventbus to stall in cases where
a number of network deltas arrived later and weren't processed.

To address that, let's separate the concerns: As before, we'll Subscribe early
to avoid conflicts with shutdown; but instead of using the subscriber directly
to determine readiness, we'll keep track of the last-known network state in a
selectable condition that the subscriber updates for us.  When we want to wait,
we'll wait on that condition (or until our context ends), ensuring all the
events get processed in a timely manner.

Updates #17638
Updates #15160

Change-Id: I28339a372be4ab24be46e2834a218874c33a0d2d
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-30 08:18:32 -07:00
M. J. FrombergerandGitHub 06b092388e ipn/ipnlocal: do not stall event processing for appc route updates (#17663)
A follow-up to #17411. Put AppConnector events into a task queue, as they may
take some time to process. Ensure that the queue is stopped at shutdown so that
cleanup will remain orderly.

Because events are delivered on a separate goroutine, slow processing of an
event does not cause an immediate problem; however, a subscriber that blocks
for a long time will push back on the bus as a whole. See
https://godoc.org/tailscale.com/util/eventbus#hdr-Expected_subscriber_behavior
for more discussion.

Updates #17192
Updates #15160

Change-Id: Ib313cc68aec273daf2b1ad79538266c81ef063e3
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-29 08:37:19 -07:00
M. J. FrombergerandGitHub 09a2a1048d derp: fix an unchecked error in a test (#17694)
Found by staticcheck, the test was calling derphttp.NewClient but not checking
its error result before doing other things to it.

Updates #cleanup

Change-Id: I4ade35a7de7c473571f176e747866bc0ab5774db
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-28 14:20:19 -07:00
M. J. FrombergerandGitHub db5815fb97 Revert "logtail: avoid racing eventbus subscriptions with Shutdown (#17639)" (#17684)
This reverts commit 4346615d77.
We averted the shutdown race, but will need to service the subscriber even when
we are not waiting for a change so that we do not delay the bus as a whole.

Updates #17638

Change-Id: I5488466ed83f5ad1141c95267f5ae54878a24657
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-28 08:45:22 -07:00
M. J. FrombergerandGitHub 4346615d77 logtail: avoid racing eventbus subscriptions with Shutdown (#17639)
When the eventbus is enabled, set up the subscription for change deltas at the
beginning when the client is created, rather than waiting for the first
awaitInternetUp check.

Otherwise, it is possible for a check to race with the client close in
Shutdown, which triggers a panic.

Updates #17638

Change-Id: I461c07939eca46699072b14b1814ecf28eec750c
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-24 14:08:47 -07:00
M. J. FrombergerandGitHub 3dde233cd3 ipn/ipnlocal: use eventbus.SubscribeFunc in LocalBackend (#17524)
This does not change which subscriptions are made, it only swaps them to use
the SubscribeFunc API instead of Subscribe.

Updates #15160
Updates #17487

Change-Id: Id56027836c96942206200567a118f8bcf9c07f64
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-20 12:22:16 -07:00
M. J. FrombergerandGitHub 0a33aae823 util/eventbus: run subscriber functions in a goroutine (#17510)
With a channel subscriber, the subscription processing always occurs on another
goroutine. The SubscriberFunc (prior to this commit) runs its callbacks on the
client's own goroutine. This changes the semantics, though: In addition to more
directly pushing back on the publisher, a publisher and subscriber can deadlock
in a SubscriberFunc but succeed on a Subscriber. They should behave
equivalently regardless which interface they use.

Arguably the caller should deal with this by creating its own goroutine if it
needs to. However, that loses much of the benefit of the SubscriberFunc API, as
it will need to manage the lifecycle of that goroutine. So, for practical
ergonomics, let's make the SubscriberFunc do this management on the user's
behalf. (We discussed doing this in #17432, but decided not to do it yet).  We
can optimize this approach further, if we need to, without changing the API.

Updates #17487

Change-Id: I19ea9e8f246f7b406711f5a16518ef7ff21a1ac9
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-10 09:03:38 -07:00
M. J. FrombergerandGitHub 154d36f73d wgengine/magicsock: do not apply node view updates to a closed Conn (#17517)
Fixes #17516

Change-Id: Iae2dab42d6f7bc618478d360a1005537c1fa1bbd
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-09 23:58:03 -07:00
M. J. FrombergerandM. J. Fromberger 109cb50d5f ipn/ipnlocal: use eventbus.SubscribeFunc in expiryManager
Updates #15160
Updates #17487

Change-Id: I8721e3ac1af505630edca7c5cb50695b0aad832a
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-08 08:00:42 -07:00
M. J. FrombergerandM. J. Fromberger 241ea1c98b wgengine/magicsock: use eventbus.SubscribeFunc in Conn
Updates #15160
Updates #17487

Change-Id: Ic9eb8d82b21d9dc38cb3c681b87101dfbc95af16
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-08 08:00:42 -07:00
M. J. FrombergerandM. J. Fromberger 5833730577 wgengine/router: use eventbus.SubscribeFunc in linuxRouter
Updates #15160
Updates #17487

Change-Id: Ib798e2321e55a078c8bd37f366fe4e73054e4520
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-08 08:00:42 -07:00
M. J. FrombergerandM. J. Fromberger 2a3d67e9b7 wgengine: use eventbus.SubscribeFunc in userspaceEngine
Updates #15160
Updates #17487

Change-Id: Id852098c4f9c2fdeab9151b0b8c14dceff73b99d
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-08 08:00:42 -07:00
M. J. FrombergerandGitHub ad6cf2f8f3 util/eventbus: add a function-based subscriber type (#17432)
Originally proposed by @bradfitz in #17413.

In practice, a lot of subscribers have only one event type of interest, or a
small number of mostly independent ones. In that case, the overhead of running
and maintaining a goroutine to select on multiple channels winds up being more
noisy than we'd like for the user of the API.

For this common case, add a new SubscriberFunc[T] type that delivers events to
a callback owned by the subscriber, directly on the goroutine belonging to the
client itself. This frees the consumer from the need to maintain their own
goroutine to pull events from the channel, and to watch for closure of the
subscriber.

Before:

     s := eventbus.Subscribe[T](eventClient)
     go func() {
       for {
          select {
          case <-s.Done():
            return
          case e := <-s.Events():
            doSomethingWith(e)
          }
       }
     }()
     // ...
     s.Close()

After:

     func doSomethingWithT(e T) { ... }
     s := eventbus.SubscribeFunc(eventClient, doSomethingWithT)
     // ...
     s.Close()

Moreover, unless the caller wants to explicitly stop the subscriber separately
from its governing client, it need not capture the SubscriberFunc value at all.

One downside of this approach is that a slow or deadlocked callback could block
client's service routine and thus stall all other subscriptions on that client,
However, this can already happen more broadly if a subscriber fails to service
its delivery channel in a timely manner, it just feeds back more immediately.

Updates #17487

Change-Id: I64592d786005177aa9fd445c263178ed415784d5
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-07 16:43:22 -07:00
M. J. FrombergerandGitHub 0415a56b6c ipn/ipnlocal: fix another racy test (#17472)
Some of the test cases access fields of the backend that are supposed to be
locked while the test is running, which can trigger the race detector.  I fixed
a few of these in #17411, but I missed these two cases.

Updates #15160
Updates #17192

Change-Id: I45664d5e34320ecdccd2844e0f8b228145aaf603
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-06 20:59:47 -07:00
M. J. FrombergerandGitHub e0f222b686 appc,ipn/ipnlocal: receive AppConnector updates via the event bus (#17411)
Add subscribers for AppConnector events

Make the RouteAdvertiser interface optional We cannot yet remove it because
the tests still depend on it to verify correctness. We will need to separately
update the test fixtures to remove that dependency.

Publish RouteInfo via the event bus, so we do not need a callback to do that. 
Replace it with a flag that indicates whether to treat the route info the connector 
has as "definitive" for filtering purposes.

Update the tests to simplify the construction of AppConnector values now that a
store callback is no longer required. Also fix a couple of pre-existing racy tests that 
were hidden by not being concurrent in the same way production is.

Updates #15160
Updates #17192

Change-Id: Id39525c0f02184e88feaf0d8a3c05504850e47ee
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-06 15:04:17 -07:00
M. J. FrombergerandGitHub 127a967207 appc,*: publish events for route updates and storage (#17392)
Add and wire up event publishers for these two event types in the AppConnector.
Nothing currently subscribes to them, so this is harmless. Subscribers for
these events will be added in a near-future commit.

As part of this, move the appc.RouteInfo type to the types/appctype package.
It does not contain any package-specific details from appc. Beside it, add
appctype.RouteUpdate to carry route update event state, likewise not specific
to appc.  Update all usage of the appc.* types throughout to use appctype.*
instead, and update depaware files to reflect these changes.

Add a Close method to the AppConnector to make sure the client gets cleaned up
when the connector is dropped (we re-create connectors).

Update the unit tests in the appc package to also check the events published
alongside calls to the RouteAdvertiser.

For now the tests still rely on the RouteAdvertiser for correctness; this is OK
for now as the two methods are always performed together.  In the near future,
we need to rework the tests so not require that, but that will require building
some more test fixtures that we can handle separately.

Updates #15160
Updates #17192

Change-Id: I184670ba2fb920e0d2cb2be7c6816259bca77afe
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-02 09:31:42 -07:00
M. J. FrombergerandGitHub 3c32f87624 feature/relayserver: use eventbus.Monitor to simplify lifecycle management (#17234)
Instead of using separate channels to manage the lifecycle of the eventbus
client, use the recently-added eventbus.Monitor, which handles signaling the
processing loop to stop and waiting for it to complete.  This allows us to
simplify some of the setup and cleanup code in the relay server.

Updates #15160

Change-Id: Ia1a47ce2e5a31bc8f546dca4c56c3141a40d67af
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-02 09:18:55 -07:00
M. J. FrombergerandGitHub 67f1081269 appc,ipn/ipnlocal: add a required event bus to the AppConnector type (#17390)
Require the presence of the bus, but do not use it yet.  Check for required
fields and update tests and production use to plumb the necessary arguments.

Updates #15160
Updates #17192

Change-Id: I8cefd2fdb314ca9945317d3320bd5ea6a92e8dcb
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-01 12:00:32 -07:00
M. J. FrombergerandGitHub 6f7ce5eb5d appc: factor app connector arguments into a Config type (#17389)
Replace the positional arguments to NewAppConnector with a Config struct.
Update the existing uses. Other than the API change, there are no functional
changes in this commit.

Updates #15160
Updates #17192

Change-Id: Ibf37f021372155a4db8aaf738f4b4f2c746bf623
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-10-01 11:39:01 -07:00
M. J. FrombergerandGitHub df747f1c1b util/eventbus: add a Done method to the Monitor type (#17263)
Some systems need to tell whether the monitored goroutine has finished
alongside other channel operations (notably in this case the relay server, but
there seem likely to be others similarly situated).

Updates #15160

Change-Id: I5f0f3fae827b07f9b7102a3b08f60cda9737fe28
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-24 09:14:41 -07:00
M. J. FrombergerandGitHub daad5c2b5c wgengine/router: use eventbus.Monitor in linuxRouter (#17232)
This commit does not change the order or meaning of any eventbus activity, it
only updates the way the plumbing is set up.

Updates #15160

Change-Id: I61b863f9c05459d530a4c34063a8bad9046c0e27
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-22 10:49:28 -07:00
M. J. FrombergerandGitHub 1b5201023f ipn/ipnlocal: use eventbus.Monitor in LocalBackend (#17225)
This commit does not change the order or meaning of any eventbus activity, it
only updates the way the plumbing is set up.

Updates #15160

Change-Id: I06860ac4e43952a9bb4d85366138c9d9a17fd9cd
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-22 08:43:39 -07:00
M. J. FrombergerandGitHub e59fbaab64 util/eventbus: give a nicer error when attempting to use a closed client (#17208)
It is a programming error to Publish or Subscribe on a closed Client, but now
the way you discover that is by getting a panic from down in the machinery of
the bus after the client state has been cleaned up.

To provide a more helpful error, let's panic explicitly when that happens and
say what went wrong ("the client is closed"), by preventing subscriptions from
interleaving with closure of the client. With this change, either an attachment
fails outright (because the client is already closed) or completes and then
shuts down in good order in the normal course.

This does not change the semantics of the client, publishers, or subscribers,
it's just making the failure more eager so we can attach explanatory text.

Updates #15160

Change-Id: Ia492f4c1dea7535aec2cdcc2e5ea5410ed5218d2
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-22 07:07:57 -07:00
M. J. FrombergerandGitHub f9c699812a ipn/ipnlocal: use eventbus.Monitor in expiryManager (#17204)
This commit does not change the order or meaning of any eventbus activity, it
only updates the way the plumbing is set up.

Updates #15160

Change-Id: I0a175e67e867459daaedba0731bf68bd331e5ebc
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-19 14:31:55 -07:00
M. J. FrombergerandGitHub 2b6bc11586 wgengine: use eventbus.Client.Monitor to simplify subscriber maintenance (#17203)
This commit does not change the order or meaning of any eventbus activity, it
only updates the way the plumbing is set up.

Updates #15160

Change-Id: I40c23b183c2a6a6ea3feec7767c8e5417019fc07
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-19 13:20:50 -07:00
M. J. FrombergerandGitHub ca9d795006 util/eventbus: add a Monitor type to manage subscriber goroutines (#17127)
A common pattern in event bus usage is to run a goroutine to service a
collection of subscribers on a single bus client. To have an orderly shutdown,
however, we need a way to wait for such a goroutine to be finished.

This commit adds a Monitor type that makes this pattern easier to wire up:
rather than having to track all the subscribers and an extra channel, the
component need only track the client and the monitor.  For example:

   cli := bus.Client("example")
   m := cli.Monitor(func(c *eventbus.Client) {
     s1 := eventbus.Subscribe[T](cli)
     s2 := eventbus.Subscribe[U](cli)
     for {
       select {
       case <-c.Done():
         return
       case t := <-s1.Events():
          processT(t)
       case u := <-s2.Events():
          processU(u)
       }
     }
   })

To shut down the client and wait for the goroutine, the caller can write:

   m.Close()

which closes cli and waits for the goroutine to finish. Or, separately:

   cli.Close()
   // do other stuff
   m.Wait()

While the goroutine management is not explicitly tied to subscriptions, it is a
common enough pattern that this seems like a useful simplification in use.

Updates #15160

Change-Id: I657afda1cfaf03465a9dce1336e9fd518a968bca
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-19 12:34:06 -07:00
M. J. FrombergerandGitHub fc9a74a405 util/eventbus: fix flakes in eventbustest tests (#17198)
When tests run in parallel, events from multiple tests on the same bus can
intercede with each other. This is working as intended, but for the test cases
we want to control exactly what goes through the bus.

To fix that, allocate a fresh bus for each subtest.

Fixes #17197

Change-Id: I53f285ebed8da82e72a2ed136a61884667ef9a5e
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-19 07:56:45 -07:00
M. J. FrombergerandGitHub 4f211ea5c5 util/eventbus: add a LogAllEvents helper for testing (#17187)
When developing (and debugging) tests, it is useful to be able to see all the
traffic that transits the event bus during the execution of a test.

Updates #15160

Change-Id: I929aee62ccf13bdd4bd07d786924ce9a74acd17a
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-18 12:44:06 -07:00
M. J. FrombergerandGitHub 6992f958fc util/eventbus: add an EqualTo helper for testing (#17178)
For a common case of events being simple struct types with some exported
fields, add a helper to check (reflectively) for equal values using cmp.Diff so
that a failed comparison gives a useful diff in the test output.

More complex uses will still want to provide their own comparisons; this
(intentionally) does not export diff options or other hooks from the cmp
package.

Updates #15160

Change-Id: I86bee1771cad7debd9e3491aa6713afe6fd577a6
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-17 08:39:29 -07:00
M. J. FrombergerandGitHub 48029a897d util/eventbus: allow test expectations reporting only an error (#17146)
Extend the Expect method of a Watcher to allow filter functions that report
only an error value, and which "pass" when the reported error is nil.

Updates #15160

Change-Id: I582d804554bd1066a9e499c1f3992d068c9e8148
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-17 07:20:34 -07:00
M. J. FrombergerandGitHub 8608e42103 feature,ipn/ipnlocal,wgengine: improve how eventbus shutdown is handled (#17156)
Instead of waiting for a designated subscription to close as a canary for the
bus being stopped, use the bus Client's own signal for closure added in #17118.

Updates #cleanup

Change-Id: I384ea39f3f1f6a030a6282356f7b5bdcdf8d7102
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-16 10:52:39 -07:00
M. J. FrombergerandGitHub 5b5ae2b2ee util/eventbus: add a Done channel to the Client (#17118)
Subscribers already have a Done channel that the caller can use to detect when
the subscriber has been closed. Typically this happens when the governing
Client closes, which in turn is typically because the Bus closed.

But clients and subscribers can stop at other times too, and a caller has no
good way to tell the difference between "this subscriber closed but the rest
are OK" and "the client closed and all these subscribers are finished".

We've worked around this in practice by knowing the closure of one subscriber
implies the fate of the rest, but we can do better: Add a Done method to the
Client that allows us to tell when that has been closed explicitly, after all
the publishers and subscribers associated with that client have been closed.
This allows the caller to be sure that, by the time that occurs, no further
pending events are forthcoming on that client.

Updates #15160

Change-Id: Id601a79ba043365ecdb47dd035f1fdadd984f303
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-09-16 07:44:08 -07:00
M. J. FrombergerandGitHub 9e9bf13063 ipn/ipnlocal: revert some locking changes ahead of release branch cut (#17011) 2025-09-02 15:57:31 -07:00
M. J. FrombergerandM. J. Fromberger b411ffb52f ipn/ipnlocal: remove UnlockEarly from doSetHostinfoFilterServices
Pull the lock-bearing code into a closure, and use a clone rather than a
shallow copy of the hostinfo record.

Updates #11649

Change-Id: I4f1d42c42ce45e493b204baae0d50b1cbf82b102
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-08-25 11:26:26 -07:00
M. J. FrombergerandM. J. Fromberger 9002e5fd6b ipn/ipnlocal: remove an unnecessary unlock shortcut
The early unlock on this branch was required because the "send" method goes on
to acquire the mutex itself. Rather than release the lock just to acquire it
again, call the underlying locked helper directly.

Updates #11649

Change-Id: I50d81864a00150fc41460b7486a9c65655f282f5
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-08-25 11:26:26 -07:00
M. J. FrombergerandM. J. Fromberger 2fb9472990 ipn/ipnlocal: remove unnecessary usees of lockAndGetUnlock
In places where we are locking the LocakBackend and immediately deferring an
unlock, and where there is no shortcut path in the control flow below the
deferral, we do not need the unlockOnce helper. Replace all these with use of
the lock directly.

Updates #11649

Change-Id: I3e6a7110dfc9ec6c1d38d2585c5367a0d4e76514
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-08-25 11:26:26 -07:00
M. J. FrombergerandGitHub 16bd60f9ca ipn,tsnet: update AdvertiseTags documentation (#16931)
Instead of referring to groups, which is a term of art for a different entity,
update the doc comments to more accurately describe what tags are in reference
to the policy document.

Updates #cleanup

Change-Id: Iefff6f84981985f834bae7c6a6c34044f53f2ea2
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-08-23 08:07:36 -07:00
M. J. FrombergerandGitHub 6c8fef961e ipn/ipnlocal: replace the LockedOnEntry pattern with conventional lock/unlock discipline (#16925)
There are several methods within the LocalBackend that used an unusual and
error-prone lock discipline whereby they require the caller to hold the backend
mutex on entry, but release it on the way out.

In #11650 we added some support code to make this pattern more visible.
Now it is time to eliminate the pattern (at least within this package).
This is intended to produce no semantic changes, though I am relying on
integration tests and careful inspection to achieve that.

To the extent possible I preserved the existing control flow. In a few places,
however, I replaced this with an unlock/lock closure. This means we will
sometimes reacquire a lock only to release it again one frame up the stack, but
these operations are not performance sensitive and the legibility gain seems
worthwhile.

We can probably also pull some of these out into separate methods, but I did
not do that here so as to avoid other variable scope changes that might be hard
to see. I would like to do some more cleanup separately.

As a follow-up, we could also remove the unlockOnce helper, but I did not do
that here either.

Updates #11649

Change-Id: I4c92d4536eca629cfcd6187528381c33f4d64e20
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-08-23 02:07:22 -07:00
M. J. FrombergerandGitHub 55698c8511 ipn/localapi: plumb an event bus through the localapi.Handler (#16892)
Some of the operations of the local API need an event bus to correctly
instantiate other components (notably including the portmapper).

This commit adds that, and as the parameter list is starting to get a bit long
and hard to read, I took the opportunity to move the arguments to a config
type. Only a few call sites needed to be updated and this API is not intended
for general use, so I did not bother to stage the change.

Updates #15160
Updates #16842

Change-Id: I7b057d71161bd859f5acb96e2f878a34c85be0ef
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-08-18 10:56:17 -07:00
M. J. FrombergerandGitHub ee0c7b05a5 cmd/tailscale: fix a panic in netcheck portmapper construction (#16843)
This affects the 1.87.33 unstable release.

Updates #16842
Updates #15160

Change-Id: Ie6d1b2c094d1a6059fbd1023760567900f06e0ad
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-08-12 10:19:33 -07:00
M. J. FrombergerandGitHub b34cdc9710 ipn,net,tsnet,wgengine: make an eventbus mandatory where it is used (#16594)
In the components where an event bus is already plumbed through, remove the
exceptions that allow it to be omitted, and update all the tests that relied on
those workarounds execute properly.

This change applies only to the places where we're already using the bus; it
does not enforce the existence of a bus in other components (yet),

Updates #15160

Change-Id: Iebb92243caba82b5eb420c49fc3e089a77454f65
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-07-29 09:04:08 -07:00
M. J. FrombergerandGitHub 5ce3845a02 net/portmapper: avert a panic when a mapping is not available (#16686)
Ideally when we attempt to create a new port mapping, we should not return
without error when no mapping is available. We already log these cases as
unexpected, so this change is just to avoiding panicking dispatch on the
invalid result in those cases. We still separately need to fix the underlying
control flow.

Updates #16662

Change-Id: I51e8a116b922b49eda45e31cd27f6b89dd51abc8

Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-07-28 09:01:41 -07:00
M. J. FrombergerandGitHub 67514f5eb2 ssh/tailssh: fix path of "true" on Darwin (#16569)
This is a follow-up to #15351, which fixed the test for Linux but not for
Darwin, which stores its "true" executable in /usr/bin instead of /bin.
Try both paths when not running on Windows.

In addition, disable CGo in the integration test build, which was causing the
linker to fail. These tests do not need CGo, and it appears we had some version
skew with the base image on the runners.

In addition, in error cases the recover step of the permissions check was
spuriously panicking and masking the "real" failure reason. Don't do that check
when a command was not produced.

Updates #15350

Change-Id: Icd91517f45c90f7554310ebf1c888cdfd109f43a
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-07-16 08:08:59 -07:00
M. J. FrombergerandGitHub fe391d5694 client/local: use an iterator to stream bus events (#16269)
This means the caller does not have to remember to close the reader, and avoids
having to duplicate the logic to decode JSON into events.

Updates #15160

Change-Id: I20186fabb02f72522f61d5908c4cc80b86b8936b
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-06-13 15:47:35 -07:00
M. J. FrombergerandGitHub 36df320e6a tsnet: remove an expired configuration-path migration step (#16120)
As note in the comment, it now being more than six months since this was
deprecated and there being no (further) uses of the old pattern in our internal
services, let's drop the migrator.

Updates #cleanup

Change-Id: Ie4fb9518b2ca04a9b361e09c51cbbacf1e2633a8
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-05-28 14:12:24 -07:00
M. J. FrombergerandM. J. Fromberger dda2c0d2c2 wgengine/magicsock: subscribe to portmapper updates
When an event bus is plumbed in, use it to subscribe and react to port mapping
updates instead of using the client's callback mechanism. For now, the callback
remains available as a fallback when an event bus is not provided.

Updates #15160

Change-Id: I026adca44bf6187692ee87ae8ec02641c12f7774
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-04-16 10:10:45 -07:00
M. J. FrombergerandM. J. Fromberger 2731171c5e net/portmapper: fire an event when a port mapping is updated (#15371)
When an event bus is configured publish an event each time a new port mapping
is updated. Publication is unconditional and occurs prior to calling any
callback that is registered. For now, the callback is still fired in a separate
goroutine as before -- later, those callbacks should become subscriptions to
the published event.

For now, the event type is defined as a new type here in the package. We will
want to move it to a more central package when there are subscribers. The event
wrapper is effectively a subset of the data exported by the internal mapping
interface, but on a concrete struct so the bus plumbing can inspect it.

Updates #15160

Change-Id: I951f212429ac791223af8d75b6eb39a0d2a0053a
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-04-16 10:10:45 -07:00
M. J. FrombergerandM. J. Fromberger deb0b255ff all: update the tsd.System constructor name (#15372)
Replace NewSystemWithEventBus with plain NewSystem, and update all usage.
See https://github.com/tailscale/tailscale/pull/15355#discussion_r2003910766

Updates #15160

Change-Id: I64d337f09576b41d9ad78eba301a74b9a9d6ebf4
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-04-16 10:10:45 -07:00
M. J. FrombergerandM. J. Fromberger baead61e44 {wgengine,util/portmapper}: add and plumb an event bus (#15359)
Updates #15160

Change-Id: I2510fb4a8905fb0abe8a8e0c5b81adb15d50a6f8
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-04-16 10:10:45 -07:00
M. J. FrombergerandM. J. Fromberger 418e19fb5e portmapper: update NewClient to use a Config argument
In preparation for adding more parameters (and later, moving some away), rework
the portmapper constructor to accept its arguments on a Config struct rather
than positionally.

This is a breaking change to the function signature, but one that is very easy
to update, and a search of GitHub reveals only six instances of usage outside
clones and forks of Tailscale itself, that are not direct copies of the code
fixed up here.

While we could stub in another constructor, I think it is safe to let those
folks do the update in-place, since their usage is already affected by other
changes we can't test for anyway.

Updates #15160

Change-Id: I9f8a5e12b38885074c98894b7376039261b43f43
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-04-16 10:10:45 -07:00
M. J. FrombergerandM. J. Fromberger 2ac6e1edfd wgengine: plumb an event bus into the userspace engine
Updates #15160

Change-Id: Ia695ccdddd09cd950de22abd000d4c531d6bf3c8
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-04-16 10:10:45 -07:00
M. J. FrombergerandM. J. Fromberger bcd6a0d0ac tsnet: shut down the event bus on Close
Updates #15160

Change-Id: I29c8194b4b41e95848e5f160e9970db352588449
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-04-16 10:10:45 -07:00
M. J. FrombergerandM. J. Fromberger ffb22ee353 all: construct new System values with an event bus pre-populated
Although, at the moment, we do not yet require an event bus to be present, as
we start to add more pieces we will want to ensure it is always available.  Add
a new constructor and replace existing uses of new(tsd.System) throughout.
Update generated files for import changes.

Updates #15160

Change-Id: Ie5460985571ade87b8eac8b416948c7f49f0f64b
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-04-16 10:10:45 -07:00
M. J. FrombergerandGitHub 0663412559 util/eventbus: add basic throughput benchmarks (#15284)
Shovel small events through the pipeine as fast as possible in a few basic
configurations, to establish some baseline performance numbers.

Updates #15160

Change-Id: I1dcbbd1109abb7b93aa4dcb70da57f183eb0e60e
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2025-03-13 08:06:20 -07:00
M. J. FrombergerandGitHub 6ff85846bc safeweb: add a Shutdown method to the Server type (#14048)
Updates #14047

Change-Id: I2d20454c715b11ad9c6aad1d81445e05a170c3a2
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2024-11-08 10:02:16 -08:00
M. J. FrombergerandGitHub 5f89c93274 safeweb: add a ListenAndServe method to the Server type (#13498)
Updates #13497

Change-Id: I398e9fa58ad0b9dc799ea280c9c7a32150150ee4
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2024-09-17 12:59:28 -07:00
M. J. FrombergerandGitHub 9e24a6508a control/controlclient: avert a data race when logging (#8863)
The read of the synced field for logging takes place outside the lock, and
races with other (locked) writes of this field, including for example the one
at current line 556 in mapRoutine.

Updates tailscale/corp#13856

Change-Id: I056b36d7a93025aafdf73528dd7645f10b791af6
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2023-08-11 18:49:40 -07:00
M. J. FrombergerandGitHub 459744c9ea .github: mark bots for exemption by issuebot (#8041)
#cleanup

Change-Id: I13757ee20240166af72606d166e840ae3ee797d7
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2023-05-03 18:36:19 -07:00
M. J. FrombergerandGitHub c09578d060 .github: update tibdex/github-app-token to release v1.8.0 (#7495)
The main motivation for this change is to stop using the deprecated
set-output function which triggers deprecation warnings in the action.

Change-Id: I80496c44ea1166b9c40d5cd9e450129778ad4aaf
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2023-03-07 14:30:19 -08:00
M. J. FrombergerandGitHub a75360ccd6 util: add truncate package (#7490)
This package handles cases where we need to truncate human-readable text to fit
a length constraint without leaving "ragged" multi-byte rune fragments at the
end of the truncated value.

Change-Id: Id972135d1880485f41b1fedfb65c2b8cc012d416
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2023-03-07 11:51:36 -08:00
M. J. FrombergerandGitHub 9be47f789c ipn/ipnlocal: fix the path for writing cert files (#7203)
Fixes #7202.

Change-Id: I1f8e9c59d5e42e7df7a3fbbd82ae2b4293845916
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2023-02-07 14:34:04 -08:00
M. J. FrombergerandGitHub 3eae75b1b8 ci: make gofmt check fail for a non-empty diff (#7131)
Fixes #7130.

Change-Id: If47eb472ea98a8d8b250c5c681c7862d252645fb
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
2023-01-31 11:16:39 -08:00