br_sysfs_br.c 12.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 *	Sysfs attributes of bridge ports
 *	Linux ethernet bridge
 *
 *	Authors:
 *	Stephen Hemminger		<shemminger@osdl.org>
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License
 *	as published by the Free Software Foundation; either version
 *	2 of the License, or (at your option) any later version.
 */

14
#include <linux/capability.h>
L
Linus Torvalds 已提交
15 16 17 18 19 20 21 22 23
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/if_bridge.h>
#include <linux/rtnetlink.h>
#include <linux/spinlock.h>
#include <linux/times.h>

#include "br_private.h"

24
#define to_dev(obj)	container_of(obj, struct device, kobj)
L
Linus Torvalds 已提交
25 26 27 28 29
#define to_bridge(cd)	((struct net_bridge *)(to_net_dev(cd)->priv))

/*
 * Common code for storing bridge parameters.
 */
30
static ssize_t store_bridge_parm(struct device *d,
L
Linus Torvalds 已提交
31 32 33
				 const char *buf, size_t len,
				 void (*set)(struct net_bridge *, unsigned long))
{
34
	struct net_bridge *br = to_bridge(d);
L
Linus Torvalds 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
	char *endp;
	unsigned long val;

	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	val = simple_strtoul(buf, &endp, 0);
	if (endp == buf)
		return -EINVAL;

	spin_lock_bh(&br->lock);
	(*set)(br, val);
	spin_unlock_bh(&br->lock);
	return len;
}


52 53
static ssize_t show_forward_delay(struct device *d,
				  struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
54
{
55
	struct net_bridge *br = to_bridge(d);
L
Linus Torvalds 已提交
56 57 58 59 60 61 62 63 64 65 66
	return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->forward_delay));
}

static void set_forward_delay(struct net_bridge *br, unsigned long val)
{
	unsigned long delay = clock_t_to_jiffies(val);
	br->forward_delay = delay;
	if (br_is_root_bridge(br))
		br->bridge_forward_delay = delay;
}

67 68 69
static ssize_t store_forward_delay(struct device *d,
				   struct device_attribute *attr,
				   const char *buf, size_t len)
L
Linus Torvalds 已提交
70
{
71
	return store_bridge_parm(d, buf, len, set_forward_delay);
L
Linus Torvalds 已提交
72
}
73 74
static DEVICE_ATTR(forward_delay, S_IRUGO | S_IWUSR,
		   show_forward_delay, store_forward_delay);
L
Linus Torvalds 已提交
75

76 77
static ssize_t show_hello_time(struct device *d, struct device_attribute *attr,
			       char *buf)
L
Linus Torvalds 已提交
78 79
{
	return sprintf(buf, "%lu\n",
80
		       jiffies_to_clock_t(to_bridge(d)->hello_time));
L
Linus Torvalds 已提交
81 82 83 84 85 86 87 88 89 90
}

static void set_hello_time(struct net_bridge *br, unsigned long val)
{
	unsigned long t = clock_t_to_jiffies(val);
	br->hello_time = t;
	if (br_is_root_bridge(br))
		br->bridge_hello_time = t;
}

91 92
static ssize_t store_hello_time(struct device *d,
				struct device_attribute *attr, const char *buf,
L
Linus Torvalds 已提交
93 94
				size_t len)
{
95
	return store_bridge_parm(d, buf, len, set_hello_time);
L
Linus Torvalds 已提交
96
}
97 98
static DEVICE_ATTR(hello_time, S_IRUGO | S_IWUSR, show_hello_time,
		   store_hello_time);
L
Linus Torvalds 已提交
99

100 101
static ssize_t show_max_age(struct device *d, struct device_attribute *attr,
			    char *buf)
L
Linus Torvalds 已提交
102 103
{
	return sprintf(buf, "%lu\n",
104
		       jiffies_to_clock_t(to_bridge(d)->max_age));
L
Linus Torvalds 已提交
105 106 107 108 109 110 111 112 113 114
}

static void set_max_age(struct net_bridge *br, unsigned long val)
{
	unsigned long t = clock_t_to_jiffies(val);
	br->max_age = t;
	if (br_is_root_bridge(br))
		br->bridge_max_age = t;
}

115 116
static ssize_t store_max_age(struct device *d, struct device_attribute *attr,
			     const char *buf, size_t len)
L
Linus Torvalds 已提交
117
{
118
	return store_bridge_parm(d, buf, len, set_max_age);
L
Linus Torvalds 已提交
119
}
120
static DEVICE_ATTR(max_age, S_IRUGO | S_IWUSR, show_max_age, store_max_age);
L
Linus Torvalds 已提交
121

122 123
static ssize_t show_ageing_time(struct device *d,
				struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
124
{
125
	struct net_bridge *br = to_bridge(d);
L
Linus Torvalds 已提交
126 127 128 129 130 131 132 133
	return sprintf(buf, "%lu\n", jiffies_to_clock_t(br->ageing_time));
}

static void set_ageing_time(struct net_bridge *br, unsigned long val)
{
	br->ageing_time = clock_t_to_jiffies(val);
}

134 135 136
static ssize_t store_ageing_time(struct device *d,
				 struct device_attribute *attr,
				 const char *buf, size_t len)
L
Linus Torvalds 已提交
137
{
138
	return store_bridge_parm(d, buf, len, set_ageing_time);
L
Linus Torvalds 已提交
139
}
140 141
static DEVICE_ATTR(ageing_time, S_IRUGO | S_IWUSR, show_ageing_time,
		   store_ageing_time);
L
Linus Torvalds 已提交
142

143 144
static ssize_t show_stp_state(struct device *d,
			      struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
145
{
146
	struct net_bridge *br = to_bridge(d);
L
Linus Torvalds 已提交
147 148 149 150 151 152 153 154
	return sprintf(buf, "%d\n", br->stp_enabled);
}

static void set_stp_state(struct net_bridge *br, unsigned long val)
{
	br->stp_enabled = val;
}

155 156 157
static ssize_t store_stp_state(struct device *d,
			       struct device_attribute *attr, const char *buf,
			       size_t len)
L
Linus Torvalds 已提交
158
{
159
	return store_bridge_parm(d, buf, len, set_stp_state);
L
Linus Torvalds 已提交
160
}
161 162
static DEVICE_ATTR(stp_state, S_IRUGO | S_IWUSR, show_stp_state,
		   store_stp_state);
L
Linus Torvalds 已提交
163

164 165
static ssize_t show_priority(struct device *d, struct device_attribute *attr,
			     char *buf)
L
Linus Torvalds 已提交
166
{
167
	struct net_bridge *br = to_bridge(d);
L
Linus Torvalds 已提交
168 169 170 171 172 173 174 175 176
	return sprintf(buf, "%d\n",
		       (br->bridge_id.prio[0] << 8) | br->bridge_id.prio[1]);
}

static void set_priority(struct net_bridge *br, unsigned long val)
{
	br_stp_set_bridge_priority(br, (u16) val);
}

177
static ssize_t store_priority(struct device *d, struct device_attribute *attr,
L
Linus Torvalds 已提交
178 179
			       const char *buf, size_t len)
{
180
	return store_bridge_parm(d, buf, len, set_priority);
L
Linus Torvalds 已提交
181
}
182
static DEVICE_ATTR(priority, S_IRUGO | S_IWUSR, show_priority, store_priority);
L
Linus Torvalds 已提交
183

184 185
static ssize_t show_root_id(struct device *d, struct device_attribute *attr,
			    char *buf)
L
Linus Torvalds 已提交
186
{
187
	return br_show_bridge_id(buf, &to_bridge(d)->designated_root);
L
Linus Torvalds 已提交
188
}
189
static DEVICE_ATTR(root_id, S_IRUGO, show_root_id, NULL);
L
Linus Torvalds 已提交
190

191 192
static ssize_t show_bridge_id(struct device *d, struct device_attribute *attr,
			      char *buf)
L
Linus Torvalds 已提交
193
{
194
	return br_show_bridge_id(buf, &to_bridge(d)->bridge_id);
L
Linus Torvalds 已提交
195
}
196
static DEVICE_ATTR(bridge_id, S_IRUGO, show_bridge_id, NULL);
L
Linus Torvalds 已提交
197

198 199
static ssize_t show_root_port(struct device *d, struct device_attribute *attr,
			      char *buf)
L
Linus Torvalds 已提交
200
{
201
	return sprintf(buf, "%d\n", to_bridge(d)->root_port);
L
Linus Torvalds 已提交
202
}
203
static DEVICE_ATTR(root_port, S_IRUGO, show_root_port, NULL);
L
Linus Torvalds 已提交
204

205 206
static ssize_t show_root_path_cost(struct device *d,
				   struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
207
{
208
	return sprintf(buf, "%d\n", to_bridge(d)->root_path_cost);
L
Linus Torvalds 已提交
209
}
210
static DEVICE_ATTR(root_path_cost, S_IRUGO, show_root_path_cost, NULL);
L
Linus Torvalds 已提交
211

212 213
static ssize_t show_topology_change(struct device *d,
				    struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
214
{
215
	return sprintf(buf, "%d\n", to_bridge(d)->topology_change);
L
Linus Torvalds 已提交
216
}
217
static DEVICE_ATTR(topology_change, S_IRUGO, show_topology_change, NULL);
L
Linus Torvalds 已提交
218

219 220 221
static ssize_t show_topology_change_detected(struct device *d,
					     struct device_attribute *attr,
					     char *buf)
L
Linus Torvalds 已提交
222
{
223
	struct net_bridge *br = to_bridge(d);
L
Linus Torvalds 已提交
224 225
	return sprintf(buf, "%d\n", br->topology_change_detected);
}
226 227
static DEVICE_ATTR(topology_change_detected, S_IRUGO,
		   show_topology_change_detected, NULL);
L
Linus Torvalds 已提交
228

229 230
static ssize_t show_hello_timer(struct device *d,
				struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
231
{
232
	struct net_bridge *br = to_bridge(d);
L
Linus Torvalds 已提交
233 234
	return sprintf(buf, "%ld\n", br_timer_value(&br->hello_timer));
}
235
static DEVICE_ATTR(hello_timer, S_IRUGO, show_hello_timer, NULL);
L
Linus Torvalds 已提交
236

237 238
static ssize_t show_tcn_timer(struct device *d, struct device_attribute *attr,
			      char *buf)
L
Linus Torvalds 已提交
239
{
240
	struct net_bridge *br = to_bridge(d);
L
Linus Torvalds 已提交
241 242
	return sprintf(buf, "%ld\n", br_timer_value(&br->tcn_timer));
}
243
static DEVICE_ATTR(tcn_timer, S_IRUGO, show_tcn_timer, NULL);
L
Linus Torvalds 已提交
244

245 246 247
static ssize_t show_topology_change_timer(struct device *d,
					  struct device_attribute *attr,
					  char *buf)
L
Linus Torvalds 已提交
248
{
249
	struct net_bridge *br = to_bridge(d);
L
Linus Torvalds 已提交
250 251
	return sprintf(buf, "%ld\n", br_timer_value(&br->topology_change_timer));
}
252 253
static DEVICE_ATTR(topology_change_timer, S_IRUGO, show_topology_change_timer,
		   NULL);
L
Linus Torvalds 已提交
254

255 256
static ssize_t show_gc_timer(struct device *d, struct device_attribute *attr,
			     char *buf)
L
Linus Torvalds 已提交
257
{
258
	struct net_bridge *br = to_bridge(d);
L
Linus Torvalds 已提交
259 260
	return sprintf(buf, "%ld\n", br_timer_value(&br->gc_timer));
}
261
static DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
L
Linus Torvalds 已提交
262

263 264
static ssize_t show_group_addr(struct device *d,
			       struct device_attribute *attr, char *buf)
265
{
266
	struct net_bridge *br = to_bridge(d);
267 268 269 270 271 272
	return sprintf(buf, "%x:%x:%x:%x:%x:%x\n",
		       br->group_addr[0], br->group_addr[1],
		       br->group_addr[2], br->group_addr[3],
		       br->group_addr[4], br->group_addr[5]);
}

273 274 275
static ssize_t store_group_addr(struct device *d,
				struct device_attribute *attr,
				const char *buf, size_t len)
276
{
277
	struct net_bridge *br = to_bridge(d);
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
	unsigned new_addr[6];
	int i;

	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	if (sscanf(buf, "%x:%x:%x:%x:%x:%x",
		   &new_addr[0], &new_addr[1], &new_addr[2],
		   &new_addr[3], &new_addr[4], &new_addr[5]) != 6)
		return -EINVAL;

	/* Must be 01:80:c2:00:00:0X */
	for (i = 0; i < 5; i++)
		if (new_addr[i] != br_group_address[i])
			return -EINVAL;

	if (new_addr[5] & ~0xf)
		return -EINVAL;

	if (new_addr[5] == 1 	/* 802.3x Pause address */
	    || new_addr[5] == 2 /* 802.3ad Slow protocols */
	    || new_addr[5] == 3) /* 802.1X PAE address */
		return -EINVAL;

	spin_lock_bh(&br->lock);
	for (i = 0; i < 6; i++)
		br->group_addr[i] = new_addr[i];
	spin_unlock_bh(&br->lock);
	return len;
}

309 310
static DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
		   show_group_addr, store_group_addr);
311

312 313 314 315 316 317 318 319 320 321 322 323 324
static ssize_t store_flush(struct device *d,
			   struct device_attribute *attr,
			   const char *buf, size_t len)
{
	struct net_bridge *br = to_bridge(d);

	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	br_fdb_flush(br);
	return len;
}
static DEVICE_ATTR(flush, S_IWUSR, NULL, store_flush);
325

L
Linus Torvalds 已提交
326
static struct attribute *bridge_attrs[] = {
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
	&dev_attr_forward_delay.attr,
	&dev_attr_hello_time.attr,
	&dev_attr_max_age.attr,
	&dev_attr_ageing_time.attr,
	&dev_attr_stp_state.attr,
	&dev_attr_priority.attr,
	&dev_attr_bridge_id.attr,
	&dev_attr_root_id.attr,
	&dev_attr_root_path_cost.attr,
	&dev_attr_root_port.attr,
	&dev_attr_topology_change.attr,
	&dev_attr_topology_change_detected.attr,
	&dev_attr_hello_timer.attr,
	&dev_attr_tcn_timer.attr,
	&dev_attr_topology_change_timer.attr,
	&dev_attr_gc_timer.attr,
	&dev_attr_group_addr.attr,
344
	&dev_attr_flush.attr,
L
Linus Torvalds 已提交
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
	NULL
};

static struct attribute_group bridge_group = {
	.name = SYSFS_BRIDGE_ATTR,
	.attrs = bridge_attrs,
};

/*
 * Export the forwarding information table as a binary file
 * The records are struct __fdb_entry.
 *
 * Returns the number of bytes read.
 */
static ssize_t brforward_read(struct kobject *kobj, char *buf,
			   loff_t off, size_t count)
{
362 363
	struct device *dev = to_dev(kobj);
	struct net_bridge *br = to_bridge(dev);
L
Linus Torvalds 已提交
364 365 366 367 368 369
	int n;

	/* must read whole records */
	if (off % sizeof(struct __fdb_entry) != 0)
		return -EINVAL;

370
	n =  br_fdb_fillbuf(br, buf,
L
Linus Torvalds 已提交
371 372 373 374 375
			    count / sizeof(struct __fdb_entry),
			    off / sizeof(struct __fdb_entry));

	if (n > 0)
		n *= sizeof(struct __fdb_entry);
376

L
Linus Torvalds 已提交
377 378 379 380 381
	return n;
}

static struct bin_attribute bridge_forward = {
	.attr = { .name = SYSFS_BRIDGE_FDB,
382
		  .mode = S_IRUGO,
L
Linus Torvalds 已提交
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
		  .owner = THIS_MODULE, },
	.read = brforward_read,
};

/*
 * Add entries in sysfs onto the existing network class device
 * for the bridge.
 *   Adds a attribute group "bridge" containing tuning parameters.
 *   Binary attribute containing the forward table
 *   Sub directory to hold links to interfaces.
 *
 * Note: the ifobj exists only to be a subdirectory
 *   to hold links.  The ifobj exists in same data structure
 *   as it's parent the bridge so reference counting works.
 */
int br_sysfs_addbr(struct net_device *dev)
{
400
	struct kobject *brobj = &dev->dev.kobj;
L
Linus Torvalds 已提交
401 402 403 404 405 406 407 408 409 410 411 412
	struct net_bridge *br = netdev_priv(dev);
	int err;

	err = sysfs_create_group(brobj, &bridge_group);
	if (err) {
		pr_info("%s: can't create group %s/%s\n",
			__FUNCTION__, dev->name, bridge_group.name);
		goto out1;
	}

	err = sysfs_create_bin_file(brobj, &bridge_forward);
	if (err) {
413
		pr_info("%s: can't create attribute file %s/%s\n",
L
Linus Torvalds 已提交
414 415 416 417
			__FUNCTION__, dev->name, bridge_forward.attr.name);
		goto out2;
	}

418

L
Linus Torvalds 已提交
419 420 421 422 423 424 425 426 427 428 429 430 431
	kobject_set_name(&br->ifobj, SYSFS_BRIDGE_PORT_SUBDIR);
	br->ifobj.ktype = NULL;
	br->ifobj.kset = NULL;
	br->ifobj.parent = brobj;

	err = kobject_register(&br->ifobj);
	if (err) {
		pr_info("%s: can't add kobject (directory) %s/%s\n",
			__FUNCTION__, dev->name, br->ifobj.name);
		goto out3;
	}
	return 0;
 out3:
432
	sysfs_remove_bin_file(&dev->dev.kobj, &bridge_forward);
L
Linus Torvalds 已提交
433
 out2:
434
	sysfs_remove_group(&dev->dev.kobj, &bridge_group);
L
Linus Torvalds 已提交
435 436 437 438 439 440 441
 out1:
	return err;

}

void br_sysfs_delbr(struct net_device *dev)
{
442
	struct kobject *kobj = &dev->dev.kobj;
L
Linus Torvalds 已提交
443 444 445 446 447 448
	struct net_bridge *br = netdev_priv(dev);

	kobject_unregister(&br->ifobj);
	sysfs_remove_bin_file(kobj, &bridge_forward);
	sysfs_remove_group(kobj, &bridge_group);
}