Commit cf7f0645 authored by Ondřej Zajíček's avatar Ondřej Zajíček

Fixes problem with sticky neighbors and iface address changes.

Thanks Matthias Schiffer for the bugreport and the original patch.
parent 2779d50a
......@@ -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)
......
......@@ -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 *);
/*
......
......@@ -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)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment