summaryrefslogtreecommitdiff
path: root/nest/neighbor.c
diff options
context:
space:
mode:
Diffstat (limited to 'nest/neighbor.c')
-rw-r--r--nest/neighbor.c63
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));
}
/**