diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2012-03-15 11:58:08 +0100 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2012-03-15 12:13:04 +0100 |
commit | c0adf7e9fc0bb920175a639c6f56ed7b4190f3e4 (patch) | |
tree | 8bc6ac9e4c9288d7e7009a80a04d7278325ff05c /nest/proto.c | |
parent | 46c1a583a5c1ea81e8d8f372bd7f614506a63938 (diff) |
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.
Diffstat (limited to 'nest/proto.c')
-rw-r--r-- | nest/proto.c | 210 |
1 files changed, 122 insertions, 88 deletions
diff --git a/nest/proto.c b/nest/proto.c index 0fc72ce1..9a7f123e 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -112,8 +112,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; @@ -126,49 +124,102 @@ proto_init_instance(struct proto *p) p->pool = rp_new(proto_pool, p->proto->name); p->attn = ev_new(p->pool); p->attn->data = p; - rt_lock_table(p->table); + + if (! p->proto->multitable) + rt_lock_table(p->table); } +extern pool *rt_table_pool; /** * proto_add_announce_hook - connect protocol to a routing table * @p: protocol instance * @t: routing table to connect to + * @in: input filter + * @out: output filter + * @stats: per-table protocol statistics * * This function creates a connection between the protocol instance @p * and the routing table @t, making the protocol hear all changes in * the table. * + * The announce hook is linked in the protocol ahook list and, if the + * protocol accepts routes, also in the table ahook list. Announce + * hooks are allocated from the routing table resource pool, they are + * unlinked from the table ahook list after the protocol went down, + * (in proto_schedule_flush()) and they are automatically freed after the + * protocol is flushed (in proto_fell_down()). + * * Unless you want to listen to multiple routing tables (as the Pipe * 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. */ struct announce_hook * -proto_add_announce_hook(struct proto *p, struct rtable *t) +proto_add_announce_hook(struct proto *p, struct rtable *t, struct filter *in, + struct filter *out, struct proto_stats *stats) { struct announce_hook *h; - if (!p->rt_notify) - 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(rt_table_pool, sizeof(struct announce_hook)); h->table = t; h->proto = p; + h->in_filter = in; + h->out_filter = out; + h->stats = stats; + h->next = p->ahooks; p->ahooks = h; - add_tail(&t->hooks, &h->n); + + if (p->rt_notify) + add_tail(&t->hooks, &h->n); 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) +proto_unlink_ahooks(struct proto *p) { struct announce_hook *h; - for(h=p->ahooks; h; h=h->next) - rem_node(&h->n); + if (p->rt_notify) + for(h=p->ahooks; h; h=h->next) + rem_node(&h->n); +} + +static void +proto_free_ahooks(struct proto *p) +{ + struct announce_hook *h, *hn; + + for(h = p->ahooks; h; h = hn) + { + hn = h->next; + mb_free(h); + } + p->ahooks = NULL; + p->main_ahook = NULL; } /** @@ -322,6 +373,8 @@ proto_init(struct proto_config *c) return q; } +int proto_reconfig_type; /* Hack to propagate type info to pipe reconfigure hook */ + static int proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type) { @@ -336,23 +389,10 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config (proto_get_router_id(nc) != proto_get_router_id(oc))) return 0; - int import_changed = (type != RECONFIG_SOFT) && ! filter_same(nc->in_filter, oc->in_filter); - int export_changed = (type != RECONFIG_SOFT) && ! filter_same(nc->out_filter, oc->out_filter); - - /* We treat a change in preferences by reimporting routes */ - if (nc->preference != oc->preference) - import_changed = 1; - - /* If the protocol in not UP, it has no routes and we can ignore such changes */ - if (p->proto_state != PS_UP) - import_changed = export_changed = 0; - - /* Without this hook we cannot reload routes and have to restart the protocol */ - if (import_changed && ! p->reload_routes) - return 0; p->debug = nc->debug; p->mrtdump = nc->mrtdump; + proto_reconfig_type = type; /* Execute protocol specific reconfigure hook */ if (! (p->proto->reconfigure && p->proto->reconfigure(p, nc))) @@ -362,14 +402,37 @@ 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; + + /* Multitable protocols handle rest in their reconfigure hooks */ + if (p->proto->multitable) + return 1; + + /* Update filters in the main announce hook */ + if (p->main_ahook) + { + p->main_ahook->in_filter = nc->in_filter; + p->main_ahook->out_filter = nc->out_filter; + } + + /* Update routes when filters changed. If the protocol in not UP, + it has no routes and we can ignore such changes */ + if ((p->proto_state != PS_UP) || (type == RECONFIG_SOFT)) + return 1; + + int import_changed = ! filter_same(nc->in_filter, oc->in_filter); + int export_changed = ! filter_same(nc->out_filter, oc->out_filter); + + /* We treat a change in preferences by reimporting routes */ + if (nc->preference != oc->preference) + import_changed = 1; + if (import_changed || export_changed) log(L_INFO "Reloading protocol %s", p->name); - if (import_changed && ! p->reload_routes(p)) + /* If import filter changed, call reload hook */ + if (import_changed && ! (p->reload_routes && p->reload_routes(p))) { /* Now, the protocol is reconfigured. But route reload failed and we have to do regular protocol restart. */ @@ -553,6 +616,7 @@ void protos_dump_all(void) { struct proto *p; + struct announce_hook *a; debug("Protocols:\n"); @@ -560,10 +624,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) @@ -647,7 +715,10 @@ proto_fell_down(struct proto *p) log(L_ERR "Protocol %s is down but still has %d routes", p->name, p->stats.imp_routes); bzero(&p->stats, sizeof(struct proto_stats)); - rt_unlock_table(p->table); + proto_free_ahooks(p); + + if (! p->proto->multitable) + rt_unlock_table(p->table); if (p->proto->cleanup) p->proto->cleanup(p); @@ -686,7 +757,7 @@ proto_feed_initial(void *P) return; DBG("Feeding protocol %s\n", p->name); - proto_add_announce_hook(p, p->table); + if_feed_baby(p); proto_feed_more(P); } @@ -701,7 +772,7 @@ proto_schedule_flush(struct proto *p) DBG("%s: Scheduling flush\n", p->name); p->core_state = FS_FLUSHING; proto_relink(p); - proto_flush_hooks(p); + proto_unlink_ahooks(p); ev_schedule(proto_flush_event); } @@ -716,6 +787,11 @@ proto_schedule_feed(struct proto *p, int initial) if (!initial) p->stats.exp_routes = 0; + /* Connect protocol to routing table */ + if (initial && !p->proto->multitable) + p->main_ahook = proto_add_announce_hook(p, p->table, + p->cf->in_filter, p->cf->out_filter, &p->stats); + proto_relink(p); p->attn->hook = initial ? proto_feed_initial : proto_feed_more; ev_schedule(p->attn); @@ -855,9 +931,8 @@ proto_state_name(struct proto *p) } static void -proto_do_show_stats(struct proto *p) +proto_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,47 +950,17 @@ proto_do_show_stats(struct proto *p) s->exp_withdraws_received, s->exp_withdraws_accepted); } -#ifdef CONFIG_PIPE -static void -proto_do_show_pipe_stats(struct proto *p) +void +proto_show_basic_info(struct proto *p) { - struct proto_stats *s1 = &p->stats; - struct proto_stats *s2 = pipe_get_peer_stats(p); - - /* - * Pipe stats (as anything related to pipes) are a bit tricky. There - * are two sets of stats - s1 for routes going from the primary - * routing table to the secondary routing table ('exported' from the - * user point of view) and s2 for routes going in the other - * direction ('imported' from the user point of view). - * - * 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, ...) - */ + // cli_msg(-1006, " Table: %s", p->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)); - cli_msg(-1006, " Routes: %u imported, %u exported", - s2->imp_routes, s1->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); - 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); - 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); - 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); + if (p->proto_state != PS_DOWN) + proto_show_stats(&p->stats); } -#endif void proto_cmd_show(struct proto *p, unsigned int verbose, int cnt) @@ -943,22 +988,11 @@ proto_cmd_show(struct proto *p, unsigned int verbose, int cnt) cli_msg(-1006, " Description: %s", p->cf->dsc); 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) - { -#ifdef CONFIG_PIPE - if (proto_is_pipe(p)) - proto_do_show_pipe_stats(p); - else -#endif - proto_do_show_stats(p); - } if (p->proto->show_proto_info) p->proto->show_proto_info(p); + else + proto_show_basic_info(p); cli_msg(-1006, ""); } |