diff options
Diffstat (limited to 'nest/neighbor.c')
-rw-r--r-- | nest/neighbor.c | 63 |
1 files changed, 46 insertions, 17 deletions
diff --git a/nest/neighbor.c b/nest/neighbor.c index c27db989..88ac2860 100644 --- a/nest/neighbor.c +++ b/nest/neighbor.c @@ -258,13 +258,15 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags) add_tail((scope >= 0) ? &iface->neighbors : &sticky_neigh_list, &n->if_n); proto_neigh_add_tail(&p->neighbors, n); n->addr = a; - n->ifa = addr; - n->iface = iface; - n->ifreq = ifreq; + ifa_link(n->ifa = addr); + if_link(n->iface = iface); + if_link(n->ifreq = ifreq); n->proto = p; n->flags = flags; n->scope = scope; + neigh_link(n); + return n; } @@ -309,19 +311,20 @@ neigh_dump_all(void) static inline void neigh_notify(neighbor *n) { - if (n->proto && n->proto->neigh_notify && (n->proto->proto_state != PS_STOP)) - n->proto->neigh_notify(n); + if_enqueue_notify_to((struct iface_notification) { .type = IFNOT_NEIGHBOR, .n = n, }, &n->proto->iface_sub); } static void neigh_up(neighbor *n, struct iface *i, struct ifa *a, int scope) { DBG("Waking up sticky neighbor %I\n", n->addr); - n->iface = i; - n->ifa = a; + if_link(n->iface = i); + ifa_link(n->ifa = a); + n->scope = scope; - rem_node(&n->if_n); + rem_node(&n->if_n); /* HACK: Here the neighbor is always in the sticky list, + regardless whether it is sticky or not */ add_tail(&i->neighbors, &n->if_n); neigh_notify(n); @@ -331,25 +334,48 @@ static void neigh_down(neighbor *n) { DBG("Flushing neighbor %I on %s\n", n->addr, n->iface->name); - n->iface = NULL; - n->ifa = NULL; + n->scope = -1; rem_node(&n->if_n); add_tail(&sticky_neigh_list, &n->if_n); + ifa_unlink(n->ifa); + n->ifa = NULL; + + if_unlink(n->iface); + n->iface = NULL; + neigh_notify(n); } -static inline void -neigh_free(neighbor *n) +void +neigh_link(neighbor *n) { - proto_neigh_rem_node(&n->proto->neighbors, n); + n->uc++; +} + +void +neigh_unlink(neighbor *n) +{ + if (--n->uc) + return; + + struct proto *p = n->proto; + proto_neigh_rem_node(&p->neighbors, n); + + if ((p->proto_state == PS_DOWN) && EMPTY_TLIST(proto_neigh, &p->neighbors)) + ev_schedule(p->event); + n->proto = NULL; rem_node(&n->n); rem_node(&n->if_n); + ifa_unlink(n->ifa); + if_unlink(n->iface); + if_unlink(n->ifreq); + sl_free(n); } @@ -399,7 +425,8 @@ neigh_update(neighbor *n, struct iface *iface) { if (ifa != n->ifa) { - n->ifa = ifa; + ifa_unlink(n->ifa); + ifa_link(n->ifa = ifa); neigh_notify(n); } @@ -413,7 +440,7 @@ neigh_update(neighbor *n, struct iface *iface) if ((n->scope < 0) && !(n->flags & NEF_STICKY)) { - neigh_free(n); + neigh_unlink(n); return; } @@ -534,8 +561,10 @@ neigh_ifa_down(struct ifa *a) void neigh_prune(struct proto *p) { - while (!EMPTY_TLIST(proto_neigh, &p->neighbors)) - neigh_free(THEAD(proto_neigh, &p->neighbors)); + WALK_TLIST_DELSAFE(proto_neigh, n, &p->neighbors) + neigh_unlink(n); + + ASSERT_DIE(EMPTY_TLIST(proto_neigh, &p->neighbors)); } /** |