]> git.proxmox.com Git - mirror_iproute2.git/blob - bridge/vlan.c
bridge: implement json pretty print flag
[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_name(jw_global,
262 ll_index_to_name(ifi_index));
263 jsonw_start_array(jw_global);
264 } else {
265 fprintf(fp, "%s",
266 ll_index_to_name(ifi_index));
267 }
268 }
269
270 static void start_json_vlan_flags_array(bool *vlan_flags)
271 {
272 if (*vlan_flags)
273 return;
274 jsonw_name(jw_global, "flags");
275 jsonw_start_array(jw_global);
276 *vlan_flags = true;
277 }
278
279 static void print_vlan_tunnel_info(FILE *fp, struct rtattr *tb, int ifindex)
280 {
281 bool jsonw_end_parray = false;
282 struct rtattr *i, *list = tb;
283 int rem = RTA_PAYLOAD(list);
284 __u16 last_vid_start = 0;
285 __u32 last_tunid_start = 0;
286
287 if (!filter_vlan) {
288 print_vlan_port(fp, ifindex);
289 jsonw_end_parray = 1;
290 }
291
292 for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
293 struct rtattr *ttb[IFLA_BRIDGE_VLAN_TUNNEL_MAX+1];
294 __u32 tunnel_id = 0;
295 __u16 tunnel_vid = 0;
296 __u16 tunnel_flags = 0;
297 int vcheck_ret;
298
299 if (i->rta_type != IFLA_BRIDGE_VLAN_TUNNEL_INFO)
300 continue;
301
302 parse_rtattr(ttb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
303 RTA_DATA(i), RTA_PAYLOAD(i));
304
305 if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
306 tunnel_vid =
307 rta_getattr_u32(ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
308 else
309 continue;
310
311 if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID])
312 tunnel_id =
313 rta_getattr_u32(ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
314
315 if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
316 tunnel_flags =
317 rta_getattr_u32(ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
318
319 if (!(tunnel_flags & BRIDGE_VLAN_INFO_RANGE_END)) {
320 last_vid_start = tunnel_vid;
321 last_tunid_start = tunnel_id;
322 }
323 vcheck_ret = filter_vlan_check(tunnel_vid, tunnel_flags);
324 if (vcheck_ret == -1)
325 break;
326 else if (vcheck_ret == 0)
327 continue;
328
329 if (tunnel_flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
330 continue;
331
332 if (filter_vlan) {
333 print_vlan_port(fp, ifindex);
334 jsonw_end_parray = 1;
335 }
336
337 if (jw_global) {
338 jsonw_start_object(jw_global);
339 jsonw_uint_field(jw_global, "vlan",
340 last_vid_start);
341 } else {
342 fprintf(fp, "\t %hu", last_vid_start);
343 }
344 if (last_vid_start != tunnel_vid) {
345 if (jw_global)
346 jsonw_uint_field(jw_global, "vlanEnd",
347 tunnel_vid);
348 else
349 fprintf(fp, "-%hu", tunnel_vid);
350 }
351
352 if (jw_global) {
353 jsonw_uint_field(jw_global, "tunid",
354 last_tunid_start);
355 } else {
356 fprintf(fp, "\t %hu", last_tunid_start);
357 }
358 if (last_vid_start != tunnel_vid) {
359 if (jw_global)
360 jsonw_uint_field(jw_global, "tunidEnd",
361 tunnel_id);
362 else
363 fprintf(fp, "-%hu", tunnel_id);
364 }
365
366 if (jw_global)
367 jsonw_end_object(jw_global);
368 else
369 fprintf(fp, "\n");
370 }
371
372 if (jsonw_end_parray) {
373 if (jw_global)
374 jsonw_end_array(jw_global);
375 else
376 fprintf(fp, "\n");
377 }
378 }
379
380 static int print_vlan_tunnel(const struct sockaddr_nl *who,
381 struct nlmsghdr *n,
382 void *arg)
383 {
384 struct ifinfomsg *ifm = NLMSG_DATA(n);
385 struct rtattr *tb[IFLA_MAX+1];
386 int len = n->nlmsg_len;
387 FILE *fp = arg;
388
389 if (n->nlmsg_type != RTM_NEWLINK) {
390 fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
391 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
392 return 0;
393 }
394
395 len -= NLMSG_LENGTH(sizeof(*ifm));
396 if (len < 0) {
397 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
398 return -1;
399 }
400
401 if (ifm->ifi_family != AF_BRIDGE)
402 return 0;
403
404 if (filter_index && filter_index != ifm->ifi_index)
405 return 0;
406
407 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
408
409 /* if AF_SPEC isn't there, vlan table is not preset for this port */
410 if (!tb[IFLA_AF_SPEC]) {
411 if (!filter_vlan && !jw_global)
412 fprintf(fp, "%s\tNone\n",
413 ll_index_to_name(ifm->ifi_index));
414 return 0;
415 }
416
417 print_vlan_tunnel_info(fp, tb[IFLA_AF_SPEC], ifm->ifi_index);
418
419 fflush(fp);
420 return 0;
421 }
422
423 static int print_vlan(const struct sockaddr_nl *who,
424 struct nlmsghdr *n,
425 void *arg)
426 {
427 FILE *fp = arg;
428 struct ifinfomsg *ifm = NLMSG_DATA(n);
429 int len = n->nlmsg_len;
430 struct rtattr *tb[IFLA_MAX+1];
431
432 if (n->nlmsg_type != RTM_NEWLINK) {
433 fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
434 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
435 return 0;
436 }
437
438 len -= NLMSG_LENGTH(sizeof(*ifm));
439 if (len < 0) {
440 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
441 return -1;
442 }
443
444 if (ifm->ifi_family != AF_BRIDGE)
445 return 0;
446
447 if (filter_index && filter_index != ifm->ifi_index)
448 return 0;
449
450 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
451
452 /* if AF_SPEC isn't there, vlan table is not preset for this port */
453 if (!tb[IFLA_AF_SPEC]) {
454 if (!filter_vlan && !jw_global)
455 fprintf(fp, "%s\tNone\n",
456 ll_index_to_name(ifm->ifi_index));
457 return 0;
458 }
459
460 print_vlan_info(fp, tb[IFLA_AF_SPEC], ifm->ifi_index);
461
462 fflush(fp);
463 return 0;
464 }
465
466 static void print_one_vlan_stats(FILE *fp,
467 const struct bridge_vlan_xstats *vstats,
468 int ifindex)
469 {
470 const char *ifname = "";
471
472 if (filter_vlan && filter_vlan != vstats->vid)
473 return;
474 /* skip pure port entries, they'll be dumped via the slave stats call */
475 if ((vstats->flags & BRIDGE_VLAN_INFO_MASTER) &&
476 !(vstats->flags & BRIDGE_VLAN_INFO_BRENTRY))
477 return;
478
479 if (last_ifidx != ifindex) {
480 ifname = ll_index_to_name(ifindex);
481 last_ifidx = ifindex;
482 }
483 fprintf(fp, "%-16s %hu", ifname, vstats->vid);
484 if (vstats->flags & BRIDGE_VLAN_INFO_PVID)
485 fprintf(fp, " PVID");
486 if (vstats->flags & BRIDGE_VLAN_INFO_UNTAGGED)
487 fprintf(fp, " Egress Untagged");
488 fprintf(fp, "\n");
489 fprintf(fp, "%-16s RX: %llu bytes %llu packets\n",
490 "", vstats->rx_bytes, vstats->rx_packets);
491 fprintf(fp, "%-16s TX: %llu bytes %llu packets\n",
492 "", vstats->tx_bytes, vstats->tx_packets);
493 }
494
495 static void print_vlan_stats_attr(FILE *fp, struct rtattr *attr, int ifindex)
496 {
497 struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
498 struct rtattr *i, *list;
499 int rem;
500
501 parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr),
502 RTA_PAYLOAD(attr));
503 if (!brtb[LINK_XSTATS_TYPE_BRIDGE])
504 return;
505
506 list = brtb[LINK_XSTATS_TYPE_BRIDGE];
507 rem = RTA_PAYLOAD(list);
508 for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
509 if (i->rta_type != BRIDGE_XSTATS_VLAN)
510 continue;
511 print_one_vlan_stats(fp, RTA_DATA(i), ifindex);
512 }
513 }
514
515 static int print_vlan_stats(const struct sockaddr_nl *who,
516 struct nlmsghdr *n,
517 void *arg)
518 {
519 struct if_stats_msg *ifsm = NLMSG_DATA(n);
520 struct rtattr *tb[IFLA_STATS_MAX+1];
521 int len = n->nlmsg_len;
522 FILE *fp = arg;
523
524 len -= NLMSG_LENGTH(sizeof(*ifsm));
525 if (len < 0) {
526 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
527 return -1;
528 }
529
530 if (filter_index && filter_index != ifsm->ifindex)
531 return 0;
532
533 parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
534
535 /* We have to check if any of the two attrs are usable */
536 if (tb[IFLA_STATS_LINK_XSTATS])
537 print_vlan_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS],
538 ifsm->ifindex);
539
540 if (tb[IFLA_STATS_LINK_XSTATS_SLAVE])
541 print_vlan_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS_SLAVE],
542 ifsm->ifindex);
543
544 fflush(fp);
545 return 0;
546 }
547
548 static int vlan_show(int argc, char **argv)
549 {
550 char *filter_dev = NULL;
551 int ret = 0;
552
553 while (argc > 0) {
554 if (strcmp(*argv, "dev") == 0) {
555 NEXT_ARG();
556 if (filter_dev)
557 duparg("dev", *argv);
558 filter_dev = *argv;
559 } else if (strcmp(*argv, "vid") == 0) {
560 NEXT_ARG();
561 if (filter_vlan)
562 duparg("vid", *argv);
563 filter_vlan = atoi(*argv);
564 }
565 argc--; argv++;
566 }
567
568 if (filter_dev) {
569 filter_index = ll_name_to_index(filter_dev);
570 if (filter_index == 0) {
571 fprintf(stderr, "Cannot find device \"%s\"\n",
572 filter_dev);
573 return -1;
574 }
575 }
576
577 if (!show_stats) {
578 if (rtnl_wilddump_req_filter(&rth, PF_BRIDGE, RTM_GETLINK,
579 (compress_vlans ?
580 RTEXT_FILTER_BRVLAN_COMPRESSED :
581 RTEXT_FILTER_BRVLAN)) < 0) {
582 perror("Cannont send dump request");
583 exit(1);
584 }
585
586 if (json) {
587 jw_global = jsonw_new(stdout);
588 if (!jw_global) {
589 fprintf(stderr, "Error allocation json object\n");
590 exit(1);
591 }
592 if (pretty)
593 jsonw_pretty(jw_global, 1);
594
595 jsonw_start_object(jw_global);
596 } else {
597 if (show_vlan_tunnel_info)
598 printf("port\tvlan ids\ttunnel id\n");
599 else
600 printf("port\tvlan ids\n");
601 }
602
603 if (show_vlan_tunnel_info)
604 ret = rtnl_dump_filter(&rth, print_vlan_tunnel,
605 stdout);
606 else
607 ret = rtnl_dump_filter(&rth, print_vlan, stdout);
608
609 if (ret < 0) {
610 fprintf(stderr, "Dump ternminated\n");
611 exit(1);
612 }
613 } else {
614 __u32 filt_mask;
615
616 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
617 if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC,
618 RTM_GETSTATS,
619 filt_mask) < 0) {
620 perror("Cannont send dump request");
621 exit(1);
622 }
623
624 printf("%-16s vlan id\n", "port");
625 if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
626 fprintf(stderr, "Dump terminated\n");
627 exit(1);
628 }
629
630 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE);
631 if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC,
632 RTM_GETSTATS,
633 filt_mask) < 0) {
634 perror("Cannont send slave dump request");
635 exit(1);
636 }
637
638 if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
639 fprintf(stderr, "Dump terminated\n");
640 exit(1);
641 }
642 }
643
644 if (jw_global) {
645 jsonw_end_object(jw_global);
646 jsonw_destroy(&jw_global);
647 }
648
649 return 0;
650 }
651
652 void print_vlan_info(FILE *fp, struct rtattr *tb, int ifindex)
653 {
654 struct rtattr *i, *list = tb;
655 int rem = RTA_PAYLOAD(list);
656 __u16 last_vid_start = 0;
657 bool vlan_flags = false;
658 bool jsonw_end_parray = false;
659
660 if (!filter_vlan) {
661 print_vlan_port(fp, ifindex);
662 jsonw_end_parray = true;
663 }
664
665 for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
666 struct bridge_vlan_info *vinfo;
667 int vcheck_ret;
668
669 if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
670 continue;
671
672 vinfo = RTA_DATA(i);
673
674 if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
675 last_vid_start = vinfo->vid;
676 vcheck_ret = filter_vlan_check(vinfo->vid, vinfo->flags);
677 if (vcheck_ret == -1)
678 break;
679 else if (vcheck_ret == 0)
680 continue;
681
682 if (filter_vlan) {
683 print_vlan_port(fp, ifindex);
684 jsonw_end_parray = true;
685 }
686 if (jw_global) {
687 jsonw_start_object(jw_global);
688 jsonw_uint_field(jw_global, "vlan",
689 last_vid_start);
690 if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
691 continue;
692 } else {
693 fprintf(fp, "\t %hu", last_vid_start);
694 }
695 if (last_vid_start != vinfo->vid) {
696 if (jw_global)
697 jsonw_uint_field(jw_global, "vlanEnd",
698 vinfo->vid);
699 else
700 fprintf(fp, "-%hu", vinfo->vid);
701 }
702 if (vinfo->flags & BRIDGE_VLAN_INFO_PVID) {
703 if (jw_global) {
704 start_json_vlan_flags_array(&vlan_flags);
705 jsonw_string(jw_global, "PVID");
706 } else {
707 fprintf(fp, " PVID");
708 }
709 }
710 if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED) {
711 if (jw_global) {
712 start_json_vlan_flags_array(&vlan_flags);
713 jsonw_string(jw_global,
714 "Egress Untagged");
715 } else {
716 fprintf(fp, " Egress Untagged");
717 }
718 }
719 if (jw_global && vlan_flags) {
720 jsonw_end_array(jw_global);
721 vlan_flags = false;
722 }
723
724 if (jw_global)
725 jsonw_end_object(jw_global);
726 else
727 fprintf(fp, "\n");
728 }
729
730 if (jsonw_end_parray) {
731 if (jw_global)
732 jsonw_end_array(jw_global);
733 else
734 fprintf(fp, "\n");
735
736 }
737 }
738
739 int do_vlan(int argc, char **argv)
740 {
741 ll_init_map(&rth);
742
743 if (argc > 0) {
744 if (matches(*argv, "add") == 0)
745 return vlan_modify(RTM_SETLINK, argc-1, argv+1);
746 if (matches(*argv, "delete") == 0)
747 return vlan_modify(RTM_DELLINK, argc-1, argv+1);
748 if (matches(*argv, "show") == 0 ||
749 matches(*argv, "lst") == 0 ||
750 matches(*argv, "list") == 0)
751 return vlan_show(argc-1, argv+1);
752 if (matches(*argv, "tunnelshow") == 0) {
753 show_vlan_tunnel_info = 1;
754 return vlan_show(argc-1, argv+1);
755 }
756 if (matches(*argv, "help") == 0)
757 usage();
758 } else {
759 return vlan_show(0, NULL);
760 }
761
762 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv);
763 exit(-1);
764 }