]> git.proxmox.com Git - mirror_iproute2.git/blame - bridge/vlan.c
Merge branch 'master' into net-next
[mirror_iproute2.git] / bridge / vlan.c
CommitLineData
9eff0e5c
VY
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>
d82a49ce 10#include <json_writer.h>
9eff0e5c
VY
11#include <string.h>
12
13#include "libnetlink.h"
14#include "br_common.h"
15#include "utils.h"
16
5a2d0201 17static unsigned int filter_index, filter_vlan;
7abf5de6 18static int last_ifidx = -1;
9eff0e5c 19
d82a49ce
RP
20json_writer_t *jw_global = NULL;
21
9eff0e5c
VY
22static void usage(void)
23{
7abf5de6 24 fprintf(stderr, "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ pvid ] [ untagged ]\n");
9eff0e5c 25 fprintf(stderr, " [ self ] [ master ]\n");
5a2d0201 26 fprintf(stderr, " bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n");
9eff0e5c
VY
27 exit(-1);
28}
29
30static int vlan_modify(int cmd, int argc, char **argv)
31{
32 struct {
df4b043f
SH
33 struct nlmsghdr n;
34 struct ifinfomsg ifm;
35 char buf[1024];
d17b136f
PS
36 } req = {
37 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
38 .n.nlmsg_flags = NLM_F_REQUEST,
39 .n.nlmsg_type = cmd,
40 .ifm.ifi_family = PF_BRIDGE,
41 };
9eff0e5c
VY
42 char *d = NULL;
43 short vid = -1;
3ac0d36d 44 short vid_end = -1;
9eff0e5c 45 struct rtattr *afspec;
d17b136f 46 struct bridge_vlan_info vinfo = {};
9eff0e5c
VY
47 unsigned short flags = 0;
48
9eff0e5c
VY
49 while (argc > 0) {
50 if (strcmp(*argv, "dev") == 0) {
51 NEXT_ARG();
52 d = *argv;
53 } else if (strcmp(*argv, "vid") == 0) {
3ac0d36d 54 char *p;
df4b043f 55
9eff0e5c 56 NEXT_ARG();
3ac0d36d
RP
57 p = strchr(*argv, '-');
58 if (p) {
59 *p = '\0';
60 p++;
61 vid = atoi(*argv);
62 vid_end = atoi(p);
63 vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN;
64 } else {
65 vid = atoi(*argv);
66 }
9eff0e5c
VY
67 } else if (strcmp(*argv, "self") == 0) {
68 flags |= BRIDGE_FLAGS_SELF;
69 } else if (strcmp(*argv, "master") == 0) {
70 flags |= BRIDGE_FLAGS_MASTER;
71 } else if (strcmp(*argv, "pvid") == 0) {
72 vinfo.flags |= BRIDGE_VLAN_INFO_PVID;
73 } else if (strcmp(*argv, "untagged") == 0) {
74 vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED;
75 } else {
76 if (matches(*argv, "help") == 0) {
77 NEXT_ARG();
78 }
79 }
80 argc--; argv++;
81 }
82
83 if (d == NULL || vid == -1) {
84 fprintf(stderr, "Device and VLAN ID are required arguments.\n");
42ecedd4 85 return -1;
9eff0e5c
VY
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
3ac0d36d
RP
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 }
9eff0e5c
VY
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
3ac0d36d
RP
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 }
9eff0e5c
VY
133
134 addattr_nest_end(&req.n, afspec);
135
c079e121 136 if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
42ecedd4 137 return -1;
9eff0e5c
VY
138
139 return 0;
140}
141
5a2d0201
NA
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 */
149static 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
d82a49ce
RP
162static 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
175static 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
9eff0e5c
VY
184static 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;
df4b043f 191 struct rtattr *tb[IFLA_MAX+1];
e40d6b2b 192 bool vlan_flags = false;
9eff0e5c
VY
193
194 if (n->nlmsg_type != RTM_NEWLINK) {
195 fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n",
196 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
197 return 0;
198 }
199
200 len -= NLMSG_LENGTH(sizeof(*ifm));
201 if (len < 0) {
202 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
203 return -1;
204 }
205
206 if (ifm->ifi_family != AF_BRIDGE)
207 return 0;
208
209 if (filter_index && filter_index != ifm->ifi_index)
210 return 0;
211
212 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len);
213
214 /* if AF_SPEC isn't there, vlan table is not preset for this port */
215 if (!tb[IFLA_AF_SPEC]) {
1eeb6fda 216 if (!filter_vlan && !jw_global)
5a2d0201
NA
217 fprintf(fp, "%s\tNone\n",
218 ll_index_to_name(ifm->ifi_index));
9eff0e5c
VY
219 return 0;
220 } else {
221 struct rtattr *i, *list = tb[IFLA_AF_SPEC];
222 int rem = RTA_PAYLOAD(list);
5a2d0201 223 __u16 last_vid_start = 0;
9eff0e5c 224
5a2d0201 225 if (!filter_vlan)
d82a49ce
RP
226 print_vlan_port(fp, ifm->ifi_index);
227
9eff0e5c
VY
228 for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
229 struct bridge_vlan_info *vinfo;
5a2d0201 230 int vcheck_ret;
9eff0e5c
VY
231
232 if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
233 continue;
234
235 vinfo = RTA_DATA(i);
5a2d0201
NA
236
237 if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
238 last_vid_start = vinfo->vid;
239 vcheck_ret = filter_vlan_check(vinfo);
240 if (vcheck_ret == -1)
241 break;
242 else if (vcheck_ret == 0)
a2f7934d 243 continue;
5a2d0201
NA
244
245 if (filter_vlan)
d82a49ce
RP
246 print_vlan_port(fp, ifm->ifi_index);
247 if (jw_global) {
248 jsonw_start_object(jw_global);
249 jsonw_uint_field(jw_global, "vlan",
250 last_vid_start);
251 if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
252 continue;
253 } else {
254 fprintf(fp, "\t %hu", last_vid_start);
255 }
256 if (last_vid_start != vinfo->vid) {
257 if (jw_global)
258 jsonw_uint_field(jw_global, "vlanEnd",
259 vinfo->vid);
260 else
261 fprintf(fp, "-%hu", vinfo->vid);
262 }
263 if (vinfo->flags & BRIDGE_VLAN_INFO_PVID) {
264 if (jw_global) {
265 start_json_vlan_flags_array(&vlan_flags);
266 jsonw_string(jw_global, "PVID");
267 } else {
268 fprintf(fp, " PVID");
269 }
270 }
271 if (vinfo->flags & BRIDGE_VLAN_INFO_UNTAGGED) {
272 if (jw_global) {
273 start_json_vlan_flags_array(&vlan_flags);
274 jsonw_string(jw_global,
275 "Egress Untagged");
276 } else {
277 fprintf(fp, " Egress Untagged");
278 }
279 }
e40d6b2b 280 if (jw_global && vlan_flags) {
d82a49ce
RP
281 jsonw_end_array(jw_global);
282 vlan_flags = false;
283 }
284
285 if (jw_global)
286 jsonw_end_object(jw_global);
287 else
288 fprintf(fp, "\n");
9eff0e5c
VY
289 }
290 }
d82a49ce
RP
291 if (!filter_vlan) {
292 if (jw_global)
293 jsonw_end_array(jw_global);
294 else
295 fprintf(fp, "\n");
296
297 }
9eff0e5c
VY
298 fflush(fp);
299 return 0;
300}
301
7abf5de6
NA
302static void print_one_vlan_stats(FILE *fp,
303 const struct bridge_vlan_xstats *vstats,
304 int ifindex)
305{
306 const char *ifname = "";
307
308 if (filter_vlan && filter_vlan != vstats->vid)
309 return;
310 /* skip pure port entries, they'll be dumped via the slave stats call */
311 if ((vstats->flags & BRIDGE_VLAN_INFO_MASTER) &&
312 !(vstats->flags & BRIDGE_VLAN_INFO_BRENTRY))
313 return;
314
315 if (last_ifidx != ifindex) {
316 ifname = ll_index_to_name(ifindex);
317 last_ifidx = ifindex;
318 }
319 fprintf(fp, "%-16s %hu", ifname, vstats->vid);
320 if (vstats->flags & BRIDGE_VLAN_INFO_PVID)
321 fprintf(fp, " PVID");
322 if (vstats->flags & BRIDGE_VLAN_INFO_UNTAGGED)
323 fprintf(fp, " Egress Untagged");
324 fprintf(fp, "\n");
325 fprintf(fp, "%-16s RX: %llu bytes %llu packets\n",
326 "", vstats->rx_bytes, vstats->rx_packets);
327 fprintf(fp, "%-16s TX: %llu bytes %llu packets\n",
328 "", vstats->tx_bytes, vstats->tx_packets);
329}
330
331static void print_vlan_stats_attr(FILE *fp, struct rtattr *attr, int ifindex)
332{
333 struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
334 struct rtattr *i, *list;
335 int rem;
336
337 parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr),
338 RTA_PAYLOAD(attr));
339 if (!brtb[LINK_XSTATS_TYPE_BRIDGE])
340 return;
341
342 list = brtb[LINK_XSTATS_TYPE_BRIDGE];
343 rem = RTA_PAYLOAD(list);
344 for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
345 if (i->rta_type != BRIDGE_XSTATS_VLAN)
346 continue;
347 print_one_vlan_stats(fp, RTA_DATA(i), ifindex);
348 }
349}
350
351static int print_vlan_stats(const struct sockaddr_nl *who,
352 struct nlmsghdr *n,
353 void *arg)
354{
355 struct if_stats_msg *ifsm = NLMSG_DATA(n);
356 struct rtattr *tb[IFLA_STATS_MAX+1];
357 int len = n->nlmsg_len;
358 FILE *fp = arg;
359
360 len -= NLMSG_LENGTH(sizeof(*ifsm));
361 if (len < 0) {
362 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
363 return -1;
364 }
365
366 if (filter_index && filter_index != ifsm->ifindex)
367 return 0;
368
369 parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
370
371 /* We have to check if any of the two attrs are usable */
372 if (tb[IFLA_STATS_LINK_XSTATS])
373 print_vlan_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS],
374 ifsm->ifindex);
375
376 if (tb[IFLA_STATS_LINK_XSTATS_SLAVE])
377 print_vlan_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS_SLAVE],
378 ifsm->ifindex);
379
380 fflush(fp);
381 return 0;
382}
383
9eff0e5c
VY
384static int vlan_show(int argc, char **argv)
385{
386 char *filter_dev = NULL;
387
388 while (argc > 0) {
389 if (strcmp(*argv, "dev") == 0) {
390 NEXT_ARG();
391 if (filter_dev)
392 duparg("dev", *argv);
393 filter_dev = *argv;
5a2d0201
NA
394 } else if (strcmp(*argv, "vid") == 0) {
395 NEXT_ARG();
396 if (filter_vlan)
397 duparg("vid", *argv);
398 filter_vlan = atoi(*argv);
9eff0e5c
VY
399 }
400 argc--; argv++;
401 }
402
403 if (filter_dev) {
404 if ((filter_index = if_nametoindex(filter_dev)) == 0) {
405 fprintf(stderr, "Cannot find device \"%s\"\n",
406 filter_dev);
407 return -1;
408 }
409 }
410
7abf5de6
NA
411 if (!show_stats) {
412 if (rtnl_wilddump_req_filter(&rth, PF_BRIDGE, RTM_GETLINK,
413 (compress_vlans ?
414 RTEXT_FILTER_BRVLAN_COMPRESSED :
415 RTEXT_FILTER_BRVLAN)) < 0) {
416 perror("Cannont send dump request");
417 exit(1);
418 }
419 if (json_output) {
420 jw_global = jsonw_new(stdout);
421 if (!jw_global) {
422 fprintf(stderr, "Error allocation json object\n");
423 exit(1);
424 }
425 jsonw_start_object(jw_global);
426 } else {
427 printf("port\tvlan ids\n");
428 }
9eff0e5c 429
7abf5de6
NA
430 if (rtnl_dump_filter(&rth, print_vlan, stdout) < 0) {
431 fprintf(stderr, "Dump ternminated\n");
d82a49ce
RP
432 exit(1);
433 }
d82a49ce 434 } else {
7abf5de6 435 __u32 filt_mask;
d82a49ce 436
7abf5de6
NA
437 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
438 if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC,
439 RTM_GETSTATS,
440 filt_mask) < 0) {
441 perror("Cannont send dump request");
442 exit(1);
443 }
444
445 printf("%-16s vlan id\n", "port");
446 if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
447 fprintf(stderr, "Dump terminated\n");
448 exit(1);
449 }
450
451 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE);
452 if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC,
453 RTM_GETSTATS,
454 filt_mask) < 0) {
455 perror("Cannont send slave dump request");
456 exit(1);
457 }
458
459 if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) {
460 fprintf(stderr, "Dump terminated\n");
461 exit(1);
462 }
9eff0e5c
VY
463 }
464
d82a49ce
RP
465 if (jw_global) {
466 jsonw_end_object(jw_global);
467 jsonw_destroy(&jw_global);
468 }
469
9eff0e5c
VY
470 return 0;
471}
472
9eff0e5c
VY
473int do_vlan(int argc, char **argv)
474{
475 ll_init_map(&rth);
476
477 if (argc > 0) {
478 if (matches(*argv, "add") == 0)
479 return vlan_modify(RTM_SETLINK, argc-1, argv+1);
480 if (matches(*argv, "delete") == 0)
481 return vlan_modify(RTM_DELLINK, argc-1, argv+1);
482 if (matches(*argv, "show") == 0 ||
483 matches(*argv, "lst") == 0 ||
484 matches(*argv, "list") == 0)
485 return vlan_show(argc-1, argv+1);
486 if (matches(*argv, "help") == 0)
487 usage();
7abf5de6 488 } else {
9eff0e5c 489 return vlan_show(0, NULL);
7abf5de6 490 }
9eff0e5c 491
296cee6f 492 fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv);
9eff0e5c
VY
493 exit(-1);
494}