xref: /openbmc/linux/net/bridge/br_sysfs_br.c (revision 2874c5fd284268364ece81a7bd936f3c8168e567)
1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
315401946SWang Sheng-Hui  *	Sysfs attributes of bridge
41da177e4SLinus Torvalds  *	Linux ethernet bridge
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *	Authors:
71da177e4SLinus Torvalds  *	Stephen Hemminger		<shemminger@osdl.org>
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
104fc268d2SRandy Dunlap #include <linux/capability.h>
111da177e4SLinus Torvalds #include <linux/kernel.h>
121da177e4SLinus Torvalds #include <linux/netdevice.h>
13b3343a2aSJohn Fastabend #include <linux/etherdevice.h>
141da177e4SLinus Torvalds #include <linux/if_bridge.h>
151da177e4SLinus Torvalds #include <linux/rtnetlink.h>
161da177e4SLinus Torvalds #include <linux/spinlock.h>
171da177e4SLinus Torvalds #include <linux/times.h>
18174cd4b1SIngo Molnar #include <linux/sched/signal.h>
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #include "br_private.h"
211da177e4SLinus Torvalds 
22524ad0a7SWang Chen #define to_bridge(cd)	((struct net_bridge *)netdev_priv(to_net_dev(cd)))
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds /*
251da177e4SLinus Torvalds  * Common code for storing bridge parameters.
261da177e4SLinus Torvalds  */
2743cb76d9SGreg Kroah-Hartman static ssize_t store_bridge_parm(struct device *d,
281da177e4SLinus Torvalds 				 const char *buf, size_t len,
298d4698f7SStephen Hemminger 				 int (*set)(struct net_bridge *, unsigned long))
301da177e4SLinus Torvalds {
3143cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
321da177e4SLinus Torvalds 	char *endp;
331da177e4SLinus Torvalds 	unsigned long val;
348d4698f7SStephen Hemminger 	int err;
351da177e4SLinus Torvalds 
36cb990503SEric W. Biederman 	if (!ns_capable(dev_net(br->dev)->user_ns, CAP_NET_ADMIN))
371da177e4SLinus Torvalds 		return -EPERM;
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds 	val = simple_strtoul(buf, &endp, 0);
401da177e4SLinus Torvalds 	if (endp == buf)
411da177e4SLinus Torvalds 		return -EINVAL;
421da177e4SLinus Torvalds 
43047831a9SXin Long 	if (!rtnl_trylock())
44047831a9SXin Long 		return restart_syscall();
45047831a9SXin Long 
468d4698f7SStephen Hemminger 	err = (*set)(br, val);
47047831a9SXin Long 	if (!err)
48047831a9SXin Long 		netdev_state_change(br->dev);
49047831a9SXin Long 	rtnl_unlock();
50047831a9SXin Long 
518d4698f7SStephen Hemminger 	return err ? err : len;
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 
55fbf2671bSsfeldma@cumulusnetworks.com static ssize_t forward_delay_show(struct device *d,
5643cb76d9SGreg Kroah-Hartman 				  struct device_attribute *attr, char *buf)
571da177e4SLinus Torvalds {
5843cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
591da177e4SLinus Torvalds 	return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
601da177e4SLinus Torvalds }
611da177e4SLinus Torvalds 
62fbf2671bSsfeldma@cumulusnetworks.com static ssize_t forward_delay_store(struct device *d,
6343cb76d9SGreg Kroah-Hartman 				   struct device_attribute *attr,
6443cb76d9SGreg Kroah-Hartman 				   const char *buf, size_t len)
651da177e4SLinus Torvalds {
6614f98f25Sstephen hemminger 	return store_bridge_parm(d, buf, len, br_set_forward_delay);
671da177e4SLinus Torvalds }
68fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(forward_delay);
691da177e4SLinus Torvalds 
70fbf2671bSsfeldma@cumulusnetworks.com static ssize_t hello_time_show(struct device *d, struct device_attribute *attr,
7143cb76d9SGreg Kroah-Hartman 			       char *buf)
721da177e4SLinus Torvalds {
731da177e4SLinus Torvalds 	return sprintf(buf, "%lu\n",
7443cb76d9SGreg Kroah-Hartman 		       jiffies_to_clock_t(to_bridge(d)->hello_time));
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds 
77fbf2671bSsfeldma@cumulusnetworks.com static ssize_t hello_time_store(struct device *d,
7843cb76d9SGreg Kroah-Hartman 				struct device_attribute *attr, const char *buf,
791da177e4SLinus Torvalds 				size_t len)
801da177e4SLinus Torvalds {
8114f98f25Sstephen hemminger 	return store_bridge_parm(d, buf, len, br_set_hello_time);
821da177e4SLinus Torvalds }
83fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(hello_time);
841da177e4SLinus Torvalds 
85fbf2671bSsfeldma@cumulusnetworks.com static ssize_t max_age_show(struct device *d, struct device_attribute *attr,
8643cb76d9SGreg Kroah-Hartman 			    char *buf)
871da177e4SLinus Torvalds {
881da177e4SLinus Torvalds 	return sprintf(buf, "%lu\n",
8943cb76d9SGreg Kroah-Hartman 		       jiffies_to_clock_t(to_bridge(d)->max_age));
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
92fbf2671bSsfeldma@cumulusnetworks.com static ssize_t max_age_store(struct device *d, struct device_attribute *attr,
9343cb76d9SGreg Kroah-Hartman 			     const char *buf, size_t len)
941da177e4SLinus Torvalds {
9514f98f25Sstephen hemminger 	return store_bridge_parm(d, buf, len, br_set_max_age);
961da177e4SLinus Torvalds }
97fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(max_age);
981da177e4SLinus Torvalds 
99fbf2671bSsfeldma@cumulusnetworks.com static ssize_t ageing_time_show(struct device *d,
10043cb76d9SGreg Kroah-Hartman 				struct device_attribute *attr, char *buf)
1011da177e4SLinus Torvalds {
10243cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
1031da177e4SLinus Torvalds 	return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time));
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
1068d4698f7SStephen Hemminger static int set_ageing_time(struct net_bridge *br, unsigned long val)
1071da177e4SLinus Torvalds {
108047831a9SXin Long 	return br_set_ageing_time(br, val);
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds 
111fbf2671bSsfeldma@cumulusnetworks.com static ssize_t ageing_time_store(struct device *d,
11243cb76d9SGreg Kroah-Hartman 				 struct device_attribute *attr,
11343cb76d9SGreg Kroah-Hartman 				 const char *buf, size_t len)
1141da177e4SLinus Torvalds {
11543cb76d9SGreg Kroah-Hartman 	return store_bridge_parm(d, buf, len, set_ageing_time);
1161da177e4SLinus Torvalds }
117fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(ageing_time);
11843cb76d9SGreg Kroah-Hartman 
119fbf2671bSsfeldma@cumulusnetworks.com static ssize_t stp_state_show(struct device *d,
12043cb76d9SGreg Kroah-Hartman 			      struct device_attribute *attr, char *buf)
1211da177e4SLinus Torvalds {
12243cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
1231da177e4SLinus Torvalds 	return sprintf(buf, "%d\n", br->stp_enabled);
1241da177e4SLinus Torvalds }
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 
1274436156bSXin Long static int set_stp_state(struct net_bridge *br, unsigned long val)
1281da177e4SLinus Torvalds {
12917120889SStephen Hemminger 	br_stp_set_enabled(br, val);
13017120889SStephen Hemminger 
1314436156bSXin Long 	return 0;
1324436156bSXin Long }
1334436156bSXin Long 
1344436156bSXin Long static ssize_t stp_state_store(struct device *d,
1354436156bSXin Long 			       struct device_attribute *attr, const char *buf,
1364436156bSXin Long 			       size_t len)
1374436156bSXin Long {
1384436156bSXin Long 	return store_bridge_parm(d, buf, len, set_stp_state);
1391da177e4SLinus Torvalds }
140fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(stp_state);
1411da177e4SLinus Torvalds 
142fbf2671bSsfeldma@cumulusnetworks.com static ssize_t group_fwd_mask_show(struct device *d,
143fbf2671bSsfeldma@cumulusnetworks.com 				   struct device_attribute *attr,
144fbf2671bSsfeldma@cumulusnetworks.com 				   char *buf)
145515853ccSstephen hemminger {
146515853ccSstephen hemminger 	struct net_bridge *br = to_bridge(d);
147515853ccSstephen hemminger 	return sprintf(buf, "%#x\n", br->group_fwd_mask);
148515853ccSstephen hemminger }
149515853ccSstephen hemminger 
150347db6b4SXin Long static int set_group_fwd_mask(struct net_bridge *br, unsigned long val)
151347db6b4SXin Long {
152347db6b4SXin Long 	if (val & BR_GROUPFWD_RESTRICTED)
153347db6b4SXin Long 		return -EINVAL;
154347db6b4SXin Long 
155347db6b4SXin Long 	br->group_fwd_mask = val;
156347db6b4SXin Long 
157347db6b4SXin Long 	return 0;
158347db6b4SXin Long }
159515853ccSstephen hemminger 
160fbf2671bSsfeldma@cumulusnetworks.com static ssize_t group_fwd_mask_store(struct device *d,
161fbf2671bSsfeldma@cumulusnetworks.com 				    struct device_attribute *attr,
162fbf2671bSsfeldma@cumulusnetworks.com 				    const char *buf,
163515853ccSstephen hemminger 				    size_t len)
164515853ccSstephen hemminger {
165347db6b4SXin Long 	return store_bridge_parm(d, buf, len, set_group_fwd_mask);
166515853ccSstephen hemminger }
167fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(group_fwd_mask);
168515853ccSstephen hemminger 
169fbf2671bSsfeldma@cumulusnetworks.com static ssize_t priority_show(struct device *d, struct device_attribute *attr,
17043cb76d9SGreg Kroah-Hartman 			     char *buf)
1711da177e4SLinus Torvalds {
17243cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
1731da177e4SLinus Torvalds 	return sprintf(buf, "%d\n",
1741da177e4SLinus Torvalds 		       (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]);
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds 
1778d4698f7SStephen Hemminger static int set_priority(struct net_bridge *br, unsigned long val)
1781da177e4SLinus Torvalds {
1791da177e4SLinus Torvalds 	br_stp_set_bridge_priority(br, (u16) val);
1808d4698f7SStephen Hemminger 	return 0;
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds 
183fbf2671bSsfeldma@cumulusnetworks.com static ssize_t priority_store(struct device *d, struct device_attribute *attr,
1841da177e4SLinus Torvalds 			      const char *buf, size_t len)
1851da177e4SLinus Torvalds {
18643cb76d9SGreg Kroah-Hartman 	return store_bridge_parm(d, buf, len, set_priority);
1871da177e4SLinus Torvalds }
188fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(priority);
1891da177e4SLinus Torvalds 
190fbf2671bSsfeldma@cumulusnetworks.com static ssize_t root_id_show(struct device *d, struct device_attribute *attr,
19143cb76d9SGreg Kroah-Hartman 			    char *buf)
1921da177e4SLinus Torvalds {
19343cb76d9SGreg Kroah-Hartman 	return br_show_bridge_id(buf, &to_bridge(d)->designated_root);
1941da177e4SLinus Torvalds }
195fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RO(root_id);
1961da177e4SLinus Torvalds 
197fbf2671bSsfeldma@cumulusnetworks.com static ssize_t bridge_id_show(struct device *d, struct device_attribute *attr,
19843cb76d9SGreg Kroah-Hartman 			      char *buf)
1991da177e4SLinus Torvalds {
20043cb76d9SGreg Kroah-Hartman 	return br_show_bridge_id(buf, &to_bridge(d)->bridge_id);
2011da177e4SLinus Torvalds }
202fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RO(bridge_id);
2031da177e4SLinus Torvalds 
204fbf2671bSsfeldma@cumulusnetworks.com static ssize_t root_port_show(struct device *d, struct device_attribute *attr,
20543cb76d9SGreg Kroah-Hartman 			      char *buf)
2061da177e4SLinus Torvalds {
20743cb76d9SGreg Kroah-Hartman 	return sprintf(buf, "%d\n", to_bridge(d)->root_port);
2081da177e4SLinus Torvalds }
209fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RO(root_port);
2101da177e4SLinus Torvalds 
211fbf2671bSsfeldma@cumulusnetworks.com static ssize_t root_path_cost_show(struct device *d,
21243cb76d9SGreg Kroah-Hartman 				   struct device_attribute *attr, char *buf)
2131da177e4SLinus Torvalds {
21443cb76d9SGreg Kroah-Hartman 	return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost);
2151da177e4SLinus Torvalds }
216fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RO(root_path_cost);
2171da177e4SLinus Torvalds 
218fbf2671bSsfeldma@cumulusnetworks.com static ssize_t topology_change_show(struct device *d,
21943cb76d9SGreg Kroah-Hartman 				    struct device_attribute *attr, char *buf)
2201da177e4SLinus Torvalds {
22143cb76d9SGreg Kroah-Hartman 	return sprintf(buf, "%d\n", to_bridge(d)->topology_change);
2221da177e4SLinus Torvalds }
223fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RO(topology_change);
2241da177e4SLinus Torvalds 
225fbf2671bSsfeldma@cumulusnetworks.com static ssize_t topology_change_detected_show(struct device *d,
22643cb76d9SGreg Kroah-Hartman 					     struct device_attribute *attr,
22743cb76d9SGreg Kroah-Hartman 					     char *buf)
2281da177e4SLinus Torvalds {
22943cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
2301da177e4SLinus Torvalds 	return sprintf(buf, "%d\n", br->topology_change_detected);
2311da177e4SLinus Torvalds }
232fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RO(topology_change_detected);
2331da177e4SLinus Torvalds 
234fbf2671bSsfeldma@cumulusnetworks.com static ssize_t hello_timer_show(struct device *d,
23543cb76d9SGreg Kroah-Hartman 				struct device_attribute *attr, char *buf)
2361da177e4SLinus Torvalds {
23743cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
2381da177e4SLinus Torvalds 	return sprintf(buf, "%ld\n", br_timer_value(&br->hello_timer));
2391da177e4SLinus Torvalds }
240fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RO(hello_timer);
2411da177e4SLinus Torvalds 
242fbf2671bSsfeldma@cumulusnetworks.com static ssize_t tcn_timer_show(struct device *d, struct device_attribute *attr,
24343cb76d9SGreg Kroah-Hartman 			      char *buf)
2441da177e4SLinus Torvalds {
24543cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
2461da177e4SLinus Torvalds 	return sprintf(buf, "%ld\n", br_timer_value(&br->tcn_timer));
2471da177e4SLinus Torvalds }
248fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RO(tcn_timer);
2491da177e4SLinus Torvalds 
250fbf2671bSsfeldma@cumulusnetworks.com static ssize_t topology_change_timer_show(struct device *d,
25143cb76d9SGreg Kroah-Hartman 					  struct device_attribute *attr,
25243cb76d9SGreg Kroah-Hartman 					  char *buf)
2531da177e4SLinus Torvalds {
25443cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
2551da177e4SLinus Torvalds 	return sprintf(buf, "%ld\n", br_timer_value(&br->topology_change_timer));
2561da177e4SLinus Torvalds }
257fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RO(topology_change_timer);
2581da177e4SLinus Torvalds 
259fbf2671bSsfeldma@cumulusnetworks.com static ssize_t gc_timer_show(struct device *d, struct device_attribute *attr,
26043cb76d9SGreg Kroah-Hartman 			     char *buf)
2611da177e4SLinus Torvalds {
26243cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
263f7cdee8aSNikolay Aleksandrov 	return sprintf(buf, "%ld\n", br_timer_value(&br->gc_work.timer));
2641da177e4SLinus Torvalds }
265fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RO(gc_timer);
2661da177e4SLinus Torvalds 
267fbf2671bSsfeldma@cumulusnetworks.com static ssize_t group_addr_show(struct device *d,
26843cb76d9SGreg Kroah-Hartman 			       struct device_attribute *attr, char *buf)
269fda93d92SStephen Hemminger {
27043cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
271223b229bSAndy Shevchenko 	return sprintf(buf, "%pM\n", br->group_addr);
272fda93d92SStephen Hemminger }
273fda93d92SStephen Hemminger 
274fbf2671bSsfeldma@cumulusnetworks.com static ssize_t group_addr_store(struct device *d,
27543cb76d9SGreg Kroah-Hartman 				struct device_attribute *attr,
27643cb76d9SGreg Kroah-Hartman 				const char *buf, size_t len)
277fda93d92SStephen Hemminger {
27843cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(d);
2794197f24bSBen Hutchings 	u8 new_addr[6];
280fda93d92SStephen Hemminger 
281cb990503SEric W. Biederman 	if (!ns_capable(dev_net(br->dev)->user_ns, CAP_NET_ADMIN))
282fda93d92SStephen Hemminger 		return -EPERM;
283fda93d92SStephen Hemminger 
284223b229bSAndy Shevchenko 	if (!mac_pton(buf, new_addr))
285fda93d92SStephen Hemminger 		return -EINVAL;
286fda93d92SStephen Hemminger 
28746acc460SBen Hutchings 	if (!is_link_local_ether_addr(new_addr))
288fda93d92SStephen Hemminger 		return -EINVAL;
289fda93d92SStephen Hemminger 
290f64f9e71SJoe Perches 	if (new_addr[5] == 1 ||		/* 802.3x Pause address */
291f64f9e71SJoe Perches 	    new_addr[5] == 2 ||		/* 802.3ad Slow protocols */
292f64f9e71SJoe Perches 	    new_addr[5] == 3)		/* 802.1X PAE address */
293fda93d92SStephen Hemminger 		return -EINVAL;
294fda93d92SStephen Hemminger 
295204177f3SToshiaki Makita 	if (!rtnl_trylock())
296204177f3SToshiaki Makita 		return restart_syscall();
297204177f3SToshiaki Makita 
298fda93d92SStephen Hemminger 	spin_lock_bh(&br->lock);
299223b229bSAndy Shevchenko 	ether_addr_copy(br->group_addr, new_addr);
300fda93d92SStephen Hemminger 	spin_unlock_bh(&br->lock);
301204177f3SToshiaki Makita 
302be3664a0SNikolay Aleksandrov 	br_opt_toggle(br, BROPT_GROUP_ADDR_SET, true);
303204177f3SToshiaki Makita 	br_recalculate_fwd_mask(br);
304047831a9SXin Long 	netdev_state_change(br->dev);
305204177f3SToshiaki Makita 
306204177f3SToshiaki Makita 	rtnl_unlock();
307204177f3SToshiaki Makita 
308fda93d92SStephen Hemminger 	return len;
309fda93d92SStephen Hemminger }
310fda93d92SStephen Hemminger 
311fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(group_addr);
312fda93d92SStephen Hemminger 
31314f31bb3SXin Long static int set_flush(struct net_bridge *br, unsigned long val)
31414f31bb3SXin Long {
31514f31bb3SXin Long 	br_fdb_flush(br);
31614f31bb3SXin Long 	return 0;
31714f31bb3SXin Long }
31814f31bb3SXin Long 
319fbf2671bSsfeldma@cumulusnetworks.com static ssize_t flush_store(struct device *d,
3209cf63747SStephen Hemminger 			   struct device_attribute *attr,
3219cf63747SStephen Hemminger 			   const char *buf, size_t len)
3229cf63747SStephen Hemminger {
32314f31bb3SXin Long 	return store_bridge_parm(d, buf, len, set_flush);
3249cf63747SStephen Hemminger }
325fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_WO(flush);
326fda93d92SStephen Hemminger 
32770e4272bSNikolay Aleksandrov static ssize_t no_linklocal_learn_show(struct device *d,
32870e4272bSNikolay Aleksandrov 				       struct device_attribute *attr,
32970e4272bSNikolay Aleksandrov 				       char *buf)
33070e4272bSNikolay Aleksandrov {
33170e4272bSNikolay Aleksandrov 	struct net_bridge *br = to_bridge(d);
33270e4272bSNikolay Aleksandrov 	return sprintf(buf, "%d\n", br_boolopt_get(br, BR_BOOLOPT_NO_LL_LEARN));
33370e4272bSNikolay Aleksandrov }
33470e4272bSNikolay Aleksandrov 
33570e4272bSNikolay Aleksandrov static int set_no_linklocal_learn(struct net_bridge *br, unsigned long val)
33670e4272bSNikolay Aleksandrov {
33770e4272bSNikolay Aleksandrov 	return br_boolopt_toggle(br, BR_BOOLOPT_NO_LL_LEARN, !!val, NULL);
33870e4272bSNikolay Aleksandrov }
33970e4272bSNikolay Aleksandrov 
34070e4272bSNikolay Aleksandrov static ssize_t no_linklocal_learn_store(struct device *d,
34170e4272bSNikolay Aleksandrov 					struct device_attribute *attr,
34270e4272bSNikolay Aleksandrov 					const char *buf, size_t len)
34370e4272bSNikolay Aleksandrov {
34470e4272bSNikolay Aleksandrov 	return store_bridge_parm(d, buf, len, set_no_linklocal_learn);
34570e4272bSNikolay Aleksandrov }
34670e4272bSNikolay Aleksandrov static DEVICE_ATTR_RW(no_linklocal_learn);
34770e4272bSNikolay Aleksandrov 
3480909e117SHerbert Xu #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
349fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_router_show(struct device *d,
3500909e117SHerbert Xu 				     struct device_attribute *attr, char *buf)
3510909e117SHerbert Xu {
3520909e117SHerbert Xu 	struct net_bridge *br = to_bridge(d);
3530909e117SHerbert Xu 	return sprintf(buf, "%d\n", br->multicast_router);
3540909e117SHerbert Xu }
3550909e117SHerbert Xu 
356fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_router_store(struct device *d,
3570909e117SHerbert Xu 				      struct device_attribute *attr,
3580909e117SHerbert Xu 				      const char *buf, size_t len)
3590909e117SHerbert Xu {
3600909e117SHerbert Xu 	return store_bridge_parm(d, buf, len, br_multicast_set_router);
3610909e117SHerbert Xu }
362fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_router);
363561f1103SHerbert Xu 
364fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_snooping_show(struct device *d,
365561f1103SHerbert Xu 				       struct device_attribute *attr,
366561f1103SHerbert Xu 				       char *buf)
367561f1103SHerbert Xu {
368561f1103SHerbert Xu 	struct net_bridge *br = to_bridge(d);
36913cefad2SNikolay Aleksandrov 	return sprintf(buf, "%d\n", br_opt_get(br, BROPT_MULTICAST_ENABLED));
370561f1103SHerbert Xu }
371561f1103SHerbert Xu 
372fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_snooping_store(struct device *d,
373561f1103SHerbert Xu 					struct device_attribute *attr,
374561f1103SHerbert Xu 					const char *buf, size_t len)
375561f1103SHerbert Xu {
376561f1103SHerbert Xu 	return store_bridge_parm(d, buf, len, br_multicast_toggle);
377561f1103SHerbert Xu }
378fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_snooping);
379b195167fSHerbert Xu 
380fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_query_use_ifaddr_show(struct device *d,
3811c8ad5bfSCong Wang 					       struct device_attribute *attr,
3821c8ad5bfSCong Wang 					       char *buf)
3831c8ad5bfSCong Wang {
3841c8ad5bfSCong Wang 	struct net_bridge *br = to_bridge(d);
385675779adSNikolay Aleksandrov 	return sprintf(buf, "%d\n",
386675779adSNikolay Aleksandrov 		       br_opt_get(br, BROPT_MULTICAST_QUERY_USE_IFADDR));
3871c8ad5bfSCong Wang }
3881c8ad5bfSCong Wang 
3891c8ad5bfSCong Wang static int set_query_use_ifaddr(struct net_bridge *br, unsigned long val)
3901c8ad5bfSCong Wang {
391675779adSNikolay Aleksandrov 	br_opt_toggle(br, BROPT_MULTICAST_QUERY_USE_IFADDR, !!val);
3921c8ad5bfSCong Wang 	return 0;
3931c8ad5bfSCong Wang }
3941c8ad5bfSCong Wang 
3951c8ad5bfSCong Wang static ssize_t
396fbf2671bSsfeldma@cumulusnetworks.com multicast_query_use_ifaddr_store(struct device *d,
3971c8ad5bfSCong Wang 				 struct device_attribute *attr,
3981c8ad5bfSCong Wang 				 const char *buf, size_t len)
3991c8ad5bfSCong Wang {
4001c8ad5bfSCong Wang 	return store_bridge_parm(d, buf, len, set_query_use_ifaddr);
4011c8ad5bfSCong Wang }
402fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_query_use_ifaddr);
4031c8ad5bfSCong Wang 
404fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_querier_show(struct device *d,
405c5c23260SHerbert Xu 				      struct device_attribute *attr,
406c5c23260SHerbert Xu 				      char *buf)
407c5c23260SHerbert Xu {
408c5c23260SHerbert Xu 	struct net_bridge *br = to_bridge(d);
409675779adSNikolay Aleksandrov 	return sprintf(buf, "%d\n", br_opt_get(br, BROPT_MULTICAST_QUERIER));
410c5c23260SHerbert Xu }
411c5c23260SHerbert Xu 
412fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_querier_store(struct device *d,
413c5c23260SHerbert Xu 				       struct device_attribute *attr,
414c5c23260SHerbert Xu 				       const char *buf, size_t len)
415c5c23260SHerbert Xu {
416c5c23260SHerbert Xu 	return store_bridge_parm(d, buf, len, br_multicast_set_querier);
417c5c23260SHerbert Xu }
418fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_querier);
419c5c23260SHerbert Xu 
420fbf2671bSsfeldma@cumulusnetworks.com static ssize_t hash_elasticity_show(struct device *d,
421b195167fSHerbert Xu 				    struct device_attribute *attr, char *buf)
422b195167fSHerbert Xu {
423cf332bcaSNikolay Aleksandrov 	return sprintf(buf, "%u\n", RHT_ELASTICITY);
424b195167fSHerbert Xu }
425b195167fSHerbert Xu 
426b195167fSHerbert Xu static int set_elasticity(struct net_bridge *br, unsigned long val)
427b195167fSHerbert Xu {
428cf332bcaSNikolay Aleksandrov 	br_warn(br, "the hash_elasticity option has been deprecated and is always %u\n",
429cf332bcaSNikolay Aleksandrov 		RHT_ELASTICITY);
430b195167fSHerbert Xu 	return 0;
431b195167fSHerbert Xu }
432b195167fSHerbert Xu 
433fbf2671bSsfeldma@cumulusnetworks.com static ssize_t hash_elasticity_store(struct device *d,
434b195167fSHerbert Xu 				     struct device_attribute *attr,
435b195167fSHerbert Xu 				     const char *buf, size_t len)
436b195167fSHerbert Xu {
437b195167fSHerbert Xu 	return store_bridge_parm(d, buf, len, set_elasticity);
438b195167fSHerbert Xu }
439fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(hash_elasticity);
440b195167fSHerbert Xu 
441fbf2671bSsfeldma@cumulusnetworks.com static ssize_t hash_max_show(struct device *d, struct device_attribute *attr,
442b195167fSHerbert Xu 			     char *buf)
443b195167fSHerbert Xu {
444b195167fSHerbert Xu 	struct net_bridge *br = to_bridge(d);
445b195167fSHerbert Xu 	return sprintf(buf, "%u\n", br->hash_max);
446b195167fSHerbert Xu }
447b195167fSHerbert Xu 
44819e3a9c9SNikolay Aleksandrov static int set_hash_max(struct net_bridge *br, unsigned long val)
44919e3a9c9SNikolay Aleksandrov {
45019e3a9c9SNikolay Aleksandrov 	br->hash_max = val;
45119e3a9c9SNikolay Aleksandrov 	return 0;
45219e3a9c9SNikolay Aleksandrov }
45319e3a9c9SNikolay Aleksandrov 
454fbf2671bSsfeldma@cumulusnetworks.com static ssize_t hash_max_store(struct device *d, struct device_attribute *attr,
455b195167fSHerbert Xu 			      const char *buf, size_t len)
456b195167fSHerbert Xu {
45719e3a9c9SNikolay Aleksandrov 	return store_bridge_parm(d, buf, len, set_hash_max);
458b195167fSHerbert Xu }
459fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(hash_max);
460d902eee4SHerbert Xu 
4615e923585SNikolay Aleksandrov static ssize_t multicast_igmp_version_show(struct device *d,
4625e923585SNikolay Aleksandrov 					   struct device_attribute *attr,
4635e923585SNikolay Aleksandrov 					   char *buf)
4645e923585SNikolay Aleksandrov {
4655e923585SNikolay Aleksandrov 	struct net_bridge *br = to_bridge(d);
4665e923585SNikolay Aleksandrov 
4675e923585SNikolay Aleksandrov 	return sprintf(buf, "%u\n", br->multicast_igmp_version);
4685e923585SNikolay Aleksandrov }
4695e923585SNikolay Aleksandrov 
4705e923585SNikolay Aleksandrov static ssize_t multicast_igmp_version_store(struct device *d,
4715e923585SNikolay Aleksandrov 					    struct device_attribute *attr,
4725e923585SNikolay Aleksandrov 					    const char *buf, size_t len)
4735e923585SNikolay Aleksandrov {
4745e923585SNikolay Aleksandrov 	return store_bridge_parm(d, buf, len, br_multicast_set_igmp_version);
4755e923585SNikolay Aleksandrov }
4765e923585SNikolay Aleksandrov static DEVICE_ATTR_RW(multicast_igmp_version);
4775e923585SNikolay Aleksandrov 
478fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_last_member_count_show(struct device *d,
479d902eee4SHerbert Xu 						struct device_attribute *attr,
480d902eee4SHerbert Xu 						char *buf)
481d902eee4SHerbert Xu {
482d902eee4SHerbert Xu 	struct net_bridge *br = to_bridge(d);
483d902eee4SHerbert Xu 	return sprintf(buf, "%u\n", br->multicast_last_member_count);
484d902eee4SHerbert Xu }
485d902eee4SHerbert Xu 
486d902eee4SHerbert Xu static int set_last_member_count(struct net_bridge *br, unsigned long val)
487d902eee4SHerbert Xu {
488d902eee4SHerbert Xu 	br->multicast_last_member_count = val;
489d902eee4SHerbert Xu 	return 0;
490d902eee4SHerbert Xu }
491d902eee4SHerbert Xu 
492fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_last_member_count_store(struct device *d,
493d902eee4SHerbert Xu 						 struct device_attribute *attr,
494d902eee4SHerbert Xu 						 const char *buf, size_t len)
495d902eee4SHerbert Xu {
496d902eee4SHerbert Xu 	return store_bridge_parm(d, buf, len, set_last_member_count);
497d902eee4SHerbert Xu }
498fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_last_member_count);
499d902eee4SHerbert Xu 
500fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_startup_query_count_show(
501d902eee4SHerbert Xu 	struct device *d, struct device_attribute *attr, char *buf)
502d902eee4SHerbert Xu {
503d902eee4SHerbert Xu 	struct net_bridge *br = to_bridge(d);
504d902eee4SHerbert Xu 	return sprintf(buf, "%u\n", br->multicast_startup_query_count);
505d902eee4SHerbert Xu }
506d902eee4SHerbert Xu 
507d902eee4SHerbert Xu static int set_startup_query_count(struct net_bridge *br, unsigned long val)
508d902eee4SHerbert Xu {
509d902eee4SHerbert Xu 	br->multicast_startup_query_count = val;
510d902eee4SHerbert Xu 	return 0;
511d902eee4SHerbert Xu }
512d902eee4SHerbert Xu 
513fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_startup_query_count_store(
514d902eee4SHerbert Xu 	struct device *d, struct device_attribute *attr, const char *buf,
515d902eee4SHerbert Xu 	size_t len)
516d902eee4SHerbert Xu {
517d902eee4SHerbert Xu 	return store_bridge_parm(d, buf, len, set_startup_query_count);
518d902eee4SHerbert Xu }
519fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_startup_query_count);
520d902eee4SHerbert Xu 
521fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_last_member_interval_show(
522d902eee4SHerbert Xu 	struct device *d, struct device_attribute *attr, char *buf)
523d902eee4SHerbert Xu {
524d902eee4SHerbert Xu 	struct net_bridge *br = to_bridge(d);
525d902eee4SHerbert Xu 	return sprintf(buf, "%lu\n",
526d902eee4SHerbert Xu 		       jiffies_to_clock_t(br->multicast_last_member_interval));
527d902eee4SHerbert Xu }
528d902eee4SHerbert Xu 
529d902eee4SHerbert Xu static int set_last_member_interval(struct net_bridge *br, unsigned long val)
530d902eee4SHerbert Xu {
531d902eee4SHerbert Xu 	br->multicast_last_member_interval = clock_t_to_jiffies(val);
532d902eee4SHerbert Xu 	return 0;
533d902eee4SHerbert Xu }
534d902eee4SHerbert Xu 
535fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_last_member_interval_store(
536d902eee4SHerbert Xu 	struct device *d, struct device_attribute *attr, const char *buf,
537d902eee4SHerbert Xu 	size_t len)
538d902eee4SHerbert Xu {
539d902eee4SHerbert Xu 	return store_bridge_parm(d, buf, len, set_last_member_interval);
540d902eee4SHerbert Xu }
541fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_last_member_interval);
542d902eee4SHerbert Xu 
543fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_membership_interval_show(
544d902eee4SHerbert Xu 	struct device *d, struct device_attribute *attr, char *buf)
545d902eee4SHerbert Xu {
546d902eee4SHerbert Xu 	struct net_bridge *br = to_bridge(d);
547d902eee4SHerbert Xu 	return sprintf(buf, "%lu\n",
548d902eee4SHerbert Xu 		       jiffies_to_clock_t(br->multicast_membership_interval));
549d902eee4SHerbert Xu }
550d902eee4SHerbert Xu 
551d902eee4SHerbert Xu static int set_membership_interval(struct net_bridge *br, unsigned long val)
552d902eee4SHerbert Xu {
553d902eee4SHerbert Xu 	br->multicast_membership_interval = clock_t_to_jiffies(val);
554d902eee4SHerbert Xu 	return 0;
555d902eee4SHerbert Xu }
556d902eee4SHerbert Xu 
557fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_membership_interval_store(
558d902eee4SHerbert Xu 	struct device *d, struct device_attribute *attr, const char *buf,
559d902eee4SHerbert Xu 	size_t len)
560d902eee4SHerbert Xu {
561d902eee4SHerbert Xu 	return store_bridge_parm(d, buf, len, set_membership_interval);
562d902eee4SHerbert Xu }
563fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_membership_interval);
564d902eee4SHerbert Xu 
565fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_querier_interval_show(struct device *d,
566d902eee4SHerbert Xu 					       struct device_attribute *attr,
567d902eee4SHerbert Xu 					       char *buf)
568d902eee4SHerbert Xu {
569d902eee4SHerbert Xu 	struct net_bridge *br = to_bridge(d);
570d902eee4SHerbert Xu 	return sprintf(buf, "%lu\n",
571d902eee4SHerbert Xu 		       jiffies_to_clock_t(br->multicast_querier_interval));
572d902eee4SHerbert Xu }
573d902eee4SHerbert Xu 
574d902eee4SHerbert Xu static int set_querier_interval(struct net_bridge *br, unsigned long val)
575d902eee4SHerbert Xu {
576d902eee4SHerbert Xu 	br->multicast_querier_interval = clock_t_to_jiffies(val);
577d902eee4SHerbert Xu 	return 0;
578d902eee4SHerbert Xu }
579d902eee4SHerbert Xu 
580fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_querier_interval_store(struct device *d,
581d902eee4SHerbert Xu 						struct device_attribute *attr,
582d902eee4SHerbert Xu 						const char *buf, size_t len)
583d902eee4SHerbert Xu {
584d902eee4SHerbert Xu 	return store_bridge_parm(d, buf, len, set_querier_interval);
585d902eee4SHerbert Xu }
586fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_querier_interval);
587d902eee4SHerbert Xu 
588fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_query_interval_show(struct device *d,
589d902eee4SHerbert Xu 					     struct device_attribute *attr,
590d902eee4SHerbert Xu 					     char *buf)
591d902eee4SHerbert Xu {
592d902eee4SHerbert Xu 	struct net_bridge *br = to_bridge(d);
593d902eee4SHerbert Xu 	return sprintf(buf, "%lu\n",
594d902eee4SHerbert Xu 		       jiffies_to_clock_t(br->multicast_query_interval));
595d902eee4SHerbert Xu }
596d902eee4SHerbert Xu 
597d902eee4SHerbert Xu static int set_query_interval(struct net_bridge *br, unsigned long val)
598d902eee4SHerbert Xu {
599d902eee4SHerbert Xu 	br->multicast_query_interval = clock_t_to_jiffies(val);
600d902eee4SHerbert Xu 	return 0;
601d902eee4SHerbert Xu }
602d902eee4SHerbert Xu 
603fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_query_interval_store(struct device *d,
604d902eee4SHerbert Xu 					      struct device_attribute *attr,
605d902eee4SHerbert Xu 					      const char *buf, size_t len)
606d902eee4SHerbert Xu {
607d902eee4SHerbert Xu 	return store_bridge_parm(d, buf, len, set_query_interval);
608d902eee4SHerbert Xu }
609fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_query_interval);
610d902eee4SHerbert Xu 
611fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_query_response_interval_show(
612d902eee4SHerbert Xu 	struct device *d, struct device_attribute *attr, char *buf)
613d902eee4SHerbert Xu {
614d902eee4SHerbert Xu 	struct net_bridge *br = to_bridge(d);
615d902eee4SHerbert Xu 	return sprintf(
616d902eee4SHerbert Xu 		buf, "%lu\n",
617d902eee4SHerbert Xu 		jiffies_to_clock_t(br->multicast_query_response_interval));
618d902eee4SHerbert Xu }
619d902eee4SHerbert Xu 
620d902eee4SHerbert Xu static int set_query_response_interval(struct net_bridge *br, unsigned long val)
621d902eee4SHerbert Xu {
622d902eee4SHerbert Xu 	br->multicast_query_response_interval = clock_t_to_jiffies(val);
623d902eee4SHerbert Xu 	return 0;
624d902eee4SHerbert Xu }
625d902eee4SHerbert Xu 
626fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_query_response_interval_store(
627d902eee4SHerbert Xu 	struct device *d, struct device_attribute *attr, const char *buf,
628d902eee4SHerbert Xu 	size_t len)
629d902eee4SHerbert Xu {
630d902eee4SHerbert Xu 	return store_bridge_parm(d, buf, len, set_query_response_interval);
631d902eee4SHerbert Xu }
632fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_query_response_interval);
633d902eee4SHerbert Xu 
634fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_startup_query_interval_show(
635d902eee4SHerbert Xu 	struct device *d, struct device_attribute *attr, char *buf)
636d902eee4SHerbert Xu {
637d902eee4SHerbert Xu 	struct net_bridge *br = to_bridge(d);
638d902eee4SHerbert Xu 	return sprintf(
639d902eee4SHerbert Xu 		buf, "%lu\n",
640d902eee4SHerbert Xu 		jiffies_to_clock_t(br->multicast_startup_query_interval));
641d902eee4SHerbert Xu }
642d902eee4SHerbert Xu 
643d902eee4SHerbert Xu static int set_startup_query_interval(struct net_bridge *br, unsigned long val)
644d902eee4SHerbert Xu {
645d902eee4SHerbert Xu 	br->multicast_startup_query_interval = clock_t_to_jiffies(val);
646d902eee4SHerbert Xu 	return 0;
647d902eee4SHerbert Xu }
648d902eee4SHerbert Xu 
649fbf2671bSsfeldma@cumulusnetworks.com static ssize_t multicast_startup_query_interval_store(
650d902eee4SHerbert Xu 	struct device *d, struct device_attribute *attr, const char *buf,
651d902eee4SHerbert Xu 	size_t len)
652d902eee4SHerbert Xu {
653d902eee4SHerbert Xu 	return store_bridge_parm(d, buf, len, set_startup_query_interval);
654d902eee4SHerbert Xu }
655fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(multicast_startup_query_interval);
6561080ab95SNikolay Aleksandrov 
6571080ab95SNikolay Aleksandrov static ssize_t multicast_stats_enabled_show(struct device *d,
6581080ab95SNikolay Aleksandrov 					    struct device_attribute *attr,
6591080ab95SNikolay Aleksandrov 					    char *buf)
6601080ab95SNikolay Aleksandrov {
6611080ab95SNikolay Aleksandrov 	struct net_bridge *br = to_bridge(d);
6621080ab95SNikolay Aleksandrov 
663675779adSNikolay Aleksandrov 	return sprintf(buf, "%d\n",
664675779adSNikolay Aleksandrov 		       br_opt_get(br, BROPT_MULTICAST_STATS_ENABLED));
6651080ab95SNikolay Aleksandrov }
6661080ab95SNikolay Aleksandrov 
6671080ab95SNikolay Aleksandrov static int set_stats_enabled(struct net_bridge *br, unsigned long val)
6681080ab95SNikolay Aleksandrov {
669675779adSNikolay Aleksandrov 	br_opt_toggle(br, BROPT_MULTICAST_STATS_ENABLED, !!val);
6701080ab95SNikolay Aleksandrov 	return 0;
6711080ab95SNikolay Aleksandrov }
6721080ab95SNikolay Aleksandrov 
6731080ab95SNikolay Aleksandrov static ssize_t multicast_stats_enabled_store(struct device *d,
6741080ab95SNikolay Aleksandrov 					     struct device_attribute *attr,
6751080ab95SNikolay Aleksandrov 					     const char *buf,
6761080ab95SNikolay Aleksandrov 					     size_t len)
6771080ab95SNikolay Aleksandrov {
6781080ab95SNikolay Aleksandrov 	return store_bridge_parm(d, buf, len, set_stats_enabled);
6791080ab95SNikolay Aleksandrov }
6801080ab95SNikolay Aleksandrov static DEVICE_ATTR_RW(multicast_stats_enabled);
681aa2ae3e7SNikolay Aleksandrov 
682aa2ae3e7SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
683aa2ae3e7SNikolay Aleksandrov static ssize_t multicast_mld_version_show(struct device *d,
684aa2ae3e7SNikolay Aleksandrov 					  struct device_attribute *attr,
685aa2ae3e7SNikolay Aleksandrov 					  char *buf)
686aa2ae3e7SNikolay Aleksandrov {
687aa2ae3e7SNikolay Aleksandrov 	struct net_bridge *br = to_bridge(d);
688aa2ae3e7SNikolay Aleksandrov 
689aa2ae3e7SNikolay Aleksandrov 	return sprintf(buf, "%u\n", br->multicast_mld_version);
690aa2ae3e7SNikolay Aleksandrov }
691aa2ae3e7SNikolay Aleksandrov 
692aa2ae3e7SNikolay Aleksandrov static ssize_t multicast_mld_version_store(struct device *d,
693aa2ae3e7SNikolay Aleksandrov 					   struct device_attribute *attr,
694aa2ae3e7SNikolay Aleksandrov 					   const char *buf, size_t len)
695aa2ae3e7SNikolay Aleksandrov {
696aa2ae3e7SNikolay Aleksandrov 	return store_bridge_parm(d, buf, len, br_multicast_set_mld_version);
697aa2ae3e7SNikolay Aleksandrov }
698aa2ae3e7SNikolay Aleksandrov static DEVICE_ATTR_RW(multicast_mld_version);
699aa2ae3e7SNikolay Aleksandrov #endif
7000909e117SHerbert Xu #endif
70134666d46SPablo Neira Ayuso #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
702fbf2671bSsfeldma@cumulusnetworks.com static ssize_t nf_call_iptables_show(
7034df53d8bSPatrick McHardy 	struct device *d, struct device_attribute *attr, char *buf)
7044df53d8bSPatrick McHardy {
7054df53d8bSPatrick McHardy 	struct net_bridge *br = to_bridge(d);
7068df3510fSNikolay Aleksandrov 	return sprintf(buf, "%u\n", br_opt_get(br, BROPT_NF_CALL_IPTABLES));
7074df53d8bSPatrick McHardy }
7084df53d8bSPatrick McHardy 
7094df53d8bSPatrick McHardy static int set_nf_call_iptables(struct net_bridge *br, unsigned long val)
7104df53d8bSPatrick McHardy {
7118df3510fSNikolay Aleksandrov 	br_opt_toggle(br, BROPT_NF_CALL_IPTABLES, !!val);
7124df53d8bSPatrick McHardy 	return 0;
7134df53d8bSPatrick McHardy }
7144df53d8bSPatrick McHardy 
715fbf2671bSsfeldma@cumulusnetworks.com static ssize_t nf_call_iptables_store(
7164df53d8bSPatrick McHardy 	struct device *d, struct device_attribute *attr, const char *buf,
7174df53d8bSPatrick McHardy 	size_t len)
7184df53d8bSPatrick McHardy {
7194df53d8bSPatrick McHardy 	return store_bridge_parm(d, buf, len, set_nf_call_iptables);
7204df53d8bSPatrick McHardy }
721fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(nf_call_iptables);
7224df53d8bSPatrick McHardy 
723fbf2671bSsfeldma@cumulusnetworks.com static ssize_t nf_call_ip6tables_show(
7244df53d8bSPatrick McHardy 	struct device *d, struct device_attribute *attr, char *buf)
7254df53d8bSPatrick McHardy {
7264df53d8bSPatrick McHardy 	struct net_bridge *br = to_bridge(d);
7278df3510fSNikolay Aleksandrov 	return sprintf(buf, "%u\n", br_opt_get(br, BROPT_NF_CALL_IP6TABLES));
7284df53d8bSPatrick McHardy }
7294df53d8bSPatrick McHardy 
7304df53d8bSPatrick McHardy static int set_nf_call_ip6tables(struct net_bridge *br, unsigned long val)
7314df53d8bSPatrick McHardy {
7328df3510fSNikolay Aleksandrov 	br_opt_toggle(br, BROPT_NF_CALL_IP6TABLES, !!val);
7334df53d8bSPatrick McHardy 	return 0;
7344df53d8bSPatrick McHardy }
7354df53d8bSPatrick McHardy 
736fbf2671bSsfeldma@cumulusnetworks.com static ssize_t nf_call_ip6tables_store(
7374df53d8bSPatrick McHardy 	struct device *d, struct device_attribute *attr, const char *buf,
7384df53d8bSPatrick McHardy 	size_t len)
7394df53d8bSPatrick McHardy {
7404df53d8bSPatrick McHardy 	return store_bridge_parm(d, buf, len, set_nf_call_ip6tables);
7414df53d8bSPatrick McHardy }
742fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(nf_call_ip6tables);
7434df53d8bSPatrick McHardy 
744fbf2671bSsfeldma@cumulusnetworks.com static ssize_t nf_call_arptables_show(
7454df53d8bSPatrick McHardy 	struct device *d, struct device_attribute *attr, char *buf)
7464df53d8bSPatrick McHardy {
7474df53d8bSPatrick McHardy 	struct net_bridge *br = to_bridge(d);
7488df3510fSNikolay Aleksandrov 	return sprintf(buf, "%u\n", br_opt_get(br, BROPT_NF_CALL_ARPTABLES));
7494df53d8bSPatrick McHardy }
7504df53d8bSPatrick McHardy 
7514df53d8bSPatrick McHardy static int set_nf_call_arptables(struct net_bridge *br, unsigned long val)
7524df53d8bSPatrick McHardy {
7538df3510fSNikolay Aleksandrov 	br_opt_toggle(br, BROPT_NF_CALL_ARPTABLES, !!val);
7544df53d8bSPatrick McHardy 	return 0;
7554df53d8bSPatrick McHardy }
7564df53d8bSPatrick McHardy 
757fbf2671bSsfeldma@cumulusnetworks.com static ssize_t nf_call_arptables_store(
7584df53d8bSPatrick McHardy 	struct device *d, struct device_attribute *attr, const char *buf,
7594df53d8bSPatrick McHardy 	size_t len)
7604df53d8bSPatrick McHardy {
7614df53d8bSPatrick McHardy 	return store_bridge_parm(d, buf, len, set_nf_call_arptables);
7624df53d8bSPatrick McHardy }
763fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(nf_call_arptables);
7644df53d8bSPatrick McHardy #endif
765243a2e63SVlad Yasevich #ifdef CONFIG_BRIDGE_VLAN_FILTERING
766fbf2671bSsfeldma@cumulusnetworks.com static ssize_t vlan_filtering_show(struct device *d,
767243a2e63SVlad Yasevich 				   struct device_attribute *attr,
768243a2e63SVlad Yasevich 				   char *buf)
769243a2e63SVlad Yasevich {
770243a2e63SVlad Yasevich 	struct net_bridge *br = to_bridge(d);
771ae75767eSNikolay Aleksandrov 	return sprintf(buf, "%d\n", br_opt_get(br, BROPT_VLAN_ENABLED));
772243a2e63SVlad Yasevich }
773243a2e63SVlad Yasevich 
774fbf2671bSsfeldma@cumulusnetworks.com static ssize_t vlan_filtering_store(struct device *d,
775243a2e63SVlad Yasevich 				    struct device_attribute *attr,
776243a2e63SVlad Yasevich 				    const char *buf, size_t len)
777243a2e63SVlad Yasevich {
778243a2e63SVlad Yasevich 	return store_bridge_parm(d, buf, len, br_vlan_filter_toggle);
779243a2e63SVlad Yasevich }
780fbf2671bSsfeldma@cumulusnetworks.com static DEVICE_ATTR_RW(vlan_filtering);
781204177f3SToshiaki Makita 
782204177f3SToshiaki Makita static ssize_t vlan_protocol_show(struct device *d,
783204177f3SToshiaki Makita 				  struct device_attribute *attr,
784204177f3SToshiaki Makita 				  char *buf)
785204177f3SToshiaki Makita {
786204177f3SToshiaki Makita 	struct net_bridge *br = to_bridge(d);
787204177f3SToshiaki Makita 	return sprintf(buf, "%#06x\n", ntohs(br->vlan_proto));
788204177f3SToshiaki Makita }
789204177f3SToshiaki Makita 
790204177f3SToshiaki Makita static ssize_t vlan_protocol_store(struct device *d,
791204177f3SToshiaki Makita 				   struct device_attribute *attr,
792204177f3SToshiaki Makita 				   const char *buf, size_t len)
793204177f3SToshiaki Makita {
794204177f3SToshiaki Makita 	return store_bridge_parm(d, buf, len, br_vlan_set_proto);
795204177f3SToshiaki Makita }
796204177f3SToshiaki Makita static DEVICE_ATTR_RW(vlan_protocol);
79796a20d9dSVlad Yasevich 
79896a20d9dSVlad Yasevich static ssize_t default_pvid_show(struct device *d,
79996a20d9dSVlad Yasevich 				 struct device_attribute *attr,
80096a20d9dSVlad Yasevich 				 char *buf)
80196a20d9dSVlad Yasevich {
80296a20d9dSVlad Yasevich 	struct net_bridge *br = to_bridge(d);
80396a20d9dSVlad Yasevich 	return sprintf(buf, "%d\n", br->default_pvid);
80496a20d9dSVlad Yasevich }
80596a20d9dSVlad Yasevich 
80696a20d9dSVlad Yasevich static ssize_t default_pvid_store(struct device *d,
80796a20d9dSVlad Yasevich 				  struct device_attribute *attr,
80896a20d9dSVlad Yasevich 				  const char *buf, size_t len)
80996a20d9dSVlad Yasevich {
81096a20d9dSVlad Yasevich 	return store_bridge_parm(d, buf, len, br_vlan_set_default_pvid);
81196a20d9dSVlad Yasevich }
81296a20d9dSVlad Yasevich static DEVICE_ATTR_RW(default_pvid);
8136dada9b1SNikolay Aleksandrov 
8146dada9b1SNikolay Aleksandrov static ssize_t vlan_stats_enabled_show(struct device *d,
8156dada9b1SNikolay Aleksandrov 				       struct device_attribute *attr,
8166dada9b1SNikolay Aleksandrov 				       char *buf)
8176dada9b1SNikolay Aleksandrov {
8186dada9b1SNikolay Aleksandrov 	struct net_bridge *br = to_bridge(d);
819ae75767eSNikolay Aleksandrov 	return sprintf(buf, "%u\n", br_opt_get(br, BROPT_VLAN_STATS_ENABLED));
8206dada9b1SNikolay Aleksandrov }
8216dada9b1SNikolay Aleksandrov 
8226dada9b1SNikolay Aleksandrov static ssize_t vlan_stats_enabled_store(struct device *d,
8236dada9b1SNikolay Aleksandrov 					struct device_attribute *attr,
8246dada9b1SNikolay Aleksandrov 					const char *buf, size_t len)
8256dada9b1SNikolay Aleksandrov {
8266dada9b1SNikolay Aleksandrov 	return store_bridge_parm(d, buf, len, br_vlan_set_stats);
8276dada9b1SNikolay Aleksandrov }
8286dada9b1SNikolay Aleksandrov static DEVICE_ATTR_RW(vlan_stats_enabled);
8299163a0fcSNikolay Aleksandrov 
8309163a0fcSNikolay Aleksandrov static ssize_t vlan_stats_per_port_show(struct device *d,
8319163a0fcSNikolay Aleksandrov 					struct device_attribute *attr,
8329163a0fcSNikolay Aleksandrov 					char *buf)
8339163a0fcSNikolay Aleksandrov {
8349163a0fcSNikolay Aleksandrov 	struct net_bridge *br = to_bridge(d);
8359163a0fcSNikolay Aleksandrov 	return sprintf(buf, "%u\n", br_opt_get(br, BROPT_VLAN_STATS_PER_PORT));
8369163a0fcSNikolay Aleksandrov }
8379163a0fcSNikolay Aleksandrov 
8389163a0fcSNikolay Aleksandrov static ssize_t vlan_stats_per_port_store(struct device *d,
8399163a0fcSNikolay Aleksandrov 					 struct device_attribute *attr,
8409163a0fcSNikolay Aleksandrov 					 const char *buf, size_t len)
8419163a0fcSNikolay Aleksandrov {
8429163a0fcSNikolay Aleksandrov 	return store_bridge_parm(d, buf, len, br_vlan_set_stats_per_port);
8439163a0fcSNikolay Aleksandrov }
8449163a0fcSNikolay Aleksandrov static DEVICE_ATTR_RW(vlan_stats_per_port);
845243a2e63SVlad Yasevich #endif
8460909e117SHerbert Xu 
8471da177e4SLinus Torvalds static struct attribute *bridge_attrs[] = {
84843cb76d9SGreg Kroah-Hartman 	&dev_attr_forward_delay.attr,
84943cb76d9SGreg Kroah-Hartman 	&dev_attr_hello_time.attr,
85043cb76d9SGreg Kroah-Hartman 	&dev_attr_max_age.attr,
85143cb76d9SGreg Kroah-Hartman 	&dev_attr_ageing_time.attr,
85243cb76d9SGreg Kroah-Hartman 	&dev_attr_stp_state.attr,
853515853ccSstephen hemminger 	&dev_attr_group_fwd_mask.attr,
85443cb76d9SGreg Kroah-Hartman 	&dev_attr_priority.attr,
85543cb76d9SGreg Kroah-Hartman 	&dev_attr_bridge_id.attr,
85643cb76d9SGreg Kroah-Hartman 	&dev_attr_root_id.attr,
85743cb76d9SGreg Kroah-Hartman 	&dev_attr_root_path_cost.attr,
85843cb76d9SGreg Kroah-Hartman 	&dev_attr_root_port.attr,
85943cb76d9SGreg Kroah-Hartman 	&dev_attr_topology_change.attr,
86043cb76d9SGreg Kroah-Hartman 	&dev_attr_topology_change_detected.attr,
86143cb76d9SGreg Kroah-Hartman 	&dev_attr_hello_timer.attr,
86243cb76d9SGreg Kroah-Hartman 	&dev_attr_tcn_timer.attr,
86343cb76d9SGreg Kroah-Hartman 	&dev_attr_topology_change_timer.attr,
86443cb76d9SGreg Kroah-Hartman 	&dev_attr_gc_timer.attr,
86543cb76d9SGreg Kroah-Hartman 	&dev_attr_group_addr.attr,
8669cf63747SStephen Hemminger 	&dev_attr_flush.attr,
86770e4272bSNikolay Aleksandrov 	&dev_attr_no_linklocal_learn.attr,
8680909e117SHerbert Xu #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
8690909e117SHerbert Xu 	&dev_attr_multicast_router.attr,
870561f1103SHerbert Xu 	&dev_attr_multicast_snooping.attr,
871c5c23260SHerbert Xu 	&dev_attr_multicast_querier.attr,
8721c8ad5bfSCong Wang 	&dev_attr_multicast_query_use_ifaddr.attr,
873b195167fSHerbert Xu 	&dev_attr_hash_elasticity.attr,
874b195167fSHerbert Xu 	&dev_attr_hash_max.attr,
875d902eee4SHerbert Xu 	&dev_attr_multicast_last_member_count.attr,
876d902eee4SHerbert Xu 	&dev_attr_multicast_startup_query_count.attr,
877d902eee4SHerbert Xu 	&dev_attr_multicast_last_member_interval.attr,
878d902eee4SHerbert Xu 	&dev_attr_multicast_membership_interval.attr,
879d902eee4SHerbert Xu 	&dev_attr_multicast_querier_interval.attr,
880d902eee4SHerbert Xu 	&dev_attr_multicast_query_interval.attr,
881d902eee4SHerbert Xu 	&dev_attr_multicast_query_response_interval.attr,
882d902eee4SHerbert Xu 	&dev_attr_multicast_startup_query_interval.attr,
8831080ab95SNikolay Aleksandrov 	&dev_attr_multicast_stats_enabled.attr,
8845e923585SNikolay Aleksandrov 	&dev_attr_multicast_igmp_version.attr,
885aa2ae3e7SNikolay Aleksandrov #if IS_ENABLED(CONFIG_IPV6)
886aa2ae3e7SNikolay Aleksandrov 	&dev_attr_multicast_mld_version.attr,
887aa2ae3e7SNikolay Aleksandrov #endif
8880909e117SHerbert Xu #endif
88934666d46SPablo Neira Ayuso #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
8904df53d8bSPatrick McHardy 	&dev_attr_nf_call_iptables.attr,
8914df53d8bSPatrick McHardy 	&dev_attr_nf_call_ip6tables.attr,
8924df53d8bSPatrick McHardy 	&dev_attr_nf_call_arptables.attr,
8934df53d8bSPatrick McHardy #endif
894243a2e63SVlad Yasevich #ifdef CONFIG_BRIDGE_VLAN_FILTERING
895243a2e63SVlad Yasevich 	&dev_attr_vlan_filtering.attr,
896204177f3SToshiaki Makita 	&dev_attr_vlan_protocol.attr,
89796a20d9dSVlad Yasevich 	&dev_attr_default_pvid.attr,
8986dada9b1SNikolay Aleksandrov 	&dev_attr_vlan_stats_enabled.attr,
8999163a0fcSNikolay Aleksandrov 	&dev_attr_vlan_stats_per_port.attr,
900243a2e63SVlad Yasevich #endif
9011da177e4SLinus Torvalds 	NULL
9021da177e4SLinus Torvalds };
9031da177e4SLinus Torvalds 
904cddbb79fSArvind Yadav static const struct attribute_group bridge_group = {
9051da177e4SLinus Torvalds 	.name = SYSFS_BRIDGE_ATTR,
9061da177e4SLinus Torvalds 	.attrs = bridge_attrs,
9071da177e4SLinus Torvalds };
9081da177e4SLinus Torvalds 
9091da177e4SLinus Torvalds /*
9101da177e4SLinus Torvalds  * Export the forwarding information table as a binary file
9111da177e4SLinus Torvalds  * The records are struct __fdb_entry.
9121da177e4SLinus Torvalds  *
9131da177e4SLinus Torvalds  * Returns the number of bytes read.
9141da177e4SLinus Torvalds  */
9152c3c8beaSChris Wright static ssize_t brforward_read(struct file *filp, struct kobject *kobj,
91691a69029SZhang Rui 			      struct bin_attribute *bin_attr,
91791a69029SZhang Rui 			      char *buf, loff_t off, size_t count)
9181da177e4SLinus Torvalds {
919aeb7ed14SGeliang Tang 	struct device *dev = kobj_to_dev(kobj);
92043cb76d9SGreg Kroah-Hartman 	struct net_bridge *br = to_bridge(dev);
9211da177e4SLinus Torvalds 	int n;
9221da177e4SLinus Torvalds 
9231da177e4SLinus Torvalds 	/* must read whole records */
9241da177e4SLinus Torvalds 	if (off % sizeof(struct __fdb_entry) != 0)
9251da177e4SLinus Torvalds 		return -EINVAL;
9261da177e4SLinus Torvalds 
9271da177e4SLinus Torvalds 	n =  br_fdb_fillbuf(br, buf,
9281da177e4SLinus Torvalds 			    count / sizeof(struct __fdb_entry),
9291da177e4SLinus Torvalds 			    off / sizeof(struct __fdb_entry));
9301da177e4SLinus Torvalds 
9311da177e4SLinus Torvalds 	if (n > 0)
9321da177e4SLinus Torvalds 		n *= sizeof(struct __fdb_entry);
9331da177e4SLinus Torvalds 
9341da177e4SLinus Torvalds 	return n;
9351da177e4SLinus Torvalds }
9361da177e4SLinus Torvalds 
9371da177e4SLinus Torvalds static struct bin_attribute bridge_forward = {
9381da177e4SLinus Torvalds 	.attr = { .name = SYSFS_BRIDGE_FDB,
939d6444062SJoe Perches 		  .mode = 0444, },
9401da177e4SLinus Torvalds 	.read = brforward_read,
9411da177e4SLinus Torvalds };
9421da177e4SLinus Torvalds 
9431da177e4SLinus Torvalds /*
9441da177e4SLinus Torvalds  * Add entries in sysfs onto the existing network class device
9451da177e4SLinus Torvalds  * for the bridge.
9461da177e4SLinus Torvalds  *   Adds a attribute group "bridge" containing tuning parameters.
9471da177e4SLinus Torvalds  *   Binary attribute containing the forward table
9481da177e4SLinus Torvalds  *   Sub directory to hold links to interfaces.
9491da177e4SLinus Torvalds  *
9501da177e4SLinus Torvalds  * Note: the ifobj exists only to be a subdirectory
9511da177e4SLinus Torvalds  *   to hold links.  The ifobj exists in same data structure
9521da177e4SLinus Torvalds  *   as it's parent the bridge so reference counting works.
9531da177e4SLinus Torvalds  */
9541da177e4SLinus Torvalds int br_sysfs_addbr(struct net_device *dev)
9551da177e4SLinus Torvalds {
95643cb76d9SGreg Kroah-Hartman 	struct kobject *brobj = &dev->dev.kobj;
9571da177e4SLinus Torvalds 	struct net_bridge *br = netdev_priv(dev);
9581da177e4SLinus Torvalds 	int err;
9591da177e4SLinus Torvalds 
9601da177e4SLinus Torvalds 	err = sysfs_create_group(brobj, &bridge_group);
9611da177e4SLinus Torvalds 	if (err) {
9621da177e4SLinus Torvalds 		pr_info("%s: can't create group %s/%s\n",
9630dc47877SHarvey Harrison 			__func__, dev->name, bridge_group.name);
9641da177e4SLinus Torvalds 		goto out1;
9651da177e4SLinus Torvalds 	}
9661da177e4SLinus Torvalds 
9671da177e4SLinus Torvalds 	err = sysfs_create_bin_file(brobj, &bridge_forward);
9681da177e4SLinus Torvalds 	if (err) {
9691842c4beSRandy Dunlap 		pr_info("%s: can't create attribute file %s/%s\n",
9700dc47877SHarvey Harrison 			__func__, dev->name, bridge_forward.attr.name);
9711da177e4SLinus Torvalds 		goto out2;
9721da177e4SLinus Torvalds 	}
9731da177e4SLinus Torvalds 
97443b98c4aSGreg Kroah-Hartman 	br->ifobj = kobject_create_and_add(SYSFS_BRIDGE_PORT_SUBDIR, brobj);
97543b98c4aSGreg Kroah-Hartman 	if (!br->ifobj) {
9761da177e4SLinus Torvalds 		pr_info("%s: can't add kobject (directory) %s/%s\n",
9770dc47877SHarvey Harrison 			__func__, dev->name, SYSFS_BRIDGE_PORT_SUBDIR);
978b5958963SPan Bian 		err = -ENOMEM;
9791da177e4SLinus Torvalds 		goto out3;
9801da177e4SLinus Torvalds 	}
9811da177e4SLinus Torvalds 	return 0;
9821da177e4SLinus Torvalds  out3:
98343cb76d9SGreg Kroah-Hartman 	sysfs_remove_bin_file(&dev->dev.kobj, &bridge_forward);
9841da177e4SLinus Torvalds  out2:
98543cb76d9SGreg Kroah-Hartman 	sysfs_remove_group(&dev->dev.kobj, &bridge_group);
9861da177e4SLinus Torvalds  out1:
9871da177e4SLinus Torvalds 	return err;
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds }
9901da177e4SLinus Torvalds 
9911da177e4SLinus Torvalds void br_sysfs_delbr(struct net_device *dev)
9921da177e4SLinus Torvalds {
99343cb76d9SGreg Kroah-Hartman 	struct kobject *kobj = &dev->dev.kobj;
9941da177e4SLinus Torvalds 	struct net_bridge *br = netdev_priv(dev);
9951da177e4SLinus Torvalds 
99678a2d906SGreg Kroah-Hartman 	kobject_put(br->ifobj);
9971da177e4SLinus Torvalds 	sysfs_remove_bin_file(kobj, &bridge_forward);
9981da177e4SLinus Torvalds 	sysfs_remove_group(kobj, &bridge_group);
9991da177e4SLinus Torvalds }
1000