]> git.ipfire.org Git - thirdparty/iproute2.git/commitdiff
bridge: vlan: add support for the new rtm dump call
authorNikolay Aleksandrov <nikolay@nvidia.com>
Sun, 18 Apr 2021 12:01:36 +0000 (15:01 +0300)
committerDavid Ahern <dsahern@kernel.org>
Thu, 22 Apr 2021 05:13:34 +0000 (05:13 +0000)
Use the new bridge vlan rtm dump helper to dump all of the available
vlan information when -details (-d) is used with vlan show. It is also
capable of dumping vlan stats if -statistics (-s) is added.
Currently this is the only interface capable of dumping per-vlan
options. The vlan dump format is compatible with current vlan show, it
uses the same helpers to dump vlan information. The new addition is one
line which will contain the per-vlan options (similar to ip -d link show
for ports). Currently only the vlan STP state is printed.
The call uses compressed vlan format by default.

Example:
$ bridge -s -d vlan show
port              vlan-id
virbr1            1 PVID Egress Untagged
                    state forwarding

Signed-off-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: David Ahern <dsahern@kernel.org>
bridge/br_common.h
bridge/vlan.c
include/libnetlink.h
man/man8/bridge.8

index 33e56452702b1357a6a5816e1f5255136d85e649..43870546ff284999b28457f9c246e33fb18ce1a6 100644 (file)
@@ -12,6 +12,7 @@ int print_mdb_mon(struct nlmsghdr *n, void *arg);
 int print_fdb(struct nlmsghdr *n, void *arg);
 void print_stp_state(__u8 state);
 int parse_stp_state(const char *arg);
+int print_vlan_rtm(struct nlmsghdr *n, void *arg);
 
 int do_fdb(int argc, char **argv);
 int do_mdb(int argc, char **argv);
index 09884870df81d6cdb33f4a1a8d8ab1701f59c496..c681e14189b8afeabd7ba1159711cb8de8cdf517 100644 (file)
@@ -16,6 +16,7 @@
 #include "utils.h"
 
 static unsigned int filter_index, filter_vlan;
+static int vlan_rtm_cur_ifidx = -1;
 
 enum vlan_show_subject {
        VLAN_SHOW_VLAN,
@@ -517,14 +518,8 @@ static void print_vlan_flags(__u16 flags)
        close_json_array(PRINT_JSON, NULL);
 }
 
-static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
+static void __print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
 {
-       open_json_object(NULL);
-
-       print_hu(PRINT_ANY, "vid", "%hu", vstats->vid);
-       print_vlan_flags(vstats->flags);
-       print_nl();
-
        print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s    ", "");
        print_lluint(PRINT_ANY, "rx_bytes", "RX: %llu bytes",
                     vstats->rx_bytes);
@@ -536,6 +531,16 @@ static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
                     vstats->tx_bytes);
        print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n",
                     vstats->tx_packets);
+}
+
+static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
+{
+       open_json_object(NULL);
+
+       print_hu(PRINT_ANY, "vid", "%hu", vstats->vid);
+       print_vlan_flags(vstats->flags);
+       print_nl();
+       __print_one_vlan_stats(vstats);
 
        close_json_object();
 }
@@ -616,6 +621,105 @@ static int print_vlan_stats(struct nlmsghdr *n, void *arg)
        return 0;
 }
 
+int print_vlan_rtm(struct nlmsghdr *n, void *arg)
+{
+       struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1], *a;
+       struct br_vlan_msg *bvm = NLMSG_DATA(n);
+       int len = n->nlmsg_len;
+       bool newport = false;
+       int rem;
+
+       if (n->nlmsg_type != RTM_NEWVLAN && n->nlmsg_type != RTM_DELVLAN &&
+           n->nlmsg_type != RTM_GETVLAN) {
+               fprintf(stderr, "Unknown vlan rtm message: %08x %08x %08x\n",
+                       n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+               return 0;
+       }
+
+       len -= NLMSG_LENGTH(sizeof(*bvm));
+       if (len < 0) {
+               fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+               return -1;
+       }
+
+       if (bvm->family != AF_BRIDGE)
+               return 0;
+
+       if (filter_index && filter_index != bvm->ifindex)
+               return 0;
+
+       if (vlan_rtm_cur_ifidx == -1 || vlan_rtm_cur_ifidx != bvm->ifindex) {
+               if (vlan_rtm_cur_ifidx != -1)
+                       close_vlan_port();
+               open_vlan_port(bvm->ifindex, VLAN_SHOW_VLAN);
+               vlan_rtm_cur_ifidx = bvm->ifindex;
+               newport = true;
+       }
+
+       rem = len;
+       for (a = BRVLAN_RTA(bvm); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) {
+               struct bridge_vlan_xstats vstats;
+               struct bridge_vlan_info *vinfo;
+               __u32 vrange = 0;
+               __u8 state = 0;
+
+               parse_rtattr_flags(vtb, BRIDGE_VLANDB_ENTRY_MAX, RTA_DATA(a),
+                                  RTA_PAYLOAD(a), NLA_F_NESTED);
+               vinfo = RTA_DATA(vtb[BRIDGE_VLANDB_ENTRY_INFO]);
+
+               memset(&vstats, 0, sizeof(vstats));
+               if (vtb[BRIDGE_VLANDB_ENTRY_RANGE])
+                       vrange = rta_getattr_u16(vtb[BRIDGE_VLANDB_ENTRY_RANGE]);
+               else
+                       vrange = vinfo->vid;
+
+               if (vtb[BRIDGE_VLANDB_ENTRY_STATE])
+                       state = rta_getattr_u8(vtb[BRIDGE_VLANDB_ENTRY_STATE]);
+
+               if (vtb[BRIDGE_VLANDB_ENTRY_STATS]) {
+                       struct rtattr *stb[BRIDGE_VLANDB_STATS_MAX+1];
+                       struct rtattr *attr;
+
+                       attr = vtb[BRIDGE_VLANDB_ENTRY_STATS];
+                       parse_rtattr(stb, BRIDGE_VLANDB_STATS_MAX, RTA_DATA(attr),
+                                    RTA_PAYLOAD(attr));
+
+                       if (stb[BRIDGE_VLANDB_STATS_RX_BYTES]) {
+                               attr = stb[BRIDGE_VLANDB_STATS_RX_BYTES];
+                               vstats.rx_bytes = rta_getattr_u64(attr);
+                       }
+                       if (stb[BRIDGE_VLANDB_STATS_RX_PACKETS]) {
+                               attr = stb[BRIDGE_VLANDB_STATS_RX_PACKETS];
+                               vstats.rx_packets = rta_getattr_u64(attr);
+                       }
+                       if (stb[BRIDGE_VLANDB_STATS_TX_PACKETS]) {
+                               attr = stb[BRIDGE_VLANDB_STATS_TX_PACKETS];
+                               vstats.tx_packets = rta_getattr_u64(attr);
+                       }
+                       if (stb[BRIDGE_VLANDB_STATS_TX_BYTES]) {
+                               attr = stb[BRIDGE_VLANDB_STATS_TX_BYTES];
+                               vstats.tx_bytes = rta_getattr_u64(attr);
+                       }
+               }
+               open_json_object(NULL);
+               if (!newport)
+                       print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s  ", "");
+               else
+                       newport = false;
+               print_range("vlan", vinfo->vid, vrange);
+               print_vlan_flags(vinfo->flags);
+               print_nl();
+               print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s    ", "");
+               print_stp_state(state);
+               print_nl();
+               if (show_stats)
+                       __print_one_vlan_stats(&vstats);
+               close_json_object();
+       }
+
+       return 0;
+}
+
 static int vlan_show(int argc, char **argv, int subject)
 {
        char *filter_dev = NULL;
@@ -644,6 +748,34 @@ static int vlan_show(int argc, char **argv, int subject)
 
        new_json_obj(json);
 
+       /* if show_details is true then use the new bridge vlan dump format */
+       if (show_details && subject == VLAN_SHOW_VLAN) {
+               __u32 dump_flags = show_stats ? BRIDGE_VLANDB_DUMPF_STATS : 0;
+
+               if (rtnl_brvlandump_req(&rth, PF_BRIDGE, dump_flags) < 0) {
+                       perror("Cannot send dump request");
+                       exit(1);
+               }
+
+               if (!is_json_context()) {
+                       printf("%-" __stringify(IFNAMSIZ) "s  %-"
+                              __stringify(VLAN_ID_LEN) "s", "port",
+                              "vlan-id");
+                       printf("\n");
+               }
+
+               ret = rtnl_dump_filter(&rth, print_vlan_rtm, &subject);
+               if (ret < 0) {
+                       fprintf(stderr, "Dump terminated\n");
+                       exit(1);
+               }
+
+               if (vlan_rtm_cur_ifidx != -1)
+                       close_vlan_port();
+
+               goto out;
+       }
+
        if (!show_stats) {
                if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE,
                                             (compress_vlans ?
@@ -697,6 +829,7 @@ static int vlan_show(int argc, char **argv, int subject)
                }
        }
 
+out:
        delete_json_obj();
        fflush(stdout);
        return 0;
index da96c69b9edef5f1b0150eec588282ef54890f95..6bff6bae6ddfadfbc9681bbcb7a3497ab1e0a582 100644 (file)
@@ -285,6 +285,11 @@ int rtnl_from_file(FILE *, rtnl_listen_filter_t handler,
        ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct if_stats_msg))))
 #endif
 
+#ifndef BRVLAN_RTA
+#define BRVLAN_RTA(r) \
+       ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_vlan_msg))))
+#endif
+
 /* User defined nlmsg_type which is used mostly for logging netlink
  * messages from dump file */
 #define NLMSG_TSTAMP   15
index 90dcae73ce713b7fc1ce0646d4f7d60146cc103b..9c8ebac3c6aa754820047cc828294b13bba3ffec 100644 (file)
@@ -171,7 +171,7 @@ As a rule, the information is statistics or some time values.
 
 .TP
 .BR "\-d" , " \-details"
-print detailed information about MDB router ports.
+print detailed information about bridge vlan filter entries or MDB router ports.
 
 .TP
 .BR "\-n" , " \-net" , " \-netns " <NETNS>
@@ -881,6 +881,11 @@ STP BPDUs.
 
 This command displays the current VLAN filter table.
 
+.PP
+With the
+.B -details
+option, the command becomes verbose. It displays the per-vlan options.
+
 .PP
 With the
 .B -statistics