diff options
Diffstat (limited to 'nest/neighbor.c')
-rw-r--r-- | nest/neighbor.c | 143 |
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; } /** |