summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2011-12-12 00:24:15 +0100
committerOndrej Zajicek <santiago@crfreenet.org>2011-12-12 00:24:15 +0100
commitcf7f0645316f5df0984467cf7001f5466254eaf3 (patch)
tree660ed7a04d393a172be0138051d93991ba11196d
parent2779d50a24dc1b7c6b4cf83a17af817c02462855 (diff)
downloadbird-cf7f0645316f5df0984467cf7001f5466254eaf3.tar
bird-cf7f0645316f5df0984467cf7001f5466254eaf3.zip
Fixes problem with sticky neighbors and iface address changes.
Thanks Matthias Schiffer for the bugreport and the original patch.
-rw-r--r--nest/iface.c15
-rw-r--r--nest/iface.h1
-rw-r--r--nest/neighbor.c85
3 files changed, 72 insertions, 29 deletions
diff --git a/nest/iface.c b/nest/iface.c
index 2ff2611..bf57a9b 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -152,16 +152,24 @@ 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)
{
if (p->if_notify)
@@ -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)
diff --git a/nest/iface.h b/nest/iface.h
index a9cf6cd..7307844 100644
--- a/nest/iface.h
+++ b/nest/iface.h
@@ -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 *);
/*
diff --git a/nest/neighbor.c b/nest/neighbor.c
index 1a5ac21..67bd32b 100644
--- a/nest/neighbor.c
+++ b/nest/neighbor.c
@@ -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)
{