diff options
author | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2017-04-25 19:02:31 +0200 |
---|---|---|
committer | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2017-04-25 19:02:31 +0200 |
commit | b29499996bbc1612a63a7e715bb53a8abf0940e3 (patch) | |
tree | 2cda557718d08df68f0be9f5b05283bedeeb71b1 /nest | |
parent | 6f535924ebbb5a08d96c4a8d0cf0984b130a0995 (diff) |
Nest: Update of show route cmd
Some code cleanup, multiple bugfixes, allows to specify also channel
for 'show route export'. Interesting how such apparenty simple thing
like show route cmd has plenty of ugly corner cases.
Diffstat (limited to 'nest')
-rw-r--r-- | nest/config.Y | 51 | ||||
-rw-r--r-- | nest/proto.c | 19 | ||||
-rw-r--r-- | nest/protocol.h | 1 | ||||
-rw-r--r-- | nest/route.h | 18 | ||||
-rw-r--r-- | nest/rt-table.c | 302 |
5 files changed, 248 insertions, 143 deletions
diff --git a/nest/config.Y b/nest/config.Y index 0b34fb24..b0f9642f 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -75,6 +75,9 @@ CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SO CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS) +/* For r_args_channel */ +CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC) + CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL) CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED) @@ -93,6 +96,7 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID) %type <cl> limit_spec %type <net> r_args_for_val %type <net_ptr> r_args_for +%type <t> r_args_channel CF_GRAMMAR @@ -514,7 +518,7 @@ CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filte r_args: /* empty */ { $$ = cfg_allocz(sizeof(struct rt_show_data)); - init_list(&($$->table)); + init_list(&($$->tables)); $$->filter = FILTER_ACCEPT; } | r_args net_any { @@ -566,13 +570,24 @@ r_args: | r_args export_mode SYM { struct proto_config *c = (struct proto_config *) $3->def; $$ = $1; - if ($$->export_mode) cf_error("Protocol specified twice"); + if ($$->export_mode) cf_error("Export specified twice"); if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name); $$->export_mode = $2; $$->export_protocol = c->proto; $$->running_on_config = c->proto->cf->global; $$->tables_defined_by = RSD_TDB_INDIRECT; } + | r_args export_mode SYM '.' r_args_channel { + struct proto_config *c = (struct proto_config *) $3->def; + $$ = $1; + if ($$->export_mode) cf_error("Export specified twice"); + if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name); + $$->export_mode = $2; + $$->export_channel = proto_find_channel_by_name(c->proto, $5); + if (!$$->export_channel) cf_error("Export channel not found"); + $$->running_on_config = c->proto->cf->global; + $$->tables_defined_by = RSD_TDB_INDIRECT; + } | r_args PROTOCOL SYM { struct proto_config *c = (struct proto_config *) $3->def; $$ = $1; @@ -590,16 +605,6 @@ r_args: $$ = $1; $$->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; - } ; r_args_for: @@ -642,6 +647,28 @@ export_mode: | NOEXPORT { $$ = RSEM_NOEXPORT; } ; +/* This is ugly hack */ +r_args_channel: + IPV4 { $$ = "ipv4"; } + | IPV4_MC { $$ = "ipv4-mc"; } + | IPV4_MPLS { $$ = "ipv4-mpls"; } + | IPV6 { $$ = "ipv6"; } + | IPV6_MC { $$ = "ipv6-mc"; } + | IPV6_MPLS { $$ = "ipv6-mpls"; } + | VPN4 { $$ = "vpn4"; } + | VPN4_MC { $$ = "vpn4-mc"; } + | VPN4_MPLS { $$ = "vpn4-mpls"; } + | VPN6 { $$ = "vpn6"; } + | VPN6_MC { $$ = "vpn6-mc"; } + | VPN6_MPLS { $$ = "vpn6-mpls"; } + | ROA4 { $$ = "roa4"; } + | ROA6 { $$ = "roa6"; } + | FLOW4 { $$ = "flow4"; } + | FLOW6 { $$ = "flow6"; } + | MPLS { $$ = "mpls"; } + | PRI { $$ = "pri"; } + | SEC { $$ = "sec"; } + ; CF_CLI_HELP(SHOW SYMBOLS, ..., [[Show all known symbolic names]]) CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|<symbol>], [[Show all known symbolic names]]) diff --git a/nest/proto.c b/nest/proto.c index 0a7a32a6..3d764df0 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -105,6 +105,25 @@ proto_find_channel_by_table(struct proto *p, struct rtable *t) } /** + * proto_find_channel_by_name - find channel by its name + * @p: protocol instance + * @n: channel name + * + * Returns pointer to channel or NULL + */ +struct channel * +proto_find_channel_by_name(struct proto *p, const char *n) +{ + struct channel *c; + + WALK_LIST(c, p->channels) + if (!strcmp(c->name, n)) + return c; + + return NULL; +} + +/** * proto_add_channel - connect protocol to a routing table * @p: protocol instance * @cf: channel configuration diff --git a/nest/protocol.h b/nest/protocol.h index 6efaaaf7..f0958678 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -563,6 +563,7 @@ static inline struct channel_config *proto_cf_main_channel(struct proto_config * { struct channel_config *cc = HEAD(pc->channels); return NODE_VALID(cc) ? cc : NULL; } struct channel *proto_find_channel_by_table(struct proto *p, struct rtable *t); +struct channel *proto_find_channel_by_name(struct proto *p, const char *n); struct channel *proto_add_channel(struct proto *p, struct channel_config *cf); int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf); diff --git a/nest/route.h b/nest/route.h index 12968cb8..6c9b00c2 100644 --- a/nest/route.h +++ b/nest/route.h @@ -313,26 +313,30 @@ struct rtable_config *rt_new_table(struct symbol *s, uint addr_type); struct rt_show_data_rtable { node n; rtable *table; + struct channel *export_channel; }; struct rt_show_data { net_addr *addr; - list table; - struct rt_show_data_rtable *tit; - struct filter *filter; + list tables; + struct rt_show_data_rtable *tab; /* Iterator over table list */ + struct rt_show_data_rtable *last_table; /* Last table in output */ + struct fib_iterator fit; /* Iterator over networks in table */ int verbose, tables_defined_by; - struct fib_iterator fit; + struct filter *filter; struct proto *show_protocol; struct proto *export_protocol; struct channel *export_channel; - int export_mode, primary_only, filtered; struct config *running_on_config; + int export_mode, primary_only, filtered, stats, show_for; + + int table_open; /* Iteration (fit) is open */ int net_counter, rt_counter, show_counter, table_counter; 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_add_table(struct rt_show_data *d, rtable *t); +struct rt_show_data_rtable * 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 */ diff --git a/nest/rt-table.c b/nest/rt-table.c index be764313..67ccc885 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -2504,6 +2504,18 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep) */ static void +rt_show_table(struct cli *c, struct rt_show_data *d) +{ + /* No table blocks in 'show route count' */ + if (d->stats == 2) + return; + + if (d->last_table) cli_printf(c, -1007, ""); + cli_printf(c, -1007, "Table %s:", d->tab->table->name); + d->last_table = d->tab; +} + +static void rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tmpa) { byte from[IPA_MAX_TEXT_LENGTH+8]; @@ -2535,8 +2547,8 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm else bsprintf(info, " (%d)", e->pref); - if (!d->show_counter) - cli_printf(c, -1007, "Table %s:", d->tit->table->name); + if (d->last_table != d->tab) + rt_show_table(c, d); 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); @@ -2570,7 +2582,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) rte *e, *ee; byte ia[NET_MAX_TEXT_LENGTH+1]; struct ea_list *tmpa; - struct channel *ec = d->export_channel; + struct channel *ec = d->tab->export_channel; int first = 1; int pass = 0; @@ -2592,6 +2604,10 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) rte_update_lock(); /* We use the update buffer for filtering */ tmpa = make_tmp_attrs(e, rte_update_pool); + /* Export channel is down, do not try to export routes to it */ + if (ec && (ec->export_state == ES_DOWN)) + goto skip; + /* Special case for merged export */ if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED)) { @@ -2604,7 +2620,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) } else if (d->export_mode) { - struct proto *ep = d->export_protocol; + struct proto *ep = ec->proto; int ic = ep->import_control ? ep->import_control(ep, &e, &tmpa, rte_update_pool) : 0; if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED) @@ -2656,25 +2672,19 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) } } -static struct channel * -rt_show_export_channel(struct rt_show_data *d) -{ - if (! d->export_protocol->rt_notify) - return NULL; - - 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; + struct rt_show_data_rtable *tab; /* 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); + if (d->table_open) + fit_get(&d->tab->table->fib, &d->fit); + + /* Unlock referenced tables */ + WALK_LIST(tab, d->tables) + rt_unlock_table(tab->table); } static void @@ -2686,84 +2696,110 @@ rt_show_cont(struct cli *c) #else unsigned max = 64; #endif - struct fib *fib = &d->tit->table->fib; + struct fib *fib = &d->tab->table->fib; struct fib_iterator *it = &d->fit; - if (d->export_mode) - { - /* Ensure we have current export channel */ - d->export_channel = rt_show_export_channel(d); - if (!d->export_channel || (d->export_channel->export_state == ES_DOWN)) - { - cli_printf(c, 8005, "Channel is down"); - rt_show_cleanup(c); - goto done; - } - } + if (d->running_on_config && (d->running_on_config != config)) + { + cli_printf(c, 8004, "Stopped due to reconfiguration"); + goto done; + } - FIB_ITERATE_START(fib, it, net, n) - { - if (!max--) - { - FIB_ITERATE_PUT(it); - return; - } - rt_show_net(c, n, d); - } - FIB_ITERATE_END; + if (!d->table_open) + { + FIB_ITERATE_INIT(&d->fit, &d->tab->table->fib); + d->table_open = 1; + d->table_counter++; - if (!d->show_counter && (d->tables_defined_by & RSD_TDB_SET)) - cli_printf(c, -1007, "Table %s:", d->tit->table->name); + d->show_counter_last = d->show_counter; + d->rt_counter_last = d->rt_counter; + d->net_counter_last = d->net_counter; - 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); + if (d->tables_defined_by & RSD_TDB_SET) + rt_show_table(c, d); + } - rt_unlock_table(d->tit->table); - d->table_counter++; - if (NODE_VALID(NODE_NEXT(d->tit))) + FIB_ITERATE_START(fib, it, net, n) + { + if (!max--) { - 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; + FIB_ITERATE_PUT(it); return; } + rt_show_net(c, n, d); + } + FIB_ITERATE_END; if (d->stats) - 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); + { + if (d->last_table != d->tab) + rt_show_table(c, d); + + 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->tab->table->name); + } + + d->table_open = 0; + d->tab = NODE_NEXT(d->tab); + + if (NODE_VALID(d->tab)) + return; + + if (d->stats && (d->table_counter > 1)) + { + if (d->last_table) cli_printf(c, -1007, ""); + 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 cli_printf(c, 0, ""); + done: + rt_show_cleanup(c); c->cont = c->cleanup = NULL; } -void rt_show_add_table(struct rt_show_data *d, rtable *t) +struct rt_show_data_rtable * +rt_show_add_table(struct rt_show_data *d, rtable *t) { - struct rt_show_data_rtable *rsdr = cfg_alloc(sizeof(struct rt_show_data_rtable)); - rsdr->table = t; - add_tail(&(d->table), &(rsdr->n)); + struct rt_show_data_rtable *tab = cfg_allocz(sizeof(struct rt_show_data_rtable)); + tab->table = t; + add_tail(&(d->tables), &(tab->n)); + return tab; } static inline void -rt_show_get_table(struct proto *p, struct rt_show_data *d) +rt_show_get_default_tables(struct rt_show_data *d) { struct channel *c; - WALK_LIST(c, p->channels) - if (c->table) - rt_show_add_table(d, c->table); + struct rt_show_data_rtable *tab; -} + if (d->export_channel) + { + c = d->export_channel; + tab = rt_show_add_table(d, c->table); + tab->export_channel = c; + return; + } -static inline void -rt_show_get_default_table(struct rt_show_data *d) -{ - if (d->export_protocol || d->show_protocol) + if (d->export_protocol) + { + WALK_LIST(c, d->export_protocol->channels) + { + if (c->export_state == ES_DOWN) + continue; + + tab = rt_show_add_table(d, c->table); + tab->export_channel = c; + } + return; + } + + if (d->show_protocol) { - rt_show_get_table(d->export_protocol ?: d->show_protocol, d); + WALK_LIST(c, d->show_protocol->channels) + rt_show_add_table(d, c->table); return; } @@ -2772,78 +2808,96 @@ rt_show_get_default_table(struct rt_show_data *d) rt_show_add_table(d, config->def_tables[i]->table); } -void -rt_show(struct rt_show_data *d) +static inline void +rt_show_prepare_tables(struct rt_show_data *d) { - net *n; + struct rt_show_data_rtable *tab, *tabx; - /* There may be implicit tables. */ - if (EMPTY_LIST(d->table)) rt_show_get_default_table(d); + /* Add implicit tables if no table is specified */ + if (EMPTY_LIST(d->tables)) + rt_show_get_default_tables(d); - /* Filtered routes are neither exported nor have sensible ordering */ - if (d->filtered && (d->export_mode || d->primary_only)) - cli_msg(0, ""); - - if (!d->addr) + WALK_LIST_DELSAFE(tab, tabx, d->tables) + { + /* Ensure there is defined export_channel for each table */ + if (d->export_mode) { - struct rt_show_data_rtable *rsdr; - WALK_LIST(rsdr, d->table) + if (!tab->export_channel && d->export_channel && + (tab->table == d->export_channel->table)) + tab->export_channel = d->export_channel; + + if (!tab->export_channel && d->export_protocol) + tab->export_channel = proto_find_channel_by_table(d->export_protocol, tab->table); + + if (!tab->export_channel) { - rt_lock_table(rsdr->table); + if (d->tables_defined_by & RSD_TDB_NMN) + cf_error("No export channel for table %s", tab->table->name); + + rem_node(&(tab->n)); + continue; } - d->tit = HEAD(d->table); - FIB_ITERATE_INIT(&d->fit, &d->tit->table->fib); - this_cli->cont = rt_show_cont; - this_cli->cleanup = rt_show_cleanup; - this_cli->rover = d; } - else + + /* Ensure specified network is compatible with each table */ + if (d->addr && (tab->table->addr_type != d->addr->type)) { - if (d->export_mode) - { - /* Find channel associated with the export protocol */ - d->export_channel = rt_show_export_channel(d); - if (!d->export_channel || (d->export_channel->export_state == ES_DOWN)) - { - cli_msg(8005, "Channel is down"); - return; - } - } + if (d->tables_defined_by & RSD_TDB_NMN) + cf_error("Incompatible type of prefix/ip for table %s", tab->table->name); - struct rt_show_data_rtable *rsdr, *rn; - WALK_LIST_DELSAFE(rsdr, rn, d->table) - { - /* Check table net types matching to query */ - if (rsdr->table->addr_type == d->addr->type) - continue; + rem_node(&(tab->n)); + continue; + } + } - if (d->tables_defined_by & RSD_TDB_NMN) - { - cli_msg(8001, "Incompatible type of prefix/ip with table %s", rsdr->table->name); - return; - } + /* Ensure there is at least one table */ + if (EMPTY_LIST(d->tables)) + cf_error("No valid tables"); +} - rem_node(&(rsdr->n)); - } +void +rt_show(struct rt_show_data *d) +{ + struct rt_show_data_rtable *tab; + net *n; - WALK_LIST(rsdr, d->table) - { - d->tit = rsdr; + /* Filtered routes are neither exported nor have sensible ordering */ + if (d->filtered && (d->export_mode || d->primary_only)) + cf_error("Incompatible show route options"); - if (d->show_for) - n = net_route(rsdr->table, d->addr); - else - n = net_find(rsdr->table, d->addr); + rt_show_prepare_tables(d); - if (n) - rt_show_net(this_cli, n, d); - } + if (!d->addr) + { + WALK_LIST(tab, d->tables) + rt_lock_table(tab->table); + + /* There is at least one table */ + d->tab = HEAD(d->tables); + this_cli->cont = rt_show_cont; + this_cli->cleanup = rt_show_cleanup; + this_cli->rover = d; + } + else + { + WALK_LIST(tab, d->tables) + { + d->tab = tab; - if (d->rt_counter) - cli_msg(0, ""); + if (d->show_for) + n = net_route(tab->table, d->addr); else - cli_msg(8001, "Network not found in any specified table"); + n = net_find(tab->table, d->addr); + + if (n) + rt_show_net(this_cli, n, d); } + + if (d->rt_counter) + cli_msg(0, ""); + else + cli_msg(8001, "Network not found"); + } } /* |