diff options
author | Cong Wang <xiyou.wangcong@gmail.com> | 2013-02-15 17:20:46 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-02-18 12:27:32 -0500 |
commit | 96b45cbd956ce83908378d87d009b05645353f22 (patch) | |
tree | c8decd7182281de10bd86a5d299afd37a1a16864 /net/core | |
parent | 31d17536631f79344c24fc44b50a0a241d0a701e (diff) |
net: move ioctl functions into a separated file
They well deserve a separated unit.
Cc: "David S. Miller" <davem@davemloft.net>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/Makefile | 2 | ||||
-rw-r--r-- | net/core/dev.c | 573 | ||||
-rw-r--r-- | net/core/dev_ioctl.c | 576 |
3 files changed, 577 insertions, 574 deletions
diff --git a/net/core/Makefile b/net/core/Makefile index 674641b13aea..0c5e3618c80b 100644 --- a/net/core/Makefile +++ b/net/core/Makefile | |||
@@ -9,7 +9,7 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o | |||
9 | 9 | ||
10 | obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \ | 10 | obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \ |
11 | neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ | 11 | neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ |
12 | sock_diag.o | 12 | sock_diag.o dev_ioctl.o |
13 | 13 | ||
14 | obj-$(CONFIG_XFRM) += flow.o | 14 | obj-$(CONFIG_XFRM) += flow.o |
15 | obj-y += net-sysfs.o | 15 | obj-y += net-sysfs.o |
diff --git a/net/core/dev.c b/net/core/dev.c index 6ad37896a324..1932d351ed7c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -106,7 +106,6 @@ | |||
106 | #include <net/xfrm.h> | 106 | #include <net/xfrm.h> |
107 | #include <linux/highmem.h> | 107 | #include <linux/highmem.h> |
108 | #include <linux/init.h> | 108 | #include <linux/init.h> |
109 | #include <linux/kmod.h> | ||
110 | #include <linux/module.h> | 109 | #include <linux/module.h> |
111 | #include <linux/netpoll.h> | 110 | #include <linux/netpoll.h> |
112 | #include <linux/rcupdate.h> | 111 | #include <linux/rcupdate.h> |
@@ -132,7 +131,6 @@ | |||
132 | #include <linux/pci.h> | 131 | #include <linux/pci.h> |
133 | #include <linux/inetdevice.h> | 132 | #include <linux/inetdevice.h> |
134 | #include <linux/cpu_rmap.h> | 133 | #include <linux/cpu_rmap.h> |
135 | #include <linux/net_tstamp.h> | ||
136 | #include <linux/static_key.h> | 134 | #include <linux/static_key.h> |
137 | 135 | ||
138 | #include "net-sysfs.h" | 136 | #include "net-sysfs.h" |
@@ -1226,36 +1224,6 @@ void netdev_notify_peers(struct net_device *dev) | |||
1226 | } | 1224 | } |
1227 | EXPORT_SYMBOL(netdev_notify_peers); | 1225 | EXPORT_SYMBOL(netdev_notify_peers); |
1228 | 1226 | ||
1229 | /** | ||
1230 | * dev_load - load a network module | ||
1231 | * @net: the applicable net namespace | ||
1232 | * @name: name of interface | ||
1233 | * | ||
1234 | * If a network interface is not present and the process has suitable | ||
1235 | * privileges this function loads the module. If module loading is not | ||
1236 | * available in this kernel then it becomes a nop. | ||
1237 | */ | ||
1238 | |||
1239 | void dev_load(struct net *net, const char *name) | ||
1240 | { | ||
1241 | struct net_device *dev; | ||
1242 | int no_module; | ||
1243 | |||
1244 | rcu_read_lock(); | ||
1245 | dev = dev_get_by_name_rcu(net, name); | ||
1246 | rcu_read_unlock(); | ||
1247 | |||
1248 | no_module = !dev; | ||
1249 | if (no_module && capable(CAP_NET_ADMIN)) | ||
1250 | no_module = request_module("netdev-%s", name); | ||
1251 | if (no_module && capable(CAP_SYS_MODULE)) { | ||
1252 | if (!request_module("%s", name)) | ||
1253 | pr_warn("Loading kernel module for a network device with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev-%s instead.\n", | ||
1254 | name); | ||
1255 | } | ||
1256 | } | ||
1257 | EXPORT_SYMBOL(dev_load); | ||
1258 | |||
1259 | static int __dev_open(struct net_device *dev) | 1227 | static int __dev_open(struct net_device *dev) |
1260 | { | 1228 | { |
1261 | const struct net_device_ops *ops = dev->netdev_ops; | 1229 | const struct net_device_ops *ops = dev->netdev_ops; |
@@ -1645,57 +1613,6 @@ static inline void net_timestamp_set(struct sk_buff *skb) | |||
1645 | __net_timestamp(SKB); \ | 1613 | __net_timestamp(SKB); \ |
1646 | } \ | 1614 | } \ |
1647 | 1615 | ||
1648 | static int net_hwtstamp_validate(struct ifreq *ifr) | ||
1649 | { | ||
1650 | struct hwtstamp_config cfg; | ||
1651 | enum hwtstamp_tx_types tx_type; | ||
1652 | enum hwtstamp_rx_filters rx_filter; | ||
1653 | int tx_type_valid = 0; | ||
1654 | int rx_filter_valid = 0; | ||
1655 | |||
1656 | if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) | ||
1657 | return -EFAULT; | ||
1658 | |||
1659 | if (cfg.flags) /* reserved for future extensions */ | ||
1660 | return -EINVAL; | ||
1661 | |||
1662 | tx_type = cfg.tx_type; | ||
1663 | rx_filter = cfg.rx_filter; | ||
1664 | |||
1665 | switch (tx_type) { | ||
1666 | case HWTSTAMP_TX_OFF: | ||
1667 | case HWTSTAMP_TX_ON: | ||
1668 | case HWTSTAMP_TX_ONESTEP_SYNC: | ||
1669 | tx_type_valid = 1; | ||
1670 | break; | ||
1671 | } | ||
1672 | |||
1673 | switch (rx_filter) { | ||
1674 | case HWTSTAMP_FILTER_NONE: | ||
1675 | case HWTSTAMP_FILTER_ALL: | ||
1676 | case HWTSTAMP_FILTER_SOME: | ||
1677 | case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: | ||
1678 | case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: | ||
1679 | case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: | ||
1680 | case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: | ||
1681 | case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: | ||
1682 | case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: | ||
1683 | case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: | ||
1684 | case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: | ||
1685 | case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: | ||
1686 | case HWTSTAMP_FILTER_PTP_V2_EVENT: | ||
1687 | case HWTSTAMP_FILTER_PTP_V2_SYNC: | ||
1688 | case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: | ||
1689 | rx_filter_valid = 1; | ||
1690 | break; | ||
1691 | } | ||
1692 | |||
1693 | if (!tx_type_valid || !rx_filter_valid) | ||
1694 | return -ERANGE; | ||
1695 | |||
1696 | return 0; | ||
1697 | } | ||
1698 | |||
1699 | static inline bool is_skb_forwardable(struct net_device *dev, | 1616 | static inline bool is_skb_forwardable(struct net_device *dev, |
1700 | struct sk_buff *skb) | 1617 | struct sk_buff *skb) |
1701 | { | 1618 | { |
@@ -4300,127 +4217,6 @@ softnet_break: | |||
4300 | goto out; | 4217 | goto out; |
4301 | } | 4218 | } |
4302 | 4219 | ||
4303 | static gifconf_func_t *gifconf_list[NPROTO]; | ||
4304 | |||
4305 | /** | ||
4306 | * register_gifconf - register a SIOCGIF handler | ||
4307 | * @family: Address family | ||
4308 | * @gifconf: Function handler | ||
4309 | * | ||
4310 | * Register protocol dependent address dumping routines. The handler | ||
4311 | * that is passed must not be freed or reused until it has been replaced | ||
4312 | * by another handler. | ||
4313 | */ | ||
4314 | int register_gifconf(unsigned int family, gifconf_func_t *gifconf) | ||
4315 | { | ||
4316 | if (family >= NPROTO) | ||
4317 | return -EINVAL; | ||
4318 | gifconf_list[family] = gifconf; | ||
4319 | return 0; | ||
4320 | } | ||
4321 | EXPORT_SYMBOL(register_gifconf); | ||
4322 | |||
4323 | |||
4324 | /* | ||
4325 | * Map an interface index to its name (SIOCGIFNAME) | ||
4326 | */ | ||
4327 | |||
4328 | /* | ||
4329 | * We need this ioctl for efficient implementation of the | ||
4330 | * if_indextoname() function required by the IPv6 API. Without | ||
4331 | * it, we would have to search all the interfaces to find a | ||
4332 | * match. --pb | ||
4333 | */ | ||
4334 | |||
4335 | static int dev_ifname(struct net *net, struct ifreq __user *arg) | ||
4336 | { | ||
4337 | struct net_device *dev; | ||
4338 | struct ifreq ifr; | ||
4339 | unsigned seq; | ||
4340 | |||
4341 | /* | ||
4342 | * Fetch the caller's info block. | ||
4343 | */ | ||
4344 | |||
4345 | if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) | ||
4346 | return -EFAULT; | ||
4347 | |||
4348 | retry: | ||
4349 | seq = read_seqcount_begin(&devnet_rename_seq); | ||
4350 | rcu_read_lock(); | ||
4351 | dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex); | ||
4352 | if (!dev) { | ||
4353 | rcu_read_unlock(); | ||
4354 | return -ENODEV; | ||
4355 | } | ||
4356 | |||
4357 | strcpy(ifr.ifr_name, dev->name); | ||
4358 | rcu_read_unlock(); | ||
4359 | if (read_seqcount_retry(&devnet_rename_seq, seq)) | ||
4360 | goto retry; | ||
4361 | |||
4362 | if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) | ||
4363 | return -EFAULT; | ||
4364 | return 0; | ||
4365 | } | ||
4366 | |||
4367 | /* | ||
4368 | * Perform a SIOCGIFCONF call. This structure will change | ||
4369 | * size eventually, and there is nothing I can do about it. | ||
4370 | * Thus we will need a 'compatibility mode'. | ||
4371 | */ | ||
4372 | |||
4373 | static int dev_ifconf(struct net *net, char __user *arg) | ||
4374 | { | ||
4375 | struct ifconf ifc; | ||
4376 | struct net_device *dev; | ||
4377 | char __user *pos; | ||
4378 | int len; | ||
4379 | int total; | ||
4380 | int i; | ||
4381 | |||
4382 | /* | ||
4383 | * Fetch the caller's info block. | ||
4384 | */ | ||
4385 | |||
4386 | if (copy_from_user(&ifc, arg, sizeof(struct ifconf))) | ||
4387 | return -EFAULT; | ||
4388 | |||
4389 | pos = ifc.ifc_buf; | ||
4390 | len = ifc.ifc_len; | ||
4391 | |||
4392 | /* | ||
4393 | * Loop over the interfaces, and write an info block for each. | ||
4394 | */ | ||
4395 | |||
4396 | total = 0; | ||
4397 | for_each_netdev(net, dev) { | ||
4398 | for (i = 0; i < NPROTO; i++) { | ||
4399 | if (gifconf_list[i]) { | ||
4400 | int done; | ||
4401 | if (!pos) | ||
4402 | done = gifconf_list[i](dev, NULL, 0); | ||
4403 | else | ||
4404 | done = gifconf_list[i](dev, pos + total, | ||
4405 | len - total); | ||
4406 | if (done < 0) | ||
4407 | return -EFAULT; | ||
4408 | total += done; | ||
4409 | } | ||
4410 | } | ||
4411 | } | ||
4412 | |||
4413 | /* | ||
4414 | * All done. Write the updated control block back to the caller. | ||
4415 | */ | ||
4416 | ifc.ifc_len = total; | ||
4417 | |||
4418 | /* | ||
4419 | * Both BSD and Solaris return 0 here, so we do too. | ||
4420 | */ | ||
4421 | return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0; | ||
4422 | } | ||
4423 | |||
4424 | #ifdef CONFIG_PROC_FS | 4220 | #ifdef CONFIG_PROC_FS |
4425 | 4221 | ||
4426 | #define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1) | 4222 | #define BUCKET_SPACE (32 - NETDEV_HASHBITS - 1) |
@@ -5381,375 +5177,6 @@ int dev_change_carrier(struct net_device *dev, bool new_carrier) | |||
5381 | } | 5177 | } |
5382 | EXPORT_SYMBOL(dev_change_carrier); | 5178 | EXPORT_SYMBOL(dev_change_carrier); |
5383 | 5179 | ||
5384 | /* | ||
5385 | * Perform the SIOCxIFxxx calls, inside rcu_read_lock() | ||
5386 | */ | ||
5387 | static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cmd) | ||
5388 | { | ||
5389 | int err; | ||
5390 | struct net_device *dev = dev_get_by_name_rcu(net, ifr->ifr_name); | ||
5391 | |||
5392 | if (!dev) | ||
5393 | return -ENODEV; | ||
5394 | |||
5395 | switch (cmd) { | ||
5396 | case SIOCGIFFLAGS: /* Get interface flags */ | ||
5397 | ifr->ifr_flags = (short) dev_get_flags(dev); | ||
5398 | return 0; | ||
5399 | |||
5400 | case SIOCGIFMETRIC: /* Get the metric on the interface | ||
5401 | (currently unused) */ | ||
5402 | ifr->ifr_metric = 0; | ||
5403 | return 0; | ||
5404 | |||
5405 | case SIOCGIFMTU: /* Get the MTU of a device */ | ||
5406 | ifr->ifr_mtu = dev->mtu; | ||
5407 | return 0; | ||
5408 | |||
5409 | case SIOCGIFHWADDR: | ||
5410 | if (!dev->addr_len) | ||
5411 | memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data); | ||
5412 | else | ||
5413 | memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr, | ||
5414 | min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); | ||
5415 | ifr->ifr_hwaddr.sa_family = dev->type; | ||
5416 | return 0; | ||
5417 | |||
5418 | case SIOCGIFSLAVE: | ||
5419 | err = -EINVAL; | ||
5420 | break; | ||
5421 | |||
5422 | case SIOCGIFMAP: | ||
5423 | ifr->ifr_map.mem_start = dev->mem_start; | ||
5424 | ifr->ifr_map.mem_end = dev->mem_end; | ||
5425 | ifr->ifr_map.base_addr = dev->base_addr; | ||
5426 | ifr->ifr_map.irq = dev->irq; | ||
5427 | ifr->ifr_map.dma = dev->dma; | ||
5428 | ifr->ifr_map.port = dev->if_port; | ||
5429 | return 0; | ||
5430 | |||
5431 | case SIOCGIFINDEX: | ||
5432 | ifr->ifr_ifindex = dev->ifindex; | ||
5433 | return 0; | ||
5434 | |||
5435 | case SIOCGIFTXQLEN: | ||
5436 | ifr->ifr_qlen = dev->tx_queue_len; | ||
5437 | return 0; | ||
5438 | |||
5439 | default: | ||
5440 | /* dev_ioctl() should ensure this case | ||
5441 | * is never reached | ||
5442 | */ | ||
5443 | WARN_ON(1); | ||
5444 | err = -ENOTTY; | ||
5445 | break; | ||
5446 | |||
5447 | } | ||
5448 | return err; | ||
5449 | } | ||
5450 | |||
5451 | /* | ||
5452 | * Perform the SIOCxIFxxx calls, inside rtnl_lock() | ||
5453 | */ | ||
5454 | static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) | ||
5455 | { | ||
5456 | int err; | ||
5457 | struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name); | ||
5458 | const struct net_device_ops *ops; | ||
5459 | |||
5460 | if (!dev) | ||
5461 | return -ENODEV; | ||
5462 | |||
5463 | ops = dev->netdev_ops; | ||
5464 | |||
5465 | switch (cmd) { | ||
5466 | case SIOCSIFFLAGS: /* Set interface flags */ | ||
5467 | return dev_change_flags(dev, ifr->ifr_flags); | ||
5468 | |||
5469 | case SIOCSIFMETRIC: /* Set the metric on the interface | ||
5470 | (currently unused) */ | ||
5471 | return -EOPNOTSUPP; | ||
5472 | |||
5473 | case SIOCSIFMTU: /* Set the MTU of a device */ | ||
5474 | return dev_set_mtu(dev, ifr->ifr_mtu); | ||
5475 | |||
5476 | case SIOCSIFHWADDR: | ||
5477 | return dev_set_mac_address(dev, &ifr->ifr_hwaddr); | ||
5478 | |||
5479 | case SIOCSIFHWBROADCAST: | ||
5480 | if (ifr->ifr_hwaddr.sa_family != dev->type) | ||
5481 | return -EINVAL; | ||
5482 | memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, | ||
5483 | min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); | ||
5484 | call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); | ||
5485 | return 0; | ||
5486 | |||
5487 | case SIOCSIFMAP: | ||
5488 | if (ops->ndo_set_config) { | ||
5489 | if (!netif_device_present(dev)) | ||
5490 | return -ENODEV; | ||
5491 | return ops->ndo_set_config(dev, &ifr->ifr_map); | ||
5492 | } | ||
5493 | return -EOPNOTSUPP; | ||
5494 | |||
5495 | case SIOCADDMULTI: | ||
5496 | if (!ops->ndo_set_rx_mode || | ||
5497 | ifr->ifr_hwaddr.sa_family != AF_UNSPEC) | ||
5498 | return -EINVAL; | ||
5499 | if (!netif_device_present(dev)) | ||
5500 | return -ENODEV; | ||
5501 | return dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data); | ||
5502 | |||
5503 | case SIOCDELMULTI: | ||
5504 | if (!ops->ndo_set_rx_mode || | ||
5505 | ifr->ifr_hwaddr.sa_family != AF_UNSPEC) | ||
5506 | return -EINVAL; | ||
5507 | if (!netif_device_present(dev)) | ||
5508 | return -ENODEV; | ||
5509 | return dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data); | ||
5510 | |||
5511 | case SIOCSIFTXQLEN: | ||
5512 | if (ifr->ifr_qlen < 0) | ||
5513 | return -EINVAL; | ||
5514 | dev->tx_queue_len = ifr->ifr_qlen; | ||
5515 | return 0; | ||
5516 | |||
5517 | case SIOCSIFNAME: | ||
5518 | ifr->ifr_newname[IFNAMSIZ-1] = '\0'; | ||
5519 | return dev_change_name(dev, ifr->ifr_newname); | ||
5520 | |||
5521 | case SIOCSHWTSTAMP: | ||
5522 | err = net_hwtstamp_validate(ifr); | ||
5523 | if (err) | ||
5524 | return err; | ||
5525 | /* fall through */ | ||
5526 | |||
5527 | /* | ||
5528 | * Unknown or private ioctl | ||
5529 | */ | ||
5530 | default: | ||
5531 | if ((cmd >= SIOCDEVPRIVATE && | ||
5532 | cmd <= SIOCDEVPRIVATE + 15) || | ||
5533 | cmd == SIOCBONDENSLAVE || | ||
5534 | cmd == SIOCBONDRELEASE || | ||
5535 | cmd == SIOCBONDSETHWADDR || | ||
5536 | cmd == SIOCBONDSLAVEINFOQUERY || | ||
5537 | cmd == SIOCBONDINFOQUERY || | ||
5538 | cmd == SIOCBONDCHANGEACTIVE || | ||
5539 | cmd == SIOCGMIIPHY || | ||
5540 | cmd == SIOCGMIIREG || | ||
5541 | cmd == SIOCSMIIREG || | ||
5542 | cmd == SIOCBRADDIF || | ||
5543 | cmd == SIOCBRDELIF || | ||
5544 | cmd == SIOCSHWTSTAMP || | ||
5545 | cmd == SIOCWANDEV) { | ||
5546 | err = -EOPNOTSUPP; | ||
5547 | if (ops->ndo_do_ioctl) { | ||
5548 | if (netif_device_present(dev)) | ||
5549 | err = ops->ndo_do_ioctl(dev, ifr, cmd); | ||
5550 | else | ||
5551 | err = -ENODEV; | ||
5552 | } | ||
5553 | } else | ||
5554 | err = -EINVAL; | ||
5555 | |||
5556 | } | ||
5557 | return err; | ||
5558 | } | ||
5559 | |||
5560 | /* | ||
5561 | * This function handles all "interface"-type I/O control requests. The actual | ||
5562 | * 'doing' part of this is dev_ifsioc above. | ||
5563 | */ | ||
5564 | |||
5565 | /** | ||
5566 | * dev_ioctl - network device ioctl | ||
5567 | * @net: the applicable net namespace | ||
5568 | * @cmd: command to issue | ||
5569 | * @arg: pointer to a struct ifreq in user space | ||
5570 | * | ||
5571 | * Issue ioctl functions to devices. This is normally called by the | ||
5572 | * user space syscall interfaces but can sometimes be useful for | ||
5573 | * other purposes. The return value is the return from the syscall if | ||
5574 | * positive or a negative errno code on error. | ||
5575 | */ | ||
5576 | |||
5577 | int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) | ||
5578 | { | ||
5579 | struct ifreq ifr; | ||
5580 | int ret; | ||
5581 | char *colon; | ||
5582 | |||
5583 | /* One special case: SIOCGIFCONF takes ifconf argument | ||
5584 | and requires shared lock, because it sleeps writing | ||
5585 | to user space. | ||
5586 | */ | ||
5587 | |||
5588 | if (cmd == SIOCGIFCONF) { | ||
5589 | rtnl_lock(); | ||
5590 | ret = dev_ifconf(net, (char __user *) arg); | ||
5591 | rtnl_unlock(); | ||
5592 | return ret; | ||
5593 | } | ||
5594 | if (cmd == SIOCGIFNAME) | ||
5595 | return dev_ifname(net, (struct ifreq __user *)arg); | ||
5596 | |||
5597 | if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) | ||
5598 | return -EFAULT; | ||
5599 | |||
5600 | ifr.ifr_name[IFNAMSIZ-1] = 0; | ||
5601 | |||
5602 | colon = strchr(ifr.ifr_name, ':'); | ||
5603 | if (colon) | ||
5604 | *colon = 0; | ||
5605 | |||
5606 | /* | ||
5607 | * See which interface the caller is talking about. | ||
5608 | */ | ||
5609 | |||
5610 | switch (cmd) { | ||
5611 | /* | ||
5612 | * These ioctl calls: | ||
5613 | * - can be done by all. | ||
5614 | * - atomic and do not require locking. | ||
5615 | * - return a value | ||
5616 | */ | ||
5617 | case SIOCGIFFLAGS: | ||
5618 | case SIOCGIFMETRIC: | ||
5619 | case SIOCGIFMTU: | ||
5620 | case SIOCGIFHWADDR: | ||
5621 | case SIOCGIFSLAVE: | ||
5622 | case SIOCGIFMAP: | ||
5623 | case SIOCGIFINDEX: | ||
5624 | case SIOCGIFTXQLEN: | ||
5625 | dev_load(net, ifr.ifr_name); | ||
5626 | rcu_read_lock(); | ||
5627 | ret = dev_ifsioc_locked(net, &ifr, cmd); | ||
5628 | rcu_read_unlock(); | ||
5629 | if (!ret) { | ||
5630 | if (colon) | ||
5631 | *colon = ':'; | ||
5632 | if (copy_to_user(arg, &ifr, | ||
5633 | sizeof(struct ifreq))) | ||
5634 | ret = -EFAULT; | ||
5635 | } | ||
5636 | return ret; | ||
5637 | |||
5638 | case SIOCETHTOOL: | ||
5639 | dev_load(net, ifr.ifr_name); | ||
5640 | rtnl_lock(); | ||
5641 | ret = dev_ethtool(net, &ifr); | ||
5642 | rtnl_unlock(); | ||
5643 | if (!ret) { | ||
5644 | if (colon) | ||
5645 | *colon = ':'; | ||
5646 | if (copy_to_user(arg, &ifr, | ||
5647 | sizeof(struct ifreq))) | ||
5648 | ret = -EFAULT; | ||
5649 | } | ||
5650 | return ret; | ||
5651 | |||
5652 | /* | ||
5653 | * These ioctl calls: | ||
5654 | * - require superuser power. | ||
5655 | * - require strict serialization. | ||
5656 | * - return a value | ||
5657 | */ | ||
5658 | case SIOCGMIIPHY: | ||
5659 | case SIOCGMIIREG: | ||
5660 | case SIOCSIFNAME: | ||
5661 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) | ||
5662 | return -EPERM; | ||
5663 | dev_load(net, ifr.ifr_name); | ||
5664 | rtnl_lock(); | ||
5665 | ret = dev_ifsioc(net, &ifr, cmd); | ||
5666 | rtnl_unlock(); | ||
5667 | if (!ret) { | ||
5668 | if (colon) | ||
5669 | *colon = ':'; | ||
5670 | if (copy_to_user(arg, &ifr, | ||
5671 | sizeof(struct ifreq))) | ||
5672 | ret = -EFAULT; | ||
5673 | } | ||
5674 | return ret; | ||
5675 | |||
5676 | /* | ||
5677 | * These ioctl calls: | ||
5678 | * - require superuser power. | ||
5679 | * - require strict serialization. | ||
5680 | * - do not return a value | ||
5681 | */ | ||
5682 | case SIOCSIFMAP: | ||
5683 | case SIOCSIFTXQLEN: | ||
5684 | if (!capable(CAP_NET_ADMIN)) | ||
5685 | return -EPERM; | ||
5686 | /* fall through */ | ||
5687 | /* | ||
5688 | * These ioctl calls: | ||
5689 | * - require local superuser power. | ||
5690 | * - require strict serialization. | ||
5691 | * - do not return a value | ||
5692 | */ | ||
5693 | case SIOCSIFFLAGS: | ||
5694 | case SIOCSIFMETRIC: | ||
5695 | case SIOCSIFMTU: | ||
5696 | case SIOCSIFHWADDR: | ||
5697 | case SIOCSIFSLAVE: | ||
5698 | case SIOCADDMULTI: | ||
5699 | case SIOCDELMULTI: | ||
5700 | case SIOCSIFHWBROADCAST: | ||
5701 | case SIOCSMIIREG: | ||
5702 | case SIOCBONDENSLAVE: | ||
5703 | case SIOCBONDRELEASE: | ||
5704 | case SIOCBONDSETHWADDR: | ||
5705 | case SIOCBONDCHANGEACTIVE: | ||
5706 | case SIOCBRADDIF: | ||
5707 | case SIOCBRDELIF: | ||
5708 | case SIOCSHWTSTAMP: | ||
5709 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) | ||
5710 | return -EPERM; | ||
5711 | /* fall through */ | ||
5712 | case SIOCBONDSLAVEINFOQUERY: | ||
5713 | case SIOCBONDINFOQUERY: | ||
5714 | dev_load(net, ifr.ifr_name); | ||
5715 | rtnl_lock(); | ||
5716 | ret = dev_ifsioc(net, &ifr, cmd); | ||
5717 | rtnl_unlock(); | ||
5718 | return ret; | ||
5719 | |||
5720 | case SIOCGIFMEM: | ||
5721 | /* Get the per device memory space. We can add this but | ||
5722 | * currently do not support it */ | ||
5723 | case SIOCSIFMEM: | ||
5724 | /* Set the per device memory buffer space. | ||
5725 | * Not applicable in our case */ | ||
5726 | case SIOCSIFLINK: | ||
5727 | return -ENOTTY; | ||
5728 | |||
5729 | /* | ||
5730 | * Unknown or private ioctl. | ||
5731 | */ | ||
5732 | default: | ||
5733 | if (cmd == SIOCWANDEV || | ||
5734 | (cmd >= SIOCDEVPRIVATE && | ||
5735 | cmd <= SIOCDEVPRIVATE + 15)) { | ||
5736 | dev_load(net, ifr.ifr_name); | ||
5737 | rtnl_lock(); | ||
5738 | ret = dev_ifsioc(net, &ifr, cmd); | ||
5739 | rtnl_unlock(); | ||
5740 | if (!ret && copy_to_user(arg, &ifr, | ||
5741 | sizeof(struct ifreq))) | ||
5742 | ret = -EFAULT; | ||
5743 | return ret; | ||
5744 | } | ||
5745 | /* Take care of Wireless Extensions */ | ||
5746 | if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) | ||
5747 | return wext_handle_ioctl(net, &ifr, cmd, arg); | ||
5748 | return -ENOTTY; | ||
5749 | } | ||
5750 | } | ||
5751 | |||
5752 | |||
5753 | /** | 5180 | /** |
5754 | * dev_new_index - allocate an ifindex | 5181 | * dev_new_index - allocate an ifindex |
5755 | * @net: the applicable net namespace | 5182 | * @net: the applicable net namespace |
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c new file mode 100644 index 000000000000..6cc0481faade --- /dev/null +++ b/net/core/dev_ioctl.c | |||
@@ -0,0 +1,576 @@ | |||
1 | #include <linux/kmod.h> | ||
2 | #include <linux/netdevice.h> | ||
3 | #include <linux/etherdevice.h> | ||
4 | #include <linux/rtnetlink.h> | ||
5 | #include <linux/net_tstamp.h> | ||
6 | #include <linux/wireless.h> | ||
7 | #include <net/wext.h> | ||
8 | |||
9 | /* | ||
10 | * Map an interface index to its name (SIOCGIFNAME) | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * We need this ioctl for efficient implementation of the | ||
15 | * if_indextoname() function required by the IPv6 API. Without | ||
16 | * it, we would have to search all the interfaces to find a | ||
17 | * match. --pb | ||
18 | */ | ||
19 | |||
20 | static int dev_ifname(struct net *net, struct ifreq __user *arg) | ||
21 | { | ||
22 | struct net_device *dev; | ||
23 | struct ifreq ifr; | ||
24 | unsigned seq; | ||
25 | |||
26 | /* | ||
27 | * Fetch the caller's info block. | ||
28 | */ | ||
29 | |||
30 | if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) | ||
31 | return -EFAULT; | ||
32 | |||
33 | retry: | ||
34 | seq = read_seqcount_begin(&devnet_rename_seq); | ||
35 | rcu_read_lock(); | ||
36 | dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex); | ||
37 | if (!dev) { | ||
38 | rcu_read_unlock(); | ||
39 | return -ENODEV; | ||
40 | } | ||
41 | |||
42 | strcpy(ifr.ifr_name, dev->name); | ||
43 | rcu_read_unlock(); | ||
44 | if (read_seqcount_retry(&devnet_rename_seq, seq)) | ||
45 | goto retry; | ||
46 | |||
47 | if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) | ||
48 | return -EFAULT; | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static gifconf_func_t *gifconf_list[NPROTO]; | ||
53 | |||
54 | /** | ||
55 | * register_gifconf - register a SIOCGIF handler | ||
56 | * @family: Address family | ||
57 | * @gifconf: Function handler | ||
58 | * | ||
59 | * Register protocol dependent address dumping routines. The handler | ||
60 | * that is passed must not be freed or reused until it has been replaced | ||
61 | * by another handler. | ||
62 | */ | ||
63 | int register_gifconf(unsigned int family, gifconf_func_t *gifconf) | ||
64 | { | ||
65 | if (family >= NPROTO) | ||
66 | return -EINVAL; | ||
67 | gifconf_list[family] = gifconf; | ||
68 | return 0; | ||
69 | } | ||
70 | EXPORT_SYMBOL(register_gifconf); | ||
71 | |||
72 | /* | ||
73 | * Perform a SIOCGIFCONF call. This structure will change | ||
74 | * size eventually, and there is nothing I can do about it. | ||
75 | * Thus we will need a 'compatibility mode'. | ||
76 | */ | ||
77 | |||
78 | static int dev_ifconf(struct net *net, char __user *arg) | ||
79 | { | ||
80 | struct ifconf ifc; | ||
81 | struct net_device *dev; | ||
82 | char __user *pos; | ||
83 | int len; | ||
84 | int total; | ||
85 | int i; | ||
86 | |||
87 | /* | ||
88 | * Fetch the caller's info block. | ||
89 | */ | ||
90 | |||
91 | if (copy_from_user(&ifc, arg, sizeof(struct ifconf))) | ||
92 | return -EFAULT; | ||
93 | |||
94 | pos = ifc.ifc_buf; | ||
95 | len = ifc.ifc_len; | ||
96 | |||
97 | /* | ||
98 | * Loop over the interfaces, and write an info block for each. | ||
99 | */ | ||
100 | |||
101 | total = 0; | ||
102 | for_each_netdev(net, dev) { | ||
103 | for (i = 0; i < NPROTO; i++) { | ||
104 | if (gifconf_list[i]) { | ||
105 | int done; | ||
106 | if (!pos) | ||
107 | done = gifconf_list[i](dev, NULL, 0); | ||
108 | else | ||
109 | done = gifconf_list[i](dev, pos + total, | ||
110 | len - total); | ||
111 | if (done < 0) | ||
112 | return -EFAULT; | ||
113 | total += done; | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * All done. Write the updated control block back to the caller. | ||
120 | */ | ||
121 | ifc.ifc_len = total; | ||
122 | |||
123 | /* | ||
124 | * Both BSD and Solaris return 0 here, so we do too. | ||
125 | */ | ||
126 | return copy_to_user(arg, &ifc, sizeof(struct ifconf)) ? -EFAULT : 0; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * Perform the SIOCxIFxxx calls, inside rcu_read_lock() | ||
131 | */ | ||
132 | static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cmd) | ||
133 | { | ||
134 | int err; | ||
135 | struct net_device *dev = dev_get_by_name_rcu(net, ifr->ifr_name); | ||
136 | |||
137 | if (!dev) | ||
138 | return -ENODEV; | ||
139 | |||
140 | switch (cmd) { | ||
141 | case SIOCGIFFLAGS: /* Get interface flags */ | ||
142 | ifr->ifr_flags = (short) dev_get_flags(dev); | ||
143 | return 0; | ||
144 | |||
145 | case SIOCGIFMETRIC: /* Get the metric on the interface | ||
146 | (currently unused) */ | ||
147 | ifr->ifr_metric = 0; | ||
148 | return 0; | ||
149 | |||
150 | case SIOCGIFMTU: /* Get the MTU of a device */ | ||
151 | ifr->ifr_mtu = dev->mtu; | ||
152 | return 0; | ||
153 | |||
154 | case SIOCGIFHWADDR: | ||
155 | if (!dev->addr_len) | ||
156 | memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data); | ||
157 | else | ||
158 | memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr, | ||
159 | min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); | ||
160 | ifr->ifr_hwaddr.sa_family = dev->type; | ||
161 | return 0; | ||
162 | |||
163 | case SIOCGIFSLAVE: | ||
164 | err = -EINVAL; | ||
165 | break; | ||
166 | |||
167 | case SIOCGIFMAP: | ||
168 | ifr->ifr_map.mem_start = dev->mem_start; | ||
169 | ifr->ifr_map.mem_end = dev->mem_end; | ||
170 | ifr->ifr_map.base_addr = dev->base_addr; | ||
171 | ifr->ifr_map.irq = dev->irq; | ||
172 | ifr->ifr_map.dma = dev->dma; | ||
173 | ifr->ifr_map.port = dev->if_port; | ||
174 | return 0; | ||
175 | |||
176 | case SIOCGIFINDEX: | ||
177 | ifr->ifr_ifindex = dev->ifindex; | ||
178 | return 0; | ||
179 | |||
180 | case SIOCGIFTXQLEN: | ||
181 | ifr->ifr_qlen = dev->tx_queue_len; | ||
182 | return 0; | ||
183 | |||
184 | default: | ||
185 | /* dev_ioctl() should ensure this case | ||
186 | * is never reached | ||
187 | */ | ||
188 | WARN_ON(1); | ||
189 | err = -ENOTTY; | ||
190 | break; | ||
191 | |||
192 | } | ||
193 | return err; | ||
194 | } | ||
195 | |||
196 | static int net_hwtstamp_validate(struct ifreq *ifr) | ||
197 | { | ||
198 | struct hwtstamp_config cfg; | ||
199 | enum hwtstamp_tx_types tx_type; | ||
200 | enum hwtstamp_rx_filters rx_filter; | ||
201 | int tx_type_valid = 0; | ||
202 | int rx_filter_valid = 0; | ||
203 | |||
204 | if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) | ||
205 | return -EFAULT; | ||
206 | |||
207 | if (cfg.flags) /* reserved for future extensions */ | ||
208 | return -EINVAL; | ||
209 | |||
210 | tx_type = cfg.tx_type; | ||
211 | rx_filter = cfg.rx_filter; | ||
212 | |||
213 | switch (tx_type) { | ||
214 | case HWTSTAMP_TX_OFF: | ||
215 | case HWTSTAMP_TX_ON: | ||
216 | case HWTSTAMP_TX_ONESTEP_SYNC: | ||
217 | tx_type_valid = 1; | ||
218 | break; | ||
219 | } | ||
220 | |||
221 | switch (rx_filter) { | ||
222 | case HWTSTAMP_FILTER_NONE: | ||
223 | case HWTSTAMP_FILTER_ALL: | ||
224 | case HWTSTAMP_FILTER_SOME: | ||
225 | case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: | ||
226 | case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: | ||
227 | case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: | ||
228 | case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: | ||
229 | case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: | ||
230 | case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: | ||
231 | case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: | ||
232 | case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: | ||
233 | case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: | ||
234 | case HWTSTAMP_FILTER_PTP_V2_EVENT: | ||
235 | case HWTSTAMP_FILTER_PTP_V2_SYNC: | ||
236 | case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: | ||
237 | rx_filter_valid = 1; | ||
238 | break; | ||
239 | } | ||
240 | |||
241 | if (!tx_type_valid || !rx_filter_valid) | ||
242 | return -ERANGE; | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Perform the SIOCxIFxxx calls, inside rtnl_lock() | ||
249 | */ | ||
250 | static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) | ||
251 | { | ||
252 | int err; | ||
253 | struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name); | ||
254 | const struct net_device_ops *ops; | ||
255 | |||
256 | if (!dev) | ||
257 | return -ENODEV; | ||
258 | |||
259 | ops = dev->netdev_ops; | ||
260 | |||
261 | switch (cmd) { | ||
262 | case SIOCSIFFLAGS: /* Set interface flags */ | ||
263 | return dev_change_flags(dev, ifr->ifr_flags); | ||
264 | |||
265 | case SIOCSIFMETRIC: /* Set the metric on the interface | ||
266 | (currently unused) */ | ||
267 | return -EOPNOTSUPP; | ||
268 | |||
269 | case SIOCSIFMTU: /* Set the MTU of a device */ | ||
270 | return dev_set_mtu(dev, ifr->ifr_mtu); | ||
271 | |||
272 | case SIOCSIFHWADDR: | ||
273 | return dev_set_mac_address(dev, &ifr->ifr_hwaddr); | ||
274 | |||
275 | case SIOCSIFHWBROADCAST: | ||
276 | if (ifr->ifr_hwaddr.sa_family != dev->type) | ||
277 | return -EINVAL; | ||
278 | memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data, | ||
279 | min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len)); | ||
280 | call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); | ||
281 | return 0; | ||
282 | |||
283 | case SIOCSIFMAP: | ||
284 | if (ops->ndo_set_config) { | ||
285 | if (!netif_device_present(dev)) | ||
286 | return -ENODEV; | ||
287 | return ops->ndo_set_config(dev, &ifr->ifr_map); | ||
288 | } | ||
289 | return -EOPNOTSUPP; | ||
290 | |||
291 | case SIOCADDMULTI: | ||
292 | if (!ops->ndo_set_rx_mode || | ||
293 | ifr->ifr_hwaddr.sa_family != AF_UNSPEC) | ||
294 | return -EINVAL; | ||
295 | if (!netif_device_present(dev)) | ||
296 | return -ENODEV; | ||
297 | return dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data); | ||
298 | |||
299 | case SIOCDELMULTI: | ||
300 | if (!ops->ndo_set_rx_mode || | ||
301 | ifr->ifr_hwaddr.sa_family != AF_UNSPEC) | ||
302 | return -EINVAL; | ||
303 | if (!netif_device_present(dev)) | ||
304 | return -ENODEV; | ||
305 | return dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data); | ||
306 | |||
307 | case SIOCSIFTXQLEN: | ||
308 | if (ifr->ifr_qlen < 0) | ||
309 | return -EINVAL; | ||
310 | dev->tx_queue_len = ifr->ifr_qlen; | ||
311 | return 0; | ||
312 | |||
313 | case SIOCSIFNAME: | ||
314 | ifr->ifr_newname[IFNAMSIZ-1] = '\0'; | ||
315 | return dev_change_name(dev, ifr->ifr_newname); | ||
316 | |||
317 | case SIOCSHWTSTAMP: | ||
318 | err = net_hwtstamp_validate(ifr); | ||
319 | if (err) | ||
320 | return err; | ||
321 | /* fall through */ | ||
322 | |||
323 | /* | ||
324 | * Unknown or private ioctl | ||
325 | */ | ||
326 | default: | ||
327 | if ((cmd >= SIOCDEVPRIVATE && | ||
328 | cmd <= SIOCDEVPRIVATE + 15) || | ||
329 | cmd == SIOCBONDENSLAVE || | ||
330 | cmd == SIOCBONDRELEASE || | ||
331 | cmd == SIOCBONDSETHWADDR || | ||
332 | cmd == SIOCBONDSLAVEINFOQUERY || | ||
333 | cmd == SIOCBONDINFOQUERY || | ||
334 | cmd == SIOCBONDCHANGEACTIVE || | ||
335 | cmd == SIOCGMIIPHY || | ||
336 | cmd == SIOCGMIIREG || | ||
337 | cmd == SIOCSMIIREG || | ||
338 | cmd == SIOCBRADDIF || | ||
339 | cmd == SIOCBRDELIF || | ||
340 | cmd == SIOCSHWTSTAMP || | ||
341 | cmd == SIOCWANDEV) { | ||
342 | err = -EOPNOTSUPP; | ||
343 | if (ops->ndo_do_ioctl) { | ||
344 | if (netif_device_present(dev)) | ||
345 | err = ops->ndo_do_ioctl(dev, ifr, cmd); | ||
346 | else | ||
347 | err = -ENODEV; | ||
348 | } | ||
349 | } else | ||
350 | err = -EINVAL; | ||
351 | |||
352 | } | ||
353 | return err; | ||
354 | } | ||
355 | |||
356 | /** | ||
357 | * dev_load - load a network module | ||
358 | * @net: the applicable net namespace | ||
359 | * @name: name of interface | ||
360 | * | ||
361 | * If a network interface is not present and the process has suitable | ||
362 | * privileges this function loads the module. If module loading is not | ||
363 | * available in this kernel then it becomes a nop. | ||
364 | */ | ||
365 | |||
366 | void dev_load(struct net *net, const char *name) | ||
367 | { | ||
368 | struct net_device *dev; | ||
369 | int no_module; | ||
370 | |||
371 | rcu_read_lock(); | ||
372 | dev = dev_get_by_name_rcu(net, name); | ||
373 | rcu_read_unlock(); | ||
374 | |||
375 | no_module = !dev; | ||
376 | if (no_module && capable(CAP_NET_ADMIN)) | ||
377 | no_module = request_module("netdev-%s", name); | ||
378 | if (no_module && capable(CAP_SYS_MODULE)) { | ||
379 | if (!request_module("%s", name)) | ||
380 | pr_warn("Loading kernel module for a network device with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev-%s instead.\n", | ||
381 | name); | ||
382 | } | ||
383 | } | ||
384 | EXPORT_SYMBOL(dev_load); | ||
385 | |||
386 | /* | ||
387 | * This function handles all "interface"-type I/O control requests. The actual | ||
388 | * 'doing' part of this is dev_ifsioc above. | ||
389 | */ | ||
390 | |||
391 | /** | ||
392 | * dev_ioctl - network device ioctl | ||
393 | * @net: the applicable net namespace | ||
394 | * @cmd: command to issue | ||
395 | * @arg: pointer to a struct ifreq in user space | ||
396 | * | ||
397 | * Issue ioctl functions to devices. This is normally called by the | ||
398 | * user space syscall interfaces but can sometimes be useful for | ||
399 | * other purposes. The return value is the return from the syscall if | ||
400 | * positive or a negative errno code on error. | ||
401 | */ | ||
402 | |||
403 | int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg) | ||
404 | { | ||
405 | struct ifreq ifr; | ||
406 | int ret; | ||
407 | char *colon; | ||
408 | |||
409 | /* One special case: SIOCGIFCONF takes ifconf argument | ||
410 | and requires shared lock, because it sleeps writing | ||
411 | to user space. | ||
412 | */ | ||
413 | |||
414 | if (cmd == SIOCGIFCONF) { | ||
415 | rtnl_lock(); | ||
416 | ret = dev_ifconf(net, (char __user *) arg); | ||
417 | rtnl_unlock(); | ||
418 | return ret; | ||
419 | } | ||
420 | if (cmd == SIOCGIFNAME) | ||
421 | return dev_ifname(net, (struct ifreq __user *)arg); | ||
422 | |||
423 | if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) | ||
424 | return -EFAULT; | ||
425 | |||
426 | ifr.ifr_name[IFNAMSIZ-1] = 0; | ||
427 | |||
428 | colon = strchr(ifr.ifr_name, ':'); | ||
429 | if (colon) | ||
430 | *colon = 0; | ||
431 | |||
432 | /* | ||
433 | * See which interface the caller is talking about. | ||
434 | */ | ||
435 | |||
436 | switch (cmd) { | ||
437 | /* | ||
438 | * These ioctl calls: | ||
439 | * - can be done by all. | ||
440 | * - atomic and do not require locking. | ||
441 | * - return a value | ||
442 | */ | ||
443 | case SIOCGIFFLAGS: | ||
444 | case SIOCGIFMETRIC: | ||
445 | case SIOCGIFMTU: | ||
446 | case SIOCGIFHWADDR: | ||
447 | case SIOCGIFSLAVE: | ||
448 | case SIOCGIFMAP: | ||
449 | case SIOCGIFINDEX: | ||
450 | case SIOCGIFTXQLEN: | ||
451 | dev_load(net, ifr.ifr_name); | ||
452 | rcu_read_lock(); | ||
453 | ret = dev_ifsioc_locked(net, &ifr, cmd); | ||
454 | rcu_read_unlock(); | ||
455 | if (!ret) { | ||
456 | if (colon) | ||
457 | *colon = ':'; | ||
458 | if (copy_to_user(arg, &ifr, | ||
459 | sizeof(struct ifreq))) | ||
460 | ret = -EFAULT; | ||
461 | } | ||
462 | return ret; | ||
463 | |||
464 | case SIOCETHTOOL: | ||
465 | dev_load(net, ifr.ifr_name); | ||
466 | rtnl_lock(); | ||
467 | ret = dev_ethtool(net, &ifr); | ||
468 | rtnl_unlock(); | ||
469 | if (!ret) { | ||
470 | if (colon) | ||
471 | *colon = ':'; | ||
472 | if (copy_to_user(arg, &ifr, | ||
473 | sizeof(struct ifreq))) | ||
474 | ret = -EFAULT; | ||
475 | } | ||
476 | return ret; | ||
477 | |||
478 | /* | ||
479 | * These ioctl calls: | ||
480 | * - require superuser power. | ||
481 | * - require strict serialization. | ||
482 | * - return a value | ||
483 | */ | ||
484 | case SIOCGMIIPHY: | ||
485 | case SIOCGMIIREG: | ||
486 | case SIOCSIFNAME: | ||
487 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) | ||
488 | return -EPERM; | ||
489 | dev_load(net, ifr.ifr_name); | ||
490 | rtnl_lock(); | ||
491 | ret = dev_ifsioc(net, &ifr, cmd); | ||
492 | rtnl_unlock(); | ||
493 | if (!ret) { | ||
494 | if (colon) | ||
495 | *colon = ':'; | ||
496 | if (copy_to_user(arg, &ifr, | ||
497 | sizeof(struct ifreq))) | ||
498 | ret = -EFAULT; | ||
499 | } | ||
500 | return ret; | ||
501 | |||
502 | /* | ||
503 | * These ioctl calls: | ||
504 | * - require superuser power. | ||
505 | * - require strict serialization. | ||
506 | * - do not return a value | ||
507 | */ | ||
508 | case SIOCSIFMAP: | ||
509 | case SIOCSIFTXQLEN: | ||
510 | if (!capable(CAP_NET_ADMIN)) | ||
511 | return -EPERM; | ||
512 | /* fall through */ | ||
513 | /* | ||
514 | * These ioctl calls: | ||
515 | * - require local superuser power. | ||
516 | * - require strict serialization. | ||
517 | * - do not return a value | ||
518 | */ | ||
519 | case SIOCSIFFLAGS: | ||
520 | case SIOCSIFMETRIC: | ||
521 | case SIOCSIFMTU: | ||
522 | case SIOCSIFHWADDR: | ||
523 | case SIOCSIFSLAVE: | ||
524 | case SIOCADDMULTI: | ||
525 | case SIOCDELMULTI: | ||
526 | case SIOCSIFHWBROADCAST: | ||
527 | case SIOCSMIIREG: | ||
528 | case SIOCBONDENSLAVE: | ||
529 | case SIOCBONDRELEASE: | ||
530 | case SIOCBONDSETHWADDR: | ||
531 | case SIOCBONDCHANGEACTIVE: | ||
532 | case SIOCBRADDIF: | ||
533 | case SIOCBRDELIF: | ||
534 | case SIOCSHWTSTAMP: | ||
535 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) | ||
536 | return -EPERM; | ||
537 | /* fall through */ | ||
538 | case SIOCBONDSLAVEINFOQUERY: | ||
539 | case SIOCBONDINFOQUERY: | ||
540 | dev_load(net, ifr.ifr_name); | ||
541 | rtnl_lock(); | ||
542 | ret = dev_ifsioc(net, &ifr, cmd); | ||
543 | rtnl_unlock(); | ||
544 | return ret; | ||
545 | |||
546 | case SIOCGIFMEM: | ||
547 | /* Get the per device memory space. We can add this but | ||
548 | * currently do not support it */ | ||
549 | case SIOCSIFMEM: | ||
550 | /* Set the per device memory buffer space. | ||
551 | * Not applicable in our case */ | ||
552 | case SIOCSIFLINK: | ||
553 | return -ENOTTY; | ||
554 | |||
555 | /* | ||
556 | * Unknown or private ioctl. | ||
557 | */ | ||
558 | default: | ||
559 | if (cmd == SIOCWANDEV || | ||
560 | (cmd >= SIOCDEVPRIVATE && | ||
561 | cmd <= SIOCDEVPRIVATE + 15)) { | ||
562 | dev_load(net, ifr.ifr_name); | ||
563 | rtnl_lock(); | ||
564 | ret = dev_ifsioc(net, &ifr, cmd); | ||
565 | rtnl_unlock(); | ||
566 | if (!ret && copy_to_user(arg, &ifr, | ||
567 | sizeof(struct ifreq))) | ||
568 | ret = -EFAULT; | ||
569 | return ret; | ||
570 | } | ||
571 | /* Take care of Wireless Extensions */ | ||
572 | if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) | ||
573 | return wext_handle_ioctl(net, &ifr, cmd, arg); | ||
574 | return -ENOTTY; | ||
575 | } | ||
576 | } | ||