diff options
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 127 |
1 files changed, 108 insertions, 19 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 7433bb2e4451..28583aa0c17d 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -78,6 +78,7 @@ #include <linux/mutex.h> #include <linux/uaccess.h> +#include <linux/proc_fs.h> /* Uncomment to enable debugging */ /* #define TUN_DEBUG 1 */ @@ -655,7 +656,7 @@ static struct tun_struct *tun_enable_queue(struct tun_file *tfile) return tun; } -static void tun_ptr_free(void *ptr) +void tun_ptr_free(void *ptr) { if (!ptr) return; @@ -667,6 +668,7 @@ static void tun_ptr_free(void *ptr) __skb_array_destroy_skb(ptr); } } +EXPORT_SYMBOL_GPL(tun_ptr_free); static void tun_queue_purge(struct tun_file *tfile) { @@ -741,8 +743,15 @@ static void __tun_detach(struct tun_file *tfile, bool clean) static void tun_detach(struct tun_file *tfile, bool clean) { + struct tun_struct *tun; + struct net_device *dev; + rtnl_lock(); + tun = rtnl_dereference(tfile->tun); + dev = tun ? tun->dev : NULL; __tun_detach(tfile, clean); + if (dev) + netdev_state_change(dev); rtnl_unlock(); } @@ -1612,7 +1621,6 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, unsigned int delta = 0; char *buf; size_t copied; - bool xdp_xmit = false; int err, pad = TUN_RX_PAD; rcu_read_lock(); @@ -1670,8 +1678,14 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, preempt_enable(); return NULL; case XDP_TX: - xdp_xmit = true; - /* fall through */ + get_page(alloc_frag->page); + alloc_frag->offset += buflen; + if (tun_xdp_xmit(tun->dev, &xdp)) + goto err_redirect; + tun_xdp_flush(tun->dev); + rcu_read_unlock(); + preempt_enable(); + return NULL; case XDP_PASS: delta = orig_data - xdp.data; break; @@ -1698,14 +1712,6 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, get_page(alloc_frag->page); alloc_frag->offset += buflen; - if (xdp_xmit) { - skb->dev = tun->dev; - generic_xdp_tx(skb, xdp_prog); - rcu_read_unlock(); - preempt_enable(); - return NULL; - } - rcu_read_unlock(); preempt_enable(); @@ -2286,11 +2292,67 @@ static int tun_validate(struct nlattr *tb[], struct nlattr *data[], return -EINVAL; } +static size_t tun_get_size(const struct net_device *dev) +{ + BUILD_BUG_ON(sizeof(u32) != sizeof(uid_t)); + BUILD_BUG_ON(sizeof(u32) != sizeof(gid_t)); + + return nla_total_size(sizeof(uid_t)) + /* OWNER */ + nla_total_size(sizeof(gid_t)) + /* GROUP */ + nla_total_size(sizeof(u8)) + /* TYPE */ + nla_total_size(sizeof(u8)) + /* PI */ + nla_total_size(sizeof(u8)) + /* VNET_HDR */ + nla_total_size(sizeof(u8)) + /* PERSIST */ + nla_total_size(sizeof(u8)) + /* MULTI_QUEUE */ + nla_total_size(sizeof(u32)) + /* NUM_QUEUES */ + nla_total_size(sizeof(u32)) + /* NUM_DISABLED_QUEUES */ + 0; +} + +static int tun_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct tun_struct *tun = netdev_priv(dev); + + if (nla_put_u8(skb, IFLA_TUN_TYPE, tun->flags & TUN_TYPE_MASK)) + goto nla_put_failure; + if (uid_valid(tun->owner) && + nla_put_u32(skb, IFLA_TUN_OWNER, + from_kuid_munged(current_user_ns(), tun->owner))) + goto nla_put_failure; + if (gid_valid(tun->group) && + nla_put_u32(skb, IFLA_TUN_GROUP, + from_kgid_munged(current_user_ns(), tun->group))) + goto nla_put_failure; + if (nla_put_u8(skb, IFLA_TUN_PI, !(tun->flags & IFF_NO_PI))) + goto nla_put_failure; + if (nla_put_u8(skb, IFLA_TUN_VNET_HDR, !!(tun->flags & IFF_VNET_HDR))) + goto nla_put_failure; + if (nla_put_u8(skb, IFLA_TUN_PERSIST, !!(tun->flags & IFF_PERSIST))) + goto nla_put_failure; + if (nla_put_u8(skb, IFLA_TUN_MULTI_QUEUE, + !!(tun->flags & IFF_MULTI_QUEUE))) + goto nla_put_failure; + if (tun->flags & IFF_MULTI_QUEUE) { + if (nla_put_u32(skb, IFLA_TUN_NUM_QUEUES, tun->numqueues)) + goto nla_put_failure; + if (nla_put_u32(skb, IFLA_TUN_NUM_DISABLED_QUEUES, + tun->numdisabled)) + goto nla_put_failure; + } + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + static struct rtnl_link_ops tun_link_ops __read_mostly = { .kind = DRV_NAME, .priv_size = sizeof(struct tun_struct), .setup = tun_setup, .validate = tun_validate, + .get_size = tun_get_size, + .fill_info = tun_fill_info, }; static void tun_sock_write_space(struct sock *sk) @@ -2507,10 +2569,15 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) /* One or more queue has already been attached, no need * to initialize the device again. */ + netdev_state_change(dev); return 0; } - } - else { + + tun->flags = (tun->flags & ~TUN_FEATURES) | + (ifr->ifr_flags & TUN_FEATURES); + + netdev_state_change(dev); + } else { char *name; unsigned long flags = 0; int queues = ifr->ifr_flags & IFF_MULTI_QUEUE ? @@ -2587,6 +2654,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) ~(NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX); + tun->flags = (tun->flags & ~TUN_FEATURES) | + (ifr->ifr_flags & TUN_FEATURES); + INIT_LIST_HEAD(&tun->disabled); err = tun_attach(tun, file, false, ifr->ifr_flags & IFF_NAPI); if (err < 0) @@ -2601,9 +2671,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) tun_debug(KERN_INFO, tun, "tun_set_iff\n"); - tun->flags = (tun->flags & ~TUN_FEATURES) | - (ifr->ifr_flags & TUN_FEATURES); - /* Make sure persistent devices do not get stuck in * xoff state. */ @@ -2750,6 +2817,9 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr) } else ret = -EINVAL; + if (ret >= 0) + netdev_state_change(tun->dev); + unlock: rtnl_unlock(); return ret; @@ -2782,6 +2852,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, struct tun_struct *tun; void __user* argp = (void __user*)arg; struct ifreq ifr; + struct net *net; kuid_t owner; kgid_t group; int sndbuf; @@ -2789,8 +2860,10 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, unsigned int ifindex; int le; int ret; + bool do_notify = false; - if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == SOCK_IOC_TYPE) { + if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || + (_IOC_TYPE(cmd) == SOCK_IOC_TYPE && cmd != SIOCGSKNS)) { if (copy_from_user(&ifr, argp, ifreq_len)) return -EFAULT; } else { @@ -2810,6 +2883,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, rtnl_lock(); tun = tun_get(tfile); + net = sock_net(&tfile->sk); if (cmd == TUNSETIFF) { ret = -EEXIST; if (tun) @@ -2817,7 +2891,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, ifr.ifr_name[IFNAMSIZ-1] = '\0'; - ret = tun_set_iff(sock_net(&tfile->sk), file, &ifr); + ret = tun_set_iff(net, file, &ifr); if (ret) goto unlock; @@ -2839,6 +2913,14 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, tfile->ifindex = ifindex; goto unlock; } + if (cmd == SIOCGSKNS) { + ret = -EPERM; + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) + goto unlock; + + ret = open_related_ns(&net->ns, get_net_ns); + goto unlock; + } ret = -EBADFD; if (!tun) @@ -2875,10 +2957,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, if (arg && !(tun->flags & IFF_PERSIST)) { tun->flags |= IFF_PERSIST; __module_get(THIS_MODULE); + do_notify = true; } if (!arg && (tun->flags & IFF_PERSIST)) { tun->flags &= ~IFF_PERSIST; module_put(THIS_MODULE); + do_notify = true; } tun_debug(KERN_INFO, tun, "persist %s\n", @@ -2893,6 +2977,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, break; } tun->owner = owner; + do_notify = true; tun_debug(KERN_INFO, tun, "owner set to %u\n", from_kuid(&init_user_ns, tun->owner)); break; @@ -2905,6 +2990,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, break; } tun->group = group; + do_notify = true; tun_debug(KERN_INFO, tun, "group set to %u\n", from_kgid(&init_user_ns, tun->group)); break; @@ -3064,6 +3150,9 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, break; } + if (do_notify) + netdev_state_change(tun->dev); + unlock: rtnl_unlock(); if (tun) |