]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * Sysfs attributes of bridge ports | |
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> | |
13 | #include <linux/if_bridge.h> | |
14 | #include <linux/rtnetlink.h> | |
15 | #include <linux/spinlock.h> | |
174cd4b1 | 16 | #include <linux/sched/signal.h> |
1da177e4 LT |
17 | |
18 | #include "br_private.h" | |
19 | ||
20 | struct brport_attribute { | |
21 | struct attribute attr; | |
22 | ssize_t (*show)(struct net_bridge_port *, char *); | |
14f98f25 | 23 | int (*store)(struct net_bridge_port *, unsigned long); |
a5f3ea54 NA |
24 | int (*store_raw)(struct net_bridge_port *, char *); |
25 | }; | |
26 | ||
27 | #define BRPORT_ATTR_RAW(_name, _mode, _show, _store) \ | |
28 | const struct brport_attribute brport_attr_##_name = { \ | |
29 | .attr = {.name = __stringify(_name), \ | |
30 | .mode = _mode }, \ | |
31 | .show = _show, \ | |
32 | .store_raw = _store, \ | |
1da177e4 LT |
33 | }; |
34 | ||
31a5b837 | 35 | #define BRPORT_ATTR(_name, _mode, _show, _store) \ |
5a0d513b | 36 | const struct brport_attribute brport_attr_##_name = { \ |
1da177e4 | 37 | .attr = {.name = __stringify(_name), \ |
7b595756 | 38 | .mode = _mode }, \ |
1da177e4 LT |
39 | .show = _show, \ |
40 | .store = _store, \ | |
41 | }; | |
42 | ||
cd753732 | 43 | #define BRPORT_ATTR_FLAG(_name, _mask) \ |
44 | static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \ | |
45 | { \ | |
46 | return sprintf(buf, "%d\n", !!(p->flags & _mask)); \ | |
47 | } \ | |
48 | static int store_##_name(struct net_bridge_port *p, unsigned long v) \ | |
49 | { \ | |
63c3a622 | 50 | return store_flag(p, v, _mask); \ |
cd753732 | 51 | } \ |
d6444062 | 52 | static BRPORT_ATTR(_name, 0644, \ |
cd753732 | 53 | show_##_name, store_##_name) |
54 | ||
63c3a622 VY |
55 | static int store_flag(struct net_bridge_port *p, unsigned long v, |
56 | unsigned long mask) | |
57 | { | |
e028e4b8 VY |
58 | unsigned long flags; |
59 | ||
60 | flags = p->flags; | |
63c3a622 VY |
61 | |
62 | if (v) | |
63 | flags |= mask; | |
64 | else | |
65 | flags &= ~mask; | |
66 | ||
67 | if (flags != p->flags) { | |
68 | p->flags = flags; | |
e028e4b8 | 69 | br_port_flags_change(p, mask); |
63c3a622 VY |
70 | } |
71 | return 0; | |
72 | } | |
cd753732 | 73 | |
1da177e4 LT |
74 | static ssize_t show_path_cost(struct net_bridge_port *p, char *buf) |
75 | { | |
76 | return sprintf(buf, "%d\n", p->path_cost); | |
77 | } | |
14f98f25 | 78 | |
d6444062 | 79 | static BRPORT_ATTR(path_cost, 0644, |
14f98f25 | 80 | show_path_cost, br_stp_set_path_cost); |
1da177e4 LT |
81 | |
82 | static ssize_t show_priority(struct net_bridge_port *p, char *buf) | |
83 | { | |
84 | return sprintf(buf, "%d\n", p->priority); | |
85 | } | |
14f98f25 | 86 | |
d6444062 | 87 | static BRPORT_ATTR(priority, 0644, |
14f98f25 | 88 | show_priority, br_stp_set_port_priority); |
1da177e4 LT |
89 | |
90 | static ssize_t show_designated_root(struct net_bridge_port *p, char *buf) | |
91 | { | |
92 | return br_show_bridge_id(buf, &p->designated_root); | |
93 | } | |
d6444062 | 94 | static BRPORT_ATTR(designated_root, 0444, show_designated_root, NULL); |
1da177e4 LT |
95 | |
96 | static ssize_t show_designated_bridge(struct net_bridge_port *p, char *buf) | |
97 | { | |
98 | return br_show_bridge_id(buf, &p->designated_bridge); | |
99 | } | |
d6444062 | 100 | static BRPORT_ATTR(designated_bridge, 0444, show_designated_bridge, NULL); |
1da177e4 LT |
101 | |
102 | static ssize_t show_designated_port(struct net_bridge_port *p, char *buf) | |
103 | { | |
104 | return sprintf(buf, "%d\n", p->designated_port); | |
105 | } | |
d6444062 | 106 | static BRPORT_ATTR(designated_port, 0444, show_designated_port, NULL); |
1da177e4 LT |
107 | |
108 | static ssize_t show_designated_cost(struct net_bridge_port *p, char *buf) | |
109 | { | |
110 | return sprintf(buf, "%d\n", p->designated_cost); | |
111 | } | |
d6444062 | 112 | static BRPORT_ATTR(designated_cost, 0444, show_designated_cost, NULL); |
1da177e4 LT |
113 | |
114 | static ssize_t show_port_id(struct net_bridge_port *p, char *buf) | |
115 | { | |
116 | return sprintf(buf, "0x%x\n", p->port_id); | |
117 | } | |
d6444062 | 118 | static BRPORT_ATTR(port_id, 0444, show_port_id, NULL); |
1da177e4 LT |
119 | |
120 | static ssize_t show_port_no(struct net_bridge_port *p, char *buf) | |
121 | { | |
122 | return sprintf(buf, "0x%x\n", p->port_no); | |
123 | } | |
124 | ||
d6444062 | 125 | static BRPORT_ATTR(port_no, 0444, show_port_no, NULL); |
1da177e4 LT |
126 | |
127 | static ssize_t show_change_ack(struct net_bridge_port *p, char *buf) | |
128 | { | |
129 | return sprintf(buf, "%d\n", p->topology_change_ack); | |
130 | } | |
d6444062 | 131 | static BRPORT_ATTR(change_ack, 0444, show_change_ack, NULL); |
1da177e4 LT |
132 | |
133 | static ssize_t show_config_pending(struct net_bridge_port *p, char *buf) | |
134 | { | |
135 | return sprintf(buf, "%d\n", p->config_pending); | |
136 | } | |
d6444062 | 137 | static BRPORT_ATTR(config_pending, 0444, show_config_pending, NULL); |
1da177e4 LT |
138 | |
139 | static ssize_t show_port_state(struct net_bridge_port *p, char *buf) | |
140 | { | |
141 | return sprintf(buf, "%d\n", p->state); | |
142 | } | |
d6444062 | 143 | static BRPORT_ATTR(state, 0444, show_port_state, NULL); |
1da177e4 LT |
144 | |
145 | static ssize_t show_message_age_timer(struct net_bridge_port *p, | |
146 | char *buf) | |
147 | { | |
148 | return sprintf(buf, "%ld\n", br_timer_value(&p->message_age_timer)); | |
149 | } | |
d6444062 | 150 | static BRPORT_ATTR(message_age_timer, 0444, show_message_age_timer, NULL); |
1da177e4 LT |
151 | |
152 | static ssize_t show_forward_delay_timer(struct net_bridge_port *p, | |
153 | char *buf) | |
154 | { | |
155 | return sprintf(buf, "%ld\n", br_timer_value(&p->forward_delay_timer)); | |
156 | } | |
d6444062 | 157 | static BRPORT_ATTR(forward_delay_timer, 0444, show_forward_delay_timer, NULL); |
1da177e4 LT |
158 | |
159 | static ssize_t show_hold_timer(struct net_bridge_port *p, | |
160 | char *buf) | |
161 | { | |
162 | return sprintf(buf, "%ld\n", br_timer_value(&p->hold_timer)); | |
163 | } | |
d6444062 | 164 | static BRPORT_ATTR(hold_timer, 0444, show_hold_timer, NULL); |
1da177e4 | 165 | |
14f98f25 | 166 | static int store_flush(struct net_bridge_port *p, unsigned long v) |
9cf63747 | 167 | { |
1ea2d020 | 168 | br_fdb_delete_by_port(p->br, p, 0, 0); // Don't delete local entry |
9cf63747 SH |
169 | return 0; |
170 | } | |
d6444062 | 171 | static BRPORT_ATTR(flush, 0200, NULL, store_flush); |
9cf63747 | 172 | |
5af48b59 NA |
173 | static ssize_t show_group_fwd_mask(struct net_bridge_port *p, char *buf) |
174 | { | |
175 | return sprintf(buf, "%#x\n", p->group_fwd_mask); | |
176 | } | |
177 | ||
178 | static int store_group_fwd_mask(struct net_bridge_port *p, | |
179 | unsigned long v) | |
180 | { | |
181 | if (v & BR_GROUPFWD_MACPAUSE) | |
182 | return -EINVAL; | |
183 | p->group_fwd_mask = v; | |
184 | ||
185 | return 0; | |
186 | } | |
d6444062 | 187 | static BRPORT_ATTR(group_fwd_mask, 0644, show_group_fwd_mask, |
5af48b59 NA |
188 | store_group_fwd_mask); |
189 | ||
2756f68c NA |
190 | static ssize_t show_backup_port(struct net_bridge_port *p, char *buf) |
191 | { | |
192 | struct net_bridge_port *backup_p; | |
193 | int ret = 0; | |
194 | ||
195 | rcu_read_lock(); | |
196 | backup_p = rcu_dereference(p->backup_port); | |
197 | if (backup_p) | |
198 | ret = sprintf(buf, "%s\n", backup_p->dev->name); | |
199 | rcu_read_unlock(); | |
200 | ||
201 | return ret; | |
202 | } | |
203 | ||
204 | static int store_backup_port(struct net_bridge_port *p, char *buf) | |
205 | { | |
206 | struct net_device *backup_dev = NULL; | |
207 | char *nl = strchr(buf, '\n'); | |
208 | ||
209 | if (nl) | |
210 | *nl = '\0'; | |
211 | ||
212 | if (strlen(buf) > 0) { | |
213 | backup_dev = __dev_get_by_name(dev_net(p->dev), buf); | |
214 | if (!backup_dev) | |
215 | return -ENOENT; | |
216 | } | |
217 | ||
218 | return nbp_backup_change(p, backup_dev); | |
219 | } | |
220 | static BRPORT_ATTR_RAW(backup_port, 0644, show_backup_port, store_backup_port); | |
221 | ||
cd753732 | 222 | BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE); |
a2e01a65 | 223 | BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD); |
1007dd1a | 224 | BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK); |
9ba18891 | 225 | BRPORT_ATTR_FLAG(learning, BR_LEARNING); |
867a5943 | 226 | BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD); |
95850116 | 227 | BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP); |
842a9ae0 | 228 | BRPORT_ATTR_FLAG(proxyarp_wifi, BR_PROXYARP_WIFI); |
b6cb5ac8 | 229 | BRPORT_ATTR_FLAG(multicast_flood, BR_MCAST_FLOOD); |
99f906e9 | 230 | BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD); |
821f1b21 | 231 | BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS); |
7d850abd | 232 | BRPORT_ATTR_FLAG(isolated, BR_ISOLATED); |
3982d3d2 | 233 | |
0909e117 HX |
234 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING |
235 | static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) | |
236 | { | |
237 | return sprintf(buf, "%d\n", p->multicast_router); | |
238 | } | |
239 | ||
14f98f25 | 240 | static int store_multicast_router(struct net_bridge_port *p, |
0909e117 HX |
241 | unsigned long v) |
242 | { | |
243 | return br_multicast_set_port_router(p, v); | |
244 | } | |
d6444062 | 245 | static BRPORT_ATTR(multicast_router, 0644, show_multicast_router, |
0909e117 | 246 | store_multicast_router); |
50426b59 | 247 | |
c2d3babf | 248 | BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE); |
6db6f0ea | 249 | BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST); |
0909e117 HX |
250 | #endif |
251 | ||
5a0d513b | 252 | static const struct brport_attribute *brport_attrs[] = { |
1da177e4 LT |
253 | &brport_attr_path_cost, |
254 | &brport_attr_priority, | |
255 | &brport_attr_port_id, | |
256 | &brport_attr_port_no, | |
257 | &brport_attr_designated_root, | |
258 | &brport_attr_designated_bridge, | |
259 | &brport_attr_designated_port, | |
260 | &brport_attr_designated_cost, | |
261 | &brport_attr_state, | |
262 | &brport_attr_change_ack, | |
263 | &brport_attr_config_pending, | |
264 | &brport_attr_message_age_timer, | |
265 | &brport_attr_forward_delay_timer, | |
266 | &brport_attr_hold_timer, | |
9cf63747 | 267 | &brport_attr_flush, |
3982d3d2 | 268 | &brport_attr_hairpin_mode, |
a2e01a65 | 269 | &brport_attr_bpdu_guard, |
1007dd1a | 270 | &brport_attr_root_block, |
9ba18891 | 271 | &brport_attr_learning, |
867a5943 | 272 | &brport_attr_unicast_flood, |
0909e117 HX |
273 | #ifdef CONFIG_BRIDGE_IGMP_SNOOPING |
274 | &brport_attr_multicast_router, | |
50426b59 | 275 | &brport_attr_multicast_fast_leave, |
6db6f0ea | 276 | &brport_attr_multicast_to_unicast, |
0909e117 | 277 | #endif |
95850116 | 278 | &brport_attr_proxyarp, |
842a9ae0 | 279 | &brport_attr_proxyarp_wifi, |
4eb6753c | 280 | &brport_attr_multicast_flood, |
99f906e9 | 281 | &brport_attr_broadcast_flood, |
5af48b59 | 282 | &brport_attr_group_fwd_mask, |
821f1b21 | 283 | &brport_attr_neigh_suppress, |
7d850abd | 284 | &brport_attr_isolated, |
2756f68c | 285 | &brport_attr_backup_port, |
1da177e4 LT |
286 | NULL |
287 | }; | |
288 | ||
289 | #define to_brport_attr(_at) container_of(_at, struct brport_attribute, attr) | |
1da177e4 | 290 | |
56b148eb | 291 | static ssize_t brport_show(struct kobject *kobj, |
292 | struct attribute *attr, char *buf) | |
1da177e4 | 293 | { |
56b148eb | 294 | struct brport_attribute *brport_attr = to_brport_attr(attr); |
705e0dea | 295 | struct net_bridge_port *p = kobj_to_brport(kobj); |
1da177e4 | 296 | |
1b12580a XL |
297 | if (!brport_attr->show) |
298 | return -EINVAL; | |
299 | ||
1da177e4 LT |
300 | return brport_attr->show(p, buf); |
301 | } | |
302 | ||
56b148eb | 303 | static ssize_t brport_store(struct kobject *kobj, |
304 | struct attribute *attr, | |
305 | const char *buf, size_t count) | |
1da177e4 | 306 | { |
56b148eb | 307 | struct brport_attribute *brport_attr = to_brport_attr(attr); |
705e0dea | 308 | struct net_bridge_port *p = kobj_to_brport(kobj); |
1da177e4 | 309 | ssize_t ret = -EINVAL; |
1da177e4 | 310 | unsigned long val; |
a5f3ea54 | 311 | char *endp; |
1da177e4 | 312 | |
cb990503 | 313 | if (!ns_capable(dev_net(p->dev)->user_ns, CAP_NET_ADMIN)) |
1da177e4 LT |
314 | return -EPERM; |
315 | ||
a5f3ea54 NA |
316 | if (!rtnl_trylock()) |
317 | return restart_syscall(); | |
318 | ||
a5f3ea54 NA |
319 | if (brport_attr->store_raw) { |
320 | char *buf_copy; | |
321 | ||
322 | buf_copy = kstrndup(buf, count, GFP_KERNEL); | |
323 | if (!buf_copy) { | |
324 | ret = -ENOMEM; | |
325 | goto out_unlock; | |
1da177e4 | 326 | } |
a5f3ea54 NA |
327 | spin_lock_bh(&p->br->lock); |
328 | ret = brport_attr->store_raw(p, buf_copy); | |
329 | spin_unlock_bh(&p->br->lock); | |
330 | kfree(buf_copy); | |
331 | } else if (brport_attr->store) { | |
332 | val = simple_strtoul(buf, &endp, 0); | |
333 | if (endp == buf) | |
334 | goto out_unlock; | |
335 | spin_lock_bh(&p->br->lock); | |
336 | ret = brport_attr->store(p, val); | |
337 | spin_unlock_bh(&p->br->lock); | |
1da177e4 | 338 | } |
a5f3ea54 NA |
339 | |
340 | if (!ret) { | |
341 | br_ifinfo_notify(RTM_NEWLINK, NULL, p); | |
342 | ret = count; | |
343 | } | |
344 | out_unlock: | |
345 | rtnl_unlock(); | |
346 | ||
1da177e4 LT |
347 | return ret; |
348 | } | |
349 | ||
52cf25d0 | 350 | const struct sysfs_ops brport_sysfs_ops = { |
1da177e4 LT |
351 | .show = brport_show, |
352 | .store = brport_store, | |
353 | }; | |
354 | ||
1da177e4 LT |
355 | /* |
356 | * Add sysfs entries to ethernet device added to a bridge. | |
357 | * Creates a brport subdirectory with bridge attributes. | |
e0f43752 | 358 | * Puts symlink in bridge's brif subdirectory |
1da177e4 LT |
359 | */ |
360 | int br_sysfs_addif(struct net_bridge_port *p) | |
361 | { | |
362 | struct net_bridge *br = p->br; | |
5a0d513b | 363 | const struct brport_attribute **a; |
1da177e4 LT |
364 | int err; |
365 | ||
43cb76d9 | 366 | err = sysfs_create_link(&p->kobj, &br->dev->dev.kobj, |
1da177e4 LT |
367 | SYSFS_BRIDGE_PORT_LINK); |
368 | if (err) | |
e0f43752 | 369 | return err; |
1da177e4 LT |
370 | |
371 | for (a = brport_attrs; *a; ++a) { | |
372 | err = sysfs_create_file(&p->kobj, &((*a)->attr)); | |
373 | if (err) | |
e0f43752 | 374 | return err; |
1da177e4 LT |
375 | } |
376 | ||
e0f43752 SA |
377 | strlcpy(p->sysfs_name, p->dev->name, IFNAMSIZ); |
378 | return sysfs_create_link(br->ifobj, &p->kobj, p->sysfs_name); | |
379 | } | |
380 | ||
381 | /* Rename bridge's brif symlink */ | |
382 | int br_sysfs_renameif(struct net_bridge_port *p) | |
383 | { | |
384 | struct net_bridge *br = p->br; | |
385 | int err; | |
386 | ||
387 | /* If a rename fails, the rollback will cause another | |
388 | * rename call with the existing name. | |
389 | */ | |
390 | if (!strncmp(p->sysfs_name, p->dev->name, IFNAMSIZ)) | |
391 | return 0; | |
392 | ||
393 | err = sysfs_rename_link(br->ifobj, &p->kobj, | |
394 | p->sysfs_name, p->dev->name); | |
395 | if (err) | |
396 | netdev_notice(br->dev, "unable to rename link %s to %s", | |
397 | p->sysfs_name, p->dev->name); | |
398 | else | |
399 | strlcpy(p->sysfs_name, p->dev->name, IFNAMSIZ); | |
400 | ||
1da177e4 LT |
401 | return err; |
402 | } |