iplink: bridge: add support for displaying xstats

Add support for the new parse/print_ifla_xstats callbacks and use them to
print the per-bridge multicast stats.
Example:
$ ip link xstats type bridge
br0
                    IGMP queries:
                      RX: v1 0 v2 0 v3 0
                      TX: v1 0 v2 0 v3 0
                    IGMP reports:
                      RX: v1 0 v2 0 v3 0
                      TX: v1 0 v2 0 v3 0
                    IGMP leaves: RX: 0 TX: 0
                    IGMP parse errors: 0
                    MLD queries:
                      RX: v1 0 v2 0
                      TX: v1 0 v2 0
                    MLD reports:
                      RX: v1 0 v2 0
                      TX: v1 0 v2 0
                    MLD leaves: RX: 0 TX: 0
                    MLD parse errors: 0

Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
This commit is contained in:
Nikolay Aleksandrov 2017-02-15 15:23:12 +01:00 committed by Stephen Hemminger
parent 94f1a22aa7
commit 60ec0ecf0f
1 changed files with 153 additions and 0 deletions

View File

@ -12,13 +12,19 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <linux/if_link.h>
#include <linux/if_bridge.h>
#include <netinet/ether.h>
#include <net/if.h>
#include "rt_names.h"
#include "utils.h"
#include "ip_common.h"
static unsigned int xstats_print_attr;
static int filter_index;
static void print_explain(FILE *f)
{
fprintf(f,
@ -582,10 +588,157 @@ static void bridge_print_help(struct link_util *lu, int argc, char **argv,
print_explain(f);
}
static void bridge_print_xstats_help(struct link_util *lu, FILE *f)
{
fprintf(f, "Usage: ... %s [ igmp ] [ dev DEVICE ]\n", lu->id);
}
static void bridge_print_stats_attr(FILE *f, struct rtattr *attr, int ifindex)
{
struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
struct br_mcast_stats *mstats;
struct rtattr *i, *list;
const char *ifname = "";
int rem;
parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr),
RTA_PAYLOAD(attr));
if (!brtb[LINK_XSTATS_TYPE_BRIDGE])
return;
list = brtb[LINK_XSTATS_TYPE_BRIDGE];
rem = RTA_PAYLOAD(list);
for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
if (xstats_print_attr && i->rta_type != xstats_print_attr)
continue;
switch (i->rta_type) {
case BRIDGE_XSTATS_MCAST:
mstats = RTA_DATA(i);
ifname = ll_index_to_name(ifindex);
fprintf(f, "%-16s\n", ifname);
fprintf(f, "%-16s IGMP queries:\n", "");
fprintf(f, "%-16s RX: v1 %llu v2 %llu v3 %llu\n",
"",
mstats->igmp_v1queries[BR_MCAST_DIR_RX],
mstats->igmp_v2queries[BR_MCAST_DIR_RX],
mstats->igmp_v3queries[BR_MCAST_DIR_RX]);
fprintf(f, "%-16s TX: v1 %llu v2 %llu v3 %llu\n",
"",
mstats->igmp_v1queries[BR_MCAST_DIR_TX],
mstats->igmp_v2queries[BR_MCAST_DIR_TX],
mstats->igmp_v3queries[BR_MCAST_DIR_TX]);
fprintf(f, "%-16s IGMP reports:\n", "");
fprintf(f, "%-16s RX: v1 %llu v2 %llu v3 %llu\n",
"",
mstats->igmp_v1reports[BR_MCAST_DIR_RX],
mstats->igmp_v2reports[BR_MCAST_DIR_RX],
mstats->igmp_v3reports[BR_MCAST_DIR_RX]);
fprintf(f, "%-16s TX: v1 %llu v2 %llu v3 %llu\n",
"",
mstats->igmp_v1reports[BR_MCAST_DIR_TX],
mstats->igmp_v2reports[BR_MCAST_DIR_TX],
mstats->igmp_v3reports[BR_MCAST_DIR_TX]);
fprintf(f, "%-16s IGMP leaves: RX: %llu TX: %llu\n",
"",
mstats->igmp_leaves[BR_MCAST_DIR_RX],
mstats->igmp_leaves[BR_MCAST_DIR_TX]);
fprintf(f, "%-16s IGMP parse errors: %llu\n",
"", mstats->igmp_parse_errors);
fprintf(f, "%-16s MLD queries:\n", "");
fprintf(f, "%-16s RX: v1 %llu v2 %llu\n",
"",
mstats->mld_v1queries[BR_MCAST_DIR_RX],
mstats->mld_v2queries[BR_MCAST_DIR_RX]);
fprintf(f, "%-16s TX: v1 %llu v2 %llu\n",
"",
mstats->mld_v1queries[BR_MCAST_DIR_TX],
mstats->mld_v2queries[BR_MCAST_DIR_TX]);
fprintf(f, "%-16s MLD reports:\n", "");
fprintf(f, "%-16s RX: v1 %llu v2 %llu\n",
"",
mstats->mld_v1reports[BR_MCAST_DIR_RX],
mstats->mld_v2reports[BR_MCAST_DIR_RX]);
fprintf(f, "%-16s TX: v1 %llu v2 %llu\n",
"",
mstats->mld_v1reports[BR_MCAST_DIR_TX],
mstats->mld_v2reports[BR_MCAST_DIR_TX]);
fprintf(f, "%-16s MLD leaves: RX: %llu TX: %llu\n",
"",
mstats->mld_leaves[BR_MCAST_DIR_RX],
mstats->mld_leaves[BR_MCAST_DIR_TX]);
fprintf(f, "%-16s MLD parse errors: %llu\n",
"", mstats->mld_parse_errors);
break;
}
}
}
static int bridge_print_xstats(const struct sockaddr_nl *who,
struct nlmsghdr *n, void *arg)
{
struct if_stats_msg *ifsm = NLMSG_DATA(n);
struct rtattr *tb[IFLA_STATS_MAX+1];
int len = n->nlmsg_len;
FILE *fp = arg;
len -= NLMSG_LENGTH(sizeof(*ifsm));
if (len < 0) {
fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
return -1;
}
if (filter_index && filter_index != ifsm->ifindex)
return 0;
parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
if (tb[IFLA_STATS_LINK_XSTATS])
bridge_print_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS],
ifsm->ifindex);
if (tb[IFLA_STATS_LINK_XSTATS_SLAVE])
bridge_print_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS_SLAVE],
ifsm->ifindex);
return 0;
}
static int bridge_parse_xstats(struct link_util *lu, int argc, char **argv)
{
while (argc > 0) {
if (strcmp(*argv, "igmp") == 0 || strcmp(*argv, "mcast") == 0) {
xstats_print_attr = BRIDGE_XSTATS_MCAST;
} else if (strcmp(*argv, "dev") == 0) {
NEXT_ARG();
filter_index = if_nametoindex(*argv);
if (filter_index == 0) {
fprintf(stderr, "Cannot find device \"%s\"\n",
*argv);
return -1;
}
} else if (strcmp(*argv, "help") == 0) {
bridge_print_xstats_help(lu, stdout);
exit(0);
} else {
invarg("unknown attribute", *argv);
}
argc--; argv++;
}
return 0;
}
struct link_util bridge_link_util = {
.id = "bridge",
.maxattr = IFLA_BR_MAX,
.parse_opt = bridge_parse_opt,
.print_opt = bridge_print_opt,
.print_help = bridge_print_help,
.parse_ifla_xstats = bridge_parse_xstats,
.print_ifla_xstats = bridge_print_xstats,
};