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