summaryrefslogtreecommitdiff
path: root/nest
diff options
context:
space:
mode:
Diffstat (limited to 'nest')
-rw-r--r--nest/iface.c203
-rw-r--r--nest/iface.h66
-rw-r--r--nest/neighbor.c63
-rw-r--r--nest/proto.c14
-rw-r--r--nest/protocol.h4
-rw-r--r--nest/rt-dev.c4
6 files changed, 303 insertions, 51 deletions
diff --git a/nest/iface.c b/nest/iface.c
index fc63dc75..740c1878 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -34,6 +34,9 @@
#include "conf/conf.h"
#include "sysdep/unix/krt.h"
+
+static TLIST_LIST(ifsub) iface_sub_list;
+static slab *iface_sub_slab;
static pool *if_pool;
list iface_list;
@@ -140,13 +143,51 @@ 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)
+{
+ 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;
+
+ ifnot_add_tail(&s->queue, in);
+ ev_schedule(&s->event);
+}
+
+void
+if_enqueue_notify(struct iface_notification x)
+{
+ 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 || p->vrf == a->iface->master))
{
@@ -154,19 +195,21 @@ ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
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
@@ -182,9 +225,11 @@ 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 || p->vrf == i->master))
{
@@ -197,14 +242,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)
@@ -225,8 +269,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)
@@ -320,6 +367,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;
@@ -334,9 +382,10 @@ 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);
return i;
@@ -386,32 +435,117 @@ if_end_update(void)
}
}
+void
+if_link(struct iface *i)
+{
+ if (i)
+ i->uc++;
+}
+
+void
+if_unlink(struct iface *i)
+{
+ if (i)
+ i->uc--;
+ /* TODO: Do some interface object cleanup */
+}
+
+static void
+iface_notify_hook(void *_s)
+{
+ struct iface_subscription *s = _s;
+
+ while (!EMPTY_TLIST(ifnot, &s->queue))
+ {
+ struct iface_notification *n = THEAD(ifnot, &s->queue);
+ switch (n->type) {
+ case IFNOT_ADDRESS:
+ ifa_send_notify(s, n->flags, n->a);
+ ifa_unlink(n->a);
+ break;
+ case IFNOT_INTERFACE:
+ if_send_notify(s, n->flags, n->i);
+ if_unlink(n->i);
+ break;
+ case IFNOT_NEIGHBOR:
+ s->neigh_notify(n->n);
+ neigh_unlink(n->n);
+ break;
+ default:
+ bug("Bad interface notification type: %d", n->type);
+ }
+
+ ifnot_rem_node(&s->queue, n);
+ sl_free(n);
+ }
+}
+
+
/**
- * 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;
+ ifsub_add_tail(&iface_sub_list, s);
+ s->event = (event) {
+ .hook = iface_notify_hook,
+ .data = s,
+ };
- if (!p->if_notify && !p->ifa_notify) /* shortcut */
+ if (!s->if_notify && !s->ifa_notify) /* shortcut */
return;
+
+ struct iface *i;
DBG("Announcing interfaces to new protocol %s\n", p->name);
WALK_LIST(i, iface_list)
{
- if_send_notify(p, IF_CHANGE_CREATE | ((i->flags & IF_UP) ? IF_CHANGE_UP : 0), i);
+ if_send_notify(s, IF_CHANGE_CREATE | ((i->flags & IF_UP) ? IF_CHANGE_UP : 0), i);
+
+ struct ifa *a;
if (i->flags & IF_UP)
WALK_LIST(a, i->addrs)
- ifa_send_notify(p, IF_CHANGE_CREATE | IF_CHANGE_UP, a);
+ ifa_send_notify(s, IF_CHANGE_CREATE | IF_CHANGE_UP, a);
}
}
/**
+ * iface_unsubscribe - unsubscribe from interface updates
+ * @s: subscription structure
+ */
+void
+iface_unsubscribe(struct iface_subscription *s)
+{
+ ifsub_rem_node(&iface_sub_list, s);
+ ev_postpone(&s->event);
+
+ WALK_TLIST_DELSAFE(ifnot, n, &s->queue)
+ {
+ 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);
+ }
+}
+
+/**
* if_find_by_index - find interface by ifindex
* @idx: ifindex
*
@@ -600,6 +734,8 @@ 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;
@@ -646,11 +782,29 @@ ifa_delete(struct ifa *a)
if (i->flags & IF_UP)
ifa_notify_change(IF_CHANGE_DOWN, b);
- mb_free(b);
+ ifa_unlink(b);
return;
}
}
+void ifa_link(struct ifa *a)
+{
+ if (a)
+ a->uc++;
+}
+
+void ifa_unlink(struct ifa *a)
+{
+ if (!a)
+ return;
+
+ if (--a->uc)
+ return;
+
+ if_unlink(a->iface);
+ mb_free(a);
+}
+
u32
if_choose_router_id(struct iface_patt *mask, u32 old_id)
{
@@ -706,6 +860,7 @@ if_init(void)
{
if_pool = rp_new(&root_pool, "Interfaces");
init_list(&iface_list);
+ iface_sub_slab = sl_new(if_pool, sizeof(struct iface_notification));
strcpy(default_vrf.name, "default");
neigh_init(if_pool);
}
diff --git a/nest/iface.h b/nest/iface.h
index fb27f99e..a3f4f30a 100644
--- a/nest/iface.h
+++ b/nest/iface.h
@@ -9,6 +9,7 @@
#ifndef _BIRD_IFACE_H_
#define _BIRD_IFACE_H_
+#include "lib/event.h"
#include "lib/lists.h"
#include "lib/tlists.h"
#include "lib/ip.h"
@@ -27,6 +28,7 @@ struct ifa { /* Interface address */
ip_addr opposite; /* Opposite end of a point-to-point link */
unsigned scope; /* Interface address scope */
unsigned flags; /* Analogous to iface->flags */
+ unsigned uc; /* Use (link) count */
};
extern struct iface default_vrf;
@@ -45,6 +47,7 @@ struct iface {
struct ifa *llv6; /* Primary link-local address for IPv6 */
ip4_addr sysdep; /* Arbitrary IPv4 address for internal sysdep use */
list neighbors; /* All neighbors on this interface */
+ unsigned uc; /* Use (link) count */
};
#define IF_UP 1 /* Currently just IF_ADMIN_UP */
@@ -115,12 +118,15 @@ void ifa_delete(struct ifa *);
void if_start_update(void);
void if_end_partial_update(struct iface *);
void if_end_update(void);
-void if_feed_baby(struct proto *);
struct iface *if_find_by_index(unsigned);
struct iface *if_find_by_name(const char *);
struct iface *if_get_by_name(const char *);
void if_recalc_all_preferred_addresses(void);
+void if_link(struct iface *);
+void if_unlink(struct iface *);
+void ifa_link(struct ifa *);
+void ifa_unlink(struct ifa *);
/* The Neighbor Cache */
@@ -138,6 +144,7 @@ typedef struct neighbor {
u16 flags; /* NEF_* flags */
s16 scope; /* Address scope, -1 for unreachable neighbors,
SCOPE_HOST when it's our own address */
+ uint uc; /* Use (link) count */
} neighbor;
#define TLIST_PREFIX proto_neigh
@@ -164,6 +171,63 @@ void neigh_ifa_up(struct ifa *a);
void neigh_ifa_down(struct ifa *a);
void neigh_init(struct pool *);
+void neigh_link(neighbor *);
+void neigh_unlink(neighbor *);
+
+/*
+ * Notification mechanism
+ */
+
+#define TLIST_PREFIX ifnot
+#define TLIST_TYPE struct iface_notification
+#define TLIST_ITEM nn
+#define TLIST_WANT_WALK
+#define TLIST_WANT_ADD_TAIL
+
+struct iface_notification {
+ TLIST_DEFAULT_NODE;
+ enum {
+ IFNOT_INVALID,
+ IFNOT_ADDRESS,
+ IFNOT_INTERFACE,
+ IFNOT_NEIGHBOR,
+ } type;
+ unsigned flags;
+ union {
+ struct ifa *a;
+ struct iface *i;
+ neighbor *n;
+ };
+};
+
+#include "lib/tlists.h"
+
+#define TLIST_PREFIX ifsub
+#define TLIST_TYPE struct iface_subscription
+#define TLIST_ITEM n
+#define TLIST_WANT_WALK
+#define TLIST_WANT_ADD_TAIL
+
+struct iface_subscription {
+ TLIST_DEFAULT_NODE;
+
+ event event;
+ TLIST_LIST(ifnot) queue;
+
+ void (*if_notify)(struct proto *, unsigned flags, struct iface *i);
+ void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a);
+ void (*neigh_notify)(struct neighbor *neigh);
+};
+
+#include "lib/tlists.h"
+
+void if_enqueue_notify(struct iface_notification);
+void if_enqueue_notify_to(struct iface_notification x, struct iface_subscription *s);
+
+void iface_flush_notifications(struct iface_subscription *);
+void iface_subscribe(struct iface_subscription *);
+void iface_unsubscribe(struct iface_subscription *);
+
/*
* Interface Pattern Lists
*/
diff --git a/nest/neighbor.c b/nest/neighbor.c
index c27db989..88ac2860 100644
--- a/nest/neighbor.c
+++ b/nest/neighbor.c
@@ -258,13 +258,15 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
add_tail((scope >= 0) ? &iface->neighbors : &sticky_neigh_list, &n->if_n);
proto_neigh_add_tail(&p->neighbors, n);
n->addr = a;
- n->ifa = addr;
- n->iface = iface;
- n->ifreq = ifreq;
+ ifa_link(n->ifa = addr);
+ if_link(n->iface = iface);
+ if_link(n->ifreq = ifreq);
n->proto = p;
n->flags = flags;
n->scope = scope;
+ neigh_link(n);
+
return n;
}
@@ -309,19 +311,20 @@ neigh_dump_all(void)
static inline void
neigh_notify(neighbor *n)
{
- if (n->proto && n->proto->neigh_notify && (n->proto->proto_state != PS_STOP))
- n->proto->neigh_notify(n);
+ if_enqueue_notify_to((struct iface_notification) { .type = IFNOT_NEIGHBOR, .n = n, }, &n->proto->iface_sub);
}
static void
neigh_up(neighbor *n, struct iface *i, struct ifa *a, int scope)
{
DBG("Waking up sticky neighbor %I\n", n->addr);
- n->iface = i;
- n->ifa = a;
+ if_link(n->iface = i);
+ ifa_link(n->ifa = a);
+
n->scope = scope;
- rem_node(&n->if_n);
+ rem_node(&n->if_n); /* HACK: Here the neighbor is always in the sticky list,
+ regardless whether it is sticky or not */
add_tail(&i->neighbors, &n->if_n);
neigh_notify(n);
@@ -331,25 +334,48 @@ static void
neigh_down(neighbor *n)
{
DBG("Flushing neighbor %I on %s\n", n->addr, n->iface->name);
- n->iface = NULL;
- n->ifa = NULL;
+
n->scope = -1;
rem_node(&n->if_n);
add_tail(&sticky_neigh_list, &n->if_n);
+ ifa_unlink(n->ifa);
+ n->ifa = NULL;
+
+ if_unlink(n->iface);
+ n->iface = NULL;
+
neigh_notify(n);
}
-static inline void
-neigh_free(neighbor *n)
+void
+neigh_link(neighbor *n)
{
- proto_neigh_rem_node(&n->proto->neighbors, n);
+ n->uc++;
+}
+
+void
+neigh_unlink(neighbor *n)
+{
+ if (--n->uc)
+ return;
+
+ struct proto *p = n->proto;
+ proto_neigh_rem_node(&p->neighbors, n);
+
+ if ((p->proto_state == PS_DOWN) && EMPTY_TLIST(proto_neigh, &p->neighbors))
+ ev_schedule(p->event);
+
n->proto = NULL;
rem_node(&n->n);
rem_node(&n->if_n);
+ ifa_unlink(n->ifa);
+ if_unlink(n->iface);
+ if_unlink(n->ifreq);
+
sl_free(n);
}
@@ -399,7 +425,8 @@ neigh_update(neighbor *n, struct iface *iface)
{
if (ifa != n->ifa)
{
- n->ifa = ifa;
+ ifa_unlink(n->ifa);
+ ifa_link(n->ifa = ifa);
neigh_notify(n);
}
@@ -413,7 +440,7 @@ neigh_update(neighbor *n, struct iface *iface)
if ((n->scope < 0) && !(n->flags & NEF_STICKY))
{
- neigh_free(n);
+ neigh_unlink(n);
return;
}
@@ -534,8 +561,10 @@ neigh_ifa_down(struct ifa *a)
void
neigh_prune(struct proto *p)
{
- while (!EMPTY_TLIST(proto_neigh, &p->neighbors))
- neigh_free(THEAD(proto_neigh, &p->neighbors));
+ WALK_TLIST_DELSAFE(proto_neigh, n, &p->neighbors)
+ neigh_unlink(n);
+
+ ASSERT_DIE(EMPTY_TLIST(proto_neigh, &p->neighbors));
}
/**
diff --git a/nest/proto.c b/nest/proto.c
index 2614943c..39e8b999 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -57,7 +57,11 @@ static inline void channel_reset_limit(struct channel_limit *l);
static inline int proto_is_done(struct proto *p)
-{ return (p->proto_state == PS_DOWN) && (p->active_channels == 0); }
+{
+ return (p->proto_state == PS_DOWN)
+ && (p->active_channels == 0)
+ && EMPTY_TLIST(proto_neigh, &p->neighbors);
+}
static inline int channel_is_active(struct channel *c)
{ return (c->channel_state == CS_START) || (c->channel_state == CS_UP); }
@@ -962,14 +966,18 @@ proto_event(void *ptr)
if (p->do_start)
{
- if_feed_baby(p);
+ iface_subscribe(&p->iface_sub);
p->do_start = 0;
}
if (p->do_stop)
{
+ iface_unsubscribe(&p->iface_sub);
+ neigh_prune(p);
+
p->do_stop = 0;
}
+
if (proto_is_done(p))
{
if (p->proto->cleanup)
@@ -1860,7 +1868,6 @@ proto_do_stop(struct proto *p)
p->down_sched = 0;
p->gr_recovery = 0;
-
if (p->main_source)
{
rt_unlock_source(p->main_source);
@@ -1877,7 +1884,6 @@ static void
proto_do_down(struct proto *p)
{
p->down_code = 0;
- neigh_prune(p);
rfree(p->pool);
p->pool = NULL;
diff --git a/nest/protocol.h b/nest/protocol.h
index 9fbe9158..6d5714a7 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -171,6 +171,7 @@ struct proto {
struct rte_src *main_source; /* Primary route source */
struct iface *vrf; /* Related VRF instance, NULL if global */
TLIST_LIST(proto_neigh) neighbors; /* List of neighbor structures */
+ struct iface_subscription iface_sub; /* Interface notification subscription */
const char *name; /* Name of this instance (== cf->name) */
u32 debug; /* Debugging flags */
@@ -210,10 +211,7 @@ struct proto {
* feed_end Notify channel about finish of route feeding.
*/
- void (*if_notify)(struct proto *, unsigned flags, struct iface *i);
- void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a);
void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
- void (*neigh_notify)(struct neighbor *neigh);
int (*preexport)(struct channel *, struct rte *rt);
void (*reload_routes)(struct channel *);
void (*feed_begin)(struct channel *, int initial);
diff --git a/nest/rt-dev.c b/nest/rt-dev.c
index 7932b8b7..42b6b499 100644
--- a/nest/rt-dev.c
+++ b/nest/rt-dev.c
@@ -141,8 +141,8 @@ dev_init(struct proto_config *CF)
proto_configure_channel(P, &p->ip4_channel, cf->ip4_channel);
proto_configure_channel(P, &p->ip6_channel, cf->ip6_channel);
- P->if_notify = dev_if_notify;
- P->ifa_notify = dev_ifa_notify;
+ P->iface_sub.if_notify = dev_if_notify;
+ P->iface_sub.ifa_notify = dev_ifa_notify;
return P;
}