summaryrefslogtreecommitdiff
path: root/nest/neighbor.c
diff options
context:
space:
mode:
Diffstat (limited to 'nest/neighbor.c')
-rw-r--r--nest/neighbor.c139
1 files changed, 99 insertions, 40 deletions
diff --git a/nest/neighbor.c b/nest/neighbor.c
index f8159d35..fb05d96c 100644
--- a/nest/neighbor.c
+++ b/nest/neighbor.c
@@ -46,14 +46,15 @@
#include "lib/resource.h"
#define NEIGH_HASH_SIZE 256
+#define NEIGH_HASH_OFFSET 24
static slab *neigh_slab;
-static list sticky_neigh_list, neigh_hash_table[NEIGH_HASH_SIZE];
+static list sticky_neigh_list, iface_neigh_list, neigh_hash_table[NEIGH_HASH_SIZE];
static inline uint
neigh_hash(struct proto *p, ip_addr *a)
{
- return (p->hash_key ^ ipa_hash(*a)) & (NEIGH_HASH_SIZE-1);
+ return (p->hash_key ^ ipa_hash(*a)) >> NEIGH_HASH_OFFSET;
}
static int
@@ -80,17 +81,17 @@ if_connected(ip_addr *a, struct iface *i, struct ifa **ap)
}
else
{
- if (ipa_in_net(*a, b->prefix, b->pxlen))
+ if (ipa_in_netX(*a, &b->prefix))
{
-#ifndef IPV6
- if ((b->pxlen < (BITS_PER_IP_ADDRESS - 1)) &&
- (ipa_equal(*a, b->prefix) || /* Network address */
+ /* Do not allow IPv4 network and broadcast addresses */
+ if (ipa_is_ip4(*a) &&
+ (net_pxlen(&b->prefix) < (IP4_MAX_PREFIX_LENGTH - 1)) &&
+ (ipa_equal(*a, net_prefix(&b->prefix)) || /* Network address */
ipa_equal(*a, b->brd))) /* Broadcast */
{
*ap = NULL;
return -1;
}
-#endif
return b->scope;
}
@@ -167,6 +168,8 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
return NULL;
n = sl_alloc(neigh_slab);
+ memset(n, 0, sizeof(neighbor));
+
n->addr = *a;
if (scope >= 0)
{
@@ -188,6 +191,35 @@ neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
return n;
}
+neighbor *
+neigh_find_iface(struct proto *p, struct iface *ifa)
+{
+ neighbor *n;
+ node *nn;
+
+ /* We keep neighbors with NEF_IFACE foremost in ifa->neighbors list */
+ WALK_LIST2(n, nn, ifa->neighbors, if_n)
+ {
+ if (! (n->flags & NEF_IFACE))
+ break;
+
+ if (n->proto == p)
+ return n;
+ }
+
+ n = sl_alloc(neigh_slab);
+ memset(n, 0, sizeof(neighbor));
+
+ add_tail(&iface_neigh_list, &n->n);
+ add_head(&ifa->neighbors, &n->if_n);
+ n->iface = ifa;
+ n->proto = p;
+ n->flags = NEF_IFACE;
+ n->scope = (ifa->flags & IF_UP) ? SCOPE_HOST : -1;
+
+ return n;
+}
+
/**
* neigh_dump - dump specified neighbor entry.
* @n: the entry to dump
@@ -206,6 +238,8 @@ neigh_dump(neighbor *n)
debug("%s %p %08x scope %s", n->proto->name, n->data, n->aux, ip_scope_text(n->scope));
if (n->flags & NEF_STICKY)
debug(" STICKY");
+ if (n->flags & NEF_IFACE)
+ debug(" IFACE");
debug("\n");
}
@@ -224,6 +258,8 @@ neigh_dump_all(void)
debug("Known neighbors:\n");
WALK_LIST(n, sticky_neigh_list)
neigh_dump(n);
+ WALK_LIST(n, iface_neigh_list)
+ neigh_dump(n);
for(i=0; i<NEIGH_HASH_SIZE; i++)
WALK_LIST(n, neigh_hash_table[i])
neigh_dump(n);
@@ -233,14 +269,19 @@ neigh_dump_all(void)
static void
neigh_up(neighbor *n, struct iface *i, int scope, struct ifa *a)
{
+ DBG("Waking up sticky neighbor %I\n", n->addr);
n->iface = i;
n->ifa = a;
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)
+
+ if (! (n->flags & NEF_IFACE))
+ {
+ add_tail(&i->neighbors, &n->if_n);
+ rem_node(&n->n);
+ add_tail(&neigh_hash_table[neigh_hash(n->proto, &n->addr)], &n->n);
+ }
+
+ if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
n->proto->neigh_notify(n);
}
@@ -248,14 +289,20 @@ static void
neigh_down(neighbor *n)
{
DBG("Flushing neighbor %I on %s\n", n->addr, n->iface->name);
- rem_node(&n->if_n);
- if (! (n->flags & NEF_BIND))
+ if (! (n->flags & (NEF_BIND | NEF_IFACE)))
n->iface = NULL;
n->ifa = NULL;
n->scope = -1;
- if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
+
+ if (! (n->flags & NEF_IFACE))
+ {
+ rem_node(&n->if_n);
+ rem_node(&n->n);
+ }
+
+ if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
n->proto->neigh_notify(n);
- rem_node(&n->n);
+
if (n->flags & NEF_STICKY)
{
add_tail(&sticky_neigh_list, &n->n);
@@ -273,7 +320,8 @@ neigh_down(neighbor *n)
return;
}
}
- else
+
+ if (! (n->flags & (NEF_STICKY | NEF_IFACE)))
sl_free(neigh_slab, n);
}
@@ -291,10 +339,17 @@ void
neigh_if_up(struct iface *i)
{
struct ifa *a;
- neighbor *n, *next;
+ neighbor *n;
+ node *x, *y;
int scope;
- WALK_LIST_DELSAFE(n, next, sticky_neigh_list)
+ /* Wake up all iface neighbors */
+ WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
+ if ((n->scope < 0) && (n->flags & NEF_IFACE))
+ neigh_up(n, i, SCOPE_HOST, NULL);
+
+ /* Wake up appropriate sticky neighbors */
+ WALK_LIST_DELSAFE(n, x, sticky_neigh_list)
if ((!n->iface || n->iface == i) &&
((scope = if_connected(&n->addr, i, &a)) >= 0))
neigh_up(n, i, scope, a);
@@ -312,10 +367,11 @@ neigh_if_up(struct iface *i)
void
neigh_if_down(struct iface *i)
{
+ neighbor *n;
node *x, *y;
- WALK_LIST_DELSAFE(x, y, i->neighbors)
- neigh_down(SKIP_BACK(neighbor, if_n, x));
+ WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
+ neigh_down(n);
}
/**
@@ -329,14 +385,12 @@ neigh_if_down(struct iface *i)
void
neigh_if_link(struct iface *i)
{
+ neighbor *n;
node *x, *y;
- WALK_LIST_DELSAFE(x, y, i->neighbors)
- {
- neighbor *n = SKIP_BACK(neighbor, if_n, x);
- if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING)
- n->proto->neigh_notify(n);
- }
+ WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
+ if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
+ n->proto->neigh_notify(n);
}
/**
@@ -353,19 +407,21 @@ void
neigh_ifa_update(struct ifa *a)
{
struct iface *i = a->iface;
+ struct ifa *aa;
node *x, *y;
-
+ neighbor *n;
+ int scope;
+
/* Remove all neighbors whose scope has changed */
- WALK_LIST_DELSAFE(x, y, i->neighbors)
- {
- struct ifa *aa;
- neighbor *n = SKIP_BACK(neighbor, if_n, x);
- if (if_connected(&n->addr, i, &aa) != n->scope)
- neigh_down(n);
- }
+ WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
+ if (n->ifa && (if_connected(&n->addr, i, &aa) != n->scope))
+ neigh_down(n);
/* Wake up all sticky neighbors that are reachable now */
- neigh_if_up(i);
+ WALK_LIST_DELSAFE(n, x, sticky_neigh_list)
+ if ((!n->iface || n->iface == i) &&
+ ((scope = if_connected(&n->addr, i, &aa)) >= 0))
+ neigh_up(n, i, scope, aa);
}
static inline void
@@ -374,7 +430,7 @@ neigh_prune_one(neighbor *n)
if (n->proto->proto_state != PS_DOWN)
return;
rem_node(&n->n);
- if (n->scope >= 0)
+ if (n->if_n.next)
rem_node(&n->if_n);
sl_free(neigh_slab, n);
}
@@ -399,6 +455,8 @@ neigh_prune(void)
neigh_prune_one(n);
WALK_LIST_DELSAFE(n, m, sticky_neigh_list)
neigh_prune_one(n);
+ WALK_LIST_DELSAFE(n, m, iface_neigh_list)
+ neigh_prune_one(n);
}
/**
@@ -411,10 +469,11 @@ neigh_prune(void)
void
neigh_init(pool *if_pool)
{
- int i;
-
neigh_slab = sl_new(if_pool, sizeof(neighbor));
+
init_list(&sticky_neigh_list);
- for(i=0; i<NEIGH_HASH_SIZE; i++)
+ init_list(&iface_neigh_list);
+
+ for(int i = 0; i < NEIGH_HASH_SIZE; i++)
init_list(&neigh_hash_table[i]);
}