net-sysfs.c 13.6 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4
/*
 * net-sysfs.c - network device class and attributes
 *
 * Copyright (c) 2003 Stephen Hemminger <shemminger@osdl.org>
5
 *
Linus Torvalds's avatar
Linus Torvalds committed
6 7 8 9 10 11
 *	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.
 */

12
#include <linux/capability.h>
Linus Torvalds's avatar
Linus Torvalds committed
13 14 15 16 17 18
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <net/sock.h>
#include <linux/rtnetlink.h>
#include <linux/wireless.h>
19
#include <net/iw_handler.h>
Linus Torvalds's avatar
Linus Torvalds committed
20

21 22
#include "net-sysfs.h"

23
#ifdef CONFIG_SYSFS
Linus Torvalds's avatar
Linus Torvalds committed
24
static const char fmt_hex[] = "%#x\n";
25
static const char fmt_long_hex[] = "%#lx\n";
Linus Torvalds's avatar
Linus Torvalds committed
26 27 28
static const char fmt_dec[] = "%d\n";
static const char fmt_ulong[] = "%lu\n";

29
static inline int dev_isalive(const struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
30
{
31
	return dev->reg_state <= NETREG_REGISTERED;
Linus Torvalds's avatar
Linus Torvalds committed
32 33 34
}

/* use same locking rules as GIF* ioctl's */
35 36
static ssize_t netdev_show(const struct device *dev,
			   struct device_attribute *attr, char *buf,
Linus Torvalds's avatar
Linus Torvalds committed
37 38
			   ssize_t (*format)(const struct net_device *, char *))
{
39
	struct net_device *net = to_net_dev(dev);
Linus Torvalds's avatar
Linus Torvalds committed
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
	ssize_t ret = -EINVAL;

	read_lock(&dev_base_lock);
	if (dev_isalive(net))
		ret = (*format)(net, buf);
	read_unlock(&dev_base_lock);

	return ret;
}

/* generate a show function for simple field */
#define NETDEVICE_SHOW(field, format_string)				\
static ssize_t format_##field(const struct net_device *net, char *buf)	\
{									\
	return sprintf(buf, format_string, net->field);			\
}									\
56 57
static ssize_t show_##field(struct device *dev,				\
			    struct device_attribute *attr, char *buf)	\
Linus Torvalds's avatar
Linus Torvalds committed
58
{									\
59
	return netdev_show(dev, attr, buf, format_##field);		\
Linus Torvalds's avatar
Linus Torvalds committed
60 61 62 63
}


/* use same locking and permission rules as SIF* ioctl's */
64
static ssize_t netdev_store(struct device *dev, struct device_attribute *attr,
Linus Torvalds's avatar
Linus Torvalds committed
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
			    const char *buf, size_t len,
			    int (*set)(struct net_device *, unsigned long))
{
	struct net_device *net = to_net_dev(dev);
	char *endp;
	unsigned long new;
	int ret = -EINVAL;

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

	new = simple_strtoul(buf, &endp, 0);
	if (endp == buf)
		goto err;

	rtnl_lock();
	if (dev_isalive(net)) {
		if ((ret = (*set)(net, new)) == 0)
			ret = len;
	}
	rtnl_unlock();
 err:
	return ret;
}

90
NETDEVICE_SHOW(dev_id, fmt_hex);
91 92 93 94 95
NETDEVICE_SHOW(addr_len, fmt_dec);
NETDEVICE_SHOW(iflink, fmt_dec);
NETDEVICE_SHOW(ifindex, fmt_dec);
NETDEVICE_SHOW(features, fmt_long_hex);
NETDEVICE_SHOW(type, fmt_dec);
96
NETDEVICE_SHOW(link_mode, fmt_dec);
Linus Torvalds's avatar
Linus Torvalds committed
97 98

/* use same locking rules as GIFHWADDR ioctl's */
99 100
static ssize_t show_address(struct device *dev, struct device_attribute *attr,
			    char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
101 102 103 104 105 106
{
	struct net_device *net = to_net_dev(dev);
	ssize_t ret = -EINVAL;

	read_lock(&dev_base_lock);
	if (dev_isalive(net))
107
		ret = sysfs_format_mac(buf, net->dev_addr, net->addr_len);
Linus Torvalds's avatar
Linus Torvalds committed
108 109 110 111
	read_unlock(&dev_base_lock);
	return ret;
}

112 113
static ssize_t show_broadcast(struct device *dev,
			    struct device_attribute *attr, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
114 115 116
{
	struct net_device *net = to_net_dev(dev);
	if (dev_isalive(net))
117
		return sysfs_format_mac(buf, net->broadcast, net->addr_len);
Linus Torvalds's avatar
Linus Torvalds committed
118 119 120
	return -EINVAL;
}

121 122
static ssize_t show_carrier(struct device *dev,
			    struct device_attribute *attr, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
123 124 125 126 127 128 129 130
{
	struct net_device *netdev = to_net_dev(dev);
	if (netif_running(netdev)) {
		return sprintf(buf, fmt_dec, !!netif_carrier_ok(netdev));
	}
	return -EINVAL;
}

131 132
static ssize_t show_dormant(struct device *dev,
			    struct device_attribute *attr, char *buf)
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
{
	struct net_device *netdev = to_net_dev(dev);

	if (netif_running(netdev))
		return sprintf(buf, fmt_dec, !!netif_dormant(netdev));

	return -EINVAL;
}

static const char *operstates[] = {
	"unknown",
	"notpresent", /* currently unused */
	"down",
	"lowerlayerdown",
	"testing", /* currently unused */
	"dormant",
	"up"
};

152 153
static ssize_t show_operstate(struct device *dev,
			      struct device_attribute *attr, char *buf)
154 155 156 157 158 159 160 161 162 163
{
	const struct net_device *netdev = to_net_dev(dev);
	unsigned char operstate;

	read_lock(&dev_base_lock);
	operstate = netdev->operstate;
	if (!netif_running(netdev))
		operstate = IF_OPER_DOWN;
	read_unlock(&dev_base_lock);

164
	if (operstate >= ARRAY_SIZE(operstates))
165 166 167 168 169
		return -EINVAL; /* should not happen */

	return sprintf(buf, "%s\n", operstates[operstate]);
}

Linus Torvalds's avatar
Linus Torvalds committed
170 171 172 173 174 175 176 177
/* read-write attributes */
NETDEVICE_SHOW(mtu, fmt_dec);

static int change_mtu(struct net_device *net, unsigned long new_mtu)
{
	return dev_set_mtu(net, (int) new_mtu);
}

178 179
static ssize_t store_mtu(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t len)
Linus Torvalds's avatar
Linus Torvalds committed
180
{
181
	return netdev_store(dev, attr, buf, len, change_mtu);
Linus Torvalds's avatar
Linus Torvalds committed
182 183 184 185 186 187 188 189 190
}

NETDEVICE_SHOW(flags, fmt_hex);

static int change_flags(struct net_device *net, unsigned long new_flags)
{
	return dev_change_flags(net, (unsigned) new_flags);
}

191 192
static ssize_t store_flags(struct device *dev, struct device_attribute *attr,
			   const char *buf, size_t len)
Linus Torvalds's avatar
Linus Torvalds committed
193
{
194
	return netdev_store(dev, attr, buf, len, change_flags);
Linus Torvalds's avatar
Linus Torvalds committed
195 196 197 198 199 200 201 202 203 204
}

NETDEVICE_SHOW(tx_queue_len, fmt_ulong);

static int change_tx_queue_len(struct net_device *net, unsigned long new_len)
{
	net->tx_queue_len = new_len;
	return 0;
}

205 206 207
static ssize_t store_tx_queue_len(struct device *dev,
				  struct device_attribute *attr,
				  const char *buf, size_t len)
Linus Torvalds's avatar
Linus Torvalds committed
208
{
209
	return netdev_store(dev, attr, buf, len, change_tx_queue_len);
Linus Torvalds's avatar
Linus Torvalds committed
210 211
}

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
static ssize_t store_ifalias(struct device *dev, struct device_attribute *attr,
			     const char *buf, size_t len)
{
	struct net_device *netdev = to_net_dev(dev);
	size_t count = len;
	ssize_t ret;

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

	/* ignore trailing newline */
	if (len >  0 && buf[len - 1] == '\n')
		--count;

	rtnl_lock();
	ret = dev_set_alias(netdev, buf, count);
	rtnl_unlock();

	return ret < 0 ? ret : len;
}

static ssize_t show_ifalias(struct device *dev,
			    struct device_attribute *attr, char *buf)
{
	const struct net_device *netdev = to_net_dev(dev);
	ssize_t ret = 0;

	rtnl_lock();
	if (netdev->ifalias)
		ret = sprintf(buf, "%s\n", netdev->ifalias);
	rtnl_unlock();
	return ret;
}

246
static struct device_attribute net_class_attributes[] = {
247
	__ATTR(addr_len, S_IRUGO, show_addr_len, NULL),
248
	__ATTR(dev_id, S_IRUGO, show_dev_id, NULL),
249
	__ATTR(ifalias, S_IRUGO | S_IWUSR, show_ifalias, store_ifalias),
250 251 252 253
	__ATTR(iflink, S_IRUGO, show_iflink, NULL),
	__ATTR(ifindex, S_IRUGO, show_ifindex, NULL),
	__ATTR(features, S_IRUGO, show_features, NULL),
	__ATTR(type, S_IRUGO, show_type, NULL),
254
	__ATTR(link_mode, S_IRUGO, show_link_mode, NULL),
255 256 257
	__ATTR(address, S_IRUGO, show_address, NULL),
	__ATTR(broadcast, S_IRUGO, show_broadcast, NULL),
	__ATTR(carrier, S_IRUGO, show_carrier, NULL),
258 259
	__ATTR(dormant, S_IRUGO, show_dormant, NULL),
	__ATTR(operstate, S_IRUGO, show_operstate, NULL),
260 261 262 263 264
	__ATTR(mtu, S_IRUGO | S_IWUSR, show_mtu, store_mtu),
	__ATTR(flags, S_IRUGO | S_IWUSR, show_flags, store_flags),
	__ATTR(tx_queue_len, S_IRUGO | S_IWUSR, show_tx_queue_len,
	       store_tx_queue_len),
	{}
Linus Torvalds's avatar
Linus Torvalds committed
265 266 267
};

/* Show a given an attribute in the statistics group */
268 269
static ssize_t netstat_show(const struct device *d,
			    struct device_attribute *attr, char *buf,
Linus Torvalds's avatar
Linus Torvalds committed
270 271
			    unsigned long offset)
{
272
	struct net_device *dev = to_net_dev(d);
Linus Torvalds's avatar
Linus Torvalds committed
273 274 275
	struct net_device_stats *stats;
	ssize_t ret = -EINVAL;

276 277
	WARN_ON(offset > sizeof(struct net_device_stats) ||
			offset % sizeof(unsigned long) != 0);
Linus Torvalds's avatar
Linus Torvalds committed
278 279

	read_lock(&dev_base_lock);
280 281
	if (dev_isalive(dev)) {
		stats = dev->get_stats(dev);
Linus Torvalds's avatar
Linus Torvalds committed
282 283
		ret = sprintf(buf, fmt_ulong,
			      *(unsigned long *)(((u8 *) stats) + offset));
284
	}
Linus Torvalds's avatar
Linus Torvalds committed
285 286 287 288 289 290
	read_unlock(&dev_base_lock);
	return ret;
}

/* generate a read-only statistics attribute */
#define NETSTAT_ENTRY(name)						\
291 292
static ssize_t show_##name(struct device *d,				\
			   struct device_attribute *attr, char *buf) 	\
Linus Torvalds's avatar
Linus Torvalds committed
293
{									\
294
	return netstat_show(d, attr, buf,				\
Linus Torvalds's avatar
Linus Torvalds committed
295 296
			    offsetof(struct net_device_stats, name));	\
}									\
297
static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
Linus Torvalds's avatar
Linus Torvalds committed
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323

NETSTAT_ENTRY(rx_packets);
NETSTAT_ENTRY(tx_packets);
NETSTAT_ENTRY(rx_bytes);
NETSTAT_ENTRY(tx_bytes);
NETSTAT_ENTRY(rx_errors);
NETSTAT_ENTRY(tx_errors);
NETSTAT_ENTRY(rx_dropped);
NETSTAT_ENTRY(tx_dropped);
NETSTAT_ENTRY(multicast);
NETSTAT_ENTRY(collisions);
NETSTAT_ENTRY(rx_length_errors);
NETSTAT_ENTRY(rx_over_errors);
NETSTAT_ENTRY(rx_crc_errors);
NETSTAT_ENTRY(rx_frame_errors);
NETSTAT_ENTRY(rx_fifo_errors);
NETSTAT_ENTRY(rx_missed_errors);
NETSTAT_ENTRY(tx_aborted_errors);
NETSTAT_ENTRY(tx_carrier_errors);
NETSTAT_ENTRY(tx_fifo_errors);
NETSTAT_ENTRY(tx_heartbeat_errors);
NETSTAT_ENTRY(tx_window_errors);
NETSTAT_ENTRY(rx_compressed);
NETSTAT_ENTRY(tx_compressed);

static struct attribute *netstat_attrs[] = {
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
	&dev_attr_rx_packets.attr,
	&dev_attr_tx_packets.attr,
	&dev_attr_rx_bytes.attr,
	&dev_attr_tx_bytes.attr,
	&dev_attr_rx_errors.attr,
	&dev_attr_tx_errors.attr,
	&dev_attr_rx_dropped.attr,
	&dev_attr_tx_dropped.attr,
	&dev_attr_multicast.attr,
	&dev_attr_collisions.attr,
	&dev_attr_rx_length_errors.attr,
	&dev_attr_rx_over_errors.attr,
	&dev_attr_rx_crc_errors.attr,
	&dev_attr_rx_frame_errors.attr,
	&dev_attr_rx_fifo_errors.attr,
	&dev_attr_rx_missed_errors.attr,
	&dev_attr_tx_aborted_errors.attr,
	&dev_attr_tx_carrier_errors.attr,
	&dev_attr_tx_fifo_errors.attr,
	&dev_attr_tx_heartbeat_errors.attr,
	&dev_attr_tx_window_errors.attr,
	&dev_attr_rx_compressed.attr,
	&dev_attr_tx_compressed.attr,
Linus Torvalds's avatar
Linus Torvalds committed
347 348 349 350 351 352 353 354 355
	NULL
};


static struct attribute_group netstat_group = {
	.name  = "statistics",
	.attrs  = netstat_attrs,
};

356
#ifdef CONFIG_WIRELESS_EXT_SYSFS
Linus Torvalds's avatar
Linus Torvalds committed
357
/* helper function that does all the locking etc for wireless stats */
358
static ssize_t wireless_show(struct device *d, char *buf,
Linus Torvalds's avatar
Linus Torvalds committed
359 360 361
			     ssize_t (*format)(const struct iw_statistics *,
					       char *))
{
362
	struct net_device *dev = to_net_dev(d);
363
	const struct iw_statistics *iw = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
364
	ssize_t ret = -EINVAL;
365

Linus Torvalds's avatar
Linus Torvalds committed
366
	read_lock(&dev_base_lock);
367
	if (dev_isalive(dev)) {
368 369
		if (dev->wireless_handlers &&
		    dev->wireless_handlers->get_wireless_stats)
370 371 372 373
			iw = dev->wireless_handlers->get_wireless_stats(dev);
		if (iw != NULL)
			ret = (*format)(iw, buf);
	}
Linus Torvalds's avatar
Linus Torvalds committed
374 375 376 377 378 379 380 381 382 383 384
	read_unlock(&dev_base_lock);

	return ret;
}

/* show function template for wireless fields */
#define WIRELESS_SHOW(name, field, format_string)			\
static ssize_t format_iw_##name(const struct iw_statistics *iw, char *buf) \
{									\
	return sprintf(buf, format_string, iw->field);			\
}									\
385 386
static ssize_t show_iw_##name(struct device *d,				\
			      struct device_attribute *attr, char *buf)	\
Linus Torvalds's avatar
Linus Torvalds committed
387
{									\
388
	return wireless_show(d, buf, format_iw_##name);			\
Linus Torvalds's avatar
Linus Torvalds committed
389
}									\
390
static DEVICE_ATTR(name, S_IRUGO, show_iw_##name, NULL)
Linus Torvalds's avatar
Linus Torvalds committed
391 392 393 394 395 396 397 398 399 400 401 402 403

WIRELESS_SHOW(status, status, fmt_hex);
WIRELESS_SHOW(link, qual.qual, fmt_dec);
WIRELESS_SHOW(level, qual.level, fmt_dec);
WIRELESS_SHOW(noise, qual.noise, fmt_dec);
WIRELESS_SHOW(nwid, discard.nwid, fmt_dec);
WIRELESS_SHOW(crypt, discard.code, fmt_dec);
WIRELESS_SHOW(fragment, discard.fragment, fmt_dec);
WIRELESS_SHOW(misc, discard.misc, fmt_dec);
WIRELESS_SHOW(retries, discard.retries, fmt_dec);
WIRELESS_SHOW(beacon, miss.beacon, fmt_dec);

static struct attribute *wireless_attrs[] = {
404 405 406 407 408 409 410 411 412 413
	&dev_attr_status.attr,
	&dev_attr_link.attr,
	&dev_attr_level.attr,
	&dev_attr_noise.attr,
	&dev_attr_nwid.attr,
	&dev_attr_crypt.attr,
	&dev_attr_fragment.attr,
	&dev_attr_retries.attr,
	&dev_attr_misc.attr,
	&dev_attr_beacon.attr,
Linus Torvalds's avatar
Linus Torvalds committed
414 415 416 417 418 419 420 421 422
	NULL
};

static struct attribute_group wireless_group = {
	.name = "wireless",
	.attrs = wireless_attrs,
};
#endif

423 424
#endif /* CONFIG_SYSFS */

Linus Torvalds's avatar
Linus Torvalds committed
425
#ifdef CONFIG_HOTPLUG
426
static int netdev_uevent(struct device *d, struct kobj_uevent_env *env)
Linus Torvalds's avatar
Linus Torvalds committed
427
{
428
	struct net_device *dev = to_net_dev(d);
429
	int retval;
Linus Torvalds's avatar
Linus Torvalds committed
430

431
	/* pass interface to uevent. */
432
	retval = add_uevent_var(env, "INTERFACE=%s", dev->name);
433 434
	if (retval)
		goto exit;
435 436 437 438

	/* pass ifindex to uevent.
	 * ifindex is useful as it won't change (interface name may change)
	 * and is what RtNetlink uses natively. */
439
	retval = add_uevent_var(env, "IFINDEX=%d", dev->ifindex);
Linus Torvalds's avatar
Linus Torvalds committed
440

441 442
exit:
	return retval;
Linus Torvalds's avatar
Linus Torvalds committed
443 444 445 446
}
#endif

/*
447
 *	netdev_release -- destroy and free a dead device.
448
 *	Called when last reference to device kobject is gone.
Linus Torvalds's avatar
Linus Torvalds committed
449
 */
450
static void netdev_release(struct device *d)
Linus Torvalds's avatar
Linus Torvalds committed
451
{
452
	struct net_device *dev = to_net_dev(d);
Linus Torvalds's avatar
Linus Torvalds committed
453 454 455

	BUG_ON(dev->reg_state != NETREG_RELEASED);

456
	kfree(dev->ifalias);
Linus Torvalds's avatar
Linus Torvalds committed
457 458 459 460 461
	kfree((char *)dev - dev->padded);
}

static struct class net_class = {
	.name = "net",
462
	.dev_release = netdev_release,
463
#ifdef CONFIG_SYSFS
464
	.dev_attrs = net_class_attributes,
465
#endif /* CONFIG_SYSFS */
Linus Torvalds's avatar
Linus Torvalds committed
466
#ifdef CONFIG_HOTPLUG
467
	.dev_uevent = netdev_uevent,
Linus Torvalds's avatar
Linus Torvalds committed
468 469 470
#endif
};

471 472 473
/* Delete sysfs entries but hold kobject reference until after all
 * netdev references are gone.
 */
474
void netdev_unregister_kobject(struct net_device * net)
Linus Torvalds's avatar
Linus Torvalds committed
475
{
476 477 478 479
	struct device *dev = &(net->dev);

	kobject_get(&dev->kobj);
	device_del(dev);
Linus Torvalds's avatar
Linus Torvalds committed
480 481 482
}

/* Create sysfs entries for network device. */
483
int netdev_register_kobject(struct net_device *net)
Linus Torvalds's avatar
Linus Torvalds committed
484
{
485
	struct device *dev = &(net->dev);
486
	struct attribute_group **groups = net->sysfs_groups;
Linus Torvalds's avatar
Linus Torvalds committed
487

488 489 490
	dev->class = &net_class;
	dev->platform_data = net;
	dev->groups = groups;
Linus Torvalds's avatar
Linus Torvalds committed
491

492
	BUILD_BUG_ON(BUS_ID_SIZE < IFNAMSIZ);
493
	strlcpy(dev->bus_id, net->name, BUS_ID_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
494

495
#ifdef CONFIG_SYSFS
496
	*groups++ = &netstat_group;
Linus Torvalds's avatar
Linus Torvalds committed
497

498
#ifdef CONFIG_WIRELESS_EXT_SYSFS
499
	if (net->wireless_handlers && net->wireless_handlers->get_wireless_stats)
500
		*groups++ = &wireless_group;
Linus Torvalds's avatar
Linus Torvalds committed
501
#endif
502
#endif /* CONFIG_SYSFS */
Linus Torvalds's avatar
Linus Torvalds committed
503

504
	return device_add(dev);
Linus Torvalds's avatar
Linus Torvalds committed
505 506
}

507 508 509 510 511 512 513 514 515 516 517 518 519
int netdev_class_create_file(struct class_attribute *class_attr)
{
	return class_create_file(&net_class, class_attr);
}

void netdev_class_remove_file(struct class_attribute *class_attr)
{
	class_remove_file(&net_class, class_attr);
}

EXPORT_SYMBOL(netdev_class_create_file);
EXPORT_SYMBOL(netdev_class_remove_file);

520 521 522 523 524 525
void netdev_initialize_kobject(struct net_device *net)
{
	struct device *device = &(net->dev);
	device_initialize(device);
}

526
int netdev_kobject_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
527 528 529
{
	return class_register(&net_class);
}