[bgpd] Add RIB reporting commands, show bgp ... statistics
2006-09-13 Paul Jakma <paul.jakma@sun.com>
* bgp_aspath.c: (aspath_highest) new, return highest ASN in an
aspath.
* bgp_route.c: (bgp_peer_count_walker) new, do the walk done
in bgp_peer_counts as a thread.
(bgp_peer_counts) move walk to previous and call it via
thread_execute so this RIB walk shows up in thread stats.
(bgp_table_stats) New, gather some statistics for a given
RIB.
(bgp_table_stats_walker) New, RIB walker thread for former.
(bgp_table_stats_vty) Parsing front-end for 'show bgp ...',
useful model for future rationalisation of 'show ... bgp'.
(bgp_route_init) Add new RIB stats commands.
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index ca7cbd1..2ce2ef4 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -8390,7 +8390,362 @@
return peer;
}
+
+enum bgp_stats
+{
+ BGP_STATS_MAXBITLEN = 0,
+ BGP_STATS_RIB,
+ BGP_STATS_PREFIXES,
+ BGP_STATS_TOTPLEN,
+ BGP_STATS_UNAGGREGATEABLE,
+ BGP_STATS_MAX_AGGREGATEABLE,
+ BGP_STATS_AGGREGATES,
+ BGP_STATS_SPACE,
+ BGP_STATS_ASPATH_COUNT,
+ BGP_STATS_ASPATH_MAXHOPS,
+ BGP_STATS_ASPATH_TOTHOPS,
+ BGP_STATS_ASPATH_MAXSIZE,
+ BGP_STATS_ASPATH_TOTSIZE,
+ BGP_STATS_ASN_HIGHEST,
+ BGP_STATS_MAX,
+};
+static const char *table_stats_strs[] =
+{
+ [BGP_STATS_PREFIXES] = "Total Prefixes",
+ [BGP_STATS_TOTPLEN] = "Average prefix length",
+ [BGP_STATS_RIB] = "Total Advertisements",
+ [BGP_STATS_UNAGGREGATEABLE] = "Unaggregateable prefixes",
+ [BGP_STATS_MAX_AGGREGATEABLE] = "Maximum aggregateable prefixes",
+ [BGP_STATS_AGGREGATES] = "BGP Aggregate advertisements",
+ [BGP_STATS_SPACE] = "Address space advertised",
+ [BGP_STATS_ASPATH_COUNT] = "Advertisements with paths",
+ [BGP_STATS_ASPATH_MAXHOPS] = "Longest AS-Path (hops)",
+ [BGP_STATS_ASPATH_MAXSIZE] = "Largest AS-Path (bytes)",
+ [BGP_STATS_ASPATH_TOTHOPS] = "Average AS-Path length (hops)",
+ [BGP_STATS_ASPATH_TOTSIZE] = "Average AS-Path size (bytes)",
+ [BGP_STATS_ASN_HIGHEST] = "Highest public ASN",
+ [BGP_STATS_MAX] = NULL,
+};
+
+struct bgp_table_stats
+{
+ struct bgp_table *table;
+ unsigned long long counts[BGP_STATS_MAX];
+};
+
+#if 0
+#define TALLY_SIGFIG 100000
+static unsigned long
+ravg_tally (unsigned long count, unsigned long oldavg, unsigned long newval)
+{
+ unsigned long newtot = (count-1) * oldavg + (newval * TALLY_SIGFIG);
+ unsigned long res = (newtot * TALLY_SIGFIG) / count;
+ unsigned long ret = newtot / count;
+
+ if ((res % TALLY_SIGFIG) > (TALLY_SIGFIG/2))
+ return ret + 1;
+ else
+ return ret;
+}
+#endif
+
+static int
+bgp_table_stats_walker (struct thread *t)
+{
+ struct bgp_node *rn;
+ struct bgp_node *top;
+ struct bgp_table_stats *ts = THREAD_ARG (t);
+ unsigned int space = 0;
+
+ top = bgp_table_top (ts->table);
+
+ switch (top->p.family)
+ {
+ case AF_INET:
+ space = IPV4_MAX_BITLEN;
+ break;
+ case AF_INET6:
+ space = IPV6_MAX_BITLEN;
+ break;
+ }
+
+ ts->counts[BGP_STATS_MAXBITLEN] = space;
+
+ for (rn = top; rn; rn = bgp_route_next (rn))
+ {
+ struct bgp_info *ri;
+ struct bgp_node *prn = rn->parent;
+ unsigned int rinum = 0;
+
+ if (rn == top)
+ continue;
+
+ if (!rn->info)
+ continue;
+
+ ts->counts[BGP_STATS_PREFIXES]++;
+ ts->counts[BGP_STATS_TOTPLEN] += rn->p.prefixlen;
+
+#if 0
+ ts->counts[BGP_STATS_AVGPLEN]
+ = ravg_tally (ts->counts[BGP_STATS_PREFIXES],
+ ts->counts[BGP_STATS_AVGPLEN],
+ rn->p.prefixlen);
+#endif
+
+ /* check if the prefix is included by any other announcements */
+ while (prn && !prn->info)
+ prn = prn->parent;
+
+ if (prn == NULL || prn == top)
+ ts->counts[BGP_STATS_UNAGGREGATEABLE]++;
+ else if (prn->info)
+ ts->counts[BGP_STATS_MAX_AGGREGATEABLE]++;
+
+ /* announced address space */
+ if (space)
+ ts->counts[BGP_STATS_SPACE] += 1 << (space - rn->p.prefixlen);
+
+ for (ri = rn->info; ri; ri = ri->next)
+ {
+ rinum++;
+ ts->counts[BGP_STATS_RIB]++;
+
+ if (ri->attr &&
+ (CHECK_FLAG (ri->attr->flag,
+ ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE))))
+ ts->counts[BGP_STATS_AGGREGATES]++;
+
+ /* as-path stats */
+ if (ri->attr && ri->attr->aspath)
+ {
+ unsigned int hops = aspath_count_hops (ri->attr->aspath);
+ unsigned int size = aspath_size (ri->attr->aspath);
+ as_t highest = aspath_highest (ri->attr->aspath);
+
+ ts->counts[BGP_STATS_ASPATH_COUNT]++;
+
+ if (hops > ts->counts[BGP_STATS_ASPATH_MAXHOPS])
+ ts->counts[BGP_STATS_ASPATH_MAXHOPS] = hops;
+
+ if (size > ts->counts[BGP_STATS_ASPATH_MAXSIZE])
+ ts->counts[BGP_STATS_ASPATH_MAXSIZE] = size;
+
+ ts->counts[BGP_STATS_ASPATH_TOTHOPS] += hops;
+ ts->counts[BGP_STATS_ASPATH_TOTSIZE] += size;
+#if 0
+ ts->counts[BGP_STATS_ASPATH_AVGHOPS]
+ = ravg_tally (ts->counts[BGP_STATS_ASPATH_COUNT],
+ ts->counts[BGP_STATS_ASPATH_AVGHOPS],
+ hops);
+ ts->counts[BGP_STATS_ASPATH_AVGSIZE]
+ = ravg_tally (ts->counts[BGP_STATS_ASPATH_COUNT],
+ ts->counts[BGP_STATS_ASPATH_AVGSIZE],
+ size);
+#endif
+ if (highest > ts->counts[BGP_STATS_ASN_HIGHEST])
+ ts->counts[BGP_STATS_ASN_HIGHEST] = highest;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+bgp_table_stats (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ struct bgp_table_stats ts;
+ unsigned int i;
+
+ if (!bgp->rib[afi][safi])
+ {
+ vty_out (vty, "%% No RIB exist for the AFI/SAFI%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ memset (&ts, 0, sizeof (ts));
+ ts.table = bgp->rib[afi][safi];
+ thread_execute (bm->master, bgp_table_stats_walker, &ts, 0);
+
+ vty_out (vty, "BGP %s RIB statistics%s%s",
+ afi_safi_print (afi, safi), VTY_NEWLINE, VTY_NEWLINE);
+
+ for (i = 0; i < BGP_STATS_MAX; i++)
+ {
+ if (!table_stats_strs[i])
+ continue;
+
+ switch (i)
+ {
+#if 0
+ case BGP_STATS_ASPATH_AVGHOPS:
+ case BGP_STATS_ASPATH_AVGSIZE:
+ case BGP_STATS_AVGPLEN:
+ vty_out (vty, "%-30s: ", table_stats_strs[i]);
+ vty_out (vty, "%12.2f",
+ (float)ts.counts[i] / (float)TALLY_SIGFIG);
+ break;
+#endif
+ case BGP_STATS_ASPATH_TOTHOPS:
+ case BGP_STATS_ASPATH_TOTSIZE:
+ vty_out (vty, "%-30s: ", table_stats_strs[i]);
+ vty_out (vty, "%12.2f",
+ ts.counts[i] ?
+ (float)ts.counts[i] /
+ (float)ts.counts[BGP_STATS_ASPATH_COUNT]
+ : 0);
+ break;
+ case BGP_STATS_TOTPLEN:
+ vty_out (vty, "%-30s: ", table_stats_strs[i]);
+ vty_out (vty, "%12.2f",
+ ts.counts[i] ?
+ (float)ts.counts[i] /
+ (float)ts.counts[BGP_STATS_PREFIXES]
+ : 0);
+ break;
+ case BGP_STATS_SPACE:
+ vty_out (vty, "%-30s: ", table_stats_strs[i]);
+ vty_out (vty, "%12llu%s", ts.counts[i], VTY_NEWLINE);
+ if (ts.counts[BGP_STATS_MAXBITLEN] < 9)
+ break;
+ vty_out (vty, "%30s: ", "\% announced ");
+ vty_out (vty, "%12.2f%s",
+ 100 * (float)ts.counts[BGP_STATS_SPACE] /
+ (float)((u_int64_t)1UL << ts.counts[BGP_STATS_MAXBITLEN]),
+ VTY_NEWLINE);
+ vty_out (vty, "%30s: ", "/8 equivalent ");
+ vty_out (vty, "%12.2f%s",
+ (float)ts.counts[BGP_STATS_SPACE] /
+ (float)(1UL << (ts.counts[BGP_STATS_MAXBITLEN] - 8)),
+ VTY_NEWLINE);
+ if (ts.counts[BGP_STATS_MAXBITLEN] < 25)
+ break;
+ vty_out (vty, "%30s: ", "/24 equivalent ");
+ vty_out (vty, "%12.2f",
+ (float)ts.counts[BGP_STATS_SPACE] /
+ (float)(1UL << (ts.counts[BGP_STATS_MAXBITLEN] - 24)));
+ break;
+ default:
+ vty_out (vty, "%-30s: ", table_stats_strs[i]);
+ vty_out (vty, "%12llu", ts.counts[i]);
+ }
+
+ vty_out (vty, "%s", VTY_NEWLINE);
+ }
+ return CMD_SUCCESS;
+}
+
+static int
+bgp_table_stats_vty (struct vty *vty, const char *name,
+ const char *afi_str, const char *safi_str)
+{
+ struct bgp *bgp;
+ afi_t afi;
+ safi_t safi;
+
+ if (name)
+ bgp = bgp_lookup_by_name (name);
+ else
+ bgp = bgp_get_default ();
+
+ if (!bgp)
+ {
+ vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (strncmp (afi_str, "ipv", 3) == 0)
+ {
+ if (strncmp (afi_str, "ipv4", 4) == 0)
+ afi = AFI_IP;
+ else if (strncmp (afi_str, "ipv6", 4) == 0)
+ afi = AFI_IP6;
+ else
+ {
+ vty_out (vty, "%% Invalid address family %s%s",
+ afi_str, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (strncmp (safi_str, "m", 1) == 0)
+ safi = SAFI_MULTICAST;
+ else if (strncmp (safi_str, "u", 1) == 0)
+ safi = SAFI_UNICAST;
+ else if (strncmp (safi_str, "vpnv4", 5) == 0)
+ safi = BGP_SAFI_VPNV4;
+ else if (strncmp (safi_str, "vpnv6", 6) == 0)
+ safi = BGP_SAFI_VPNV6;
+ else
+ {
+ vty_out (vty, "%% Invalid subsequent address family %s%s",
+ safi_str, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+ else
+ {
+ vty_out (vty, "%% Invalid address family %s%s",
+ afi_str, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if ((afi == AFI_IP && safi == BGP_SAFI_VPNV6)
+ || (afi == AFI_IP6 && safi == BGP_SAFI_VPNV4))
+ {
+ vty_out (vty, "%% Invalid subsequent address family %s for %s%s",
+ afi_str, safi_str, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ return bgp_table_stats (vty, bgp, afi, safi);
+}
+
+DEFUN (show_bgp_statistics,
+ show_bgp_statistics_cmd,
+ "show bgp (ipv4|ipv6) (unicast|multicast) statistics",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "BGP RIB advertisement statistics\n")
+{
+ return bgp_table_stats_vty (vty, NULL, argv[0], argv[1]);
+}
+
+ALIAS (show_bgp_statistics,
+ show_bgp_statistics_vpnv4_cmd,
+ "show bgp (ipv4) (vpnv4) statistics",
+ SHOW_STR
+ BGP_STR
+ "Address family\n"
+ "Address Family modifier\n"
+ "BGP RIB advertisement statistics\n")
+
+DEFUN (show_bgp_statistics_view,
+ show_bgp_statistics_view_cmd,
+ "show bgp view WORD (ipv4|ipv6) (unicast|multicast) statistics",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "Address family\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "Address Family modifier\n"
+ "BGP RIB advertisement statistics\n")
+{
+ return bgp_table_stats_vty (vty, NULL, argv[0], argv[1]);
+}
+
+ALIAS (show_bgp_statistics_view,
+ show_bgp_statistics_view_vpnv4_cmd,
+ "show bgp view WORD (ipv4) (vpnv4) statistics",
+ SHOW_STR
+ BGP_STR
+ "BGP view\n"
+ "Address family\n"
+ "Address Family modifier\n"
+ "BGP RIB advertisement statistics\n")
+
enum bgp_pcounts
{
PCOUNT_ADJ_IN = 0,
@@ -8419,30 +8774,28 @@
[PCOUNT_MAX] = NULL,
};
+struct peer_pcounts
+{
+ unsigned int count[PCOUNT_MAX];
+ const struct peer *peer;
+ const struct bgp_table *table;
+};
+
static int
-bgp_peer_counts (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi)
+bgp_peer_count_walker (struct thread *t)
{
struct bgp_node *rn;
- struct bgp_info *ri;
- unsigned int pcounts[PCOUNT_MAX], i;
+ struct peer_pcounts *pc = THREAD_ARG (t);
+ const struct peer *peer = pc->peer;
- if (!peer || !peer->bgp || !peer->afc[afi][safi]
- || !peer->bgp->rib[afi][safi])
- {
- vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- memset (&pcounts, 0, sizeof(pcounts));
-
- for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn;
- rn = bgp_route_next (rn))
+ for (rn = bgp_table_top (pc->table); rn; rn = bgp_route_next (rn))
{
struct bgp_adj_in *ain;
+ struct bgp_info *ri;
for (ain = rn->adj_in; ain; ain = ain->next)
if (ain->peer == peer)
- pcounts[PCOUNT_ADJ_IN]++;
+ pc->count[PCOUNT_ADJ_IN]++;
for (ri = rn->info; ri; ri = ri->next)
{
@@ -8451,24 +8804,24 @@
if (ri->peer != peer)
continue;
- pcounts[PCOUNT_ALL]++;
+ pc->count[PCOUNT_ALL]++;
if (CHECK_FLAG (ri->flags, BGP_INFO_DAMPED))
- pcounts[PCOUNT_DAMPED]++;
+ pc->count[PCOUNT_DAMPED]++;
if (CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
- pcounts[PCOUNT_HISTORY]++;
+ pc->count[PCOUNT_HISTORY]++;
if (CHECK_FLAG (ri->flags, BGP_INFO_REMOVED))
- pcounts[PCOUNT_REMOVED]++;
+ pc->count[PCOUNT_REMOVED]++;
if (CHECK_FLAG (ri->flags, BGP_INFO_STALE))
- pcounts[PCOUNT_STALE]++;
+ pc->count[PCOUNT_STALE]++;
if (CHECK_FLAG (ri->flags, BGP_INFO_VALID))
- pcounts[PCOUNT_VALID]++;
+ pc->count[PCOUNT_VALID]++;
if (!CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE))
- pcounts[PCOUNT_PFCNT]++;
+ pc->count[PCOUNT_PFCNT]++;
if (CHECK_FLAG (ri->flags, BGP_INFO_COUNTED))
{
- pcounts[PCOUNT_COUNTED]++;
+ pc->count[PCOUNT_COUNTED]++;
if (CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE))
plog_warn (peer->log,
"%s [pcount] %s/%d is counted but flags 0x%x",
@@ -8491,7 +8844,32 @@
}
}
}
+ return 0;
+}
+static int
+bgp_peer_counts (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer_pcounts pcounts = { .peer = peer };
+ unsigned int i;
+
+ if (!peer || !peer->bgp || !peer->afc[afi][safi]
+ || !peer->bgp->rib[afi][safi])
+ {
+ vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ memset (&pcounts, 0, sizeof(pcounts));
+ pcounts.peer = peer;
+ pcounts.table = peer->bgp->rib[afi][safi];
+
+ /* in-place call via thread subsystem so as to record execution time
+ * stats for the thread-walk (i.e. ensure this can't be blamed on
+ * on just vty_read()).
+ */
+ thread_execute (bm->master, bgp_peer_count_walker, &pcounts, 0);
+
vty_out (vty, "Prefix counts for %s, %s%s",
peer->host, afi_safi_print (afi, safi), VTY_NEWLINE);
vty_out (vty, "PfxCt: %ld%s", peer->pcount[afi][safi], VTY_NEWLINE);
@@ -8499,9 +8877,10 @@
VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
for (i = 0; i < PCOUNT_MAX; i++)
- vty_out (vty, "%20s: %-10d%s", pcount_strs[i], pcounts[i], VTY_NEWLINE);
+ vty_out (vty, "%20s: %-10d%s",
+ pcount_strs[i], pcounts.count[i], VTY_NEWLINE);
- if (pcounts[PCOUNT_PFCNT] != peer->pcount[afi][safi])
+ if (pcounts.count[PCOUNT_PFCNT] != peer->pcount[afi][safi])
{
vty_out (vty, "%s [pcount] PfxCt drift!%s",
peer->host, VTY_NEWLINE);
@@ -11058,7 +11437,13 @@
install_element (ENABLE_NODE, &show_bgp_view_rsclient_cmd);
install_element (ENABLE_NODE, &show_bgp_view_rsclient_route_cmd);
install_element (ENABLE_NODE, &show_bgp_view_rsclient_prefix_cmd);
-
+
+ /* Statistics */
+ install_element (ENABLE_NODE, &show_bgp_statistics_cmd);
+ install_element (ENABLE_NODE, &show_bgp_statistics_vpnv4_cmd);
+ install_element (ENABLE_NODE, &show_bgp_statistics_view_cmd);
+ install_element (ENABLE_NODE, &show_bgp_statistics_view_vpnv4_cmd);
+
/* old command */
install_element (VIEW_NODE, &show_ipv6_bgp_cmd);
install_element (VIEW_NODE, &show_ipv6_bgp_route_cmd);