/*
* BIRD -- Table-to-Table Routing Protocol a.k.a Pipe
*
- * (c) 1999--2000 Martin Mares <mj@ucw.cz>
+ * (c) 2022 Vojtech Vilimek <vojtech.vilimek@nic.cz>
+ * (c) 2022 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
/**
- * DOC: Pipe
+ * DOC: Stats
*
- * The Pipe protocol is very simple. It just connects to two routing tables
- * using proto_add_announce_hook() and whenever it receives a rt_notify()
- * about a change in one of the tables, it converts it to a rte_update()
- * in the other one.
- *
- * 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. Route limits are
- * handled similarly, but on the import side of ahooks.
*/
#undef LOCAL_DEBUG
stats_rt_notify(struct proto *P, struct channel *src_ch, const net_addr *n, rte *new, const rte *old)
{
struct stats_proto *p = (void *) P;
- struct channel *dst = (src_ch == p->pri) ? p->sec : p->pri;
-
- if (!new && !old)
- return;
-
- if (new)
- {
- rte e0 = {
- .attrs = new->attrs,
- .src = new->src,
- .generation = new->generation + 1,
- };
-
- ea_unset_attr(&e0.attrs, 0, &ea_gen_hostentry);
-
- rte_update(dst, n, &e0, new->src);
- }
- else
- rte_update(dst, n, NULL, old->src);
}
static int
{
struct stats_proto *p = (void *) c->proto;
- /* Avoid direct loopbacks */
- if (e->sender == c->in_req.hook)
- return -1;
-
- /* Indirection check */
- uint max_generation = ((struct stats_config *) p->p.cf)->max_generation;
- if (e->generation >= max_generation)
- {
- log_rl(&p->rl_gen, L_ERR "Route overpiped (%u hops of %u configured in %s) in table %s: %N %s/%u:%u",
- e->generation, max_generation, c->proto->name,
- c->table->name, e->net, e->src->proto->name, e->src->private_id, e->src->global_id);
-
- return -1;
- }
-
return 0;
}
struct stats_proto *p = (void *) C->proto;
/* Route reload on one channel is just refeed on the other */
- channel_request_feeding((C == p->pri) ? p->sec : p->pri);
-}
-
-
-static void
-stats_postconfig(struct proto_config *CF)
-{
- struct stats_config *cf = (void *) CF;
- struct channel_config *cc = proto_cf_main_channel(CF);
-
- if (!cc->table)
- cf_error("Primary routing table not specified");
-
- if (!cf->peer)
- cf_error("Secondary routing table not specified");
-
- if (cc->table == cf->peer)
- cf_error("Primary table and peer table must be different");
-
- if (cc->table->addr_type != cf->peer->addr_type)
- cf_error("Primary table and peer table must have the same type");
-
- if (cc->out_subprefix && (cc->table->addr_type != cc->out_subprefix->type))
- cf_error("Export subprefix must match table type");
-
- if (cf->in_subprefix && (cc->table->addr_type != cf->in_subprefix->type))
- cf_error("Import subprefix must match table type");
-
- if (cc->rx_limit.action)
- cf_error("Pipe protocol does not support receive limits");
-
- if (cc->in_keep)
- cf_error("Pipe protocol prohibits keeping filtered routes");
-
- cc->debug = cf->c.debug;
+ channel_request_feeding(p->c);
}
-static int
-stats_configure_channels(struct stats_proto *s, struct stats_config *cf)
-{
- struct channel_config *cc = proto_cf_main_channel(&cf->c);
-
- struct channel_config pri_cf = {
- .name = "pri",
- .channel = cc->channel,
- .table = cc->table,
- .out_filter = cc->out_filter,
- .out_subprefix = cc->out_subprefix,
- .in_limit = cc->in_limit,
- .ra_mode = RA_ANY,
- .debug = cc->debug,
- .rpki_reload = cc->rpki_reload,
- };
-
- struct channel_config sec_cf = {
- .name = "sec",
- .channel = cc->channel,
- .table = cf->peer,
- .out_filter = cc->in_filter,
- .out_subprefix = cf->in_subprefix,
- .in_limit = cc->out_limit,
- .ra_mode = RA_ANY,
- .debug = cc->debug,
- .rpki_reload = cc->rpki_reload,
- };
-
- return
- proto_configure_channel(&s->p, &s->pri, &pri_cf) &&
- proto_configure_channel(&s->p, &s->sec, &sec_cf);
-}
static struct proto *
stats_init(struct proto_config *CF)
p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
- stats_configure_channels(p, cf);
+ struct channel_config *cc;
+ WALK_LIST(cc, CF->channels)
+ {
+ struct channel *c = NULL;
+ proto_configure_channel(P, &c, cc);
+ }
return P;
}
struct stats_proto *p = (void *) P;
struct stats_config *cf = (void *) CF;
- return stats_configure_channels(p, cf);
+ //return stats_configure_channels(p, cf);
+ return 1;
}
static void
stats_get_status(struct proto *P, byte *buf)
{
struct stats_proto *p = (void *) P;
-
- bsprintf(buf, "%s <=> %s", p->pri->table->name, p->sec->table->name);
}
static void
stats_show_stats(struct stats_proto *p)
{
- struct channel_import_stats *s1i = &p->pri->import_stats;
- struct channel_export_stats *s1e = &p->pri->export_stats;
- struct channel_import_stats *s2i = &p->sec->import_stats;
- struct channel_export_stats *s2e = &p->sec->export_stats;
-
- struct rt_import_stats *rs1i = p->pri->in_req.hook ? &p->pri->in_req.hook->stats : NULL;
- struct rt_export_stats *rs1e = p->pri->out_req.hook ? &p->pri->out_req.hook->stats : NULL;
- struct rt_import_stats *rs2i = p->sec->in_req.hook ? &p->sec->in_req.hook->stats : NULL;
- struct rt_export_stats *rs2e = p->sec->out_req.hook ? &p->sec->out_req.hook->stats : NULL;
-
- u32 pri_routes = p->pri->in_limit.count;
- u32 sec_routes = p->sec->in_limit.count;
- /*
- * 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",
- pri_routes, sec_routes);
- cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
- cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
- rs2e->updates_received, s2e->updates_rejected + s1i->updates_invalid,
- s2e->updates_filtered, rs1i->updates_ignored, rs1i->updates_accepted);
- cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
- rs2e->withdraws_received, s1i->withdraws_invalid,
- rs1i->withdraws_ignored, rs1i->withdraws_accepted);
- cli_msg(-1006, " Export updates: %10u %10u %10u %10u %10u",
- rs1e->updates_received, s1e->updates_rejected + s2i->updates_invalid,
- s1e->updates_filtered, rs2i->updates_ignored, rs2i->updates_accepted);
- cli_msg(-1006, " Export withdraws: %10u %10u --- %10u %10u",
- rs1e->withdraws_received, s2i->withdraws_invalid,
- rs2i->withdraws_ignored, rs2i->withdraws_accepted);
}
static void
{
struct stats_proto *p = (void *) P;
- cli_msg(-1006, " Channel %s", "main");
- cli_msg(-1006, " Table: %s", p->pri->table->name);
- cli_msg(-1006, " Peer table: %s", p->sec->table->name);
- cli_msg(-1006, " Import state: %s", rt_export_state_name(rt_export_get_state(p->sec->out_req.hook)));
- cli_msg(-1006, " Export state: %s", rt_export_state_name(rt_export_get_state(p->pri->out_req.hook)));
- cli_msg(-1006, " Import filter: %s", filter_name(p->sec->out_filter));
- cli_msg(-1006, " Export filter: %s", filter_name(p->pri->out_filter));
-
-
-
- channel_show_limit(&p->pri->in_limit, "Import limit:",
- (p->pri->limit_active & (1 << PLD_IN)), p->pri->limit_actions[PLD_IN]);
- channel_show_limit(&p->sec->in_limit, "Export limit:",
- (p->sec->limit_active & (1 << PLD_IN)), p->sec->limit_actions[PLD_IN]);
-
- if (P->proto_state != PS_DOWN)
- stats_show_stats(p);
}
void
{
struct stats_proto *p = (void *) P;
- p->pri->debug = p->sec->debug = p->p.debug;
+ p->c->debug = p->p.debug;
}
struct protocol proto_stats = {
.name = "Stats",
.template = "stat%d",
+ .channel_mask = NB_ANY,
.proto_size = sizeof(struct stats_proto),
.config_size = sizeof(struct stats_config),
- .postconfig = stats_postconfig,
.init = stats_init,
.reconfigure = stats_reconfigure,
.copy_config = stats_copy_config,