ipv6: streamline addrconf_set_dstaddr
authorChristoph Hellwig <hch@lst.de>
Tue, 19 May 2020 13:03:18 +0000 (15:03 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 19 May 2020 22:45:12 +0000 (15:45 -0700)
Factor out a addrconf_set_sit_dstaddr helper for the actual work if we
found a SIT device, and only hold the rtnl lock around the device lookup
and that new helper, as there is no point in holding it over a
copy_from_user call.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/addrconf.c

index 8300176..c827edf 100644 (file)
@@ -2783,6 +2783,38 @@ put:
        in6_dev_put(in6_dev);
 }
 
+static int addrconf_set_sit_dstaddr(struct net *net, struct net_device *dev,
+               struct in6_ifreq *ireq)
+{
+       struct ip_tunnel_parm p = { };
+       mm_segment_t oldfs = get_fs();
+       struct ifreq ifr;
+       int err;
+
+       if (!(ipv6_addr_type(&ireq->ifr6_addr) & IPV6_ADDR_COMPATv4))
+               return -EADDRNOTAVAIL;
+
+       p.iph.daddr = ireq->ifr6_addr.s6_addr32[3];
+       p.iph.version = 4;
+       p.iph.ihl = 5;
+       p.iph.protocol = IPPROTO_IPV6;
+       p.iph.ttl = 64;
+       ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
+
+       if (!dev->netdev_ops->ndo_do_ioctl)
+               return -EOPNOTSUPP;
+       set_fs(KERNEL_DS);
+       err = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
+       set_fs(oldfs);
+       if (err)
+               return err;
+
+       dev = __dev_get_by_name(net, p.name);
+       if (!dev)
+               return -ENOBUFS;
+       return dev_open(dev, NULL);
+}
+
 /*
  *     Set destination address.
  *     Special case for SIT interfaces where we create a new "virtual"
@@ -2790,62 +2822,19 @@ put:
  */
 int addrconf_set_dstaddr(struct net *net, void __user *arg)
 {
-       struct in6_ifreq ireq;
        struct net_device *dev;
-       int err = -EINVAL;
+       struct in6_ifreq ireq;
+       int err = -ENODEV;
 
        if (!IS_ENABLED(CONFIG_IPV6_SIT))
                return -ENODEV;
-
-       rtnl_lock();
-
-       err = -EFAULT;
        if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
-               goto err_exit;
+               return -EFAULT;
 
+       rtnl_lock();
        dev = __dev_get_by_index(net, ireq.ifr6_ifindex);
-
-       err = -ENODEV;
-       if (!dev)
-               goto err_exit;
-
-       if (dev->type == ARPHRD_SIT) {
-               const struct net_device_ops *ops = dev->netdev_ops;
-               struct ifreq ifr;
-               struct ip_tunnel_parm p;
-
-               err = -EADDRNOTAVAIL;
-               if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4))
-                       goto err_exit;
-
-               memset(&p, 0, sizeof(p));
-               p.iph.daddr = ireq.ifr6_addr.s6_addr32[3];
-               p.iph.saddr = 0;
-               p.iph.version = 4;
-               p.iph.ihl = 5;
-               p.iph.protocol = IPPROTO_IPV6;
-               p.iph.ttl = 64;
-               ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
-
-               if (ops->ndo_do_ioctl) {
-                       mm_segment_t oldfs = get_fs();
-
-                       set_fs(KERNEL_DS);
-                       err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
-                       set_fs(oldfs);
-               } else
-                       err = -EOPNOTSUPP;
-
-               if (err == 0) {
-                       err = -ENOBUFS;
-                       dev = __dev_get_by_name(net, p.name);
-                       if (!dev)
-                               goto err_exit;
-                       err = dev_open(dev, NULL);
-               }
-       }
-
-err_exit:
+       if (dev && dev->type == ARPHRD_SIT)
+               err = addrconf_set_sit_dstaddr(net, dev, &ireq);
        rtnl_unlock();
        return err;
 }