1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <sys/socket.h>
6 #include <net/if.h>
7 #include <netinet/in.h>
8 #include <linux/if_bridge.h>
9 #include <linux/if_ether.h>
10 #include <json_writer.h>
11 #include <string.h>
12
13 #include "libnetlink.h"
14 #include "br_common.h"
15 #include "utils.h"
16
17 static unsigned int filter_index, filter_vlan;
18 static int last_ifidx = -1;
19
20 json_writer_t *jw_global;
21
usage(void)22 static void usage(void)
23 {
24 fprintf(stderr,
25 "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ pvid ] [ untagged ]\n"
26 " [ self ] [ master ]\n"
27 " bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n");
28 exit(-1);
29 }
30
vlan_modify(int cmd,int argc,char ** argv)31 static int vlan_modify(int cmd, int argc, char **argv)
32 {
33 struct {
34 struct nlmsghdr n;
35 struct ifinfomsg ifm;
36 char buf[1024];
37 } req = {
38 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
39 .n.nlmsg_flags = NLM_F_REQUEST,
40 .n.nlmsg_type = cmd,
41 .ifm.ifi_family = PF_BRIDGE,
42 };
43 char *d = NULL;
44 short vid = -1;
45 short vid_end = -1;
46 struct rtattr *afspec;
47 struct bridge_vlan_info vinfo = {};
48 unsigned short flags = 0;
49
50 while (argc > 0) {
51 if (strcmp(*argv, "dev") == 0) {
52 NEXT_ARG();
53 d = *argv;
54 } else if (strcmp(*argv, "vid") == 0) {
55 char *p;
56
57 NEXT_ARG();
58 p = strchr(*argv, '-');
59 if (p) {
60 *p = '\0';
61 p++;
62 vid = atoi(*argv);
63 vid_end = atoi(p);
64 vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
65 } else {
66 vid = atoi(*argv);
67 }
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;
76 } else {
77 if (matches(*argv, "help") == 0)
78 NEXT_ARG();
79 }
80 argc--; argv++;
81 }
82
83 if (d == NULL || vid == -1) {
84 fprintf(stderr, "Device and VLAN ID are required arguments.\n");
85 return -1;
86 }
87
88 req.ifm.ifi_index = ll_name_to_index(d);
89 if (req.ifm.ifi_index == 0) {
90 fprintf(stderr, "Cannot find bridge device \"%s\"\n", d);
91 return -1;
92 }
93
94 if (vid >= 4096) {
95 fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid);
96 return -1;
97 }
98
99 if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
100 if (vid_end == -1 || vid_end >= 4096 || vid >= vid_end) {
101 fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
102 vid, vid_end);
103 return -1;
104 }
105 if (vinfo.flags & BRIDGE_VLAN_INFO_PVID) {
106 fprintf(stderr,
107 "pvid cannot be configured for a vlan range\n");
108 return -1;
109 }
110 }
111
112 afspec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC);
113
114 if (flags)
115 addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags);
116
117 vinfo.vid = vid;
118 if (vid_end != -1) {
119 /* send vlan range start */
120 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
121 sizeof(vinfo));
122 vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
123
124 /* Now send the vlan range end */
125 vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
126 vinfo.vid = vid_end;
127 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
128 sizeof(vinfo));
129 } else {
130 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
131 sizeof(vinfo));
132 }
133
134 addattr_nest_end(&req.n, afspec);
135
136 if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
137 return -1;
138
139 return 0;
140 }
141
142 /* In order to use this function for both filtering and non-filtering cases
143 * we need to make it a tristate:
144 * return -1 - if filtering we've gone over so don't continue
145 * return 0 - skip entry and continue (applies to range start or to entries
146 * which are less than filter_vlan)
147 * return 1 - print the entry and continue
148 */
filter_vlan_check(struct bridge_vlan_info * vinfo)149 static int filter_vlan_check(struct bridge_vlan_info *vinfo)
150 {
151 /* if we're filtering we should stop on the first greater entry */
152 if (filter_vlan && vinfo->vid > filter_vlan &&
153 !(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
154 return -1;
155 if ((vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) ||
156 vinfo->vid < filter_vlan)
157 return 0;
158
159 return 1;
160 }
161
print_vlan_port(FILE * fp,int ifi_index)162 static void print_vlan_port(FILE *fp, int ifi_index)
163 {
164 if (jw_global) {
165 jsonw_pretty(jw_global, 1);
166 jsonw_name(jw_global,
167 ll_index_to_name(ifi_index));
168 jsonw_start_array(jw_global);
169 } else {
170 fprintf(fp, "%s",
171 ll_index_to_name(ifi_index));
172 }
173 }
174
start_json_vlan_flags_array(bool * vlan_flags)175 static void start_json_vlan_flags_array(bool *vlan_flags)
176 {
177 if (*vlan_flags)
178 return;
179 jsonw_name(jw_global, "flags");
180 jsonw_start_array(jw_global);
181 *vlan_flags = true;
182 }
183
print_vlan(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)184 static int print_vlan(const struct sockaddr_nl *who,
185 struct nlmsghdr *n,
186 void *arg)
187 {
188 FILE *fp = arg;
189 struct ifinfomsg *ifm = NLMSG_DATA(n);
190 int len = n->nlmsg_len;
191 struct rtattr *tb[IFLA_MAX+1];
192
193 if (n->nlmsg_type != RTM_NEWLINK) {
194 fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
195 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
196 return 0;
197 }
198
199 len -= NLMSG_LENGTH(sizeof(*ifm));
200 if (len < 0) {
201 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
202 return -1;
203 }
204
205 if (ifm->ifi_family != AF_BRIDGE)
206 return 0;
207
208 if (filter_index && filter_index != ifm->ifi_index)
209 return 0;
210
211 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
212
213 /* if AF_SPEC isn't there, vlan table is not preset for this port */
214 if (!tb[IFLA_AF_SPEC]) {
215 if (!filter_vlan && !jw_global)
216 fprintf(fp, "%s\tNone\n",
217 ll_index_to_name(ifm->ifi_index));
218 return 0;
219 }
220
221 print_vlan_info(fp, tb[IFLA_AF_SPEC], ifm->ifi_index);
222 if (!filter_vlan) {
223 if (jw_global)
224 jsonw_end_array(jw_global);
225 else
226 fprintf(fp, "\n");
227
228 }
229 fflush(fp);
230 return 0;
231 }
232
print_one_vlan_stats(FILE * fp,const struct bridge_vlan_xstats * vstats,int ifindex)233 static void print_one_vlan_stats(FILE *fp,
234 const struct bridge_vlan_xstats *vstats,
235 int ifindex)
236 {
237 const char *ifname = "";
238
239 if (filter_vlan && filter_vlan != vstats->vid)
240 return;
241 /* skip pure port entries, they'll be dumped via the slave stats call */
242 if ((vstats->flags & BRIDGE_VLAN_INFO_MASTER) &&
243 !(vstats->flags & BRIDGE_VLAN_INFO_BRENTRY))
244 return;
245
246 if (last_ifidx != ifindex) {
247 ifname = ll_index_to_name(ifindex);
248 last_ifidx = ifindex;
249 }
250 fprintf(fp, "%-16s %hu", ifname, vstats->vid);
251 if (vstats->flags & BRIDGE_VLAN_INFO_PVID)
252 fprintf(fp, " PVID");
253 if (vstats->flags & BRIDGE_VLAN_INFO_UNTAGGED)
254 fprintf(fp, " Egress Untagged");
255 fprintf(fp, "\n");
256 fprintf(fp, "%-16s RX: %llu bytes %llu packets\n",
257 "", vstats->rx_bytes, vstats->rx_packets);
258 fprintf(fp, "%-16s TX: %llu bytes %llu packets\n",
259 "", vstats->tx_bytes, vstats->tx_packets);
260 }
261
print_vlan_stats_attr(FILE * fp,struct rtattr * attr,int ifindex)262 static void print_vlan_stats_attr(FILE *fp, struct rtattr *attr, int ifindex)
263 {
264 struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
265 struct rtattr *i, *list;
266 int rem;
267
268 parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr),
269 RTA_PAYLOAD(attr));
270 if (!brtb[LINK_XSTATS_TYPE_BRIDGE])
271 return;
272
273 list = brtb[LINK_XSTATS_TYPE_BRIDGE];
274 rem = RTA_PAYLOAD(list);
275 for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
276 if (i->rta_type != BRIDGE_XSTATS_VLAN)
277 continue;
278 print_one_vlan_stats(fp, RTA_DATA(i), ifindex);
279 }
280 }
281
print_vlan_stats(const struct sockaddr_nl * who,struct nlmsghdr * n,void * arg)282 static int print_vlan_stats(const struct sockaddr_nl *who,
283 struct nlmsghdr *n,
284 void *arg)
285 {
286 struct if_stats_msg *ifsm = NLMSG_DATA(n);
287 struct rtattr *tb[IFLA_STATS_MAX+1];
288 int len = n->nlmsg_len;
289 FILE *fp = arg;
290
291 len -= NLMSG_LENGTH(sizeof(*ifsm));
292 if (len < 0) {
293 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
294 return -1;
295 }
296
297 if (filter_index && filter_index != ifsm->ifindex)
298 return 0;
299
300 parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
301
302 /* We have to check if any of the two attrs are usable */
303 if (tb[IFLA_STATS_LINK_XSTATS])
304 print_vlan_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS],
305 ifsm->ifindex);
306
307 if (tb[IFLA_STATS_LINK_XSTATS_SLAVE])
308 print_vlan_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS_SLAVE],
309 ifsm->ifindex);
310
311 fflush(fp);
312 return 0;
313 }
314
vlan_show(int argc,char ** argv)315 static int vlan_show(int argc, char **argv)
316 {
317 char *filter_dev = NULL;
318
319 while (argc > 0) {
320 if (strcmp(*argv, "dev") == 0) {
321 NEXT_ARG();
322 if (filter_dev)
323 duparg("dev", *argv);
324 filter_dev = *argv;
325 } else if (strcmp(*argv, "vid") == 0) {
326 NEXT_ARG();
327 if (filter_vlan)
328 duparg("vid", *argv);
329 filter_vlan = atoi(*argv);
330 }
331 argc--; argv++;
332 }
333
334 if (filter_dev) {
335 filter_index = if_nametoindex(filter_dev);
336 if (filter_index == 0) {
337 fprintf(stderr, "Cannot find device \"%s\"\n",
338 filter_dev);
339 return -1;
340 }
341 }
342
343 if (!show_stats) {
344 if (rtnl_wilddump_req_filter(&rth, PF_BRIDGE, RTM_GETLINK,
345 (compress_vlans ?
346 RTEXT_FILTER_BRVLAN_COMPRESSED :
347 RTEXT_FILTER_BRVLAN)) < 0) {
348 perror("Cannont send dump request");
349 exit(1);
350 }
351 if (json_output) {
352 jw_global = jsonw_new(stdout);
353 if (!jw_global) {
354 fprintf(stderr, "Error allocation json object\n");
355 exit(1);
356 }
357 jsonw_start_object(jw_global);
358 } else {
359 printf("port\tvlan ids\n");
360 }
361
362 if (rtnl_dump_filter(&rth, print_vlan, stdout) < 0) {
363 fprintf(stderr, "Dump ternminated\n");
364 exit(1);
365 }
366 } else {
367 __u32 filt_mask;
368
369 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
370 if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC,
371 RTM_GETSTATS,
372 filt_mask) < 0) {
373 perror("Cannont send dump request");
374 exit(1);
375 }
376
377 printf("%-16s vlan id\n", "port");
378 if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
379 fprintf(stderr, "Dump terminated\n");
380 exit(1);
381 }
382
383 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE);
384 if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC,
385 RTM_GETSTATS,
386 filt_mask) < 0) {
387 perror("Cannont send slave dump request");
388 exit(1);
389 }
390
391 if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
392 fprintf(stderr, "Dump terminated\n");
393 exit(1);
394 }
395 }
396
397 if (jw_global) {
398 jsonw_end_object(jw_global);
399 jsonw_destroy(&jw_global);
400 }
401
402 return 0;
403 }
404
print_vlan_info(FILE * fp,struct rtattr * tb,int ifindex)405 void print_vlan_info(FILE *fp, struct rtattr *tb, int ifindex)
406 {
407 struct rtattr *i, *list = tb;
408 int rem = RTA_PAYLOAD(list);
409 __u16 last_vid_start = 0;
410 bool vlan_flags = false;
411
412 if (!filter_vlan)
413 print_vlan_port(fp, ifindex);
414
415 for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
416 struct bridge_vlan_info *vinfo;
417 int vcheck_ret;
418
419 if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
420 continue;
421
422 vinfo = RTA_DATA(i);
423
424 if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
425 last_vid_start = vinfo->vid;
426 vcheck_ret = filter_vlan_check(vinfo);
427 if (vcheck_ret == -1)
428 break;
429 else if (vcheck_ret == 0)
430 continue;
431
432 if (filter_vlan)
433 print_vlan_port(fp, ifindex);
434 if (jw_global) {
435 jsonw_start_object(jw_global);
436 jsonw_uint_field(jw_global, "vlan",
437 last_vid_start);
438 if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
439 continue;
440 } else {
441 fprintf(fp, "\t %hu", last_vid_start);
442 }
443 if (last_vid_start != vinfo->vid) {
444 if (jw_global)
445 jsonw_uint_field(jw_global, "vlanEnd",
446 vinfo->vid);
447 else
448 fprintf(fp, "-%hu", vinfo->vid);
449 }
450 if (vinfo->flags & BRIDGE_VLAN_INFO_PVID) {
451 if (jw_global) {
452 start_json_vlan_flags_array(&vlan_flags);
453 jsonw_string(jw_global, "PVID");
454 } else {
455 fprintf(fp, " PVID");
456 }
457 }
458 if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED) {
459 if (jw_global) {
460 start_json_vlan_flags_array(&vlan_flags);
461 jsonw_string(jw_global,
462 "Egress Untagged");
463 } else {
464 fprintf(fp, " Egress Untagged");
465 }
466 }
467 if (jw_global && vlan_flags) {
468 jsonw_end_array(jw_global);
469 vlan_flags = false;
470 }
471
472 if (jw_global)
473 jsonw_end_object(jw_global);
474 else
475 fprintf(fp, "\n");
476 }
477 }
478
do_vlan(int argc,char ** argv)479 int do_vlan(int argc, char **argv)
480 {
481 ll_init_map(&rth);
482
483 if (argc > 0) {
484 if (matches(*argv, "add") == 0)
485 return vlan_modify(RTM_SETLINK, argc-1, argv+1);
486 if (matches(*argv, "delete") == 0)
487 return vlan_modify(RTM_DELLINK, argc-1, argv+1);
488 if (matches(*argv, "show") == 0 ||
489 matches(*argv, "lst") == 0 ||
490 matches(*argv, "list") == 0)
491 return vlan_show(argc-1, argv+1);
492 if (matches(*argv, "help") == 0)
493 usage();
494 } else {
495 return vlan_show(0, NULL);
496 }
497
498 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv);
499 exit(-1);
500 }
501