diff options
author | Jason Wang <jasowang@redhat.com> | 2012-10-31 15:46:01 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-11-01 11:14:08 -0400 |
commit | cde8b15f1aabe327038ee4e0e11dd6b798572f69 (patch) | |
tree | 920146d9cb4e1918c1e4b5063ce23de675f5988f /drivers/net/tun.c | |
parent | c8d68e6be1c3b242f1c598595830890b65cea64a (diff) |
tuntap: add ioctl to attach or detach a file form tuntap device
Sometimes usespace may need to active/deactive a queue, this could be done by
detaching and attaching a file from tuntap device.
This patch introduces a new ioctls - TUNSETQUEUE which could be used to do
this. Flag IFF_ATTACH_QUEUE were introduced to do attaching while
IFF_DETACH_QUEUE were introduced to do the detaching.
Signed-off-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 56 |
1 files changed, 48 insertions, 8 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 2762c55aeb66..79b6f9ecc12c 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -195,6 +195,15 @@ static u16 tun_select_queue(struct net_device *dev, struct sk_buff *skb) | |||
195 | return txq; | 195 | return txq; |
196 | } | 196 | } |
197 | 197 | ||
198 | static inline bool tun_not_capable(struct tun_struct *tun) | ||
199 | { | ||
200 | const struct cred *cred = current_cred(); | ||
201 | |||
202 | return ((uid_valid(tun->owner) && !uid_eq(cred->euid, tun->owner)) || | ||
203 | (gid_valid(tun->group) && !in_egroup_p(tun->group))) && | ||
204 | !capable(CAP_NET_ADMIN); | ||
205 | } | ||
206 | |||
198 | static void tun_set_real_num_queues(struct tun_struct *tun) | 207 | static void tun_set_real_num_queues(struct tun_struct *tun) |
199 | { | 208 | { |
200 | netif_set_real_num_tx_queues(tun->dev, tun->numqueues); | 209 | netif_set_real_num_tx_queues(tun->dev, tun->numqueues); |
@@ -1310,8 +1319,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) | |||
1310 | 1319 | ||
1311 | dev = __dev_get_by_name(net, ifr->ifr_name); | 1320 | dev = __dev_get_by_name(net, ifr->ifr_name); |
1312 | if (dev) { | 1321 | if (dev) { |
1313 | const struct cred *cred = current_cred(); | ||
1314 | |||
1315 | if (ifr->ifr_flags & IFF_TUN_EXCL) | 1322 | if (ifr->ifr_flags & IFF_TUN_EXCL) |
1316 | return -EBUSY; | 1323 | return -EBUSY; |
1317 | if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops) | 1324 | if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops) |
@@ -1321,9 +1328,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) | |||
1321 | else | 1328 | else |
1322 | return -EINVAL; | 1329 | return -EINVAL; |
1323 | 1330 | ||
1324 | if (((uid_valid(tun->owner) && !uid_eq(cred->euid, tun->owner)) || | 1331 | if (tun_not_capable(tun)) |
1325 | (gid_valid(tun->group) && !in_egroup_p(tun->group))) && | ||
1326 | !capable(CAP_NET_ADMIN)) | ||
1327 | return -EPERM; | 1332 | return -EPERM; |
1328 | err = security_tun_dev_attach(tfile->socket.sk); | 1333 | err = security_tun_dev_attach(tfile->socket.sk); |
1329 | if (err < 0) | 1334 | if (err < 0) |
@@ -1530,6 +1535,40 @@ static void tun_set_sndbuf(struct tun_struct *tun) | |||
1530 | } | 1535 | } |
1531 | } | 1536 | } |
1532 | 1537 | ||
1538 | static int tun_set_queue(struct file *file, struct ifreq *ifr) | ||
1539 | { | ||
1540 | struct tun_file *tfile = file->private_data; | ||
1541 | struct tun_struct *tun; | ||
1542 | struct net_device *dev; | ||
1543 | int ret = 0; | ||
1544 | |||
1545 | rtnl_lock(); | ||
1546 | |||
1547 | if (ifr->ifr_flags & IFF_ATTACH_QUEUE) { | ||
1548 | dev = __dev_get_by_name(tfile->net, ifr->ifr_name); | ||
1549 | if (!dev) { | ||
1550 | ret = -EINVAL; | ||
1551 | goto unlock; | ||
1552 | } | ||
1553 | |||
1554 | tun = netdev_priv(dev); | ||
1555 | if (dev->netdev_ops != &tap_netdev_ops && | ||
1556 | dev->netdev_ops != &tun_netdev_ops) | ||
1557 | ret = -EINVAL; | ||
1558 | else if (tun_not_capable(tun)) | ||
1559 | ret = -EPERM; | ||
1560 | else | ||
1561 | ret = tun_attach(tun, file); | ||
1562 | } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) | ||
1563 | __tun_detach(tfile, false); | ||
1564 | else | ||
1565 | ret = -EINVAL; | ||
1566 | |||
1567 | unlock: | ||
1568 | rtnl_unlock(); | ||
1569 | return ret; | ||
1570 | } | ||
1571 | |||
1533 | static long __tun_chr_ioctl(struct file *file, unsigned int cmd, | 1572 | static long __tun_chr_ioctl(struct file *file, unsigned int cmd, |
1534 | unsigned long arg, int ifreq_len) | 1573 | unsigned long arg, int ifreq_len) |
1535 | { | 1574 | { |
@@ -1543,7 +1582,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, | |||
1543 | int vnet_hdr_sz; | 1582 | int vnet_hdr_sz; |
1544 | int ret; | 1583 | int ret; |
1545 | 1584 | ||
1546 | if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) { | 1585 | if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == 0x89) { |
1547 | if (copy_from_user(&ifr, argp, ifreq_len)) | 1586 | if (copy_from_user(&ifr, argp, ifreq_len)) |
1548 | return -EFAULT; | 1587 | return -EFAULT; |
1549 | } else { | 1588 | } else { |
@@ -1554,9 +1593,10 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, | |||
1554 | * This is needed because we never checked for invalid flags on | 1593 | * This is needed because we never checked for invalid flags on |
1555 | * TUNSETIFF. */ | 1594 | * TUNSETIFF. */ |
1556 | return put_user(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE | | 1595 | return put_user(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE | |
1557 | IFF_VNET_HDR, | 1596 | IFF_VNET_HDR | IFF_MULTI_QUEUE, |
1558 | (unsigned int __user*)argp); | 1597 | (unsigned int __user*)argp); |
1559 | } | 1598 | } else if (cmd == TUNSETQUEUE) |
1599 | return tun_set_queue(file, &ifr); | ||
1560 | 1600 | ||
1561 | ret = 0; | 1601 | ret = 0; |
1562 | rtnl_lock(); | 1602 | rtnl_lock(); |