1 /* SPDX-License-Identifier: GPL-2.0 */
6 #include <sys/socket.h>
8 #include <netinet/in.h>
9 #include <linux/if_bridge.h>
10 #include <linux/if_ether.h>
13 #include "json_print.h"
14 #include "libnetlink.h"
15 #include "br_common.h"
18 static unsigned int filter_index
, filter_vlan
;
19 static int show_vlan_tunnel_info
= 0;
21 static void usage(void)
24 "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ tunnel_info id TUNNEL_ID ]\n"
25 " [ pvid ] [ untagged ]\n"
26 " [ self ] [ master ]\n"
27 " bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n"
28 " bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n");
32 static int parse_tunnel_info(int *argcp
, char ***argvp
, __u32
*tun_id_start
,
40 if (!matches(*argv
, "id")) {
42 t
= strchr(*argv
, '-');
45 if (get_u32(tun_id_start
, *argv
, 0) ||
46 *tun_id_start
>= 1u << 24)
47 invarg("invalid tun id", *argv
);
48 if (get_u32(tun_id_end
, t
+ 1, 0) ||
49 *tun_id_end
>= 1u << 24)
50 invarg("invalid tun id", *argv
);
53 if (get_u32(tun_id_start
, *argv
, 0) ||
54 *tun_id_start
>= 1u << 24)
55 invarg("invalid tun id", *argv
);
58 invarg("tunnel id expected", *argv
);
67 static int add_tunnel_info(struct nlmsghdr
*n
, int reqsize
,
68 __u16 vid
, __u32 tun_id
, __u16 flags
)
72 tinfo
= addattr_nest(n
, reqsize
, IFLA_BRIDGE_VLAN_TUNNEL_INFO
);
73 addattr32(n
, reqsize
, IFLA_BRIDGE_VLAN_TUNNEL_ID
, tun_id
);
74 addattr32(n
, reqsize
, IFLA_BRIDGE_VLAN_TUNNEL_VID
, vid
);
75 addattr32(n
, reqsize
, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS
, flags
);
77 addattr_nest_end(n
, tinfo
);
82 static int add_tunnel_info_range(struct nlmsghdr
*n
, int reqsize
,
83 __u16 vid_start
, int16_t vid_end
,
84 __u32 tun_id_start
, __u32 tun_id_end
)
86 if (vid_end
!= -1 && (vid_end
- vid_start
) > 0) {
87 add_tunnel_info(n
, reqsize
, vid_start
, tun_id_start
,
88 BRIDGE_VLAN_INFO_RANGE_BEGIN
);
90 add_tunnel_info(n
, reqsize
, vid_end
, tun_id_end
,
91 BRIDGE_VLAN_INFO_RANGE_END
);
93 add_tunnel_info(n
, reqsize
, vid_start
, tun_id_start
, 0);
99 static int add_vlan_info_range(struct nlmsghdr
*n
, int reqsize
, __u16 vid_start
,
100 int16_t vid_end
, __u16 flags
)
102 struct bridge_vlan_info vinfo
= {};
105 vinfo
.vid
= vid_start
;
107 /* send vlan range start */
108 addattr_l(n
, reqsize
, IFLA_BRIDGE_VLAN_INFO
, &vinfo
,
110 vinfo
.flags
&= ~BRIDGE_VLAN_INFO_RANGE_BEGIN
;
112 /* Now send the vlan range end */
113 vinfo
.flags
|= BRIDGE_VLAN_INFO_RANGE_END
;
115 addattr_l(n
, reqsize
, IFLA_BRIDGE_VLAN_INFO
, &vinfo
,
118 addattr_l(n
, reqsize
, IFLA_BRIDGE_VLAN_INFO
, &vinfo
,
125 static int vlan_modify(int cmd
, int argc
, char **argv
)
129 struct ifinfomsg ifm
;
132 .n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
)),
133 .n
.nlmsg_flags
= NLM_F_REQUEST
,
135 .ifm
.ifi_family
= PF_BRIDGE
,
140 struct rtattr
*afspec
;
141 struct bridge_vlan_info vinfo
= {};
142 bool tunnel_info_set
= false;
143 unsigned short flags
= 0;
144 __u32 tun_id_start
= 0;
145 __u32 tun_id_end
= 0;
148 if (strcmp(*argv
, "dev") == 0) {
151 } else if (strcmp(*argv
, "vid") == 0) {
155 p
= strchr(*argv
, '-');
161 vinfo
.flags
|= BRIDGE_VLAN_INFO_RANGE_BEGIN
;
165 } else if (strcmp(*argv
, "self") == 0) {
166 flags
|= BRIDGE_FLAGS_SELF
;
167 } else if (strcmp(*argv
, "master") == 0) {
168 flags
|= BRIDGE_FLAGS_MASTER
;
169 } else if (strcmp(*argv
, "pvid") == 0) {
170 vinfo
.flags
|= BRIDGE_VLAN_INFO_PVID
;
171 } else if (strcmp(*argv
, "untagged") == 0) {
172 vinfo
.flags
|= BRIDGE_VLAN_INFO_UNTAGGED
;
173 } else if (strcmp(*argv
, "tunnel_info") == 0) {
174 if (parse_tunnel_info(&argc
, &argv
,
178 tunnel_info_set
= true;
180 if (matches(*argv
, "help") == 0)
186 if (d
== NULL
|| vid
== -1) {
187 fprintf(stderr
, "Device and VLAN ID are required arguments.\n");
191 req
.ifm
.ifi_index
= ll_name_to_index(d
);
192 if (req
.ifm
.ifi_index
== 0) {
193 fprintf(stderr
, "Cannot find bridge device \"%s\"\n", d
);
198 fprintf(stderr
, "Invalid VLAN ID \"%hu\"\n", vid
);
202 if (vinfo
.flags
& BRIDGE_VLAN_INFO_RANGE_BEGIN
) {
203 if (vid_end
== -1 || vid_end
>= 4096 || vid
>= vid_end
) {
204 fprintf(stderr
, "Invalid VLAN range \"%hu-%hu\"\n",
208 if (vinfo
.flags
& BRIDGE_VLAN_INFO_PVID
) {
210 "pvid cannot be configured for a vlan range\n");
215 afspec
= addattr_nest(&req
.n
, sizeof(req
), IFLA_AF_SPEC
);
218 addattr16(&req
.n
, sizeof(req
), IFLA_BRIDGE_FLAGS
, flags
);
221 add_tunnel_info_range(&req
.n
, sizeof(req
), vid
, vid_end
,
222 tun_id_start
, tun_id_end
);
224 add_vlan_info_range(&req
.n
, sizeof(req
), vid
, vid_end
,
227 addattr_nest_end(&req
.n
, afspec
);
229 if (rtnl_talk(&rth
, &req
.n
, NULL
) < 0)
235 /* In order to use this function for both filtering and non-filtering cases
236 * we need to make it a tristate:
237 * return -1 - if filtering we've gone over so don't continue
238 * return 0 - skip entry and continue (applies to range start or to entries
239 * which are less than filter_vlan)
240 * return 1 - print the entry and continue
242 static int filter_vlan_check(__u16 vid
, __u16 flags
)
244 /* if we're filtering we should stop on the first greater entry */
245 if (filter_vlan
&& vid
> filter_vlan
&&
246 !(flags
& BRIDGE_VLAN_INFO_RANGE_END
))
248 if ((flags
& BRIDGE_VLAN_INFO_RANGE_BEGIN
) ||
255 static void open_vlan_port(int ifi_index
, const char *fmt
)
257 open_json_object(NULL
);
258 print_color_string(PRINT_ANY
, COLOR_IFNAME
, "ifname", fmt
,
259 ll_index_to_name(ifi_index
));
260 open_json_array(PRINT_JSON
, "vlans");
263 static void close_vlan_port(void)
265 close_json_array(PRINT_JSON
, NULL
);
269 static void print_range(const char *name
, __u16 start
, __u16 id
)
273 snprintf(end
, sizeof(end
), "%sEnd", name
);
275 print_hu(PRINT_ANY
, name
, "\t %hu", start
);
277 print_hu(PRINT_ANY
, end
, "-%hu", id
);
281 static void print_vlan_tunnel_info(FILE *fp
, struct rtattr
*tb
, int ifindex
)
283 struct rtattr
*i
, *list
= tb
;
284 int rem
= RTA_PAYLOAD(list
);
285 __u16 last_vid_start
= 0;
286 __u32 last_tunid_start
= 0;
289 open_vlan_port(ifindex
, "%s");
291 open_json_array(PRINT_JSON
, "tunnel");
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];
295 __u16 tunnel_vid
= 0;
296 __u16 tunnel_flags
= 0;
299 if (i
->rta_type
!= IFLA_BRIDGE_VLAN_TUNNEL_INFO
)
302 parse_rtattr(ttb
, IFLA_BRIDGE_VLAN_TUNNEL_MAX
,
303 RTA_DATA(i
), RTA_PAYLOAD(i
));
305 if (ttb
[IFLA_BRIDGE_VLAN_TUNNEL_VID
])
307 rta_getattr_u32(ttb
[IFLA_BRIDGE_VLAN_TUNNEL_VID
]);
311 if (ttb
[IFLA_BRIDGE_VLAN_TUNNEL_ID
])
313 rta_getattr_u32(ttb
[IFLA_BRIDGE_VLAN_TUNNEL_ID
]);
315 if (ttb
[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS
])
317 rta_getattr_u32(ttb
[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS
]);
319 if (!(tunnel_flags
& BRIDGE_VLAN_INFO_RANGE_END
)) {
320 last_vid_start
= tunnel_vid
;
321 last_tunid_start
= tunnel_id
;
324 vcheck_ret
= filter_vlan_check(tunnel_vid
, tunnel_flags
);
325 if (vcheck_ret
== -1)
327 else if (vcheck_ret
== 0)
330 if (tunnel_flags
& BRIDGE_VLAN_INFO_RANGE_BEGIN
)
334 open_vlan_port(ifindex
, "%s");
336 open_json_object(NULL
);
337 print_range("vlan", last_vid_start
, tunnel_vid
);
338 print_range("tunid", last_tunid_start
, tunnel_id
);
341 print_string(PRINT_FP
, NULL
, "%s", _SL_
);
350 static int print_vlan_tunnel(struct nlmsghdr
*n
, void *arg
)
352 struct ifinfomsg
*ifm
= NLMSG_DATA(n
);
353 struct rtattr
*tb
[IFLA_MAX
+1];
354 int len
= n
->nlmsg_len
;
357 if (n
->nlmsg_type
!= RTM_NEWLINK
) {
358 fprintf(stderr
, "Not RTM_NEWLINK: %08x %08x %08x\n",
359 n
->nlmsg_len
, n
->nlmsg_type
, n
->nlmsg_flags
);
363 len
-= NLMSG_LENGTH(sizeof(*ifm
));
365 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
369 if (ifm
->ifi_family
!= AF_BRIDGE
)
372 if (filter_index
&& filter_index
!= ifm
->ifi_index
)
375 parse_rtattr(tb
, IFLA_MAX
, IFLA_RTA(ifm
), len
);
377 /* if AF_SPEC isn't there, vlan table is not preset for this port */
378 if (!tb
[IFLA_AF_SPEC
]) {
379 if (!filter_vlan
&& !is_json_context()) {
380 color_fprintf(fp
, COLOR_IFNAME
, "%s",
381 ll_index_to_name(ifm
->ifi_index
));
382 fprintf(fp
, "\tNone\n");
387 print_vlan_tunnel_info(fp
, tb
[IFLA_AF_SPEC
], ifm
->ifi_index
);
393 static int print_vlan(struct nlmsghdr
*n
, void *arg
)
396 struct ifinfomsg
*ifm
= NLMSG_DATA(n
);
397 int len
= n
->nlmsg_len
;
398 struct rtattr
*tb
[IFLA_MAX
+1];
400 if (n
->nlmsg_type
!= RTM_NEWLINK
) {
401 fprintf(stderr
, "Not RTM_NEWLINK: %08x %08x %08x\n",
402 n
->nlmsg_len
, n
->nlmsg_type
, n
->nlmsg_flags
);
406 len
-= NLMSG_LENGTH(sizeof(*ifm
));
408 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
412 if (ifm
->ifi_family
!= AF_BRIDGE
)
415 if (filter_index
&& filter_index
!= ifm
->ifi_index
)
418 parse_rtattr(tb
, IFLA_MAX
, IFLA_RTA(ifm
), len
);
420 /* if AF_SPEC isn't there, vlan table is not preset for this port */
421 if (!tb
[IFLA_AF_SPEC
]) {
422 if (!filter_vlan
&& !is_json_context()) {
423 color_fprintf(fp
, COLOR_IFNAME
, "%s",
424 ll_index_to_name(ifm
->ifi_index
));
425 fprintf(fp
, "\tNone\n");
430 print_vlan_info(tb
[IFLA_AF_SPEC
], ifm
->ifi_index
);
431 print_string(PRINT_FP
, NULL
, "%s", _SL_
);
437 static void print_vlan_flags(__u16 flags
)
442 open_json_array(PRINT_JSON
, "flags");
443 if (flags
& BRIDGE_VLAN_INFO_PVID
)
444 print_string(PRINT_ANY
, NULL
, " %s", "PVID");
446 if (flags
& BRIDGE_VLAN_INFO_UNTAGGED
)
447 print_string(PRINT_ANY
, NULL
, " %s", "Egress Untagged");
448 close_json_array(PRINT_JSON
, NULL
);
451 static void print_one_vlan_stats(const struct bridge_vlan_xstats
*vstats
)
453 open_json_object(NULL
);
454 print_hu(PRINT_ANY
, "vid", " %hu", vstats
->vid
);
456 print_vlan_flags(vstats
->flags
);
458 print_lluint(PRINT_ANY
, "rx_bytes",
461 print_lluint(PRINT_ANY
, "rx_packets", " %llu packets\n",
463 print_lluint(PRINT_ANY
, "tx_bytes",
466 print_lluint(PRINT_ANY
, "tx_packets", " %llu packets\n",
471 static void print_vlan_stats_attr(struct rtattr
*attr
, int ifindex
)
473 struct rtattr
*brtb
[LINK_XSTATS_TYPE_MAX
+1];
474 struct rtattr
*i
, *list
;
475 bool found_vlan
= false;
478 parse_rtattr(brtb
, LINK_XSTATS_TYPE_MAX
, RTA_DATA(attr
),
480 if (!brtb
[LINK_XSTATS_TYPE_BRIDGE
])
483 list
= brtb
[LINK_XSTATS_TYPE_BRIDGE
];
484 rem
= RTA_PAYLOAD(list
);
486 for (i
= RTA_DATA(list
); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
)) {
487 const struct bridge_vlan_xstats
*vstats
= RTA_DATA(i
);
489 if (i
->rta_type
!= BRIDGE_XSTATS_VLAN
)
492 if (filter_vlan
&& filter_vlan
!= vstats
->vid
)
495 /* skip pure port entries, they'll be dumped via the slave stats call */
496 if ((vstats
->flags
& BRIDGE_VLAN_INFO_MASTER
) &&
497 !(vstats
->flags
& BRIDGE_VLAN_INFO_BRENTRY
))
500 /* found vlan stats, first time print the interface name */
502 open_vlan_port(ifindex
, "%-16s");
505 print_string(PRINT_FP
, NULL
, "%-16s", "");
507 print_one_vlan_stats(vstats
);
510 /* vlan_port is opened only if there are any vlan stats */
515 static int print_vlan_stats(struct nlmsghdr
*n
, void *arg
)
517 struct if_stats_msg
*ifsm
= NLMSG_DATA(n
);
518 struct rtattr
*tb
[IFLA_STATS_MAX
+1];
519 int len
= n
->nlmsg_len
;
522 len
-= NLMSG_LENGTH(sizeof(*ifsm
));
524 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
528 if (filter_index
&& filter_index
!= ifsm
->ifindex
)
531 parse_rtattr(tb
, IFLA_STATS_MAX
, IFLA_STATS_RTA(ifsm
), len
);
533 /* We have to check if any of the two attrs are usable */
534 if (tb
[IFLA_STATS_LINK_XSTATS
])
535 print_vlan_stats_attr(tb
[IFLA_STATS_LINK_XSTATS
],
538 if (tb
[IFLA_STATS_LINK_XSTATS_SLAVE
])
539 print_vlan_stats_attr(tb
[IFLA_STATS_LINK_XSTATS_SLAVE
],
546 static int vlan_show(int argc
, char **argv
)
548 char *filter_dev
= NULL
;
552 if (strcmp(*argv
, "dev") == 0) {
555 duparg("dev", *argv
);
557 } else if (strcmp(*argv
, "vid") == 0) {
560 duparg("vid", *argv
);
561 filter_vlan
= atoi(*argv
);
567 filter_index
= ll_name_to_index(filter_dev
);
569 return nodev(filter_dev
);
575 if (rtnl_linkdump_req_filter(&rth
, PF_BRIDGE
,
577 RTEXT_FILTER_BRVLAN_COMPRESSED
:
578 RTEXT_FILTER_BRVLAN
)) < 0) {
579 perror("Cannont send dump request");
583 if (!is_json_context()) {
584 if (show_vlan_tunnel_info
)
585 printf("port\tvlan ids\ttunnel id\n");
587 printf("port\tvlan ids\n");
590 if (show_vlan_tunnel_info
)
591 ret
= rtnl_dump_filter(&rth
, print_vlan_tunnel
,
594 ret
= rtnl_dump_filter(&rth
, print_vlan
, stdout
);
596 fprintf(stderr
, "Dump ternminated\n");
602 filt_mask
= IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS
);
603 if (rtnl_statsdump_req_filter(&rth
, AF_UNSPEC
, filt_mask
) < 0) {
604 perror("Cannont send dump request");
608 if (!is_json_context())
609 printf("%-16s vlan id\n", "port");
611 if (rtnl_dump_filter(&rth
, print_vlan_stats
, stdout
) < 0) {
612 fprintf(stderr
, "Dump terminated\n");
616 filt_mask
= IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE
);
617 if (rtnl_statsdump_req_filter(&rth
, AF_UNSPEC
, filt_mask
) < 0) {
618 perror("Cannont send slave dump request");
622 if (rtnl_dump_filter(&rth
, print_vlan_stats
, stdout
) < 0) {
623 fprintf(stderr
, "Dump terminated\n");
633 void print_vlan_info(struct rtattr
*tb
, int ifindex
)
635 struct rtattr
*i
, *list
= tb
;
636 int rem
= RTA_PAYLOAD(list
);
637 __u16 last_vid_start
= 0;
639 open_vlan_port(ifindex
, "%s");
641 for (i
= RTA_DATA(list
); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
)) {
642 struct bridge_vlan_info
*vinfo
;
645 if (i
->rta_type
!= IFLA_BRIDGE_VLAN_INFO
)
650 if (!(vinfo
->flags
& BRIDGE_VLAN_INFO_RANGE_END
))
651 last_vid_start
= vinfo
->vid
;
652 vcheck_ret
= filter_vlan_check(vinfo
->vid
, vinfo
->flags
);
653 if (vcheck_ret
== -1)
655 else if (vcheck_ret
== 0)
658 open_json_object(NULL
);
659 print_range("vlan", last_vid_start
, vinfo
->vid
);
661 print_vlan_flags(vinfo
->flags
);
663 print_string(PRINT_FP
, NULL
, "%s", _SL_
);
668 int do_vlan(int argc
, char **argv
)
673 if (matches(*argv
, "add") == 0)
674 return vlan_modify(RTM_SETLINK
, argc
-1, argv
+1);
675 if (matches(*argv
, "delete") == 0)
676 return vlan_modify(RTM_DELLINK
, argc
-1, argv
+1);
677 if (matches(*argv
, "show") == 0 ||
678 matches(*argv
, "lst") == 0 ||
679 matches(*argv
, "list") == 0)
680 return vlan_show(argc
-1, argv
+1);
681 if (matches(*argv
, "tunnelshow") == 0) {
682 show_vlan_tunnel_info
= 1;
683 return vlan_show(argc
-1, argv
+1);
685 if (matches(*argv
, "help") == 0)
688 return vlan_show(0, NULL
);
691 fprintf(stderr
, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv
);