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