diff options
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 122 |
1 files changed, 61 insertions, 61 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 027f7aba26a..3f5d28851aa 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -103,13 +103,10 @@ struct tun_struct { uid_t owner; gid_t group; - struct sk_buff_head readq; - struct net_device *dev; struct fasync_struct *fasync; struct tap_filter txflt; - struct sock *sk; struct socket socket; #ifdef TUN_DEBUG @@ -130,17 +127,10 @@ static inline struct tun_sock *tun_sk(struct sock *sk) static int tun_attach(struct tun_struct *tun, struct file *file) { struct tun_file *tfile = file->private_data; - const struct cred *cred = current_cred(); int err; ASSERT_RTNL(); - /* Check permissions */ - if (((tun->owner != -1 && cred->euid != tun->owner) || - (tun->group != -1 && !in_egroup_p(tun->group))) && - !capable(CAP_NET_ADMIN)) - return -EPERM; - netif_tx_lock_bh(tun->dev); err = -EINVAL; @@ -155,7 +145,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file) tfile->tun = tun; tun->tfile = tfile; dev_hold(tun->dev); - sock_hold(tun->sk); + sock_hold(tun->socket.sk); atomic_inc(&tfile->count); out: @@ -171,7 +161,7 @@ static void __tun_detach(struct tun_struct *tun) netif_tx_unlock_bh(tun->dev); /* Drop read queue */ - skb_queue_purge(&tun->readq); + skb_queue_purge(&tun->socket.sk->sk_receive_queue); /* Drop the extra count on the net device */ dev_put(tun->dev); @@ -340,7 +330,7 @@ static void tun_free_netdev(struct net_device *dev) { struct tun_struct *tun = netdev_priv(dev); - sock_put(tun->sk); + sock_put(tun->socket.sk); } /* Net device open. */ @@ -358,7 +348,7 @@ static int tun_net_close(struct net_device *dev) } /* Net device start xmit */ -static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) { struct tun_struct *tun = netdev_priv(dev); @@ -374,7 +364,7 @@ static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev) if (!check_filter(&tun->txflt, skb)) goto drop; - if (skb_queue_len(&tun->readq) >= dev->tx_queue_len) { + if (skb_queue_len(&tun->socket.sk->sk_receive_queue) >= dev->tx_queue_len) { if (!(tun->flags & TUN_ONE_QUEUE)) { /* Normal queueing mode. */ /* Packet scheduler handles dropping of further packets. */ @@ -391,19 +381,19 @@ static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev) } /* Enqueue packet */ - skb_queue_tail(&tun->readq, skb); + skb_queue_tail(&tun->socket.sk->sk_receive_queue, skb); dev->trans_start = jiffies; /* Notify and wake up reader process */ if (tun->flags & TUN_FASYNC) kill_fasync(&tun->fasync, SIGIO, POLL_IN); wake_up_interruptible(&tun->socket.wait); - return 0; + return NETDEV_TX_OK; drop: dev->stats.tx_dropped++; kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static void tun_net_mclist(struct net_device *dev) @@ -492,13 +482,13 @@ static unsigned int tun_chr_poll(struct file *file, poll_table * wait) if (!tun) return POLLERR; - sk = tun->sk; + sk = tun->socket.sk; DBG(KERN_INFO "%s: tun_chr_poll\n", tun->dev->name); poll_wait(file, &tun->socket.wait, wait); - if (!skb_queue_empty(&tun->readq)) + if (!skb_queue_empty(&sk->sk_receive_queue)) mask |= POLLIN | POLLRDNORM; if (sock_writeable(sk) || @@ -519,7 +509,7 @@ static inline struct sk_buff *tun_alloc_skb(struct tun_struct *tun, size_t prepad, size_t len, size_t linear, int noblock) { - struct sock *sk = tun->sk; + struct sock *sk = tun->socket.sk; struct sk_buff *skb; int err; @@ -641,6 +631,9 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, case VIRTIO_NET_HDR_GSO_TCPV6: skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; break; + case VIRTIO_NET_HDR_GSO_UDP: + skb_shinfo(skb)->gso_type = SKB_GSO_UDP; + break; default: tun->dev->stats.rx_frame_errors++; kfree_skb(skb); @@ -726,6 +719,8 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun, gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV4; else if (sinfo->gso_type & SKB_GSO_TCPV6) gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + else if (sinfo->gso_type & SKB_GSO_UDP) + gso.gso_type = VIRTIO_NET_HDR_GSO_UDP; else BUG(); if (sinfo->gso_type & SKB_GSO_TCP_ECN) @@ -782,7 +777,7 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv, current->state = TASK_INTERRUPTIBLE; /* Read frames from the queue */ - if (!(skb=skb_dequeue(&tun->readq))) { + if (!(skb=skb_dequeue(&tun->socket.sk->sk_receive_queue))) { if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; break; @@ -819,8 +814,6 @@ static void tun_setup(struct net_device *dev) { struct tun_struct *tun = netdev_priv(dev); - skb_queue_head_init(&tun->readq); - tun->owner = -1; tun->group = -1; @@ -926,6 +919,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) dev = __dev_get_by_name(net, ifr->ifr_name); if (dev) { + const struct cred *cred = current_cred(); + if (ifr->ifr_flags & IFF_TUN_EXCL) return -EBUSY; if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops) @@ -935,6 +930,14 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) else return -EINVAL; + if (((tun->owner != -1 && cred->euid != tun->owner) || + (tun->group != -1 && !in_egroup_p(tun->group))) && + !capable(CAP_NET_ADMIN)) + return -EPERM; + err = security_tun_dev_attach(tun->socket.sk); + if (err < 0) + return err; + err = tun_attach(tun, file); if (err < 0) return err; @@ -947,6 +950,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) if (!capable(CAP_NET_ADMIN)) return -EPERM; + err = security_tun_dev_create(); + if (err < 0) + return err; /* Set dev type */ if (ifr->ifr_flags & IFF_TUN) { @@ -986,9 +992,10 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) sk->sk_write_space = tun_sock_write_space; sk->sk_sndbuf = INT_MAX; - tun->sk = sk; container_of(sk, struct tun_sock, sk)->tun = tun; + security_tun_dev_post_create(sk); + tun_net_init(dev); if (strchr(dev->name, '%')) { @@ -997,7 +1004,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) goto err_free_sk; } - err = -EINVAL; err = register_netdevice(tun->dev); if (err < 0) goto err_free_sk; @@ -1048,20 +1054,15 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) return err; } -static int tun_get_iff(struct net *net, struct file *file, struct ifreq *ifr) +static int tun_get_iff(struct net *net, struct tun_struct *tun, + struct ifreq *ifr) { - struct tun_struct *tun = tun_get(file); - - if (!tun) - return -EBADFD; - DBG(KERN_INFO "%s: tun_get_iff\n", tun->dev->name); strcpy(ifr->ifr_name, tun->dev->name); ifr->ifr_flags = tun_flags(tun); - tun_put(tun); return 0; } @@ -1074,7 +1075,8 @@ static int set_offload(struct net_device *dev, unsigned long arg) old_features = dev->features; /* Unset features, set them as we chew on the arg. */ features = (old_features & ~(NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST - |NETIF_F_TSO_ECN|NETIF_F_TSO|NETIF_F_TSO6)); + |NETIF_F_TSO_ECN|NETIF_F_TSO|NETIF_F_TSO6 + |NETIF_F_UFO)); if (arg & TUN_F_CSUM) { features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; @@ -1091,6 +1093,11 @@ static int set_offload(struct net_device *dev, unsigned long arg) features |= NETIF_F_TSO6; arg &= ~(TUN_F_TSO4|TUN_F_TSO6); } + + if (arg & TUN_F_UFO) { + features |= NETIF_F_UFO; + arg &= ~TUN_F_UFO; + } } /* This gives the user a way to test for new features in future by @@ -1105,8 +1112,8 @@ static int set_offload(struct net_device *dev, unsigned long arg) return 0; } -static int tun_chr_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long tun_chr_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { struct tun_file *tfile = file->private_data; struct tun_struct *tun; @@ -1128,34 +1135,32 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, (unsigned int __user*)argp); } + rtnl_lock(); + tun = __tun_get(tfile); if (cmd == TUNSETIFF && !tun) { - int err; - ifr.ifr_name[IFNAMSIZ-1] = '\0'; - rtnl_lock(); - err = tun_set_iff(tfile->net, file, &ifr); - rtnl_unlock(); + ret = tun_set_iff(tfile->net, file, &ifr); - if (err) - return err; + if (ret) + goto unlock; if (copy_to_user(argp, &ifr, sizeof(ifr))) - return -EFAULT; - return 0; + ret = -EFAULT; + goto unlock; } - + ret = -EBADFD; if (!tun) - return -EBADFD; + goto unlock; DBG(KERN_INFO "%s: tun_chr_ioctl cmd %d\n", tun->dev->name, cmd); ret = 0; switch (cmd) { case TUNGETIFF: - ret = tun_get_iff(current->nsproxy->net_ns, file, &ifr); + ret = tun_get_iff(current->nsproxy->net_ns, tun, &ifr); if (ret) break; @@ -1201,7 +1206,6 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, case TUNSETLINK: /* Only allow setting the type when the interface is down */ - rtnl_lock(); if (tun->dev->flags & IFF_UP) { DBG(KERN_INFO "%s: Linktype set failed because interface is up\n", tun->dev->name); @@ -1211,7 +1215,6 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, DBG(KERN_INFO "%s: linktype set to %d\n", tun->dev->name, tun->dev->type); ret = 0; } - rtnl_unlock(); break; #ifdef TUN_DEBUG @@ -1220,9 +1223,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, break; #endif case TUNSETOFFLOAD: - rtnl_lock(); ret = set_offload(tun->dev, arg); - rtnl_unlock(); break; case TUNSETTXFILTER: @@ -1230,9 +1231,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, ret = -EINVAL; if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV) break; - rtnl_lock(); ret = update_filter(&tun->txflt, (void __user *)arg); - rtnl_unlock(); break; case SIOCGIFHWADDR: @@ -1248,13 +1247,11 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, DBG(KERN_DEBUG "%s: set hw address: %pM\n", tun->dev->name, ifr.ifr_hwaddr.sa_data); - rtnl_lock(); ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr); - rtnl_unlock(); break; case TUNGETSNDBUF: - sndbuf = tun->sk->sk_sndbuf; + sndbuf = tun->socket.sk->sk_sndbuf; if (copy_to_user(argp, &sndbuf, sizeof(sndbuf))) ret = -EFAULT; break; @@ -1265,7 +1262,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, break; } - tun->sk->sk_sndbuf = sndbuf; + tun->socket.sk->sk_sndbuf = sndbuf; break; default: @@ -1273,7 +1270,10 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, break; }; - tun_put(tun); +unlock: + rtnl_unlock(); + if (tun) + tun_put(tun); return ret; } @@ -1345,7 +1345,7 @@ static int tun_chr_close(struct inode *inode, struct file *file) tun = tfile->tun; if (tun) - sock_put(tun->sk); + sock_put(tun->socket.sk); put_net(tfile->net); kfree(tfile); @@ -1361,7 +1361,7 @@ static const struct file_operations tun_fops = { .write = do_sync_write, .aio_write = tun_chr_aio_write, .poll = tun_chr_poll, - .ioctl = tun_chr_ioctl, + .unlocked_ioctl = tun_chr_ioctl, .open = tun_chr_open, .release = tun_chr_close, .fasync = tun_chr_fasync |