Fixes problem with sticky neighbors and iface address changes.
Thanks Matthias Schiffer for the bugreport and the original patch.
This commit is contained in:
parent
2779d50a24
commit
cf7f064531
3 changed files with 72 additions and 29 deletions
15
nest/iface.c
15
nest/iface.c
|
@ -152,15 +152,23 @@ ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ifa_notify_change(unsigned c, struct ifa *a)
|
ifa_notify_change_dep(unsigned c, struct ifa *a)
|
||||||
{
|
{
|
||||||
struct proto *p;
|
struct proto *p;
|
||||||
|
|
||||||
DBG("IFA change notification (%x) for %s:%I\n", c, a->iface->name, a->ip);
|
DBG("IFA change notification (%x) for %s:%I\n", c, a->iface->name, a->ip);
|
||||||
|
|
||||||
WALK_LIST(p, active_proto_list)
|
WALK_LIST(p, active_proto_list)
|
||||||
ifa_send_notify(p, c, a);
|
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
|
static inline void
|
||||||
if_send_notify(struct proto *p, unsigned c, struct iface *i)
|
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)
|
if (c & IF_CHANGE_UP)
|
||||||
neigh_if_up(i);
|
neigh_if_up(i);
|
||||||
|
|
||||||
if (c & IF_CHANGE_DOWN)
|
if (c & IF_CHANGE_DOWN)
|
||||||
WALK_LIST(a, i->addrs)
|
WALK_LIST(a, i->addrs)
|
||||||
{
|
{
|
||||||
a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
|
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)
|
WALK_LIST(p, active_proto_list)
|
||||||
|
@ -211,7 +220,7 @@ if_notify_change(unsigned c, struct iface *i)
|
||||||
WALK_LIST(a, i->addrs)
|
WALK_LIST(a, i->addrs)
|
||||||
{
|
{
|
||||||
a->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
|
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)
|
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_up(struct iface *);
|
||||||
void neigh_if_down(struct iface *);
|
void neigh_if_down(struct iface *);
|
||||||
void neigh_if_link(struct iface *);
|
void neigh_if_link(struct iface *);
|
||||||
|
void neigh_ifa_update(struct ifa *);
|
||||||
void neigh_init(struct pool *);
|
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
|
* If the node is not connected directly or *@a is not a valid unicast
|
||||||
* IP address, neigh_find() returns %NULL.
|
* IP address, neigh_find() returns %NULL.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
neighbor *
|
neighbor *
|
||||||
neigh_find(struct proto *p, ip_addr *a, unsigned flags)
|
neigh_find(struct proto *p, ip_addr *a, unsigned flags)
|
||||||
{
|
{
|
||||||
|
@ -219,6 +217,35 @@ neigh_dump_all(void)
|
||||||
debug("\n");
|
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
|
* neigh_if_up: notify neighbor cache about interface up event
|
||||||
* @i: interface in question
|
* @i: interface in question
|
||||||
|
@ -236,16 +263,7 @@ neigh_if_up(struct iface *i)
|
||||||
|
|
||||||
WALK_LIST_DELSAFE(n, next, sticky_neigh_list)
|
WALK_LIST_DELSAFE(n, next, sticky_neigh_list)
|
||||||
if ((scope = if_connected(&n->addr, i)) >= 0)
|
if ((scope = if_connected(&n->addr, i)) >= 0)
|
||||||
{
|
neigh_up(n, i, 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -263,19 +281,7 @@ neigh_if_down(struct iface *i)
|
||||||
node *x, *y;
|
node *x, *y;
|
||||||
|
|
||||||
WALK_LIST_DELSAFE(x, y, i->neighbors)
|
WALK_LIST_DELSAFE(x, y, i->neighbors)
|
||||||
{
|
neigh_down(SKIP_BACK(neighbor, if_n, x));
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -286,7 +292,6 @@ neigh_if_down(struct iface *i)
|
||||||
* All owners of neighbor entries connected to this interface are
|
* All owners of neighbor entries connected to this interface are
|
||||||
* notified.
|
* notified.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
neigh_if_link(struct iface *i)
|
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
|
static inline void
|
||||||
neigh_prune_one(neighbor *n)
|
neigh_prune_one(neighbor *n)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue