mirror of
https://gitlab.labs.nic.cz/labs/bird.git
synced 2024-05-11 16:54:54 +00:00
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.
This commit is contained in:
210
nest/proto.c
210
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);
|
||||
// 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));
|
||||
|
||||
/*
|
||||
* 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, " 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, "");
|
||||
}
|
||||
|
Reference in New Issue
Block a user