Newer
Older
static int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list,
int addr_len,
{
int err;
struct netdev_hw_addr *ha, *ha2;
unsigned char type;
list_for_each_entry(ha, &from_list->list, list) {
type = addr_type ? addr_type : ha->type;
err = __hw_addr_add(to_list, ha->addr, addr_len, type);
if (err)
goto unroll;
}
return 0;
unroll:
list_for_each_entry(ha2, &from_list->list, list) {
if (ha2 == ha)
break;
type = addr_type ? addr_type : ha2->type;
__hw_addr_del(to_list, ha2->addr, addr_len, type);
}
return err;
}
static void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list,
int addr_len,
{
struct netdev_hw_addr *ha;
unsigned char type;
list_for_each_entry(ha, &from_list->list, list) {
type = addr_type ? addr_type : ha->type;
__hw_addr_del(to_list, ha->addr, addr_len, addr_type);
static int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list,
int addr_len)
{
int err = 0;
struct netdev_hw_addr *ha, *tmp;
list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
err = __hw_addr_add(to_list, ha->addr,
addr_len, ha->type);
if (err)
break;
ha->synced = true;
ha->refcount++;
} else if (ha->refcount == 1) {
__hw_addr_del(to_list, ha->addr, addr_len, ha->type);
__hw_addr_del(from_list, ha->addr, addr_len, ha->type);
static void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list,
int addr_len)
{
struct netdev_hw_addr *ha, *tmp;
list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
addr_len, ha->type);
ha->synced = false;
addr_len, ha->type);
}
}
}
static void __hw_addr_flush(struct netdev_hw_addr_list *list)
{
struct netdev_hw_addr *ha, *tmp;
list_for_each_entry_safe(ha, tmp, &list->list, list) {
list_del_rcu(&ha->list);
call_rcu(&ha->rcu_head, ha_rcu_free);
}
list->count = 0;
}
static void __hw_addr_init(struct netdev_hw_addr_list *list)
{
INIT_LIST_HEAD(&list->list);
list->count = 0;
}
/* Device addresses handling functions */
static void dev_addr_flush(struct net_device *dev)
{
/* rtnl_mutex must be held here */
dev->dev_addr = NULL;
}
static int dev_addr_init(struct net_device *dev)
{
unsigned char addr[MAX_ADDR_LEN];
struct netdev_hw_addr *ha;
int err;
/* rtnl_mutex must be held here */
err = __hw_addr_add(&dev->dev_addrs, addr, sizeof(addr),
NETDEV_HW_ADDR_T_LAN);
if (!err) {
/*
* Get the first (previously created) address from the list
* and set dev_addr pointer to this location.
*/
ha = list_first_entry(&dev->dev_addrs.list,
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
struct netdev_hw_addr, list);
dev->dev_addr = ha->addr;
}
return err;
}
/**
* dev_addr_add - Add a device address
* @dev: device
* @addr: address to add
* @addr_type: address type
*
* Add a device address to the device or increase the reference count if
* it already exists.
*
* The caller must hold the rtnl_mutex.
*/
int dev_addr_add(struct net_device *dev, unsigned char *addr,
unsigned char addr_type)
{
int err;
ASSERT_RTNL();
err = __hw_addr_add(&dev->dev_addrs, addr, dev->addr_len, addr_type);
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
if (!err)
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
return err;
}
EXPORT_SYMBOL(dev_addr_add);
/**
* dev_addr_del - Release a device address.
* @dev: device
* @addr: address to delete
* @addr_type: address type
*
* Release reference to a device address and remove it from the device
* if the reference count drops to zero.
*
* The caller must hold the rtnl_mutex.
*/
int dev_addr_del(struct net_device *dev, unsigned char *addr,
unsigned char addr_type)
{
int err;
ASSERT_RTNL();
/*
* We can not remove the first address from the list because
* dev->dev_addr points to that.
*/
ha = list_first_entry(&dev->dev_addrs.list,
struct netdev_hw_addr, list);
if (ha->addr == dev->dev_addr && ha->refcount == 1)
return -ENOENT;
err = __hw_addr_del(&dev->dev_addrs, addr, dev->addr_len,
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
if (!err)
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
return err;
}
EXPORT_SYMBOL(dev_addr_del);
/**
* dev_addr_add_multiple - Add device addresses from another device
* @to_dev: device to which addresses will be added
* @from_dev: device from which addresses will be added
* @addr_type: address type - 0 means type will be used from from_dev
*
* Add device addresses of the one device to another.
**
* The caller must hold the rtnl_mutex.
*/
int dev_addr_add_multiple(struct net_device *to_dev,
struct net_device *from_dev,
unsigned char addr_type)
{
int err;
ASSERT_RTNL();
if (from_dev->addr_len != to_dev->addr_len)
return -EINVAL;
err = __hw_addr_add_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs,
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
if (!err)
call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
return err;
}
EXPORT_SYMBOL(dev_addr_add_multiple);
/**
* dev_addr_del_multiple - Delete device addresses by another device
* @to_dev: device where the addresses will be deleted
* @from_dev: device by which addresses the addresses will be deleted
* @addr_type: address type - 0 means type will used from from_dev
*
* Deletes addresses in to device by the list of addresses in from device.
*
* The caller must hold the rtnl_mutex.
*/
int dev_addr_del_multiple(struct net_device *to_dev,
struct net_device *from_dev,
unsigned char addr_type)
{
ASSERT_RTNL();
if (from_dev->addr_len != to_dev->addr_len)
return -EINVAL;
__hw_addr_del_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs,
call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
return 0;
}
EXPORT_SYMBOL(dev_addr_del_multiple);
/* multicast addresses handling functions */
int __dev_addr_delete(struct dev_addr_list **list, int *count,
void *addr, int alen, int glbl)
{
struct dev_addr_list *da;
for (; (da = *list) != NULL; list = &da->next) {
if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
alen == da->da_addrlen) {
if (glbl) {
int old_glbl = da->da_gusers;
da->da_gusers = 0;
if (old_glbl == 0)
break;
}
if (--da->da_users)
return 0;
*list = da->next;
kfree(da);
(*count)--;
return 0;
}
}
return -ENOENT;
}
int __dev_addr_add(struct dev_addr_list **list, int *count,
void *addr, int alen, int glbl)
{
struct dev_addr_list *da;
for (da = *list; da != NULL; da = da->next) {
if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
da->da_addrlen == alen) {
if (glbl) {
int old_glbl = da->da_gusers;
da->da_gusers = 1;
if (old_glbl)
return 0;
}
da->da_users++;
return 0;
}
}
da = kzalloc(sizeof(*da), GFP_ATOMIC);
if (da == NULL)
return -ENOMEM;
memcpy(da->da_addr, addr, alen);
da->da_addrlen = alen;
da->da_users = 1;
da->da_gusers = glbl ? 1 : 0;
da->next = *list;
*list = da;
(*count)++;
return 0;
}
/**
* dev_unicast_delete - Release secondary unicast address.
* @dev: device
*
* Release reference to a secondary unicast address and remove it
* from the device if the reference count drops to zero.
*
* The caller must hold the rtnl_mutex.
*/
int dev_unicast_delete(struct net_device *dev, void *addr)
{
int err;
ASSERT_RTNL();
netif_addr_lock_bh(dev);
err = __hw_addr_del(&dev->uc, addr, dev->addr_len,
NETDEV_HW_ADDR_T_UNICAST);
if (!err)
__dev_set_rx_mode(dev);
netif_addr_unlock_bh(dev);
return err;
}
EXPORT_SYMBOL(dev_unicast_delete);
/**
* dev_unicast_add - add a secondary unicast address
* @dev: device
*
* Add a secondary unicast address to the device or increase
* the reference count if it already exists.
*
* The caller must hold the rtnl_mutex.
*/
int dev_unicast_add(struct net_device *dev, void *addr)
{
int err;
ASSERT_RTNL();
netif_addr_lock_bh(dev);
err = __hw_addr_add(&dev->uc, addr, dev->addr_len,
NETDEV_HW_ADDR_T_UNICAST);
if (!err)
__dev_set_rx_mode(dev);
netif_addr_unlock_bh(dev);
return err;
}
EXPORT_SYMBOL(dev_unicast_add);
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
int __dev_addr_sync(struct dev_addr_list **to, int *to_count,
struct dev_addr_list **from, int *from_count)
{
struct dev_addr_list *da, *next;
int err = 0;
da = *from;
while (da != NULL) {
next = da->next;
if (!da->da_synced) {
err = __dev_addr_add(to, to_count,
da->da_addr, da->da_addrlen, 0);
if (err < 0)
break;
da->da_synced = 1;
da->da_users++;
} else if (da->da_users == 1) {
__dev_addr_delete(to, to_count,
da->da_addr, da->da_addrlen, 0);
__dev_addr_delete(from, from_count,
da->da_addr, da->da_addrlen, 0);
}
da = next;
}
return err;
}
EXPORT_SYMBOL_GPL(__dev_addr_sync);
void __dev_addr_unsync(struct dev_addr_list **to, int *to_count,
struct dev_addr_list **from, int *from_count)
{
struct dev_addr_list *da, *next;
da = *from;
while (da != NULL) {
next = da->next;
if (da->da_synced) {
__dev_addr_delete(to, to_count,
da->da_addr, da->da_addrlen, 0);
da->da_synced = 0;
__dev_addr_delete(from, from_count,
da->da_addr, da->da_addrlen, 0);
}
da = next;
}
}
EXPORT_SYMBOL_GPL(__dev_addr_unsync);
/**
* dev_unicast_sync - Synchronize device's unicast list to another device
* @to: destination device
* @from: source device
*
* Add newly added addresses to the destination device and release
* addresses that have no users left. The source device must be
* locked by netif_tx_lock_bh.
*
* This function is intended to be called from the dev->set_rx_mode
* function of layered software devices.
*/
int dev_unicast_sync(struct net_device *to, struct net_device *from)
{
int err = 0;
if (to->addr_len != from->addr_len)
return -EINVAL;
netif_addr_lock_bh(to);
err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len);
if (!err)
__dev_set_rx_mode(to);
netif_addr_unlock_bh(to);
return err;
}
EXPORT_SYMBOL(dev_unicast_sync);
/**
* dev_unicast_unsync - Remove synchronized addresses from the destination device
* @to: destination device
* @from: source device
*
* Remove all addresses that were added to the destination device by
* dev_unicast_sync(). This function is intended to be called from the
* dev->stop function of layered software devices.
*/
void dev_unicast_unsync(struct net_device *to, struct net_device *from)
{
if (to->addr_len != from->addr_len)
return;
netif_addr_lock_bh(from);
netif_addr_lock(to);
__hw_addr_unsync(&to->uc, &from->uc, to->addr_len);
netif_addr_unlock(to);
netif_addr_unlock_bh(from);
}
EXPORT_SYMBOL(dev_unicast_unsync);
static void dev_unicast_flush(struct net_device *dev)
{
netif_addr_lock_bh(dev);
netif_addr_unlock_bh(dev);
}
static void dev_unicast_init(struct net_device *dev)
{
static void __dev_addr_discard(struct dev_addr_list **list)
{
struct dev_addr_list *tmp;
while (*list != NULL) {
tmp = *list;
*list = tmp->next;
if (tmp->da_users > tmp->da_gusers)
printk("__dev_addr_discard: address leakage! "
"da_users=%d\n", tmp->da_users);
kfree(tmp);
}
}
static void dev_addr_discard(struct net_device *dev)
netif_addr_lock_bh(dev);
__dev_addr_discard(&dev->mc_list);
netdev_mc_count(dev) = 0;
netif_addr_unlock_bh(dev);
/**
* dev_get_flags - get flags reported to userspace
* @dev: device
*
* Get the combination of flag bits exported through APIs to userspace.
*/
unsigned dev_get_flags(const struct net_device *dev)
{
unsigned flags;
flags = (dev->flags & ~(IFF_PROMISC |
IFF_ALLMULTI |
IFF_RUNNING |
IFF_LOWER_UP |
IFF_DORMANT)) |
(dev->gflags & (IFF_PROMISC |
IFF_ALLMULTI));
if (netif_running(dev)) {
if (netif_oper_up(dev))
flags |= IFF_RUNNING;
if (netif_carrier_ok(dev))
flags |= IFF_LOWER_UP;
if (netif_dormant(dev))
flags |= IFF_DORMANT;
}
int __dev_change_flags(struct net_device *dev, unsigned int flags)
/*
* Set the flags on our device.
*/
dev->flags = (flags & (IFF_DEBUG | IFF_NOTRAILERS | IFF_NOARP |
IFF_DYNAMIC | IFF_MULTICAST | IFF_PORTSEL |
IFF_AUTOMEDIA)) |
(dev->flags & (IFF_UP | IFF_VOLATILE | IFF_PROMISC |
IFF_ALLMULTI));
/*
* Load in the correct multicast list now the flags have changed.
*/
if ((old_flags ^ flags) & IFF_MULTICAST)
dev_change_rx_flags(dev, IFF_MULTICAST);
/*
* Have we downed the interface. We handle IFF_UP ourselves
* according to user attempts to set it, rather than blindly
* setting it.
*/
ret = 0;
if ((old_flags ^ flags) & IFF_UP) { /* Bit is different ? */
ret = ((old_flags & IFF_UP) ? __dev_close : __dev_open)(dev);
}
if ((flags ^ dev->gflags) & IFF_PROMISC) {
int inc = (flags & IFF_PROMISC) ? 1 : -1;
dev->gflags ^= IFF_PROMISC;
dev_set_promiscuity(dev, inc);
}
/* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI
is important. Some (broken) drivers set IFF_PROMISC, when
IFF_ALLMULTI is requested not asking us and not reporting.
*/
if ((flags ^ dev->gflags) & IFF_ALLMULTI) {
int inc = (flags & IFF_ALLMULTI) ? 1 : -1;
dev->gflags ^= IFF_ALLMULTI;
dev_set_allmulti(dev, inc);
}
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
return ret;
}
void __dev_notify_flags(struct net_device *dev, unsigned int old_flags)
{
unsigned int changes = dev->flags ^ old_flags;
if (changes & IFF_UP) {
if (dev->flags & IFF_UP)
call_netdevice_notifiers(NETDEV_UP, dev);
else
call_netdevice_notifiers(NETDEV_DOWN, dev);
}
if (dev->flags & IFF_UP &&
(changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE)))
call_netdevice_notifiers(NETDEV_CHANGE, dev);
}
/**
* dev_change_flags - change device settings
* @dev: device
* @flags: device state flags
*
* Change settings on device based state flags. The flags are
* in the userspace exported format.
*/
int dev_change_flags(struct net_device *dev, unsigned flags)
{
int ret, changes;
int old_flags = dev->flags;
ret = __dev_change_flags(dev, flags);
if (ret < 0)
return ret;
changes = old_flags ^ dev->flags;
if (changes)
rtmsg_ifinfo(RTM_NEWLINK, dev, changes);
__dev_notify_flags(dev, old_flags);
/**
* dev_set_mtu - Change maximum transfer unit
* @dev: device
* @new_mtu: new transfer unit
*
* Change the maximum transfer size of the network device.
*/
int dev_set_mtu(struct net_device *dev, int new_mtu)
{
const struct net_device_ops *ops = dev->netdev_ops;
int err;
if (new_mtu == dev->mtu)
return 0;
/* MTU must be positive. */
if (new_mtu < 0)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
err = 0;
if (ops->ndo_change_mtu)
err = ops->ndo_change_mtu(dev, new_mtu);
call_netdevice_notifiers(NETDEV_CHANGEMTU, dev);
/**
* dev_set_mac_address - Change Media Access Control Address
* @dev: device
* @sa: new address
*
* Change the hardware (MAC) address of the device
*/
int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
{
const struct net_device_ops *ops = dev->netdev_ops;
if (!ops->ndo_set_mac_address)
return -EOPNOTSUPP;
if (sa->sa_family != dev->type)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
err = ops->ndo_set_mac_address(dev, sa);
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
* Perform the SIOCxIFxxx calls, inside rcu_read_lock()
static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cmd)
struct net_device *dev = dev_get_by_name_rcu(net, ifr->ifr_name);
if (!dev)
return -ENODEV;
switch (cmd) {
case SIOCGIFFLAGS: /* Get interface flags */
ifr->ifr_flags = (short) dev_get_flags(dev);
return 0;
case SIOCGIFMETRIC: /* Get the metric on the interface
(currently unused) */
ifr->ifr_metric = 0;
return 0;
case SIOCGIFMTU: /* Get the MTU of a device */
ifr->ifr_mtu = dev->mtu;
return 0;
case SIOCGIFHWADDR:
if (!dev->addr_len)
memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data);
else
memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr,
min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len));
ifr->ifr_hwaddr.sa_family = dev->type;
return 0;
case SIOCGIFSLAVE:
err = -EINVAL;
break;
case SIOCGIFMAP:
ifr->ifr_map.mem_start = dev->mem_start;
ifr->ifr_map.mem_end = dev->mem_end;
ifr->ifr_map.base_addr = dev->base_addr;
ifr->ifr_map.irq = dev->irq;
ifr->ifr_map.dma = dev->dma;
ifr->ifr_map.port = dev->if_port;
return 0;
case SIOCGIFINDEX:
ifr->ifr_ifindex = dev->ifindex;
return 0;
case SIOCGIFTXQLEN:
ifr->ifr_qlen = dev->tx_queue_len;
return 0;
default:
/* dev_ioctl() should ensure this case
* is never reached
*/
WARN_ON(1);
err = -EINVAL;
break;
}
return err;
}
/*
* Perform the SIOCxIFxxx calls, inside rtnl_lock()
*/
static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
{
int err;
struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
if (!dev)
return -ENODEV;
case SIOCSIFFLAGS: /* Set interface flags */
return dev_change_flags(dev, ifr->ifr_flags);
case SIOCSIFMETRIC: /* Set the metric on the interface
(currently unused) */
return -EOPNOTSUPP;
case SIOCSIFMTU: /* Set the MTU of a device */
return dev_set_mtu(dev, ifr->ifr_mtu);
case SIOCSIFHWADDR:
return dev_set_mac_address(dev, &ifr->ifr_hwaddr);
case SIOCSIFHWBROADCAST:
if (ifr->ifr_hwaddr.sa_family != dev->type)
return -EINVAL;
memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data,
min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len));
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
return 0;
case SIOCSIFMAP:
if (ops->ndo_set_config) {
return ops->ndo_set_config(dev, &ifr->ifr_map);
}
return -EOPNOTSUPP;
case SIOCADDMULTI:
if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) ||
ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
return dev_mc_add(dev, ifr->ifr_hwaddr.sa_data,
dev->addr_len, 1);
case SIOCDELMULTI:
if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) ||
ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
return dev_mc_delete(dev, ifr->ifr_hwaddr.sa_data,
dev->addr_len, 1);
case SIOCSIFTXQLEN:
if (ifr->ifr_qlen < 0)
return -EINVAL;
dev->tx_queue_len = ifr->ifr_qlen;
return 0;
case SIOCSIFNAME:
ifr->ifr_newname[IFNAMSIZ-1] = '\0';
return dev_change_name(dev, ifr->ifr_newname);
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
/*
* Unknown or private ioctl
*/
default:
if ((cmd >= SIOCDEVPRIVATE &&
cmd <= SIOCDEVPRIVATE + 15) ||
cmd == SIOCBONDENSLAVE ||
cmd == SIOCBONDRELEASE ||
cmd == SIOCBONDSETHWADDR ||
cmd == SIOCBONDSLAVEINFOQUERY ||
cmd == SIOCBONDINFOQUERY ||
cmd == SIOCBONDCHANGEACTIVE ||
cmd == SIOCGMIIPHY ||
cmd == SIOCGMIIREG ||
cmd == SIOCSMIIREG ||
cmd == SIOCBRADDIF ||
cmd == SIOCBRDELIF ||
cmd == SIOCSHWTSTAMP ||
cmd == SIOCWANDEV) {
err = -EOPNOTSUPP;
if (ops->ndo_do_ioctl) {
if (netif_device_present(dev))
err = ops->ndo_do_ioctl(dev, ifr, cmd);
else
err = -ENODEV;
}
} else
err = -EINVAL;
}
return err;
}
/*
* This function handles all "interface"-type I/O control requests. The actual
* 'doing' part of this is dev_ifsioc above.
*/
/**
* dev_ioctl - network device ioctl
* @net: the applicable net namespace
* @cmd: command to issue
* @arg: pointer to a struct ifreq in user space
*
* Issue ioctl functions to devices. This is normally called by the
* user space syscall interfaces but can sometimes be useful for
* other purposes. The return value is the return from the syscall if
* positive or a negative errno code on error.
*/
int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
{
struct ifreq ifr;
int ret;
char *colon;
/* One special case: SIOCGIFCONF takes ifconf argument
and requires shared lock, because it sleeps writing
to user space.
*/
if (cmd == SIOCGIFCONF) {
ret = dev_ifconf(net, (char __user *) arg);
return dev_ifname(net, (struct ifreq __user *)arg);
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
return -EFAULT;
ifr.ifr_name[IFNAMSIZ-1] = 0;
colon = strchr(ifr.ifr_name, ':');
if (colon)
*colon = 0;
/*
* See which interface the caller is talking about.
*/
switch (cmd) {
/*
* These ioctl calls:
* - can be done by all.
* - atomic and do not require locking.
* - return a value
*/
case SIOCGIFFLAGS:
case SIOCGIFMETRIC:
case SIOCGIFMTU:
case SIOCGIFHWADDR:
case SIOCGIFSLAVE:
case SIOCGIFMAP:
case SIOCGIFINDEX:
case SIOCGIFTXQLEN:
dev_load(net, ifr.ifr_name);
if (!ret) {
if (colon)
*colon = ':';
if (copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
}
return ret;
case SIOCETHTOOL:
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ethtool(net, &ifr);
rtnl_unlock();
if (!ret) {
if (colon)
*colon = ':';
if (copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
}
return ret;
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
/*
* These ioctl calls:
* - require superuser power.
* - require strict serialization.
* - return a value
*/
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSIFNAME:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ifsioc(net, &ifr, cmd);
rtnl_unlock();
if (!ret) {
if (colon)
*colon = ':';
if (copy_to_user(arg, &ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
}
return ret;
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
/*
* These ioctl calls:
* - require superuser power.
* - require strict serialization.
* - do not return a value
*/
case SIOCSIFFLAGS:
case SIOCSIFMETRIC:
case SIOCSIFMTU:
case SIOCSIFMAP:
case SIOCSIFHWADDR:
case SIOCSIFSLAVE:
case SIOCADDMULTI:
case SIOCDELMULTI:
case SIOCSIFHWBROADCAST:
case SIOCSIFTXQLEN:
case SIOCSMIIREG:
case SIOCBONDENSLAVE:
case SIOCBONDRELEASE:
case SIOCBONDSETHWADDR:
case SIOCBONDCHANGEACTIVE:
case SIOCBRADDIF:
case SIOCBRDELIF: