Commit 68ad6886 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by David S. Miller
Browse files

ipv6: streamline addrconf_set_dstaddr



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: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f0988460
Loading
Loading
Loading
Loading
+38 −49
Original line number Diff line number Diff line
@@ -2783,69 +2783,58 @@ put:
	in6_dev_put(in6_dev);
}

/*
 *	Set destination address.
 *	Special case for SIT interfaces where we create a new "virtual"
 *	device.
 */
int addrconf_set_dstaddr(struct net *net, void __user *arg)
static int addrconf_set_sit_dstaddr(struct net *net, struct net_device *dev,
		struct in6_ifreq *ireq)
{
	struct in6_ifreq ireq;
	struct net_device *dev;
	int err = -EINVAL;

	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;

	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 ip_tunnel_parm p = { };
	mm_segment_t oldfs = get_fs();
	struct ifreq ifr;
		struct ip_tunnel_parm p;
	int err;

		err = -EADDRNOTAVAIL;
		if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4))
			goto err_exit;
	if (!(ipv6_addr_type(&ireq->ifr6_addr) & IPV6_ADDR_COMPATv4))
		return -EADDRNOTAVAIL;

		memset(&p, 0, sizeof(p));
		p.iph.daddr = ireq.ifr6_addr.s6_addr32[3];
		p.iph.saddr = 0;
	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 (ops->ndo_do_ioctl) {
			mm_segment_t oldfs = get_fs();

	if (!dev->netdev_ops->ndo_do_ioctl)
		return -EOPNOTSUPP;
	set_fs(KERNEL_DS);
			err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
	err = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
	set_fs(oldfs);
		} else
			err = -EOPNOTSUPP;
	if (err)
		return err;

		if (err == 0) {
			err = -ENOBUFS;
	dev = __dev_get_by_name(net, p.name);
	if (!dev)
				goto err_exit;
			err = dev_open(dev, NULL);
		}
		return -ENOBUFS;
	return dev_open(dev, NULL);
}

err_exit:
/*
 *	Set destination address.
 *	Special case for SIT interfaces where we create a new "virtual"
 *	device.
 */
int addrconf_set_dstaddr(struct net *net, void __user *arg)
{
	struct net_device *dev;
	struct in6_ifreq ireq;
	int err = -ENODEV;

	if (!IS_ENABLED(CONFIG_IPV6_SIT))
		return -ENODEV;
	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
		return -EFAULT;

	rtnl_lock();
	dev = __dev_get_by_index(net, ireq.ifr6_ifindex);
	if (dev && dev->type == ARPHRD_SIT)
		err = addrconf_set_sit_dstaddr(net, dev, &ireq);
	rtnl_unlock();
	return err;
}