]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Neighbor cache: fixed neighbor referencing
authorMaria Matejka <mq@ucw.cz>
Sun, 10 Nov 2024 12:33:22 +0000 (13:33 +0100)
committerMaria Matejka <mq@ucw.cz>
Thu, 14 Nov 2024 10:41:37 +0000 (11:41 +0100)
nest/iface.c
nest/iface.h
nest/neighbor.c
nest/proto.c
proto/bfd/bfd.c
proto/bgp/bgp.c
proto/rip/rip.c
proto/static/static.c

index 1059ae06d6accb57b2e15bcd77d287d7d4c2e979..3d1483ace3ad21f59722eba760e79fd01e66ac4f 100644 (file)
@@ -262,7 +262,7 @@ if_enqueue_notify_to(struct iface_notification x, struct iface_subscription *s)
       break;
     case IFNOT_NEIGHBOR:
       if (!s->neigh_notify) return;
-      neigh_link(x.n);
+      neigh_link_locked(x.n);
       break;
     default:
       bug("Unknown interface notification type: %d", x.type);
@@ -583,9 +583,7 @@ iface_notify_hook(void *_s)
        break;
       case IFNOT_NEIGHBOR:
        s->neigh_notify(n->n);
-       IFACE_LOCK;
        neigh_unlink(n->n);
-       IFACE_UNLOCK;
        break;
       default:
        bug("Bad interface notification type: %d", n->type);
@@ -658,8 +656,6 @@ iface_unsubscribe(struct iface_subscription *s)
   IFACE_LOCK;
 
   SKIP_BACK_DECLARE(struct proto, p, iface_sub, s);
-  WALK_TLIST_DELSAFE(proto_neigh, n, &p->neighbors)
-    neigh_unlink(n);
 
   ifsub_rem_node(&iface_sub_list, s);
   ev_postpone(&s->event);
@@ -676,7 +672,7 @@ iface_unsubscribe(struct iface_subscription *s)
        if_unlink(n->i);
        break;
       case IFNOT_NEIGHBOR:
-       neigh_unlink(n->n);
+       neigh_unlink_locked(n->n);
        break;
       default:
        bug("Bad interface notification type: %d", n->type);
@@ -686,6 +682,12 @@ iface_unsubscribe(struct iface_subscription *s)
     sl_free(n);
   }
 
+  WALK_TLIST_DELSAFE(proto_neigh, n, &p->neighbors)
+  {
+    log(L_WARN "%s: Unlinking forgotten neighbor %I", p->name, n->addr);
+    neigh_unlink_locked(n);
+  }
+
   ASSERT_DIE(EMPTY_TLIST(proto_neigh, &p->neighbors));
 
   IFACE_UNLOCK;
index a98bdf374f557a7cb2c00f703fd1ba19d310c175..699cffc2c969eb8224920750f8a676f6ab25dc81 100644 (file)
@@ -10,6 +10,7 @@
 #define _BIRD_IFACE_H_
 
 #include "lib/locking.h"
+#include "lib/defer.h"
 #include "lib/event.h"
 #include "lib/lists.h"
 #include "lib/tlists.h"
@@ -178,6 +179,27 @@ void neigh_init(struct pool *);
 void neigh_link(neighbor *);
 void neigh_unlink(neighbor *);
 
+struct neigh_unlink_deferred {
+  struct deferred_call dc;
+  neighbor *n;
+};
+
+void neigh_unlink_deferred(struct deferred_call *dc);
+
+static inline void neigh_unlink_later(neighbor *n)
+{
+  struct neigh_unlink_deferred nud = {
+    .dc.hook = neigh_unlink_deferred,
+    .n = n,
+  };
+
+  defer_call(&nud.dc, sizeof nud);
+}
+
+/* For internal use */
+void neigh_link_locked(neighbor *);
+void neigh_unlink_locked(neighbor *);
+
 /*
  *     Notification mechanism
  */
index 991ed0ead51e0f4fcf0d6f9b538c657534f6b370..257e61c4d669eedfa70598e0892e12a872694e78 100644 (file)
@@ -283,7 +283,8 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
   n->flags = flags;
   n->scope = scope;
 
-  neigh_link(n);
+  neigh_link_locked(n);
+  neigh_unlink_later(n);
 
   IFACE_UNLOCK;
   return n;
@@ -378,14 +379,14 @@ neigh_down(neighbor *n)
 }
 
 void
-neigh_link(neighbor *n)
+neigh_link_locked(neighbor *n)
 {
   IFACE_ASSERT_LOCKED;
   n->uc++;
 }
 
 void
-neigh_unlink(neighbor *n)
+neigh_unlink_locked(neighbor *n)
 {
   IFACE_ASSERT_LOCKED;
   if (--n->uc)
@@ -409,6 +410,27 @@ neigh_unlink(neighbor *n)
   sl_free(n);
 }
 
+void
+neigh_link(neighbor *n)
+{
+  IFACE_LOCK;
+  neigh_link_locked(n);
+  IFACE_UNLOCK;
+}
+
+void
+neigh_unlink(neighbor *n)
+{
+  IFACE_LOCK;
+  neigh_unlink_locked(n);
+  IFACE_UNLOCK;
+}
+
+void neigh_unlink_deferred(struct deferred_call *dc)
+{
+  neigh_unlink(SKIP_BACK(struct neigh_unlink_deferred, dc, dc)->n);
+}
+
 /**
  * neigh_update: update neighbor entry w.r.t. change on specific iface
  * @n: neighbor to update
@@ -472,7 +494,7 @@ neigh_update(neighbor *n, struct iface *iface)
 
   if ((n->scope < 0) && !(n->flags & NEF_STICKY))
   {
-    neigh_unlink(n);
+    neigh_unlink_locked(n);
     return;
   }
 
index 195935332012b55005185128be05311ee2951877..dee72eb2b28af3ede256b4347e184edeaec15ea9 100644 (file)
@@ -1291,13 +1291,17 @@ proto_event(void *ptr)
 
   if (p->do_stop)
   {
-    iface_unsubscribe(&p->iface_sub);
-
     p->do_stop = 0;
   }
 
   if (proto_is_done(p) && p->pool_inloop)  /* perusing pool_inloop to do this once only */
   {
+    /* Interface notification unsubscribe can't be done
+     * before the protocol is really done, as it also destroys
+     * the neighbors which may be needed (e.g. by BGP->MRT)
+     * during the STOP phase as well. */
+    iface_unsubscribe(&p->iface_sub);
+
     rp_free(p->pool_inloop);
     p->pool_inloop = NULL;
     if (p->loop != &main_birdloop)
index e6bf3f39096944c70f43b3f27cc4e48d14277d2c..923381e9e2c275e9f5587d328ad6cab3f321bf27 100644 (file)
@@ -994,6 +994,8 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
     return;
   }
 
+  neigh_link(nb);
+
   n->neigh = nb;
   nb->data = n;
 
@@ -1007,8 +1009,11 @@ static void
 bfd_stop_neighbor(struct bfd_proto *p UNUSED, struct bfd_neighbor *n)
 {
   if (n->neigh)
+  {
     n->neigh->data = NULL;
-  n->neigh = NULL;
+    neigh_unlink(n->neigh);
+    n->neigh = NULL;
+  }
 
   rfree(n->req);
   n->req = NULL;
index 1a284df8eb48574567cb5d6065d57c6d3ce9b238..6dd121495e7647d574034931221dbb4d9de53279 100644 (file)
@@ -588,7 +588,11 @@ bgp_down(struct bgp_proto *p)
     bgp_close(p);
   }
 
-  p->neigh = NULL;
+  if (p->neigh)
+  {
+    neigh_unlink(p->neigh);
+    p->neigh = NULL;
+  }
 
   BGP_TRACE(D_EVENTS, "Down");
   proto_notify_state(&p->p, PS_DOWN);
@@ -1749,6 +1753,7 @@ bgp_start_locked(void *_p)
   }
 
   p->neigh = n;
+  neigh_link(n);
 
   if (n->scope <= 0)
     BGP_TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", p->remote_ip, cf->iface);
index 1d4e6cf5c59f153d409c492fcd63d7d9e02f24a8..f62ec667cf14e34f671146f79ee3fda329ea1b6f 100644 (file)
@@ -454,6 +454,8 @@ rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa)
   struct rip_neighbor *n = mb_allocz(p->p.pool, sizeof(struct rip_neighbor));
   n->ifa = ifa;
   n->nbr = nbr;
+  neigh_link(nbr);
+
   nbr->data = n;
   n->csn = nbr->aux;
 
@@ -475,6 +477,8 @@ rip_remove_neighbor(struct rip_proto *p, struct rip_neighbor *n)
   nbr->data = NULL;
   nbr->aux = n->csn;
 
+  neigh_unlink(nbr);
+
   rfree(n->bfd_req);
   n->bfd_req = NULL;
   n->last_seen = 0;
index c8fdcc37b3ac8fc58ba2f71dadb6b4ecd59f1ed9..d091924bdc7dea888a61bca5d10947346000f34f 100644 (file)
@@ -302,6 +302,8 @@ static_add_rte(struct static_proto *p, struct static_route *r)
        continue;
       }
 
+      neigh_link(n);
+
       r2->neigh = n;
       r2->chain = n->data;
       n->data = r2;
@@ -321,7 +323,13 @@ static_reset_rte(struct static_proto *p UNUSED, struct static_route *r)
 
   for (r2 = r; r2; r2 = r2->mp_next)
   {
-    r2->neigh = NULL;
+    if (r2->neigh)
+    {
+      r2->neigh->data = NULL;
+      neigh_unlink(r2->neigh);
+      r2->neigh = NULL;
+    }
+
     r2->chain = NULL;
 
     r2->state = 0;