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 <niko...@cumulusnetworks.com>
---
 ip/iplink_bridge.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 153 insertions(+)

diff --git a/ip/iplink_bridge.c b/ip/iplink_bridge.c
index a17ff3555488..62ceee6b571e 100644
--- a/ip/iplink_bridge.c
+++ b/ip/iplink_bridge.c
@@ -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,
 };
-- 
2.1.4

Reply via email to