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:
committed by
Brad Fitzpatrick
parent
60b935e30f
commit
edcc2c94d9
@@ -902,6 +902,10 @@ func (h *Handler) serveWatchIPNBus(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
mask = ipn.NotifyWatchOpt(v)
|
||||
}
|
||||
if err := ipn.ValidateNotifyWatchOpt(mask); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// NotifyInitialNetMap is permitted alongside NotifyPeerChanges /
|
||||
// NotifyPeerPatches for backwards compatibility with clients that
|
||||
// set both (e.g. the Apple client). On platforms where
|
||||
|
||||
@@ -634,6 +634,7 @@ func TestServeWatchIPNBus(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
permitRead, permitWrite bool
|
||||
mask ipn.NotifyWatchOpt
|
||||
wantStatus int
|
||||
}{
|
||||
{
|
||||
@@ -654,6 +655,12 @@ func TestServeWatchIPNBus(t *testing.T) {
|
||||
permitWrite: true,
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "invalid-rate-limit-mask",
|
||||
permitRead: true,
|
||||
mask: ipn.NotifyRateLimit | ipn.NotifyPeerChanges,
|
||||
wantStatus: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -668,7 +675,11 @@ func TestServeWatchIPNBus(t *testing.T) {
|
||||
c := s.Client()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/localapi/v0/watch-ipn-bus?mask=%d", s.URL, ipn.NotifyInitialState), nil)
|
||||
mask := tt.mask
|
||||
if mask == 0 {
|
||||
mask = ipn.NotifyInitialState
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/localapi/v0/watch-ipn-bus?mask=%d", s.URL, mask), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user