diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2011-12-12 00:24:15 +0100 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2011-12-12 00:24:15 +0100 |
commit | cf7f0645316f5df0984467cf7001f5466254eaf3 (patch) | |
tree | 660ed7a04d393a172be0138051d93991ba11196d /nest | |
parent | 2779d50a24dc1b7c6b4cf83a17af817c02462855 (diff) |
Fixes problem with sticky neighbors and iface address changes.
Thanks Matthias Schiffer for the bugreport and the original patch.
Diffstat (limited to 'nest')
-rw-r--r-- | nest/iface.c | 15 | ||||
-rw-r--r-- | nest/iface.h | 1 | ||||
-rw-r--r-- | nest/neighbor.c | 85 |
3 files changed, 72 insertions, 29 deletions
diff --git a/nest/iface.c b/nest/iface.c index 2ff2611a..bf57a9b1 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 a9cf6cd2..7307844e 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 1a5ac215..67bd32b3 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) { |