summaryrefslogtreecommitdiff
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c91
1 files changed, 78 insertions, 13 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 28cfa642e39a..a1ba262f40ad 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 */
@@ -1613,7 +1614,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();
@@ -1671,8 +1671,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;
@@ -1699,14 +1705,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();
@@ -2287,11 +2285,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)
@@ -2783,6 +2837,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;
@@ -2791,7 +2846,8 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
int le;
int ret;
- 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 {
@@ -2811,6 +2867,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)
@@ -2818,7 +2875,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;
@@ -2840,6 +2897,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)