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 <net/if.h>
   8#include <netinet/in.h>
   9#include <linux/if_bridge.h>
  10#include <linux/if_ether.h>
  11#include <string.h>
  12
  13#include "json_print.h"
  14#include "libnetlink.h"
  15#include "br_common.h"
  16#include "utils.h"
  17
  18static unsigned int filter_index, filter_vlan;
  19
  20enum vlan_show_subject {
  21        VLAN_SHOW_VLAN,
  22        VLAN_SHOW_TUNNELINFO,
  23};
  24
  25#define VLAN_ID_LEN 9
  26
  27#define __stringify_1(x...) #x
  28#define __stringify(x...) __stringify_1(x)
  29
  30static void usage(void)
  31{
  32        fprintf(stderr,
  33                "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ tunnel_info id TUNNEL_ID ]\n"
  34                "                                                     [ pvid ] [ untagged ]\n"
  35                "                                                     [ self ] [ master ]\n"
  36                "       bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n"
  37                "       bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n");
  38        exit(-1);
  39}
  40
  41static int parse_tunnel_info(int *argcp, char ***argvp, __u32 *tun_id_start,
  42                             __u32 *tun_id_end)
  43{
  44        char **argv = *argvp;
  45        int argc = *argcp;
  46        char *t;
  47
  48        NEXT_ARG();
  49        if (!matches(*argv, "id")) {
  50                NEXT_ARG();
  51                t = strchr(*argv, '-');
  52                if (t) {
  53                        *t = '\0';
  54                        if (get_u32(tun_id_start, *argv, 0) ||
  55                                    *tun_id_start >= 1u << 24)
  56                                invarg("invalid tun id", *argv);
  57                        if (get_u32(tun_id_end, t + 1, 0) ||
  58                                    *tun_id_end >= 1u << 24)
  59                                invarg("invalid tun id", *argv);
  60
  61                } else {
  62                        if (get_u32(tun_id_start, *argv, 0) ||
  63                                    *tun_id_start >= 1u << 24)
  64                                invarg("invalid tun id", *argv);
  65                }
  66        } else {
  67                invarg("tunnel id expected", *argv);
  68        }
  69
  70        *argcp = argc;
  71        *argvp = argv;
  72
  73        return 0;
  74}
  75
  76static int add_tunnel_info(struct nlmsghdr *n, int reqsize,
  77                           __u16 vid, __u32 tun_id, __u16 flags)
  78{
  79        struct rtattr *tinfo;
  80
  81        tinfo = addattr_nest(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
  82        addattr32(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_ID, tun_id);
  83        addattr16(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_VID, vid);
  84        addattr16(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, flags);
  85
  86        addattr_nest_end(n, tinfo);
  87
  88        return 0;
  89}
  90
  91static int add_tunnel_info_range(struct nlmsghdr *n, int reqsize,
  92                                 __u16 vid_start, int16_t vid_end,
  93                                 __u32 tun_id_start, __u32 tun_id_end)
  94{
  95        if (vid_end != -1 && (vid_end - vid_start) > 0) {
  96                add_tunnel_info(n, reqsize, vid_start, tun_id_start,
  97                                BRIDGE_VLAN_INFO_RANGE_BEGIN);
  98
  99                add_tunnel_info(n, reqsize, vid_end, tun_id_end,
 100                                BRIDGE_VLAN_INFO_RANGE_END);
 101        } else {
 102                add_tunnel_info(n, reqsize, vid_start, tun_id_start, 0);
 103        }
 104
 105        return 0;
 106}
 107
 108static int add_vlan_info_range(struct nlmsghdr *n, int reqsize, __u16 vid_start,
 109                               int16_t vid_end, __u16 flags)
 110{
 111        struct bridge_vlan_info vinfo = {};
 112
 113        vinfo.flags = flags;
 114        vinfo.vid = vid_start;
 115        if (vid_end != -1) {
 116                /* send vlan range start */
 117                addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
 118                          sizeof(vinfo));
 119                vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
 120
 121                /* Now send the vlan range end */
 122                vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
 123                vinfo.vid = vid_end;
 124                addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
 125                          sizeof(vinfo));
 126        } else {
 127                addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo,
 128                          sizeof(vinfo));
 129        }
 130
 131        return 0;
 132}
 133
 134static int vlan_modify(int cmd, int argc, char **argv)
 135{
 136        struct {
 137                struct nlmsghdr n;
 138                struct ifinfomsg        ifm;
 139                char                    buf[1024];
 140        } req = {
 141                .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
 142                .n.nlmsg_flags = NLM_F_REQUEST,
 143                .n.nlmsg_type = cmd,
 144                .ifm.ifi_family = PF_BRIDGE,
 145        };
 146        char *d = NULL;
 147        short vid = -1;
 148        short vid_end = -1;
 149        struct rtattr *afspec;
 150        struct bridge_vlan_info vinfo = {};
 151        bool tunnel_info_set = false;
 152        unsigned short flags = 0;
 153        __u32 tun_id_start = 0;
 154        __u32 tun_id_end = 0;
 155
 156        while (argc > 0) {
 157                if (strcmp(*argv, "dev") == 0) {
 158                        NEXT_ARG();
 159                        d = *argv;
 160                } else if (strcmp(*argv, "vid") == 0) {
 161                        char *p;
 162
 163                        NEXT_ARG();
 164                        p = strchr(*argv, '-');
 165                        if (p) {
 166                                *p = '\0';
 167                                p++;
 168                                vid = atoi(*argv);
 169                                vid_end = atoi(p);
 170                                vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
 171                        } else {
 172                                vid = atoi(*argv);
 173                        }
 174                } else if (strcmp(*argv, "self") == 0) {
 175                        flags |= BRIDGE_FLAGS_SELF;
 176                } else if (strcmp(*argv, "master") == 0) {
 177                        flags |= BRIDGE_FLAGS_MASTER;
 178                } else if (strcmp(*argv, "pvid") == 0) {
 179                        vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
 180                } else if (strcmp(*argv, "untagged") == 0) {
 181                        vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
 182                } else if (strcmp(*argv, "tunnel_info") == 0) {
 183                                if (parse_tunnel_info(&argc, &argv,
 184                                                      &tun_id_start,
 185                                                      &tun_id_end))
 186                                        return -1;
 187                                tunnel_info_set = true;
 188                } else {
 189                        if (matches(*argv, "help") == 0)
 190                                NEXT_ARG();
 191                }
 192                argc--; argv++;
 193        }
 194
 195        if (d == NULL || vid == -1) {
 196                fprintf(stderr, "Device and VLAN ID are required arguments.\n");
 197                return -1;
 198        }
 199
 200        req.ifm.ifi_index = ll_name_to_index(d);
 201        if (req.ifm.ifi_index == 0) {
 202                fprintf(stderr, "Cannot find bridge device \"%s\"\n", d);
 203                return -1;
 204        }
 205
 206        if (vid >= 4096) {
 207                fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid);
 208                return -1;
 209        }
 210
 211        if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
 212                if (vid_end == -1 || vid_end >= 4096 || vid >= vid_end) {
 213                        fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
 214                                vid, vid_end);
 215                        return -1;
 216                }
 217                if (vinfo.flags & BRIDGE_VLAN_INFO_PVID) {
 218                        fprintf(stderr,
 219                                "pvid cannot be configured for a vlan range\n");
 220                        return -1;
 221                }
 222        }
 223
 224        afspec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC);
 225
 226        if (flags)
 227                addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags);
 228
 229        if (tunnel_info_set)
 230                add_tunnel_info_range(&req.n, sizeof(req), vid, vid_end,
 231                                      tun_id_start, tun_id_end);
 232        else
 233                add_vlan_info_range(&req.n, sizeof(req), vid, vid_end,
 234                                    vinfo.flags);
 235
 236        addattr_nest_end(&req.n, afspec);
 237
 238        if (rtnl_talk(&rth, &req.n, NULL) < 0)
 239                return -1;
 240
 241        return 0;
 242}
 243
 244/* In order to use this function for both filtering and non-filtering cases
 245 * we need to make it a tristate:
 246 * return -1 - if filtering we've gone over so don't continue
 247 * return  0 - skip entry and continue (applies to range start or to entries
 248 *             which are less than filter_vlan)
 249 * return  1 - print the entry and continue
 250 */
 251static int filter_vlan_check(__u16 vid, __u16 flags)
 252{
 253        /* if we're filtering we should stop on the first greater entry */
 254        if (filter_vlan && vid > filter_vlan &&
 255            !(flags & BRIDGE_VLAN_INFO_RANGE_END))
 256                return -1;
 257        if ((flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) ||
 258            vid < filter_vlan)
 259                return 0;
 260
 261        return 1;
 262}
 263
 264static void open_vlan_port(int ifi_index, enum vlan_show_subject subject)
 265{
 266        open_json_object(NULL);
 267        print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname",
 268                           "%-" __stringify(IFNAMSIZ) "s  ",
 269                           ll_index_to_name(ifi_index));
 270        open_json_array(PRINT_JSON,
 271                        subject == VLAN_SHOW_VLAN ? "vlans": "tunnels");
 272}
 273
 274static void close_vlan_port(void)
 275{
 276        close_json_array(PRINT_JSON, NULL);
 277        close_json_object();
 278}
 279
 280static unsigned int print_range(const char *name, __u32 start, __u32 id)
 281{
 282        char end[64];
 283        int width;
 284
 285        snprintf(end, sizeof(end), "%sEnd", name);
 286
 287        width = print_uint(PRINT_ANY, name, "%u", start);
 288        if (start != id)
 289                width += print_uint(PRINT_ANY, end, "-%u", id);
 290
 291        return width;
 292}
 293
 294static void print_vlan_tunnel_info(struct rtattr *tb, int ifindex)
 295{
 296        struct rtattr *i, *list = tb;
 297        int rem = RTA_PAYLOAD(list);
 298        __u16 last_vid_start = 0;
 299        __u32 last_tunid_start = 0;
 300        bool opened = false;
 301
 302        for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 303                struct rtattr *ttb[IFLA_BRIDGE_VLAN_TUNNEL_MAX+1];
 304                __u32 tunnel_id = 0;
 305                __u16 tunnel_vid = 0;
 306                __u16 tunnel_flags = 0;
 307                unsigned int width;
 308                int vcheck_ret;
 309
 310                if (i->rta_type != IFLA_BRIDGE_VLAN_TUNNEL_INFO)
 311                        continue;
 312
 313                parse_rtattr(ttb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
 314                             RTA_DATA(i), RTA_PAYLOAD(i));
 315
 316                if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
 317                        tunnel_vid =
 318                                rta_getattr_u16(ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
 319                else
 320                        continue;
 321
 322                if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID])
 323                        tunnel_id =
 324                                rta_getattr_u32(ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
 325
 326                if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
 327                        tunnel_flags =
 328                                rta_getattr_u16(ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
 329
 330                if (!(tunnel_flags & BRIDGE_VLAN_INFO_RANGE_END)) {
 331                        last_vid_start = tunnel_vid;
 332                        last_tunid_start = tunnel_id;
 333                }
 334
 335                vcheck_ret = filter_vlan_check(tunnel_vid, tunnel_flags);
 336                if (vcheck_ret == -1)
 337                        break;
 338                else if (vcheck_ret == 0)
 339                        continue;
 340
 341                if (!opened) {
 342                        open_vlan_port(ifindex, VLAN_SHOW_TUNNELINFO);
 343                        opened = true;
 344                } else {
 345                        print_string(PRINT_FP, NULL,
 346                                     "%-" __stringify(IFNAMSIZ) "s  ", "");
 347                }
 348
 349                open_json_object(NULL);
 350                width = print_range("vlan", last_vid_start, tunnel_vid);
 351                if (width <= VLAN_ID_LEN) {
 352                        char buf[VLAN_ID_LEN + 1];
 353
 354                        snprintf(buf, sizeof(buf), "%-*s",
 355                                 VLAN_ID_LEN - width, "");
 356                        print_string(PRINT_FP, NULL, "%s  ", buf);
 357                } else {
 358                        fprintf(stderr, "BUG: vlan range too wide, %u\n",
 359                                width);
 360                }
 361                print_range("tunid", last_tunid_start, tunnel_id);
 362                close_json_object();
 363                print_nl();
 364        }
 365
 366        if (opened)
 367                close_vlan_port();
 368}
 369
 370static int print_vlan(struct nlmsghdr *n, void *arg)
 371{
 372        enum vlan_show_subject *subject = arg;
 373        struct ifinfomsg *ifm = NLMSG_DATA(n);
 374        int len = n->nlmsg_len;
 375        struct rtattr *tb[IFLA_MAX+1];
 376
 377        if (n->nlmsg_type != RTM_NEWLINK) {
 378                fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
 379                        n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
 380                return 0;
 381        }
 382
 383        len -= NLMSG_LENGTH(sizeof(*ifm));
 384        if (len < 0) {
 385                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 386                return -1;
 387        }
 388
 389        if (ifm->ifi_family != AF_BRIDGE)
 390                return 0;
 391
 392        if (filter_index && filter_index != ifm->ifi_index)
 393                return 0;
 394
 395        parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
 396        if (!tb[IFLA_AF_SPEC])
 397                return 0;
 398
 399        switch (*subject) {
 400        case VLAN_SHOW_VLAN:
 401                print_vlan_info(tb[IFLA_AF_SPEC], ifm->ifi_index);
 402                break;
 403        case VLAN_SHOW_TUNNELINFO:
 404                print_vlan_tunnel_info(tb[IFLA_AF_SPEC], ifm->ifi_index);
 405                break;
 406        }
 407
 408        return 0;
 409}
 410
 411static void print_vlan_flags(__u16 flags)
 412{
 413        if (flags == 0)
 414                return;
 415
 416        open_json_array(PRINT_JSON, "flags");
 417        if (flags & BRIDGE_VLAN_INFO_PVID)
 418                print_string(PRINT_ANY, NULL, " %s", "PVID");
 419
 420        if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
 421                print_string(PRINT_ANY, NULL, " %s", "Egress Untagged");
 422        close_json_array(PRINT_JSON, NULL);
 423}
 424
 425static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
 426{
 427        open_json_object(NULL);
 428
 429        print_hu(PRINT_ANY, "vid", "%hu", vstats->vid);
 430        print_vlan_flags(vstats->flags);
 431        print_nl();
 432
 433        print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s    ", "");
 434        print_lluint(PRINT_ANY, "rx_bytes", "RX: %llu bytes",
 435                     vstats->rx_bytes);
 436        print_lluint(PRINT_ANY, "rx_packets", " %llu packets\n",
 437                     vstats->rx_packets);
 438
 439        print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s    ", "");
 440        print_lluint(PRINT_ANY, "tx_bytes", "TX: %llu bytes",
 441                     vstats->tx_bytes);
 442        print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n",
 443                     vstats->tx_packets);
 444
 445        close_json_object();
 446}
 447
 448static void print_vlan_stats_attr(struct rtattr *attr, int ifindex)
 449{
 450        struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
 451        struct rtattr *i, *list;
 452        bool found_vlan = false;
 453        int rem;
 454
 455        parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr),
 456                     RTA_PAYLOAD(attr));
 457        if (!brtb[LINK_XSTATS_TYPE_BRIDGE])
 458                return;
 459
 460        list = brtb[LINK_XSTATS_TYPE_BRIDGE];
 461        rem = RTA_PAYLOAD(list);
 462
 463        for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 464                const struct bridge_vlan_xstats *vstats = RTA_DATA(i);
 465
 466                if (i->rta_type != BRIDGE_XSTATS_VLAN)
 467                        continue;
 468
 469                if (filter_vlan && filter_vlan != vstats->vid)
 470                        continue;
 471
 472                /* skip pure port entries, they'll be dumped via the slave stats call */
 473                if ((vstats->flags & BRIDGE_VLAN_INFO_MASTER) &&
 474                    !(vstats->flags & BRIDGE_VLAN_INFO_BRENTRY))
 475                        continue;
 476
 477                /* found vlan stats, first time print the interface name */
 478                if (!found_vlan) {
 479                        open_vlan_port(ifindex, VLAN_SHOW_VLAN);
 480                        found_vlan = true;
 481                } else {
 482                        print_string(PRINT_FP, NULL,
 483                                     "%-" __stringify(IFNAMSIZ) "s  ", "");
 484                }
 485                print_one_vlan_stats(vstats);
 486        }
 487
 488        /* vlan_port is opened only if there are any vlan stats */
 489        if (found_vlan)
 490                close_vlan_port();
 491}
 492
 493static int print_vlan_stats(struct nlmsghdr *n, void *arg)
 494{
 495        struct if_stats_msg *ifsm = NLMSG_DATA(n);
 496        struct rtattr *tb[IFLA_STATS_MAX+1];
 497        int len = n->nlmsg_len;
 498        FILE *fp = arg;
 499
 500        len -= NLMSG_LENGTH(sizeof(*ifsm));
 501        if (len < 0) {
 502                fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
 503                return -1;
 504        }
 505
 506        if (filter_index && filter_index != ifsm->ifindex)
 507                return 0;
 508
 509        parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
 510
 511        /* We have to check if any of the two attrs are usable */
 512        if (tb[IFLA_STATS_LINK_XSTATS])
 513                print_vlan_stats_attr(tb[IFLA_STATS_LINK_XSTATS],
 514                                      ifsm->ifindex);
 515
 516        if (tb[IFLA_STATS_LINK_XSTATS_SLAVE])
 517                print_vlan_stats_attr(tb[IFLA_STATS_LINK_XSTATS_SLAVE],
 518                                      ifsm->ifindex);
 519
 520        fflush(fp);
 521        return 0;
 522}
 523
 524static int vlan_show(int argc, char **argv, int subject)
 525{
 526        char *filter_dev = NULL;
 527        int ret = 0;
 528
 529        while (argc > 0) {
 530                if (strcmp(*argv, "dev") == 0) {
 531                        NEXT_ARG();
 532                        if (filter_dev)
 533                                duparg("dev", *argv);
 534                        filter_dev = *argv;
 535                } else if (strcmp(*argv, "vid") == 0) {
 536                        NEXT_ARG();
 537                        if (filter_vlan)
 538                                duparg("vid", *argv);
 539                        filter_vlan = atoi(*argv);
 540                }
 541                argc--; argv++;
 542        }
 543
 544        if (filter_dev) {
 545                filter_index = ll_name_to_index(filter_dev);
 546                if (!filter_index)
 547                        return nodev(filter_dev);
 548        }
 549
 550        new_json_obj(json);
 551
 552        if (!show_stats) {
 553                if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE,
 554                                             (compress_vlans ?
 555                                              RTEXT_FILTER_BRVLAN_COMPRESSED :
 556                                              RTEXT_FILTER_BRVLAN)) < 0) {
 557                        perror("Cannot send dump request");
 558                        exit(1);
 559                }
 560
 561                if (!is_json_context()) {
 562                        printf("%-" __stringify(IFNAMSIZ) "s  %-"
 563                               __stringify(VLAN_ID_LEN) "s", "port",
 564                               "vlan-id");
 565                        if (subject == VLAN_SHOW_TUNNELINFO)
 566                                printf("  tunnel-id");
 567                        printf("\n");
 568                }
 569
 570                ret = rtnl_dump_filter(&rth, print_vlan, &subject);
 571                if (ret < 0) {
 572                        fprintf(stderr, "Dump terminated\n");
 573                        exit(1);
 574                }
 575        } else {
 576                __u32 filt_mask;
 577
 578                filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
 579                if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
 580                        perror("Cannot send dump request");
 581                        exit(1);
 582                }
 583
 584                if (!is_json_context())
 585                        printf("%-" __stringify(IFNAMSIZ) "s  vlan-id\n",
 586                               "port");
 587
 588                if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
 589                        fprintf(stderr, "Dump terminated\n");
 590                        exit(1);
 591                }
 592
 593                filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE);
 594                if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
 595                        perror("Cannot send slave dump request");
 596                        exit(1);
 597                }
 598
 599                if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
 600                        fprintf(stderr, "Dump terminated\n");
 601                        exit(1);
 602                }
 603        }
 604
 605        delete_json_obj();
 606        fflush(stdout);
 607        return 0;
 608}
 609
 610void print_vlan_info(struct rtattr *tb, int ifindex)
 611{
 612        struct rtattr *i, *list = tb;
 613        int rem = RTA_PAYLOAD(list);
 614        __u16 last_vid_start = 0;
 615        bool opened = false;
 616
 617        for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 618                struct bridge_vlan_info *vinfo;
 619                int vcheck_ret;
 620
 621                if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
 622                        continue;
 623
 624                vinfo = RTA_DATA(i);
 625
 626                if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
 627                        last_vid_start = vinfo->vid;
 628                vcheck_ret = filter_vlan_check(vinfo->vid, vinfo->flags);
 629                if (vcheck_ret == -1)
 630                        break;
 631                else if (vcheck_ret == 0)
 632                        continue;
 633
 634                if (!opened) {
 635                        open_vlan_port(ifindex, VLAN_SHOW_VLAN);
 636                        opened = true;
 637                } else {
 638                        print_string(PRINT_FP, NULL, "%-"
 639                                     __stringify(IFNAMSIZ) "s  ", "");
 640                }
 641
 642                open_json_object(NULL);
 643                print_range("vlan", last_vid_start, vinfo->vid);
 644
 645                print_vlan_flags(vinfo->flags);
 646                close_json_object();
 647                print_nl();
 648        }
 649
 650        if (opened)
 651                close_vlan_port();
 652}
 653
 654int do_vlan(int argc, char **argv)
 655{
 656        ll_init_map(&rth);
 657
 658        if (argc > 0) {
 659                if (matches(*argv, "add") == 0)
 660                        return vlan_modify(RTM_SETLINK, argc-1, argv+1);
 661                if (matches(*argv, "delete") == 0)
 662                        return vlan_modify(RTM_DELLINK, argc-1, argv+1);
 663                if (matches(*argv, "show") == 0 ||
 664                    matches(*argv, "lst") == 0 ||
 665                    matches(*argv, "list") == 0)
 666                        return vlan_show(argc-1, argv+1, VLAN_SHOW_VLAN);
 667                if (matches(*argv, "tunnelshow") == 0) {
 668                        return vlan_show(argc-1, argv+1, VLAN_SHOW_TUNNELINFO);
 669                }
 670                if (matches(*argv, "help") == 0)
 671                        usage();
 672        } else {
 673                return vlan_show(0, NULL, VLAN_SHOW_VLAN);
 674        }
 675
 676        fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv);
 677        exit(-1);
 678}
 679