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