1962 lines · 45.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * net-sysfs.c - network device class and attributes * * Copyright (c) 2003 Stephen Hemminger <shemminger@osdl.org> */ #include <linux/capability.h>#include <linux/kernel.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/slab.h>#include <linux/sched/signal.h>#include <linux/sched/isolation.h>#include <linux/nsproxy.h>#include <net/sock.h>#include <net/net_namespace.h>#include <linux/rtnetlink.h>#include <linux/vmalloc.h>#include <linux/export.h>#include <linux/jiffies.h>#include <linux/pm_runtime.h>#include <linux/of.h>#include <linux/of_net.h>#include <linux/cpu.h> #include "net-sysfs.h" #ifdef CONFIG_SYSFSstatic const char fmt_hex[] = "%#x\n";static const char fmt_dec[] = "%d\n";static const char fmt_ulong[] = "%lu\n";static const char fmt_u64[] = "%llu\n"; static inline int dev_isalive(const struct net_device *dev){	return dev->reg_state <= NETREG_REGISTERED;} /* use same locking rules as GIF* ioctl's */static ssize_t netdev_show(const struct device *dev,			   struct device_attribute *attr, char *buf,			   ssize_t (*format)(const struct net_device *, char *)){	struct net_device *ndev = to_net_dev(dev);	ssize_t ret = -EINVAL; 	read_lock(&dev_base_lock);	if (dev_isalive(ndev))		ret = (*format)(ndev, 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 *dev, char *buf)	\{									\	return sprintf(buf, format_string, dev->field);			\}									\static ssize_t field##_show(struct device *dev,				\			    struct device_attribute *attr, char *buf)	\{									\	return netdev_show(dev, attr, buf, format_##field);		\}									\ #define NETDEVICE_SHOW_RO(field, format_string)				\NETDEVICE_SHOW(field, format_string);					\static DEVICE_ATTR_RO(field) #define NETDEVICE_SHOW_RW(field, format_string)				\NETDEVICE_SHOW(field, format_string);					\static DEVICE_ATTR_RW(field) /* use same locking and permission rules as SIF* ioctl's */static ssize_t netdev_store(struct device *dev, struct device_attribute *attr,			    const char *buf, size_t len,			    int (*set)(struct net_device *, unsigned long)){	struct net_device *netdev = to_net_dev(dev);	struct net *net = dev_net(netdev);	unsigned long new;	int ret; 	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))		return -EPERM; 	ret = kstrtoul(buf, 0, &new);	if (ret)		goto err; 	if (!rtnl_trylock())		return restart_syscall(); 	if (dev_isalive(netdev)) {		ret = (*set)(netdev, new);		if (ret == 0)			ret = len;	}	rtnl_unlock(); err:	return ret;} NETDEVICE_SHOW_RO(dev_id, fmt_hex);NETDEVICE_SHOW_RO(dev_port, fmt_dec);NETDEVICE_SHOW_RO(addr_assign_type, fmt_dec);NETDEVICE_SHOW_RO(addr_len, fmt_dec);NETDEVICE_SHOW_RO(ifindex, fmt_dec);NETDEVICE_SHOW_RO(type, fmt_dec);NETDEVICE_SHOW_RO(link_mode, fmt_dec); static ssize_t iflink_show(struct device *dev, struct device_attribute *attr,			   char *buf){	struct net_device *ndev = to_net_dev(dev); 	return sprintf(buf, fmt_dec, dev_get_iflink(ndev));}static DEVICE_ATTR_RO(iflink); static ssize_t format_name_assign_type(const struct net_device *dev, char *buf){	return sprintf(buf, fmt_dec, dev->name_assign_type);} static ssize_t name_assign_type_show(struct device *dev,				     struct device_attribute *attr,				     char *buf){	struct net_device *ndev = to_net_dev(dev);	ssize_t ret = -EINVAL; 	if (ndev->name_assign_type != NET_NAME_UNKNOWN)		ret = netdev_show(dev, attr, buf, format_name_assign_type); 	return ret;}static DEVICE_ATTR_RO(name_assign_type); /* use same locking rules as GIFHWADDR ioctl's */static ssize_t address_show(struct device *dev, struct device_attribute *attr,			    char *buf){	struct net_device *ndev = to_net_dev(dev);	ssize_t ret = -EINVAL; 	read_lock(&dev_base_lock);	if (dev_isalive(ndev))		ret = sysfs_format_mac(buf, ndev->dev_addr, ndev->addr_len);	read_unlock(&dev_base_lock);	return ret;}static DEVICE_ATTR_RO(address); static ssize_t broadcast_show(struct device *dev,			      struct device_attribute *attr, char *buf){	struct net_device *ndev = to_net_dev(dev); 	if (dev_isalive(ndev))		return sysfs_format_mac(buf, ndev->broadcast, ndev->addr_len);	return -EINVAL;}static DEVICE_ATTR_RO(broadcast); static int change_carrier(struct net_device *dev, unsigned long new_carrier){	if (!netif_running(dev))		return -EINVAL;	return dev_change_carrier(dev, (bool)new_carrier);} static ssize_t carrier_store(struct device *dev, struct device_attribute *attr,			     const char *buf, size_t len){	return netdev_store(dev, attr, buf, len, change_carrier);} static ssize_t carrier_show(struct device *dev,			    struct device_attribute *attr, char *buf){	struct net_device *netdev = to_net_dev(dev); 	if (netif_running(netdev))		return sprintf(buf, fmt_dec, !!netif_carrier_ok(netdev)); 	return -EINVAL;}static DEVICE_ATTR_RW(carrier); static ssize_t speed_show(struct device *dev,			  struct device_attribute *attr, char *buf){	struct net_device *netdev = to_net_dev(dev);	int ret = -EINVAL; 	if (!rtnl_trylock())		return restart_syscall(); 	if (netif_running(netdev)) {		struct ethtool_link_ksettings cmd; 		if (!__ethtool_get_link_ksettings(netdev, &cmd))			ret = sprintf(buf, fmt_dec, cmd.base.speed);	}	rtnl_unlock();	return ret;}static DEVICE_ATTR_RO(speed); static ssize_t duplex_show(struct device *dev,			   struct device_attribute *attr, char *buf){	struct net_device *netdev = to_net_dev(dev);	int ret = -EINVAL; 	if (!rtnl_trylock())		return restart_syscall(); 	if (netif_running(netdev)) {		struct ethtool_link_ksettings cmd; 		if (!__ethtool_get_link_ksettings(netdev, &cmd)) {			const char *duplex; 			switch (cmd.base.duplex) {			case DUPLEX_HALF:				duplex = "half";				break;			case DUPLEX_FULL:				duplex = "full";				break;			default:				duplex = "unknown";				break;			}			ret = sprintf(buf, "%s\n", duplex);		}	}	rtnl_unlock();	return ret;}static DEVICE_ATTR_RO(duplex); static ssize_t testing_show(struct device *dev,			    struct device_attribute *attr, char *buf){	struct net_device *netdev = to_net_dev(dev); 	if (netif_running(netdev))		return sprintf(buf, fmt_dec, !!netif_testing(netdev)); 	return -EINVAL;}static DEVICE_ATTR_RO(testing); static ssize_t dormant_show(struct device *dev,			    struct device_attribute *attr, char *buf){	struct net_device *netdev = to_net_dev(dev); 	if (netif_running(netdev))		return sprintf(buf, fmt_dec, !!netif_dormant(netdev)); 	return -EINVAL;}static DEVICE_ATTR_RO(dormant); static const char *const operstates[] = {	"unknown",	"notpresent", /* currently unused */	"down",	"lowerlayerdown",	"testing",	"dormant",	"up"}; static ssize_t operstate_show(struct device *dev,			      struct device_attribute *attr, char *buf){	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); 	if (operstate >= ARRAY_SIZE(operstates))		return -EINVAL; /* should not happen */ 	return sprintf(buf, "%s\n", operstates[operstate]);}static DEVICE_ATTR_RO(operstate); static ssize_t carrier_changes_show(struct device *dev,				    struct device_attribute *attr,				    char *buf){	struct net_device *netdev = to_net_dev(dev); 	return sprintf(buf, fmt_dec,		       atomic_read(&netdev->carrier_up_count) +		       atomic_read(&netdev->carrier_down_count));}static DEVICE_ATTR_RO(carrier_changes); static ssize_t carrier_up_count_show(struct device *dev,				     struct device_attribute *attr,				     char *buf){	struct net_device *netdev = to_net_dev(dev); 	return sprintf(buf, fmt_dec, atomic_read(&netdev->carrier_up_count));}static DEVICE_ATTR_RO(carrier_up_count); static ssize_t carrier_down_count_show(struct device *dev,				       struct device_attribute *attr,				       char *buf){	struct net_device *netdev = to_net_dev(dev); 	return sprintf(buf, fmt_dec, atomic_read(&netdev->carrier_down_count));}static DEVICE_ATTR_RO(carrier_down_count); /* read-write attributes */ static int change_mtu(struct net_device *dev, unsigned long new_mtu){	return dev_set_mtu(dev, (int)new_mtu);} static ssize_t mtu_store(struct device *dev, struct device_attribute *attr,			 const char *buf, size_t len){	return netdev_store(dev, attr, buf, len, change_mtu);}NETDEVICE_SHOW_RW(mtu, fmt_dec); static int change_flags(struct net_device *dev, unsigned long new_flags){	return dev_change_flags(dev, (unsigned int)new_flags, NULL);} static ssize_t flags_store(struct device *dev, struct device_attribute *attr,			   const char *buf, size_t len){	return netdev_store(dev, attr, buf, len, change_flags);}NETDEVICE_SHOW_RW(flags, fmt_hex); static ssize_t tx_queue_len_store(struct device *dev,				  struct device_attribute *attr,				  const char *buf, size_t len){	if (!capable(CAP_NET_ADMIN))		return -EPERM; 	return netdev_store(dev, attr, buf, len, dev_change_tx_queue_len);}NETDEVICE_SHOW_RW(tx_queue_len, fmt_dec); static int change_gro_flush_timeout(struct net_device *dev, unsigned long val){	WRITE_ONCE(dev->gro_flush_timeout, val);	return 0;} static ssize_t gro_flush_timeout_store(struct device *dev,				       struct device_attribute *attr,				       const char *buf, size_t len){	if (!capable(CAP_NET_ADMIN))		return -EPERM; 	return netdev_store(dev, attr, buf, len, change_gro_flush_timeout);}NETDEVICE_SHOW_RW(gro_flush_timeout, fmt_ulong); static int change_napi_defer_hard_irqs(struct net_device *dev, unsigned long val){	WRITE_ONCE(dev->napi_defer_hard_irqs, val);	return 0;} static ssize_t napi_defer_hard_irqs_store(struct device *dev,					  struct device_attribute *attr,					  const char *buf, size_t len){	if (!capable(CAP_NET_ADMIN))		return -EPERM; 	return netdev_store(dev, attr, buf, len, change_napi_defer_hard_irqs);}NETDEVICE_SHOW_RW(napi_defer_hard_irqs, fmt_dec); static ssize_t ifalias_store(struct device *dev, struct device_attribute *attr,			     const char *buf, size_t len){	struct net_device *netdev = to_net_dev(dev);	struct net *net = dev_net(netdev);	size_t count = len;	ssize_t ret = 0; 	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))		return -EPERM; 	/* ignore trailing newline */	if (len >  0 && buf[len - 1] == '\n')		--count; 	if (!rtnl_trylock())		return restart_syscall(); 	if (dev_isalive(netdev)) {		ret = dev_set_alias(netdev, buf, count);		if (ret < 0)			goto err;		ret = len;		netdev_state_change(netdev);	}err:	rtnl_unlock(); 	return ret;} static ssize_t ifalias_show(struct device *dev,			    struct device_attribute *attr, char *buf){	const struct net_device *netdev = to_net_dev(dev);	char tmp[IFALIASZ];	ssize_t ret = 0; 	ret = dev_get_alias(netdev, tmp, sizeof(tmp));	if (ret > 0)		ret = sprintf(buf, "%s\n", tmp);	return ret;}static DEVICE_ATTR_RW(ifalias); static int change_group(struct net_device *dev, unsigned long new_group){	dev_set_group(dev, (int)new_group);	return 0;} static ssize_t group_store(struct device *dev, struct device_attribute *attr,			   const char *buf, size_t len){	return netdev_store(dev, attr, buf, len, change_group);}NETDEVICE_SHOW(group, fmt_dec);static DEVICE_ATTR(netdev_group, 0644, group_show, group_store); static int change_proto_down(struct net_device *dev, unsigned long proto_down){	return dev_change_proto_down(dev, (bool)proto_down);} static ssize_t proto_down_store(struct device *dev,				struct device_attribute *attr,				const char *buf, size_t len){	return netdev_store(dev, attr, buf, len, change_proto_down);}NETDEVICE_SHOW_RW(proto_down, fmt_dec); static ssize_t phys_port_id_show(struct device *dev,				 struct device_attribute *attr, char *buf){	struct net_device *netdev = to_net_dev(dev);	ssize_t ret = -EINVAL; 	if (!rtnl_trylock())		return restart_syscall(); 	if (dev_isalive(netdev)) {		struct netdev_phys_item_id ppid; 		ret = dev_get_phys_port_id(netdev, &ppid);		if (!ret)			ret = sprintf(buf, "%*phN\n", ppid.id_len, ppid.id);	}	rtnl_unlock(); 	return ret;}static DEVICE_ATTR_RO(phys_port_id); static ssize_t phys_port_name_show(struct device *dev,				   struct device_attribute *attr, char *buf){	struct net_device *netdev = to_net_dev(dev);

Lines 1–500 of 1962.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t