ipn: enforce lossless IPN bus delta streams

New-style IPN bus subscribers consume stateful delta streams. Reject
NotifyRateLimit when it is combined with those subscription bits so
tailscaled cannot merge or delay messages that clients need to apply in
order.

Also stop silently dropping notifications when a watcher falls behind.
Remove the watcher, replace its stale queue with one terminal ErrMessage
notification, and close the watch.

Updates #20062

Change-Id: Id9d402ea76f4011cd23f122adf62f30dd4b6f90b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2026-06-09 11:12:20 -07:00
committed by Brad Fitzpatrick
parent 60b935e30f
commit edcc2c94d9
8 changed files with 206 additions and 25 deletions
+19
View File
@@ -158,6 +158,25 @@ const (
NotifyPeerPatches NotifyWatchOpt = 1 << 15
)
// NotifyRateLimitIncompatibleBits is the set of new-style IPN bus
// subscription bits that cannot be combined with [NotifyRateLimit].
//
// Those bits describe stateful delta streams. Randomly delaying or merging
// messages in those streams would break the consumer's ability to maintain a
// coherent local view.
const NotifyRateLimitIncompatibleBits = NotifyPeerChanges | NotifyNoNetMap | NotifyInitialStatus | NotifyPeerPatches
// ValidateNotifyWatchOpt reports whether mask is a valid WatchIPNBus
// subscription mask.
func ValidateNotifyWatchOpt(mask NotifyWatchOpt) error {
if mask&NotifyRateLimit != 0 {
if bad := mask & NotifyRateLimitIncompatibleBits; bad != 0 {
return fmt.Errorf("NotifyRateLimit is incompatible with new-style IPN bus subscription bits %v", bad)
}
}
return nil
}
// Notify is a communication from a backend (e.g. tailscaled) to a frontend
// (cmd/tailscale, iOS, macOS, Win Tasktray).
// In any given notification, any or all of these may be nil, meaning