Hello,

On BIRD 3.3.2 a static route with an interface or neighbour next hop
stops reacting to interface state after the first `birdc configure`. It
is frozen at whatever state that reconfiguration evaluated and never
moves again -- it neither withdraws when the interface goes down nor
returns when it comes back -- until `birdc restart <proto>`.
`configure soft` and `configure undo` behave the same, and
`birdc reload` does not repair it. `check link` makes no difference
either way.

The attached reproducer, bird-repro-static-iface-tracking.sh, needs only
root, iproute2 and an installed bird3, cleans up after itself, and marks
any step whose result differs from expectation. On Debian's bird3
3.3.2-1~bpo13+1:

BIRD version 3.3.2
  fresh: iface up          route=yes want=yes
  fresh: iface down     route=no want=no
  fresh: iface up          route=yes want=yes

  -- birdc configure, configuration unchanged --
  after cfg: iface up       route=yes want=yes
  after cfg: iface down  route=yes want=no  <-- MISMATCH
  after cfg: iface up      route=yes want=yes

  -- birdc restart s1 --
  after restart: down    route=no want=no

  -- birdc configure, taken while the iface is DOWN --
  still down                route=no want=no
  then: iface up         route=no want=yes  <-- MISMATCH
  then: iface down     route=no want=no

The same script on the same machine, with BIRD= and BIRDC= pointing at
Debian's bird2 2.17.5-0+deb13u1:

BIRD version 2.17.5
  fresh: iface up           route=yes want=yes
  fresh: iface down      route=no want=no
  fresh: iface up           route=yes want=yes

  -- birdc configure, configuration unchanged --
  after cfg: iface up       route=yes want=yes
  after cfg: iface down  route=no want=no
  after cfg: iface up      route=yes want=yes

  -- birdc restart s1 --
  after restart: down    route=no want=no

  -- birdc configure, taken while the iface is DOWN --
  still down                 route=no want=no
  then: iface up          route=yes want=yes
  then: iface down      route=no want=no

Where it seems to come from: the dependency is the route chain hanging
off the neighbour-cache entry. `static_reconfigure_rte()` calls
`static_add_rte()`, which links the new route into `n->data`, and then
`static_reset_rte()`, which sets `r2->neigh->data = NULL`.
`neigh_find()` returns the same cached neighbour for an unchanged next
hop, so the reset clears the chain the add has just built, and
`static_neigh_notify()` then walks an empty list.

That came in with commit eb6918e4db57b7e5fabc531963deb46313289d80
("Neighbor cache: fixed neighbor referencing"), which replaced a plain
`r2->neigh = NULL;` in `static_reset_rte` with a block that also clears
the shared `r2->neigh->data`, and added the matching `neigh_link()` in
`static_add_rte`. The commit is not reachable from master, no v2 tag
contains it, and the earliest release tag that does is v3.0.0 -- which
matches bird2 passing the run above.

Thanks,
Dan