]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/tun.c
mlxsw: spectrum_router: Correctly remove nexthop groups
[karo-tx-linux.git] / drivers / net / tun.c
index 6f9df375c5d411dc52510b95ec677f79f1996e4e..57e88b8147002237fd34ec39ebeb33093e6503e0 100644 (file)
@@ -731,14 +731,9 @@ static int update_filter(struct tap_filter *filter, void __user *arg)
        }
 
        alen = ETH_ALEN * uf.count;
-       addr = kmalloc(alen, GFP_KERNEL);
-       if (!addr)
-               return -ENOMEM;
-
-       if (copy_from_user(addr, arg + sizeof(uf), alen)) {
-               err = -EFAULT;
-               goto done;
-       }
+       addr = memdup_user(arg + sizeof(uf), alen);
+       if (IS_ERR(addr))
+               return PTR_ERR(addr);
 
        /* The filter is updated without holding any locks. Which is
         * perfectly safe. We disable it first and in the worst
@@ -758,7 +753,7 @@ static int update_filter(struct tap_filter *filter, void __user *arg)
        for (; n < uf.count; n++) {
                if (!is_multicast_ether_addr(addr[n].u)) {
                        err = 0; /* no filter */
-                       goto done;
+                       goto free_addr;
                }
                addr_hash_set(filter->mask, addr[n].u);
        }
@@ -774,8 +769,7 @@ static int update_filter(struct tap_filter *filter, void __user *arg)
 
        /* Return the number of exact filters */
        err = nexact;
-
-done:
+free_addr:
        kfree(addr);
        return err;
 }
@@ -884,13 +878,6 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
            sk_filter(tfile->socket.sk, skb))
                goto drop;
 
-       /* Limit the number of packets queued by dividing txq length with the
-        * number of queues.
-        */
-       if (skb_queue_len(&tfile->socket.sk->sk_receive_queue) * numqueues
-                         >= dev->tx_queue_len)
-               goto drop;
-
        if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
                goto drop;
 
@@ -931,18 +918,6 @@ static void tun_net_mclist(struct net_device *dev)
         */
 }
 
-#define MIN_MTU 68
-#define MAX_MTU 65535
-
-static int
-tun_net_change_mtu(struct net_device *dev, int new_mtu)
-{
-       if (new_mtu < MIN_MTU || new_mtu + dev->hard_header_len > MAX_MTU)
-               return -EINVAL;
-       dev->mtu = new_mtu;
-       return 0;
-}
-
 static netdev_features_t tun_net_fix_features(struct net_device *dev,
        netdev_features_t features)
 {
@@ -1020,7 +995,6 @@ static const struct net_device_ops tun_netdev_ops = {
        .ndo_open               = tun_net_open,
        .ndo_stop               = tun_net_close,
        .ndo_start_xmit         = tun_net_xmit,
-       .ndo_change_mtu         = tun_net_change_mtu,
        .ndo_fix_features       = tun_net_fix_features,
        .ndo_select_queue       = tun_select_queue,
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -1035,7 +1009,6 @@ static const struct net_device_ops tap_netdev_ops = {
        .ndo_open               = tun_net_open,
        .ndo_stop               = tun_net_close,
        .ndo_start_xmit         = tun_net_xmit,
-       .ndo_change_mtu         = tun_net_change_mtu,
        .ndo_fix_features       = tun_net_fix_features,
        .ndo_set_rx_mode        = tun_net_mclist,
        .ndo_set_mac_address    = eth_mac_addr,
@@ -1068,6 +1041,9 @@ static void tun_flow_uninit(struct tun_struct *tun)
        tun_flow_flush(tun);
 }
 
+#define MIN_MTU 68
+#define MAX_MTU 65535
+
 /* Initialize net device. */
 static void tun_net_init(struct net_device *dev)
 {
@@ -1098,6 +1074,9 @@ static void tun_net_init(struct net_device *dev)
 
                break;
        }
+
+       dev->min_mtu = MIN_MTU;
+       dev->max_mtu = MAX_MTU - dev->hard_header_len;
 }
 
 /* Character device part */
@@ -1177,7 +1156,6 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
        bool zerocopy = false;
        int err;
        u32 rxhash;
-       ssize_t n;
 
        if (!(tun->dev->flags & IFF_UP))
                return -EIO;
@@ -1187,8 +1165,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
                        return -EINVAL;
                len -= sizeof(pi);
 
-               n = copy_from_iter(&pi, sizeof(pi), from);
-               if (n != sizeof(pi))
+               if (!copy_from_iter_full(&pi, sizeof(pi), from))
                        return -EFAULT;
        }
 
@@ -1197,8 +1174,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
                        return -EINVAL;
                len -= tun->vnet_hdr_sz;
 
-               n = copy_from_iter(&gso, sizeof(gso), from);
-               if (n != sizeof(gso))
+               if (!copy_from_iter_full(&gso, sizeof(gso), from))
                        return -EFAULT;
 
                if ((gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
@@ -1252,13 +1228,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
 
        if (zerocopy)
                err = zerocopy_sg_from_iter(skb, from);
-       else {
+       else
                err = skb_copy_datagram_from_iter(skb, 0, from, len);
-               if (!err && msg_control) {
-                       struct ubuf_info *uarg = msg_control;
-                       uarg->callback(uarg, false);
-               }
-       }
 
        if (err) {
                this_cpu_inc(tun->pcpu_stats->rx_dropped);
@@ -1266,8 +1237,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
                return -EFAULT;
        }
 
-       err = virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun));
-       if (err) {
+       if (virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun))) {
                this_cpu_inc(tun->pcpu_stats->rx_frame_errors);
                kfree_skb(skb);
                return -EINVAL;
@@ -1304,13 +1274,22 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
                skb_shinfo(skb)->destructor_arg = msg_control;
                skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
                skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
+       } else if (msg_control) {
+               struct ubuf_info *uarg = msg_control;
+               uarg->callback(uarg, false);
        }
 
        skb_reset_network_header(skb);
        skb_probe_transport_header(skb, 0);
 
        rxhash = skb_get_hash(skb);
+#ifndef CONFIG_4KSTACKS
+       local_bh_disable();
+       netif_receive_skb(skb);
+       local_bh_enable();
+#else
        netif_rx_ni(skb);
+#endif
 
        stats = get_cpu_ptr(tun->pcpu_stats);
        u64_stats_update_begin(&stats->syncp);
@@ -1375,15 +1354,13 @@ static ssize_t tun_put_user(struct tun_struct *tun,
        }
 
        if (vnet_hdr_sz) {
-               struct virtio_net_hdr gso = { 0 }; /* no info leak */
-               int ret;
+               struct virtio_net_hdr gso;
 
                if (iov_iter_count(iter) < vnet_hdr_sz)
                        return -EINVAL;
 
-               ret = virtio_net_hdr_from_skb(skb, &gso,
-                                             tun_is_little_endian(tun));
-               if (ret) {
+               if (virtio_net_hdr_from_skb(skb, &gso,
+                                           tun_is_little_endian(tun))) {
                        struct skb_shared_info *sinfo = skb_shinfo(skb);
                        pr_err("unexpected GSO type: "
                               "0x%x, gso_size %d, hdr_len %d\n",
@@ -1999,7 +1976,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
        int le;
        int ret;
 
-       if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == 0x89) {
+       if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == SOCK_IOC_TYPE) {
                if (copy_from_user(&ifr, argp, ifreq_len))
                        return -EFAULT;
        } else {
@@ -2019,7 +1996,11 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
        rtnl_lock();
 
        tun = __tun_get(tfile);
-       if (cmd == TUNSETIFF && !tun) {
+       if (cmd == TUNSETIFF) {
+               ret = -EEXIST;
+               if (tun)
+                       goto unlock;
+
                ifr.ifr_name[IFNAMSIZ-1] = '\0';
 
                ret = tun_set_iff(sock_net(&tfile->sk), file, &ifr);