]>
Commit | Line | Data |
---|---|---|
28d84b42 JP |
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> | |
60ec0ecf | 15 | #include <netinet/in.h> |
28d84b42 | 16 | #include <linux/if_link.h> |
60ec0ecf | 17 | #include <linux/if_bridge.h> |
70dfb0b8 | 18 | #include <netinet/ether.h> |
60ec0ecf | 19 | #include <net/if.h> |
28d84b42 | 20 | |
1eea5c46 | 21 | #include "rt_names.h" |
28d84b42 JP |
22 | #include "utils.h" |
23 | #include "ip_common.h" | |
24 | ||
60ec0ecf NA |
25 | static unsigned int xstats_print_attr; |
26 | static int filter_index; | |
27 | ||
43367ef7 | 28 | static void print_explain(FILE *f) |
28d84b42 | 29 | { |
43367ef7 | 30 | fprintf(f, |
f3372d62 HL |
31 | "Usage: ... bridge [ fdb_flush ]\n" |
32 | " [ forward_delay FORWARD_DELAY ]\n" | |
28d84b42 JP |
33 | " [ hello_time HELLO_TIME ]\n" |
34 | " [ max_age MAX_AGE ]\n" | |
6c99fb60 | 35 | " [ ageing_time AGEING_TIME ]\n" |
dab04962 | 36 | " [ stp_state STP_STATE ]\n" |
b0197a04 | 37 | " [ priority PRIORITY ]\n" |
8caaf33b | 38 | " [ group_fwd_mask MASK ]\n" |
0a61aa39 | 39 | " [ group_address ADDRESS ]\n" |
e4d456f0 | 40 | " [ vlan_filtering VLAN_FILTERING ]\n" |
1eea5c46 | 41 | " [ vlan_protocol VLAN_PROTOCOL ]\n" |
719832af | 42 | " [ vlan_default_pvid VLAN_DEFAULT_PVID ]\n" |
7d93b567 | 43 | " [ vlan_stats_enabled VLAN_STATS_ENABLED ]\n" |
7ddd2d94 | 44 | " [ mcast_snooping MULTICAST_SNOOPING ]\n" |
963d137c | 45 | " [ mcast_router MULTICAST_ROUTER ]\n" |
449843d1 | 46 | " [ mcast_query_use_ifaddr MCAST_QUERY_USE_IFADDR ]\n" |
0778b741 | 47 | " [ mcast_querier MULTICAST_QUERIER ]\n" |
92c0ef70 | 48 | " [ mcast_hash_elasticity HASH_ELASTICITY ]\n" |
8b9eb7cd | 49 | " [ mcast_hash_max HASH_MAX ]\n" |
fb44cadb | 50 | " [ mcast_last_member_count LAST_MEMBER_COUNT ]\n" |
ceb64866 | 51 | " [ mcast_startup_query_count STARTUP_QUERY_COUNT ]\n" |
10082a25 | 52 | " [ mcast_last_member_interval LAST_MEMBER_INTERVAL ]\n" |
7f3d5592 | 53 | " [ mcast_membership_interval MEMBERSHIP_INTERVAL ]\n" |
1f2244b8 | 54 | " [ mcast_querier_interval QUERIER_INTERVAL ]\n" |
5a32388f | 55 | " [ mcast_query_interval QUERY_INTERVAL ]\n" |
483df11c | 56 | " [ mcast_query_response_interval QUERY_RESPONSE_INTERVAL ]\n" |
178b1806 | 57 | " [ mcast_startup_query_interval STARTUP_QUERY_INTERVAL ]\n" |
6ddad009 | 58 | " [ mcast_stats_enabled MCAST_STATS_ENABLED ]\n" |
19756950 | 59 | " [ mcast_igmp_version IGMP_VERSION ]\n" |
c3f1e3c4 | 60 | " [ mcast_mld_version MLD_VERSION ]\n" |
861c5dae NA |
61 | " [ nf_call_iptables NF_CALL_IPTABLES ]\n" |
62 | " [ nf_call_ip6tables NF_CALL_IP6TABLES ]\n" | |
63 | " [ nf_call_arptables NF_CALL_ARPTABLES ]\n" | |
1eea5c46 TM |
64 | "\n" |
65 | "Where: VLAN_PROTOCOL := { 802.1Q | 802.1ad }\n" | |
28d84b42 JP |
66 | ); |
67 | } | |
68 | ||
43367ef7 ZS |
69 | static void explain(void) |
70 | { | |
71 | print_explain(stderr); | |
72 | } | |
73 | ||
3069539f | 74 | void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len) |
70dfb0b8 NA |
75 | { |
76 | char eaddr[32]; | |
77 | ||
78 | ether_ntoa_r((const struct ether_addr *)id->addr, eaddr); | |
79 | snprintf(buf, len, "%.2x%.2x.%s", id->prio[0], id->prio[1], eaddr); | |
80 | } | |
81 | ||
28d84b42 JP |
82 | static int bridge_parse_opt(struct link_util *lu, int argc, char **argv, |
83 | struct nlmsghdr *n) | |
84 | { | |
85 | __u32 val; | |
86 | ||
87 | while (argc > 0) { | |
88 | if (matches(*argv, "forward_delay") == 0) { | |
89 | NEXT_ARG(); | |
6a9ce30e | 90 | if (get_u32(&val, *argv, 0)) |
28d84b42 | 91 | invarg("invalid forward_delay", *argv); |
6a9ce30e | 92 | |
28d84b42 JP |
93 | addattr32(n, 1024, IFLA_BR_FORWARD_DELAY, val); |
94 | } else if (matches(*argv, "hello_time") == 0) { | |
95 | NEXT_ARG(); | |
6a9ce30e | 96 | if (get_u32(&val, *argv, 0)) |
28d84b42 | 97 | invarg("invalid hello_time", *argv); |
6a9ce30e | 98 | |
28d84b42 JP |
99 | addattr32(n, 1024, IFLA_BR_HELLO_TIME, val); |
100 | } else if (matches(*argv, "max_age") == 0) { | |
101 | NEXT_ARG(); | |
6a9ce30e | 102 | if (get_u32(&val, *argv, 0)) |
28d84b42 | 103 | invarg("invalid max_age", *argv); |
6a9ce30e | 104 | |
28d84b42 | 105 | addattr32(n, 1024, IFLA_BR_MAX_AGE, val); |
6c99fb60 NA |
106 | } else if (matches(*argv, "ageing_time") == 0) { |
107 | NEXT_ARG(); | |
6a9ce30e | 108 | if (get_u32(&val, *argv, 0)) |
6c99fb60 | 109 | invarg("invalid ageing_time", *argv); |
6a9ce30e | 110 | |
6c99fb60 | 111 | addattr32(n, 1024, IFLA_BR_AGEING_TIME, val); |
dab04962 NA |
112 | } else if (matches(*argv, "stp_state") == 0) { |
113 | NEXT_ARG(); | |
6a9ce30e | 114 | if (get_u32(&val, *argv, 0)) |
dab04962 | 115 | invarg("invalid stp_state", *argv); |
6a9ce30e | 116 | |
dab04962 | 117 | addattr32(n, 1024, IFLA_BR_STP_STATE, val); |
b0197a04 NA |
118 | } else if (matches(*argv, "priority") == 0) { |
119 | __u16 prio; | |
120 | ||
121 | NEXT_ARG(); | |
6a9ce30e | 122 | if (get_u16(&prio, *argv, 0)) |
b0197a04 | 123 | invarg("invalid priority", *argv); |
6a9ce30e | 124 | |
b0197a04 | 125 | addattr16(n, 1024, IFLA_BR_PRIORITY, prio); |
e4d456f0 NA |
126 | } else if (matches(*argv, "vlan_filtering") == 0) { |
127 | __u8 vlan_filter; | |
128 | ||
129 | NEXT_ARG(); | |
8a4cd394 | 130 | if (get_u8(&vlan_filter, *argv, 0)) |
e4d456f0 | 131 | invarg("invalid vlan_filtering", *argv); |
8a4cd394 | 132 | |
e4d456f0 | 133 | addattr8(n, 1024, IFLA_BR_VLAN_FILTERING, vlan_filter); |
1eea5c46 TM |
134 | } else if (matches(*argv, "vlan_protocol") == 0) { |
135 | __u16 vlan_proto; | |
136 | ||
137 | NEXT_ARG(); | |
8a4cd394 | 138 | if (ll_proto_a2n(&vlan_proto, *argv)) |
1eea5c46 | 139 | invarg("invalid vlan_protocol", *argv); |
8a4cd394 | 140 | |
1eea5c46 | 141 | addattr16(n, 1024, IFLA_BR_VLAN_PROTOCOL, vlan_proto); |
8caaf33b NA |
142 | } else if (matches(*argv, "group_fwd_mask") == 0) { |
143 | __u16 fwd_mask; | |
144 | ||
145 | NEXT_ARG(); | |
146 | if (get_u16(&fwd_mask, *argv, 0)) | |
147 | invarg("invalid group_fwd_mask", *argv); | |
148 | ||
149 | addattr16(n, 1024, IFLA_BR_GROUP_FWD_MASK, fwd_mask); | |
0a61aa39 NA |
150 | } else if (matches(*argv, "group_address") == 0) { |
151 | char llabuf[32]; | |
152 | int len; | |
153 | ||
154 | NEXT_ARG(); | |
155 | len = ll_addr_a2n(llabuf, sizeof(llabuf), *argv); | |
156 | if (len < 0) | |
157 | return -1; | |
158 | addattr_l(n, 1024, IFLA_BR_GROUP_ADDR, llabuf, len); | |
f3372d62 HL |
159 | } else if (matches(*argv, "fdb_flush") == 0) { |
160 | addattr(n, 1024, IFLA_BR_FDB_FLUSH); | |
719832af NA |
161 | } else if (matches(*argv, "vlan_default_pvid") == 0) { |
162 | __u16 default_pvid; | |
163 | ||
164 | NEXT_ARG(); | |
165 | if (get_u16(&default_pvid, *argv, 0)) | |
166 | invarg("invalid vlan_default_pvid", *argv); | |
167 | ||
168 | addattr16(n, 1024, IFLA_BR_VLAN_DEFAULT_PVID, | |
169 | default_pvid); | |
7d93b567 HL |
170 | } else if (matches(*argv, "vlan_stats_enabled") == 0) { |
171 | __u8 vlan_stats_enabled; | |
172 | ||
173 | NEXT_ARG(); | |
174 | if (get_u8(&vlan_stats_enabled, *argv, 0)) | |
175 | invarg("invalid vlan_stats_enabled", *argv); | |
176 | addattr8(n, 1024, IFLA_BR_VLAN_STATS_ENABLED, | |
177 | vlan_stats_enabled); | |
963d137c NA |
178 | } else if (matches(*argv, "mcast_router") == 0) { |
179 | __u8 mcast_router; | |
180 | ||
181 | NEXT_ARG(); | |
182 | if (get_u8(&mcast_router, *argv, 0)) | |
183 | invarg("invalid mcast_router", *argv); | |
184 | ||
185 | addattr8(n, 1024, IFLA_BR_MCAST_ROUTER, mcast_router); | |
7ddd2d94 NA |
186 | } else if (matches(*argv, "mcast_snooping") == 0) { |
187 | __u8 mcast_snoop; | |
188 | ||
189 | NEXT_ARG(); | |
190 | if (get_u8(&mcast_snoop, *argv, 0)) | |
191 | invarg("invalid mcast_snooping", *argv); | |
192 | ||
193 | addattr8(n, 1024, IFLA_BR_MCAST_SNOOPING, mcast_snoop); | |
449843d1 NA |
194 | } else if (matches(*argv, "mcast_query_use_ifaddr") == 0) { |
195 | __u8 mcast_qui; | |
196 | ||
197 | NEXT_ARG(); | |
198 | if (get_u8(&mcast_qui, *argv, 0)) | |
199 | invarg("invalid mcast_query_use_ifaddr", | |
200 | *argv); | |
201 | ||
202 | addattr8(n, 1024, IFLA_BR_MCAST_QUERY_USE_IFADDR, | |
203 | mcast_qui); | |
0778b741 NA |
204 | } else if (matches(*argv, "mcast_querier") == 0) { |
205 | __u8 mcast_querier; | |
206 | ||
207 | NEXT_ARG(); | |
208 | if (get_u8(&mcast_querier, *argv, 0)) | |
209 | invarg("invalid mcast_querier", *argv); | |
210 | ||
211 | addattr8(n, 1024, IFLA_BR_MCAST_QUERIER, mcast_querier); | |
92c0ef70 NA |
212 | } else if (matches(*argv, "mcast_hash_elasticity") == 0) { |
213 | __u32 mcast_hash_el; | |
214 | ||
215 | NEXT_ARG(); | |
216 | if (get_u32(&mcast_hash_el, *argv, 0)) | |
217 | invarg("invalid mcast_hash_elasticity", | |
218 | *argv); | |
219 | ||
220 | addattr32(n, 1024, IFLA_BR_MCAST_HASH_ELASTICITY, | |
221 | mcast_hash_el); | |
8b9eb7cd NA |
222 | } else if (matches(*argv, "mcast_hash_max") == 0) { |
223 | __u32 mcast_hash_max; | |
224 | ||
225 | NEXT_ARG(); | |
226 | if (get_u32(&mcast_hash_max, *argv, 0)) | |
227 | invarg("invalid mcast_hash_max", *argv); | |
228 | ||
229 | addattr32(n, 1024, IFLA_BR_MCAST_HASH_MAX, | |
230 | mcast_hash_max); | |
fb44cadb NA |
231 | } else if (matches(*argv, "mcast_last_member_count") == 0) { |
232 | __u32 mcast_lmc; | |
233 | ||
234 | NEXT_ARG(); | |
235 | if (get_u32(&mcast_lmc, *argv, 0)) | |
236 | invarg("invalid mcast_last_member_count", | |
237 | *argv); | |
238 | ||
239 | addattr32(n, 1024, IFLA_BR_MCAST_LAST_MEMBER_CNT, | |
240 | mcast_lmc); | |
ceb64866 NA |
241 | } else if (matches(*argv, "mcast_startup_query_count") == 0) { |
242 | __u32 mcast_sqc; | |
243 | ||
244 | NEXT_ARG(); | |
245 | if (get_u32(&mcast_sqc, *argv, 0)) | |
246 | invarg("invalid mcast_startup_query_count", | |
247 | *argv); | |
248 | ||
249 | addattr32(n, 1024, IFLA_BR_MCAST_STARTUP_QUERY_CNT, | |
250 | mcast_sqc); | |
10082a25 NA |
251 | } else if (matches(*argv, "mcast_last_member_interval") == 0) { |
252 | __u64 mcast_last_member_intvl; | |
253 | ||
254 | NEXT_ARG(); | |
255 | if (get_u64(&mcast_last_member_intvl, *argv, 0)) | |
256 | invarg("invalid mcast_last_member_interval", | |
257 | *argv); | |
258 | ||
259 | addattr64(n, 1024, IFLA_BR_MCAST_LAST_MEMBER_INTVL, | |
260 | mcast_last_member_intvl); | |
7f3d5592 NA |
261 | } else if (matches(*argv, "mcast_membership_interval") == 0) { |
262 | __u64 mcast_membership_intvl; | |
263 | ||
264 | NEXT_ARG(); | |
8a4cd394 | 265 | if (get_u64(&mcast_membership_intvl, *argv, 0)) |
7f3d5592 NA |
266 | invarg("invalid mcast_membership_interval", |
267 | *argv); | |
8a4cd394 | 268 | |
7f3d5592 NA |
269 | addattr64(n, 1024, IFLA_BR_MCAST_MEMBERSHIP_INTVL, |
270 | mcast_membership_intvl); | |
1f2244b8 NA |
271 | } else if (matches(*argv, "mcast_querier_interval") == 0) { |
272 | __u64 mcast_querier_intvl; | |
273 | ||
274 | NEXT_ARG(); | |
8a4cd394 | 275 | if (get_u64(&mcast_querier_intvl, *argv, 0)) |
1f2244b8 NA |
276 | invarg("invalid mcast_querier_interval", |
277 | *argv); | |
8a4cd394 | 278 | |
1f2244b8 NA |
279 | addattr64(n, 1024, IFLA_BR_MCAST_QUERIER_INTVL, |
280 | mcast_querier_intvl); | |
5a32388f NA |
281 | } else if (matches(*argv, "mcast_query_interval") == 0) { |
282 | __u64 mcast_query_intvl; | |
283 | ||
284 | NEXT_ARG(); | |
8a4cd394 | 285 | if (get_u64(&mcast_query_intvl, *argv, 0)) |
5a32388f NA |
286 | invarg("invalid mcast_query_interval", |
287 | *argv); | |
8a4cd394 | 288 | |
5a32388f NA |
289 | addattr64(n, 1024, IFLA_BR_MCAST_QUERY_INTVL, |
290 | mcast_query_intvl); | |
483df11c NA |
291 | } else if (!matches(*argv, "mcast_query_response_interval")) { |
292 | __u64 mcast_query_resp_intvl; | |
293 | ||
294 | NEXT_ARG(); | |
8a4cd394 | 295 | if (get_u64(&mcast_query_resp_intvl, *argv, 0)) |
483df11c NA |
296 | invarg("invalid mcast_query_response_interval", |
297 | *argv); | |
8a4cd394 | 298 | |
483df11c NA |
299 | addattr64(n, 1024, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, |
300 | mcast_query_resp_intvl); | |
178b1806 NA |
301 | } else if (!matches(*argv, "mcast_startup_query_interval")) { |
302 | __u64 mcast_startup_query_intvl; | |
303 | ||
304 | NEXT_ARG(); | |
8a4cd394 | 305 | if (get_u64(&mcast_startup_query_intvl, *argv, 0)) |
178b1806 NA |
306 | invarg("invalid mcast_startup_query_interval", |
307 | *argv); | |
8a4cd394 | 308 | |
178b1806 NA |
309 | addattr64(n, 1024, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, |
310 | mcast_startup_query_intvl); | |
6ddad009 HL |
311 | } else if (matches(*argv, "mcast_stats_enabled") == 0) { |
312 | __u8 mcast_stats_enabled; | |
313 | ||
314 | NEXT_ARG(); | |
315 | if (get_u8(&mcast_stats_enabled, *argv, 0)) | |
316 | invarg("invalid mcast_stats_enabled", *argv); | |
317 | addattr8(n, 1024, IFLA_BR_MCAST_STATS_ENABLED, | |
318 | mcast_stats_enabled); | |
19756950 HL |
319 | } else if (matches(*argv, "mcast_igmp_version") == 0) { |
320 | __u8 igmp_version; | |
321 | ||
322 | NEXT_ARG(); | |
323 | if (get_u8(&igmp_version, *argv, 0)) | |
324 | invarg("invalid mcast_igmp_version", *argv); | |
325 | addattr8(n, 1024, IFLA_BR_MCAST_IGMP_VERSION, | |
326 | igmp_version); | |
c3f1e3c4 HL |
327 | } else if (matches(*argv, "mcast_mld_version") == 0) { |
328 | __u8 mld_version; | |
329 | ||
330 | NEXT_ARG(); | |
331 | if (get_u8(&mld_version, *argv, 0)) | |
332 | invarg("invalid mcast_mld_version", *argv); | |
333 | addattr8(n, 1024, IFLA_BR_MCAST_MLD_VERSION, | |
334 | mld_version); | |
861c5dae NA |
335 | } else if (matches(*argv, "nf_call_iptables") == 0) { |
336 | __u8 nf_call_ipt; | |
337 | ||
338 | NEXT_ARG(); | |
8a4cd394 | 339 | if (get_u8(&nf_call_ipt, *argv, 0)) |
861c5dae | 340 | invarg("invalid nf_call_iptables", *argv); |
8a4cd394 | 341 | |
861c5dae NA |
342 | addattr8(n, 1024, IFLA_BR_NF_CALL_IPTABLES, |
343 | nf_call_ipt); | |
344 | } else if (matches(*argv, "nf_call_ip6tables") == 0) { | |
345 | __u8 nf_call_ip6t; | |
346 | ||
347 | NEXT_ARG(); | |
8a4cd394 | 348 | if (get_u8(&nf_call_ip6t, *argv, 0)) |
861c5dae | 349 | invarg("invalid nf_call_ip6tables", *argv); |
8a4cd394 | 350 | |
861c5dae NA |
351 | addattr8(n, 1024, IFLA_BR_NF_CALL_IP6TABLES, |
352 | nf_call_ip6t); | |
353 | } else if (matches(*argv, "nf_call_arptables") == 0) { | |
354 | __u8 nf_call_arpt; | |
355 | ||
356 | NEXT_ARG(); | |
8a4cd394 | 357 | if (get_u8(&nf_call_arpt, *argv, 0)) |
861c5dae | 358 | invarg("invalid nf_call_arptables", *argv); |
8a4cd394 | 359 | |
861c5dae NA |
360 | addattr8(n, 1024, IFLA_BR_NF_CALL_ARPTABLES, |
361 | nf_call_arpt); | |
28d84b42 JP |
362 | } else if (matches(*argv, "help") == 0) { |
363 | explain(); | |
364 | return -1; | |
365 | } else { | |
366 | fprintf(stderr, "bridge: unknown command \"%s\"?\n", *argv); | |
367 | explain(); | |
368 | return -1; | |
369 | } | |
370 | argc--, argv++; | |
371 | } | |
372 | ||
373 | return 0; | |
374 | } | |
375 | ||
376 | static void bridge_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) | |
377 | { | |
378 | if (!tb) | |
379 | return; | |
380 | ||
381 | if (tb[IFLA_BR_FORWARD_DELAY]) | |
382 | fprintf(f, "forward_delay %u ", | |
383 | rta_getattr_u32(tb[IFLA_BR_FORWARD_DELAY])); | |
384 | ||
385 | if (tb[IFLA_BR_HELLO_TIME]) | |
386 | fprintf(f, "hello_time %u ", | |
387 | rta_getattr_u32(tb[IFLA_BR_HELLO_TIME])); | |
388 | ||
389 | if (tb[IFLA_BR_MAX_AGE]) | |
390 | fprintf(f, "max_age %u ", | |
391 | rta_getattr_u32(tb[IFLA_BR_MAX_AGE])); | |
fdba0515 NA |
392 | |
393 | if (tb[IFLA_BR_AGEING_TIME]) | |
394 | fprintf(f, "ageing_time %u ", | |
395 | rta_getattr_u32(tb[IFLA_BR_AGEING_TIME])); | |
396 | ||
397 | if (tb[IFLA_BR_STP_STATE]) | |
398 | fprintf(f, "stp_state %u ", | |
399 | rta_getattr_u32(tb[IFLA_BR_STP_STATE])); | |
400 | ||
401 | if (tb[IFLA_BR_PRIORITY]) | |
402 | fprintf(f, "priority %u ", | |
403 | rta_getattr_u16(tb[IFLA_BR_PRIORITY])); | |
e4d456f0 NA |
404 | |
405 | if (tb[IFLA_BR_VLAN_FILTERING]) | |
406 | fprintf(f, "vlan_filtering %u ", | |
407 | rta_getattr_u8(tb[IFLA_BR_VLAN_FILTERING])); | |
1eea5c46 TM |
408 | |
409 | if (tb[IFLA_BR_VLAN_PROTOCOL]) { | |
410 | SPRINT_BUF(b1); | |
411 | ||
412 | fprintf(f, "vlan_protocol %s ", | |
413 | ll_proto_n2a(rta_getattr_u16(tb[IFLA_BR_VLAN_PROTOCOL]), | |
414 | b1, sizeof(b1))); | |
415 | } | |
70dfb0b8 NA |
416 | |
417 | if (tb[IFLA_BR_BRIDGE_ID]) { | |
418 | char bridge_id[32]; | |
419 | ||
420 | br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), bridge_id, | |
421 | sizeof(bridge_id)); | |
422 | fprintf(f, "bridge_id %s ", bridge_id); | |
423 | } | |
424 | ||
425 | if (tb[IFLA_BR_ROOT_ID]) { | |
426 | char root_id[32]; | |
427 | ||
428 | br_dump_bridge_id(RTA_DATA(tb[IFLA_BR_BRIDGE_ID]), root_id, | |
429 | sizeof(root_id)); | |
430 | fprintf(f, "designated_root %s ", root_id); | |
431 | } | |
4e3bbc66 NA |
432 | |
433 | if (tb[IFLA_BR_ROOT_PORT]) | |
434 | fprintf(f, "root_port %u ", | |
435 | rta_getattr_u16(tb[IFLA_BR_ROOT_PORT])); | |
436 | ||
437 | if (tb[IFLA_BR_ROOT_PATH_COST]) | |
438 | fprintf(f, "root_path_cost %u ", | |
439 | rta_getattr_u32(tb[IFLA_BR_ROOT_PATH_COST])); | |
440 | ||
441 | if (tb[IFLA_BR_TOPOLOGY_CHANGE]) | |
442 | fprintf(f, "topology_change %u ", | |
443 | rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE])); | |
444 | ||
445 | if (tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED]) | |
446 | fprintf(f, "topology_change_detected %u ", | |
447 | rta_getattr_u8(tb[IFLA_BR_TOPOLOGY_CHANGE_DETECTED])); | |
8c0f7a16 NA |
448 | |
449 | if (tb[IFLA_BR_HELLO_TIMER]) { | |
450 | struct timeval tv; | |
451 | ||
452 | __jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_HELLO_TIMER])); | |
453 | fprintf(f, "hello_timer %4i.%.2i ", (int)tv.tv_sec, | |
454 | (int)tv.tv_usec/10000); | |
455 | } | |
456 | ||
457 | if (tb[IFLA_BR_TCN_TIMER]) { | |
458 | struct timeval tv; | |
459 | ||
460 | __jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_TCN_TIMER])); | |
461 | fprintf(f, "tcn_timer %4i.%.2i ", (int)tv.tv_sec, | |
462 | (int)tv.tv_usec/10000); | |
463 | } | |
464 | ||
465 | if (tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]) { | |
466 | unsigned long jiffies; | |
467 | struct timeval tv; | |
468 | ||
469 | jiffies = rta_getattr_u64(tb[IFLA_BR_TOPOLOGY_CHANGE_TIMER]); | |
470 | __jiffies_to_tv(&tv, jiffies); | |
471 | fprintf(f, "topology_change_timer %4i.%.2i ", (int)tv.tv_sec, | |
472 | (int)tv.tv_usec/10000); | |
473 | } | |
474 | ||
475 | if (tb[IFLA_BR_GC_TIMER]) { | |
476 | struct timeval tv; | |
477 | ||
478 | __jiffies_to_tv(&tv, rta_getattr_u64(tb[IFLA_BR_GC_TIMER])); | |
479 | fprintf(f, "gc_timer %4i.%.2i ", (int)tv.tv_sec, | |
480 | (int)tv.tv_usec/10000); | |
481 | } | |
8caaf33b | 482 | |
719832af NA |
483 | if (tb[IFLA_BR_VLAN_DEFAULT_PVID]) |
484 | fprintf(f, "vlan_default_pvid %u ", | |
485 | rta_getattr_u16(tb[IFLA_BR_VLAN_DEFAULT_PVID])); | |
486 | ||
7d93b567 HL |
487 | if (tb[IFLA_BR_VLAN_STATS_ENABLED]) |
488 | fprintf(f, "vlan_stats_enabled %u ", | |
489 | rta_getattr_u8(tb[IFLA_BR_VLAN_STATS_ENABLED])); | |
490 | ||
8caaf33b NA |
491 | if (tb[IFLA_BR_GROUP_FWD_MASK]) |
492 | fprintf(f, "group_fwd_mask %#x ", | |
493 | rta_getattr_u16(tb[IFLA_BR_GROUP_FWD_MASK])); | |
0a61aa39 NA |
494 | |
495 | if (tb[IFLA_BR_GROUP_ADDR]) { | |
496 | SPRINT_BUF(mac); | |
497 | ||
498 | fprintf(f, "group_address %s ", | |
499 | ll_addr_n2a(RTA_DATA(tb[IFLA_BR_GROUP_ADDR]), | |
500 | RTA_PAYLOAD(tb[IFLA_BR_GROUP_ADDR]), | |
501 | 1 /*ARPHDR_ETHER*/, mac, sizeof(mac))); | |
502 | } | |
963d137c | 503 | |
7ddd2d94 NA |
504 | if (tb[IFLA_BR_MCAST_SNOOPING]) |
505 | fprintf(f, "mcast_snooping %u ", | |
506 | rta_getattr_u8(tb[IFLA_BR_MCAST_SNOOPING])); | |
507 | ||
963d137c NA |
508 | if (tb[IFLA_BR_MCAST_ROUTER]) |
509 | fprintf(f, "mcast_router %u ", | |
510 | rta_getattr_u8(tb[IFLA_BR_MCAST_ROUTER])); | |
449843d1 NA |
511 | |
512 | if (tb[IFLA_BR_MCAST_QUERY_USE_IFADDR]) | |
513 | fprintf(f, "mcast_query_use_ifaddr %u ", | |
514 | rta_getattr_u8(tb[IFLA_BR_MCAST_QUERY_USE_IFADDR])); | |
0778b741 NA |
515 | |
516 | if (tb[IFLA_BR_MCAST_QUERIER]) | |
517 | fprintf(f, "mcast_querier %u ", | |
518 | rta_getattr_u8(tb[IFLA_BR_MCAST_QUERIER])); | |
92c0ef70 NA |
519 | |
520 | if (tb[IFLA_BR_MCAST_HASH_ELASTICITY]) | |
521 | fprintf(f, "mcast_hash_elasticity %u ", | |
522 | rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_ELASTICITY])); | |
8b9eb7cd NA |
523 | |
524 | if (tb[IFLA_BR_MCAST_HASH_MAX]) | |
525 | fprintf(f, "mcast_hash_max %u ", | |
526 | rta_getattr_u32(tb[IFLA_BR_MCAST_HASH_MAX])); | |
fb44cadb NA |
527 | |
528 | if (tb[IFLA_BR_MCAST_LAST_MEMBER_CNT]) | |
529 | fprintf(f, "mcast_last_member_count %u ", | |
530 | rta_getattr_u32(tb[IFLA_BR_MCAST_LAST_MEMBER_CNT])); | |
ceb64866 NA |
531 | |
532 | if (tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT]) | |
533 | fprintf(f, "mcast_startup_query_count %u ", | |
534 | rta_getattr_u32(tb[IFLA_BR_MCAST_STARTUP_QUERY_CNT])); | |
10082a25 NA |
535 | |
536 | if (tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL]) | |
537 | fprintf(f, "mcast_last_member_interval %llu ", | |
538 | rta_getattr_u64(tb[IFLA_BR_MCAST_LAST_MEMBER_INTVL])); | |
7f3d5592 NA |
539 | |
540 | if (tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL]) | |
541 | fprintf(f, "mcast_membership_interval %llu ", | |
542 | rta_getattr_u64(tb[IFLA_BR_MCAST_MEMBERSHIP_INTVL])); | |
1f2244b8 NA |
543 | |
544 | if (tb[IFLA_BR_MCAST_QUERIER_INTVL]) | |
545 | fprintf(f, "mcast_querier_interval %llu ", | |
546 | rta_getattr_u64(tb[IFLA_BR_MCAST_QUERIER_INTVL])); | |
5a32388f NA |
547 | |
548 | if (tb[IFLA_BR_MCAST_QUERY_INTVL]) | |
549 | fprintf(f, "mcast_query_interval %llu ", | |
550 | rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_INTVL])); | |
483df11c NA |
551 | |
552 | if (tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL]) | |
553 | fprintf(f, "mcast_query_response_interval %llu ", | |
554 | rta_getattr_u64(tb[IFLA_BR_MCAST_QUERY_RESPONSE_INTVL])); | |
178b1806 NA |
555 | |
556 | if (tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL]) | |
557 | fprintf(f, "mcast_startup_query_interval %llu ", | |
558 | rta_getattr_u64(tb[IFLA_BR_MCAST_STARTUP_QUERY_INTVL])); | |
861c5dae | 559 | |
6ddad009 HL |
560 | if (tb[IFLA_BR_MCAST_STATS_ENABLED]) |
561 | fprintf(f, "mcast_stats_enabled %u ", | |
562 | rta_getattr_u8(tb[IFLA_BR_MCAST_STATS_ENABLED])); | |
563 | ||
19756950 HL |
564 | if (tb[IFLA_BR_MCAST_IGMP_VERSION]) |
565 | fprintf(f, "mcast_igmp_version %u ", | |
566 | rta_getattr_u8(tb[IFLA_BR_MCAST_IGMP_VERSION])); | |
567 | ||
c3f1e3c4 HL |
568 | if (tb[IFLA_BR_MCAST_MLD_VERSION]) |
569 | fprintf(f, "mcast_mld_version %u ", | |
570 | rta_getattr_u8(tb[IFLA_BR_MCAST_MLD_VERSION])); | |
571 | ||
861c5dae NA |
572 | if (tb[IFLA_BR_NF_CALL_IPTABLES]) |
573 | fprintf(f, "nf_call_iptables %u ", | |
574 | rta_getattr_u8(tb[IFLA_BR_NF_CALL_IPTABLES])); | |
575 | ||
576 | if (tb[IFLA_BR_NF_CALL_IP6TABLES]) | |
577 | fprintf(f, "nf_call_ip6tables %u ", | |
578 | rta_getattr_u8(tb[IFLA_BR_NF_CALL_IP6TABLES])); | |
579 | ||
580 | if (tb[IFLA_BR_NF_CALL_ARPTABLES]) | |
581 | fprintf(f, "nf_call_arptables %u ", | |
582 | rta_getattr_u8(tb[IFLA_BR_NF_CALL_ARPTABLES])); | |
28d84b42 JP |
583 | } |
584 | ||
43367ef7 ZS |
585 | static void bridge_print_help(struct link_util *lu, int argc, char **argv, |
586 | FILE *f) | |
587 | { | |
588 | print_explain(f); | |
589 | } | |
590 | ||
60ec0ecf NA |
591 | static void bridge_print_xstats_help(struct link_util *lu, FILE *f) |
592 | { | |
593 | fprintf(f, "Usage: ... %s [ igmp ] [ dev DEVICE ]\n", lu->id); | |
594 | } | |
595 | ||
596 | static void bridge_print_stats_attr(FILE *f, struct rtattr *attr, int ifindex) | |
597 | { | |
598 | struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1]; | |
599 | struct br_mcast_stats *mstats; | |
600 | struct rtattr *i, *list; | |
601 | const char *ifname = ""; | |
602 | int rem; | |
603 | ||
604 | parse_rtattr(brtb, LINK_XSTATS_TYPE_MAX, RTA_DATA(attr), | |
605 | RTA_PAYLOAD(attr)); | |
606 | if (!brtb[LINK_XSTATS_TYPE_BRIDGE]) | |
607 | return; | |
608 | ||
609 | list = brtb[LINK_XSTATS_TYPE_BRIDGE]; | |
610 | rem = RTA_PAYLOAD(list); | |
611 | for (i = RTA_DATA(list); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { | |
612 | if (xstats_print_attr && i->rta_type != xstats_print_attr) | |
613 | continue; | |
614 | switch (i->rta_type) { | |
615 | case BRIDGE_XSTATS_MCAST: | |
616 | mstats = RTA_DATA(i); | |
617 | ifname = ll_index_to_name(ifindex); | |
618 | fprintf(f, "%-16s\n", ifname); | |
619 | fprintf(f, "%-16s IGMP queries:\n", ""); | |
620 | fprintf(f, "%-16s RX: v1 %llu v2 %llu v3 %llu\n", | |
621 | "", | |
622 | mstats->igmp_v1queries[BR_MCAST_DIR_RX], | |
623 | mstats->igmp_v2queries[BR_MCAST_DIR_RX], | |
624 | mstats->igmp_v3queries[BR_MCAST_DIR_RX]); | |
625 | fprintf(f, "%-16s TX: v1 %llu v2 %llu v3 %llu\n", | |
626 | "", | |
627 | mstats->igmp_v1queries[BR_MCAST_DIR_TX], | |
628 | mstats->igmp_v2queries[BR_MCAST_DIR_TX], | |
629 | mstats->igmp_v3queries[BR_MCAST_DIR_TX]); | |
630 | ||
631 | fprintf(f, "%-16s IGMP reports:\n", ""); | |
632 | fprintf(f, "%-16s RX: v1 %llu v2 %llu v3 %llu\n", | |
633 | "", | |
634 | mstats->igmp_v1reports[BR_MCAST_DIR_RX], | |
635 | mstats->igmp_v2reports[BR_MCAST_DIR_RX], | |
636 | mstats->igmp_v3reports[BR_MCAST_DIR_RX]); | |
637 | fprintf(f, "%-16s TX: v1 %llu v2 %llu v3 %llu\n", | |
638 | "", | |
639 | mstats->igmp_v1reports[BR_MCAST_DIR_TX], | |
640 | mstats->igmp_v2reports[BR_MCAST_DIR_TX], | |
641 | mstats->igmp_v3reports[BR_MCAST_DIR_TX]); | |
642 | ||
643 | fprintf(f, "%-16s IGMP leaves: RX: %llu TX: %llu\n", | |
644 | "", | |
645 | mstats->igmp_leaves[BR_MCAST_DIR_RX], | |
646 | mstats->igmp_leaves[BR_MCAST_DIR_TX]); | |
647 | ||
648 | fprintf(f, "%-16s IGMP parse errors: %llu\n", | |
649 | "", mstats->igmp_parse_errors); | |
650 | ||
651 | fprintf(f, "%-16s MLD queries:\n", ""); | |
652 | fprintf(f, "%-16s RX: v1 %llu v2 %llu\n", | |
653 | "", | |
654 | mstats->mld_v1queries[BR_MCAST_DIR_RX], | |
655 | mstats->mld_v2queries[BR_MCAST_DIR_RX]); | |
656 | fprintf(f, "%-16s TX: v1 %llu v2 %llu\n", | |
657 | "", | |
658 | mstats->mld_v1queries[BR_MCAST_DIR_TX], | |
659 | mstats->mld_v2queries[BR_MCAST_DIR_TX]); | |
660 | ||
661 | fprintf(f, "%-16s MLD reports:\n", ""); | |
662 | fprintf(f, "%-16s RX: v1 %llu v2 %llu\n", | |
663 | "", | |
664 | mstats->mld_v1reports[BR_MCAST_DIR_RX], | |
665 | mstats->mld_v2reports[BR_MCAST_DIR_RX]); | |
666 | fprintf(f, "%-16s TX: v1 %llu v2 %llu\n", | |
667 | "", | |
668 | mstats->mld_v1reports[BR_MCAST_DIR_TX], | |
669 | mstats->mld_v2reports[BR_MCAST_DIR_TX]); | |
670 | ||
671 | fprintf(f, "%-16s MLD leaves: RX: %llu TX: %llu\n", | |
672 | "", | |
673 | mstats->mld_leaves[BR_MCAST_DIR_RX], | |
674 | mstats->mld_leaves[BR_MCAST_DIR_TX]); | |
675 | ||
676 | fprintf(f, "%-16s MLD parse errors: %llu\n", | |
677 | "", mstats->mld_parse_errors); | |
678 | break; | |
679 | } | |
680 | } | |
681 | } | |
682 | ||
683 | static int bridge_print_xstats(const struct sockaddr_nl *who, | |
684 | struct nlmsghdr *n, void *arg) | |
685 | { | |
686 | struct if_stats_msg *ifsm = NLMSG_DATA(n); | |
687 | struct rtattr *tb[IFLA_STATS_MAX+1]; | |
688 | int len = n->nlmsg_len; | |
689 | FILE *fp = arg; | |
690 | ||
691 | len -= NLMSG_LENGTH(sizeof(*ifsm)); | |
692 | if (len < 0) { | |
693 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
694 | return -1; | |
695 | } | |
696 | if (filter_index && filter_index != ifsm->ifindex) | |
697 | return 0; | |
698 | ||
699 | parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len); | |
700 | if (tb[IFLA_STATS_LINK_XSTATS]) | |
701 | bridge_print_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS], | |
702 | ifsm->ifindex); | |
703 | ||
704 | if (tb[IFLA_STATS_LINK_XSTATS_SLAVE]) | |
705 | bridge_print_stats_attr(fp, tb[IFLA_STATS_LINK_XSTATS_SLAVE], | |
706 | ifsm->ifindex); | |
707 | ||
708 | return 0; | |
709 | } | |
710 | ||
711 | static int bridge_parse_xstats(struct link_util *lu, int argc, char **argv) | |
712 | { | |
713 | while (argc > 0) { | |
714 | if (strcmp(*argv, "igmp") == 0 || strcmp(*argv, "mcast") == 0) { | |
715 | xstats_print_attr = BRIDGE_XSTATS_MCAST; | |
716 | } else if (strcmp(*argv, "dev") == 0) { | |
717 | NEXT_ARG(); | |
718 | filter_index = if_nametoindex(*argv); | |
719 | if (filter_index == 0) { | |
720 | fprintf(stderr, "Cannot find device \"%s\"\n", | |
721 | *argv); | |
722 | return -1; | |
723 | } | |
724 | } else if (strcmp(*argv, "help") == 0) { | |
725 | bridge_print_xstats_help(lu, stdout); | |
726 | exit(0); | |
727 | } else { | |
728 | invarg("unknown attribute", *argv); | |
729 | } | |
730 | argc--; argv++; | |
731 | } | |
732 | ||
733 | return 0; | |
734 | } | |
735 | ||
28d84b42 JP |
736 | struct link_util bridge_link_util = { |
737 | .id = "bridge", | |
738 | .maxattr = IFLA_BR_MAX, | |
739 | .parse_opt = bridge_parse_opt, | |
740 | .print_opt = bridge_print_opt, | |
43367ef7 | 741 | .print_help = bridge_print_help, |
60ec0ecf NA |
742 | .parse_ifla_xstats = bridge_parse_xstats, |
743 | .print_ifla_xstats = bridge_print_xstats, | |
28d84b42 | 744 | }; |