From 73bc04d4e2ced371779adcd93b2c1125264d0913 Mon Sep 17 00:00:00 2001 From: Eric Li Date: Wed, 30 Sep 2026 10:31:26 -0400 Subject: [PATCH] fix: fix seqno spam --- core/nylon_apply.go | 9 ++++ integration/apply_config_test.go | 70 ++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/core/nylon_apply.go b/core/nylon_apply.go index 3ed33ed9..c9b0e55e 100644 --- a/core/nylon_apply.go +++ b/core/nylon_apply.go @@ -109,6 +109,15 @@ func (n *Nylon) reconcileRouterState(next *state.CentralCfg) error { } neighs = append(neighs, stNeigh) } + // drop routes for removed prefixes so we stop requesting seqnos for them + prefixes := next.GetPrefixes() + for _, neigh := range neighs { + for prefix := range neigh.Routes { + if !slices.Contains(prefixes, prefix) { + delete(neigh.Routes, prefix) + } + } + } n.RouterState.Neighbours = neighs if n.EndpointResolver != nil { addresses := make(map[string]struct{}) diff --git a/integration/apply_config_test.go b/integration/apply_config_test.go index 84cc2112..ac48e13c 100644 --- a/integration/apply_config_test.go +++ b/integration/apply_config_test.go @@ -200,6 +200,76 @@ func TestApplyCentralConfigRotatesPeerKeyWithoutChangingNextHop(t *testing.T) { waitForPayload(t, vh, errs, received, 2) } +func TestApplyCentralConfigRemovedPrefixStopsSeqnoRequests(t *testing.T) { + defer goleak.VerifyNone(t) + + vh := &VirtualHarness{} + a1 := "192.168.40.1:1234" + vh.NewNode("a", "10.0.0.1/32") + b1 := "192.168.40.2:1234" + vh.NewNode("b", "10.0.0.2/32") + vh.Central.Graph = []string{"a, b"} + vh.Endpoints = map[string]state.NodeId{ + a1: "a", + b1: "b", + } + vh.AddLink(a1, b1) + vh.AddLink(b1, a1) + removed := netip.MustParsePrefix("10.1.0.2/32") + bi := vh.IndexOf("b") + vh.Central.Routers[bi].Prefixes = append(vh.Central.Routers[bi].Prefixes, state.PrefixHealthWrapper{ + PrefixHealth: &state.StaticPrefixHealth{Prefix: removed}, + }) + + errs := vh.Start() + defer vh.Stop() + + a := vh.Nylons[vh.IndexOf("a")].Load() + b := vh.Nylons[bi].Load() + neighHasRoute := func() bool { + res := make(chan bool, 1) + a.Dispatch(func() error { + route, ok := a.RouterState.GetNeighbour("b").Routes[removed] + res <- ok && route.Metric != state.INF + return nil + }) + select { + case ok := <-res: + return ok + case err := <-errs: + t.Fatal(err) + case <-time.After(5 * time.Second): + t.Fatal("timed out waiting for dispatch") + } + return false + } + deadline := time.Now().Add(10 * time.Second) + for !neighHasRoute() { + if time.Now().After(deadline) { + t.Fatal("timed out waiting for a to learn the prefix") + } + time.Sleep(100 * time.Millisecond) + } + + err, next := vh.Central.Clone() + assert.NoError(t, err) + next.Timestamp++ + next.Routers[bi].Prefixes = next.Routers[bi].Prefixes[:1] + applyConfigAndWait(t, b, next) + applyConfigAndWait(t, a, next) + + // stale route keeps a requesting seqnos until it expires + done := make(chan struct{}) + var stale bool + a.Dispatch(func() error { + _, stale = a.RouterState.GetNeighbour("b").Routes[removed] + close(done) + return nil + }) + <-done + assert.False(t, stale) +} + func applyConfigAndWait(t *testing.T, n *core.Nylon, cfg *state.CentralCfg) { t.Helper() done := make(chan struct{})