]> git.proxmox.com Git - mirror_iproute2.git/blob - bridge/vlan.c
Merge branch 'master' into net-next
[mirror_iproute2.git] / bridge / vlan.c
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 = NULL;
21
22 static void usage(void)
23 {
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");
28 exit(-1);
29 }
30
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 }
81 argc--; argv++;
82 }
83
84 if (d == NULL || vid == -1) {
85 fprintf(stderr, "Device and VLAN ID are required arguments.\n");
86 return -1;
87 }
88
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);
92 return -1;
93 }
94
95 if (vid >= 4096) {
96 fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid);
97 return -1;
98 }
99
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",
103 vid, vid_end);
104 return -1;
105 }
106 if (vinfo.flags & BRIDGE_VLAN_INFO_PVID) {
107 fprintf(stderr,
108 "pvid cannot be configured for a vlan range\n");
109 return -1;
110 }
111 }
112
113 afspec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC);
114
115 if (flags)
116 addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags);
117
118 vinfo.vid = vid;
119 if (vid_end != -1) {
120 /* send vlan range start */
121 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
122 sizeof(vinfo));
123 vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN;
124
125 /* Now send the vlan range end */
126 vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END;
127 vinfo.vid = vid_end;
128 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
129 sizeof(vinfo));
130 } else {
131 addattr_l(&req.n, sizeof(req), IFLA_BRIDGE_VLAN_INFO, &vinfo,
132 sizeof(vinfo));
133 }
134
135 addattr_nest_end(&req.n, afspec);
136
137 if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
138 return -1;
139
140 return 0;
141 }
142
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
149 */
150 static int filter_vlan_check(struct bridge_vlan_info *vinfo)
151 {
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))
155 return -1;
156 if ((vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) ||
157 vinfo->vid < filter_vlan)
158 return 0;
159
160 return 1;
161 }
162
163 static void print_vlan_port(FILE *fp, int ifi_index)
164 {
165 if (jw_global) {
166 jsonw_pretty(jw_global, 1);
167 jsonw_name(jw_global,
168 ll_index_to_name(ifi_index));
169 jsonw_start_array(jw_global);
170 } else {
171 fprintf(fp, "%s",
172 ll_index_to_name(ifi_index));
173 }
174 }
175
176 static void start_json_vlan_flags_array(bool *vlan_flags)
177 {
178 if (*vlan_flags)
179 return;
180 jsonw_name(jw_global, "flags");
181 jsonw_start_array(jw_global);
182 *vlan_flags = true;
183 }
184
185 static int print_vlan(const struct sockaddr_nl *who,
186 struct nlmsghdr *n,
187 void *arg)
188 {
189 FILE *fp = arg;
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;
194
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);
198 return 0;
199 }
200
201 len -= NLMSG_LENGTH(sizeof(*ifm));
202 if (len < 0) {
203 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
204 return -1;
205 }
206
207 if (ifm->ifi_family != AF_BRIDGE)
208 return 0;
209
210 if (filter_index && filter_index != ifm->ifi_index)
211 return 0;
212
213 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
214
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));
220 return 0;
221 } else {
222 struct rtattr *i, *list = tb[IFLA_AF_SPEC];
223 int rem = RTA_PAYLOAD(list);
224 __u16 last_vid_start = 0;
225
226 if (!filter_vlan)
227 print_vlan_port(fp, ifm->ifi_index);
228
229 for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
230 struct bridge_vlan_info *vinfo;
231 int vcheck_ret;
232
233 if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
234 continue;
235
236 vinfo = RTA_DATA(i);
237
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)
242 break;
243 else if (vcheck_ret == 0)
244 continue;
245
246 if (filter_vlan)
247 print_vlan_port(fp, ifm->ifi_index);
248 if (jw_global) {
249 jsonw_start_object(jw_global);
250 jsonw_uint_field(jw_global, "vlan",
251 last_vid_start);
252 if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
253 continue;
254 } else {
255 fprintf(fp, "\t %hu", last_vid_start);
256 }
257 if (last_vid_start != vinfo->vid) {
258 if (jw_global)
259 jsonw_uint_field(jw_global, "vlanEnd",
260 vinfo->vid);
261 else
262 fprintf(fp, "-%hu", vinfo->vid);
263 }
264 if (vinfo->flags & BRIDGE_VLAN_INFO_PVID) {
265 if (jw_global) {
266 start_json_vlan_flags_array(&vlan_flags);
267 jsonw_string(jw_global, "PVID");
268 } else {
269 fprintf(fp, " PVID");
270 }
271 }
272 if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED) {
273 if (jw_global) {
274 start_json_vlan_flags_array(&vlan_flags);
275 jsonw_string(jw_global,
276 "Egress Untagged");
277 } else {
278 fprintf(fp, " Egress Untagged");
279 }
280 }
281 if (jw_global && vlan_flags) {
282 jsonw_end_array(jw_global);
283 vlan_flags = false;
284 }
285
286 if (jw_global)
287 jsonw_end_object(jw_global);
288 else
289 fprintf(fp, "\n");
290 }
291 }
292 if (!filter_vlan) {
293 if (jw_global)
294 jsonw_end_array(jw_global);
295 else
296 fprintf(fp, "\n");
297
298 }
299 fflush(fp);
300 return 0;
301 }
302
303 static void print_one_vlan_stats(FILE *fp,
304 const struct bridge_vlan_xstats *vstats,
305 int ifindex)
306 {
307 const char *ifname = "";
308
309 if (filter_vlan && filter_vlan != vstats->vid)
310 return;
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))
314 return;
315
316 if (last_ifidx != ifindex) {
317 ifname = ll_index_to_name(ifindex);
318 last_ifidx = ifindex;
319 }
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");
325 fprintf(fp, "\n");
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);
330 }
331
332 static void print_vlan_stats_attr(FILE *fp, struct rtattr *attr, int ifindex)
333 {
334 struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
335 struct rtattr *i, *list;
336 int rem;
337
338 parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr),
339 RTA_PAYLOAD(attr));
340 if (!brtb[LINK_XSTATS_TYPE_BRIDGE])
341 return;
342
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)
347 continue;
348 print_one_vlan_stats(fp, RTA_DATA(i), ifindex);
349 }
350 }
351
352 static int print_vlan_stats(const struct sockaddr_nl *who,
353 struct nlmsghdr *n,
354 void *arg)
355 {
356 struct if_stats_msg *ifsm = NLMSG_DATA(n);
357 struct rtattr *tb[IFLA_STATS_MAX+1];
358 int len = n->nlmsg_len;
359 FILE *fp = arg;
360
361 len -= NLMSG_LENGTH(sizeof(*ifsm));
362 if (len < 0) {
363 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
364 return -1;
365 }
366
367 if (filter_index && filter_index != ifsm->ifindex)
368 return 0;
369
370 parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
371
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],
375 ifsm->ifindex);
376
377 if (tb[IFLA_STATS_LINK_XSTATS_SLAVE])
378 print_vlan_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS_SLAVE],
379 ifsm->ifindex);
380
381 fflush(fp);
382 return 0;
383 }
384
385 static int vlan_show(int argc, char **argv)
386 {
387 char *filter_dev = NULL;
388
389 while (argc > 0) {
390 if (strcmp(*argv, "dev") == 0) {
391 NEXT_ARG();
392 if (filter_dev)
393 duparg("dev", *argv);
394 filter_dev = *argv;
395 } else if (strcmp(*argv, "vid") == 0) {
396 NEXT_ARG();
397 if (filter_vlan)
398 duparg("vid", *argv);
399 filter_vlan = atoi(*argv);
400 }
401 argc--; argv++;
402 }
403
404 if (filter_dev) {
405 if ((filter_index = if_nametoindex(filter_dev)) == 0) {
406 fprintf(stderr, "Cannot find device \"%s\"\n",
407 filter_dev);
408 return -1;
409 }
410 }
411
412 if (!show_stats) {
413 if (rtnl_wilddump_req_filter(&rth, PF_BRIDGE, RTM_GETLINK,
414 (compress_vlans ?
415 RTEXT_FILTER_BRVLAN_COMPRESSED :
416 RTEXT_FILTER_BRVLAN)) < 0) {
417 perror("Cannont send dump request");
418 exit(1);
419 }
420 if (json_output) {
421 jw_global = jsonw_new(stdout);
422 if (!jw_global) {
423 fprintf(stderr, "Error allocation json object\n");
424 exit(1);
425 }
426 jsonw_start_object(jw_global);
427 } else {
428 printf("port\tvlan ids\n");
429 }
430
431 if (rtnl_dump_filter(&rth, print_vlan, stdout) < 0) {
432 fprintf(stderr, "Dump ternminated\n");
433 exit(1);
434 }
435 } else {
436 __u32 filt_mask;
437
438 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
439 if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC,
440 RTM_GETSTATS,
441 filt_mask) < 0) {
442 perror("Cannont send dump request");
443 exit(1);
444 }
445
446 printf("%-16s vlan id\n", "port");
447 if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
448 fprintf(stderr, "Dump terminated\n");
449 exit(1);
450 }
451
452 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE);
453 if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC,
454 RTM_GETSTATS,
455 filt_mask) < 0) {
456 perror("Cannont send slave dump request");
457 exit(1);
458 }
459
460 if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
461 fprintf(stderr, "Dump terminated\n");
462 exit(1);
463 }
464 }
465
466 if (jw_global) {
467 jsonw_end_object(jw_global);
468 jsonw_destroy(&jw_global);
469 }
470
471 return 0;
472 }
473
474 int do_vlan(int argc, char **argv)
475 {
476 ll_init_map(&rth);
477
478 if (argc > 0) {
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)
488 usage();
489 } else {
490 return vlan_show(0, NULL);
491 }
492
493 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv);
494 exit(-1);
495 }