1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Sysfs attributes of bridge |
4 | * Linux ethernet bridge |
5 | * |
6 | * Authors: |
7 | * Stephen Hemminger <shemminger@osdl.org> |
8 | */ |
9 | |
10 | #include <linux/capability.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/netdevice.h> |
13 | #include <linux/etherdevice.h> |
14 | #include <linux/if_bridge.h> |
15 | #include <linux/rtnetlink.h> |
16 | #include <linux/spinlock.h> |
17 | #include <linux/times.h> |
18 | #include <linux/sched/signal.h> |
19 | |
20 | #include "br_private.h" |
21 | |
22 | /* IMPORTANT: new bridge options must be added with netlink support only |
23 | * please do not add new sysfs entries |
24 | */ |
25 | |
26 | #define to_bridge(cd) ((struct net_bridge *)netdev_priv(to_net_dev(cd))) |
27 | |
28 | /* |
29 | * Common code for storing bridge parameters. |
30 | */ |
31 | static ssize_t store_bridge_parm(struct device *d, |
32 | const char *buf, size_t len, |
33 | int (*set)(struct net_bridge *br, unsigned long val, |
34 | struct netlink_ext_ack *extack)) |
35 | { |
36 | struct net_bridge *br = to_bridge(d); |
37 | struct netlink_ext_ack extack = {0}; |
38 | unsigned long val; |
39 | int err; |
40 | |
41 | if (!ns_capable(ns: dev_net(dev: br->dev)->user_ns, CAP_NET_ADMIN)) |
42 | return -EPERM; |
43 | |
44 | err = kstrtoul(s: buf, base: 0, res: &val); |
45 | if (err != 0) |
46 | return err; |
47 | |
48 | if (!rtnl_trylock()) |
49 | return restart_syscall(); |
50 | |
51 | err = (*set)(br, val, &extack); |
52 | if (!err) |
53 | netdev_state_change(dev: br->dev); |
54 | if (extack._msg) { |
55 | if (err) |
56 | br_err(br, "%s\n" , extack._msg); |
57 | else |
58 | br_warn(br, "%s\n" , extack._msg); |
59 | } |
60 | rtnl_unlock(); |
61 | |
62 | return err ? err : len; |
63 | } |
64 | |
65 | |
66 | static ssize_t forward_delay_show(struct device *d, |
67 | struct device_attribute *attr, char *buf) |
68 | { |
69 | struct net_bridge *br = to_bridge(d); |
70 | return sprintf(buf, fmt: "%lu\n" , jiffies_to_clock_t(x: br->forward_delay)); |
71 | } |
72 | |
73 | static int set_forward_delay(struct net_bridge *br, unsigned long val, |
74 | struct netlink_ext_ack *extack) |
75 | { |
76 | return br_set_forward_delay(br, x: val); |
77 | } |
78 | |
79 | static ssize_t forward_delay_store(struct device *d, |
80 | struct device_attribute *attr, |
81 | const char *buf, size_t len) |
82 | { |
83 | return store_bridge_parm(d, buf, len, set: set_forward_delay); |
84 | } |
85 | static DEVICE_ATTR_RW(forward_delay); |
86 | |
87 | static ssize_t hello_time_show(struct device *d, struct device_attribute *attr, |
88 | char *buf) |
89 | { |
90 | return sprintf(buf, fmt: "%lu\n" , |
91 | jiffies_to_clock_t(to_bridge(d)->hello_time)); |
92 | } |
93 | |
94 | static int set_hello_time(struct net_bridge *br, unsigned long val, |
95 | struct netlink_ext_ack *extack) |
96 | { |
97 | return br_set_hello_time(br, x: val); |
98 | } |
99 | |
100 | static ssize_t hello_time_store(struct device *d, |
101 | struct device_attribute *attr, const char *buf, |
102 | size_t len) |
103 | { |
104 | return store_bridge_parm(d, buf, len, set: set_hello_time); |
105 | } |
106 | static DEVICE_ATTR_RW(hello_time); |
107 | |
108 | static ssize_t max_age_show(struct device *d, struct device_attribute *attr, |
109 | char *buf) |
110 | { |
111 | return sprintf(buf, fmt: "%lu\n" , |
112 | jiffies_to_clock_t(to_bridge(d)->max_age)); |
113 | } |
114 | |
115 | static int set_max_age(struct net_bridge *br, unsigned long val, |
116 | struct netlink_ext_ack *extack) |
117 | { |
118 | return br_set_max_age(br, x: val); |
119 | } |
120 | |
121 | static ssize_t max_age_store(struct device *d, struct device_attribute *attr, |
122 | const char *buf, size_t len) |
123 | { |
124 | return store_bridge_parm(d, buf, len, set: set_max_age); |
125 | } |
126 | static DEVICE_ATTR_RW(max_age); |
127 | |
128 | static ssize_t ageing_time_show(struct device *d, |
129 | struct device_attribute *attr, char *buf) |
130 | { |
131 | struct net_bridge *br = to_bridge(d); |
132 | return sprintf(buf, fmt: "%lu\n" , jiffies_to_clock_t(x: br->ageing_time)); |
133 | } |
134 | |
135 | static int set_ageing_time(struct net_bridge *br, unsigned long val, |
136 | struct netlink_ext_ack *extack) |
137 | { |
138 | return br_set_ageing_time(br, ageing_time: val); |
139 | } |
140 | |
141 | static ssize_t ageing_time_store(struct device *d, |
142 | struct device_attribute *attr, |
143 | const char *buf, size_t len) |
144 | { |
145 | return store_bridge_parm(d, buf, len, set: set_ageing_time); |
146 | } |
147 | static DEVICE_ATTR_RW(ageing_time); |
148 | |
149 | static ssize_t stp_state_show(struct device *d, |
150 | struct device_attribute *attr, char *buf) |
151 | { |
152 | struct net_bridge *br = to_bridge(d); |
153 | return sprintf(buf, fmt: "%d\n" , br->stp_enabled); |
154 | } |
155 | |
156 | |
157 | static int set_stp_state(struct net_bridge *br, unsigned long val, |
158 | struct netlink_ext_ack *extack) |
159 | { |
160 | return br_stp_set_enabled(br, val, extack); |
161 | } |
162 | |
163 | static ssize_t stp_state_store(struct device *d, |
164 | struct device_attribute *attr, const char *buf, |
165 | size_t len) |
166 | { |
167 | return store_bridge_parm(d, buf, len, set: set_stp_state); |
168 | } |
169 | static DEVICE_ATTR_RW(stp_state); |
170 | |
171 | static ssize_t group_fwd_mask_show(struct device *d, |
172 | struct device_attribute *attr, |
173 | char *buf) |
174 | { |
175 | struct net_bridge *br = to_bridge(d); |
176 | return sprintf(buf, fmt: "%#x\n" , br->group_fwd_mask); |
177 | } |
178 | |
179 | static int set_group_fwd_mask(struct net_bridge *br, unsigned long val, |
180 | struct netlink_ext_ack *extack) |
181 | { |
182 | if (val & BR_GROUPFWD_RESTRICTED) |
183 | return -EINVAL; |
184 | |
185 | br->group_fwd_mask = val; |
186 | |
187 | return 0; |
188 | } |
189 | |
190 | static ssize_t group_fwd_mask_store(struct device *d, |
191 | struct device_attribute *attr, |
192 | const char *buf, |
193 | size_t len) |
194 | { |
195 | return store_bridge_parm(d, buf, len, set: set_group_fwd_mask); |
196 | } |
197 | static DEVICE_ATTR_RW(group_fwd_mask); |
198 | |
199 | static ssize_t priority_show(struct device *d, struct device_attribute *attr, |
200 | char *buf) |
201 | { |
202 | struct net_bridge *br = to_bridge(d); |
203 | return sprintf(buf, fmt: "%d\n" , |
204 | (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]); |
205 | } |
206 | |
207 | static int set_priority(struct net_bridge *br, unsigned long val, |
208 | struct netlink_ext_ack *extack) |
209 | { |
210 | br_stp_set_bridge_priority(br, newprio: (u16) val); |
211 | return 0; |
212 | } |
213 | |
214 | static ssize_t priority_store(struct device *d, struct device_attribute *attr, |
215 | const char *buf, size_t len) |
216 | { |
217 | return store_bridge_parm(d, buf, len, set: set_priority); |
218 | } |
219 | static DEVICE_ATTR_RW(priority); |
220 | |
221 | static ssize_t root_id_show(struct device *d, struct device_attribute *attr, |
222 | char *buf) |
223 | { |
224 | return br_show_bridge_id(buf, id: &to_bridge(d)->designated_root); |
225 | } |
226 | static DEVICE_ATTR_RO(root_id); |
227 | |
228 | static ssize_t bridge_id_show(struct device *d, struct device_attribute *attr, |
229 | char *buf) |
230 | { |
231 | return br_show_bridge_id(buf, id: &to_bridge(d)->bridge_id); |
232 | } |
233 | static DEVICE_ATTR_RO(bridge_id); |
234 | |
235 | static ssize_t root_port_show(struct device *d, struct device_attribute *attr, |
236 | char *buf) |
237 | { |
238 | return sprintf(buf, fmt: "%d\n" , to_bridge(d)->root_port); |
239 | } |
240 | static DEVICE_ATTR_RO(root_port); |
241 | |
242 | static ssize_t root_path_cost_show(struct device *d, |
243 | struct device_attribute *attr, char *buf) |
244 | { |
245 | return sprintf(buf, fmt: "%d\n" , to_bridge(d)->root_path_cost); |
246 | } |
247 | static DEVICE_ATTR_RO(root_path_cost); |
248 | |
249 | static ssize_t topology_change_show(struct device *d, |
250 | struct device_attribute *attr, char *buf) |
251 | { |
252 | return sprintf(buf, fmt: "%d\n" , to_bridge(d)->topology_change); |
253 | } |
254 | static DEVICE_ATTR_RO(topology_change); |
255 | |
256 | static ssize_t topology_change_detected_show(struct device *d, |
257 | struct device_attribute *attr, |
258 | char *buf) |
259 | { |
260 | struct net_bridge *br = to_bridge(d); |
261 | return sprintf(buf, fmt: "%d\n" , br->topology_change_detected); |
262 | } |
263 | static DEVICE_ATTR_RO(topology_change_detected); |
264 | |
265 | static ssize_t hello_timer_show(struct device *d, |
266 | struct device_attribute *attr, char *buf) |
267 | { |
268 | struct net_bridge *br = to_bridge(d); |
269 | return sprintf(buf, fmt: "%ld\n" , br_timer_value(timer: &br->hello_timer)); |
270 | } |
271 | static DEVICE_ATTR_RO(hello_timer); |
272 | |
273 | static ssize_t tcn_timer_show(struct device *d, struct device_attribute *attr, |
274 | char *buf) |
275 | { |
276 | struct net_bridge *br = to_bridge(d); |
277 | return sprintf(buf, fmt: "%ld\n" , br_timer_value(timer: &br->tcn_timer)); |
278 | } |
279 | static DEVICE_ATTR_RO(tcn_timer); |
280 | |
281 | static ssize_t topology_change_timer_show(struct device *d, |
282 | struct device_attribute *attr, |
283 | char *buf) |
284 | { |
285 | struct net_bridge *br = to_bridge(d); |
286 | return sprintf(buf, fmt: "%ld\n" , br_timer_value(timer: &br->topology_change_timer)); |
287 | } |
288 | static DEVICE_ATTR_RO(topology_change_timer); |
289 | |
290 | static ssize_t gc_timer_show(struct device *d, struct device_attribute *attr, |
291 | char *buf) |
292 | { |
293 | struct net_bridge *br = to_bridge(d); |
294 | return sprintf(buf, fmt: "%ld\n" , br_timer_value(timer: &br->gc_work.timer)); |
295 | } |
296 | static DEVICE_ATTR_RO(gc_timer); |
297 | |
298 | static ssize_t group_addr_show(struct device *d, |
299 | struct device_attribute *attr, char *buf) |
300 | { |
301 | struct net_bridge *br = to_bridge(d); |
302 | return sprintf(buf, fmt: "%pM\n" , br->group_addr); |
303 | } |
304 | |
305 | static ssize_t group_addr_store(struct device *d, |
306 | struct device_attribute *attr, |
307 | const char *buf, size_t len) |
308 | { |
309 | struct net_bridge *br = to_bridge(d); |
310 | u8 new_addr[6]; |
311 | |
312 | if (!ns_capable(ns: dev_net(dev: br->dev)->user_ns, CAP_NET_ADMIN)) |
313 | return -EPERM; |
314 | |
315 | if (!mac_pton(s: buf, mac: new_addr)) |
316 | return -EINVAL; |
317 | |
318 | if (!is_link_local_ether_addr(addr: new_addr)) |
319 | return -EINVAL; |
320 | |
321 | if (new_addr[5] == 1 || /* 802.3x Pause address */ |
322 | new_addr[5] == 2 || /* 802.3ad Slow protocols */ |
323 | new_addr[5] == 3) /* 802.1X PAE address */ |
324 | return -EINVAL; |
325 | |
326 | if (!rtnl_trylock()) |
327 | return restart_syscall(); |
328 | |
329 | spin_lock_bh(lock: &br->lock); |
330 | ether_addr_copy(dst: br->group_addr, src: new_addr); |
331 | spin_unlock_bh(lock: &br->lock); |
332 | |
333 | br_opt_toggle(br, opt: BROPT_GROUP_ADDR_SET, on: true); |
334 | br_recalculate_fwd_mask(br); |
335 | netdev_state_change(dev: br->dev); |
336 | |
337 | rtnl_unlock(); |
338 | |
339 | return len; |
340 | } |
341 | |
342 | static DEVICE_ATTR_RW(group_addr); |
343 | |
344 | static int set_flush(struct net_bridge *br, unsigned long val, |
345 | struct netlink_ext_ack *extack) |
346 | { |
347 | struct net_bridge_fdb_flush_desc desc = { |
348 | .flags_mask = BIT(BR_FDB_STATIC) |
349 | }; |
350 | |
351 | br_fdb_flush(br, desc: &desc); |
352 | return 0; |
353 | } |
354 | |
355 | static ssize_t flush_store(struct device *d, |
356 | struct device_attribute *attr, |
357 | const char *buf, size_t len) |
358 | { |
359 | return store_bridge_parm(d, buf, len, set: set_flush); |
360 | } |
361 | static DEVICE_ATTR_WO(flush); |
362 | |
363 | static ssize_t no_linklocal_learn_show(struct device *d, |
364 | struct device_attribute *attr, |
365 | char *buf) |
366 | { |
367 | struct net_bridge *br = to_bridge(d); |
368 | return sprintf(buf, fmt: "%d\n" , br_boolopt_get(br, opt: BR_BOOLOPT_NO_LL_LEARN)); |
369 | } |
370 | |
371 | static int set_no_linklocal_learn(struct net_bridge *br, unsigned long val, |
372 | struct netlink_ext_ack *extack) |
373 | { |
374 | return br_boolopt_toggle(br, opt: BR_BOOLOPT_NO_LL_LEARN, on: !!val, extack); |
375 | } |
376 | |
377 | static ssize_t no_linklocal_learn_store(struct device *d, |
378 | struct device_attribute *attr, |
379 | const char *buf, size_t len) |
380 | { |
381 | return store_bridge_parm(d, buf, len, set: set_no_linklocal_learn); |
382 | } |
383 | static DEVICE_ATTR_RW(no_linklocal_learn); |
384 | |
385 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING |
386 | static ssize_t multicast_router_show(struct device *d, |
387 | struct device_attribute *attr, char *buf) |
388 | { |
389 | struct net_bridge *br = to_bridge(d); |
390 | return sprintf(buf, fmt: "%d\n" , br->multicast_ctx.multicast_router); |
391 | } |
392 | |
393 | static int set_multicast_router(struct net_bridge *br, unsigned long val, |
394 | struct netlink_ext_ack *extack) |
395 | { |
396 | return br_multicast_set_router(brmctx: &br->multicast_ctx, val); |
397 | } |
398 | |
399 | static ssize_t multicast_router_store(struct device *d, |
400 | struct device_attribute *attr, |
401 | const char *buf, size_t len) |
402 | { |
403 | return store_bridge_parm(d, buf, len, set: set_multicast_router); |
404 | } |
405 | static DEVICE_ATTR_RW(multicast_router); |
406 | |
407 | static ssize_t multicast_snooping_show(struct device *d, |
408 | struct device_attribute *attr, |
409 | char *buf) |
410 | { |
411 | struct net_bridge *br = to_bridge(d); |
412 | return sprintf(buf, fmt: "%d\n" , br_opt_get(br, opt: BROPT_MULTICAST_ENABLED)); |
413 | } |
414 | |
415 | static ssize_t multicast_snooping_store(struct device *d, |
416 | struct device_attribute *attr, |
417 | const char *buf, size_t len) |
418 | { |
419 | return store_bridge_parm(d, buf, len, set: br_multicast_toggle); |
420 | } |
421 | static DEVICE_ATTR_RW(multicast_snooping); |
422 | |
423 | static ssize_t multicast_query_use_ifaddr_show(struct device *d, |
424 | struct device_attribute *attr, |
425 | char *buf) |
426 | { |
427 | struct net_bridge *br = to_bridge(d); |
428 | return sprintf(buf, fmt: "%d\n" , |
429 | br_opt_get(br, opt: BROPT_MULTICAST_QUERY_USE_IFADDR)); |
430 | } |
431 | |
432 | static int set_query_use_ifaddr(struct net_bridge *br, unsigned long val, |
433 | struct netlink_ext_ack *extack) |
434 | { |
435 | br_opt_toggle(br, opt: BROPT_MULTICAST_QUERY_USE_IFADDR, on: !!val); |
436 | return 0; |
437 | } |
438 | |
439 | static ssize_t |
440 | multicast_query_use_ifaddr_store(struct device *d, |
441 | struct device_attribute *attr, |
442 | const char *buf, size_t len) |
443 | { |
444 | return store_bridge_parm(d, buf, len, set: set_query_use_ifaddr); |
445 | } |
446 | static DEVICE_ATTR_RW(multicast_query_use_ifaddr); |
447 | |
448 | static ssize_t multicast_querier_show(struct device *d, |
449 | struct device_attribute *attr, |
450 | char *buf) |
451 | { |
452 | struct net_bridge *br = to_bridge(d); |
453 | return sprintf(buf, fmt: "%d\n" , br->multicast_ctx.multicast_querier); |
454 | } |
455 | |
456 | static int set_multicast_querier(struct net_bridge *br, unsigned long val, |
457 | struct netlink_ext_ack *extack) |
458 | { |
459 | return br_multicast_set_querier(brmctx: &br->multicast_ctx, val); |
460 | } |
461 | |
462 | static ssize_t multicast_querier_store(struct device *d, |
463 | struct device_attribute *attr, |
464 | const char *buf, size_t len) |
465 | { |
466 | return store_bridge_parm(d, buf, len, set: set_multicast_querier); |
467 | } |
468 | static DEVICE_ATTR_RW(multicast_querier); |
469 | |
470 | static ssize_t hash_elasticity_show(struct device *d, |
471 | struct device_attribute *attr, char *buf) |
472 | { |
473 | return sprintf(buf, fmt: "%u\n" , RHT_ELASTICITY); |
474 | } |
475 | |
476 | static int set_elasticity(struct net_bridge *br, unsigned long val, |
477 | struct netlink_ext_ack *extack) |
478 | { |
479 | /* 16 is RHT_ELASTICITY */ |
480 | NL_SET_ERR_MSG_MOD(extack, |
481 | "the hash_elasticity option has been deprecated and is always 16" ); |
482 | return 0; |
483 | } |
484 | |
485 | static ssize_t hash_elasticity_store(struct device *d, |
486 | struct device_attribute *attr, |
487 | const char *buf, size_t len) |
488 | { |
489 | return store_bridge_parm(d, buf, len, set: set_elasticity); |
490 | } |
491 | static DEVICE_ATTR_RW(hash_elasticity); |
492 | |
493 | static ssize_t hash_max_show(struct device *d, struct device_attribute *attr, |
494 | char *buf) |
495 | { |
496 | struct net_bridge *br = to_bridge(d); |
497 | return sprintf(buf, fmt: "%u\n" , br->hash_max); |
498 | } |
499 | |
500 | static int set_hash_max(struct net_bridge *br, unsigned long val, |
501 | struct netlink_ext_ack *extack) |
502 | { |
503 | br->hash_max = val; |
504 | return 0; |
505 | } |
506 | |
507 | static ssize_t hash_max_store(struct device *d, struct device_attribute *attr, |
508 | const char *buf, size_t len) |
509 | { |
510 | return store_bridge_parm(d, buf, len, set: set_hash_max); |
511 | } |
512 | static DEVICE_ATTR_RW(hash_max); |
513 | |
514 | static ssize_t multicast_igmp_version_show(struct device *d, |
515 | struct device_attribute *attr, |
516 | char *buf) |
517 | { |
518 | struct net_bridge *br = to_bridge(d); |
519 | |
520 | return sprintf(buf, fmt: "%u\n" , br->multicast_ctx.multicast_igmp_version); |
521 | } |
522 | |
523 | static int set_multicast_igmp_version(struct net_bridge *br, unsigned long val, |
524 | struct netlink_ext_ack *extack) |
525 | { |
526 | return br_multicast_set_igmp_version(brmctx: &br->multicast_ctx, val); |
527 | } |
528 | |
529 | static ssize_t multicast_igmp_version_store(struct device *d, |
530 | struct device_attribute *attr, |
531 | const char *buf, size_t len) |
532 | { |
533 | return store_bridge_parm(d, buf, len, set: set_multicast_igmp_version); |
534 | } |
535 | static DEVICE_ATTR_RW(multicast_igmp_version); |
536 | |
537 | static ssize_t multicast_last_member_count_show(struct device *d, |
538 | struct device_attribute *attr, |
539 | char *buf) |
540 | { |
541 | struct net_bridge *br = to_bridge(d); |
542 | return sprintf(buf, fmt: "%u\n" , br->multicast_ctx.multicast_last_member_count); |
543 | } |
544 | |
545 | static int set_last_member_count(struct net_bridge *br, unsigned long val, |
546 | struct netlink_ext_ack *extack) |
547 | { |
548 | br->multicast_ctx.multicast_last_member_count = val; |
549 | return 0; |
550 | } |
551 | |
552 | static ssize_t multicast_last_member_count_store(struct device *d, |
553 | struct device_attribute *attr, |
554 | const char *buf, size_t len) |
555 | { |
556 | return store_bridge_parm(d, buf, len, set: set_last_member_count); |
557 | } |
558 | static DEVICE_ATTR_RW(multicast_last_member_count); |
559 | |
560 | static ssize_t multicast_startup_query_count_show( |
561 | struct device *d, struct device_attribute *attr, char *buf) |
562 | { |
563 | struct net_bridge *br = to_bridge(d); |
564 | return sprintf(buf, fmt: "%u\n" , br->multicast_ctx.multicast_startup_query_count); |
565 | } |
566 | |
567 | static int set_startup_query_count(struct net_bridge *br, unsigned long val, |
568 | struct netlink_ext_ack *extack) |
569 | { |
570 | br->multicast_ctx.multicast_startup_query_count = val; |
571 | return 0; |
572 | } |
573 | |
574 | static ssize_t multicast_startup_query_count_store( |
575 | struct device *d, struct device_attribute *attr, const char *buf, |
576 | size_t len) |
577 | { |
578 | return store_bridge_parm(d, buf, len, set: set_startup_query_count); |
579 | } |
580 | static DEVICE_ATTR_RW(multicast_startup_query_count); |
581 | |
582 | static ssize_t multicast_last_member_interval_show( |
583 | struct device *d, struct device_attribute *attr, char *buf) |
584 | { |
585 | struct net_bridge *br = to_bridge(d); |
586 | return sprintf(buf, fmt: "%lu\n" , |
587 | jiffies_to_clock_t(x: br->multicast_ctx.multicast_last_member_interval)); |
588 | } |
589 | |
590 | static int set_last_member_interval(struct net_bridge *br, unsigned long val, |
591 | struct netlink_ext_ack *extack) |
592 | { |
593 | br->multicast_ctx.multicast_last_member_interval = clock_t_to_jiffies(x: val); |
594 | return 0; |
595 | } |
596 | |
597 | static ssize_t multicast_last_member_interval_store( |
598 | struct device *d, struct device_attribute *attr, const char *buf, |
599 | size_t len) |
600 | { |
601 | return store_bridge_parm(d, buf, len, set: set_last_member_interval); |
602 | } |
603 | static DEVICE_ATTR_RW(multicast_last_member_interval); |
604 | |
605 | static ssize_t multicast_membership_interval_show( |
606 | struct device *d, struct device_attribute *attr, char *buf) |
607 | { |
608 | struct net_bridge *br = to_bridge(d); |
609 | return sprintf(buf, fmt: "%lu\n" , |
610 | jiffies_to_clock_t(x: br->multicast_ctx.multicast_membership_interval)); |
611 | } |
612 | |
613 | static int set_membership_interval(struct net_bridge *br, unsigned long val, |
614 | struct netlink_ext_ack *extack) |
615 | { |
616 | br->multicast_ctx.multicast_membership_interval = clock_t_to_jiffies(x: val); |
617 | return 0; |
618 | } |
619 | |
620 | static ssize_t multicast_membership_interval_store( |
621 | struct device *d, struct device_attribute *attr, const char *buf, |
622 | size_t len) |
623 | { |
624 | return store_bridge_parm(d, buf, len, set: set_membership_interval); |
625 | } |
626 | static DEVICE_ATTR_RW(multicast_membership_interval); |
627 | |
628 | static ssize_t multicast_querier_interval_show(struct device *d, |
629 | struct device_attribute *attr, |
630 | char *buf) |
631 | { |
632 | struct net_bridge *br = to_bridge(d); |
633 | return sprintf(buf, fmt: "%lu\n" , |
634 | jiffies_to_clock_t(x: br->multicast_ctx.multicast_querier_interval)); |
635 | } |
636 | |
637 | static int set_querier_interval(struct net_bridge *br, unsigned long val, |
638 | struct netlink_ext_ack *extack) |
639 | { |
640 | br->multicast_ctx.multicast_querier_interval = clock_t_to_jiffies(x: val); |
641 | return 0; |
642 | } |
643 | |
644 | static ssize_t multicast_querier_interval_store(struct device *d, |
645 | struct device_attribute *attr, |
646 | const char *buf, size_t len) |
647 | { |
648 | return store_bridge_parm(d, buf, len, set: set_querier_interval); |
649 | } |
650 | static DEVICE_ATTR_RW(multicast_querier_interval); |
651 | |
652 | static ssize_t multicast_query_interval_show(struct device *d, |
653 | struct device_attribute *attr, |
654 | char *buf) |
655 | { |
656 | struct net_bridge *br = to_bridge(d); |
657 | return sprintf(buf, fmt: "%lu\n" , |
658 | jiffies_to_clock_t(x: br->multicast_ctx.multicast_query_interval)); |
659 | } |
660 | |
661 | static int set_query_interval(struct net_bridge *br, unsigned long val, |
662 | struct netlink_ext_ack *extack) |
663 | { |
664 | br_multicast_set_query_intvl(brmctx: &br->multicast_ctx, val); |
665 | return 0; |
666 | } |
667 | |
668 | static ssize_t multicast_query_interval_store(struct device *d, |
669 | struct device_attribute *attr, |
670 | const char *buf, size_t len) |
671 | { |
672 | return store_bridge_parm(d, buf, len, set: set_query_interval); |
673 | } |
674 | static DEVICE_ATTR_RW(multicast_query_interval); |
675 | |
676 | static ssize_t multicast_query_response_interval_show( |
677 | struct device *d, struct device_attribute *attr, char *buf) |
678 | { |
679 | struct net_bridge *br = to_bridge(d); |
680 | return sprintf( |
681 | buf, fmt: "%lu\n" , |
682 | jiffies_to_clock_t(x: br->multicast_ctx.multicast_query_response_interval)); |
683 | } |
684 | |
685 | static int set_query_response_interval(struct net_bridge *br, unsigned long val, |
686 | struct netlink_ext_ack *extack) |
687 | { |
688 | br->multicast_ctx.multicast_query_response_interval = clock_t_to_jiffies(x: val); |
689 | return 0; |
690 | } |
691 | |
692 | static ssize_t multicast_query_response_interval_store( |
693 | struct device *d, struct device_attribute *attr, const char *buf, |
694 | size_t len) |
695 | { |
696 | return store_bridge_parm(d, buf, len, set: set_query_response_interval); |
697 | } |
698 | static DEVICE_ATTR_RW(multicast_query_response_interval); |
699 | |
700 | static ssize_t multicast_startup_query_interval_show( |
701 | struct device *d, struct device_attribute *attr, char *buf) |
702 | { |
703 | struct net_bridge *br = to_bridge(d); |
704 | return sprintf( |
705 | buf, fmt: "%lu\n" , |
706 | jiffies_to_clock_t(x: br->multicast_ctx.multicast_startup_query_interval)); |
707 | } |
708 | |
709 | static int set_startup_query_interval(struct net_bridge *br, unsigned long val, |
710 | struct netlink_ext_ack *extack) |
711 | { |
712 | br_multicast_set_startup_query_intvl(brmctx: &br->multicast_ctx, val); |
713 | return 0; |
714 | } |
715 | |
716 | static ssize_t multicast_startup_query_interval_store( |
717 | struct device *d, struct device_attribute *attr, const char *buf, |
718 | size_t len) |
719 | { |
720 | return store_bridge_parm(d, buf, len, set: set_startup_query_interval); |
721 | } |
722 | static DEVICE_ATTR_RW(multicast_startup_query_interval); |
723 | |
724 | static ssize_t multicast_stats_enabled_show(struct device *d, |
725 | struct device_attribute *attr, |
726 | char *buf) |
727 | { |
728 | struct net_bridge *br = to_bridge(d); |
729 | |
730 | return sprintf(buf, fmt: "%d\n" , |
731 | br_opt_get(br, opt: BROPT_MULTICAST_STATS_ENABLED)); |
732 | } |
733 | |
734 | static int set_stats_enabled(struct net_bridge *br, unsigned long val, |
735 | struct netlink_ext_ack *extack) |
736 | { |
737 | br_opt_toggle(br, opt: BROPT_MULTICAST_STATS_ENABLED, on: !!val); |
738 | return 0; |
739 | } |
740 | |
741 | static ssize_t multicast_stats_enabled_store(struct device *d, |
742 | struct device_attribute *attr, |
743 | const char *buf, |
744 | size_t len) |
745 | { |
746 | return store_bridge_parm(d, buf, len, set: set_stats_enabled); |
747 | } |
748 | static DEVICE_ATTR_RW(multicast_stats_enabled); |
749 | |
750 | #if IS_ENABLED(CONFIG_IPV6) |
751 | static ssize_t multicast_mld_version_show(struct device *d, |
752 | struct device_attribute *attr, |
753 | char *buf) |
754 | { |
755 | struct net_bridge *br = to_bridge(d); |
756 | |
757 | return sprintf(buf, fmt: "%u\n" , br->multicast_ctx.multicast_mld_version); |
758 | } |
759 | |
760 | static int set_multicast_mld_version(struct net_bridge *br, unsigned long val, |
761 | struct netlink_ext_ack *extack) |
762 | { |
763 | return br_multicast_set_mld_version(brmctx: &br->multicast_ctx, val); |
764 | } |
765 | |
766 | static ssize_t multicast_mld_version_store(struct device *d, |
767 | struct device_attribute *attr, |
768 | const char *buf, size_t len) |
769 | { |
770 | return store_bridge_parm(d, buf, len, set: set_multicast_mld_version); |
771 | } |
772 | static DEVICE_ATTR_RW(multicast_mld_version); |
773 | #endif |
774 | #endif |
775 | #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) |
776 | static ssize_t nf_call_iptables_show( |
777 | struct device *d, struct device_attribute *attr, char *buf) |
778 | { |
779 | struct net_bridge *br = to_bridge(d); |
780 | return sprintf(buf, fmt: "%u\n" , br_opt_get(br, opt: BROPT_NF_CALL_IPTABLES)); |
781 | } |
782 | |
783 | static int set_nf_call_iptables(struct net_bridge *br, unsigned long val, |
784 | struct netlink_ext_ack *extack) |
785 | { |
786 | br_opt_toggle(br, opt: BROPT_NF_CALL_IPTABLES, on: !!val); |
787 | return 0; |
788 | } |
789 | |
790 | static ssize_t nf_call_iptables_store( |
791 | struct device *d, struct device_attribute *attr, const char *buf, |
792 | size_t len) |
793 | { |
794 | return store_bridge_parm(d, buf, len, set: set_nf_call_iptables); |
795 | } |
796 | static DEVICE_ATTR_RW(nf_call_iptables); |
797 | |
798 | static ssize_t nf_call_ip6tables_show( |
799 | struct device *d, struct device_attribute *attr, char *buf) |
800 | { |
801 | struct net_bridge *br = to_bridge(d); |
802 | return sprintf(buf, fmt: "%u\n" , br_opt_get(br, opt: BROPT_NF_CALL_IP6TABLES)); |
803 | } |
804 | |
805 | static int set_nf_call_ip6tables(struct net_bridge *br, unsigned long val, |
806 | struct netlink_ext_ack *extack) |
807 | { |
808 | br_opt_toggle(br, opt: BROPT_NF_CALL_IP6TABLES, on: !!val); |
809 | return 0; |
810 | } |
811 | |
812 | static ssize_t nf_call_ip6tables_store( |
813 | struct device *d, struct device_attribute *attr, const char *buf, |
814 | size_t len) |
815 | { |
816 | return store_bridge_parm(d, buf, len, set: set_nf_call_ip6tables); |
817 | } |
818 | static DEVICE_ATTR_RW(nf_call_ip6tables); |
819 | |
820 | static ssize_t nf_call_arptables_show( |
821 | struct device *d, struct device_attribute *attr, char *buf) |
822 | { |
823 | struct net_bridge *br = to_bridge(d); |
824 | return sprintf(buf, fmt: "%u\n" , br_opt_get(br, opt: BROPT_NF_CALL_ARPTABLES)); |
825 | } |
826 | |
827 | static int set_nf_call_arptables(struct net_bridge *br, unsigned long val, |
828 | struct netlink_ext_ack *extack) |
829 | { |
830 | br_opt_toggle(br, opt: BROPT_NF_CALL_ARPTABLES, on: !!val); |
831 | return 0; |
832 | } |
833 | |
834 | static ssize_t nf_call_arptables_store( |
835 | struct device *d, struct device_attribute *attr, const char *buf, |
836 | size_t len) |
837 | { |
838 | return store_bridge_parm(d, buf, len, set: set_nf_call_arptables); |
839 | } |
840 | static DEVICE_ATTR_RW(nf_call_arptables); |
841 | #endif |
842 | #ifdef CONFIG_BRIDGE_VLAN_FILTERING |
843 | static ssize_t vlan_filtering_show(struct device *d, |
844 | struct device_attribute *attr, |
845 | char *buf) |
846 | { |
847 | struct net_bridge *br = to_bridge(d); |
848 | return sprintf(buf, fmt: "%d\n" , br_opt_get(br, opt: BROPT_VLAN_ENABLED)); |
849 | } |
850 | |
851 | static ssize_t vlan_filtering_store(struct device *d, |
852 | struct device_attribute *attr, |
853 | const char *buf, size_t len) |
854 | { |
855 | return store_bridge_parm(d, buf, len, set: br_vlan_filter_toggle); |
856 | } |
857 | static DEVICE_ATTR_RW(vlan_filtering); |
858 | |
859 | static ssize_t vlan_protocol_show(struct device *d, |
860 | struct device_attribute *attr, |
861 | char *buf) |
862 | { |
863 | struct net_bridge *br = to_bridge(d); |
864 | return sprintf(buf, fmt: "%#06x\n" , ntohs(br->vlan_proto)); |
865 | } |
866 | |
867 | static ssize_t vlan_protocol_store(struct device *d, |
868 | struct device_attribute *attr, |
869 | const char *buf, size_t len) |
870 | { |
871 | return store_bridge_parm(d, buf, len, set: br_vlan_set_proto); |
872 | } |
873 | static DEVICE_ATTR_RW(vlan_protocol); |
874 | |
875 | static ssize_t default_pvid_show(struct device *d, |
876 | struct device_attribute *attr, |
877 | char *buf) |
878 | { |
879 | struct net_bridge *br = to_bridge(d); |
880 | return sprintf(buf, fmt: "%d\n" , br->default_pvid); |
881 | } |
882 | |
883 | static ssize_t default_pvid_store(struct device *d, |
884 | struct device_attribute *attr, |
885 | const char *buf, size_t len) |
886 | { |
887 | return store_bridge_parm(d, buf, len, set: br_vlan_set_default_pvid); |
888 | } |
889 | static DEVICE_ATTR_RW(default_pvid); |
890 | |
891 | static ssize_t vlan_stats_enabled_show(struct device *d, |
892 | struct device_attribute *attr, |
893 | char *buf) |
894 | { |
895 | struct net_bridge *br = to_bridge(d); |
896 | return sprintf(buf, fmt: "%u\n" , br_opt_get(br, opt: BROPT_VLAN_STATS_ENABLED)); |
897 | } |
898 | |
899 | static int set_vlan_stats_enabled(struct net_bridge *br, unsigned long val, |
900 | struct netlink_ext_ack *extack) |
901 | { |
902 | return br_vlan_set_stats(br, val); |
903 | } |
904 | |
905 | static ssize_t vlan_stats_enabled_store(struct device *d, |
906 | struct device_attribute *attr, |
907 | const char *buf, size_t len) |
908 | { |
909 | return store_bridge_parm(d, buf, len, set: set_vlan_stats_enabled); |
910 | } |
911 | static DEVICE_ATTR_RW(vlan_stats_enabled); |
912 | |
913 | static ssize_t vlan_stats_per_port_show(struct device *d, |
914 | struct device_attribute *attr, |
915 | char *buf) |
916 | { |
917 | struct net_bridge *br = to_bridge(d); |
918 | return sprintf(buf, fmt: "%u\n" , br_opt_get(br, opt: BROPT_VLAN_STATS_PER_PORT)); |
919 | } |
920 | |
921 | static int set_vlan_stats_per_port(struct net_bridge *br, unsigned long val, |
922 | struct netlink_ext_ack *extack) |
923 | { |
924 | return br_vlan_set_stats_per_port(br, val); |
925 | } |
926 | |
927 | static ssize_t vlan_stats_per_port_store(struct device *d, |
928 | struct device_attribute *attr, |
929 | const char *buf, size_t len) |
930 | { |
931 | return store_bridge_parm(d, buf, len, set: set_vlan_stats_per_port); |
932 | } |
933 | static DEVICE_ATTR_RW(vlan_stats_per_port); |
934 | #endif |
935 | |
936 | static struct attribute *bridge_attrs[] = { |
937 | &dev_attr_forward_delay.attr, |
938 | &dev_attr_hello_time.attr, |
939 | &dev_attr_max_age.attr, |
940 | &dev_attr_ageing_time.attr, |
941 | &dev_attr_stp_state.attr, |
942 | &dev_attr_group_fwd_mask.attr, |
943 | &dev_attr_priority.attr, |
944 | &dev_attr_bridge_id.attr, |
945 | &dev_attr_root_id.attr, |
946 | &dev_attr_root_path_cost.attr, |
947 | &dev_attr_root_port.attr, |
948 | &dev_attr_topology_change.attr, |
949 | &dev_attr_topology_change_detected.attr, |
950 | &dev_attr_hello_timer.attr, |
951 | &dev_attr_tcn_timer.attr, |
952 | &dev_attr_topology_change_timer.attr, |
953 | &dev_attr_gc_timer.attr, |
954 | &dev_attr_group_addr.attr, |
955 | &dev_attr_flush.attr, |
956 | &dev_attr_no_linklocal_learn.attr, |
957 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING |
958 | &dev_attr_multicast_router.attr, |
959 | &dev_attr_multicast_snooping.attr, |
960 | &dev_attr_multicast_querier.attr, |
961 | &dev_attr_multicast_query_use_ifaddr.attr, |
962 | &dev_attr_hash_elasticity.attr, |
963 | &dev_attr_hash_max.attr, |
964 | &dev_attr_multicast_last_member_count.attr, |
965 | &dev_attr_multicast_startup_query_count.attr, |
966 | &dev_attr_multicast_last_member_interval.attr, |
967 | &dev_attr_multicast_membership_interval.attr, |
968 | &dev_attr_multicast_querier_interval.attr, |
969 | &dev_attr_multicast_query_interval.attr, |
970 | &dev_attr_multicast_query_response_interval.attr, |
971 | &dev_attr_multicast_startup_query_interval.attr, |
972 | &dev_attr_multicast_stats_enabled.attr, |
973 | &dev_attr_multicast_igmp_version.attr, |
974 | #if IS_ENABLED(CONFIG_IPV6) |
975 | &dev_attr_multicast_mld_version.attr, |
976 | #endif |
977 | #endif |
978 | #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) |
979 | &dev_attr_nf_call_iptables.attr, |
980 | &dev_attr_nf_call_ip6tables.attr, |
981 | &dev_attr_nf_call_arptables.attr, |
982 | #endif |
983 | #ifdef CONFIG_BRIDGE_VLAN_FILTERING |
984 | &dev_attr_vlan_filtering.attr, |
985 | &dev_attr_vlan_protocol.attr, |
986 | &dev_attr_default_pvid.attr, |
987 | &dev_attr_vlan_stats_enabled.attr, |
988 | &dev_attr_vlan_stats_per_port.attr, |
989 | #endif |
990 | NULL |
991 | }; |
992 | |
993 | static const struct attribute_group bridge_group = { |
994 | .name = SYSFS_BRIDGE_ATTR, |
995 | .attrs = bridge_attrs, |
996 | }; |
997 | |
998 | /* |
999 | * Export the forwarding information table as a binary file |
1000 | * The records are struct __fdb_entry. |
1001 | * |
1002 | * Returns the number of bytes read. |
1003 | */ |
1004 | static ssize_t brforward_read(struct file *filp, struct kobject *kobj, |
1005 | struct bin_attribute *bin_attr, |
1006 | char *buf, loff_t off, size_t count) |
1007 | { |
1008 | struct device *dev = kobj_to_dev(kobj); |
1009 | struct net_bridge *br = to_bridge(dev); |
1010 | int n; |
1011 | |
1012 | /* must read whole records */ |
1013 | if (off % sizeof(struct __fdb_entry) != 0) |
1014 | return -EINVAL; |
1015 | |
1016 | n = br_fdb_fillbuf(br, buf, |
1017 | count: count / sizeof(struct __fdb_entry), |
1018 | off: off / sizeof(struct __fdb_entry)); |
1019 | |
1020 | if (n > 0) |
1021 | n *= sizeof(struct __fdb_entry); |
1022 | |
1023 | return n; |
1024 | } |
1025 | |
1026 | static struct bin_attribute bridge_forward = { |
1027 | .attr = { .name = SYSFS_BRIDGE_FDB, |
1028 | .mode = 0444, }, |
1029 | .read = brforward_read, |
1030 | }; |
1031 | |
1032 | /* |
1033 | * Add entries in sysfs onto the existing network class device |
1034 | * for the bridge. |
1035 | * Adds a attribute group "bridge" containing tuning parameters. |
1036 | * Binary attribute containing the forward table |
1037 | * Sub directory to hold links to interfaces. |
1038 | * |
1039 | * Note: the ifobj exists only to be a subdirectory |
1040 | * to hold links. The ifobj exists in same data structure |
1041 | * as it's parent the bridge so reference counting works. |
1042 | */ |
1043 | int br_sysfs_addbr(struct net_device *dev) |
1044 | { |
1045 | struct kobject *brobj = &dev->dev.kobj; |
1046 | struct net_bridge *br = netdev_priv(dev); |
1047 | int err; |
1048 | |
1049 | err = sysfs_create_group(kobj: brobj, grp: &bridge_group); |
1050 | if (err) { |
1051 | pr_info("%s: can't create group %s/%s\n" , |
1052 | __func__, dev->name, bridge_group.name); |
1053 | goto out1; |
1054 | } |
1055 | |
1056 | err = sysfs_create_bin_file(kobj: brobj, attr: &bridge_forward); |
1057 | if (err) { |
1058 | pr_info("%s: can't create attribute file %s/%s\n" , |
1059 | __func__, dev->name, bridge_forward.attr.name); |
1060 | goto out2; |
1061 | } |
1062 | |
1063 | br->ifobj = kobject_create_and_add(SYSFS_BRIDGE_PORT_SUBDIR, parent: brobj); |
1064 | if (!br->ifobj) { |
1065 | pr_info("%s: can't add kobject (directory) %s/%s\n" , |
1066 | __func__, dev->name, SYSFS_BRIDGE_PORT_SUBDIR); |
1067 | err = -ENOMEM; |
1068 | goto out3; |
1069 | } |
1070 | return 0; |
1071 | out3: |
1072 | sysfs_remove_bin_file(kobj: &dev->dev.kobj, attr: &bridge_forward); |
1073 | out2: |
1074 | sysfs_remove_group(kobj: &dev->dev.kobj, grp: &bridge_group); |
1075 | out1: |
1076 | return err; |
1077 | |
1078 | } |
1079 | |
1080 | void br_sysfs_delbr(struct net_device *dev) |
1081 | { |
1082 | struct kobject *kobj = &dev->dev.kobj; |
1083 | struct net_bridge *br = netdev_priv(dev); |
1084 | |
1085 | kobject_put(kobj: br->ifobj); |
1086 | sysfs_remove_bin_file(kobj, attr: &bridge_forward); |
1087 | sysfs_remove_group(kobj, grp: &bridge_group); |
1088 | } |
1089 | |