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