summaryrefslogtreecommitdiff
path: root/nest/neighbor.c
diff options
context:
space:
mode:
Diffstat (limited to 'nest/neighbor.c')
-rw-r--r--nest/neighbor.c154
1 files changed, 97 insertions, 57 deletions
diff --git a/nest/neighbor.c b/nest/neighbor.c
index 7cf9c85d..934cd35c 100644
--- a/nest/neighbor.c
+++ b/nest/neighbor.c
@@ -60,6 +60,19 @@
static slab *neigh_slab;
static list neigh_hash_table[NEIGH_HASH_SIZE], sticky_neigh_list;
+void if_link(struct iface *);
+void if_unlink(struct iface *);
+void ifa_link(struct ifa *);
+void ifa_unlink(struct ifa *);
+
+extern list global_iface_list;
+
+extern DOMAIN(attrs) iface_domain;
+
+#define IFACE_LOCK LOCK_DOMAIN(attrs, iface_domain)
+#define IFACE_UNLOCK UNLOCK_DOMAIN(attrs, iface_domain)
+#define IFACE_ASSERT_LOCKED ASSERT_DIE(DOMAIN_IS_LOCKED(attrs, iface_domain))
+
static inline uint
neigh_hash(struct proto *p, ip_addr a, struct iface *i)
{
@@ -142,7 +155,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 +165,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;
@@ -210,6 +223,8 @@ if_intersect(struct iface *ia, struct iface *ib)
neighbor *
neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
{
+ IFACE_LOCK;
+
neighbor *n;
int class, scope = -1;
uint h = neigh_hash(p, a, iface);
@@ -218,26 +233,29 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
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;
+ }
if (flags & NEF_IFACE)
{
if (ipa_nonzero(a) || !iface)
- return NULL;
+ goto bad;
}
else
{
class = ipa_classify(a);
if (class < 0) /* Invalid address */
- return NULL;
+ goto bad;
if (((class & IADDR_SCOPE_MASK) == SCOPE_HOST) ||
(((class & IADDR_SCOPE_MASK) == SCOPE_LINK) && !iface) ||
!(class & IADDR_HOST))
- return NULL; /* Bad scope or a somecast */
+ goto bad; /* Bad scope or a somecast */
}
if ((flags & NEF_ONLINK) && !iface)
- return NULL;
+ goto bad;
if (iface)
{
@@ -245,26 +263,34 @@ 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;
+ goto bad;
n = sl_allocz(neigh_slab);
add_tail(&neigh_hash_table[h], &n->n);
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);
+
+ IFACE_UNLOCK;
return n;
+
+bad:
+ IFACE_UNLOCK;
+ return NULL;
}
/**
@@ -273,7 +299,7 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
*
* This functions dumps the contents of a given neighbor entry to debug output.
*/
-void
+static void
neigh_dump(neighbor *n)
{
debug("%p %I %s %s ", n, n->addr,
@@ -295,6 +321,8 @@ neigh_dump(neighbor *n)
void
neigh_dump_all(void)
{
+ IFACE_LOCK;
+
neighbor *n;
int i;
@@ -303,24 +331,28 @@ neigh_dump_all(void)
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))
- n->proto->neigh_notify(n);
+ IFACE_ASSERT_LOCKED;
+ 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);
@@ -330,21 +362,50 @@ 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)
+{
+ IFACE_ASSERT_LOCKED;
+ n->uc++;
+}
+
+void
+neigh_unlink(neighbor *n)
{
+ IFACE_ASSERT_LOCKED;
+ 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))
+ proto_send_event(p, 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);
}
@@ -360,6 +421,8 @@ neigh_free(neighbor *n)
void
neigh_update(neighbor *n, struct iface *iface)
{
+ IFACE_ASSERT_LOCKED;
+
struct proto *p = n->proto;
struct ifa *ifa = NULL;
int scope = -1;
@@ -369,7 +432,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 +442,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
{
@@ -394,7 +457,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);
}
@@ -408,7 +472,7 @@ neigh_update(neighbor *n, struct iface *iface)
if ((n->scope < 0) && !(n->flags & NEF_STICKY))
{
- neigh_free(n);
+ neigh_unlink(n);
return;
}
@@ -429,12 +493,13 @@ neigh_update(neighbor *n, struct iface *iface)
void
neigh_if_up(struct iface *i)
{
+ IFACE_ASSERT_LOCKED;
struct iface *ii;
neighbor *n;
node *x, *y;
/* 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);
@@ -454,6 +519,7 @@ neigh_if_up(struct iface *i)
void
neigh_if_down(struct iface *i)
{
+ IFACE_ASSERT_LOCKED;
neighbor *n;
node *x, *y;
@@ -471,6 +537,7 @@ neigh_if_down(struct iface *i)
void
neigh_if_link(struct iface *i)
{
+ IFACE_ASSERT_LOCKED;
neighbor *n;
node *x, *y;
@@ -491,12 +558,13 @@ neigh_if_link(struct iface *i)
void
neigh_ifa_up(struct ifa *a)
{
+ IFACE_ASSERT_LOCKED;
struct iface *i = a->iface, *ii;
neighbor *n;
node *x, *y;
/* 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);
@@ -509,6 +577,7 @@ neigh_ifa_up(struct ifa *a)
void
neigh_ifa_down(struct ifa *a)
{
+ IFACE_ASSERT_LOCKED;
struct iface *i = a->iface;
neighbor *n;
node *x, *y;
@@ -519,35 +588,6 @@ neigh_ifa_down(struct ifa *a)
neigh_update(n, i);
}
-static inline void
-neigh_prune_one(neighbor *n)
-{
- if (n->proto->proto_state != PS_DOWN)
- return;
-
- neigh_free(n);
-}
-
-/**
- * neigh_prune - prune neighbor cache
- *
- * neigh_prune() examines all neighbor entries cached and removes those
- * corresponding to inactive protocols. It's called whenever a protocol
- * is shut down to get rid of all its heritage.
- */
-void
-neigh_prune(void)
-{
- neighbor *n;
- node *m;
- int i;
-
- 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);
-}
-
/**
* neigh_init - initialize the neighbor cache.
* @if_pool: resource pool to be used for neighbor entries.