summaryrefslogtreecommitdiff
path: root/nest/iface.c
diff options
context:
space:
mode:
Diffstat (limited to 'nest/iface.c')
-rw-r--r--nest/iface.c443
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];