Ondrej Zajicek wrote: > On Tue, Nov 22, 2011 at 12:20:22PM +0400, Alexander V. Chernikov wrote: >>> Another related problem is that exp_routes statistics became unreliable >>> when 'configure soft' is used, export filter is changed and protocol >>> is not reloaded. This is probably a reason why BGP limits were originally >>> implemented for import only.
Another related patch permitting different filters to be used per each subscribed protocol tables. Patch also removes nearly all pipe-related hacks from nest.
>From d8a2545f93c51cb152ac130eab4209cc95468491 Mon Sep 17 00:00:00 2001 From: Alexander V. Chernikov <[email protected]> Date: Fri, 25 Nov 2011 15:39:48 +0000 Subject: [PATCH 1/1] * Implement per-table protocol hooks in core --- 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
