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>
11 #include <json_writer.h>
14 #include "libnetlink.h"
15 #include "br_common.h"
18 static unsigned int filter_index
, filter_vlan
;
19 static int last_ifidx
= -1;
20 static int show_vlan_tunnel_info
= 0;
22 json_writer_t
*jw_global
;
24 static void usage(void)
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");
35 static int parse_tunnel_info(int *argcp
, char ***argvp
, __u32
*tun_id_start
,
43 if (!matches(*argv
, "id")) {
45 t
= strchr(*argv
, '-');
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
);
56 if (get_u32(tun_id_start
, *argv
, 0) ||
57 *tun_id_start
>= 1u << 24)
58 invarg("invalid tun id", *argv
);
61 invarg("tunnel id expected", *argv
);
70 static int add_tunnel_info(struct nlmsghdr
*n
, int reqsize
,
71 __u16 vid
, __u32 tun_id
, __u16 flags
)
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
);
80 addattr_nest_end(n
, tinfo
);
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
)
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
);
93 add_tunnel_info(n
, reqsize
, vid_end
, tun_id_end
,
94 BRIDGE_VLAN_INFO_RANGE_END
);
96 add_tunnel_info(n
, reqsize
, vid_start
, tun_id_start
, 0);
102 static int add_vlan_info_range(struct nlmsghdr
*n
, int reqsize
, __u16 vid_start
,
103 int16_t vid_end
, __u16 flags
)
105 struct bridge_vlan_info vinfo
= {};
108 vinfo
.vid
= vid_start
;
110 /* send vlan range start */
111 addattr_l(n
, reqsize
, IFLA_BRIDGE_VLAN_INFO
, &vinfo
,
113 vinfo
.flags
&= ~BRIDGE_VLAN_INFO_RANGE_BEGIN
;
115 /* Now send the vlan range end */
116 vinfo
.flags
|= BRIDGE_VLAN_INFO_RANGE_END
;
118 addattr_l(n
, reqsize
, IFLA_BRIDGE_VLAN_INFO
, &vinfo
,
121 addattr_l(n
, reqsize
, IFLA_BRIDGE_VLAN_INFO
, &vinfo
,
128 static int vlan_modify(int cmd
, int argc
, char **argv
)
132 struct ifinfomsg ifm
;
135 .n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
)),
136 .n
.nlmsg_flags
= NLM_F_REQUEST
,
138 .ifm
.ifi_family
= PF_BRIDGE
,
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;
151 if (strcmp(*argv
, "dev") == 0) {
154 } else if (strcmp(*argv
, "vid") == 0) {
158 p
= strchr(*argv
, '-');
164 vinfo
.flags
|= BRIDGE_VLAN_INFO_RANGE_BEGIN
;
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
,
181 tunnel_info_set
= true;
183 if (matches(*argv
, "help") == 0)
189 if (d
== NULL
|| vid
== -1) {
190 fprintf(stderr
, "Device and VLAN ID are required arguments.\n");
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
);
201 fprintf(stderr
, "Invalid VLAN ID \"%hu\"\n", vid
);
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",
211 if (vinfo
.flags
& BRIDGE_VLAN_INFO_PVID
) {
213 "pvid cannot be configured for a vlan range\n");
218 afspec
= addattr_nest(&req
.n
, sizeof(req
), IFLA_AF_SPEC
);
221 addattr16(&req
.n
, sizeof(req
), IFLA_BRIDGE_FLAGS
, flags
);
224 add_tunnel_info_range(&req
.n
, sizeof(req
), vid
, vid_end
,
225 tun_id_start
, tun_id_end
);
227 add_vlan_info_range(&req
.n
, sizeof(req
), vid
, vid_end
,
230 addattr_nest_end(&req
.n
, afspec
);
232 if (rtnl_talk(&rth
, &req
.n
, NULL
) < 0)
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
245 static int filter_vlan_check(__u16 vid
, __u16 flags
)
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
))
251 if ((flags
& BRIDGE_VLAN_INFO_RANGE_BEGIN
) ||
258 static void print_vlan_port(FILE *fp
, int ifi_index
)
261 jsonw_name(jw_global
,
262 ll_index_to_name(ifi_index
));
263 jsonw_start_array(jw_global
);
266 ll_index_to_name(ifi_index
));
270 static void start_json_vlan_flags_array(bool *vlan_flags
)
274 jsonw_name(jw_global
, "flags");
275 jsonw_start_array(jw_global
);
279 static void print_vlan_tunnel_info(FILE *fp
, struct rtattr
*tb
, int ifindex
)
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;
288 print_vlan_port(fp
, ifindex
);
289 jsonw_end_parray
= 1;
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
;
323 vcheck_ret
= filter_vlan_check(tunnel_vid
, tunnel_flags
);
324 if (vcheck_ret
== -1)
326 else if (vcheck_ret
== 0)
329 if (tunnel_flags
& BRIDGE_VLAN_INFO_RANGE_BEGIN
)
333 print_vlan_port(fp
, ifindex
);
334 jsonw_end_parray
= 1;
338 jsonw_start_object(jw_global
);
339 jsonw_uint_field(jw_global
, "vlan",
342 fprintf(fp
, "\t %hu", last_vid_start
);
344 if (last_vid_start
!= tunnel_vid
) {
346 jsonw_uint_field(jw_global
, "vlanEnd",
349 fprintf(fp
, "-%hu", tunnel_vid
);
353 jsonw_uint_field(jw_global
, "tunid",
356 fprintf(fp
, "\t %hu", last_tunid_start
);
358 if (last_vid_start
!= tunnel_vid
) {
360 jsonw_uint_field(jw_global
, "tunidEnd",
363 fprintf(fp
, "-%hu", tunnel_id
);
367 jsonw_end_object(jw_global
);
372 if (jsonw_end_parray
) {
374 jsonw_end_array(jw_global
);
380 static int print_vlan_tunnel(const struct sockaddr_nl
*who
,
384 struct ifinfomsg
*ifm
= NLMSG_DATA(n
);
385 struct rtattr
*tb
[IFLA_MAX
+1];
386 int len
= n
->nlmsg_len
;
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
);
395 len
-= NLMSG_LENGTH(sizeof(*ifm
));
397 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
401 if (ifm
->ifi_family
!= AF_BRIDGE
)
404 if (filter_index
&& filter_index
!= ifm
->ifi_index
)
407 parse_rtattr(tb
, IFLA_MAX
, IFLA_RTA(ifm
), len
);
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
));
417 print_vlan_tunnel_info(fp
, tb
[IFLA_AF_SPEC
], ifm
->ifi_index
);
423 static int print_vlan(const struct sockaddr_nl
*who
,
428 struct ifinfomsg
*ifm
= NLMSG_DATA(n
);
429 int len
= n
->nlmsg_len
;
430 struct rtattr
*tb
[IFLA_MAX
+1];
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
);
438 len
-= NLMSG_LENGTH(sizeof(*ifm
));
440 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
444 if (ifm
->ifi_family
!= AF_BRIDGE
)
447 if (filter_index
&& filter_index
!= ifm
->ifi_index
)
450 parse_rtattr(tb
, IFLA_MAX
, IFLA_RTA(ifm
), len
);
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
));
460 print_vlan_info(fp
, tb
[IFLA_AF_SPEC
], ifm
->ifi_index
);
466 static void print_one_vlan_stats(FILE *fp
,
467 const struct bridge_vlan_xstats
*vstats
,
470 const char *ifname
= "";
472 if (filter_vlan
&& filter_vlan
!= vstats
->vid
)
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
))
479 if (last_ifidx
!= ifindex
) {
480 ifname
= ll_index_to_name(ifindex
);
481 last_ifidx
= ifindex
;
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");
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
);
495 static void print_vlan_stats_attr(FILE *fp
, struct rtattr
*attr
, int ifindex
)
497 struct rtattr
*brtb
[LINK_XSTATS_TYPE_MAX
+1];
498 struct rtattr
*i
, *list
;
501 parse_rtattr(brtb
, LINK_XSTATS_TYPE_MAX
, RTA_DATA(attr
),
503 if (!brtb
[LINK_XSTATS_TYPE_BRIDGE
])
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
)
511 print_one_vlan_stats(fp
, RTA_DATA(i
), ifindex
);
515 static int print_vlan_stats(const struct sockaddr_nl
*who
,
519 struct if_stats_msg
*ifsm
= NLMSG_DATA(n
);
520 struct rtattr
*tb
[IFLA_STATS_MAX
+1];
521 int len
= n
->nlmsg_len
;
524 len
-= NLMSG_LENGTH(sizeof(*ifsm
));
526 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
530 if (filter_index
&& filter_index
!= ifsm
->ifindex
)
533 parse_rtattr(tb
, IFLA_STATS_MAX
, IFLA_STATS_RTA(ifsm
), len
);
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
],
540 if (tb
[IFLA_STATS_LINK_XSTATS_SLAVE
])
541 print_vlan_stats_attr(fp
, tb
[IFLA_STATS_LINK_XSTATS_SLAVE
],
548 static int vlan_show(int argc
, char **argv
)
550 char *filter_dev
= NULL
;
554 if (strcmp(*argv
, "dev") == 0) {
557 duparg("dev", *argv
);
559 } else if (strcmp(*argv
, "vid") == 0) {
562 duparg("vid", *argv
);
563 filter_vlan
= atoi(*argv
);
569 filter_index
= ll_name_to_index(filter_dev
);
570 if (filter_index
== 0) {
571 fprintf(stderr
, "Cannot find device \"%s\"\n",
578 if (rtnl_wilddump_req_filter(&rth
, PF_BRIDGE
, RTM_GETLINK
,
580 RTEXT_FILTER_BRVLAN_COMPRESSED
:
581 RTEXT_FILTER_BRVLAN
)) < 0) {
582 perror("Cannont send dump request");
587 jw_global
= jsonw_new(stdout
);
589 fprintf(stderr
, "Error allocation json object\n");
593 jsonw_pretty(jw_global
, 1);
595 jsonw_start_object(jw_global
);
597 if (show_vlan_tunnel_info
)
598 printf("port\tvlan ids\ttunnel id\n");
600 printf("port\tvlan ids\n");
603 if (show_vlan_tunnel_info
)
604 ret
= rtnl_dump_filter(&rth
, print_vlan_tunnel
,
607 ret
= rtnl_dump_filter(&rth
, print_vlan
, stdout
);
610 fprintf(stderr
, "Dump ternminated\n");
616 filt_mask
= IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS
);
617 if (rtnl_wilddump_stats_req_filter(&rth
, AF_UNSPEC
,
620 perror("Cannont send dump request");
624 printf("%-16s vlan id\n", "port");
625 if (rtnl_dump_filter(&rth
, print_vlan_stats
, stdout
) < 0) {
626 fprintf(stderr
, "Dump terminated\n");
630 filt_mask
= IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE
);
631 if (rtnl_wilddump_stats_req_filter(&rth
, AF_UNSPEC
,
634 perror("Cannont send slave dump request");
638 if (rtnl_dump_filter(&rth
, print_vlan_stats
, stdout
) < 0) {
639 fprintf(stderr
, "Dump terminated\n");
645 jsonw_end_object(jw_global
);
646 jsonw_destroy(&jw_global
);
652 void print_vlan_info(FILE *fp
, struct rtattr
*tb
, int ifindex
)
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;
661 print_vlan_port(fp
, ifindex
);
662 jsonw_end_parray
= true;
665 for (i
= RTA_DATA(list
); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
)) {
666 struct bridge_vlan_info
*vinfo
;
669 if (i
->rta_type
!= IFLA_BRIDGE_VLAN_INFO
)
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)
679 else if (vcheck_ret
== 0)
683 print_vlan_port(fp
, ifindex
);
684 jsonw_end_parray
= true;
687 jsonw_start_object(jw_global
);
688 jsonw_uint_field(jw_global
, "vlan",
690 if (vinfo
->flags
& BRIDGE_VLAN_INFO_RANGE_BEGIN
)
693 fprintf(fp
, "\t %hu", last_vid_start
);
695 if (last_vid_start
!= vinfo
->vid
) {
697 jsonw_uint_field(jw_global
, "vlanEnd",
700 fprintf(fp
, "-%hu", vinfo
->vid
);
702 if (vinfo
->flags
& BRIDGE_VLAN_INFO_PVID
) {
704 start_json_vlan_flags_array(&vlan_flags
);
705 jsonw_string(jw_global
, "PVID");
707 fprintf(fp
, " PVID");
710 if (vinfo
->flags
& BRIDGE_VLAN_INFO_UNTAGGED
) {
712 start_json_vlan_flags_array(&vlan_flags
);
713 jsonw_string(jw_global
,
716 fprintf(fp
, " Egress Untagged");
719 if (jw_global
&& vlan_flags
) {
720 jsonw_end_array(jw_global
);
725 jsonw_end_object(jw_global
);
730 if (jsonw_end_parray
) {
732 jsonw_end_array(jw_global
);
739 int do_vlan(int argc
, char **argv
)
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);
756 if (matches(*argv
, "help") == 0)
759 return vlan_show(0, NULL
);
762 fprintf(stderr
, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv
);