Client: multitable version of show route

This commit is contained in:
Jan Moskyto Matejka 2017-03-30 13:52:01 +02:00
parent bff21441dd
commit 2faf519cf9
5 changed files with 160 additions and 44 deletions

View file

@ -276,6 +276,8 @@ net_or_ipa:
| net_vpn6_ { $$ = *$1; } | net_vpn6_ { $$ = *$1; }
| IP4 { net_fill_ip4(&($$), $1, IP4_MAX_PREFIX_LENGTH); } | IP4 { net_fill_ip4(&($$), $1, IP4_MAX_PREFIX_LENGTH); }
| IP6 { net_fill_ip6(&($$), $1, IP6_MAX_PREFIX_LENGTH); } | IP6 { net_fill_ip6(&($$), $1, IP6_MAX_PREFIX_LENGTH); }
| VPN_RD IP4 { net_fill_vpn4(&($$), $2, IP4_MAX_PREFIX_LENGTH, $1); }
| VPN_RD IP6 { net_fill_vpn6(&($$), $2, IP6_MAX_PREFIX_LENGTH, $1); }
| SYM { | SYM {
if ($1->class == (SYM_CONSTANT | T_IP)) if ($1->class == (SYM_CONSTANT | T_IP))
net_fill_ip_host(&($$), SYM_VAL($1).ip); net_fill_ip_host(&($$), SYM_VAL($1).ip);

View file

@ -924,11 +924,13 @@ This argument can be omitted if there exists only a single instance.
Show the list of symbols defined in the configuration (names of Show the list of symbols defined in the configuration (names of
protocols, routing tables etc.). protocols, routing tables etc.).
<tag><label id="cli-show-route">show route [[for] <m/prefix/|<m/IP/] [table <m/t/] [filter <m/f/|where <m/c/] [(export|preexport|noexport) <m/p/] [protocol <m/p/] [<m/options/]</tag> <tag><label id="cli-show-route">show route [[for] <m/prefix/|<m/IP/] [table (<m/t/ | all)] [filter <m/f/|where <m/c/] [(export|preexport|noexport) <m/p/] [protocol <m/p/] [(stats|count) [by table]] [<m/options/]</tag>
Show contents of a routing table (by default of the main one or the Show contents of specified routing tables, that is routes, their metrics
table attached to a respective protocol), that is routes, their metrics
and (in case the <cf/all/ switch is given) all their attributes. and (in case the <cf/all/ switch is given) all their attributes.
<p>More tables can be specified by repeating the <cf>table <m/t/></cf> clause.
To cycle over all tables, specify <cf>table all</cf>.
<p>You can specify a <m/prefix/ if you want to print routes for a <p>You can specify a <m/prefix/ if you want to print routes for a
specific network. If you use <cf>for <m/prefix or IP/</cf>, you'll get specific network. If you use <cf>for <m/prefix or IP/</cf>, you'll get
the entry which will be used for forwarding of packets to the given the entry which will be used for forwarding of packets to the given
@ -946,18 +948,26 @@ This argument can be omitted if there exists only a single instance.
With <cf/noexport/, routes rejected by the export filter are printed With <cf/noexport/, routes rejected by the export filter are printed
instead. Note that routes not exported to the protocol for other reasons instead. Note that routes not exported to the protocol for other reasons
(e.g. secondary routes or routes imported from that protocol) are not (e.g. secondary routes or routes imported from that protocol) are not
printed even with <cf/noexport/. printed even with <cf/noexport/. These switches magically cycle over
all tables connected to the protocol.
<p>You can also select just routes added by a specific protocol. <p>You can also select just routes added by a specific protocol.
<cf>protocol <m/p/</cf>. <cf>protocol <m/p/</cf>. This switch also magically cycles over
all tables connected to the protocol.
<p>If BIRD is configured to keep filtered routes (see <cf/import keep <p>If BIRD is configured to keep filtered routes (see <cf/import keep
filtered/ option), you can show them instead of routes by using filtered/ option), you can show them instead of routes by using
<cf/filtered/ switch. <cf/filtered/ switch.
<p>If no table is specified in any way (<cf/table/, <cf/export/, <cf/preexport/, <cf/noexport/, <cf/protocol/),
the default tables are listed: <cf/master4/, <cf/master6/
and first declared table of any other net type.
<p>The <cf/stats/ switch requests showing of route statistics (the <p>The <cf/stats/ switch requests showing of route statistics (the
number of networks, number of routes before and after filtering). If number of networks, number of routes before and after filtering). If
you use <cf/count/ instead, only the statistics will be printed. you use <cf/count/ instead, only the statistics will be printed.
If you use <cf/stats by table/ or <cf/count by table/, the statistics
will be printed also per-table.
<tag><label id="cli-show-roa">show roa [<m/prefix/ | in <m/prefix/ | for <m/prefix/] [as <m/num/] [table <m/t/]</tag> <tag><label id="cli-show-roa">show roa [<m/prefix/ | in <m/prefix/ | for <m/prefix/] [as <m/num/] [table <m/t/]</tag>
Show contents of a ROA table (by default of the first one). You can Show contents of a ROA table (by default of the first one). You can

View file

@ -70,7 +70,7 @@ CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED) CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512) CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512)
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE) CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE)
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED) CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP) CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
@ -512,6 +512,7 @@ CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filte
r_args: r_args:
/* empty */ { /* empty */ {
$$ = cfg_allocz(sizeof(struct rt_show_data)); $$ = cfg_allocz(sizeof(struct rt_show_data));
init_list(&($$->table));
$$->filter = FILTER_ACCEPT; $$->filter = FILTER_ACCEPT;
} }
| r_args net_any { | r_args net_any {
@ -529,7 +530,15 @@ r_args:
| r_args TABLE SYM { | r_args TABLE SYM {
$$ = $1; $$ = $1;
if ($3->class != SYM_TABLE) cf_error("%s is not a table", $3->name); if ($3->class != SYM_TABLE) cf_error("%s is not a table", $3->name);
$$->table = ((struct rtable_config *)$3->def)->table; rt_show_add_table($$, ((struct rtable_config *)$3->def)->table);
$$->tables_defined_by = RSD_TDB_DIRECT;
}
| r_args TABLE ALL {
struct rtable_config *t;
$$ = $1;
WALK_LIST(t, config->tables)
rt_show_add_table($$, t->table);
$$->tables_defined_by = RSD_TDB_ALL;
} }
| r_args FILTER filter { | r_args FILTER filter {
$$ = $1; $$ = $1;
@ -561,6 +570,7 @@ r_args:
$$->export_mode = $2; $$->export_mode = $2;
$$->export_protocol = c->proto; $$->export_protocol = c->proto;
$$->running_on_config = c->proto->cf->global; $$->running_on_config = c->proto->cf->global;
$$->tables_defined_by = RSD_TDB_INDIRECT;
} }
| r_args PROTOCOL SYM { | r_args PROTOCOL SYM {
struct proto_config *c = (struct proto_config *) $3->def; struct proto_config *c = (struct proto_config *) $3->def;
@ -569,6 +579,7 @@ r_args:
if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name); if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
$$->show_protocol = c->proto; $$->show_protocol = c->proto;
$$->running_on_config = c->proto->cf->global; $$->running_on_config = c->proto->cf->global;
$$->tables_defined_by = RSD_TDB_INDIRECT;
} }
| r_args STATS { | r_args STATS {
$$ = $1; $$ = $1;
@ -578,6 +589,16 @@ r_args:
$$ = $1; $$ = $1;
$$->stats = 2; $$->stats = 2;
} }
| r_args STATS BY TABLE {
$$ = $1;
$$->stats = 1;
$$->stats_by_table = 1;
}
| r_args COUNT BY TABLE {
$$ = $1;
$$->stats = 2;
$$->stats_by_table = 1;
}
; ;
export_mode: export_mode:

View file

@ -308,21 +308,38 @@ void rt_feed_channel_abort(struct channel *c);
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type); struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
struct rt_show_data_rtable {
node n;
rtable *table;
};
struct rt_show_data { struct rt_show_data {
net_addr *addr; net_addr *addr;
rtable *table; list table;
struct rt_show_data_rtable *tit;
struct filter *filter; struct filter *filter;
int verbose; int verbose, tables_defined_by;
struct fib_iterator fit; struct fib_iterator fit;
struct proto *show_protocol; struct proto *show_protocol;
struct proto *export_protocol; struct proto *export_protocol;
struct channel *export_channel; struct channel *export_channel;
int export_mode, primary_only, filtered; int export_mode, primary_only, filtered;
struct config *running_on_config; struct config *running_on_config;
int net_counter, rt_counter, show_counter; int net_counter, rt_counter, show_counter, table_counter;
int stats, show_for; int net_counter_last, rt_counter_last, show_counter_last;
int stats, show_for, stats_by_table;
}; };
void rt_show(struct rt_show_data *); void rt_show(struct rt_show_data *);
void rt_show_add_table(struct rt_show_data *d, rtable *t);
/* Value of table definition mode in struct rt_show_data */
#define RSD_TDB_DEFAULT 0 /* no table specified */
#define RSD_TDB_INDIRECT 0 /* show route ... protocol P ... */
#define RSD_TDB_ALL RSD_TDB_SET /* show route ... table all ... */
#define RSD_TDB_DIRECT RSD_TDB_SET | RSD_TDB_NMN /* show route ... table X table Y ... */
#define RSD_TDB_SET 0x1 /* internal: show empty tables */
#define RSD_TDB_NMN 0x2 /* internal: need matching net */
/* Value of export_mode in struct rt_show_data */ /* Value of export_mode in struct rt_show_data */
#define RSEM_NONE 0 /* Export mode not used */ #define RSEM_NONE 0 /* Export mode not used */

View file

@ -2531,6 +2531,9 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
else else
bsprintf(info, " (%d)", e->pref); bsprintf(info, " (%d)", e->pref);
if (!d->show_counter)
cli_printf(c, -1007, "Table %s:", d->tit->table->name);
cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest), cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
a->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info); a->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
@ -2630,9 +2633,10 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
if (f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT) if (f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)
goto skip; goto skip;
d->show_counter++;
if (d->stats < 2) if (d->stats < 2)
rt_show_rte(c, ia, e, d, tmpa); rt_show_rte(c, ia, e, d, tmpa);
d->show_counter++;
ia[0] = 0; ia[0] = 0;
skip: skip:
@ -2654,7 +2658,19 @@ rt_show_export_channel(struct rt_show_data *d)
if (! d->export_protocol->rt_notify) if (! d->export_protocol->rt_notify)
return NULL; return NULL;
return proto_find_channel_by_table(d->export_protocol, d->table); return proto_find_channel_by_table(d->export_protocol, d->tit->table);
}
static void
rt_show_cleanup(struct cli *c)
{
struct rt_show_data *d = c->rover;
/* Unlink the iterator */
fit_get(&d->tit->table->fib, &d->fit);
rt_unlock_table(d->tit->table);
while (NODE_VALID(NODE_NEXT(d->tit)))
rt_unlock_table((d->tit = NODE_NEXT(d->tit))->table);
} }
static void static void
@ -2666,7 +2682,7 @@ rt_show_cont(struct cli *c)
#else #else
unsigned max = 64; unsigned max = 64;
#endif #endif
struct fib *fib = &d->table->fib; struct fib *fib = &d->tit->table->fib;
struct fib_iterator *it = &d->fit; struct fib_iterator *it = &d->fit;
if (d->export_mode) if (d->export_mode)
@ -2676,6 +2692,7 @@ rt_show_cont(struct cli *c)
if (!d->export_channel || (d->export_channel->export_state == ES_DOWN)) if (!d->export_channel || (d->export_channel->export_state == ES_DOWN))
{ {
cli_printf(c, 8005, "Channel is down"); cli_printf(c, 8005, "Channel is down");
rt_show_cleanup(c);
goto done; goto done;
} }
} }
@ -2690,35 +2707,65 @@ rt_show_cont(struct cli *c)
rt_show_net(c, n, d); rt_show_net(c, n, d);
} }
FIB_ITERATE_END; FIB_ITERATE_END;
if (!d->show_counter && (d->tables_defined_by & RSD_TDB_SET))
cli_printf(c, -1007, "Table %s:", d->tit->table->name);
if (d->stats && d->stats_by_table)
cli_printf(c, -1007, "%d of %d routes for %d networks in table %s", d->show_counter - d->show_counter_last, d->rt_counter - d->rt_counter_last, d->net_counter - d->net_counter_last, d->tit->table->name);
rt_unlock_table(d->tit->table);
d->table_counter++;
if (NODE_VALID(NODE_NEXT(d->tit)))
{
d->tit = NODE_NEXT(d->tit);
FIB_ITERATE_INIT(&d->fit, &d->tit->table->fib);
d->show_counter_last = d->show_counter;
d->rt_counter_last = d->rt_counter;
d->net_counter_last = d->net_counter;
d->show_counter = 0;
d->rt_counter = 0;
d->net_counter = 0;
return;
}
if (d->stats) if (d->stats)
cli_printf(c, 14, "%d of %d routes for %d networks", d->show_counter, d->rt_counter, d->net_counter); cli_printf(c, 14, "Total: %d of %d routes for %d networks in %d tables", d->show_counter, d->rt_counter, d->net_counter, d->table_counter);
else else
cli_printf(c, 0, ""); cli_printf(c, 0, "");
done: done:
c->cont = c->cleanup = NULL; c->cont = c->cleanup = NULL;
} }
static void void rt_show_add_table(struct rt_show_data *d, rtable *t)
rt_show_cleanup(struct cli *c)
{ {
struct rt_show_data *d = c->rover; struct rt_show_data_rtable *rsdr = cfg_alloc(sizeof(struct rt_show_data_rtable));
rsdr->table = t;
/* Unlink the iterator */ add_tail(&(d->table), &(rsdr->n));
fit_get(&d->table->fib, &d->fit);
} }
static inline rtable * static inline void
rt_show_get_table(struct proto *p) rt_show_get_table(struct proto *p, struct rt_show_data *d)
{ {
/* FIXME: Use a better way to handle multi-channel protocols */ struct channel *c;
WALK_LIST(c, p->channels)
if (c->table)
rt_show_add_table(d, c->table);
if (p->main_channel) }
return p->main_channel->table;
if (!EMPTY_LIST(p->channels)) static inline void
return ((struct channel *) HEAD(p->channels))->table; rt_show_get_default_table(struct rt_show_data *d)
{
if (d->export_protocol || d->show_protocol)
{
rt_show_get_table(d->export_protocol ?: d->show_protocol, d);
return;
}
return NULL; for (int i=1; i<NET_MAX; i++)
if (config->def_tables[i])
rt_show_add_table(d, config->def_tables[i]->table);
} }
void void
@ -2726,10 +2773,8 @@ rt_show(struct rt_show_data *d)
{ {
net *n; net *n;
/* Default is either a master table or a table related to a respective protocol */ /* There may be implicit tables. */
if (!d->table && d->export_protocol) d->table = rt_show_get_table(d->export_protocol); if (EMPTY_LIST(d->table)) rt_show_get_default_table(d);
if (!d->table && d->show_protocol) d->table = rt_show_get_table(d->show_protocol);
if (!d->table) d->table = config->def_tables[NET_IP4]->table; /* FIXME: iterate through all tables ? */
/* Filtered routes are neither exported nor have sensible ordering */ /* Filtered routes are neither exported nor have sensible ordering */
if (d->filtered && (d->export_mode || d->primary_only)) if (d->filtered && (d->export_mode || d->primary_only))
@ -2737,7 +2782,13 @@ rt_show(struct rt_show_data *d)
if (!d->addr) if (!d->addr)
{ {
FIB_ITERATE_INIT(&d->fit, &d->table->fib); struct rt_show_data_rtable *rsdr;
WALK_LIST(rsdr, d->table)
{
rt_lock_table(rsdr->table);
}
d->tit = HEAD(d->table);
FIB_ITERATE_INIT(&d->fit, &d->tit->table->fib);
this_cli->cont = rt_show_cont; this_cli->cont = rt_show_cont;
this_cli->cleanup = rt_show_cleanup; this_cli->cleanup = rt_show_cleanup;
this_cli->rover = d; this_cli->rover = d;
@ -2755,24 +2806,39 @@ rt_show(struct rt_show_data *d)
} }
} }
if (d->table->addr_type != d->addr->type) struct rt_show_data_rtable *rsdr, *rn;
WALK_LIST_DELSAFE(rsdr, rn, d->table)
{ {
cli_msg(8001, "Incompatible type of prefix/ip with table"); /* Check table net types matching to query */
if (rsdr->table->addr_type == d->addr->type)
continue;
if (d->tables_defined_by & RSD_TDB_NMN)
{
cli_msg(8001, "Incompatible type of prefix/ip with table %s", rsdr->table->name);
return; return;
} }
rem_node(&(rsdr->n));
}
WALK_LIST(rsdr, d->table)
{
d->tit = rsdr;
if (d->show_for) if (d->show_for)
n = net_route(d->table, d->addr); n = net_route(rsdr->table, d->addr);
else else
n = net_find(d->table, d->addr); n = net_find(rsdr->table, d->addr);
if (n) if (n)
rt_show_net(this_cli, n, d); rt_show_net(this_cli, n, d);
}
if (d->rt_counter) if (d->rt_counter)
cli_msg(0, ""); cli_msg(0, "");
else else
cli_msg(8001, "Network not in table"); cli_msg(8001, "Network not found in any specified table");
} }
} }