]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * iplink_bridge.c Bridge device support | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | * | |
9 | * Authors: Jiri Pirko <jiri@resnulli.us> | |
10 | */ | |
11 | ||
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include <string.h> | |
15 | #include <netinet/in.h> | |
16 | #include <netinet/ether.h> | |
17 | #include <linux/if_link.h> | |
18 | #include <linux/if_bridge.h> | |
19 | #include <net/if.h> | |
20 | ||
21 | #include "rt_names.h" | |
22 | #include "utils.h" | |
23 | #include "ip_common.h" | |
24 | ||
25 | static unsigned int xstats_print_attr; | |
26 | static int filter_index; | |
27 | ||
28 | static void print_explain(FILE *f) | |
29 | { | |
30 | fprintf(f, | |
31 | "Usage: ... bridge [ fdb_flush ]\n" | |
32 | " [ forward_delay FORWARD_DELAY ]\n" | |
33 | " [ hello_time HELLO_TIME ]\n" | |
34 | " [ max_age MAX_AGE ]\n" | |
35 | " [ ageing_time AGEING_TIME ]\n" | |
36 | " [ stp_state STP_STATE ]\n" | |
37 | " [ priority PRIORITY ]\n" | |
38 | " [ group_fwd_mask MASK ]\n" | |
39 | " [ group_address ADDRESS ]\n" | |
40 | " [ vlan_filtering VLAN_FILTERING ]\n" | |
41 | " [ vlan_protocol VLAN_PROTOCOL ]\n" | |
42 | " [ vlan_default_pvid VLAN_DEFAULT_PVID ]\n" | |
43 | " [ vlan_stats_enabled VLAN_STATS_ENABLED ]\n" | |
44 | " [ vlan_stats_per_port VLAN_STATS_PER_PORT ]\n" | |
45 | " [ mcast_snooping MULTICAST_SNOOPING ]\n" | |
46 | " [ mcast_router MULTICAST_ROUTER ]\n" | |
47 | " [ mcast_query_use_ifaddr MCAST_QUERY_USE_IFADDR ]\n" | |
48 | " [ mcast_querier MULTICAST_QUERIER ]\n" | |
49 | " [ mcast_hash_elasticity HASH_ELASTICITY ]\n" | |
50 | " [ mcast_hash_max HASH_MAX ]\n" | |
51 | " [ mcast_last_member_count LAST_MEMBER_COUNT ]\n" | |
52 | " [ mcast_startup_query_count STARTUP_QUERY_COUNT ]\n" | |
53 | " [ mcast_last_member_interval LAST_MEMBER_INTERVAL ]\n" | |
54 | " [ mcast_membership_interval MEMBERSHIP_INTERVAL ]\n" | |
55 | " [ mcast_querier_interval QUERIER_INTERVAL ]\n" | |
56 | " [ mcast_query_interval QUERY_INTERVAL ]\n" | |
57 | " [ mcast_query_response_interval QUERY_RESPONSE_INTERVAL ]\n" | |
58 | " [ mcast_startup_query_interval STARTUP_QUERY_INTERVAL ]\n" | |
59 | " [ mcast_stats_enabled MCAST_STATS_ENABLED ]\n" | |
60 | " [ mcast_igmp_version IGMP_VERSION ]\n" | |
61 | " [ mcast_mld_version MLD_VERSION ]\n" | |
62 | " [ nf_call_iptables NF_CALL_IPTABLES ]\n" | |
63 | " [ nf_call_ip6tables NF_CALL_IP6TABLES ]\n" | |
64 | " [ nf_call_arptables NF_CALL_ARPTABLES ]\n" | |
65 | "\n" | |
66 | "Where: VLAN_PROTOCOL := { 802.1Q | 802.1ad }\n" | |
67 | ); | |
68 | } | |
69 | ||
70 | static void explain(void) | |
71 | { | |
72 | print_explain(stderr); | |
73 | } | |
74 | ||
75 | void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len) | |
76 | { | |
77 | char eaddr[32]; | |
78 | ||
79 | ether_ntoa_r((const struct ether_addr *)id->addr, eaddr); | |
80 | snprintf(buf, len, "%.2x%.2x.%s", id->prio[0], id->prio[1], eaddr); | |
81 | } | |
82 | ||
83 | static int bridge_parse_opt(struct link_util *lu, int argc, char **argv, | |
84 | struct nlmsghdr *n) | |
85 | { | |
86 | __u32 val; | |
87 | ||
88 | while (argc > 0) { | |
89 | if (matches(*argv, "forward_delay") == 0) { | |
90 | NEXT_ARG(); | |
91 | if (get_u32(&val, *argv, 0)) | |
92 | invarg("invalid forward_delay", *argv); | |
93 | ||
94 | addattr32(n, 1024, IFLA_BR_FORWARD_DELAY, val); | |
95 | } else if (matches(*argv, "hello_time") == 0) { | |
96 | NEXT_ARG(); | |
97 | if (get_u32(&val, *argv, 0)) | |
98 | invarg("invalid hello_time", *argv); | |
99 | ||
100 | addattr32(n, 1024, IFLA_BR_HELLO_TIME, val); | |
101 | } else if (matches(*argv, "max_age") == 0) { | |
102 | NEXT_ARG(); | |
103 | if (get_u32(&val, *argv, 0)) | |
104 | invarg("invalid max_age", *argv); | |
105 | ||
106 | addattr32(n, 1024, IFLA_BR_MAX_AGE, val); | |
107 | } else if (matches(*argv, "ageing_time") == 0) { | |
108 | NEXT_ARG(); | |
109 | if (get_u32(&val, *argv, 0)) | |
110 | invarg("invalid ageing_time", *argv); | |
111 | ||
112 | addattr32(n, 1024, IFLA_BR_AGEING_TIME, val); | |
113 | } else if (matches(*argv, "stp_state") == 0) { | |
114 | NEXT_ARG(); | |
115 | if (get_u32(&val, *argv, 0)) | |
116 | invarg("invalid stp_state", *argv); | |
117 | ||
118 | addattr32(n, 1024, IFLA_BR_STP_STATE, val); | |
119 | } else if (matches(*argv, "priority") == 0) { | |
120 | __u16 prio; | |
121 | ||
122 | NEXT_ARG(); | |
123 | if (get_u16(&prio, *argv, 0)) | |
124 | invarg("invalid priority", *argv); | |
125 | ||
126 | addattr16(n, 1024, IFLA_BR_PRIORITY, prio); | |
127 | } else if (matches(*argv, "vlan_filtering") == 0) { | |
128 | __u8 vlan_filter; | |
129 | ||
130 | NEXT_ARG(); | |
131 | if (get_u8(&vlan_filter, *argv, 0)) | |
132 | invarg("invalid vlan_filtering", *argv); | |
133 | ||
134 | addattr8(n, 1024, IFLA_BR_VLAN_FILTERING, vlan_filter); | |
135 | } else if (matches(*argv, "vlan_protocol") == 0) { | |
136 | __u16 vlan_proto; | |
137 | ||
138 | NEXT_ARG(); | |
139 | if (ll_proto_a2n(&vlan_proto, *argv)) | |
140 | invarg("invalid vlan_protocol", *argv); | |
141 | ||
142 | addattr16(n, 1024, IFLA_BR_VLAN_PROTOCOL, vlan_proto); | |
143 | } else if (matches(*argv, "group_fwd_mask") == 0) { | |
144 | __u16 fwd_mask; | |
145 | ||
146 | NEXT_ARG(); | |
147 | if (get_u16(&fwd_mask, *argv, 0)) | |
148 | invarg("invalid group_fwd_mask", *argv); | |
149 | ||
150 | addattr16(n, 1024, IFLA_BR_GROUP_FWD_MASK, fwd_mask); | |
151 | } else if (matches(*argv, "group_address") == 0) { | |
152 | char llabuf[32]; | |
153 | int len; | |
154 | ||
155 | NEXT_ARG(); | |
156 | len = ll_addr_a2n(llabuf, sizeof(llabuf), *argv); | |
157 | if (len < 0) | |
158 | return -1; | |
159 | addattr_l(n, 1024, IFLA_BR_GROUP_ADDR, llabuf, len); | |
160 | } else if (matches(*argv, "fdb_flush") == 0) { | |
161 | addattr(n, 1024, IFLA_BR_FDB_FLUSH); | |
162 | } else if (matches(*argv, "vlan_default_pvid") == 0) { | |
163 | __u16 default_pvid; | |
164 | ||
165 | NEXT_ARG(); | |
166 | if (get_u16(&default_pvid, *argv, 0)) | |
167 | invarg("invalid vlan_default_pvid", *argv); | |
168 | ||
169 | addattr16(n, 1024, IFLA_BR_VLAN_DEFAULT_PVID, | |
170 | default_pvid); | |
171 | } else if (matches(*argv, "vlan_stats_enabled") == 0) { | |
172 | __u8 vlan_stats_enabled; | |
173 | ||
174 | NEXT_ARG(); | |
175 | if (get_u8(&vlan_stats_enabled, *argv, 0)) | |
176 | invarg("invalid vlan_stats_enabled", *argv); | |
177 | addattr8(n, 1024, IFLA_BR_VLAN_STATS_ENABLED, | |
178 | vlan_stats_enabled); | |
179 | } else if (matches(*argv, "vlan_stats_per_port") == 0) { | |
180 | __u8 vlan_stats_per_port; | |
181 | ||
182 | NEXT_ARG(); | |
183 | if (get_u8(&vlan_stats_per_port, *argv, 0)) | |
184 | invarg("invalid vlan_stats_per_port", *argv); | |
185 | addattr8(n, 1024, IFLA_BR_VLAN_STATS_PER_PORT, | |
186 | vlan_stats_per_port); | |
187 | } else if (matches(*argv, "mcast_router") == 0) { | |
188 | __u8 mcast_router; | |
189 | ||
190 | NEXT_ARG(); | |
191 | if (get_u8(&mcast_router, *argv, 0)) | |
192 | invarg("invalid mcast_router", *argv); | |
193 | ||
194 | addattr8(n, 1024, IFLA_BR_MCAST_ROUTER, mcast_router); | |
195 | } else if (matches(*argv, "mcast_snooping") == 0) { | |
196 | __u8 mcast_snoop; | |
197 | ||
198 | NEXT_ARG(); | |
199 | if (get_u8(&mcast_snoop, *argv, 0)) | |
200 | invarg("invalid mcast_snooping", *argv); | |
201 | ||
202 | addattr8(n, 1024, IFLA_BR_MCAST_SNOOPING, mcast_snoop); | |
203 | } else if (matches(*argv, "mcast_query_use_ifaddr") == 0) { | |
204 | __u8 mcast_qui; | |
205 | ||
206 | NEXT_ARG(); | |
207 | if (get_u8(&mcast_qui, *argv, 0)) | |
208 | invarg("invalid mcast_query_use_ifaddr", | |
209 | *argv); | |
210 | ||
211 | addattr8(n, 1024, IFLA_BR_MCAST_QUERY_USE_IFADDR, | |
212 | mcast_qui); | |
213 | } else if (matches(*argv, "mcast_querier") == 0) { | |
214 | __u8 mcast_querier; | |
215 | ||
216 | NEXT_ARG(); | |
217 | if (get_u8(&mcast_querier, *argv, 0)) | |
218 | invarg("invalid mcast_querier", *argv); | |
219 | ||
220 | addattr8(n, 1024, IFLA_BR_MCAST_QUERIER, mcast_querier); | |
221 | } else if (matches(*argv, "mcast_hash_elasticity") == 0) { | |
222 | __u32 mcast_hash_el; | |
223 | ||
224 | NEXT_ARG(); | |
225 | if (get_u32(&mcast_hash_el, *argv, 0)) | |
226 | invarg("invalid mcast_hash_elasticity", | |
227 | *argv); | |
228 | ||
229 | addattr32(n, 1024, IFLA_BR_MCAST_HASH_ELASTICITY, | |
230 | mcast_hash_el); | |
231 | } else if (matches(*argv, "mcast_hash_max") == 0) { | |
232 | __u32 mcast_hash_max; | |
233 | ||
234 | NEXT_ARG(); | |
235 | if (get_u32(&mcast_hash_max, *argv, 0)) | |
236 | invarg("invalid mcast_hash_max", *argv); | |
237 | ||
238 | addattr32(n, 1024, IFLA_BR_MCAST_HASH_MAX, | |
239 | mcast_hash_max); | |
240 | } else if (matches(*argv, "mcast_last_member_count") == 0) { | |
241 | __u32 mcast_lmc; | |
242 | ||
243 | NEXT_ARG(); | |
244 | if (get_u32(&mcast_lmc, *argv, 0)) | |
245 | invarg("invalid mcast_last_member_count", | |
246 | *argv); | |
247 | ||
248 | addattr32(n, 1024, IFLA_BR_MCAST_LAST_MEMBER_CNT, | |
249 | mcast_lmc); | |
250 | } else if (matches(*argv, "mcast_startup_query_count") == 0) { | |
251 | __u32 mcast_sqc; | |
252 | ||
253 | NEXT_ARG(); | |
254 | if (get_u32(&mcast_sqc, *argv, 0)) | |
255 | invarg("invalid mcast_startup_query_count", | |
256 | *argv); | |
257 | ||
258 | addattr32(n, 1024, IFLA_BR_MCAST_STARTUP_QUERY_CNT, | |
259 | mcast_sqc); | |
260 | } else if (matches(*argv, "mcast_last_member_interval") == 0) { | |
261 | __u64 mcast_last_member_intvl; | |
262 | ||
263 | NEXT_ARG(); | |
264 | if (get_u64(&mcast_last_member_intvl, *argv, 0)) | |
265 | invarg("invalid mcast_last_member_interval", | |
266 | *argv); | |
267 | ||
268 | addattr64(n, 1024, IFLA_BR_MCAST_LAST_MEMBER_INTVL, | |
269 | mcast_last_member_intvl); | |
270 | } else if (matches(*argv, "mcast_membership_interval") == 0) { | |
271 | __u64 mcast_membership_intvl; | |
272 | ||
273 | NEXT_ARG(); | |
274 | if (get_u64(&mcast_membership_intvl, *argv, 0)) | |
275 | invarg("invalid mcast_membership_interval", | |
276 | *argv); | |
277 | ||
278 | addattr64(n, 1024, IFLA_BR_MCAST_MEMBERSHIP_INTVL, | |
279 | mcast_membership_intvl); | |
280 | } else if (matches(*argv, "mcast_querier_interval") == 0) { | |
281 | __u64 mcast_querier_intvl; | |
282 | ||
283 | NEXT_ARG(); | |
284 | if (get_u64(&mcast_querier_intvl, *argv, 0)) | |
285 | invarg("invalid mcast_querier_interval", | |
286 | *argv); | |
287 | ||
288 | addattr64(n, 1024, IFLA_BR_MCAST_QUERIER_INTVL, | |
289 | mcast_querier_intvl); | |
290 | } else if (matches(*argv, "mcast_query_interval") == 0) { | |
291 | __u64 mcast_query_intvl; | |
292 | ||
293 | NEXT_ARG(); | |
294 | if (get_u64(&mcast_query_intvl, *argv, 0)) | |
295 | invarg("invalid mcast_query_interval", | |
296 | *argv); | |
297 | ||
298 | addattr64(n, 1024, IFLA_BR_MCAST_QUERY_INTVL, | |
299 | mcast_query_intvl); | |
300 | } else if (!matches(*argv, "mcast_query_response_interval")) { | |
301 | __u64 mcast_query_resp_intvl; | |
302 | ||
303 | NEXT_ARG(); | |
304 | if (get_u64(&mcast_query_resp_intvl, *argv, 0)) | |
305 | invarg("invalid mcast_query_response_interval", | |
306 | *argv); | |
307 | ||
308 | addattr64(n, 1024, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, | |
309 | mcast_query_resp_intvl); | |
310 | } else if (!matches(*argv, "mcast_startup_query_interval")) { | |
311 | __u64 mcast_startup_query_intvl; | |
312 | ||
313 | NEXT_ARG(); | |
314 | if (get_u64(&mcast_startup_query_intvl, *argv, 0)) | |
315 | invarg("invalid mcast_startup_query_interval", | |
316 | *argv); | |
317 | ||
318 | addattr64(n, 1024, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, | |
319 | mcast_startup_query_intvl); | |
320 | } else if (matches(*argv, "mcast_stats_enabled") == 0) { | |
321 | __u8 mcast_stats_enabled; | |
322 | ||
323 | NEXT_ARG(); | |
324 | if (get_u8(&mcast_stats_enabled, *argv, 0)) | |
325 | invarg("invalid mcast_stats_enabled", *argv); | |
326 | addattr8(n, 1024, IFLA_BR_MCAST_STATS_ENABLED, | |
327 | mcast_stats_enabled); | |
328 | } else if (matches(*argv, "mcast_igmp_version") == 0) { | |
329 | __u8 igmp_version; | |
330 | ||
331 | NEXT_ARG(); | |
332 | if (get_u8(&igmp_version, *argv, 0)) | |
333 | invarg("invalid mcast_igmp_version", *argv); | |
334 | addattr8(n, 1024, IFLA_BR_MCAST_IGMP_VERSION, | |
335 | igmp_version); | |
336 | } else if (matches(*argv, "mcast_mld_version") == 0) { | |
337 | __u8 mld_version; | |
338 | ||
339 | NEXT_ARG(); | |
340 | if (get_u8(&mld_version, *argv, 0)) | |
341 | invarg("invalid mcast_mld_version", *argv); | |
342 | addattr8(n, 1024, IFLA_BR_MCAST_MLD_VERSION, | |
343 | mld_version); | |
344 | } else if (matches(*argv, "nf_call_iptables") == 0) { | |
345 | __u8 nf_call_ipt; | |
346 | ||
347 | NEXT_ARG(); | |
348 | if (get_u8(&nf_call_ipt, *argv, 0)) | |
349 | invarg("invalid nf_call_iptables", *argv); | |
350 | ||
351 | addattr8(n, 1024, IFLA_BR_NF_CALL_IPTABLES, | |
352 | nf_call_ipt); | |
353 | } else if (matches(*argv, "nf_call_ip6tables") == 0) { | |
354 | __u8 nf_call_ip6t; | |
355 | ||
356 | NEXT_ARG(); | |
357 | if (get_u8(&nf_call_ip6t, *argv, 0)) | |
358 | invarg("invalid nf_call_ip6tables", *argv); | |
359 | ||
360 | addattr8(n, 1024, IFLA_BR_NF_CALL_IP6TABLES, | |
361 | nf_call_ip6t); | |
362 | } else if (matches(*argv, "nf_call_arptables") == 0) { | |
363 | __u8 nf_call_arpt; | |
364 | ||
365 | NEXT_ARG(); | |
366 | if (get_u8(&nf_call_arpt, *argv, 0)) | |
367 | invarg("invalid nf_call_arptables", *argv); | |
368 | ||
369 | addattr8(n, 1024, IFLA_BR_NF_CALL_ARPTABLES, | |
370 | nf_call_arpt); | |
371 | } else if (matches(*argv, "help") == 0) { | |
372 | explain(); | |
373 | return -1; | |
374 | } else { | |
375 | fprintf(stderr, "bridge: unknown command \"%s\"?\n", *argv); | |
376 | explain(); | |
377 | return -1; | |
378 | } | |
379 | argc--, argv++; | |
380 | } | |
381 | ||
382 | return 0; | |
383 | } | |
384 | ||
385 | static void _bridge_print_timer(FILE *f, | |
386 | const char *attr, | |
387 | struct rtattr *timer) | |
388 | { | |
389 | struct timeval tv; | |
390 | ||
391 | __jiffies_to_tv(&tv, rta_getattr_u64(timer)); | |
392 | if (is_json_context()) { | |
393 | json_writer_t *jw = get_json_writer(); | |
394 | ||
395 | jsonw_name(jw, attr); | |
396 | jsonw_printf(jw, "%i.%.2i", | |
397 | (int)tv.tv_sec, | |
398 | (int)tv.tv_usec / 10000); | |
399 | } else { | |
400 | fprintf(f, "%s %4i.%.2i ", attr, (int)tv.tv_sec, | |
401 | (int)tv.tv_usec / 10000); | |
402 | } | |
403 | } | |
404 | ||
405 | static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) | |
406 | { | |
407 | if (!tb) | |
408 | return; | |
409 | ||
410 | if (tb[IFLA_BR_FORWARD_DELAY]) | |
411 | print_uint(PRINT_ANY, | |
412 | "forward_delay", | |
413 | "forward_delay %u ", | |
414 | rta_getattr_u32(tb[IFLA_BR_FORWARD_DELAY])); | |
415 | ||
416 | if (tb[IFLA_BR_HELLO_TIME]) | |
417 | print_uint(PRINT_ANY, | |
418 | "hello_time", | |
419 | "hello_time %u ", | |
420 | rta_getattr_u32(tb[IFLA_BR_HELLO_TIME])); | |
421 | ||
422 | if (tb[IFLA_BR_MAX_AGE]) | |
423 | print_uint(PRINT_ANY, | |
424 | "max_age", | |
425 | "max_age %u ", | |
426 | rta_getattr_u32(tb[IFLA_BR_MAX_AGE])); | |
427 | ||
428 | if (tb[IFLA_BR_AGEING_TIME]) | |
429 | print_uint(PRINT_ANY, | |
430 | "ageing_time", | |
431 | "ageing_time %u ", | |
432 | rta_getattr_u32(tb[IFLA_BR_AGEING_TIME])); | |
433 | ||
434 | if (tb[IFLA_BR_STP_STATE]) | |
435 | print_uint(PRINT_ANY, | |
436 | "stp_state", | |
437 | "stp_state %u ", | |
438 | rta_getattr_u32(tb[IFLA_BR_STP_STATE])); | |
439 | ||
440 | if (tb[IFLA_BR_PRIORITY]) | |
441 | print_uint(PRINT_ANY, | |
442 | "priority", | |
443 | "priority %u ", | |
444 | rta_getattr_u16(tb[IFLA_BR_PRIORITY])); | |
445 | ||
446 | if (tb[IFLA_BR_VLAN_FILTERING]) | |
447 | print_uint(PRINT_ANY, | |
448 | "vlan_filtering", | |
449 | "vlan_filtering %u ", | |
450 | rta_getattr_u8(tb[IFLA_BR_VLAN_FILTERING])); | |
451 | ||
452 | if (tb[IFLA_BR_VLAN_PROTOCOL]) { | |
453 | SPRINT_BUF(b1); | |
454 | ||
455 | print_string(PRINT_ANY, | |
456 | "vlan_protocol", | |
457 | "vlan_protocol %s ", | |
458 | ll_proto_n2a(rta_getattr_u16(tb[IFLA_BR_VLAN_PROTOCOL]), | |
459 | b1, sizeof(b1))); | |
460 | } | |
461 | ||
462 | if (tb[IFLA_BR_BRIDGE_ID]) { | |
463 | char bridge_id[32]; | |
464 | ||
465 | br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), bridge_id, | |
466 | sizeof(bridge_id)); | |
467 | print_string(PRINT_ANY, | |
468 | "bridge_id", | |
469 | "bridge_id %s ", | |
470 | bridge_id); | |
471 | } | |
472 | ||
473 | if (tb[IFLA_BR_ROOT_ID]) { | |
474 | char root_id[32]; | |
475 | ||
476 | br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), root_id, | |
477 | sizeof(root_id)); | |
478 | print_string(PRINT_ANY, | |
479 | "root_id", | |
480 | "designated_root %s ", | |
481 | root_id); | |
482 | } | |
483 | ||
484 | if (tb[IFLA_BR_ROOT_PORT]) | |
485 | print_uint(PRINT_ANY, | |
486 | "root_port", | |
487 | "root_port %u ", | |
488 | rta_getattr_u16(tb[IFLA_BR_ROOT_PORT])); | |
489 | ||
490 | if (tb[IFLA_BR_ROOT_PATH_COST]) | |
491 | print_uint(PRINT_ANY, | |
492 | "root_path_cost", | |
493 | "root_path_cost %u ", | |
494 | rta_getattr_u32(tb[IFLA_BR_ROOT_PATH_COST])); | |
495 | ||
496 | if (tb[IFLA_BR_TOPOLOGY_CHANGE]) | |
497 | print_uint(PRINT_ANY, | |
498 | "topology_change", | |
499 | "topology_change %u ", | |
500 | rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE])); | |
501 | ||
502 | if (tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED]) | |
503 | print_uint(PRINT_ANY, | |
504 | "topology_change_detected", | |
505 | "topology_change_detected %u ", | |
506 | rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED])); | |
507 | ||
508 | if (tb[IFLA_BR_HELLO_TIMER]) | |
509 | _bridge_print_timer(f, "hello_timer", tb[IFLA_BR_HELLO_TIMER]); | |
510 | ||
511 | if (tb[IFLA_BR_TCN_TIMER]) | |
512 | _bridge_print_timer(f, "tcn_timer", tb[IFLA_BR_TCN_TIMER]); | |
513 | ||
514 | if (tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]) | |
515 | _bridge_print_timer(f, "topology_change_timer", | |
516 | tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]); | |
517 | ||
518 | if (tb[IFLA_BR_GC_TIMER]) | |
519 | _bridge_print_timer(f, "gc_timer", tb[IFLA_BR_GC_TIMER]); | |
520 | ||
521 | if (tb[IFLA_BR_VLAN_DEFAULT_PVID]) | |
522 | print_uint(PRINT_ANY, | |
523 | "vlan_default_pvid", | |
524 | "vlan_default_pvid %u ", | |
525 | rta_getattr_u16(tb[IFLA_BR_VLAN_DEFAULT_PVID])); | |
526 | ||
527 | if (tb[IFLA_BR_VLAN_STATS_ENABLED]) | |
528 | print_uint(PRINT_ANY, | |
529 | "vlan_stats_enabled", | |
530 | "vlan_stats_enabled %u ", | |
531 | rta_getattr_u8(tb[IFLA_BR_VLAN_STATS_ENABLED])); | |
532 | ||
533 | if (tb[IFLA_BR_VLAN_STATS_PER_PORT]) | |
534 | print_uint(PRINT_ANY, | |
535 | "vlan_stats_per_port", | |
536 | "vlan_stats_per_port %u ", | |
537 | rta_getattr_u8(tb[IFLA_BR_VLAN_STATS_PER_PORT])); | |
538 | ||
539 | if (tb[IFLA_BR_GROUP_FWD_MASK]) | |
540 | print_0xhex(PRINT_ANY, | |
541 | "group_fwd_mask", | |
542 | "group_fwd_mask %#llx ", | |
543 | rta_getattr_u16(tb[IFLA_BR_GROUP_FWD_MASK])); | |
544 | ||
545 | if (tb[IFLA_BR_GROUP_ADDR]) { | |
546 | SPRINT_BUF(mac); | |
547 | ||
548 | print_string(PRINT_ANY, | |
549 | "group_addr", | |
550 | "group_address %s ", | |
551 | ll_addr_n2a(RTA_DATA(tb[IFLA_BR_GROUP_ADDR]), | |
552 | RTA_PAYLOAD(tb[IFLA_BR_GROUP_ADDR]), | |
553 | 1 /*ARPHDR_ETHER*/, mac, sizeof(mac))); | |
554 | } | |
555 | ||
556 | if (tb[IFLA_BR_MCAST_SNOOPING]) | |
557 | print_uint(PRINT_ANY, | |
558 | "mcast_snooping", | |
559 | "mcast_snooping %u ", | |
560 | rta_getattr_u8(tb[IFLA_BR_MCAST_SNOOPING])); | |
561 | ||
562 | if (tb[IFLA_BR_MCAST_ROUTER]) | |
563 | print_uint(PRINT_ANY, | |
564 | "mcast_router", | |
565 | "mcast_router %u ", | |
566 | rta_getattr_u8(tb[IFLA_BR_MCAST_ROUTER])); | |
567 | ||
568 | if (tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]) | |
569 | print_uint(PRINT_ANY, | |
570 | "mcast_query_use_ifaddr", | |
571 | "mcast_query_use_ifaddr %u ", | |
572 | rta_getattr_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR])); | |
573 | ||
574 | if (tb[IFLA_BR_MCAST_QUERIER]) | |
575 | print_uint(PRINT_ANY, | |
576 | "mcast_querier", | |
577 | "mcast_querier %u ", | |
578 | rta_getattr_u8(tb[IFLA_BR_MCAST_QUERIER])); | |
579 | ||
580 | if (tb[IFLA_BR_MCAST_HASH_ELASTICITY]) | |
581 | print_uint(PRINT_ANY, | |
582 | "mcast_hash_elasticity", | |
583 | "mcast_hash_elasticity %u ", | |
584 | rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_ELASTICITY])); | |
585 | ||
586 | if (tb[IFLA_BR_MCAST_HASH_MAX]) | |
587 | print_uint(PRINT_ANY, | |
588 | "mcast_hash_max", | |
589 | "mcast_hash_max %u ", | |
590 | rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_MAX])); | |
591 | ||
592 | if (tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]) | |
593 | print_uint(PRINT_ANY, | |
594 | "mcast_last_member_cnt", | |
595 | "mcast_last_member_count %u ", | |
596 | rta_getattr_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT])); | |
597 | ||
598 | if (tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]) | |
599 | print_uint(PRINT_ANY, | |
600 | "mcast_startup_query_cnt", | |
601 | "mcast_startup_query_count %u ", | |
602 | rta_getattr_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT])); | |
603 | ||
604 | if (tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]) | |
605 | print_lluint(PRINT_ANY, | |
606 | "mcast_last_member_intvl", | |
607 | "mcast_last_member_interval %llu ", | |
608 | rta_getattr_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL])); | |
609 | ||
610 | if (tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]) | |
611 | print_lluint(PRINT_ANY, | |
612 | "mcast_membership_intvl", | |
613 | "mcast_membership_interval %llu ", | |
614 | rta_getattr_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL])); | |
615 | ||
616 | if (tb[IFLA_BR_MCAST_QUERIER_INTVL]) | |
617 | print_lluint(PRINT_ANY, | |
618 | "mcast_querier_intvl", | |
619 | "mcast_querier_interval %llu ", | |
620 | rta_getattr_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL])); | |
621 | ||
622 | if (tb[IFLA_BR_MCAST_QUERY_INTVL]) | |
623 | print_lluint(PRINT_ANY, | |
624 | "mcast_query_intvl", | |
625 | "mcast_query_interval %llu ", | |
626 | rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_INTVL])); | |
627 | ||
628 | if (tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) | |
629 | print_lluint(PRINT_ANY, | |
630 | "mcast_query_response_intvl", | |
631 | "mcast_query_response_interval %llu ", | |
632 | rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL])); | |
633 | ||
634 | if (tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]) | |
635 | print_lluint(PRINT_ANY, | |
636 | "mcast_startup_query_intvl", | |
637 | "mcast_startup_query_interval %llu ", | |
638 | rta_getattr_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL])); | |
639 | ||
640 | if (tb[IFLA_BR_MCAST_STATS_ENABLED]) | |
641 | print_uint(PRINT_ANY, | |
642 | "mcast_stats_enabled", | |
643 | "mcast_stats_enabled %u ", | |
644 | rta_getattr_u8(tb[IFLA_BR_MCAST_STATS_ENABLED])); | |
645 | ||
646 | if (tb[IFLA_BR_MCAST_IGMP_VERSION]) | |
647 | print_uint(PRINT_ANY, | |
648 | "mcast_igmp_version", | |
649 | "mcast_igmp_version %u ", | |
650 | rta_getattr_u8(tb[IFLA_BR_MCAST_IGMP_VERSION])); | |
651 | ||
652 | if (tb[IFLA_BR_MCAST_MLD_VERSION]) | |
653 | print_uint(PRINT_ANY, | |
654 | "mcast_mld_version", | |
655 | "mcast_mld_version %u ", | |
656 | rta_getattr_u8(tb[IFLA_BR_MCAST_MLD_VERSION])); | |
657 | ||
658 | if (tb[IFLA_BR_NF_CALL_IPTABLES]) | |
659 | print_uint(PRINT_ANY, | |
660 | "nf_call_iptables", | |
661 | "nf_call_iptables %u ", | |
662 | rta_getattr_u8(tb[IFLA_BR_NF_CALL_IPTABLES])); | |
663 | ||
664 | if (tb[IFLA_BR_NF_CALL_IP6TABLES]) | |
665 | print_uint(PRINT_ANY, | |
666 | "nf_call_ip6tables", | |
667 | "nf_call_ip6tables %u ", | |
668 | rta_getattr_u8(tb[IFLA_BR_NF_CALL_IP6TABLES])); | |
669 | ||
670 | if (tb[IFLA_BR_NF_CALL_ARPTABLES]) | |
671 | print_uint(PRINT_ANY, | |
672 | "nf_call_arptables", | |
673 | "nf_call_arptables %u ", | |
674 | rta_getattr_u8(tb[IFLA_BR_NF_CALL_ARPTABLES])); | |
675 | } | |
676 | ||
677 | static void bridge_print_help(struct link_util *lu, int argc, char **argv, | |
678 | FILE *f) | |
679 | { | |
680 | print_explain(f); | |
681 | } | |
682 | ||
683 | static void bridge_print_xstats_help(struct link_util *lu, FILE *f) | |
684 | { | |
685 | fprintf(f, "Usage: ... %s [ igmp ] [ dev DEVICE ]\n", lu->id); | |
686 | } | |
687 | ||
688 | static void bridge_print_stats_attr(struct rtattr *attr, int ifindex) | |
689 | { | |
690 | struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1]; | |
691 | struct br_mcast_stats *mstats; | |
692 | struct rtattr *i, *list; | |
693 | const char *ifname = ""; | |
694 | int rem; | |
695 | ||
696 | parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr), | |
697 | RTA_PAYLOAD(attr)); | |
698 | if (!brtb[LINK_XSTATS_TYPE_BRIDGE]) | |
699 | return; | |
700 | ||
701 | list = brtb[LINK_XSTATS_TYPE_BRIDGE]; | |
702 | rem = RTA_PAYLOAD(list); | |
703 | open_json_object(NULL); | |
704 | ifname = ll_index_to_name(ifindex); | |
705 | print_string(PRINT_ANY, "ifname", "%-16s\n", ifname); | |
706 | for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { | |
707 | if (xstats_print_attr && i->rta_type != xstats_print_attr) | |
708 | continue; | |
709 | switch (i->rta_type) { | |
710 | case BRIDGE_XSTATS_MCAST: | |
711 | mstats = RTA_DATA(i); | |
712 | open_json_object("multicast"); | |
713 | open_json_object("igmp_queries"); | |
714 | print_string(PRINT_FP, NULL, | |
715 | "%-16s IGMP queries:\n", ""); | |
716 | print_string(PRINT_FP, NULL, "%-16s ", ""); | |
717 | print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ", | |
718 | mstats->igmp_v1queries[BR_MCAST_DIR_RX]); | |
719 | print_u64(PRINT_ANY, "rx_v2", "v2 %llu ", | |
720 | mstats->igmp_v2queries[BR_MCAST_DIR_RX]); | |
721 | print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n", | |
722 | mstats->igmp_v3queries[BR_MCAST_DIR_RX]); | |
723 | print_string(PRINT_FP, NULL, "%-16s ", ""); | |
724 | print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ", | |
725 | mstats->igmp_v1queries[BR_MCAST_DIR_TX]); | |
726 | print_u64(PRINT_ANY, "tx_v2", "v2 %llu ", | |
727 | mstats->igmp_v2queries[BR_MCAST_DIR_TX]); | |
728 | print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n", | |
729 | mstats->igmp_v3queries[BR_MCAST_DIR_TX]); | |
730 | close_json_object(); | |
731 | ||
732 | open_json_object("igmp_reports"); | |
733 | print_string(PRINT_FP, NULL, | |
734 | "%-16s IGMP reports:\n", ""); | |
735 | print_string(PRINT_FP, NULL, "%-16s ", ""); | |
736 | print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ", | |
737 | mstats->igmp_v1reports[BR_MCAST_DIR_RX]); | |
738 | print_u64(PRINT_ANY, "rx_v2", "v2 %llu ", | |
739 | mstats->igmp_v2reports[BR_MCAST_DIR_RX]); | |
740 | print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n", | |
741 | mstats->igmp_v3reports[BR_MCAST_DIR_RX]); | |
742 | print_string(PRINT_FP, NULL, "%-16s ", ""); | |
743 | print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ", | |
744 | mstats->igmp_v1reports[BR_MCAST_DIR_TX]); | |
745 | print_u64(PRINT_ANY, "tx_v2", "v2 %llu", | |
746 | mstats->igmp_v2reports[BR_MCAST_DIR_TX]); | |
747 | print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n", | |
748 | mstats->igmp_v3reports[BR_MCAST_DIR_TX]); | |
749 | close_json_object(); | |
750 | ||
751 | open_json_object("igmp_leaves"); | |
752 | print_string(PRINT_FP, NULL, | |
753 | "%-16s IGMP leaves: ", ""); | |
754 | print_u64(PRINT_ANY, "rx", "RX: %llu ", | |
755 | mstats->igmp_leaves[BR_MCAST_DIR_RX]); | |
756 | print_u64(PRINT_ANY, "tx", "TX: %llu\n", | |
757 | mstats->igmp_leaves[BR_MCAST_DIR_TX]); | |
758 | close_json_object(); | |
759 | ||
760 | print_string(PRINT_FP, NULL, | |
761 | "%-16s IGMP parse errors: ", ""); | |
762 | print_u64(PRINT_ANY, "igmp_parse_errors", "%llu\n", | |
763 | mstats->igmp_parse_errors); | |
764 | ||
765 | open_json_object("mld_queries"); | |
766 | print_string(PRINT_FP, NULL, | |
767 | "%-16s MLD queries:\n", ""); | |
768 | print_string(PRINT_FP, NULL, "%-16s ", ""); | |
769 | print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ", | |
770 | mstats->mld_v1queries[BR_MCAST_DIR_RX]); | |
771 | print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n", | |
772 | mstats->mld_v2queries[BR_MCAST_DIR_RX]); | |
773 | print_string(PRINT_FP, NULL, "%-16s ", ""); | |
774 | print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ", | |
775 | mstats->mld_v1queries[BR_MCAST_DIR_TX]); | |
776 | print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n", | |
777 | mstats->mld_v2queries[BR_MCAST_DIR_TX]); | |
778 | close_json_object(); | |
779 | ||
780 | open_json_object("mld_reports"); | |
781 | print_string(PRINT_FP, NULL, | |
782 | "%-16s MLD reports:\n", ""); | |
783 | print_string(PRINT_FP, NULL, "%-16s ", ""); | |
784 | print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ", | |
785 | mstats->mld_v1reports[BR_MCAST_DIR_RX]); | |
786 | print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n", | |
787 | mstats->mld_v2reports[BR_MCAST_DIR_RX]); | |
788 | print_string(PRINT_FP, NULL, "%-16s ", ""); | |
789 | print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ", | |
790 | mstats->mld_v1reports[BR_MCAST_DIR_TX]); | |
791 | print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n", | |
792 | mstats->mld_v2reports[BR_MCAST_DIR_TX]); | |
793 | close_json_object(); | |
794 | ||
795 | open_json_object("mld_leaves"); | |
796 | print_string(PRINT_FP, NULL, | |
797 | "%-16s MLD leaves: ", ""); | |
798 | print_u64(PRINT_ANY, "rx", "RX: %llu ", | |
799 | mstats->mld_leaves[BR_MCAST_DIR_RX]); | |
800 | print_u64(PRINT_ANY, "tx", "TX: %llu\n", | |
801 | mstats->mld_leaves[BR_MCAST_DIR_TX]); | |
802 | close_json_object(); | |
803 | ||
804 | print_string(PRINT_FP, NULL, | |
805 | "%-16s MLD parse errors: ", ""); | |
806 | print_u64(PRINT_ANY, "mld_parse_errors", "%llu\n", | |
807 | mstats->mld_parse_errors); | |
808 | close_json_object(); | |
809 | break; | |
810 | } | |
811 | } | |
812 | close_json_object(); | |
813 | } | |
814 | ||
815 | int bridge_print_xstats(struct nlmsghdr *n, void *arg) | |
816 | { | |
817 | struct if_stats_msg *ifsm = NLMSG_DATA(n); | |
818 | struct rtattr *tb[IFLA_STATS_MAX+1]; | |
819 | int len = n->nlmsg_len; | |
820 | ||
821 | len -= NLMSG_LENGTH(sizeof(*ifsm)); | |
822 | if (len < 0) { | |
823 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
824 | return -1; | |
825 | } | |
826 | if (filter_index && filter_index != ifsm->ifindex) | |
827 | return 0; | |
828 | ||
829 | parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len); | |
830 | if (tb[IFLA_STATS_LINK_XSTATS]) | |
831 | bridge_print_stats_attr(tb[IFLA_STATS_LINK_XSTATS], | |
832 | ifsm->ifindex); | |
833 | ||
834 | if (tb[IFLA_STATS_LINK_XSTATS_SLAVE]) | |
835 | bridge_print_stats_attr(tb[IFLA_STATS_LINK_XSTATS_SLAVE], | |
836 | ifsm->ifindex); | |
837 | ||
838 | return 0; | |
839 | } | |
840 | ||
841 | int bridge_parse_xstats(struct link_util *lu, int argc, char **argv) | |
842 | { | |
843 | while (argc > 0) { | |
844 | if (strcmp(*argv, "igmp") == 0 || strcmp(*argv, "mcast") == 0) { | |
845 | xstats_print_attr = BRIDGE_XSTATS_MCAST; | |
846 | } else if (strcmp(*argv, "dev") == 0) { | |
847 | NEXT_ARG(); | |
848 | filter_index = ll_name_to_index(*argv); | |
849 | if (!filter_index) | |
850 | return nodev(*argv); | |
851 | } else if (strcmp(*argv, "help") == 0) { | |
852 | bridge_print_xstats_help(lu, stdout); | |
853 | exit(0); | |
854 | } else { | |
855 | invarg("unknown attribute", *argv); | |
856 | } | |
857 | argc--; argv++; | |
858 | } | |
859 | ||
860 | return 0; | |
861 | } | |
862 | ||
863 | struct link_util bridge_link_util = { | |
864 | .id = "bridge", | |
865 | .maxattr = IFLA_BR_MAX, | |
866 | .parse_opt = bridge_parse_opt, | |
867 | .print_opt = bridge_print_opt, | |
868 | .print_help = bridge_print_help, | |
869 | .parse_ifla_xstats = bridge_parse_xstats, | |
870 | .print_ifla_xstats = bridge_print_xstats, | |
871 | }; |