diff --git a/nest/iface.c b/nest/iface.c index 61b99a91..4c017f50 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -36,10 +36,12 @@ static pool *if_pool; +DOMAIN(attrs) iface_domain; list global_iface_list; struct iface default_vrf; static void if_recalc_preferred(struct iface *i); +static void ifa_delete_locked(struct ifa *a); /** * ifa_dump - dump interface address @@ -50,6 +52,7 @@ static void if_recalc_preferred(struct iface *i); void ifa_dump(struct ifa *a) { + IFACE_LEGACY_ACCESS; 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" : "", @@ -69,6 +72,7 @@ if_dump(struct iface *i) { struct ifa *a; + IFACE_LEGACY_ACCESS; debug("IF%d: %s", i->index, i->name); if (i->flags & IF_SHUTDOWN) debug(" SHUTDOWN"); @@ -110,6 +114,7 @@ if_dump_all(void) { struct iface *i; + IFACE_LEGACY_ACCESS; debug("Known network interfaces:\n"); WALK_LIST(i, global_iface_list) if_dump(i); @@ -307,6 +312,9 @@ if_update(struct iface *new) if (!new->master) new->master = &default_vrf; + IFACE_LEGACY_ACCESS; + IFACE_LOCK; + WALK_LIST(i, global_iface_list) if (!strcmp(new->name, i->name)) { @@ -328,6 +336,8 @@ if_update(struct iface *new) } if_copy(i, new); + IFACE_UNLOCK; + if (c) if_notify_change(c, i); @@ -341,6 +351,8 @@ newif: init_list(&i->neighbors); i->flags |= IF_UPDATED | IF_TMP_DOWN; /* Tmp down as we don't have addresses yet */ add_tail(&global_iface_list, &i->n); + IFACE_UNLOCK; + return i; } @@ -350,6 +362,7 @@ if_start_update(void) struct iface *i; struct ifa *a; + IFACE_LEGACY_ACCESS; WALK_LIST(i, global_iface_list) { i->flags &= ~IF_UPDATED; @@ -361,6 +374,8 @@ if_start_update(void) void if_end_partial_update(struct iface *i) { + IFACE_LEGACY_ACCESS; + if (i->flags & IF_NEEDS_RECALC) if_recalc_preferred(i); @@ -374,6 +389,7 @@ if_end_update(void) struct iface *i; struct ifa *a, *b; + IFACE_LEGACY_ACCESS; WALK_LIST(i, global_iface_list) { if (!(i->flags & IF_UPDATED)) @@ -382,7 +398,11 @@ if_end_update(void) { WALK_LIST_DELSAFE(a, b, i->addrs) if (!(a->flags & IA_UPDATED)) - ifa_delete(a); + { + IFACE_LOCK; + ifa_delete_locked(a); + IFACE_UNLOCK; + } if_end_partial_update(i); } } @@ -391,6 +411,7 @@ if_end_update(void) void if_flush_ifaces(struct proto *p) { + IFACE_LEGACY_ACCESS; if (p->debug & D_EVENTS) log(L_TRACE "%s: Flushing interfaces", p->name); if_start_update(); @@ -410,6 +431,8 @@ if_feed_baby(struct proto *p) struct iface *i; struct ifa *a; + IFACE_LEGACY_ACCESS; + if (!p->if_notify && !p->ifa_notify) /* shortcut */ return; DBG("Announcing interfaces to new protocol %s\n", p->name); @@ -435,9 +458,15 @@ if_find_by_index(unsigned idx) { struct iface *i; + IFACE_LOCK; WALK_LIST(i, global_iface_list) if (i->index == idx && !(i->flags & IF_SHUTDOWN)) + { + IFACE_UNLOCK; return i; + } + + IFACE_UNLOCK; return NULL; } @@ -454,9 +483,15 @@ if_find_by_name(const char *name) { struct iface *i; + 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; } @@ -465,17 +500,21 @@ if_get_by_name(const char *name) { struct iface *i; + IFACE_LEGACY_ACCESS; + WALK_LIST(i, global_iface_list) if (!strcmp(i->name, name)) return i; /* No active iface, create a dummy */ + IFACE_LOCK; i = mb_allocz(if_pool, sizeof(struct iface)); strncpy(i->name, name, sizeof(i->name)-1); i->flags = IF_SHUTDOWN; init_list(&i->addrs); init_list(&i->neighbors); add_tail(&global_iface_list, &i->n); + IFACE_UNLOCK; return i; } @@ -561,6 +600,8 @@ if_recalc_all_preferred_addresses(void) { struct iface *i; + IFACE_LEGACY_ACCESS; + WALK_LIST(i, global_iface_list) { if_recalc_preferred(i); @@ -591,6 +632,8 @@ ifa_update(struct ifa *a) struct iface *i = a->iface; struct ifa *b; + IFACE_LEGACY_ACCESS; + WALK_LIST(b, i->addrs) if (ifa_same(b, a)) { @@ -609,10 +652,12 @@ ifa_update(struct ifa *a) if ((a->prefix.type == NET_IP4) && (i->flags & IF_BROADCAST) && ipa_zero(a->brd)) log(L_WARN "Missing broadcast address for interface %s", i->name); + IFACE_LOCK; b = mb_alloc(if_pool, sizeof(struct ifa)); memcpy(b, a, sizeof(struct ifa)); add_tail(&i->addrs, &b->n); b->flags |= IA_UPDATED; + IFACE_UNLOCK; i->flags |= IF_NEEDS_RECALC; if (i->flags & IF_UP) @@ -637,6 +682,17 @@ ifa_delete(struct ifa *a) WALK_LIST(b, i->addrs) if (ifa_same(b, a)) { + IFACE_LOCK; + ifa_delete_locked(b); + IFACE_UNLOCK; + return; + } +} + +static void +ifa_delete_locked(struct ifa *b) +{ + struct iface *i = b->iface; rem_node(&b->n); if (b->flags & IA_PRIMARY) @@ -659,7 +715,6 @@ ifa_delete(struct ifa *a) mb_free(b); return; - } } u32 @@ -668,6 +723,8 @@ if_choose_router_id(struct iface_patt *mask, u32 old_id) struct iface *i; struct ifa *a, *b; + IFACE_LEGACY_ACCESS; + b = NULL; WALK_LIST(i, global_iface_list) { @@ -715,6 +772,7 @@ if_choose_router_id(struct iface_patt *mask, u32 old_id) void if_init(void) { + iface_domain = DOMAIN_NEW(attrs, "Interfaces"); if_pool = rp_new(&root_pool, &main_birdloop, "Interfaces"); init_list(&global_iface_list); strcpy(default_vrf.name, "default"); @@ -844,6 +902,8 @@ if_show(void) struct ifa *a; char *type; + IFACE_LEGACY_ACCESS; + WALK_LIST(i, global_iface_list) { if (i->flags & IF_SHUTDOWN) @@ -886,6 +946,8 @@ if_show_summary(void) { struct iface *i; + IFACE_LEGACY_ACCESS; + cli_msg(-2005, "%-10s %-6s %-18s %s", "Interface", "State", "IPv4 address", "IPv6 address"); WALK_LIST(i, global_iface_list) { diff --git a/nest/iface.h b/nest/iface.h index b3dcc408..5deb5432 100644 --- a/nest/iface.h +++ b/nest/iface.h @@ -9,10 +9,20 @@ #ifndef _BIRD_IFACE_H_ #define _BIRD_IFACE_H_ +#include "lib/event.h" #include "lib/lists.h" #include "lib/ip.h" +#include "lib/locking.h" +DEFINE_DOMAIN(attrs); extern list global_iface_list; +extern DOMAIN(attrs) iface_domain; + +#define IFACE_LEGACY_ACCESS ASSERT_DIE(birdloop_inside(&main_birdloop)) + +#define IFACE_LOCK LOCK_DOMAIN(attrs, iface_domain) +#define IFACE_UNLOCK UNLOCK_DOMAIN(attrs, iface_domain) +#define ASSERT_IFACE_LOCKED ASSERT_DIE(DOMAIN_IS_LOCKED(attrs, iface_domain)) struct proto; struct pool; @@ -131,6 +141,7 @@ typedef struct neighbor { struct ifa *ifa; /* Ifa on related iface */ struct iface *iface; /* Interface it's connected to */ struct iface *ifreq; /* Requested iface, NULL for any */ + struct event event; /* Notification event */ struct proto *proto; /* Protocol this belongs to */ void *data; /* Protocol-specific data */ uint aux; /* Protocol-specific data */ diff --git a/nest/neighbor.c b/nest/neighbor.c index 50defb96..7466e510 100644 --- a/nest/neighbor.c +++ b/nest/neighbor.c @@ -59,6 +59,7 @@ static slab *neigh_slab; static list neigh_hash_table[NEIGH_HASH_SIZE], sticky_neigh_list; +static void neigh_do_notify(void *); static inline uint neigh_hash(struct proto *p, ip_addr a, struct iface *i) @@ -210,36 +211,40 @@ if_intersect(struct iface *ia, struct iface *ib) neighbor * neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags) { - ASSERT_DIE(birdloop_inside(&main_birdloop)); - neighbor *n; int class, scope = -1; uint h = neigh_hash(p, a, iface); 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) { @@ -253,7 +258,7 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags) /* 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); @@ -265,8 +270,23 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags) n->proto = p; n->flags = flags; n->scope = scope; + n->event = (event) { .hook = neigh_do_notify, .data = n }; + ASSERT_DIE(birdloop_inside(p->loop)); + if (p->loop == &main_birdloop) + n->event.list = &global_event_list; + else + { + birdloop_link(p->loop); + n->event.list = birdloop_event_list(p->loop); + } + + IFACE_UNLOCK; return n; + +not_found: + IFACE_UNLOCK; + return NULL; } /** @@ -300,11 +320,15 @@ neigh_dump_all(void) neighbor *n; int i; + IFACE_LOCK; + debug("Known neighbors:\n"); for(i=0; iproto->neigh_notify) return; - PROTO_LOCKED_FROM_MAIN(n->proto) - if (n->proto->proto_state != PS_STOP) - n->proto->neigh_notify(n); + ev_send(n->event.list, &n->event); +} + +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); } static void @@ -351,6 +384,8 @@ neigh_free(neighbor *n) { rem_node(&n->n); rem_node(&n->if_n); + ev_postpone(&n->event); + birdloop_unlink(n->proto->loop); sl_free(neigh_slab, n); } @@ -366,6 +401,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; @@ -439,6 +476,8 @@ 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, global_iface_list) if (!EMPTY_LIST(ii->neighbors) && (ii != i) && if_intersect(i, ii)) @@ -447,6 +486,8 @@ neigh_if_up(struct iface *i) WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n) neigh_update(n, i); + + IFACE_UNLOCK; } /** @@ -463,8 +504,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; } /** @@ -480,8 +525,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; } /** @@ -501,6 +550,8 @@ 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, global_iface_list) if (!EMPTY_LIST(ii->neighbors) && ifa_intersect(a, ii)) @@ -510,6 +561,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 @@ -519,10 +572,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 @@ -548,10 +605,14 @@ neigh_prune(void) node *m; int i; + IFACE_LOCK; + DBG("Pruning neighbors\n"); for(i=0; iflags & IF_UP)) diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index b8c40c9b..049030ac 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -1225,6 +1225,7 @@ ospf_reconfigure_ifaces2(struct ospf_proto *p) struct iface *iface; struct ifa *a; + IFACE_LEGACY_ACCESS; WALK_LIST(iface, global_iface_list) { if (! (iface->flags & IF_UP)) @@ -1271,6 +1272,7 @@ ospf_reconfigure_ifaces3(struct ospf_proto *p) struct iface *iface; struct ifa *a; + IFACE_LEGACY_ACCESS; WALK_LIST(iface, global_iface_list) { if (! (iface->flags & IF_UP)) diff --git a/proto/radv/radv.c b/proto/radv/radv.c index 63549c77..15673555 100644 --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@ -663,6 +663,7 @@ radv_reconfigure(struct proto *P, struct proto_config *CF) if (!old->propagate_routes && new->propagate_routes) channel_request_feeding(p->p.main_channel); + IFACE_LEGACY_ACCESS; struct iface *iface; WALK_LIST(iface, global_iface_list) { diff --git a/proto/rip/rip.c b/proto/rip/rip.c index b4c702ae..ffb7779b 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -776,6 +776,7 @@ rip_reconfigure_ifaces(struct rip_proto *p, struct rip_config *cf) { struct iface *iface; + IFACE_LEGACY_ACCESS; WALK_LIST(iface, global_iface_list) { if (!(iface->flags & IF_UP)) diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index a0f6a7cb..923001bf 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -242,6 +242,7 @@ krt_send_route(struct krt_proto *p, int cmd, const rte *e) */ if (!i) { + IFACE_LOCK; WALK_LIST(j, global_iface_list) { if (j->flags & IF_LOOPBACK) @@ -250,6 +251,7 @@ krt_send_route(struct krt_proto *p, int cmd, const rte *e) break; } } + IFACE_UNLOCK; if (!i) { diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 9b7b502d..f52a796f 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -1147,6 +1147,7 @@ kif_do_scan(struct kif_proto *p UNUSED) log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type); /* Re-resolve master interface for slaves */ + IFACE_LEGACY_ACCESS; struct iface *i; WALK_LIST(i, global_iface_list) if (i->master_index)