5 #include <sys/socket.h>
7 #include <netinet/in.h>
8 #include <linux/if_bridge.h>
9 #include <linux/if_ether.h>
10 #include <json_writer.h>
13 #include "libnetlink.h"
14 #include "br_common.h"
17 static unsigned int filter_index
, filter_vlan
;
18 static int last_ifidx
= -1;
20 json_writer_t
*jw_global
= NULL
;
22 static void usage(void)
24 fprintf(stderr
, "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ pvid ] [ untagged ]\n");
25 fprintf(stderr
, " [ self ] [ master ]\n");
26 fprintf(stderr
, " bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n");
27 fprintf(stderr
, " bridge vlan { stats } [ dev DEV ] [ vid VLAN_ID ]\n");
31 static int vlan_modify(int cmd
, int argc
, char **argv
)
38 .n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
)),
39 .n
.nlmsg_flags
= NLM_F_REQUEST
,
41 .ifm
.ifi_family
= PF_BRIDGE
,
46 struct rtattr
*afspec
;
47 struct bridge_vlan_info vinfo
= {};
48 unsigned short flags
= 0;
51 if (strcmp(*argv
, "dev") == 0) {
54 } else if (strcmp(*argv
, "vid") == 0) {
58 p
= strchr(*argv
, '-');
64 vinfo
.flags
|= BRIDGE_VLAN_INFO_RANGE_BEGIN
;
68 } else if (strcmp(*argv
, "self") == 0) {
69 flags
|= BRIDGE_FLAGS_SELF
;
70 } else if (strcmp(*argv
, "master") == 0) {
71 flags
|= BRIDGE_FLAGS_MASTER
;
72 } else if (strcmp(*argv
, "pvid") == 0) {
73 vinfo
.flags
|= BRIDGE_VLAN_INFO_PVID
;
74 } else if (strcmp(*argv
, "untagged") == 0) {
75 vinfo
.flags
|= BRIDGE_VLAN_INFO_UNTAGGED
;
77 if (matches(*argv
, "help") == 0) {
84 if (d
== NULL
|| vid
== -1) {
85 fprintf(stderr
, "Device and VLAN ID are required arguments.\n");
89 req
.ifm
.ifi_index
= ll_name_to_index(d
);
90 if (req
.ifm
.ifi_index
== 0) {
91 fprintf(stderr
, "Cannot find bridge device \"%s\"\n", d
);
96 fprintf(stderr
, "Invalid VLAN ID \"%hu\"\n", vid
);
100 if (vinfo
.flags
& BRIDGE_VLAN_INFO_RANGE_BEGIN
) {
101 if (vid_end
== -1 || vid_end
>= 4096 || vid
>= vid_end
) {
102 fprintf(stderr
, "Invalid VLAN range \"%hu-%hu\"\n",
106 if (vinfo
.flags
& BRIDGE_VLAN_INFO_PVID
) {
108 "pvid cannot be configured for a vlan range\n");
113 afspec
= addattr_nest(&req
.n
, sizeof(req
), IFLA_AF_SPEC
);
116 addattr16(&req
.n
, sizeof(req
), IFLA_BRIDGE_FLAGS
, flags
);
120 /* send vlan range start */
121 addattr_l(&req
.n
, sizeof(req
), IFLA_BRIDGE_VLAN_INFO
, &vinfo
,
123 vinfo
.flags
&= ~BRIDGE_VLAN_INFO_RANGE_BEGIN
;
125 /* Now send the vlan range end */
126 vinfo
.flags
|= BRIDGE_VLAN_INFO_RANGE_END
;
128 addattr_l(&req
.n
, sizeof(req
), IFLA_BRIDGE_VLAN_INFO
, &vinfo
,
131 addattr_l(&req
.n
, sizeof(req
), IFLA_BRIDGE_VLAN_INFO
, &vinfo
,
135 addattr_nest_end(&req
.n
, afspec
);
137 if (rtnl_talk(&rth
, &req
.n
, NULL
, 0) < 0)
143 /* In order to use this function for both filtering and non-filtering cases
144 * we need to make it a tristate:
145 * return -1 - if filtering we've gone over so don't continue
146 * return 0 - skip entry and continue (applies to range start or to entries
147 * which are less than filter_vlan)
148 * return 1 - print the entry and continue
150 static int filter_vlan_check(struct bridge_vlan_info
*vinfo
)
152 /* if we're filtering we should stop on the first greater entry */
153 if (filter_vlan
&& vinfo
->vid
> filter_vlan
&&
154 !(vinfo
->flags
& BRIDGE_VLAN_INFO_RANGE_END
))
156 if ((vinfo
->flags
& BRIDGE_VLAN_INFO_RANGE_BEGIN
) ||
157 vinfo
->vid
< filter_vlan
)
163 static void print_vlan_port(FILE *fp
, int ifi_index
)
166 jsonw_pretty(jw_global
, 1);
167 jsonw_name(jw_global
,
168 ll_index_to_name(ifi_index
));
169 jsonw_start_array(jw_global
);
172 ll_index_to_name(ifi_index
));
176 static void start_json_vlan_flags_array(bool *vlan_flags
)
180 jsonw_name(jw_global
, "flags");
181 jsonw_start_array(jw_global
);
185 static int print_vlan(const struct sockaddr_nl
*who
,
190 struct ifinfomsg
*ifm
= NLMSG_DATA(n
);
191 int len
= n
->nlmsg_len
;
192 struct rtattr
*tb
[IFLA_MAX
+1];
193 bool vlan_flags
= false;
195 if (n
->nlmsg_type
!= RTM_NEWLINK
) {
196 fprintf(stderr
, "Not RTM_NEWLINK: %08x %08x %08x\n",
197 n
->nlmsg_len
, n
->nlmsg_type
, n
->nlmsg_flags
);
201 len
-= NLMSG_LENGTH(sizeof(*ifm
));
203 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
207 if (ifm
->ifi_family
!= AF_BRIDGE
)
210 if (filter_index
&& filter_index
!= ifm
->ifi_index
)
213 parse_rtattr(tb
, IFLA_MAX
, IFLA_RTA(ifm
), len
);
215 /* if AF_SPEC isn't there, vlan table is not preset for this port */
216 if (!tb
[IFLA_AF_SPEC
]) {
217 if (!filter_vlan
&& !jw_global
)
218 fprintf(fp
, "%s\tNone\n",
219 ll_index_to_name(ifm
->ifi_index
));
222 struct rtattr
*i
, *list
= tb
[IFLA_AF_SPEC
];
223 int rem
= RTA_PAYLOAD(list
);
224 __u16 last_vid_start
= 0;
227 print_vlan_port(fp
, ifm
->ifi_index
);
229 for (i
= RTA_DATA(list
); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
)) {
230 struct bridge_vlan_info
*vinfo
;
233 if (i
->rta_type
!= IFLA_BRIDGE_VLAN_INFO
)
238 if (!(vinfo
->flags
& BRIDGE_VLAN_INFO_RANGE_END
))
239 last_vid_start
= vinfo
->vid
;
240 vcheck_ret
= filter_vlan_check(vinfo
);
241 if (vcheck_ret
== -1)
243 else if (vcheck_ret
== 0)
247 print_vlan_port(fp
, ifm
->ifi_index
);
249 jsonw_start_object(jw_global
);
250 jsonw_uint_field(jw_global
, "vlan",
252 if (vinfo
->flags
& BRIDGE_VLAN_INFO_RANGE_BEGIN
)
255 fprintf(fp
, "\t %hu", last_vid_start
);
257 if (last_vid_start
!= vinfo
->vid
) {
259 jsonw_uint_field(jw_global
, "vlanEnd",
262 fprintf(fp
, "-%hu", vinfo
->vid
);
264 if (vinfo
->flags
& BRIDGE_VLAN_INFO_PVID
) {
266 start_json_vlan_flags_array(&vlan_flags
);
267 jsonw_string(jw_global
, "PVID");
269 fprintf(fp
, " PVID");
272 if (vinfo
->flags
& BRIDGE_VLAN_INFO_UNTAGGED
) {
274 start_json_vlan_flags_array(&vlan_flags
);
275 jsonw_string(jw_global
,
278 fprintf(fp
, " Egress Untagged");
281 if (jw_global
&& vlan_flags
) {
282 jsonw_end_array(jw_global
);
287 jsonw_end_object(jw_global
);
294 jsonw_end_array(jw_global
);
303 static void print_one_vlan_stats(FILE *fp
,
304 const struct bridge_vlan_xstats
*vstats
,
307 const char *ifname
= "";
309 if (filter_vlan
&& filter_vlan
!= vstats
->vid
)
311 /* skip pure port entries, they'll be dumped via the slave stats call */
312 if ((vstats
->flags
& BRIDGE_VLAN_INFO_MASTER
) &&
313 !(vstats
->flags
& BRIDGE_VLAN_INFO_BRENTRY
))
316 if (last_ifidx
!= ifindex
) {
317 ifname
= ll_index_to_name(ifindex
);
318 last_ifidx
= ifindex
;
320 fprintf(fp
, "%-16s %hu", ifname
, vstats
->vid
);
321 if (vstats
->flags
& BRIDGE_VLAN_INFO_PVID
)
322 fprintf(fp
, " PVID");
323 if (vstats
->flags
& BRIDGE_VLAN_INFO_UNTAGGED
)
324 fprintf(fp
, " Egress Untagged");
326 fprintf(fp
, "%-16s RX: %llu bytes %llu packets\n",
327 "", vstats
->rx_bytes
, vstats
->rx_packets
);
328 fprintf(fp
, "%-16s TX: %llu bytes %llu packets\n",
329 "", vstats
->tx_bytes
, vstats
->tx_packets
);
332 static void print_vlan_stats_attr(FILE *fp
, struct rtattr
*attr
, int ifindex
)
334 struct rtattr
*brtb
[LINK_XSTATS_TYPE_MAX
+1];
335 struct rtattr
*i
, *list
;
338 parse_rtattr(brtb
, LINK_XSTATS_TYPE_MAX
, RTA_DATA(attr
),
340 if (!brtb
[LINK_XSTATS_TYPE_BRIDGE
])
343 list
= brtb
[LINK_XSTATS_TYPE_BRIDGE
];
344 rem
= RTA_PAYLOAD(list
);
345 for (i
= RTA_DATA(list
); RTA_OK(i
, rem
); i
= RTA_NEXT(i
, rem
)) {
346 if (i
->rta_type
!= BRIDGE_XSTATS_VLAN
)
348 print_one_vlan_stats(fp
, RTA_DATA(i
), ifindex
);
352 static int print_vlan_stats(const struct sockaddr_nl
*who
,
356 struct if_stats_msg
*ifsm
= NLMSG_DATA(n
);
357 struct rtattr
*tb
[IFLA_STATS_MAX
+1];
358 int len
= n
->nlmsg_len
;
361 len
-= NLMSG_LENGTH(sizeof(*ifsm
));
363 fprintf(stderr
, "BUG: wrong nlmsg len %d\n", len
);
367 if (filter_index
&& filter_index
!= ifsm
->ifindex
)
370 parse_rtattr(tb
, IFLA_STATS_MAX
, IFLA_STATS_RTA(ifsm
), len
);
372 /* We have to check if any of the two attrs are usable */
373 if (tb
[IFLA_STATS_LINK_XSTATS
])
374 print_vlan_stats_attr(fp
, tb
[IFLA_STATS_LINK_XSTATS
],
377 if (tb
[IFLA_STATS_LINK_XSTATS_SLAVE
])
378 print_vlan_stats_attr(fp
, tb
[IFLA_STATS_LINK_XSTATS_SLAVE
],
385 static int vlan_show(int argc
, char **argv
)
387 char *filter_dev
= NULL
;
390 if (strcmp(*argv
, "dev") == 0) {
393 duparg("dev", *argv
);
395 } else if (strcmp(*argv
, "vid") == 0) {
398 duparg("vid", *argv
);
399 filter_vlan
= atoi(*argv
);
405 if ((filter_index
= if_nametoindex(filter_dev
)) == 0) {
406 fprintf(stderr
, "Cannot find device \"%s\"\n",
413 if (rtnl_wilddump_req_filter(&rth
, PF_BRIDGE
, RTM_GETLINK
,
415 RTEXT_FILTER_BRVLAN_COMPRESSED
:
416 RTEXT_FILTER_BRVLAN
)) < 0) {
417 perror("Cannont send dump request");
421 jw_global
= jsonw_new(stdout
);
423 fprintf(stderr
, "Error allocation json object\n");
426 jsonw_start_object(jw_global
);
428 printf("port\tvlan ids\n");
431 if (rtnl_dump_filter(&rth
, print_vlan
, stdout
) < 0) {
432 fprintf(stderr
, "Dump ternminated\n");
438 filt_mask
= IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS
);
439 if (rtnl_wilddump_stats_req_filter(&rth
, AF_UNSPEC
,
442 perror("Cannont send dump request");
446 printf("%-16s vlan id\n", "port");
447 if (rtnl_dump_filter(&rth
, print_vlan_stats
, stdout
) < 0) {
448 fprintf(stderr
, "Dump terminated\n");
452 filt_mask
= IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE
);
453 if (rtnl_wilddump_stats_req_filter(&rth
, AF_UNSPEC
,
456 perror("Cannont send slave dump request");
460 if (rtnl_dump_filter(&rth
, print_vlan_stats
, stdout
) < 0) {
461 fprintf(stderr
, "Dump terminated\n");
467 jsonw_end_object(jw_global
);
468 jsonw_destroy(&jw_global
);
474 int do_vlan(int argc
, char **argv
)
479 if (matches(*argv
, "add") == 0)
480 return vlan_modify(RTM_SETLINK
, argc
-1, argv
+1);
481 if (matches(*argv
, "delete") == 0)
482 return vlan_modify(RTM_DELLINK
, argc
-1, argv
+1);
483 if (matches(*argv
, "show") == 0 ||
484 matches(*argv
, "lst") == 0 ||
485 matches(*argv
, "list") == 0)
486 return vlan_show(argc
-1, argv
+1);
487 if (matches(*argv
, "help") == 0)
490 return vlan_show(0, NULL
);
493 fprintf(stderr
, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv
);