diff options
Diffstat (limited to 'nest/iface.c')
-rw-r--r-- | nest/iface.c | 443 |
1 files changed, 376 insertions, 67 deletions
diff --git a/nest/iface.c b/nest/iface.c index 682340c5..f1938664 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -31,15 +31,51 @@ #include "nest/cli.h" #include "lib/resource.h" #include "lib/string.h" +#include "lib/locking.h" #include "conf/conf.h" #include "sysdep/unix/krt.h" +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 TLIST_LIST(ifsub) iface_sub_list; +static slab *iface_sub_slab; static pool *if_pool; -list iface_list; +list global_iface_list; +struct iface default_vrf; static void if_recalc_preferred(struct iface *i); +static void ifa_dump_locked(struct ifa *); +static void if_dump_locked(struct iface *); + +struct iface * +if_walk_first(void) +{ + IFACE_LOCK; + struct iface *i = HEAD(global_iface_list); + return NODE_VALID(i) ? i : NULL; +} + +struct iface * +if_walk_next(struct iface *i) +{ + IFACE_ASSERT_LOCKED; + i = NODE_NEXT(i); + return NODE_VALID(i) ? i : NULL; +} + +void +if_walk_done(void) +{ + IFACE_ASSERT_LOCKED; + IFACE_UNLOCK; +} + /** * ifa_dump - dump interface address * @a: interface address descriptor @@ -49,6 +85,14 @@ static void if_recalc_preferred(struct iface *i); void ifa_dump(struct ifa *a) { + IFACE_LOCK; + ifa_dump_locked(a); + IFACE_UNLOCK; +} + +static void +ifa_dump_locked(struct ifa *a) +{ debug("\t%I, net %N bc %I -> %I%s%s%s%s\n", a->ip, &a->prefix, a->brd, a->opposite, (a->flags & IA_PRIMARY) ? " PRIMARY" : "", (a->flags & IA_SECONDARY) ? " SEC" : "", @@ -66,6 +110,14 @@ ifa_dump(struct ifa *a) void if_dump(struct iface *i) { + IFACE_LOCK; + if_dump_locked(i); + IFACE_UNLOCK; +} + +static void +if_dump_locked(struct iface *i) +{ struct ifa *a; debug("IF%d: %s", i->index, i->name); @@ -92,7 +144,7 @@ if_dump(struct iface *i) debug(" MTU=%d\n", i->mtu); WALK_LIST(a, i->addrs) { - ifa_dump(a); + ifa_dump_locked(a); ASSERT(!!(a->flags & IA_PRIMARY) == ((a == i->addr4) || (a == i->addr6) || (a == i->llv6))); } @@ -107,14 +159,60 @@ if_dump(struct iface *i) void if_dump_all(void) { - struct iface *i; - debug("Known network interfaces:\n"); - WALK_LIST(i, iface_list) + IFACE_WALK(i) if_dump(i); debug("Router ID: %08x\n", config->router_id); } +void +if_link(struct iface *i) +{ + IFACE_ASSERT_LOCKED; + + if (i) + i->uc++; +} + +void +if_unlink(struct iface *i) +{ + IFACE_ASSERT_LOCKED; + + if (i) + i->uc--; + /* TODO: Do some interface object cleanup */ +} + +void ifa_link(struct ifa *a) +{ + IFACE_ASSERT_LOCKED; + + if (a) + { + debug("ifa_link: %p %d\n", a, a->uc); + a->uc++; + } +} + +void ifa_unlink(struct ifa *a) +{ + IFACE_ASSERT_LOCKED; + + if (!a) + return; + + debug("ifa_unlink: %p %d\n", a, a->uc); + if (--a->uc) + return; + + if_unlink(a->iface); +#if DEBUGGING + memset(a, 0x5b, sizeof(struct ifa)); +#endif + mb_free(a); +} + static inline unsigned if_what_changed(struct iface *i, struct iface *j) { @@ -139,33 +237,79 @@ if_copy(struct iface *to, struct iface *from) to->flags = from->flags | (to->flags & IF_TMP_DOWN); to->mtu = from->mtu; to->master_index = from->master_index; - to->master = from->master; + + if_unlink(to->master); + if_link(to->master = from->master); +} + +void +if_enqueue_notify_to(struct iface_notification x, struct iface_subscription *s) +{ + IFACE_ASSERT_LOCKED; + + switch (x.type) { + case IFNOT_ADDRESS: + if (!s->ifa_notify) return; + ifa_link(x.a); + break; + case IFNOT_INTERFACE: + if (!s->if_notify) return; + if_link(x.i); + break; + case IFNOT_NEIGHBOR: + if (!s->neigh_notify) return; + neigh_link(x.n); + break; + default: + bug("Unknown interface notification type: %d", x.type); + } + + struct iface_notification *in = sl_alloc(iface_sub_slab); + *in = x; + + debug("Enqueue notify %d/%p (%p) to %p\n", x.type, x.a, in, s); + + ifnot_add_tail(&s->queue, in); + ev_send(s->target, &s->event); +} + +void +if_enqueue_notify(struct iface_notification x) +{ + IFACE_ASSERT_LOCKED; + + WALK_TLIST(ifsub, s, &iface_sub_list) + if_enqueue_notify_to(x, s); } static inline void -ifa_send_notify(struct proto *p, unsigned c, struct ifa *a) +ifa_send_notify(struct iface_subscription *s, unsigned c, struct ifa *a) { - if (p->ifa_notify && + struct proto *p = SKIP_BACK(struct proto, iface_sub, s); + + if (s->ifa_notify && (p->proto_state != PS_DOWN) && - (!p->vrf_set || p->vrf == a->iface->master)) + (!p->vrf || p->vrf == a->iface->master)) { if (p->debug & D_IFACES) log(L_TRACE "%s < address %N on interface %s %s", p->name, &a->prefix, a->iface->name, (c & IF_CHANGE_UP) ? "added" : "removed"); - p->ifa_notify(p, c, a); + s->ifa_notify(p, c, a); } } static void ifa_notify_change_(unsigned c, struct ifa *a) { - struct proto *p; - DBG("IFA change notification (%x) for %s:%I\n", c, a->iface->name, a->ip); - WALK_LIST(p, proto_list) - ifa_send_notify(p, c, a); + if_enqueue_notify((struct iface_notification) { + .type = IFNOT_ADDRESS, + .a = a, + .flags = c, + }); + } static inline void @@ -181,11 +325,13 @@ ifa_notify_change(unsigned c, struct ifa *a) } static inline void -if_send_notify(struct proto *p, unsigned c, struct iface *i) +if_send_notify(struct iface_subscription *s, unsigned c, struct iface *i) { - if (p->if_notify && + struct proto *p = SKIP_BACK(struct proto, iface_sub, s); + + if (s->if_notify && (p->proto_state != PS_DOWN) && - (!p->vrf_set || p->vrf == i->master)) + (!p->vrf || p->vrf == i->master)) { if (p->debug & D_IFACES) log(L_TRACE "%s < interface %s %s", p->name, i->name, @@ -196,14 +342,13 @@ if_send_notify(struct proto *p, unsigned c, struct iface *i) (c & IF_CHANGE_PREFERRED) ? "changes preferred address" : (c & IF_CHANGE_CREATE) ? "created" : "sends unknown event"); - p->if_notify(p, c, i); + s->if_notify(p, c, i); } } static void if_notify_change(unsigned c, struct iface *i) { - struct proto *p; struct ifa *a; if (i->flags & IF_JUST_CREATED) @@ -214,7 +359,7 @@ if_notify_change(unsigned c, struct iface *i) DBG("Interface change notification (%x) for %s\n", c, i->name); #ifdef LOCAL_DEBUG - if_dump(i); + if_dump_locked(i); #endif if (c & IF_CHANGE_DOWN) @@ -224,8 +369,11 @@ if_notify_change(unsigned c, struct iface *i) WALK_LIST(a, i->addrs) ifa_notify_change_(IF_CHANGE_DOWN, a); - WALK_LIST(p, proto_list) - if_send_notify(p, c, i); + if_enqueue_notify((struct iface_notification) { + .type = IFNOT_INTERFACE, + .i = i, + .flags = c, + }); if (c & IF_CHANGE_UP) WALK_LIST(a, i->addrs) @@ -243,7 +391,7 @@ if_recalc_flags(struct iface *i UNUSED, uint flags) { if ((flags & IF_ADMIN_UP) && !(flags & (IF_SHUTDOWN | IF_TMP_DOWN)) && - !(i->master_index && !i->master)) + !(i->master_index && i->master == &default_vrf)) flags |= IF_UP; else flags &= ~IF_UP; @@ -273,10 +421,12 @@ if_change_flags(struct iface *i, uint flags) void if_delete(struct iface *old) { + IFACE_LOCK; struct iface f = {}; strncpy(f.name, old->name, sizeof(f.name)-1); f.flags = IF_SHUTDOWN; - if_update(&f); + if_update_locked(&f); + IFACE_UNLOCK; } /** @@ -298,10 +448,22 @@ if_delete(struct iface *old) struct iface * if_update(struct iface *new) { + IFACE_LOCK; + struct iface *i = if_update_locked(new); + IFACE_UNLOCK; + return i; +} + +struct iface * +if_update_locked(struct iface *new) +{ struct iface *i; unsigned c; - WALK_LIST(i, iface_list) + if (!new->master) + new->master = &default_vrf; + + WALK_LIST(i, global_iface_list) if (!strcmp(new->name, i->name)) { new->flags = if_recalc_flags(new, new->flags); @@ -316,6 +478,7 @@ if_update(struct iface *new) new->llv6 = i->llv6; new->sysdep = i->sysdep; memcpy(&new->addrs, &i->addrs, sizeof(i->addrs)); + memcpy(&new->neighbors, &i->neighbors, sizeof(i->neighbors)); memcpy(i, new, sizeof(*i)); i->flags &= ~IF_UP; /* IF_TMP_DOWN will be added later */ goto newif; @@ -330,21 +493,21 @@ if_update(struct iface *new) } i = mb_alloc(if_pool, sizeof(struct iface)); memcpy(i, new, sizeof(*i)); + if_link(i->master); init_list(&i->addrs); -newif: init_list(&i->neighbors); +newif: i->flags |= IF_UPDATED | IF_TMP_DOWN; /* Tmp down as we don't have addresses yet */ - add_tail(&iface_list, &i->n); + add_tail(&global_iface_list, &i->n); return i; } void if_start_update(void) { - struct iface *i; struct ifa *a; - WALK_LIST(i, iface_list) + IFACE_WALK(i) { i->flags &= ~IF_UPDATED; WALK_LIST(a, i->addrs) @@ -352,8 +515,8 @@ if_start_update(void) } } -void -if_end_partial_update(struct iface *i) +static void +if_end_partial_update_locked(struct iface *i) { if (i->flags & IF_NEEDS_RECALC) if_recalc_preferred(i); @@ -363,12 +526,19 @@ if_end_partial_update(struct iface *i) } void +if_end_partial_update(struct iface *i) +{ + IFACE_LOCK; + if_end_partial_update_locked(i); + IFACE_UNLOCK; +} + +void if_end_update(void) { - struct iface *i; struct ifa *a, *b; - WALK_LIST(i, iface_list) + IFACE_WALK(i) { if (!(i->flags & IF_UPDATED)) if_change_flags(i, (i->flags & ~IF_ADMIN_UP) | IF_SHUTDOWN); @@ -377,43 +547,145 @@ if_end_update(void) WALK_LIST_DELSAFE(a, b, i->addrs) if (!(a->flags & IA_UPDATED)) ifa_delete(a); - if_end_partial_update(i); + if_end_partial_update_locked(i); } } } -void -if_flush_ifaces(struct proto *p) +static void +iface_notify_hook(void *_s) { - if (p->debug & D_EVENTS) - log(L_TRACE "%s: Flushing interfaces", p->name); - if_start_update(); - if_end_update(); + struct iface_subscription *s = _s; + + IFACE_LOCK; + + while (!EMPTY_TLIST(ifnot, &s->queue)) + { + struct iface_notification *n = THEAD(ifnot, &s->queue); + debug("Process notify %d/%p (%p) to %p\n", n->type, n->a, n, s); + IFACE_UNLOCK; + + switch (n->type) { + case IFNOT_ADDRESS: + ifa_send_notify(s, n->flags, n->a); + IFACE_LOCK; + ifa_unlink(n->a); + IFACE_UNLOCK; + break; + case IFNOT_INTERFACE: + if_send_notify(s, n->flags, n->i); + IFACE_LOCK; + if_unlink(n->i); + IFACE_UNLOCK; + break; + case IFNOT_NEIGHBOR: + s->neigh_notify(n->n); + IFACE_LOCK; + neigh_unlink(n->n); + IFACE_UNLOCK; + break; + default: + bug("Bad interface notification type: %d", n->type); + } + + IFACE_LOCK; + ifnot_rem_node(&s->queue, n); + sl_free(n); + } + + IFACE_UNLOCK; } + /** - * if_feed_baby - advertise interfaces to a new protocol - * @p: protocol to feed + * iface_subscribe - request interface updates + * @s: subscription structure * * When a new protocol starts, this function sends it a series * of notifications about all existing interfaces. */ void -if_feed_baby(struct proto *p) +iface_subscribe(struct iface_subscription *s) { - struct iface *i; - struct ifa *a; - - if (!p->if_notify && !p->ifa_notify) /* shortcut */ + IFACE_LOCK; + ifsub_add_tail(&iface_sub_list, s); + s->event = (event) { + .hook = iface_notify_hook, + .data = s, + }; + + if (!s->if_notify && !s->ifa_notify) /* shortcut */ + { + IFACE_UNLOCK; return; + } + + struct iface *i; DBG("Announcing interfaces to new protocol %s\n", p->name); - WALK_LIST(i, iface_list) + WALK_LIST(i, global_iface_list) { - if_send_notify(p, IF_CHANGE_CREATE | ((i->flags & IF_UP) ? IF_CHANGE_UP : 0), i); + if_enqueue_notify_to( + (struct iface_notification) { + .type = IFNOT_INTERFACE, + .i = i, + .flags = IF_CHANGE_CREATE | ((i->flags & IF_UP) ? IF_CHANGE_UP : 0), + }, s); + + struct ifa *a; if (i->flags & IF_UP) WALK_LIST(a, i->addrs) - ifa_send_notify(p, IF_CHANGE_CREATE | IF_CHANGE_UP, a); + if_enqueue_notify_to( + (struct iface_notification) { + .type = IFNOT_ADDRESS, + .a = a, + .flags = IF_CHANGE_CREATE | IF_CHANGE_UP, + }, s); + } + + IFACE_UNLOCK; +} + +/** + * iface_unsubscribe - unsubscribe from interface updates + * @s: subscription structure + */ +void +iface_unsubscribe(struct iface_subscription *s) +{ + IFACE_LOCK; + + struct proto *p = SKIP_BACK(struct proto, iface_sub, s); + WALK_TLIST_DELSAFE(proto_neigh, n, &p->neighbors) + neigh_unlink(n); + + ifsub_rem_node(&iface_sub_list, s); + ev_postpone(&s->event); + + WALK_TLIST_DELSAFE(ifnot, n, &s->queue) + { + debug("Drop notify %d/%p (%p) to %p\n", n->type, n->a, n, s); + switch (n->type) + { + case IFNOT_ADDRESS: + ifa_unlink(n->a); + break; + case IFNOT_INTERFACE: + if_unlink(n->i); + break; + case IFNOT_NEIGHBOR: + neigh_unlink(n->n); + break; + default: + bug("Bad interface notification type: %d", n->type); } + + ifnot_rem_node(&s->queue, n); + sl_free(n); + } + + ASSERT_DIE(EMPTY_TLIST(proto_neigh, &p->neighbors)); + + IFACE_UNLOCK; } /** @@ -425,16 +697,26 @@ if_feed_baby(struct proto *p) * if no such structure exists. */ struct iface * -if_find_by_index(unsigned idx) +if_find_by_index_locked(unsigned idx) { struct iface *i; - WALK_LIST(i, iface_list) + WALK_LIST(i, global_iface_list) if (i->index == idx && !(i->flags & IF_SHUTDOWN)) return i; + return NULL; } +struct iface * +if_find_by_index(unsigned idx) +{ + IFACE_LOCK; + struct iface *i = if_find_by_index_locked(idx); + IFACE_UNLOCK; + return i; +} + /** * if_find_by_name - find interface by name * @name: interface name @@ -448,9 +730,15 @@ if_find_by_name(const char *name) { struct iface *i; - WALK_LIST(i, iface_list) + IFACE_LOCK; + WALK_LIST(i, global_iface_list) if (!strcmp(i->name, name) && !(i->flags & IF_SHUTDOWN)) + { + IFACE_UNLOCK; return i; + } + + IFACE_UNLOCK; return NULL; } @@ -459,9 +747,13 @@ if_get_by_name(const char *name) { struct iface *i; - WALK_LIST(i, iface_list) + IFACE_LOCK; + WALK_LIST(i, global_iface_list) if (!strcmp(i->name, name)) + { + IFACE_UNLOCK; return i; + } /* No active iface, create a dummy */ i = mb_allocz(if_pool, sizeof(struct iface)); @@ -469,7 +761,9 @@ if_get_by_name(const char *name) i->flags = IF_SHUTDOWN; init_list(&i->addrs); init_list(&i->neighbors); - add_tail(&iface_list, &i->n); + add_tail(&global_iface_list, &i->n); + + IFACE_UNLOCK; return i; } @@ -553,9 +847,7 @@ if_recalc_preferred(struct iface *i) void if_recalc_all_preferred_addresses(void) { - struct iface *i; - - WALK_LIST(i, iface_list) + IFACE_WALK(i) { if_recalc_preferred(i); @@ -582,6 +874,8 @@ ifa_same(struct ifa *a, struct ifa *b) struct ifa * ifa_update(struct ifa *a) { + IFACE_LOCK; + struct iface *i = a->iface; struct ifa *b; @@ -594,6 +888,8 @@ ifa_update(struct ifa *a) !((b->flags ^ a->flags) & (IA_SECONDARY | IA_PEER | IA_HOST))) { b->flags |= IA_UPDATED; + + IFACE_UNLOCK; return b; } ifa_delete(b); @@ -605,12 +901,16 @@ ifa_update(struct ifa *a) b = mb_alloc(if_pool, sizeof(struct ifa)); memcpy(b, a, sizeof(struct ifa)); + ifa_link(b); + if_link(i); add_tail(&i->addrs, &b->n); b->flags |= IA_UPDATED; i->flags |= IF_NEEDS_RECALC; if (i->flags & IF_UP) ifa_notify_change(IF_CHANGE_CREATE | IF_CHANGE_UP, b); + + IFACE_UNLOCK; return b; } @@ -628,6 +928,8 @@ ifa_delete(struct ifa *a) struct iface *i = a->iface; struct ifa *b; + IFACE_LOCK; + WALK_LIST(b, i->addrs) if (ifa_same(b, a)) { @@ -651,19 +953,24 @@ ifa_delete(struct ifa *a) if (i->flags & IF_UP) ifa_notify_change(IF_CHANGE_DOWN, b); - mb_free(b); + ifa_unlink(b); + IFACE_UNLOCK; return; } + + IFACE_UNLOCK; } u32 if_choose_router_id(struct iface_patt *mask, u32 old_id) { + IFACE_LOCK; + struct iface *i; struct ifa *a, *b; b = NULL; - WALK_LIST(i, iface_list) + WALK_LIST(i, global_iface_list) { if (!(i->flags & IF_ADMIN_UP) || (i->flags & IF_SHUTDOWN)) @@ -690,6 +997,8 @@ if_choose_router_id(struct iface_patt *mask, u32 old_id) } } + IFACE_UNLOCK; + if (!b) return 0; @@ -710,8 +1019,11 @@ void if_init(void) { if_pool = rp_new(&root_pool, "Interfaces"); - init_list(&iface_list); + init_list(&global_iface_list); + iface_sub_slab = sl_new(if_pool, sizeof(struct iface_notification)); + strcpy(default_vrf.name, "default"); neigh_init(if_pool); + iface_domain = DOMAIN_NEW(attrs, "Interfaces"); } /* @@ -833,17 +1145,16 @@ if_show_addr(struct ifa *a) void if_show(void) { - struct iface *i; struct ifa *a; char *type; - WALK_LIST(i, iface_list) + IFACE_WALK(i) { if (i->flags & IF_SHUTDOWN) continue; char mbuf[16 + sizeof(i->name)] = {}; - if (i->master) + if (i->master != &default_vrf) bsprintf(mbuf, " master=%s", i->master->name); else if (i->master_index) bsprintf(mbuf, " master=#%u", i->master_index); @@ -877,10 +1188,8 @@ if_show(void) void if_show_summary(void) { - struct iface *i; - cli_msg(-2005, "%-10s %-6s %-18s %s", "Interface", "State", "IPv4 address", "IPv6 address"); - WALK_LIST(i, iface_list) + IFACE_WALK(i) { byte a4[IPA_MAX_TEXT_LENGTH + 17]; byte a6[IPA_MAX_TEXT_LENGTH + 17]; |