summaryrefslogtreecommitdiff
path: root/nest/neighbor.c
diff options
context:
space:
mode:
Diffstat (limited to 'nest/neighbor.c')
-rw-r--r--nest/neighbor.c143
1 files changed, 120 insertions, 23 deletions
diff --git a/nest/neighbor.c b/nest/neighbor.c
index 1a31fb79..7b951366 100644
--- a/nest/neighbor.c
+++ b/nest/neighbor.c
@@ -59,6 +59,9 @@
static slab *neigh_slab;
static list neigh_hash_table[NEIGH_HASH_SIZE], sticky_neigh_list;
+static void neigh_do_notify(void *);
+static void neigh_do_notify_main(void *);
+static void neigh_free(neighbor *n);
static inline uint
neigh_hash(struct proto *p, ip_addr a, struct iface *i)
@@ -142,7 +145,7 @@ if_connected(ip_addr a, struct iface *i, struct ifa **ap, uint flags)
}
static inline int
-if_connected_any(ip_addr a, struct iface *vrf, uint vrf_set, struct iface **iface, struct ifa **addr, uint flags)
+if_connected_any(ip_addr a, struct iface *vrf, struct iface **iface, struct ifa **addr, uint flags)
{
struct iface *i;
struct ifa *b;
@@ -152,8 +155,8 @@ if_connected_any(ip_addr a, struct iface *vrf, uint vrf_set, struct iface **ifac
*addr = NULL;
/* Prefer SCOPE_HOST or longer prefix */
- WALK_LIST(i, iface_list)
- if ((!vrf_set || vrf == i->master) && ((s = if_connected(a, i, &b, flags)) >= 0))
+ WALK_LIST(i, global_iface_list)
+ if ((!vrf || vrf == i->master) && ((s = if_connected(a, i, &b, flags)) >= 0))
if (scope_better(s, scope) || (scope_remote(s, scope) && ifa_better(b, *addr)))
{
*iface = i;
@@ -216,28 +219,34 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
struct iface *ifreq = iface;
struct ifa *addr = NULL;
+ IFACE_LOCK;
WALK_LIST(n, neigh_hash_table[h]) /* Search the cache */
if ((n->proto == p) && ipa_equal(n->addr, a) && (n->ifreq == iface))
+ {
+ IFACE_UNLOCK;
return n;
+ }
+
+#define NOT_FOUND goto not_found
if (flags & NEF_IFACE)
{
if (ipa_nonzero(a) || !iface)
- return NULL;
+ NOT_FOUND;
}
else
{
class = ipa_classify(a);
if (class < 0) /* Invalid address */
- return NULL;
+ NOT_FOUND;
if (((class & IADDR_SCOPE_MASK) == SCOPE_HOST) ||
(((class & IADDR_SCOPE_MASK) == SCOPE_LINK) && !iface) ||
!(class & IADDR_HOST))
- return NULL; /* Bad scope or a somecast */
+ NOT_FOUND; /* Bad scope or a somecast */
}
if ((flags & NEF_ONLINK) && !iface)
- return NULL;
+ NOT_FOUND;
if (iface)
{
@@ -245,13 +254,13 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
iface = (scope < 0) ? NULL : iface;
}
else
- scope = if_connected_any(a, p->vrf, p->vrf_set, &iface, &addr, flags);
+ scope = if_connected_any(a, p->vrf, &iface, &addr, flags);
/* scope < 0 means i don't know neighbor */
/* scope >= 0 <=> iface != NULL */
if ((scope < 0) && !(flags & NEF_STICKY))
- return NULL;
+ NOT_FOUND;
n = sl_allocz(neigh_slab);
add_tail(&neigh_hash_table[h], &n->n);
@@ -264,7 +273,36 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
n->flags = flags;
n->scope = scope;
+ ASSERT_DIE(birdloop_inside(p->loop));
+
+ if (flags & NEF_NOTIFY_MAIN)
+ n->event = (event) {
+ .hook = neigh_do_notify_main,
+ .data = n,
+ .list = &global_event_list,
+ };
+ else if (p->loop == &main_birdloop)
+ n->event = (event) {
+ .hook = neigh_do_notify,
+ .data = n,
+ .list = &global_event_list,
+ };
+ else
+ {
+ birdloop_link(p->loop);
+ n->event = (event) {
+ .hook = neigh_do_notify,
+ .data = n,
+ .list = birdloop_event_list(p->loop),
+ };
+ }
+
+ IFACE_UNLOCK;
return n;
+
+not_found:
+ IFACE_UNLOCK;
+ return NULL;
}
/**
@@ -298,18 +336,46 @@ neigh_dump_all(void)
neighbor *n;
int i;
+ IFACE_LOCK;
+
debug("Known neighbors:\n");
for(i=0; i<NEIGH_HASH_SIZE; i++)
WALK_LIST(n, neigh_hash_table[i])
neigh_dump(n);
debug("\n");
+
+ IFACE_UNLOCK;
}
static inline void
neigh_notify(neighbor *n)
{
- if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
+ if (!n->proto->neigh_notify)
+ return;
+
+ ev_send(n->event.list, &n->event);
+}
+
+static void
+neigh_do_notify_main(void *data)
+{
+ neighbor *n = data;
+ PROTO_LOCKED_FROM_MAIN(n->proto)
+ neigh_do_notify(data);
+}
+
+static void
+neigh_do_notify(void *data)
+{
+ neighbor *n = data;
+
+ ASSERT_DIE(birdloop_inside(n->proto->loop));
+
+ if (n->proto->proto_state != PS_STOP)
n->proto->neigh_notify(n);
+
+ if ((n->scope < 0) && !(n->flags & NEF_STICKY))
+ neigh_free(n);
}
static void
@@ -340,11 +406,21 @@ neigh_down(neighbor *n)
neigh_notify(n);
}
-static inline void
+static void
neigh_free(neighbor *n)
{
+ ASSERT_DIE(birdloop_inside(n->proto->loop));
+
+ if (n->flags & NEF_NOTIFY_MAIN)
+ ASSERT_DIE(birdloop_inside(&main_birdloop));
+
rem_node(&n->n);
rem_node(&n->if_n);
+
+ if (n->event.list != &global_event_list)
+ birdloop_unlink(n->proto->loop);
+
+ ev_postpone(&n->event);
sl_free(neigh_slab, n);
}
@@ -360,6 +436,8 @@ neigh_free(neighbor *n)
void
neigh_update(neighbor *n, struct iface *iface)
{
+ ASSERT_IFACE_LOCKED;
+
struct proto *p = n->proto;
struct ifa *ifa = NULL;
int scope = -1;
@@ -369,7 +447,7 @@ neigh_update(neighbor *n, struct iface *iface)
return;
/* VRF-bound neighbors ignore changes in other VRFs */
- if (p->vrf_set && (p->vrf != iface->master))
+ if (p->vrf && (p->vrf != iface->master))
return;
scope = if_connected(n->addr, iface, &ifa, n->flags);
@@ -379,7 +457,7 @@ neigh_update(neighbor *n, struct iface *iface)
{
/* When neighbor is going down, try to respawn it on other ifaces */
if ((scope < 0) && (n->scope >= 0) && !n->ifreq && (n->flags & NEF_STICKY))
- scope = if_connected_any(n->addr, p->vrf, p->vrf_set, &iface, &ifa, n->flags);
+ scope = if_connected_any(n->addr, p->vrf, &iface, &ifa, n->flags);
}
else
{
@@ -406,12 +484,6 @@ neigh_update(neighbor *n, struct iface *iface)
if (n->scope >= 0)
neigh_down(n);
- if ((n->scope < 0) && !(n->flags & NEF_STICKY))
- {
- neigh_free(n);
- return;
- }
-
if (scope >= 0)
neigh_up(n, iface, ifa, scope);
}
@@ -433,14 +505,18 @@ neigh_if_up(struct iface *i)
neighbor *n;
node *x, *y;
+ IFACE_LOCK;
+
/* Update neighbors that might be better off with the new iface */
- WALK_LIST(ii, iface_list)
+ WALK_LIST(ii, global_iface_list)
if (!EMPTY_LIST(ii->neighbors) && (ii != i) && if_intersect(i, ii))
WALK_LIST2_DELSAFE(n, x, y, ii->neighbors, if_n)
neigh_update(n, i);
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
neigh_update(n, i);
+
+ IFACE_UNLOCK;
}
/**
@@ -457,8 +533,12 @@ neigh_if_down(struct iface *i)
neighbor *n;
node *x, *y;
+ IFACE_LOCK;
+
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
neigh_update(n, i);
+
+ IFACE_UNLOCK;
}
/**
@@ -474,8 +554,12 @@ neigh_if_link(struct iface *i)
neighbor *n;
node *x, *y;
+ IFACE_LOCK;
+
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
neigh_notify(n);
+
+ IFACE_UNLOCK;
}
/**
@@ -495,8 +579,10 @@ neigh_ifa_up(struct ifa *a)
neighbor *n;
node *x, *y;
+ IFACE_LOCK;
+
/* Update neighbors that might be better off with the new ifa */
- WALK_LIST(ii, iface_list)
+ WALK_LIST(ii, global_iface_list)
if (!EMPTY_LIST(ii->neighbors) && ifa_intersect(a, ii))
WALK_LIST2_DELSAFE(n, x, y, ii->neighbors, if_n)
neigh_update(n, i);
@@ -504,6 +590,8 @@ neigh_ifa_up(struct ifa *a)
/* Wake up all sticky neighbors that are reachable now */
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
neigh_update(n, i);
+
+ IFACE_UNLOCK;
}
void
@@ -513,10 +601,14 @@ neigh_ifa_down(struct ifa *a)
neighbor *n;
node *x, *y;
+ IFACE_LOCK;
+
/* Update all neighbors whose scope has changed */
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
if (n->ifa == a)
neigh_update(n, i);
+
+ IFACE_UNLOCK;
}
static inline void
@@ -536,16 +628,21 @@ neigh_prune_one(neighbor *n)
* is shut down to get rid of all its heritage.
*/
void
-neigh_prune(void)
+neigh_prune(struct proto *p)
{
neighbor *n;
node *m;
int i;
+ IFACE_LOCK;
+
DBG("Pruning neighbors\n");
for(i=0; i<NEIGH_HASH_SIZE; i++)
WALK_LIST_DELSAFE(n, m, neigh_hash_table[i])
- neigh_prune_one(n);
+ if (n->proto == p)
+ neigh_prune_one(n);
+
+ IFACE_UNLOCK;
}
/**