[PATCH 1/1] * Implement per-table protocol hooks in core
Alexander V. Chernikov
melifaro at ipfw.ru
Fri Nov 25 16:39:48 CET 2011
---
nest/proto.c | 132 ++++++++++++++++++++++++++++++++++++++++-------------
nest/protocol.h | 16 ++----
nest/route.h | 3 +
nest/rt-table.c | 64 ++++++++++---------------
proto/pipe/pipe.c | 68 ++++++++++++++++++++++++++-
proto/pipe/pipe.h | 7 +--
6 files changed, 201 insertions(+), 89 deletions(-)
diff --git a/nest/proto.c b/nest/proto.c
index d55c348..3d7684c 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -19,6 +19,9 @@
#include "nest/iface.h"
#include "nest/cli.h"
#include "filter/filter.h"
+#ifdef CONFIG_PIPE
+#include "proto/pipe/pipe.h"
+#endif
pool *proto_pool;
@@ -112,8 +115,6 @@ proto_new(struct proto_config *c, unsigned size)
p->disabled = c->disabled;
p->proto = pr;
p->table = c->table->table;
- p->in_filter = c->in_filter;
- p->out_filter = c->out_filter;
p->hash_key = random_u32();
c->proto = p;
return p;
@@ -142,6 +143,8 @@ proto_init_instance(struct proto *p)
* protocol does), you needn't to worry about this function since the
* connection to the protocol's primary routing table is initialized
* automatically by the core code.
+ *
+ * Returns pointer to announce hook or NULL
*/
struct announce_hook *
proto_add_announce_hook(struct proto *p, struct rtable *t)
@@ -152,7 +155,7 @@ proto_add_announce_hook(struct proto *p, struct rtable *t)
return NULL;
DBG("Connecting protocol %s to table %s\n", p->name, t->name);
PD(p, "Connected to table %s", t->name);
- h = mb_alloc(p->pool, sizeof(struct announce_hook));
+ h = mb_allocz(p->pool, sizeof(struct announce_hook));
h->table = t;
h->proto = p;
h->next = p->ahooks;
@@ -161,6 +164,25 @@ proto_add_announce_hook(struct proto *p, struct rtable *t)
return h;
}
+/**
+ * proto_find_announce_hook - find announce hooks
+ * @p: protocol instance
+ * @t: routing table
+ *
+ * Returns pointer to announce hook or NULL
+ */
+struct announce_hook *
+proto_find_announce_hook(struct proto *p, struct rtable *t)
+{
+ struct announce_hook *a;
+
+ for (a = p->ahooks; a; a = a->next)
+ if (a->table == t)
+ return a;
+
+ return NULL;
+}
+
static void
proto_flush_hooks(struct proto *p)
{
@@ -324,6 +346,8 @@ proto_init(struct proto_config *c)
static int
proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type)
{
+ struct announce_hook *a;
+
/* If the protocol is DOWN, we just restart it */
if (p->proto_state == PS_DOWN)
return 0;
@@ -353,6 +377,19 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
p->debug = nc->debug;
p->mrtdump = nc->mrtdump;
+ /*
+ * Set import/export filters if protocol is connected to the route table.
+ * We need to to this before protocol-specific reconfigure hook due to
+ * possible filter manipulations in pipe-like protocols.
+ * If no connection to master table is available, skip this step.
+ * In this case filters are assigned in proto_feed_initial
+ */
+ if (a = p->thook)
+ {
+ a->in_filter = nc->in_filter;
+ a->out_filter = nc->out_filter;
+ }
+
/* Execute protocol specific reconfigure hook */
if (! (p->proto->reconfigure && p->proto->reconfigure(p, nc)))
return 0;
@@ -361,8 +398,6 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
PD(p, "Reconfigured");
p->cf = nc;
p->name = nc->name;
- p->in_filter = nc->in_filter;
- p->out_filter = nc->out_filter;
p->preference = nc->preference;
if (import_changed || export_changed)
@@ -552,6 +587,7 @@ void
protos_dump_all(void)
{
struct proto *p;
+ struct announce_hook *a;
debug("Protocols:\n");
@@ -559,10 +595,14 @@ protos_dump_all(void)
{
debug(" protocol %s state %s/%s\n", p->name,
p_states[p->proto_state], c_states[p->core_state]);
- if (p->in_filter)
- debug("\tInput filter: %s\n", filter_name(p->in_filter));
- if (p->out_filter != FILTER_REJECT)
- debug("\tOutput filter: %s\n", filter_name(p->out_filter));
+ for (a = p->ahooks; a; a = a->next)
+ {
+ debug("\tTABLE %s\n", a->table->name);
+ if (a->in_filter)
+ debug("\tInput filter: %s\n", filter_name(a->in_filter));
+ if (a->out_filter != FILTER_REJECT)
+ debug("\tOutput filter: %s\n", filter_name(a->out_filter));
+ }
if (p->disabled)
debug("\tDISABLED\n");
else if (p->proto->dump)
@@ -680,12 +720,30 @@ static void
proto_feed_initial(void *P)
{
struct proto *p = P;
+ struct announce_hook *a;
if (p->core_state != FS_FEEDING)
return;
DBG("Feeding protocol %s\n", p->name);
- proto_add_announce_hook(p, p->table);
+ if (a = proto_add_announce_hook(p, p->table))
+ {
+ /* Set master table */
+ p->thook = a;
+ /* Set filters */
+ a->in_filter = p->cf->in_filter;
+ a->out_filter = p->cf->out_filter;
+ /* Set pointer to stats */
+ a->stats = &p->stats;
+ }
+
+ /*
+ * Call reconfigure hook to permit possible filter changes.
+ * Old config is the same as new so no problems should arise
+ */
+ if (p->proto->reconfigure)
+ p->proto->reconfigure(p, p->cf);
+
if_feed_baby(p);
proto_feed_more(P);
}
@@ -854,9 +912,8 @@ proto_state_name(struct proto *p)
}
static void
-proto_do_show_stats(struct proto *p)
+proto_do_show_stats(struct proto_stats *s)
{
- struct proto_stats *s = &p->stats;
cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
s->imp_routes, s->exp_routes, s->pref_routes);
cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
@@ -875,11 +932,12 @@ proto_do_show_stats(struct proto *p)
}
#ifdef CONFIG_PIPE
-static void
-proto_do_show_pipe_stats(struct proto *p)
+void
+pipe_do_show_stats(struct proto *P)
{
- struct proto_stats *s1 = &p->stats;
- struct proto_stats *s2 = pipe_get_peer_stats(p);
+ struct pipe_proto *p = (struct pipe_proto *) P;
+ struct proto_stats *s1 = &P->stats;
+ struct proto_stats *s2 = &p->peer_stats;
/*
* Pipe stats (as anything related to pipes) are a bit tricky. There
@@ -899,20 +957,20 @@ proto_do_show_pipe_stats(struct proto *p)
*/
cli_msg(-1006, " Routes: %u imported, %u exported",
- s2->imp_routes, s1->imp_routes);
+ s1->imp_routes, s2->imp_routes);
cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
- s2->exp_updates_received, s2->exp_updates_rejected + s2->imp_updates_invalid,
- s2->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
+ s2->exp_updates_received, s2->exp_updates_rejected + s1->imp_updates_invalid,
+ s2->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
- s2->exp_withdraws_received, s2->imp_withdraws_invalid,
- s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
+ s2->exp_withdraws_received, s1->imp_withdraws_invalid,
+ s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u",
- s1->exp_updates_received, s1->exp_updates_rejected + s1->imp_updates_invalid,
- s1->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
+ s1->exp_updates_received, s1->exp_updates_rejected + s2->imp_updates_invalid,
+ s1->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u",
- s1->exp_withdraws_received, s1->imp_withdraws_invalid,
- s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
+ s1->exp_withdraws_received, s2->imp_withdraws_invalid,
+ s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
}
#endif
@@ -920,6 +978,7 @@ void
proto_cmd_show(struct proto *p, unsigned int verbose, int cnt)
{
byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE];
+ struct announce_hook *a;
/* First protocol - show header */
if (!cnt)
@@ -943,17 +1002,26 @@ proto_cmd_show(struct proto *p, unsigned int verbose, int cnt)
if (p->cf->router_id)
cli_msg(-1006, " Router ID: %R", p->cf->router_id);
cli_msg(-1006, " Preference: %d", p->preference);
- cli_msg(-1006, " Input filter: %s", filter_name(p->in_filter));
- cli_msg(-1006, " Output filter: %s", filter_name(p->out_filter));
- if (p->proto_state != PS_DOWN)
+ for (a = p->ahooks; a; a = a->next)
{
+ cli_msg(-1006, " Table: %s", a->table->name);
+ cli_msg(-1006, " Input filter: %s", filter_name(a->in_filter));
+ cli_msg(-1006, " Output filter: %s", filter_name(a->out_filter));
+
+ if (p->proto_state != PS_DOWN)
+ {
#ifdef CONFIG_PIPE
- if (proto_is_pipe(p))
- proto_do_show_pipe_stats(p);
- else
+ /* XXX: This block should be removed */
+ if (proto_is_pipe(p))
+ {
+ if (a == p->thook)
+ pipe_do_show_stats(p);
+ }
+ else
#endif
- proto_do_show_stats(p);
+ proto_do_show_stats(a->stats);
+ }
}
if (p->proto->show_proto_info)
diff --git a/nest/protocol.h b/nest/protocol.h
index a7518c2..09ccd9c 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -190,8 +190,7 @@ struct proto {
void (*rte_remove)(struct network *, struct rte *);
struct rtable *table; /* Our primary routing table */
- struct filter *in_filter; /* Input filter */
- struct filter *out_filter; /* Output filter */
+ struct announce_hook *thook; /* Announcement hook for the master table */
struct announce_hook *ahooks; /* Announcement hooks for this protocol */
struct fib_iterator *feed_iterator; /* Routing table iterator used during protocol feeding */
@@ -349,18 +348,13 @@ struct announce_hook {
node n;
struct rtable *table;
struct proto *proto;
+ struct filter *in_filter; /* Input filter */
+ struct filter *out_filter; /* Output filter */
+ struct proto_stats *stats; /* Per-table protocol statistics */
struct announce_hook *next; /* Next hook for the same protocol */
};
struct announce_hook *proto_add_announce_hook(struct proto *, struct rtable *);
-
-/*
- * Some pipe-specific nest hacks
- */
-
-#ifdef CONFIG_PIPE
-#include "proto/pipe/pipe.h"
-#endif
-
+struct announce_hook *proto_find_announce_hook(struct proto *p, struct rtable *t);
#endif
diff --git a/nest/route.h b/nest/route.h
index a4c0154..ff09178 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -218,6 +218,7 @@ typedef struct rte {
#define RA_ANY 2 /* Announcement of any route change */
struct config;
+struct announce_hook;
void rt_init(void);
void rt_preconfig(struct config *);
@@ -230,6 +231,8 @@ static inline net *net_get(rtable *tab, ip_addr addr, unsigned len) { return (ne
rte *rte_find(net *net, struct proto *p);
rte *rte_get_temp(struct rta *);
void rte_update(rtable *tab, net *net, struct proto *p, struct proto *src, rte *new);
+#define rte_update(tab, net, p, src, new) rte_do_update(tab, (p)->thook, net, p, src, new)
+void rte_do_update(rtable *tab, struct announce_hook *a, net *net, struct proto *p, struct proto *src, rte *new);
void rte_discard(rtable *tab, rte *old);
void rte_dump(rte *);
void rte_free(rte *);
diff --git a/nest/rt-table.c b/nest/rt-table.c
index e20d2f6..53578c0 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -187,21 +187,12 @@ static inline void
do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
{
struct proto *p = a->proto;
- struct filter *filter = p->out_filter;
- struct proto_stats *stats = &p->stats;
+ struct filter *filter = a->out_filter;
+ struct proto_stats *stats = a->stats;
rte *new0 = new;
rte *old0 = old;
int ok;
-#ifdef CONFIG_PIPE
- /* The secondary direction of the pipe */
- if (proto_is_pipe(p) && (p->table != a->table))
- {
- filter = p->in_filter;
- stats = pipe_get_peer_stats(p);
- }
-#endif
-
if (new)
{
stats->exp_updates_received++;
@@ -423,18 +414,14 @@ rte_same(rte *x, rte *y)
}
static void
-rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte *new, ea_list *tmpa)
+rte_recalculate(rtable *table, struct announce_hook *a, net *net, struct proto *p, struct proto *src, rte *new, ea_list *tmpa)
{
- struct proto_stats *stats = &p->stats;
+ struct proto_stats *stats;
rte *old_best = net->routes;
rte *old = NULL;
rte **k, *r, *s;
-#ifdef CONFIG_PIPE
- if (proto_is_pipe(p) && (p->table == table))
- stats = pipe_get_peer_stats(p);
-#endif
-
+ stats = a ? a->stats : &p->stats;
k = &net->routes; /* Find and remove original route from the same protocol */
while (old = *k)
{
@@ -607,8 +594,9 @@ rte_update_unlock(void)
}
/**
- * rte_update - enter a new update to a routing table
+ * rte_do_update - enter a new update to a routing table
* @table: table to be updated
+ * @a: pointer to table announce hook
* @net: network node
* @p: protocol submitting the update
* @src: protocol originating the update
@@ -648,28 +636,27 @@ rte_update_unlock(void)
*/
void
-rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new)
+rte_do_update(rtable *table, struct announce_hook *a, net *net, struct proto *p, struct proto *src, rte *new)
{
ea_list *tmpa = NULL;
- struct proto_stats *stats = &p->stats;
+ struct proto_stats *stats;
+ struct filter *filter;
-#ifdef CONFIG_PIPE
- if (proto_is_pipe(p) && (p->table == table))
- stats = pipe_get_peer_stats(p);
-#endif
+ if (a)
+ {
+ stats = a->stats;
+ filter = a->in_filter;
+ }
+ else
+ {
+ stats = &p->stats;
+ filter = p->cf->in_filter;
+ }
rte_update_lock();
if (new)
{
new->sender = p;
- struct filter *filter = p->in_filter;
-
- /* Do not filter routes going through the pipe,
- they are filtered in the export filter only. */
-#ifdef CONFIG_PIPE
- if (proto_is_pipe(p))
- filter = FILTER_ACCEPT;
-#endif
stats->imp_updates_received++;
if (!rte_validate(new))
@@ -706,13 +693,13 @@ rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new
else
stats->imp_withdraws_received++;
- rte_recalculate(table, net, p, src, new, tmpa);
+ rte_recalculate(table, a, net, p, src, new, tmpa);
rte_update_unlock();
return;
drop:
rte_free(new);
- rte_recalculate(table, net, p, src, NULL, NULL);
+ rte_recalculate(table, a, net, p, src, NULL, NULL);
rte_update_unlock();
}
@@ -735,7 +722,7 @@ void
rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during garbage collection */
{
rte_update_lock();
- rte_recalculate(t, old->net, old->sender, old->attrs->proto, NULL, NULL);
+ rte_recalculate(t, old->sender->thook, old->net, old->sender, old->attrs->proto, NULL, NULL);
rte_update_unlock();
}
@@ -1680,6 +1667,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
{
rte *e, *ee;
byte ia[STD_ADDRESS_P_LENGTH+8];
+ struct announce_hook *a;
int ok;
bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen);
@@ -1709,8 +1697,8 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
'configure soft' command may change the export filter
and do not update routes */
- if ((p1->out_filter == FILTER_REJECT) ||
- (p1->out_filter && f_run(p1->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
+ if ((a = proto_find_announce_hook(p1, d->table)) && ((a->out_filter == FILTER_REJECT) ||
+ (a->out_filter && f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)))
ok = 0;
}
}
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index 420c5a9..869ddb8 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -24,6 +24,7 @@
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
+#include "nest/cli.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "lib/string.h"
@@ -34,8 +35,9 @@ static void
pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs)
{
struct pipe_proto *p = (struct pipe_proto *) P;
- rtable *dest = (src_table == P->table) ? p->peer : P->table; /* The other side of the pipe */
+ rtable *dest;
struct proto *src;
+ struct announce_hook *h;
net *nn;
rte *e;
@@ -44,6 +46,17 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
if (!new && !old)
return;
+ if (src_table == P->table)
+ {
+ dest = p->peer;
+ h = p->a;
+ }
+ else
+ {
+ dest = P->table;
+ h = P->thook;
+ }
+
if (dest->pipe_busy)
{
log(L_ERR "Pipe loop detected when sending %I/%d to table %s",
@@ -85,7 +98,7 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
}
src_table->pipe_busy = 1;
- rte_update(dest, nn, &p->p, (p->mode == PIPE_OPAQUE) ? &p->p : src, e);
+ rte_do_update(dest, h, nn, &p->p, (p->mode == PIPE_OPAQUE) ? &p->p : src, e);
src_table->pipe_busy = 0;
}
@@ -126,6 +139,10 @@ pipe_start(struct proto *P)
/* Connect the protocol also to the peer routing table. */
a = proto_add_announce_hook(P, p->peer);
+ p->a = a;
+
+ /* Set up filters and stats */
+ a->stats = &p->peer_stats;
return PS_UP;
}
@@ -168,13 +185,57 @@ pipe_postconfig(struct proto_config *C)
static int
pipe_reconfigure(struct proto *P, struct proto_config *new)
{
- // struct pipe_proto *p = (struct pipe_proto *) P;
+ struct announce_hook *a, *pa;
+ struct pipe_proto *p = (struct pipe_proto *)P;
struct pipe_config *o = (struct pipe_config *) P->cf;
struct pipe_config *n = (struct pipe_config *) new;
if ((o->peer->table != n->peer->table) || (o->mode != n->mode))
return 0;
+ /*
+ * reconfigure can be called in any protocol status
+ */
+ if (!((a = P->thook) && (pa = p->a)))
+ return 1;
+
+ /* Update filters */
+ /*
+ * Export filter remains as is and import filter moves to
+ * peer table
+ *
+ * /------\ /------\
+ * | | --- (OUT)(1) (OUT)(3) --- | |
+ * | MAIN | | PIPE | | PEER |
+ * | | ---- (IN)(2) (IN)(4) --- | |
+ * \------/ \------/
+ *
+ * When new route is announced on MAIN table it gets checked by
+ * export filter (1), and, after that, it is announced to peer table
+ * via rte_update. import filter (4) is called. When new route is
+ * annouced in PEER table (3) and (2) are used. Oviously, there is
+ * no need in filtering the same route twice, so (4) and (2) filters
+ * should be set to pass all routes.
+ *
+ * User can configure (1) and (2) filters so we move filter (2) to
+ * (3) and set (2) and (4) to FILTER_ACCEPT
+ *
+ * This is the right place to do it since
+ * 1) configure hook is called exactly before initial feeding
+ * 2) it is (now) called exactly after setting new filters in proto_reconfigure
+ */
+
+ /*
+ * Check for changed filters:
+ * Import and export filters are checked in proto_reconfigure by
+ * comparing old/new configurations so no additional checks are
+ * required here
+ */
+
+ pa->out_filter = a->in_filter;
+ a->in_filter = FILTER_ACCEPT;
+ pa->in_filter = FILTER_ACCEPT;
+
return 1;
}
@@ -194,6 +255,7 @@ pipe_get_status(struct proto *P, byte *buf)
}
+
struct protocol proto_pipe = {
name: "Pipe",
template: "pipe%d",
diff --git a/proto/pipe/pipe.h b/proto/pipe/pipe.h
index fbd2129..69df612 100644
--- a/proto/pipe/pipe.h
+++ b/proto/pipe/pipe.h
@@ -22,6 +22,7 @@ struct pipe_proto {
struct proto p;
struct rtable *peer;
struct proto_stats peer_stats; /* Statistics for the direction peer->primary */
+ struct announce_hook *a; /* Announce hook for the peer table */
int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */
};
@@ -31,10 +32,6 @@ extern struct protocol proto_pipe;
static inline int proto_is_pipe(struct proto *p)
{ return p->proto == &proto_pipe; }
-static inline struct rtable * pipe_get_peer_table(struct proto *P)
-{ return ((struct pipe_proto *) P)->peer; }
-
-static inline struct proto_stats * pipe_get_peer_stats(struct proto *P)
-{ return &((struct pipe_proto *) P)->peer_stats; }
+void pipe_do_show_stats(struct proto *P);
#endif
--
1.7.3.2
--------------070306020804010102050307--
More information about the Bird-users
mailing list