From c0adf7e9fc0bb920175a639c6f56ed7b4190f3e4 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Thu, 15 Mar 2012 11:58:08 +0100 Subject: Better support for multitable protocols. The nest-protocol interaction is changed to better handle multitable protocols. Multitable protocols now declare that by 'multitable' field, which tells nest that a protocol handles things related to proto-rtable interaction (table locking, announce hook adding, reconfiguration of filters) itself. Filters and stats are moved to announce hooks, a protocol could have different filters and stats to different tables. The patch is based on one from Alexander V. Chernikov, thanks. --- proto/pipe/pipe.c | 189 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 146 insertions(+), 43 deletions(-) (limited to 'proto/pipe/pipe.c') diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index fe8618b6..36b06d43 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -16,6 +16,17 @@ * * To avoid pipe loops, Pipe keeps a `being updated' flag in each routing * table. + * + * A pipe has two announce hooks, the first connected to the main + * table, the second connected to the peer table. When a new route is + * announced on the main table, it gets checked by an export filter in + * ahook 1, and, after that, it is announced to the peer table via + * rte_update(), an import filter in ahook 2 is called. When a new + * route is announced in the peer table, an export filter in ahook2 + * and an import filter in ahook 1 are used. Oviously, there is no + * need in filtering the same route twice, so both import filters + * are set to accept, while user configured 'import' and 'export' + * filters are used as export filters in ahooks 2 and 1. */ #undef LOCAL_DEBUG @@ -24,6 +35,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,7 +46,8 @@ 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 */ + struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook; + rtable *dst_table = ah->table; struct proto *src; net *nn; @@ -44,13 +57,14 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e if (!new && !old) return; - if (dest->pipe_busy) + if (dst_table->pipe_busy) { log(L_ERR "Pipe loop detected when sending %I/%d to table %s", - n->n.prefix, n->n.pxlen, dest->name); + n->n.prefix, n->n.pxlen, dst_table->name); return; } - nn = net_get(dest, n->n.prefix, n->n.pxlen); + + nn = net_get(dst_table, n->n.prefix, n->n.pxlen); if (new) { memcpy(&a, new->attrs, sizeof(rta)); @@ -85,14 +99,14 @@ 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_update2(ah, nn, e, (p->mode == PIPE_OPAQUE) ? &p->p : src); src_table->pipe_busy = 0; } static int pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED) { - struct proto *pp = (*ee)->sender; + struct proto *pp = (*ee)->sender->proto; if (pp == P) return -1; /* Avoid local loops automatically */ @@ -112,20 +126,39 @@ pipe_reload_routes(struct proto *P) return 1; } +static struct proto * +pipe_init(struct proto_config *C) +{ + struct pipe_config *c = (struct pipe_config *) C; + struct proto *P = proto_new(C, sizeof(struct pipe_proto)); + struct pipe_proto *p = (struct pipe_proto *) P; + + p->mode = c->mode; + p->peer_table = c->peer->table; + P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY; + P->rt_notify = pipe_rt_notify; + P->import_control = pipe_import_control; + P->reload_routes = pipe_reload_routes; + + return P; +} + static int pipe_start(struct proto *P) { struct pipe_proto *p = (struct pipe_proto *) P; - struct announce_hook *a; - /* Clean up the secondary stats */ - bzero(&p->peer_stats, sizeof(struct proto_stats)); + /* Lock both tables, unlock is handled in pipe_cleanup() */ + rt_lock_table(P->table); + rt_lock_table(p->peer_table); - /* Lock the peer table, unlock is handled in pipe_cleanup() */ - rt_lock_table(p->peer); + /* Going directly to PS_UP - prepare for feeding, + connect the protocol to both routing tables */ - /* Connect the protocol also to the peer routing table. */ - a = proto_add_announce_hook(P, p->peer); + P->main_ahook = proto_add_announce_hook(P, P->table, + FILTER_ACCEPT, P->cf->out_filter, &P->stats); + p->peer_ahook = proto_add_announce_hook(P, p->peer_table, + FILTER_ACCEPT, P->cf->in_filter, &p->peer_stats); return PS_UP; } @@ -134,24 +167,15 @@ static void pipe_cleanup(struct proto *P) { struct pipe_proto *p = (struct pipe_proto *) P; - rt_unlock_table(p->peer); -} -static struct proto * -pipe_init(struct proto_config *C) -{ - struct pipe_config *c = (struct pipe_config *) C; - struct proto *P = proto_new(C, sizeof(struct pipe_proto)); - struct pipe_proto *p = (struct pipe_proto *) P; + bzero(&P->stats, sizeof(struct proto_stats)); + bzero(&p->peer_stats, sizeof(struct proto_stats)); - p->peer = c->peer->table; - p->mode = c->mode; - P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY; - P->rt_notify = pipe_rt_notify; - P->import_control = pipe_import_control; - P->reload_routes = pipe_reload_routes; + P->main_ahook = NULL; + p->peer_ahook = NULL; - return P; + rt_unlock_table(P->table); + rt_unlock_table(p->peer_table); } static void @@ -165,16 +189,34 @@ pipe_postconfig(struct proto_config *C) cf_error("Primary table and peer table must be different"); } +extern int proto_reconfig_type; + static int pipe_reconfigure(struct proto *P, struct proto_config *new) { - // 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; + struct pipe_proto *p = (struct pipe_proto *)P; + struct proto_config *old = P->cf; + struct pipe_config *oc = (struct pipe_config *) old; + struct pipe_config *nc = (struct pipe_config *) new; - if ((o->peer->table != n->peer->table) || (o->mode != n->mode)) + if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode)) return 0; + /* Update output filters in ahooks */ + if (P->main_ahook) + P->main_ahook->out_filter = new->out_filter; + + if (p->peer_ahook) + p->peer_ahook->out_filter = new->in_filter; + + if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT)) + return 1; + + if ((new->preference != old->preference) + || ! filter_same(new->in_filter, old->in_filter) + || ! filter_same(new->out_filter, old->out_filter)) + proto_request_feeding(P); + return 1; } @@ -190,19 +232,80 @@ pipe_get_status(struct proto *P, byte *buf) { struct pipe_proto *p = (struct pipe_proto *) P; - bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer->name); + bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_table->name); +} + +static void +pipe_show_stats(struct pipe_proto *p) +{ + struct proto_stats *s1 = &p->p.stats; + struct proto_stats *s2 = &p->peer_stats; + + /* + * Pipe stats (as anything related to pipes) are a bit tricky. There + * are two sets of stats - s1 for ahook to the primary routing and + * s2 for the ahook to the secondary routing table. The user point + * of view is that routes going from the primary routing table to + * the secondary routing table are 'exported', while routes going in + * the other direction are 'imported'. + * + * Each route going through a pipe is, technically, first exported + * to the pipe and then imported from that pipe and such operations + * are counted in one set of stats according to the direction of the + * route propagation. Filtering is done just in the first part + * (export). Therefore, we compose stats for one directon for one + * user direction from both import and export stats, skipping + * immediate and irrelevant steps (exp_updates_accepted, + * imp_updates_received, imp_updates_filtered, ...). + * + * Rule of thumb is that stats s1 have the correct 'polarity' + * (imp/exp), while stats s2 have switched 'polarity'. + */ + + cli_msg(-1006, " Routes: %u imported, %u exported", + 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 + 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, 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 + 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, s2->imp_withdraws_invalid, + s2->imp_withdraws_ignored, s2->imp_withdraws_accepted); +} + +static void +pipe_show_proto_info(struct proto *P) +{ + struct pipe_proto *p = (struct pipe_proto *) P; + + // cli_msg(-1006, " Table: %s", P->table->name); + // cli_msg(-1006, " Peer table: %s", p->peer_table->name); + cli_msg(-1006, " Preference: %d", P->preference); + cli_msg(-1006, " Input filter: %s", filter_name(P->cf->in_filter)); + cli_msg(-1006, " Output filter: %s", filter_name(P->cf->out_filter)); + + if (P->proto_state != PS_DOWN) + pipe_show_stats(p); } struct protocol proto_pipe = { - name: "Pipe", - template: "pipe%d", - preference: DEF_PREF_PIPE, - postconfig: pipe_postconfig, - init: pipe_init, - start: pipe_start, - cleanup: pipe_cleanup, - reconfigure: pipe_reconfigure, - copy_config: pipe_copy_config, - get_status: pipe_get_status, + name: "Pipe", + template: "pipe%d", + multitable: 1, + preference: DEF_PREF_PIPE, + postconfig: pipe_postconfig, + init: pipe_init, + start: pipe_start, + cleanup: pipe_cleanup, + reconfigure: pipe_reconfigure, + copy_config: pipe_copy_config, + get_status: pipe_get_status, + show_proto_info: pipe_show_proto_info }; -- cgit v1.2.3