]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Client: multitable version of show route
authorJan Moskyto Matejka <mq@ucw.cz>
Thu, 30 Mar 2017 11:52:01 +0000 (13:52 +0200)
committerJan Moskyto Matejka <mq@ucw.cz>
Wed, 12 Apr 2017 14:04:22 +0000 (16:04 +0200)
conf/confbase.Y
doc/bird.sgml
nest/config.Y
nest/route.h
nest/rt-table.c

index d6a6951f78e31a2bb7f31703c00250665a64b490..ce71813072ae20c732d0c4bd844f66ba9105abac 100644 (file)
@@ -276,6 +276,8 @@ net_or_ipa:
  | net_vpn6_ { $$ = *$1; }
  | IP4 { net_fill_ip4(&($$), $1, IP4_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 {
      if ($1->class == (SYM_CONSTANT | T_IP))
        net_fill_ip_host(&($$), SYM_VAL($1).ip);
index e2e8964afe3a89141a5e8c500eb21edfe8134563..1777b8f3560ec55405a2ca6ca1cc8a51a8d41b53 100644 (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
        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>
-       Show contents of a routing table (by default of the main one or the
-       table attached to a respective protocol), that is routes, their metrics
+       <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 specified routing tables, that is routes, their metrics
        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
        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
@@ -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
        instead. Note that routes not exported to the protocol for other reasons
        (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.
-       <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
        filtered/ option), you can show them instead of routes by using
        <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
        number of networks, number of routes before and after filtering). If
        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>
        Show contents of a ROA table (by default of the first one). You can
index e6b0927b8c4bee6afe9cf0dbb2cef2b816ea3f18..15bc0f30eda6880a042c5e4814fd3c97220a8bdc 100644 (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(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
 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(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
 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:
    /* empty */ {
      $$ = cfg_allocz(sizeof(struct rt_show_data));
+     init_list(&($$->table));
      $$->filter = FILTER_ACCEPT;
    }
  | r_args net_any {
@@ -529,7 +530,15 @@ r_args:
  | r_args TABLE SYM {
      $$ = $1;
      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 {
      $$ = $1;
@@ -561,6 +570,7 @@ r_args:
      $$->export_mode = $2;
      $$->export_protocol = c->proto;
      $$->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;
@@ -569,6 +579,7 @@ r_args:
      if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
      $$->show_protocol = c->proto;
      $$->running_on_config = c->proto->cf->global;
+     $$->tables_defined_by = RSD_TDB_INDIRECT;
    }
  | r_args STATS {
      $$ = $1;
@@ -578,6 +589,16 @@ 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;
+   }
  ;
 
 export_mode:
index d7d4df691c820867a196d236baa79b78493c1eb5..4d7848582d4a11e8c8dea9074bd52b1a24b126e5 100644 (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 rt_show_data_rtable {
+  node n;
+  rtable *table;
+};
+
 struct rt_show_data {
   net_addr *addr;
-  rtable *table;
+  list table;
+  struct rt_show_data_rtable *tit;
   struct filter *filter;
-  int verbose;
+  int verbose, tables_defined_by;
   struct fib_iterator fit;
   struct proto *show_protocol;
   struct proto *export_protocol;
   struct channel *export_channel;
   int export_mode, primary_only, filtered;
   struct config *running_on_config;
-  int net_counter, rt_counter, show_counter;
-  int stats, show_for;
+  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);
+
+/* 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 */
 #define RSEM_NONE      0               /* Export mode not used */
index 8be7520c689765899b75a133e5f843d4cf315c59..73cfc9e2d12feb07754a1410ac6228dbbd3729eb 100644 (file)
@@ -2531,6 +2531,9 @@ 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);
+
   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);
 
@@ -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)
        goto skip;
 
-      d->show_counter++;
       if (d->stats < 2)
        rt_show_rte(c, ia, e, d, tmpa);
+
+      d->show_counter++;
       ia[0] = 0;
 
     skip:
@@ -2654,7 +2658,19 @@ 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->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
@@ -2666,7 +2682,7 @@ rt_show_cont(struct cli *c)
 #else
   unsigned max = 64;
 #endif
-  struct fib *fib = &d->table->fib;
+  struct fib *fib = &d->tit->table->fib;
   struct fib_iterator *it = &d->fit;
 
   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))
         {
          cli_printf(c, 8005, "Channel is down");
+         rt_show_cleanup(c);
          goto done;
        }
     }
@@ -2690,35 +2707,65 @@ rt_show_cont(struct cli *c)
       rt_show_net(c, n, d);
     }
   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)
-    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
     cli_printf(c, 0, "");
 done:
   c->cont = c->cleanup = NULL;
 }
 
-static void
-rt_show_cleanup(struct cli *c)
+void rt_show_add_table(struct rt_show_data *d, rtable *t)
 {
-  struct rt_show_data *d = c->rover;
-
-  /* Unlink the iterator */
-  fit_get(&d->table->fib, &d->fit);
+  struct rt_show_data_rtable *rsdr = cfg_alloc(sizeof(struct rt_show_data_rtable));
+  rsdr->table = t;
+  add_tail(&(d->table), &(rsdr->n));
 }
 
-static inline rtable *
-rt_show_get_table(struct proto *p)
+static inline void
+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))
-    return ((struct channel *) HEAD(p->channels))->table;
+static inline void
+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
@@ -2726,10 +2773,8 @@ rt_show(struct rt_show_data *d)
 {
   net *n;
 
-  /* Default is either a master table or a table related to a respective protocol */
-  if (!d->table && d->export_protocol) d->table = rt_show_get_table(d->export_protocol);
-  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 ? */
+  /* There may be implicit tables. */
+  if (EMPTY_LIST(d->table)) rt_show_get_default_table(d);
 
   /* Filtered routes are neither exported nor have sensible ordering */
   if (d->filtered && (d->export_mode || d->primary_only))
@@ -2737,7 +2782,13 @@ rt_show(struct rt_show_data *d)
 
   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->cleanup = rt_show_cleanup;
       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");
-       return;
+       /* 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;
+       }
+
+       rem_node(&(rsdr->n));
       }
 
-      if (d->show_for)
-       n = net_route(d->table, d->addr);
-      else
-       n = net_find(d->table, d->addr);
+      WALK_LIST(rsdr, d->table)
+      {
+       d->tit = rsdr;
 
-      if (n)
-       rt_show_net(this_cli, n, d);
+       if (d->show_for)
+         n = net_route(rsdr->table, d->addr);
+       else
+         n = net_find(rsdr->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 in table");
+       cli_msg(8001, "Network not found in any specified table");
     }
 }