mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Initial commit for new protocol `stats'
The protocol was created as a copy of pipe protocol. Many things left unchanged.
This commit is contained in:
parent
5be34f5ab4
commit
a0edbf88e4
@ -304,7 +304,7 @@ if test "$enable_mpls_kernel" != no ; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip rpki static"
|
all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip rpki static stats"
|
||||||
|
|
||||||
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
|
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
|
||||||
|
|
||||||
|
@ -84,7 +84,8 @@ void protos_dump_all(void);
|
|||||||
extern struct protocol
|
extern struct protocol
|
||||||
proto_device, proto_radv, proto_rip, proto_static, proto_mrt,
|
proto_device, proto_radv, proto_rip, proto_static, proto_mrt,
|
||||||
proto_ospf, proto_perf,
|
proto_ospf, proto_perf,
|
||||||
proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki;
|
proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki,
|
||||||
|
proto_stats;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Routing Protocol Instance
|
* Routing Protocol Instance
|
||||||
|
@ -7,5 +7,6 @@ C pipe
|
|||||||
C radv
|
C radv
|
||||||
C rip
|
C rip
|
||||||
C rpki
|
C rpki
|
||||||
|
C stats
|
||||||
C static
|
C static
|
||||||
S ../nest/rt-dev.c
|
S ../nest/rt-dev.c
|
||||||
|
1
proto/stats/Doc
Normal file
1
proto/stats/Doc
Normal file
@ -0,0 +1 @@
|
|||||||
|
S stats.c
|
7
proto/stats/Makefile
Normal file
7
proto/stats/Makefile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
src := stats.c
|
||||||
|
obj := $(src-o-files)
|
||||||
|
$(all-daemon)
|
||||||
|
$(cf-local)
|
||||||
|
$(call proto-build,stats_build)
|
||||||
|
|
||||||
|
tests_objs := $(tests_objs) $(src-o-files)
|
63
proto/stats/config.Y
Normal file
63
proto/stats/config.Y
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- Table-to-Table Protocol Configuration
|
||||||
|
*
|
||||||
|
* (c) 1999 Martin Mares <mj@ucw.cz>
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
CF_HDR
|
||||||
|
|
||||||
|
/* old: #include "proto/pipe/pipe.h" */
|
||||||
|
#include "proto/stats/stats.h"
|
||||||
|
|
||||||
|
CF_DEFINES
|
||||||
|
|
||||||
|
/* old: #define PIPE_CFG ((struct pipe_config *) this_proto) */
|
||||||
|
#define STATS_CFG ((struct stats_config *) this_proto)
|
||||||
|
|
||||||
|
CF_DECLS
|
||||||
|
|
||||||
|
/* TODO here add more keywords */
|
||||||
|
/* old: CF_KEYWORDS(PIPE, PEER, TABLE, MAX, GENERATION) */
|
||||||
|
CF_KEYWORDS(STATS, PEER, TABLE, MAX, GENERATION)
|
||||||
|
|
||||||
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
proto: stats_proto '}' { this_channel = NULL; } ;
|
||||||
|
|
||||||
|
stats_proto_start: proto_start STATS
|
||||||
|
{
|
||||||
|
this_proto = proto_config_new(&proto_stats, $1);
|
||||||
|
STATS_CFG->max_generation = 16;
|
||||||
|
}
|
||||||
|
proto_name
|
||||||
|
{
|
||||||
|
this_channel = proto_cf_main_channel(this_proto);
|
||||||
|
if (!this_channel) {
|
||||||
|
this_channel = channel_config_new(NULL, NULL, 0, this_proto);
|
||||||
|
this_channel->in_filter = FILTER_ACCEPT;
|
||||||
|
this_channel->out_filter = FILTER_ACCEPT;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stats_proto:
|
||||||
|
stats_proto_start '{'
|
||||||
|
| stats_proto proto_item ';'
|
||||||
|
| stats_proto channel_item_ ';'
|
||||||
|
| stats_proto IMPORT IN net_any imexport ';' {
|
||||||
|
if (this_channel->net_type && ($4->type != this_channel->net_type))
|
||||||
|
cf_error("Incompatible export prefilter type");
|
||||||
|
STATS_CFG->in_subprefix = $4;
|
||||||
|
this_channel->in_filter = $5;
|
||||||
|
}
|
||||||
|
| stats_proto PEER TABLE rtable ';' { STATS_CFG->peer = $4; }
|
||||||
|
| stats_proto MAX GENERATION expr ';' {
|
||||||
|
if (($4 < 1) || ($4 > 254)) cf_error("Max generation must be in range 1..254, got %u", $4);
|
||||||
|
STATS_CFG->max_generation = $4;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
CF_CODE
|
||||||
|
|
||||||
|
CF_END
|
320
proto/stats/stats.c
Normal file
320
proto/stats/stats.c
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- Table-to-Table Routing Protocol a.k.a Pipe
|
||||||
|
*
|
||||||
|
* (c) 1999--2000 Martin Mares <mj@ucw.cz>
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOC: Pipe
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
#include "nest/bird.h"
|
||||||
|
#include "nest/iface.h"
|
||||||
|
#include "nest/protocol.h"
|
||||||
|
#include "nest/rt.h"
|
||||||
|
#include "nest/cli.h"
|
||||||
|
#include "conf/conf.h"
|
||||||
|
#include "filter/filter.h"
|
||||||
|
#include "lib/string.h"
|
||||||
|
|
||||||
|
#include "stats.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_BGP
|
||||||
|
#include "proto/bgp/bgp.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
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
|
||||||
|
stats_preexport(struct channel *c, rte *e)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stats_reload_routes(struct channel *C)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
struct proto *P = proto_new(CF);
|
||||||
|
struct stats_proto *p = (void *) P;
|
||||||
|
struct stats_config *cf = (void *) CF;
|
||||||
|
|
||||||
|
P->rt_notify = stats_rt_notify;
|
||||||
|
P->preexport = stats_preexport;
|
||||||
|
P->reload_routes = stats_reload_routes;
|
||||||
|
|
||||||
|
p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
|
||||||
|
|
||||||
|
stats_configure_channels(p, cf);
|
||||||
|
|
||||||
|
return P;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
stats_reconfigure(struct proto *P, struct proto_config *CF)
|
||||||
|
{
|
||||||
|
struct stats_proto *p = (void *) P;
|
||||||
|
struct stats_config *cf = (void *) CF;
|
||||||
|
|
||||||
|
return stats_configure_channels(p, cf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stats_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUSED)
|
||||||
|
{
|
||||||
|
/* Just a shallow copy, not many items here */
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
stats_show_proto_info(struct proto *P)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
stats_update_debug(struct proto *P)
|
||||||
|
{
|
||||||
|
struct stats_proto *p = (void *) P;
|
||||||
|
|
||||||
|
p->pri->debug = p->sec->debug = p->p.debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct protocol proto_stats = {
|
||||||
|
.name = "Stats",
|
||||||
|
.template = "stat%d",
|
||||||
|
.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,
|
||||||
|
.get_status = stats_get_status,
|
||||||
|
.show_proto_info = stats_show_proto_info
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
stats_build(void)
|
||||||
|
{
|
||||||
|
proto_build(&proto_stats);
|
||||||
|
}
|
26
proto/stats/stats.h
Normal file
26
proto/stats/stats.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* BIRD -- Table-to-Table Routing Protocol a.k.a Pipe
|
||||||
|
*
|
||||||
|
* (c) 1999 Martin Mares <mj@ucw.cz>
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIRD_STATS_H_
|
||||||
|
#define _BIRD_STATS_H_
|
||||||
|
|
||||||
|
struct stats_config {
|
||||||
|
struct proto_config c;
|
||||||
|
struct rtable_config *peer; /* Table we're connected to */
|
||||||
|
const net_addr *in_subprefix;
|
||||||
|
u8 max_generation;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stats_proto {
|
||||||
|
struct proto p;
|
||||||
|
struct channel *pri;
|
||||||
|
struct channel *sec;
|
||||||
|
struct tbf rl_gen;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user