]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Fixes problem with sticky neighbors and iface address changes.
authorOndrej Zajicek <santiago@crfreenet.org>
Sun, 11 Dec 2011 23:24:15 +0000 (00:24 +0100)
committerOndrej Zajicek <santiago@crfreenet.org>
Sun, 11 Dec 2011 23:24:15 +0000 (00:24 +0100)
Thanks Matthias Schiffer for the bugreport and the original patch.

nest/iface.c
nest/iface.h
nest/neighbor.c

index 2ff2611afe31bd0006d3547ec699783ef6011880..bf57a9b11ac2118294704172df9489b97bb950a9 100644 (file)
@@ -152,15 +152,23 @@ ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
 }
 
 static void
-ifa_notify_change(unsigned c, struct ifa *a)
+ifa_notify_change_dep(unsigned c, struct ifa *a)
 {
   struct proto *p;
 
   DBG("IFA change notification (%x) for %s:%I\n", c, a->iface->name, a->ip);
+
   WALK_LIST(p, active_proto_list)
     ifa_send_notify(p, c, a);
 }
 
+static inline void
+ifa_notify_change(unsigned c, struct ifa *a)
+{
+  neigh_ifa_update(a);
+  ifa_notify_change_dep(c, a);
+}
+
 static inline void
 if_send_notify(struct proto *p, unsigned c, struct iface *i)
 {
@@ -197,11 +205,12 @@ if_notify_change(unsigned c, struct iface *i)
 
   if (c & IF_CHANGE_UP)
     neigh_if_up(i);
+
   if (c & IF_CHANGE_DOWN)
     WALK_LIST(a, i->addrs)
       {
        a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
-       ifa_notify_change(IF_CHANGE_DOWN, a);
+       ifa_notify_change_dep(IF_CHANGE_DOWN, a);
       }
 
   WALK_LIST(p, active_proto_list)
@@ -211,7 +220,7 @@ if_notify_change(unsigned c, struct iface *i)
     WALK_LIST(a, i->addrs)
       {
        a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
-       ifa_notify_change(IF_CHANGE_UP, a);
+       ifa_notify_change_dep(IF_CHANGE_UP, a);
       }
 
   if ((c & (IF_CHANGE_UP | IF_CHANGE_DOWN | IF_CHANGE_LINK)) == IF_CHANGE_LINK)
index a9cf6cd2084e24a1430d7c2efcff4dd17d731f77..7307844e0c90f62e1d62481b69cb299ec6f76972 100644 (file)
@@ -131,6 +131,7 @@ void neigh_prune(void);
 void neigh_if_up(struct iface *);
 void neigh_if_down(struct iface *);
 void neigh_if_link(struct iface *);
+void neigh_ifa_update(struct ifa *);
 void neigh_init(struct pool *);
 
 /*
index 1a5ac215d2f4f856770608bbe22658c50fdfafa9..67bd32b339e607dd78398eb9b938ed7835a1fb11 100644 (file)
@@ -103,8 +103,6 @@ if_connected(ip_addr *a, struct iface *i) /* -1=error, 1=match, 0=no match */
  * If the node is not connected directly or *@a is not a valid unicast
  * IP address, neigh_find() returns %NULL.
  */
-
-
 neighbor *
 neigh_find(struct proto *p, ip_addr *a, unsigned flags)
 {
@@ -219,6 +217,35 @@ neigh_dump_all(void)
   debug("\n");
 }
 
+static void
+neigh_up(neighbor *n, struct iface *i, int scope)
+{
+  n->iface = i;
+  n->scope = scope;
+  add_tail(&i->neighbors, &n->if_n);
+  rem_node(&n->n);
+  add_tail(&neigh_hash_table[neigh_hash(n->proto, &n->addr)], &n->n);
+  DBG("Waking up sticky neighbor %I\n", n->addr);
+  if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
+    n->proto->neigh_notify(n);
+}
+
+static void
+neigh_down(neighbor *n)
+{
+  DBG("Flushing neighbor %I on %s\n", n->addr, i->name);
+  rem_node(&n->if_n);
+  n->iface = NULL;
+  if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
+    n->proto->neigh_notify(n);
+  rem_node(&n->n);
+  if (n->flags & NEF_STICKY)
+    add_tail(&sticky_neigh_list, &n->n);
+  else
+    sl_free(neigh_slab, n);
+}
+
+
 /**
  * neigh_if_up: notify neighbor cache about interface up event
  * @i: interface in question
@@ -236,16 +263,7 @@ neigh_if_up(struct iface *i)
 
   WALK_LIST_DELSAFE(n, next, sticky_neigh_list)
     if ((scope = if_connected(&n->addr, i)) >= 0)
-      {
-       n->iface = i;
-       n->scope = scope;
-       add_tail(&i->neighbors, &n->if_n);
-       rem_node(&n->n);
-       add_tail(&neigh_hash_table[neigh_hash(n->proto, &n->addr)], &n->n);
-       DBG("Waking up sticky neighbor %I\n", n->addr);
-       if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
-         n->proto->neigh_notify(n);
-      }
+      neigh_up(n, i, scope);
 }
 
 /**
@@ -263,19 +281,7 @@ neigh_if_down(struct iface *i)
   node *x, *y;
 
   WALK_LIST_DELSAFE(x, y, i->neighbors)
-    {
-      neighbor *n = SKIP_BACK(neighbor, if_n, x);
-      DBG("Flushing neighbor %I on %s\n", n->addr, i->name);
-      rem_node(&n->if_n);
-      n->iface = NULL;
-      if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
-       n->proto->neigh_notify(n);
-      rem_node(&n->n);
-      if (n->flags & NEF_STICKY)
-       add_tail(&sticky_neigh_list, &n->n);
-      else
-       sl_free(neigh_slab, n);
-    }
+    neigh_down(SKIP_BACK(neighbor, if_n, x));
 }
 
 /**
@@ -286,7 +292,6 @@ neigh_if_down(struct iface *i)
  * All owners of neighbor entries connected to this interface are
  * notified.
  */
-
 void
 neigh_if_link(struct iface *i)
 {
@@ -300,6 +305,34 @@ neigh_if_link(struct iface *i)
     }
 }
 
+/**
+ * neigh_ifa_update: notify neighbor cache about interface address add or remove event
+ * @ifa: interface address in question
+ *
+ * Tell the neighbor cache that an address was added or removed.
+ *
+ * The neighbor cache wakes up all inactive sticky neighbors with
+ * addresses belonging to prefixes of the interface belonging to @ifa
+ * and causes all unreachable neighbors to be flushed.
+ */
+void
+neigh_ifa_update(struct ifa *a)
+{
+  struct iface *i = a->iface;
+  node *x, *y;
+  /* Remove all neighbors whose scope has changed */
+  WALK_LIST_DELSAFE(x, y, i->neighbors)
+    {
+      neighbor *n = SKIP_BACK(neighbor, if_n, x);
+      if (if_connected(&n->addr, i) != n->scope)
+       neigh_down(n);
+    }
+
+  /* Wake up all sticky neighbors that are reachable now */
+  neigh_if_up(i);
+}
+
 static inline void
 neigh_prune_one(neighbor *n)
 {