]>
Commit | Line | Data |
---|---|---|
6054c1eb | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
9eff0e5c VY |
2 | #include <stdio.h> |
3 | #include <stdlib.h> | |
4 | #include <unistd.h> | |
5 | #include <fcntl.h> | |
6 | #include <sys/socket.h> | |
7 | #include <net/if.h> | |
8 | #include <netinet/in.h> | |
9 | #include <linux/if_bridge.h> | |
10 | #include <linux/if_ether.h> | |
11 | #include <string.h> | |
12 | ||
c7c1a1ef | 13 | #include "json_print.h" |
9eff0e5c VY |
14 | #include "libnetlink.h" |
15 | #include "br_common.h" | |
16 | #include "utils.h" | |
17 | ||
5a2d0201 | 18 | static unsigned int filter_index, filter_vlan; |
955a20be BP |
19 | |
20 | enum vlan_show_subject { | |
21 | VLAN_SHOW_VLAN, | |
22 | VLAN_SHOW_TUNNELINFO, | |
23 | }; | |
9eff0e5c | 24 | |
e0c457b1 BP |
25 | #define VLAN_ID_LEN 9 |
26 | ||
27 | #define __stringify_1(x...) #x | |
28 | #define __stringify(x...) __stringify_1(x) | |
29 | ||
9eff0e5c VY |
30 | static void usage(void) |
31 | { | |
bcddcddd | 32 | fprintf(stderr, |
8652eeb3 RP |
33 | "Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ tunnel_info id TUNNEL_ID ]\n" |
34 | " [ pvid ] [ untagged ]\n" | |
bcddcddd | 35 | " [ self ] [ master ]\n" |
8652eeb3 RP |
36 | " bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n" |
37 | " bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n"); | |
9eff0e5c VY |
38 | exit(-1); |
39 | } | |
40 | ||
8652eeb3 RP |
41 | static int parse_tunnel_info(int *argcp, char ***argvp, __u32 *tun_id_start, |
42 | __u32 *tun_id_end) | |
43 | { | |
44 | char **argv = *argvp; | |
45 | int argc = *argcp; | |
46 | char *t; | |
47 | ||
48 | NEXT_ARG(); | |
49 | if (!matches(*argv, "id")) { | |
50 | NEXT_ARG(); | |
51 | t = strchr(*argv, '-'); | |
52 | if (t) { | |
53 | *t = '\0'; | |
54 | if (get_u32(tun_id_start, *argv, 0) || | |
55 | *tun_id_start >= 1u << 24) | |
56 | invarg("invalid tun id", *argv); | |
57 | if (get_u32(tun_id_end, t + 1, 0) || | |
58 | *tun_id_end >= 1u << 24) | |
59 | invarg("invalid tun id", *argv); | |
60 | ||
61 | } else { | |
62 | if (get_u32(tun_id_start, *argv, 0) || | |
63 | *tun_id_start >= 1u << 24) | |
64 | invarg("invalid tun id", *argv); | |
65 | } | |
66 | } else { | |
67 | invarg("tunnel id expected", *argv); | |
68 | } | |
69 | ||
70 | *argcp = argc; | |
71 | *argvp = argv; | |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | static int add_tunnel_info(struct nlmsghdr *n, int reqsize, | |
77 | __u16 vid, __u32 tun_id, __u16 flags) | |
78 | { | |
79 | struct rtattr *tinfo; | |
80 | ||
81 | tinfo = addattr_nest(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_INFO); | |
82 | addattr32(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_ID, tun_id); | |
1f53ba72 BP |
83 | addattr16(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_VID, vid); |
84 | addattr16(n, reqsize, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, flags); | |
8652eeb3 RP |
85 | |
86 | addattr_nest_end(n, tinfo); | |
87 | ||
88 | return 0; | |
89 | } | |
90 | ||
91 | static int add_tunnel_info_range(struct nlmsghdr *n, int reqsize, | |
92 | __u16 vid_start, int16_t vid_end, | |
93 | __u32 tun_id_start, __u32 tun_id_end) | |
94 | { | |
95 | if (vid_end != -1 && (vid_end - vid_start) > 0) { | |
96 | add_tunnel_info(n, reqsize, vid_start, tun_id_start, | |
97 | BRIDGE_VLAN_INFO_RANGE_BEGIN); | |
98 | ||
99 | add_tunnel_info(n, reqsize, vid_end, tun_id_end, | |
100 | BRIDGE_VLAN_INFO_RANGE_END); | |
101 | } else { | |
102 | add_tunnel_info(n, reqsize, vid_start, tun_id_start, 0); | |
103 | } | |
104 | ||
105 | return 0; | |
106 | } | |
107 | ||
108 | static int add_vlan_info_range(struct nlmsghdr *n, int reqsize, __u16 vid_start, | |
109 | int16_t vid_end, __u16 flags) | |
110 | { | |
111 | struct bridge_vlan_info vinfo = {}; | |
112 | ||
113 | vinfo.flags = flags; | |
114 | vinfo.vid = vid_start; | |
115 | if (vid_end != -1) { | |
116 | /* send vlan range start */ | |
117 | addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo, | |
118 | sizeof(vinfo)); | |
119 | vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; | |
120 | ||
121 | /* Now send the vlan range end */ | |
122 | vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END; | |
123 | vinfo.vid = vid_end; | |
124 | addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo, | |
125 | sizeof(vinfo)); | |
126 | } else { | |
127 | addattr_l(n, reqsize, IFLA_BRIDGE_VLAN_INFO, &vinfo, | |
128 | sizeof(vinfo)); | |
129 | } | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
9eff0e5c VY |
134 | static int vlan_modify(int cmd, int argc, char **argv) |
135 | { | |
136 | struct { | |
df4b043f SH |
137 | struct nlmsghdr n; |
138 | struct ifinfomsg ifm; | |
139 | char buf[1024]; | |
d17b136f PS |
140 | } req = { |
141 | .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), | |
142 | .n.nlmsg_flags = NLM_F_REQUEST, | |
143 | .n.nlmsg_type = cmd, | |
144 | .ifm.ifi_family = PF_BRIDGE, | |
145 | }; | |
9eff0e5c VY |
146 | char *d = NULL; |
147 | short vid = -1; | |
3ac0d36d | 148 | short vid_end = -1; |
9eff0e5c | 149 | struct rtattr *afspec; |
d17b136f | 150 | struct bridge_vlan_info vinfo = {}; |
8652eeb3 | 151 | bool tunnel_info_set = false; |
9eff0e5c | 152 | unsigned short flags = 0; |
8652eeb3 RP |
153 | __u32 tun_id_start = 0; |
154 | __u32 tun_id_end = 0; | |
9eff0e5c | 155 | |
9eff0e5c VY |
156 | while (argc > 0) { |
157 | if (strcmp(*argv, "dev") == 0) { | |
158 | NEXT_ARG(); | |
159 | d = *argv; | |
160 | } else if (strcmp(*argv, "vid") == 0) { | |
3ac0d36d | 161 | char *p; |
df4b043f | 162 | |
9eff0e5c | 163 | NEXT_ARG(); |
3ac0d36d RP |
164 | p = strchr(*argv, '-'); |
165 | if (p) { | |
166 | *p = '\0'; | |
167 | p++; | |
168 | vid = atoi(*argv); | |
169 | vid_end = atoi(p); | |
170 | vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN; | |
171 | } else { | |
172 | vid = atoi(*argv); | |
173 | } | |
9eff0e5c VY |
174 | } else if (strcmp(*argv, "self") == 0) { |
175 | flags |= BRIDGE_FLAGS_SELF; | |
176 | } else if (strcmp(*argv, "master") == 0) { | |
177 | flags |= BRIDGE_FLAGS_MASTER; | |
178 | } else if (strcmp(*argv, "pvid") == 0) { | |
179 | vinfo.flags |= BRIDGE_VLAN_INFO_PVID; | |
180 | } else if (strcmp(*argv, "untagged") == 0) { | |
181 | vinfo.flags |= BRIDGE_VLAN_INFO_UNTAGGED; | |
8652eeb3 RP |
182 | } else if (strcmp(*argv, "tunnel_info") == 0) { |
183 | if (parse_tunnel_info(&argc, &argv, | |
184 | &tun_id_start, | |
185 | &tun_id_end)) | |
186 | return -1; | |
187 | tunnel_info_set = true; | |
9eff0e5c | 188 | } else { |
bcddcddd | 189 | if (matches(*argv, "help") == 0) |
9eff0e5c | 190 | NEXT_ARG(); |
9eff0e5c VY |
191 | } |
192 | argc--; argv++; | |
193 | } | |
194 | ||
195 | if (d == NULL || vid == -1) { | |
196 | fprintf(stderr, "Device and VLAN ID are required arguments.\n"); | |
42ecedd4 | 197 | return -1; |
9eff0e5c VY |
198 | } |
199 | ||
200 | req.ifm.ifi_index = ll_name_to_index(d); | |
201 | if (req.ifm.ifi_index == 0) { | |
202 | fprintf(stderr, "Cannot find bridge device \"%s\"\n", d); | |
203 | return -1; | |
204 | } | |
205 | ||
206 | if (vid >= 4096) { | |
207 | fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid); | |
208 | return -1; | |
209 | } | |
210 | ||
3ac0d36d RP |
211 | if (vinfo.flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { |
212 | if (vid_end == -1 || vid_end >= 4096 || vid >= vid_end) { | |
213 | fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n", | |
214 | vid, vid_end); | |
215 | return -1; | |
216 | } | |
217 | if (vinfo.flags & BRIDGE_VLAN_INFO_PVID) { | |
218 | fprintf(stderr, | |
219 | "pvid cannot be configured for a vlan range\n"); | |
220 | return -1; | |
221 | } | |
222 | } | |
9eff0e5c VY |
223 | |
224 | afspec = addattr_nest(&req.n, sizeof(req), IFLA_AF_SPEC); | |
225 | ||
226 | if (flags) | |
227 | addattr16(&req.n, sizeof(req), IFLA_BRIDGE_FLAGS, flags); | |
228 | ||
8652eeb3 RP |
229 | if (tunnel_info_set) |
230 | add_tunnel_info_range(&req.n, sizeof(req), vid, vid_end, | |
231 | tun_id_start, tun_id_end); | |
232 | else | |
233 | add_vlan_info_range(&req.n, sizeof(req), vid, vid_end, | |
234 | vinfo.flags); | |
9eff0e5c VY |
235 | |
236 | addattr_nest_end(&req.n, afspec); | |
237 | ||
86bf43c7 | 238 | if (rtnl_talk(&rth, &req.n, NULL) < 0) |
42ecedd4 | 239 | return -1; |
9eff0e5c VY |
240 | |
241 | return 0; | |
242 | } | |
243 | ||
5a2d0201 NA |
244 | /* In order to use this function for both filtering and non-filtering cases |
245 | * we need to make it a tristate: | |
246 | * return -1 - if filtering we've gone over so don't continue | |
247 | * return 0 - skip entry and continue (applies to range start or to entries | |
248 | * which are less than filter_vlan) | |
249 | * return 1 - print the entry and continue | |
250 | */ | |
8652eeb3 | 251 | static int filter_vlan_check(__u16 vid, __u16 flags) |
5a2d0201 NA |
252 | { |
253 | /* if we're filtering we should stop on the first greater entry */ | |
8652eeb3 RP |
254 | if (filter_vlan && vid > filter_vlan && |
255 | !(flags & BRIDGE_VLAN_INFO_RANGE_END)) | |
5a2d0201 | 256 | return -1; |
8652eeb3 RP |
257 | if ((flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) || |
258 | vid < filter_vlan) | |
5a2d0201 NA |
259 | return 0; |
260 | ||
261 | return 1; | |
262 | } | |
263 | ||
e0c457b1 | 264 | static void open_vlan_port(int ifi_index, enum vlan_show_subject subject) |
d82a49ce | 265 | { |
0f362674 | 266 | open_json_object(NULL); |
e0c457b1 BP |
267 | print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", |
268 | "%-" __stringify(IFNAMSIZ) "s ", | |
6e982e7b | 269 | ll_index_to_name(ifi_index)); |
1a500c78 BP |
270 | open_json_array(PRINT_JSON, |
271 | subject == VLAN_SHOW_VLAN ? "vlans": "tunnels"); | |
0f362674 SH |
272 | } |
273 | ||
274 | static void close_vlan_port(void) | |
275 | { | |
276 | close_json_array(PRINT_JSON, NULL); | |
277 | close_json_object(); | |
d82a49ce RP |
278 | } |
279 | ||
e0c457b1 | 280 | static unsigned int print_range(const char *name, __u32 start, __u32 id) |
d82a49ce | 281 | { |
c7c1a1ef | 282 | char end[64]; |
e0c457b1 | 283 | int width; |
c7c1a1ef SH |
284 | |
285 | snprintf(end, sizeof(end), "%sEnd", name); | |
286 | ||
e0c457b1 | 287 | width = print_uint(PRINT_ANY, name, "%u", start); |
c7c1a1ef | 288 | if (start != id) |
e0c457b1 | 289 | width += print_uint(PRINT_ANY, end, "-%u", id); |
c7c1a1ef | 290 | |
e0c457b1 | 291 | return width; |
d82a49ce RP |
292 | } |
293 | ||
955a20be | 294 | static void print_vlan_tunnel_info(struct rtattr *tb, int ifindex) |
8652eeb3 | 295 | { |
8652eeb3 RP |
296 | struct rtattr *i, *list = tb; |
297 | int rem = RTA_PAYLOAD(list); | |
298 | __u16 last_vid_start = 0; | |
299 | __u32 last_tunid_start = 0; | |
b262a9be | 300 | bool opened = false; |
8652eeb3 RP |
301 | |
302 | for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { | |
303 | struct rtattr *ttb[IFLA_BRIDGE_VLAN_TUNNEL_MAX+1]; | |
304 | __u32 tunnel_id = 0; | |
305 | __u16 tunnel_vid = 0; | |
306 | __u16 tunnel_flags = 0; | |
e0c457b1 | 307 | unsigned int width; |
8652eeb3 RP |
308 | int vcheck_ret; |
309 | ||
310 | if (i->rta_type != IFLA_BRIDGE_VLAN_TUNNEL_INFO) | |
311 | continue; | |
312 | ||
313 | parse_rtattr(ttb, IFLA_BRIDGE_VLAN_TUNNEL_MAX, | |
314 | RTA_DATA(i), RTA_PAYLOAD(i)); | |
315 | ||
316 | if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID]) | |
317 | tunnel_vid = | |
1f53ba72 | 318 | rta_getattr_u16(ttb[IFLA_BRIDGE_VLAN_TUNNEL_VID]); |
8652eeb3 RP |
319 | else |
320 | continue; | |
321 | ||
322 | if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID]) | |
323 | tunnel_id = | |
324 | rta_getattr_u32(ttb[IFLA_BRIDGE_VLAN_TUNNEL_ID]); | |
325 | ||
326 | if (ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]) | |
327 | tunnel_flags = | |
1f53ba72 | 328 | rta_getattr_u16(ttb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]); |
8652eeb3 RP |
329 | |
330 | if (!(tunnel_flags & BRIDGE_VLAN_INFO_RANGE_END)) { | |
331 | last_vid_start = tunnel_vid; | |
332 | last_tunid_start = tunnel_id; | |
333 | } | |
c7c1a1ef | 334 | |
8652eeb3 RP |
335 | vcheck_ret = filter_vlan_check(tunnel_vid, tunnel_flags); |
336 | if (vcheck_ret == -1) | |
337 | break; | |
338 | else if (vcheck_ret == 0) | |
339 | continue; | |
340 | ||
b262a9be | 341 | if (!opened) { |
e0c457b1 | 342 | open_vlan_port(ifindex, VLAN_SHOW_TUNNELINFO); |
b262a9be | 343 | opened = true; |
e0c457b1 BP |
344 | } else { |
345 | print_string(PRINT_FP, NULL, | |
346 | "%-" __stringify(IFNAMSIZ) "s ", ""); | |
b262a9be BP |
347 | } |
348 | ||
c7c1a1ef | 349 | open_json_object(NULL); |
e0c457b1 BP |
350 | width = print_range("vlan", last_vid_start, tunnel_vid); |
351 | if (width <= VLAN_ID_LEN) { | |
352 | char buf[VLAN_ID_LEN + 1]; | |
353 | ||
354 | snprintf(buf, sizeof(buf), "%-*s", | |
355 | VLAN_ID_LEN - width, ""); | |
356 | print_string(PRINT_FP, NULL, "%s ", buf); | |
357 | } else { | |
358 | fprintf(stderr, "BUG: vlan range too wide, %u\n", | |
359 | width); | |
360 | } | |
c7c1a1ef SH |
361 | print_range("tunid", last_tunid_start, tunnel_id); |
362 | close_json_object(); | |
0501fe73 | 363 | print_nl(); |
8652eeb3 | 364 | } |
b262a9be BP |
365 | |
366 | if (opened) | |
367 | close_vlan_port(); | |
8652eeb3 RP |
368 | } |
369 | ||
cd554f2c | 370 | static int print_vlan(struct nlmsghdr *n, void *arg) |
9eff0e5c | 371 | { |
955a20be | 372 | enum vlan_show_subject *subject = arg; |
9eff0e5c VY |
373 | struct ifinfomsg *ifm = NLMSG_DATA(n); |
374 | int len = n->nlmsg_len; | |
df4b043f | 375 | struct rtattr *tb[IFLA_MAX+1]; |
9eff0e5c VY |
376 | |
377 | if (n->nlmsg_type != RTM_NEWLINK) { | |
378 | fprintf(stderr, "Not RTM_NEWLINK: %08x %08x %08x\n", | |
379 | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); | |
380 | return 0; | |
381 | } | |
382 | ||
383 | len -= NLMSG_LENGTH(sizeof(*ifm)); | |
384 | if (len < 0) { | |
385 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
386 | return -1; | |
387 | } | |
388 | ||
389 | if (ifm->ifi_family != AF_BRIDGE) | |
390 | return 0; | |
391 | ||
392 | if (filter_index && filter_index != ifm->ifi_index) | |
393 | return 0; | |
394 | ||
395 | parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), len); | |
b262a9be | 396 | if (!tb[IFLA_AF_SPEC]) |
9eff0e5c | 397 | return 0; |
bcddcddd | 398 | |
955a20be BP |
399 | switch (*subject) { |
400 | case VLAN_SHOW_VLAN: | |
401 | print_vlan_info(tb[IFLA_AF_SPEC], ifm->ifi_index); | |
402 | break; | |
403 | case VLAN_SHOW_TUNNELINFO: | |
404 | print_vlan_tunnel_info(tb[IFLA_AF_SPEC], ifm->ifi_index); | |
405 | break; | |
406 | } | |
d82a49ce | 407 | |
9eff0e5c VY |
408 | return 0; |
409 | } | |
410 | ||
c7c1a1ef | 411 | static void print_vlan_flags(__u16 flags) |
7abf5de6 | 412 | { |
0f362674 SH |
413 | if (flags == 0) |
414 | return; | |
415 | ||
416 | open_json_array(PRINT_JSON, "flags"); | |
c7c1a1ef | 417 | if (flags & BRIDGE_VLAN_INFO_PVID) |
0f362674 | 418 | print_string(PRINT_ANY, NULL, " %s", "PVID"); |
7abf5de6 | 419 | |
c7c1a1ef | 420 | if (flags & BRIDGE_VLAN_INFO_UNTAGGED) |
0f362674 SH |
421 | print_string(PRINT_ANY, NULL, " %s", "Egress Untagged"); |
422 | close_json_array(PRINT_JSON, NULL); | |
c7c1a1ef | 423 | } |
7abf5de6 | 424 | |
c7c1a1ef SH |
425 | static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats) |
426 | { | |
427 | open_json_object(NULL); | |
c7c1a1ef | 428 | |
e0c457b1 | 429 | print_hu(PRINT_ANY, "vid", "%hu", vstats->vid); |
c7c1a1ef | 430 | print_vlan_flags(vstats->flags); |
e0c457b1 | 431 | print_nl(); |
c7c1a1ef | 432 | |
e0c457b1 BP |
433 | print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", ""); |
434 | print_lluint(PRINT_ANY, "rx_bytes", "RX: %llu bytes", | |
c7c1a1ef SH |
435 | vstats->rx_bytes); |
436 | print_lluint(PRINT_ANY, "rx_packets", " %llu packets\n", | |
e0c457b1 BP |
437 | vstats->rx_packets); |
438 | ||
439 | print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", ""); | |
440 | print_lluint(PRINT_ANY, "tx_bytes", "TX: %llu bytes", | |
c7c1a1ef | 441 | vstats->tx_bytes); |
6e982e7b | 442 | print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n", |
e0c457b1 BP |
443 | vstats->tx_packets); |
444 | ||
c7c1a1ef | 445 | close_json_object(); |
7abf5de6 NA |
446 | } |
447 | ||
c7c1a1ef | 448 | static void print_vlan_stats_attr(struct rtattr *attr, int ifindex) |
7abf5de6 NA |
449 | { |
450 | struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1]; | |
451 | struct rtattr *i, *list; | |
6e982e7b | 452 | bool found_vlan = false; |
7abf5de6 NA |
453 | int rem; |
454 | ||
455 | parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr), | |
456 | RTA_PAYLOAD(attr)); | |
457 | if (!brtb[LINK_XSTATS_TYPE_BRIDGE]) | |
458 | return; | |
459 | ||
460 | list = brtb[LINK_XSTATS_TYPE_BRIDGE]; | |
461 | rem = RTA_PAYLOAD(list); | |
c7c1a1ef | 462 | |
7abf5de6 | 463 | for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { |
c7c1a1ef SH |
464 | const struct bridge_vlan_xstats *vstats = RTA_DATA(i); |
465 | ||
7abf5de6 NA |
466 | if (i->rta_type != BRIDGE_XSTATS_VLAN) |
467 | continue; | |
c7c1a1ef SH |
468 | |
469 | if (filter_vlan && filter_vlan != vstats->vid) | |
470 | continue; | |
471 | ||
472 | /* skip pure port entries, they'll be dumped via the slave stats call */ | |
473 | if ((vstats->flags & BRIDGE_VLAN_INFO_MASTER) && | |
474 | !(vstats->flags & BRIDGE_VLAN_INFO_BRENTRY)) | |
475 | continue; | |
476 | ||
6e982e7b NA |
477 | /* found vlan stats, first time print the interface name */ |
478 | if (!found_vlan) { | |
e0c457b1 | 479 | open_vlan_port(ifindex, VLAN_SHOW_VLAN); |
6e982e7b NA |
480 | found_vlan = true; |
481 | } else { | |
e0c457b1 BP |
482 | print_string(PRINT_FP, NULL, |
483 | "%-" __stringify(IFNAMSIZ) "s ", ""); | |
6e982e7b | 484 | } |
c7c1a1ef | 485 | print_one_vlan_stats(vstats); |
7abf5de6 | 486 | } |
6e982e7b NA |
487 | |
488 | /* vlan_port is opened only if there are any vlan stats */ | |
489 | if (found_vlan) | |
490 | close_vlan_port(); | |
7abf5de6 NA |
491 | } |
492 | ||
cd554f2c | 493 | static int print_vlan_stats(struct nlmsghdr *n, void *arg) |
7abf5de6 NA |
494 | { |
495 | struct if_stats_msg *ifsm = NLMSG_DATA(n); | |
496 | struct rtattr *tb[IFLA_STATS_MAX+1]; | |
497 | int len = n->nlmsg_len; | |
498 | FILE *fp = arg; | |
499 | ||
500 | len -= NLMSG_LENGTH(sizeof(*ifsm)); | |
501 | if (len < 0) { | |
502 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
503 | return -1; | |
504 | } | |
505 | ||
506 | if (filter_index && filter_index != ifsm->ifindex) | |
507 | return 0; | |
508 | ||
509 | parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len); | |
510 | ||
511 | /* We have to check if any of the two attrs are usable */ | |
512 | if (tb[IFLA_STATS_LINK_XSTATS]) | |
c7c1a1ef | 513 | print_vlan_stats_attr(tb[IFLA_STATS_LINK_XSTATS], |
7abf5de6 NA |
514 | ifsm->ifindex); |
515 | ||
516 | if (tb[IFLA_STATS_LINK_XSTATS_SLAVE]) | |
c7c1a1ef | 517 | print_vlan_stats_attr(tb[IFLA_STATS_LINK_XSTATS_SLAVE], |
7abf5de6 NA |
518 | ifsm->ifindex); |
519 | ||
520 | fflush(fp); | |
521 | return 0; | |
522 | } | |
523 | ||
955a20be | 524 | static int vlan_show(int argc, char **argv, int subject) |
9eff0e5c VY |
525 | { |
526 | char *filter_dev = NULL; | |
8652eeb3 | 527 | int ret = 0; |
9eff0e5c VY |
528 | |
529 | while (argc > 0) { | |
530 | if (strcmp(*argv, "dev") == 0) { | |
531 | NEXT_ARG(); | |
532 | if (filter_dev) | |
533 | duparg("dev", *argv); | |
534 | filter_dev = *argv; | |
5a2d0201 NA |
535 | } else if (strcmp(*argv, "vid") == 0) { |
536 | NEXT_ARG(); | |
537 | if (filter_vlan) | |
538 | duparg("vid", *argv); | |
539 | filter_vlan = atoi(*argv); | |
9eff0e5c VY |
540 | } |
541 | argc--; argv++; | |
542 | } | |
543 | ||
544 | if (filter_dev) { | |
7a14358b | 545 | filter_index = ll_name_to_index(filter_dev); |
fe99adbc SP |
546 | if (!filter_index) |
547 | return nodev(filter_dev); | |
9eff0e5c VY |
548 | } |
549 | ||
c7c1a1ef SH |
550 | new_json_obj(json); |
551 | ||
7abf5de6 | 552 | if (!show_stats) { |
31ae2912 | 553 | if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE, |
7abf5de6 | 554 | (compress_vlans ? |
01842eb5 SH |
555 | RTEXT_FILTER_BRVLAN_COMPRESSED : |
556 | RTEXT_FILTER_BRVLAN)) < 0) { | |
43b0b6ec | 557 | perror("Cannot send dump request"); |
7abf5de6 NA |
558 | exit(1); |
559 | } | |
01842eb5 | 560 | |
c7c1a1ef | 561 | if (!is_json_context()) { |
e0c457b1 BP |
562 | printf("%-" __stringify(IFNAMSIZ) "s %-" |
563 | __stringify(VLAN_ID_LEN) "s", "port", | |
564 | "vlan-id"); | |
955a20be | 565 | if (subject == VLAN_SHOW_TUNNELINFO) |
e0c457b1 | 566 | printf(" tunnel-id"); |
955a20be | 567 | printf("\n"); |
7abf5de6 | 568 | } |
9eff0e5c | 569 | |
955a20be | 570 | ret = rtnl_dump_filter(&rth, print_vlan, &subject); |
8652eeb3 | 571 | if (ret < 0) { |
91b1b49e | 572 | fprintf(stderr, "Dump terminated\n"); |
d82a49ce RP |
573 | exit(1); |
574 | } | |
d82a49ce | 575 | } else { |
7abf5de6 | 576 | __u32 filt_mask; |
d82a49ce | 577 | |
7abf5de6 | 578 | filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS); |
56eeeda9 | 579 | if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) { |
43b0b6ec | 580 | perror("Cannot send dump request"); |
7abf5de6 NA |
581 | exit(1); |
582 | } | |
583 | ||
c7c1a1ef | 584 | if (!is_json_context()) |
e0c457b1 BP |
585 | printf("%-" __stringify(IFNAMSIZ) "s vlan-id\n", |
586 | "port"); | |
c7c1a1ef | 587 | |
7abf5de6 NA |
588 | if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) { |
589 | fprintf(stderr, "Dump terminated\n"); | |
590 | exit(1); | |
591 | } | |
592 | ||
593 | filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE); | |
56eeeda9 | 594 | if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) { |
43b0b6ec | 595 | perror("Cannot send slave dump request"); |
7abf5de6 NA |
596 | exit(1); |
597 | } | |
598 | ||
599 | if (rtnl_dump_filter(&rth, print_vlan_stats, stdout) < 0) { | |
600 | fprintf(stderr, "Dump terminated\n"); | |
601 | exit(1); | |
602 | } | |
9eff0e5c VY |
603 | } |
604 | ||
c7c1a1ef SH |
605 | delete_json_obj(); |
606 | fflush(stdout); | |
9eff0e5c VY |
607 | return 0; |
608 | } | |
609 | ||
0f362674 | 610 | void print_vlan_info(struct rtattr *tb, int ifindex) |
b97c679c RM |
611 | { |
612 | struct rtattr *i, *list = tb; | |
613 | int rem = RTA_PAYLOAD(list); | |
614 | __u16 last_vid_start = 0; | |
b262a9be | 615 | bool opened = false; |
b97c679c RM |
616 | |
617 | for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { | |
618 | struct bridge_vlan_info *vinfo; | |
619 | int vcheck_ret; | |
620 | ||
621 | if (i->rta_type != IFLA_BRIDGE_VLAN_INFO) | |
622 | continue; | |
623 | ||
624 | vinfo = RTA_DATA(i); | |
625 | ||
626 | if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) | |
627 | last_vid_start = vinfo->vid; | |
8652eeb3 | 628 | vcheck_ret = filter_vlan_check(vinfo->vid, vinfo->flags); |
b97c679c RM |
629 | if (vcheck_ret == -1) |
630 | break; | |
631 | else if (vcheck_ret == 0) | |
632 | continue; | |
633 | ||
b262a9be | 634 | if (!opened) { |
e0c457b1 | 635 | open_vlan_port(ifindex, VLAN_SHOW_VLAN); |
b262a9be | 636 | opened = true; |
e0c457b1 BP |
637 | } else { |
638 | print_string(PRINT_FP, NULL, "%-" | |
639 | __stringify(IFNAMSIZ) "s ", ""); | |
b262a9be BP |
640 | } |
641 | ||
c7c1a1ef SH |
642 | open_json_object(NULL); |
643 | print_range("vlan", last_vid_start, vinfo->vid); | |
b97c679c | 644 | |
c7c1a1ef SH |
645 | print_vlan_flags(vinfo->flags); |
646 | close_json_object(); | |
0501fe73 | 647 | print_nl(); |
b97c679c | 648 | } |
b262a9be BP |
649 | |
650 | if (opened) | |
651 | close_vlan_port(); | |
b97c679c RM |
652 | } |
653 | ||
9eff0e5c VY |
654 | int do_vlan(int argc, char **argv) |
655 | { | |
656 | ll_init_map(&rth); | |
657 | ||
658 | if (argc > 0) { | |
659 | if (matches(*argv, "add") == 0) | |
660 | return vlan_modify(RTM_SETLINK, argc-1, argv+1); | |
661 | if (matches(*argv, "delete") == 0) | |
662 | return vlan_modify(RTM_DELLINK, argc-1, argv+1); | |
663 | if (matches(*argv, "show") == 0 || | |
664 | matches(*argv, "lst") == 0 || | |
665 | matches(*argv, "list") == 0) | |
955a20be | 666 | return vlan_show(argc-1, argv+1, VLAN_SHOW_VLAN); |
8652eeb3 | 667 | if (matches(*argv, "tunnelshow") == 0) { |
955a20be | 668 | return vlan_show(argc-1, argv+1, VLAN_SHOW_TUNNELINFO); |
8652eeb3 | 669 | } |
9eff0e5c VY |
670 | if (matches(*argv, "help") == 0) |
671 | usage(); | |
7abf5de6 | 672 | } else { |
955a20be | 673 | return vlan_show(0, NULL, VLAN_SHOW_VLAN); |
7abf5de6 | 674 | } |
9eff0e5c | 675 | |
296cee6f | 676 | fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vlan help\".\n", *argv); |
9eff0e5c VY |
677 | exit(-1); |
678 | } |