iproute2/bridge/vlan.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2#include <stdio.h>
   3#include <stdlib.h>
   4#include <unistd.h>
   5#include <fcntl.h>
   6#include <sys/socket.h>
   7#include <sys/time.h>
   8#include <net/if.h>
   9#include <netinet/in.h>
  10#include <linux/if_bridge.h>
  11#include <linux/if_ether.h>
  12#include <string.h>
  13#include <errno.h>
  14
  15#include "json_print.h"
  16#include "libnetlink.h"
  17#include "br_common.h"
  18#include "bridge.h"
  19#include "utils.h"
  20
  21static unsigned int filter_index, filter_vlan;
  22static int vlan_rtm_cur_ifidx = -1;
  23static void print_vlan_info(struct rtattr *tb, int ifindex);
  24
  25enum vlan_show_subject {
  26        VLAN_SHOW_VLAN,
  27        VLAN_SHOW_TUNNELINFO,
  28};
  29
  30#define VLAN_ID_LEN 9
  31
  32static void usage(void)
  33{
  34        fprintf(stderr,
  35                "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ tunnel_info id TUNNEL_ID ]\n"
  36                "                                                     [ pvid ] [ untagged ]\n"
  37                "                                                     [ self ] [ master ]\n"
  38                "       bridge vlan { set } vid VLAN_ID dev DEV [ state STP_STATE ]\n"
  39                "                                               [ mcast_router MULTICAST_ROUTER ]\n"
  40                "                                               [ mcast_max_groups MAX_GROUPS ]\n"
  41                "                                               [ neigh_suppress {on | off} ]\n"
  42                "       bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n"
  43                "       bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n"
  44                "       bridge vlan global { set } vid VLAN_ID dev DEV\n"
  45                "                      [ mcast_snooping MULTICAST_SNOOPING ]\n"
  46                "                      [ mcast_querier MULTICAST_QUERIER ]\n"
  47                "                      [ mcast_igmp_version IGMP_VERSION ]\n"
  48                "                      [ mcast_mld_version MLD_VERSION ]\n"
  49                "                      [ mcast_last_member_count LAST_MEMBER_COUNT ]\n"
  50                "                      [ mcast_last_member_interval LAST_MEMBER_INTERVAL ]\n"
  51                "                      [ mcast_startup_query_count STARTUP_QUERY_COUNT ]\n"
  52                "                      [ mcast_startup_query_interval STARTUP_QUERY_INTERVAL ]\n"
  53                "                      [ mcast_membership_interval MEMBERSHIP_INTERVAL ]\n"
  54                "                      [ mcast_querier_interval QUERIER_INTERVAL ]\n"
  55                "                      [ mcast_query_interval QUERY_INTERVAL ]\n"
  56                "                      [ mcast_query_response_interval QUERY_RESPONSE_INTERVAL ]\n"
  57                "                      [ msti MSTI ]\n"
  58                "       bridge vlan global { show } [ dev DEV ] [ vid VLAN_ID ]\n");
  59        exit(-1);
  60}
  61
  62static int parse_tunnel_info(int *argcp, char ***argvp, __u32 *tun_id_start,
  63                             __u32 *tun_id_end)
  64{
  65        char **argv = *argvp;
  66        int argc = *argcp;
  67        char *t;
  68
  69        NEXT_ARG();
  70        if (!matches(*argv, "id")) {
  71                NEXT_ARG();
  72                t = strchr(*argv, '-');
  73                if (t) {
  74                        *t = '\0';
  75                        if (get_u32(tun_id_start, *argv, 0) ||
  76                                    *tun_id_start >= 1u << 24)
  77                                invarg("invalid tun id", *argv);
  78                        if (get_u32(tun_id_end, t + 1, 0) ||
  79                                    *tun_id_end >= 1u << 24)
  80                                invarg("invalid tun id", *argv);
  81
  82                } else {
  83                        if (get_u32(tun_id_start, *argv, 0) ||
  84                                    *tun_id_start >= 1u << 24)
  85                                invarg("invalid tun id", *argv);
  86                }
  87        } else {
  88                invarg("tunnel id expected", *argv);
  89        }
  90
  91        *argcp = argc;
  92        *argvp = argv;
  93
  94        return 0;
  95}
  96
  97static int add_tunnel_info(struct nlmsghdr *n, int reqsize,
  98                           __u16 vid, __u32 tun_id, __u16 flags)
  99{
 100        struct rtattr *tinfo;
 101
 102        tinfo = addattr_nest(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
 103        addattr32(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_ID, tun_id);
 104        addattr16(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_VID, vid);
 105        addattr16(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, flags);
 106
 107        addattr_nest_end(n, tinfo);
 108
 109        return 0;
 110}
 111
 112static int add_tunnel_info_range(struct nlmsghdr *n, int reqsize,
 113                                 __u16 vid_start, int16_t vid_end,
 114                                 __u32 tun_id_start, __u32 tun_id_end)
 115{
 116        if (vid_end != -1 && (vid_end - vid_start) > 0) {
 117                add_tunnel_info(n, reqsize, vid_start, tun_id_start,
 118                                BRIDGE_VLAN_INFO_RANGE_BEGIN);
 119
 120                add_tunnel_info(n, reqsize, vid_end, tun_id_end,
 121                                BRIDGE_VLAN_INFO_RANGE_END);
 122        } else {
 123                add_tunnel_info(n, reqsize, vid_start, tun_id_start, 0);
 124        }
 125
 126        return 0;
 127}
 128
 129static int add_vlan_info_range(struct nlmsghdr *n, int reqsize, __u16 vid_start,
 130                               int16_t vid_end, __u16 flags)
 131{
 132        struct bridge_vlan_info vinfo = {};
 133
 134        vinfo.flags = flags;
 135        vinfo.vid = vid_start;
 136        if (vid_end != -1) {
 137                /* send vlan range start */
 138                addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
 139                          sizeof(vinfo));
 140                vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
 141
 142                /* Now send the vlan range end */
 143                vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
 144                vinfo.vid = vid_end;
 145                addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
 146                          sizeof(vinfo));
 147        } else {
 148                addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
 149                          sizeof(vinfo));
 150        }
 151
 152        return 0;
 153}
 154
 155static int vlan_modify(int cmd, int argc, char **argv)
 156{
 157        struct {
 158                struct nlmsghdr n;
 159                struct ifinfomsg        ifm;
 160                char                    buf[1024];
 161        } req = {
 162                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
 163                .n.nlmsg_flags = NLM_F_REQUEST,
 164                .n.nlmsg_type = cmd,
 165                .ifm.ifi_family = PF_BRIDGE,
 166        };
 167        char *d = NULL;
 168        short vid = -1;
 169        short vid_end = -1;
 170        struct rtattr *afspec;
 171        struct bridge_vlan_info vinfo = {};
 172        bool tunnel_info_set = false;
 173        unsigned short flags = 0;
 174        __u32 tun_id_start = 0;
 175        __u32 tun_id_end = 0;
 176
 177        while (argc > 0) {
 178                if (strcmp(*argv, "dev") == 0) {
 179                        NEXT_ARG();
 180                        d = *argv;
 181                } else if (strcmp(*argv, "vid") == 0) {
 182                        char *p;
 183
 184                        NEXT_ARG();
 185                        p = strchr(*argv, '-');
 186                        if (p) {
 187                                *p = '\0';
 188                                p++;
 189                                vid = atoi(*argv);
 190                                vid_end = atoi(p);
 191                                vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
 192                        } else {
 193                                vid = atoi(*argv);
 194                        }
 195                } else if (strcmp(*argv, "self") == 0) {
 196                        flags |= BRIDGE_FLAGS_SELF;
 197                } else if (strcmp(*argv, "master") == 0) {
 198                        flags |= BRIDGE_FLAGS_MASTER;
 199                } else if (strcmp(*argv, "pvid") == 0) {
 200                        vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
 201                } else if (strcmp(*argv, "untagged") == 0) {
 202                        vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 203                } else if (strcmp(*argv, "tunnel_info") == 0) {
 204                                if (parse_tunnel_info(&argc, &argv,
 205                                                      &tun_id_start,
 206                                                      &tun_id_end))
 207                                        return -1;
 208                                tunnel_info_set = true;
 209                } else {
 210                        if (matches(*argv, "help") == 0)
 211                                NEXT_ARG();
 212                }
 213                argc--; argv++;
 214        }
 215
 216        if (d == NULL || vid == -1) {
 217                fprintf(stderr, "Device and VLAN ID are required arguments.\n");
 218                return -1;
 219        }
 220
 221        req.ifm.ifi_index = ll_name_to_index(d);
 222        if (req.ifm.ifi_index == 0) {
 223                fprintf(stderr, "Cannot find bridge device \"%s\"\n", d);
 224                return -1;
 225        }
 226
 227        if (vid >= 4096) {
 228                fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid);
 229                return -1;
 230        }
 231
 232        if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
 233                if (vid_end == -1 || vid_end >= 4096 || vid >= vid_end) {
 234                        fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
 235                                vid, vid_end);
 236                        return -1;
 237                }
 238                if (vinfo.flags & BRIDGE_VLAN_INFO_PVID) {
 239                        fprintf(stderr,
 240                                "pvid cannot be configured for a vlan range\n");
 241                        return -1;
 242                }
 243        }
 244
 245        afspec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC);
 246
 247        if (flags)
 248                addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags);
 249
 250        if (tunnel_info_set)
 251                add_tunnel_info_range(&req.n, sizeof(req), vid, vid_end,
 252                                      tun_id_start, tun_id_end);
 253        else
 254                add_vlan_info_range(&req.n, sizeof(req), vid, vid_end,
 255                                    vinfo.flags);
 256
 257        addattr_nest_end(&req.n, afspec);
 258
 259        if (rtnl_talk(&rth, &req.n, NULL) < 0)
 260                return -1;
 261
 262        return 0;
 263}
 264
 265static int vlan_option_set(int argc, char **argv)
 266{
 267        struct {
 268                struct nlmsghdr n;
 269                struct br_vlan_msg      bvm;
 270                char                    buf[1024];
 271        } req = {
 272                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_vlan_msg)),
 273                .n.nlmsg_flags = NLM_F_REQUEST,
 274                .n.nlmsg_type = RTM_NEWVLAN,
 275                .bvm.family = PF_BRIDGE,
 276        };
 277        struct bridge_vlan_info vinfo = {};
 278        struct rtattr *afspec;
 279        char *d = NULL;
 280        short vid = -1;
 281
 282        afspec = addattr_nest(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY);
 283        afspec->rta_type |= NLA_F_NESTED;
 284        while (argc > 0) {
 285                if (strcmp(*argv, "dev") == 0) {
 286                        NEXT_ARG();
 287                        d = *argv;
 288                        req.bvm.ifindex = ll_name_to_index(d);
 289                        if (req.bvm.ifindex == 0) {
 290                                fprintf(stderr,
 291                                        "Cannot find network device \"%s\"\n",
 292                                        d);
 293                                return -1;
 294                        }
 295                } else if (strcmp(*argv, "vid") == 0) {
 296                        short vid_end = -1;
 297                        char *p;
 298
 299                        NEXT_ARG();
 300                        p = strchr(*argv, '-');
 301                        if (p) {
 302                                *p = '\0';
 303                                p++;
 304                                vid = atoi(*argv);
 305                                vid_end = atoi(p);
 306                                if (vid >= vid_end || vid_end >= 4096) {
 307                                        fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
 308                                                vid, vid_end);
 309                                        return -1;
 310                                }
 311                        } else {
 312                                vid = atoi(*argv);
 313                        }
 314                        if (vid >= 4096) {
 315                                fprintf(stderr, "Invalid VLAN ID \"%hu\"\n",
 316                                        vid);
 317                                return -1;
 318                        }
 319
 320                        vinfo.flags = BRIDGE_VLAN_INFO_ONLY_OPTS;
 321                        vinfo.vid = vid;
 322                        addattr_l(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_INFO,
 323                                  &vinfo, sizeof(vinfo));
 324                        if (vid_end != -1)
 325                                addattr16(&req.n, sizeof(req),
 326                                          BRIDGE_VLANDB_ENTRY_RANGE, vid_end);
 327                } else if (strcmp(*argv, "state") == 0) {
 328                        char *endptr;
 329                        int state;
 330
 331                        NEXT_ARG();
 332                        state = strtol(*argv, &endptr, 10);
 333                        if (!(**argv != '\0' && *endptr == '\0'))
 334                                state = parse_stp_state(*argv);
 335                        if (state == -1) {
 336                                fprintf(stderr, "Error: invalid STP state\n");
 337                                return -1;
 338                        }
 339                        addattr8(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_STATE,
 340                                 state);
 341                } else if (strcmp(*argv, "mcast_router") == 0) {
 342                        __u8 mcast_router;
 343
 344                        NEXT_ARG();
 345                        if (get_u8(&mcast_router, *argv, 0))
 346                                invarg("invalid mcast_router", *argv);
 347                        addattr8(&req.n, sizeof(req),
 348                                 BRIDGE_VLANDB_ENTRY_MCAST_ROUTER,
 349                                 mcast_router);
 350                } else if (strcmp(*argv, "mcast_max_groups") == 0) {
 351                        __u32 max_groups;
 352
 353                        NEXT_ARG();
 354                        if (get_u32(&max_groups, *argv, 0))
 355                                invarg("invalid mcast_max_groups", *argv);
 356                        addattr32(&req.n, sizeof(req),
 357                                  BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS,
 358                                  max_groups);
 359                } else if (strcmp(*argv, "neigh_suppress") == 0) {
 360                        bool neigh_suppress;
 361                        int ret;
 362
 363                        NEXT_ARG();
 364                        neigh_suppress = parse_on_off("neigh_suppress", *argv,
 365                                                      &ret);
 366                        if (ret)
 367                                return ret;
 368                        addattr8(&req.n, sizeof(req),
 369                                 BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS,
 370                                 neigh_suppress);
 371                } else {
 372                        if (matches(*argv, "help") == 0)
 373                                NEXT_ARG();
 374                }
 375                argc--; argv++;
 376        }
 377        addattr_nest_end(&req.n, afspec);
 378
 379        if (d == NULL || vid == -1) {
 380                fprintf(stderr, "Device and VLAN ID are required arguments.\n");
 381                return -1;
 382        }
 383
 384        if (rtnl_talk(&rth, &req.n, NULL) < 0)
 385                return -1;
 386
 387        return 0;
 388}
 389
 390static int vlan_global_option_set(int argc, char **argv)
 391{
 392        struct {
 393                struct nlmsghdr n;
 394                struct br_vlan_msg      bvm;
 395                char                    buf[1024];
 396        } req = {
 397                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_vlan_msg)),
 398                .n.nlmsg_flags = NLM_F_REQUEST,
 399                .n.nlmsg_type = RTM_NEWVLAN,
 400                .bvm.family = PF_BRIDGE,
 401        };
 402        struct rtattr *afspec;
 403        short vid_end = -1;
 404        char *d = NULL;
 405        short vid = -1;
 406        __u64 val64;
 407        __u32 val32;
 408        __u16 val16;
 409        __u8 val8;
 410
 411        afspec = addattr_nest(&req.n, sizeof(req),
 412                              BRIDGE_VLANDB_GLOBAL_OPTIONS);
 413        afspec->rta_type |= NLA_F_NESTED;
 414        while (argc > 0) {
 415                if (strcmp(*argv, "dev") == 0) {
 416                        NEXT_ARG();
 417                        d = *argv;
 418                        req.bvm.ifindex = ll_name_to_index(d);
 419                        if (req.bvm.ifindex == 0) {
 420                                fprintf(stderr, "Cannot find network device \"%s\"\n",
 421                                        d);
 422                                return -1;
 423                        }
 424                } else if (strcmp(*argv, "vid") == 0) {
 425                        char *p;
 426
 427                        NEXT_ARG();
 428                        p = strchr(*argv, '-');
 429                        if (p) {
 430                                *p = '\0';
 431                                p++;
 432                                vid = atoi(*argv);
 433                                vid_end = atoi(p);
 434                                if (vid >= vid_end || vid_end >= 4096) {
 435                                        fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
 436                                                vid, vid_end);
 437                                        return -1;
 438                                }
 439                        } else {
 440                                vid = atoi(*argv);
 441                        }
 442                        if (vid >= 4096) {
 443                                fprintf(stderr, "Invalid VLAN ID \"%hu\"\n",
 444                                        vid);
 445                                return -1;
 446                        }
 447                        addattr16(&req.n, sizeof(req), BRIDGE_VLANDB_GOPTS_ID,
 448                                  vid);
 449                        if (vid_end != -1)
 450                                addattr16(&req.n, sizeof(req),
 451                                          BRIDGE_VLANDB_GOPTS_RANGE, vid_end);
 452                } else if (strcmp(*argv, "mcast_snooping") == 0) {
 453                        NEXT_ARG();
 454                        if (get_u8(&val8, *argv, 0))
 455                                invarg("invalid mcast_snooping", *argv);
 456                        addattr8(&req.n, 1024,
 457                                 BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING, val8);
 458                } else if (strcmp(*argv, "mcast_querier") == 0) {
 459                        NEXT_ARG();
 460                        if (get_u8(&val8, *argv, 0))
 461                                invarg("invalid mcast_querier", *argv);
 462                        addattr8(&req.n, 1024,
 463                                 BRIDGE_VLANDB_GOPTS_MCAST_QUERIER, val8);
 464                } else if (strcmp(*argv, "mcast_igmp_version") == 0) {
 465                        NEXT_ARG();
 466                        if (get_u8(&val8, *argv, 0))
 467                                invarg("invalid mcast_igmp_version", *argv);
 468                        addattr8(&req.n, 1024,
 469                                 BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION, val8);
 470                } else if (strcmp(*argv, "mcast_mld_version") == 0) {
 471                        NEXT_ARG();
 472                        if (get_u8(&val8, *argv, 0))
 473                                invarg("invalid mcast_mld_version", *argv);
 474                        addattr8(&req.n, 1024,
 475                                 BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION, val8);
 476                } else if (strcmp(*argv, "mcast_last_member_count") == 0) {
 477                        NEXT_ARG();
 478                        if (get_u32(&val32, *argv, 0))
 479                                invarg("invalid mcast_last_member_count", *argv);
 480                        addattr32(&req.n, 1024,
 481                                  BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT,
 482                                  val32);
 483                } else if (strcmp(*argv, "mcast_startup_query_count") == 0) {
 484                        NEXT_ARG();
 485                        if (get_u32(&val32, *argv, 0))
 486                                invarg("invalid mcast_startup_query_count",
 487                                       *argv);
 488                        addattr32(&req.n, 1024,
 489                                  BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT,
 490                                  val32);
 491                } else if (strcmp(*argv, "mcast_last_member_interval") == 0) {
 492                        NEXT_ARG();
 493                        if (get_u64(&val64, *argv, 0))
 494                                invarg("invalid mcast_last_member_interval",
 495                                       *argv);
 496                        addattr64(&req.n, 1024,
 497                                  BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL,
 498                                  val64);
 499                } else if (strcmp(*argv, "mcast_membership_interval") == 0) {
 500                        NEXT_ARG();
 501                        if (get_u64(&val64, *argv, 0))
 502                                invarg("invalid mcast_membership_interval",
 503                                       *argv);
 504                        addattr64(&req.n, 1024,
 505                                  BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL,
 506                                  val64);
 507                } else if (strcmp(*argv, "mcast_querier_interval") == 0) {
 508                        NEXT_ARG();
 509                        if (get_u64(&val64, *argv, 0))
 510                                invarg("invalid mcast_querier_interval",
 511                                       *argv);
 512                        addattr64(&req.n, 1024,
 513                                  BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL,
 514                                  val64);
 515                } else if (strcmp(*argv, "mcast_query_interval") == 0) {
 516                        NEXT_ARG();
 517                        if (get_u64(&val64, *argv, 0))
 518                                invarg("invalid mcast_query_interval",
 519                                       *argv);
 520                        addattr64(&req.n, 1024,
 521                                  BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL,
 522                                  val64);
 523                } else if (strcmp(*argv, "mcast_query_response_interval") == 0) {
 524                        NEXT_ARG();
 525                        if (get_u64(&val64, *argv, 0))
 526                                invarg("invalid mcast_query_response_interval",
 527                                       *argv);
 528                        addattr64(&req.n, 1024,
 529                                  BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL,
 530                                  val64);
 531                } else if (strcmp(*argv, "mcast_startup_query_interval") == 0) {
 532                        NEXT_ARG();
 533                        if (get_u64(&val64, *argv, 0))
 534                                invarg("invalid mcast_startup_query_interval",
 535                                       *argv);
 536                        addattr64(&req.n, 1024,
 537                                  BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL,
 538                                  val64);
 539                } else if (strcmp(*argv, "msti") == 0) {
 540                        NEXT_ARG();
 541                        if (get_u16(&val16, *argv, 0))
 542                                invarg("invalid msti", *argv);
 543                        addattr16(&req.n, 1024,
 544                                 BRIDGE_VLANDB_GOPTS_MSTI, val16);
 545                } else {
 546                        if (strcmp(*argv, "help") == 0)
 547                                NEXT_ARG();
 548                }
 549                argc--; argv++;
 550        }
 551        addattr_nest_end(&req.n, afspec);
 552
 553        if (d == NULL || vid == -1) {
 554                fprintf(stderr, "Device and VLAN ID are required arguments.\n");
 555                return -1;
 556        }
 557
 558        if (rtnl_talk(&rth, &req.n, NULL) < 0)
 559                return -1;
 560
 561        return 0;
 562}
 563
 564/* In order to use this function for both filtering and non-filtering cases
 565 * we need to make it a tristate:
 566 * return -1 - if filtering we've gone over so don't continue
 567 * return  0 - skip entry and continue (applies to range start or to entries
 568 *             which are less than filter_vlan)
 569 * return  1 - print the entry and continue
 570 */
 571static int filter_vlan_check(__u16 vid, __u16 flags)
 572{
 573        /* if we're filtering we should stop on the first greater entry */
 574        if (filter_vlan && vid > filter_vlan &&
 575            !(flags & BRIDGE_VLAN_INFO_RANGE_END))
 576                return -1;
 577        if ((flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) ||
 578            vid < filter_vlan)
 579                return 0;
 580
 581        return 1;
 582}
 583
 584static void open_vlan_port(int ifi_index, enum vlan_show_subject subject)
 585{
 586        open_json_object(NULL);
 587        print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname",
 588                           "%-" textify(IFNAMSIZ) "s  ",
 589                           ll_index_to_name(ifi_index));
 590        open_json_array(PRINT_JSON,
 591                        subject == VLAN_SHOW_VLAN ? "vlans": "tunnels");
 592}
 593
 594static void close_vlan_port(void)
 595{
 596        close_json_array(PRINT_JSON, NULL);
 597        close_json_object();
 598}
 599
 600static void print_vlan_tunnel_info(struct rtattr *tb, int ifindex)
 601{
 602        struct rtattr *i, *list = tb;
 603        int rem = RTA_PAYLOAD(list);
 604        __u16 last_vid_start = 0;
 605        __u32 last_tunid_start = 0;
 606        bool opened = false;
 607
 608        for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 609                struct rtattr *ttb[IFLA_BRIDGE_VLAN_TUNNEL_MAX+1];
 610                __u32 tunnel_id = 0;
 611                __u16 tunnel_vid = 0;
 612                __u16 tunnel_flags = 0;
 613                unsigned int width;
 614                int vcheck_ret;
 615
 616                if (i->rta_type != IFLA_BRIDGE_VLAN_TUNNEL_INFO)
 617                        continue;
 618
 619                parse_rtattr(ttb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
 620                             RTA_DATA(i), RTA_PAYLOAD(i));
 621
 622                if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
 623                        tunnel_vid =
 624                                rta_getattr_u16(ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
 625                else
 626                        continue;
 627
 628                if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID])
 629                        tunnel_id =
 630                                rta_getattr_u32(ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
 631
 632                if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
 633                        tunnel_flags =
 634                                rta_getattr_u16(ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
 635
 636                if (!(tunnel_flags & BRIDGE_VLAN_INFO_RANGE_END)) {
 637                        last_vid_start = tunnel_vid;
 638                        last_tunid_start = tunnel_id;
 639                }
 640
 641                vcheck_ret = filter_vlan_check(tunnel_vid, tunnel_flags);
 642                if (vcheck_ret == -1)
 643                        break;
 644                else if (vcheck_ret == 0)
 645                        continue;
 646
 647                if (!opened) {
 648                        open_vlan_port(ifindex, VLAN_SHOW_TUNNELINFO);
 649                        opened = true;
 650                } else {
 651                        print_string(PRINT_FP, NULL,
 652                                     "%-" textify(IFNAMSIZ) "s  ", "");
 653                }
 654
 655                open_json_object(NULL);
 656                width = print_range("vlan", last_vid_start, tunnel_vid);
 657                if (!is_json_context())
 658                        printf("%-*s  ", VLAN_ID_LEN - width, "");
 659                print_range("tunid", last_tunid_start, tunnel_id);
 660                close_json_object();
 661                print_nl();
 662        }
 663
 664        if (opened)
 665                close_vlan_port();
 666}
 667
 668static int print_vlan(struct nlmsghdr *n, void *arg)
 669{
 670        enum vlan_show_subject *subject = arg;
 671        struct ifinfomsg *ifm = NLMSG_DATA(n);
 672        int len = n->nlmsg_len;
 673        struct rtattr *tb[IFLA_MAX+1];
 674
 675        if (n->nlmsg_type != RTM_NEWLINK) {
 676                fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
 677                        n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 678                return 0;
 679        }
 680
 681        len -= NLMSG_LENGTH(sizeof(*ifm));
 682        if (len < 0) {
 683                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 684                return -1;
 685        }
 686
 687        if (ifm->ifi_family != AF_BRIDGE)
 688                return 0;
 689
 690        if (filter_index && filter_index != ifm->ifi_index)
 691                return 0;
 692
 693        parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
 694        if (!tb[IFLA_AF_SPEC])
 695                return 0;
 696
 697        switch (*subject) {
 698        case VLAN_SHOW_VLAN:
 699                print_vlan_info(tb[IFLA_AF_SPEC], ifm->ifi_index);
 700                break;
 701        case VLAN_SHOW_TUNNELINFO:
 702                print_vlan_tunnel_info(tb[IFLA_AF_SPEC], ifm->ifi_index);
 703                break;
 704        }
 705
 706        return 0;
 707}
 708
 709static void print_vlan_stats_attr(struct rtattr *attr, int ifindex)
 710{
 711        struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
 712        struct rtattr *i, *list;
 713        bool found_vlan = false;
 714        int rem;
 715
 716        parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr),
 717                     RTA_PAYLOAD(attr));
 718        if (!brtb[LINK_XSTATS_TYPE_BRIDGE])
 719                return;
 720
 721        list = brtb[LINK_XSTATS_TYPE_BRIDGE];
 722        rem = RTA_PAYLOAD(list);
 723
 724        for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 725                const struct bridge_vlan_xstats *vstats = RTA_DATA(i);
 726
 727                if (i->rta_type != BRIDGE_XSTATS_VLAN)
 728                        continue;
 729
 730                if (filter_vlan && filter_vlan != vstats->vid)
 731                        continue;
 732
 733                /* skip pure port entries, they'll be dumped via the slave stats call */
 734                if ((vstats->flags & BRIDGE_VLAN_INFO_MASTER) &&
 735                    !(vstats->flags & BRIDGE_VLAN_INFO_BRENTRY))
 736                        continue;
 737
 738                /* found vlan stats, first time print the interface name */
 739                if (!found_vlan) {
 740                        open_vlan_port(ifindex, VLAN_SHOW_VLAN);
 741                        found_vlan = true;
 742                } else {
 743                        print_string(PRINT_FP, NULL,
 744                                     "%-" textify(IFNAMSIZ) "s  ", "");
 745                }
 746                bridge_print_vlan_stats(vstats);
 747        }
 748
 749        /* vlan_port is opened only if there are any vlan stats */
 750        if (found_vlan)
 751                close_vlan_port();
 752}
 753
 754static int print_vlan_stats(struct nlmsghdr *n, void *arg)
 755{
 756        struct if_stats_msg *ifsm = NLMSG_DATA(n);
 757        struct rtattr *tb[IFLA_STATS_MAX+1];
 758        int len = n->nlmsg_len;
 759        FILE *fp = arg;
 760
 761        len -= NLMSG_LENGTH(sizeof(*ifsm));
 762        if (len < 0) {
 763                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 764                return -1;
 765        }
 766
 767        if (filter_index && filter_index != ifsm->ifindex)
 768                return 0;
 769
 770        parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
 771
 772        /* We have to check if any of the two attrs are usable */
 773        if (tb[IFLA_STATS_LINK_XSTATS])
 774                print_vlan_stats_attr(tb[IFLA_STATS_LINK_XSTATS],
 775                                      ifsm->ifindex);
 776
 777        if (tb[IFLA_STATS_LINK_XSTATS_SLAVE])
 778                print_vlan_stats_attr(tb[IFLA_STATS_LINK_XSTATS_SLAVE],
 779                                      ifsm->ifindex);
 780
 781        fflush(fp);
 782        return 0;
 783}
 784
 785static void print_vlan_router_ports(struct rtattr *rattr)
 786{
 787        int rem = RTA_PAYLOAD(rattr);
 788        struct rtattr *i;
 789
 790        print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s    ", "");
 791        open_json_array(PRINT_ANY, is_json_context() ? "router_ports" :
 792                                                       "router ports: ");
 793        for (i = RTA_DATA(rattr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 794                uint32_t *port_ifindex = RTA_DATA(i);
 795                const char *port_ifname = ll_index_to_name(*port_ifindex);
 796
 797                open_json_object(NULL);
 798                if (show_stats && i != RTA_DATA(rattr)) {
 799                        print_nl();
 800                        /* start: IFNAMSIZ + 4 + strlen("router ports: ") */
 801                        print_string(PRINT_FP, NULL,
 802                                     "%-" textify(IFNAMSIZ) "s    "
 803                                     "              ",
 804                                     "");
 805                }
 806                print_string(PRINT_ANY, "port", "%s ", port_ifname);
 807                if (show_stats)
 808                        br_print_router_port_stats(i);
 809                close_json_object();
 810        }
 811        close_json_array(PRINT_JSON, NULL);
 812        print_nl();
 813}
 814
 815static void print_vlan_global_opts(struct rtattr *a, int ifindex)
 816{
 817        struct rtattr *vtb[BRIDGE_VLANDB_GOPTS_MAX + 1], *vattr;
 818        __u16 vid, vrange = 0;
 819
 820        if (rta_type(a) != BRIDGE_VLANDB_GLOBAL_OPTIONS)
 821                return;
 822
 823        parse_rtattr_flags(vtb, BRIDGE_VLANDB_GOPTS_MAX, RTA_DATA(a),
 824                           RTA_PAYLOAD(a), NLA_F_NESTED);
 825        vid = rta_getattr_u16(vtb[BRIDGE_VLANDB_GOPTS_ID]);
 826        if (vtb[BRIDGE_VLANDB_GOPTS_RANGE])
 827                vrange = rta_getattr_u16(vtb[BRIDGE_VLANDB_GOPTS_RANGE]);
 828        else
 829                vrange = vid;
 830
 831        if (filter_vlan && (filter_vlan < vid || filter_vlan > vrange))
 832                return;
 833
 834        if (vlan_rtm_cur_ifidx != ifindex) {
 835                open_vlan_port(ifindex, VLAN_SHOW_VLAN);
 836                open_json_object(NULL);
 837                vlan_rtm_cur_ifidx = ifindex;
 838        } else {
 839                open_json_object(NULL);
 840                print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s  ", "");
 841        }
 842        print_range("vlan", vid, vrange);
 843        print_nl();
 844        print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s    ", "");
 845        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING]) {
 846                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING];
 847                print_uint(PRINT_ANY, "mcast_snooping", "mcast_snooping %u ",
 848                           rta_getattr_u8(vattr));
 849        }
 850        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER]) {
 851                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER];
 852                print_uint(PRINT_ANY, "mcast_querier", "mcast_querier %u ",
 853                           rta_getattr_u8(vattr));
 854        }
 855        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE]) {
 856                struct rtattr *attr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE];
 857
 858                bridge_print_mcast_querier_state(attr);
 859        }
 860        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION]) {
 861                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION];
 862                print_uint(PRINT_ANY, "mcast_igmp_version",
 863                           "mcast_igmp_version %u ", rta_getattr_u8(vattr));
 864        }
 865        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION]) {
 866                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION];
 867                print_uint(PRINT_ANY, "mcast_mld_version",
 868                           "mcast_mld_version %u ", rta_getattr_u8(vattr));
 869        }
 870        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION]) {
 871                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT];
 872                print_uint(PRINT_ANY, "mcast_last_member_count",
 873                           "mcast_last_member_count %u ",
 874                           rta_getattr_u32(vattr));
 875        }
 876        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL]) {
 877                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL];
 878                print_lluint(PRINT_ANY, "mcast_last_member_interval",
 879                             "mcast_last_member_interval %llu ",
 880                             rta_getattr_u64(vattr));
 881        }
 882        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT]) {
 883                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT];
 884                print_uint(PRINT_ANY, "mcast_startup_query_count",
 885                           "mcast_startup_query_count %u ",
 886                           rta_getattr_u32(vattr));
 887        }
 888        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL]) {
 889                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL];
 890                print_lluint(PRINT_ANY, "mcast_startup_query_interval",
 891                             "mcast_startup_query_interval %llu ",
 892                             rta_getattr_u64(vattr));
 893        }
 894        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL]) {
 895                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL];
 896                print_lluint(PRINT_ANY, "mcast_membership_interval",
 897                             "mcast_membership_interval %llu ",
 898                             rta_getattr_u64(vattr));
 899        }
 900        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL]) {
 901                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL];
 902                print_lluint(PRINT_ANY, "mcast_querier_interval",
 903                             "mcast_querier_interval %llu ",
 904                             rta_getattr_u64(vattr));
 905        }
 906        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL]) {
 907                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL];
 908                print_lluint(PRINT_ANY, "mcast_query_interval",
 909                             "mcast_query_interval %llu ",
 910                             rta_getattr_u64(vattr));
 911        }
 912        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL]) {
 913                vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL];
 914                print_lluint(PRINT_ANY, "mcast_query_response_interval",
 915                             "mcast_query_response_interval %llu ",
 916                             rta_getattr_u64(vattr));
 917        }
 918        if (vtb[BRIDGE_VLANDB_GOPTS_MSTI]) {
 919                vattr = vtb[BRIDGE_VLANDB_GOPTS_MSTI];
 920                print_uint(PRINT_ANY, "msti", "msti %u ",
 921                           rta_getattr_u16(vattr));
 922        }
 923        print_nl();
 924        if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS]) {
 925                vattr = RTA_DATA(vtb[BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS]);
 926                print_vlan_router_ports(vattr);
 927        }
 928        close_json_object();
 929}
 930
 931static void print_vlan_opts(struct rtattr *a, int ifindex)
 932{
 933        struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1], *vattr;
 934        struct bridge_vlan_xstats vstats;
 935        struct bridge_vlan_info *vinfo;
 936        __u16 vrange = 0;
 937        __u8 state = 0;
 938
 939        if (rta_type(a) != BRIDGE_VLANDB_ENTRY)
 940                return;
 941
 942        parse_rtattr_flags(vtb, BRIDGE_VLANDB_ENTRY_MAX, RTA_DATA(a),
 943                           RTA_PAYLOAD(a), NLA_F_NESTED);
 944        vinfo = RTA_DATA(vtb[BRIDGE_VLANDB_ENTRY_INFO]);
 945
 946        memset(&vstats, 0, sizeof(vstats));
 947        if (vtb[BRIDGE_VLANDB_ENTRY_RANGE])
 948                vrange = rta_getattr_u16(vtb[BRIDGE_VLANDB_ENTRY_RANGE]);
 949        else
 950                vrange = vinfo->vid;
 951
 952        if (filter_vlan && (filter_vlan < vinfo->vid || filter_vlan > vrange))
 953                return;
 954
 955        if (vtb[BRIDGE_VLANDB_ENTRY_STATE])
 956                state = rta_getattr_u8(vtb[BRIDGE_VLANDB_ENTRY_STATE]);
 957
 958        if (vtb[BRIDGE_VLANDB_ENTRY_STATS]) {
 959                struct rtattr *stb[BRIDGE_VLANDB_STATS_MAX+1];
 960                struct rtattr *attr;
 961
 962                attr = vtb[BRIDGE_VLANDB_ENTRY_STATS];
 963                parse_rtattr(stb, BRIDGE_VLANDB_STATS_MAX, RTA_DATA(attr),
 964                             RTA_PAYLOAD(attr));
 965
 966                if (stb[BRIDGE_VLANDB_STATS_RX_BYTES]) {
 967                        attr = stb[BRIDGE_VLANDB_STATS_RX_BYTES];
 968                        vstats.rx_bytes = rta_getattr_u64(attr);
 969                }
 970                if (stb[BRIDGE_VLANDB_STATS_RX_PACKETS]) {
 971                        attr = stb[BRIDGE_VLANDB_STATS_RX_PACKETS];
 972                        vstats.rx_packets = rta_getattr_u64(attr);
 973                }
 974                if (stb[BRIDGE_VLANDB_STATS_TX_PACKETS]) {
 975                        attr = stb[BRIDGE_VLANDB_STATS_TX_PACKETS];
 976                        vstats.tx_packets = rta_getattr_u64(attr);
 977                }
 978                if (stb[BRIDGE_VLANDB_STATS_TX_BYTES]) {
 979                        attr = stb[BRIDGE_VLANDB_STATS_TX_BYTES];
 980                        vstats.tx_bytes = rta_getattr_u64(attr);
 981                }
 982        }
 983
 984        if (vlan_rtm_cur_ifidx != ifindex) {
 985                open_vlan_port(ifindex, VLAN_SHOW_VLAN);
 986                open_json_object(NULL);
 987                vlan_rtm_cur_ifidx = ifindex;
 988        } else {
 989                open_json_object(NULL);
 990                print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s  ", "");
 991        }
 992        print_range("vlan", vinfo->vid, vrange);
 993        bridge_print_vlan_flags(vinfo->flags);
 994        print_nl();
 995        print_string(PRINT_FP, NULL, "%-" textify(IFNAMSIZ) "s    ", "");
 996        print_stp_state(state);
 997        if (vtb[BRIDGE_VLANDB_ENTRY_MCAST_ROUTER]) {
 998                vattr = vtb[BRIDGE_VLANDB_ENTRY_MCAST_ROUTER];
 999                print_uint(PRINT_ANY, "mcast_router", "mcast_router %u ",
1000                           rta_getattr_u8(vattr));
1001        }
1002        if (vtb[BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS]) {
1003                vattr = vtb[BRIDGE_VLANDB_ENTRY_MCAST_N_GROUPS];
1004                print_uint(PRINT_ANY, "mcast_n_groups", "mcast_n_groups %u ",
1005                           rta_getattr_u32(vattr));
1006        }
1007        if (vtb[BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS]) {
1008                vattr = vtb[BRIDGE_VLANDB_ENTRY_MCAST_MAX_GROUPS];
1009                print_uint(PRINT_ANY, "mcast_max_groups", "mcast_max_groups %u ",
1010                           rta_getattr_u32(vattr));
1011        }
1012        if (vtb[BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS]) {
1013                vattr = vtb[BRIDGE_VLANDB_ENTRY_NEIGH_SUPPRESS];
1014                print_on_off(PRINT_ANY, "neigh_suppress", "neigh_suppress %s ",
1015                             rta_getattr_u8(vattr));
1016        }
1017        print_nl();
1018        if (show_stats)
1019                bridge_print_vlan_stats_only(&vstats);
1020        close_json_object();
1021}
1022
1023int print_vlan_rtm(struct nlmsghdr *n, void *arg, bool monitor, bool global_only)
1024{
1025        struct br_vlan_msg *bvm = NLMSG_DATA(n);
1026        int len = n->nlmsg_len;
1027        struct rtattr *a;
1028        FILE *fp = arg;
1029        int rem;
1030
1031        if (n->nlmsg_type != RTM_NEWVLAN && n->nlmsg_type != RTM_DELVLAN &&
1032            n->nlmsg_type != RTM_GETVLAN) {
1033                fprintf(stderr, "Unknown vlan rtm message: %08x %08x %08x\n",
1034                        n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
1035                return 0;
1036        }
1037
1038        len -= NLMSG_LENGTH(sizeof(*bvm));
1039        if (len < 0) {
1040                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
1041                return -1;
1042        }
1043
1044        if (bvm->family != AF_BRIDGE)
1045                return 0;
1046
1047        if (filter_index && filter_index != bvm->ifindex)
1048                return 0;
1049
1050        print_headers(fp, "[VLAN]");
1051
1052        if (n->nlmsg_type == RTM_DELVLAN)
1053                print_bool(PRINT_ANY, "deleted", "Deleted ", true);
1054
1055        if (monitor)
1056                vlan_rtm_cur_ifidx = -1;
1057
1058        if (vlan_rtm_cur_ifidx != -1 && vlan_rtm_cur_ifidx != bvm->ifindex) {
1059                close_vlan_port();
1060                vlan_rtm_cur_ifidx = -1;
1061        }
1062
1063        rem = len;
1064        for (a = BRVLAN_RTA(bvm); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) {
1065                unsigned short attr_type = rta_type(a);
1066
1067                /* skip unknown attributes */
1068                if (attr_type > BRIDGE_VLANDB_MAX ||
1069                    (global_only && attr_type != BRIDGE_VLANDB_GLOBAL_OPTIONS))
1070                        continue;
1071
1072                switch (attr_type) {
1073                case BRIDGE_VLANDB_ENTRY:
1074                        print_vlan_opts(a, bvm->ifindex);
1075                        break;
1076                case BRIDGE_VLANDB_GLOBAL_OPTIONS:
1077                        print_vlan_global_opts(a, bvm->ifindex);
1078                        break;
1079                }
1080        }
1081
1082        return 0;
1083}
1084
1085static int print_vlan_rtm_filter(struct nlmsghdr *n, void *arg)
1086{
1087        return print_vlan_rtm(n, arg, false, false);
1088}
1089
1090static int print_vlan_rtm_global_filter(struct nlmsghdr *n, void *arg)
1091{
1092        return print_vlan_rtm(n, arg, false, true);
1093}
1094
1095static int vlan_show(int argc, char **argv, int subject)
1096{
1097        char *filter_dev = NULL;
1098        int ret = 0;
1099
1100        while (argc > 0) {
1101                if (strcmp(*argv, "dev") == 0) {
1102                        NEXT_ARG();
1103                        if (filter_dev)
1104                                duparg("dev", *argv);
1105                        filter_dev = *argv;
1106                } else if (strcmp(*argv, "vid") == 0) {
1107                        NEXT_ARG();
1108                        if (filter_vlan)
1109                                duparg("vid", *argv);
1110                        filter_vlan = atoi(*argv);
1111                }
1112                argc--; argv++;
1113        }
1114
1115        if (filter_dev) {
1116                filter_index = ll_name_to_index(filter_dev);
1117                if (!filter_index)
1118                        return nodev(filter_dev);
1119        }
1120
1121        new_json_obj(json);
1122
1123        /* if show_details is true then use the new bridge vlan dump format */
1124        if (show_details && subject == VLAN_SHOW_VLAN) {
1125                __u32 dump_flags = show_stats ? BRIDGE_VLANDB_DUMPF_STATS : 0;
1126
1127                if (rtnl_brvlandump_req(&rth, PF_BRIDGE, dump_flags) < 0) {
1128                        perror("Cannot send dump request");
1129                        exit(1);
1130                }
1131
1132                if (!is_json_context()) {
1133                        printf("%-" textify(IFNAMSIZ) "s  %-"
1134                               textify(VLAN_ID_LEN) "s", "port",
1135                               "vlan-id");
1136                        printf("\n");
1137                }
1138
1139                ret = rtnl_dump_filter(&rth, print_vlan_rtm_filter, &subject);
1140                if (ret < 0) {
1141                        fprintf(stderr, "Dump terminated\n");
1142                        exit(1);
1143                }
1144
1145                if (vlan_rtm_cur_ifidx != -1)
1146                        close_vlan_port();
1147
1148                goto out;
1149        }
1150
1151        if (!show_stats) {
1152                if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE,
1153                                             (compress_vlans ?
1154                                              RTEXT_FILTER_BRVLAN_COMPRESSED :
1155                                              RTEXT_FILTER_BRVLAN)) < 0) {
1156                        perror("Cannot send dump request");
1157                        exit(1);
1158                }
1159
1160                if (!is_json_context()) {
1161                        printf("%-" textify(IFNAMSIZ) "s  %-"
1162                               textify(VLAN_ID_LEN) "s", "port",
1163                               "vlan-id");
1164                        if (subject == VLAN_SHOW_TUNNELINFO)
1165                                printf("  tunnel-id");
1166                        printf("\n");
1167                }
1168
1169                ret = rtnl_dump_filter(&rth, print_vlan, &subject);
1170                if (ret < 0) {
1171                        fprintf(stderr, "Dump terminated\n");
1172                        exit(1);
1173                }
1174        } else {
1175                __u32 filt_mask;
1176
1177                filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
1178                if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask,
1179                                              NULL, NULL) < 0) {
1180                        perror("Cannot send dump request");
1181                        exit(1);
1182                }
1183
1184                if (!is_json_context())
1185                        printf("%-" textify(IFNAMSIZ) "s  vlan-id\n",
1186                               "port");
1187
1188                if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
1189                        fprintf(stderr, "Dump terminated\n");
1190                        exit(1);
1191                }
1192
1193                filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE);
1194                if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask,
1195                                              NULL, NULL) < 0) {
1196                        perror("Cannot send slave dump request");
1197                        exit(1);
1198                }
1199
1200                if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
1201                        fprintf(stderr, "Dump terminated\n");
1202                        exit(1);
1203                }
1204        }
1205
1206out:
1207        delete_json_obj();
1208        fflush(stdout);
1209        return 0;
1210}
1211
1212static int vlan_global_show(int argc, char **argv)
1213{
1214        __u32 dump_flags = BRIDGE_VLANDB_DUMPF_GLOBAL;
1215        int ret = 0, subject = VLAN_SHOW_VLAN;
1216        char *filter_dev = NULL;
1217
1218        while (argc > 0) {
1219                if (strcmp(*argv, "dev") == 0) {
1220                        NEXT_ARG();
1221                        if (filter_dev)
1222                                duparg("dev", *argv);
1223                        filter_dev = *argv;
1224                } else if (strcmp(*argv, "vid") == 0) {
1225                        NEXT_ARG();
1226                        if (filter_vlan)
1227                                duparg("vid", *argv);
1228                        filter_vlan = atoi(*argv);
1229                }
1230                argc--; argv++;
1231        }
1232
1233        if (filter_dev) {
1234                filter_index = ll_name_to_index(filter_dev);
1235                if (!filter_index)
1236                        return nodev(filter_dev);
1237        }
1238
1239        new_json_obj(json);
1240
1241        if (rtnl_brvlandump_req(&rth, PF_BRIDGE, dump_flags) < 0) {
1242                perror("Cannot send dump request");
1243                exit(1);
1244        }
1245
1246        if (!is_json_context()) {
1247                printf("%-" textify(IFNAMSIZ) "s  %-"
1248                       textify(VLAN_ID_LEN) "s", "port",
1249                       "vlan-id");
1250                printf("\n");
1251        }
1252
1253        ret = rtnl_dump_filter(&rth, print_vlan_rtm_global_filter, &subject);
1254        if (ret < 0) {
1255                fprintf(stderr, "Dump terminated\n");
1256                exit(1);
1257        }
1258
1259        if (vlan_rtm_cur_ifidx != -1)
1260                close_vlan_port();
1261
1262        delete_json_obj();
1263        fflush(stdout);
1264        return 0;
1265}
1266
1267static void print_vlan_info(struct rtattr *tb, int ifindex)
1268{
1269        struct rtattr *i, *list = tb;
1270        int rem = RTA_PAYLOAD(list);
1271        __u16 last_vid_start = 0;
1272        bool opened = false;
1273
1274        for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
1275                struct bridge_vlan_info *vinfo;
1276                int vcheck_ret;
1277
1278                if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
1279                        continue;
1280
1281                vinfo = RTA_DATA(i);
1282
1283                if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
1284                        last_vid_start = vinfo->vid;
1285                vcheck_ret = filter_vlan_check(vinfo->vid, vinfo->flags);
1286                if (vcheck_ret == -1)
1287                        break;
1288                else if (vcheck_ret == 0)
1289                        continue;
1290
1291                if (!opened) {
1292                        open_vlan_port(ifindex, VLAN_SHOW_VLAN);
1293                        opened = true;
1294                } else {
1295                        print_string(PRINT_FP, NULL, "%-"
1296                                     textify(IFNAMSIZ) "s  ", "");
1297                }
1298
1299                open_json_object(NULL);
1300                print_range("vlan", last_vid_start, vinfo->vid);
1301
1302                bridge_print_vlan_flags(vinfo->flags);
1303                close_json_object();
1304                print_nl();
1305        }
1306
1307        if (opened)
1308                close_vlan_port();
1309}
1310
1311static int vlan_global(int argc, char **argv)
1312{
1313        if (argc > 0) {
1314                if (strcmp(*argv, "show") == 0 ||
1315                    strcmp(*argv, "lst") == 0 ||
1316                    strcmp(*argv, "list") == 0)
1317                        return vlan_global_show(argc-1, argv+1);
1318                else if (strcmp(*argv, "set") == 0)
1319                        return vlan_global_option_set(argc-1, argv+1);
1320                else
1321                        usage();
1322        } else {
1323                return vlan_global_show(0, NULL);
1324        }
1325
1326        return 0;
1327}
1328
1329int do_vlan(int argc, char **argv)
1330{
1331        ll_init_map(&rth);
1332        timestamp = 0;
1333
1334        if (argc > 0) {
1335                if (matches(*argv, "add") == 0)
1336                        return vlan_modify(RTM_SETLINK, argc-1, argv+1);
1337                if (matches(*argv, "delete") == 0)
1338                        return vlan_modify(RTM_DELLINK, argc-1, argv+1);
1339                if (matches(*argv, "show") == 0 ||
1340                    matches(*argv, "lst") == 0 ||
1341                    matches(*argv, "list") == 0)
1342                        return vlan_show(argc-1, argv+1, VLAN_SHOW_VLAN);
1343                if (matches(*argv, "tunnelshow") == 0) {
1344                        return vlan_show(argc-1, argv+1, VLAN_SHOW_TUNNELINFO);
1345                }
1346                if (matches(*argv, "set") == 0)
1347                        return vlan_option_set(argc-1, argv+1);
1348                if (strcmp(*argv, "global") == 0)
1349                        return vlan_global(argc-1, argv+1);
1350                if (matches(*argv, "help") == 0)
1351                        usage();
1352        } else {
1353                return vlan_show(0, NULL, VLAN_SHOW_VLAN);
1354        }
1355
1356        fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv);
1357        exit(-1);
1358}
1359