]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Sysfs attributes of bridge ports | |
3 | * Linux ethernet bridge | |
4 | * | |
5 | * Authors: | |
6 | * Stephen Hemminger <[email protected]> | |
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 | ||
14 | #include <linux/kernel.h> | |
15 | #include <linux/netdevice.h> | |
16 | #include <linux/if_bridge.h> | |
17 | #include <linux/rtnetlink.h> | |
18 | #include <linux/spinlock.h> | |
19 | #include <linux/times.h> | |
20 | ||
21 | #include "br_private.h" | |
22 | ||
23 | #define to_class_dev(obj) container_of(obj,struct class_device,kobj) | |
24 | #define to_net_dev(class) container_of(class, struct net_device, class_dev) | |
25 | #define to_bridge(cd) ((struct net_bridge *)(to_net_dev(cd)->priv)) | |
26 | ||
27 | /* | |
28 | * Common code for storing bridge parameters. | |
29 | */ | |
30 | static ssize_t store_bridge_parm(struct class_device *cd, | |
31 | const char *buf, size_t len, | |
32 | void (*set)(struct net_bridge *, unsigned long)) | |
33 | { | |
34 | struct net_bridge *br = to_bridge(cd); | |
35 | char *endp; | |
36 | unsigned long val; | |
37 | ||
38 | if (!capable(CAP_NET_ADMIN)) | |
39 | return -EPERM; | |
40 | ||
41 | val = simple_strtoul(buf, &endp, 0); | |
42 | if (endp == buf) | |
43 | return -EINVAL; | |
44 | ||
45 | spin_lock_bh(&br->lock); | |
46 | (*set)(br, val); | |
47 | spin_unlock_bh(&br->lock); | |
48 | return len; | |
49 | } | |
50 | ||
51 | ||
52 | static ssize_t show_forward_delay(struct class_device *cd, char *buf) | |
53 | { | |
54 | struct net_bridge *br = to_bridge(cd); | |
55 | return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay)); | |
56 | } | |
57 | ||
58 | static void set_forward_delay(struct net_bridge *br, unsigned long val) | |
59 | { | |
60 | unsigned long delay = clock_t_to_jiffies(val); | |
61 | br->forward_delay = delay; | |
62 | if (br_is_root_bridge(br)) | |
63 | br->bridge_forward_delay = delay; | |
64 | } | |
65 | ||
66 | static ssize_t store_forward_delay(struct class_device *cd, const char *buf, | |
67 | size_t len) | |
68 | { | |
69 | return store_bridge_parm(cd, buf, len, set_forward_delay); | |
70 | } | |
71 | static CLASS_DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR, | |
72 | show_forward_delay, store_forward_delay); | |
73 | ||
74 | static ssize_t show_hello_time(struct class_device *cd, char *buf) | |
75 | { | |
76 | return sprintf(buf, "%lu\n", | |
77 | jiffies_to_clock_t(to_bridge(cd)->hello_time)); | |
78 | } | |
79 | ||
80 | static void set_hello_time(struct net_bridge *br, unsigned long val) | |
81 | { | |
82 | unsigned long t = clock_t_to_jiffies(val); | |
83 | br->hello_time = t; | |
84 | if (br_is_root_bridge(br)) | |
85 | br->bridge_hello_time = t; | |
86 | } | |
87 | ||
88 | static ssize_t store_hello_time(struct class_device *cd, const char *buf, | |
89 | size_t len) | |
90 | { | |
91 | return store_bridge_parm(cd, buf, len, set_hello_time); | |
92 | } | |
93 | ||
94 | static CLASS_DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time, | |
95 | store_hello_time); | |
96 | ||
97 | static ssize_t show_max_age(struct class_device *cd, char *buf) | |
98 | { | |
99 | return sprintf(buf, "%lu\n", | |
100 | jiffies_to_clock_t(to_bridge(cd)->max_age)); | |
101 | } | |
102 | ||
103 | static void set_max_age(struct net_bridge *br, unsigned long val) | |
104 | { | |
105 | unsigned long t = clock_t_to_jiffies(val); | |
106 | br->max_age = t; | |
107 | if (br_is_root_bridge(br)) | |
108 | br->bridge_max_age = t; | |
109 | } | |
110 | ||
111 | static ssize_t store_max_age(struct class_device *cd, const char *buf, | |
112 | size_t len) | |
113 | { | |
114 | return store_bridge_parm(cd, buf, len, set_max_age); | |
115 | } | |
116 | ||
117 | static CLASS_DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, | |
118 | store_max_age); | |
119 | ||
120 | static ssize_t show_ageing_time(struct class_device *cd, char *buf) | |
121 | { | |
122 | struct net_bridge *br = to_bridge(cd); | |
123 | return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time)); | |
124 | } | |
125 | ||
126 | static void set_ageing_time(struct net_bridge *br, unsigned long val) | |
127 | { | |
128 | br->ageing_time = clock_t_to_jiffies(val); | |
129 | } | |
130 | ||
131 | static ssize_t store_ageing_time(struct class_device *cd, const char *buf, | |
132 | size_t len) | |
133 | { | |
134 | return store_bridge_parm(cd, buf, len, set_ageing_time); | |
135 | } | |
136 | ||
137 | static CLASS_DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time, | |
138 | store_ageing_time); | |
139 | static ssize_t show_stp_state(struct class_device *cd, char *buf) | |
140 | { | |
141 | struct net_bridge *br = to_bridge(cd); | |
142 | return sprintf(buf, "%d\n", br->stp_enabled); | |
143 | } | |
144 | ||
145 | static void set_stp_state(struct net_bridge *br, unsigned long val) | |
146 | { | |
147 | br->stp_enabled = val; | |
148 | } | |
149 | ||
150 | static ssize_t store_stp_state(struct class_device *cd, | |
151 | const char *buf, size_t len) | |
152 | { | |
153 | return store_bridge_parm(cd, buf, len, set_stp_state); | |
154 | } | |
155 | ||
156 | static CLASS_DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state, | |
157 | store_stp_state); | |
158 | ||
159 | static ssize_t show_priority(struct class_device *cd, char *buf) | |
160 | { | |
161 | struct net_bridge *br = to_bridge(cd); | |
162 | return sprintf(buf, "%d\n", | |
163 | (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]); | |
164 | } | |
165 | ||
166 | static void set_priority(struct net_bridge *br, unsigned long val) | |
167 | { | |
168 | br_stp_set_bridge_priority(br, (u16) val); | |
169 | } | |
170 | ||
171 | static ssize_t store_priority(struct class_device *cd, | |
172 | const char *buf, size_t len) | |
173 | { | |
174 | return store_bridge_parm(cd, buf, len, set_priority); | |
175 | } | |
176 | static CLASS_DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, | |
177 | store_priority); | |
178 | ||
179 | static ssize_t show_root_id(struct class_device *cd, char *buf) | |
180 | { | |
181 | return br_show_bridge_id(buf, &to_bridge(cd)->designated_root); | |
182 | } | |
183 | static CLASS_DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL); | |
184 | ||
185 | static ssize_t show_bridge_id(struct class_device *cd, char *buf) | |
186 | { | |
187 | return br_show_bridge_id(buf, &to_bridge(cd)->bridge_id); | |
188 | } | |
189 | static CLASS_DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL); | |
190 | ||
191 | static ssize_t show_root_port(struct class_device *cd, char *buf) | |
192 | { | |
193 | return sprintf(buf, "%d\n", to_bridge(cd)->root_port); | |
194 | } | |
195 | static CLASS_DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL); | |
196 | ||
197 | static ssize_t show_root_path_cost(struct class_device *cd, char *buf) | |
198 | { | |
199 | return sprintf(buf, "%d\n", to_bridge(cd)->root_path_cost); | |
200 | } | |
201 | static CLASS_DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL); | |
202 | ||
203 | static ssize_t show_topology_change(struct class_device *cd, char *buf) | |
204 | { | |
205 | return sprintf(buf, "%d\n", to_bridge(cd)->topology_change); | |
206 | } | |
207 | static CLASS_DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL); | |
208 | ||
209 | static ssize_t show_topology_change_detected(struct class_device *cd, char *buf) | |
210 | { | |
211 | struct net_bridge *br = to_bridge(cd); | |
212 | return sprintf(buf, "%d\n", br->topology_change_detected); | |
213 | } | |
214 | static CLASS_DEVICE_ATTR(topology_change_detected, S_IRUGO, show_topology_change_detected, NULL); | |
215 | ||
216 | static ssize_t show_hello_timer(struct class_device *cd, char *buf) | |
217 | { | |
218 | struct net_bridge *br = to_bridge(cd); | |
219 | return sprintf(buf, "%ld\n", br_timer_value(&br->hello_timer)); | |
220 | } | |
221 | static CLASS_DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL); | |
222 | ||
223 | static ssize_t show_tcn_timer(struct class_device *cd, char *buf) | |
224 | { | |
225 | struct net_bridge *br = to_bridge(cd); | |
226 | return sprintf(buf, "%ld\n", br_timer_value(&br->tcn_timer)); | |
227 | } | |
228 | static CLASS_DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL); | |
229 | ||
230 | static ssize_t show_topology_change_timer(struct class_device *cd, char *buf) | |
231 | { | |
232 | struct net_bridge *br = to_bridge(cd); | |
233 | return sprintf(buf, "%ld\n", br_timer_value(&br->topology_change_timer)); | |
234 | } | |
235 | static CLASS_DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer, NULL); | |
236 | ||
237 | static ssize_t show_gc_timer(struct class_device *cd, char *buf) | |
238 | { | |
239 | struct net_bridge *br = to_bridge(cd); | |
240 | return sprintf(buf, "%ld\n", br_timer_value(&br->gc_timer)); | |
241 | } | |
242 | static CLASS_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL); | |
243 | ||
244 | static struct attribute *bridge_attrs[] = { | |
245 | &class_device_attr_forward_delay.attr, | |
246 | &class_device_attr_hello_time.attr, | |
247 | &class_device_attr_max_age.attr, | |
248 | &class_device_attr_ageing_time.attr, | |
249 | &class_device_attr_stp_state.attr, | |
250 | &class_device_attr_priority.attr, | |
251 | &class_device_attr_bridge_id.attr, | |
252 | &class_device_attr_root_id.attr, | |
253 | &class_device_attr_root_path_cost.attr, | |
254 | &class_device_attr_root_port.attr, | |
255 | &class_device_attr_topology_change.attr, | |
256 | &class_device_attr_topology_change_detected.attr, | |
257 | &class_device_attr_hello_timer.attr, | |
258 | &class_device_attr_tcn_timer.attr, | |
259 | &class_device_attr_topology_change_timer.attr, | |
260 | &class_device_attr_gc_timer.attr, | |
261 | NULL | |
262 | }; | |
263 | ||
264 | static struct attribute_group bridge_group = { | |
265 | .name = SYSFS_BRIDGE_ATTR, | |
266 | .attrs = bridge_attrs, | |
267 | }; | |
268 | ||
269 | /* | |
270 | * Export the forwarding information table as a binary file | |
271 | * The records are struct __fdb_entry. | |
272 | * | |
273 | * Returns the number of bytes read. | |
274 | */ | |
275 | static ssize_t brforward_read(struct kobject *kobj, char *buf, | |
276 | loff_t off, size_t count) | |
277 | { | |
278 | struct class_device *cdev = to_class_dev(kobj); | |
279 | struct net_bridge *br = to_bridge(cdev); | |
280 | int n; | |
281 | ||
282 | /* must read whole records */ | |
283 | if (off % sizeof(struct __fdb_entry) != 0) | |
284 | return -EINVAL; | |
285 | ||
286 | n = br_fdb_fillbuf(br, buf, | |
287 | count / sizeof(struct __fdb_entry), | |
288 | off / sizeof(struct __fdb_entry)); | |
289 | ||
290 | if (n > 0) | |
291 | n *= sizeof(struct __fdb_entry); | |
292 | ||
293 | return n; | |
294 | } | |
295 | ||
296 | static struct bin_attribute bridge_forward = { | |
297 | .attr = { .name = SYSFS_BRIDGE_FDB, | |
298 | .mode = S_IRUGO, | |
299 | .owner = THIS_MODULE, }, | |
300 | .read = brforward_read, | |
301 | }; | |
302 | ||
303 | /* | |
304 | * Add entries in sysfs onto the existing network class device | |
305 | * for the bridge. | |
306 | * Adds a attribute group "bridge" containing tuning parameters. | |
307 | * Binary attribute containing the forward table | |
308 | * Sub directory to hold links to interfaces. | |
309 | * | |
310 | * Note: the ifobj exists only to be a subdirectory | |
311 | * to hold links. The ifobj exists in same data structure | |
312 | * as it's parent the bridge so reference counting works. | |
313 | */ | |
314 | int br_sysfs_addbr(struct net_device *dev) | |
315 | { | |
316 | struct kobject *brobj = &dev->class_dev.kobj; | |
317 | struct net_bridge *br = netdev_priv(dev); | |
318 | int err; | |
319 | ||
320 | err = sysfs_create_group(brobj, &bridge_group); | |
321 | if (err) { | |
322 | pr_info("%s: can't create group %s/%s\n", | |
323 | __FUNCTION__, dev->name, bridge_group.name); | |
324 | goto out1; | |
325 | } | |
326 | ||
327 | err = sysfs_create_bin_file(brobj, &bridge_forward); | |
328 | if (err) { | |
329 | pr_info("%s: can't create attribue file %s/%s\n", | |
330 | __FUNCTION__, dev->name, bridge_forward.attr.name); | |
331 | goto out2; | |
332 | } | |
333 | ||
334 | ||
335 | kobject_set_name(&br->ifobj, SYSFS_BRIDGE_PORT_SUBDIR); | |
336 | br->ifobj.ktype = NULL; | |
337 | br->ifobj.kset = NULL; | |
338 | br->ifobj.parent = brobj; | |
339 | ||
340 | err = kobject_register(&br->ifobj); | |
341 | if (err) { | |
342 | pr_info("%s: can't add kobject (directory) %s/%s\n", | |
343 | __FUNCTION__, dev->name, br->ifobj.name); | |
344 | goto out3; | |
345 | } | |
346 | return 0; | |
347 | out3: | |
348 | sysfs_remove_bin_file(&dev->class_dev.kobj, &bridge_forward); | |
349 | out2: | |
350 | sysfs_remove_group(&dev->class_dev.kobj, &bridge_group); | |
351 | out1: | |
352 | return err; | |
353 | ||
354 | } | |
355 | ||
356 | void br_sysfs_delbr(struct net_device *dev) | |
357 | { | |
358 | struct kobject *kobj = &dev->class_dev.kobj; | |
359 | struct net_bridge *br = netdev_priv(dev); | |
360 | ||
361 | kobject_unregister(&br->ifobj); | |
362 | sysfs_remove_bin_file(kobj, &bridge_forward); | |
363 | sysfs_remove_group(kobj, &bridge_group); | |
364 | } |