Commit 607259a6 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by David S. Miller
Browse files

net: add a new ndo_tunnel_ioctl method



This method is used to properly allow kernel callers of the IPv4 route
management ioctls.  The exsting ip_tunnel_ioctl helper is renamed to
ip_tunnel_ctl to better reflect that it doesn't directly implement ioctls
touching user memory, and is used for the guts of ndo_tunnel_ctl
implementations. A new ip_tunnel_ioctl helper is added that can be wired
up directly to the ndo_do_ioctl method and takes care of the copy to and
from userspace.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c1fd1182
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ struct netpoll_info;
struct device;
struct phy_device;
struct dsa_port;
struct ip_tunnel_parm;
struct macsec_context;
struct macsec_ops;

@@ -1274,6 +1275,9 @@ struct netdev_net_notifier {
 *	Get devlink port instance associated with a given netdev.
 *	Called with a reference on the netdevice and devlink locks only,
 *	rtnl_lock is not held.
 * int (*ndo_tunnel_ctl)(struct net_device *dev, struct ip_tunnel_parm *p,
 *			 int cmd);
 *	Add, change, delete or get information on an IPv4 tunnel.
 */
struct net_device_ops {
	int			(*ndo_init)(struct net_device *dev);
@@ -1479,6 +1483,8 @@ struct net_device_ops {
	int			(*ndo_xsk_wakeup)(struct net_device *dev,
						  u32 queue_id, u32 flags);
	struct devlink_port *	(*ndo_get_devlink_port)(struct net_device *dev);
	int			(*ndo_tunnel_ctl)(struct net_device *dev,
						  struct ip_tunnel_parm *p, int cmd);
};

/**
+2 −1
Original line number Diff line number Diff line
@@ -269,7 +269,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
		    const struct iphdr *tnl_params, const u8 protocol);
void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
		       const u8 proto, int tunnel_hlen);
int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd);
int ip_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd);
int ip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
int __ip_tunnel_change_mtu(struct net_device *dev, int new_mtu, bool strict);
int ip_tunnel_change_mtu(struct net_device *dev, int new_mtu);

+14 −21
Original line number Diff line number Diff line
@@ -768,45 +768,37 @@ static void ipgre_link_update(struct net_device *dev, bool set_mtu)
	}
}

static int ipgre_tunnel_ioctl(struct net_device *dev,
			      struct ifreq *ifr, int cmd)
static int ipgre_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm *p,
			    int cmd)
{
	struct ip_tunnel_parm p;
	int err;

	if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
		return -EFAULT;

	if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
		if (p.iph.version != 4 || p.iph.protocol != IPPROTO_GRE ||
		    p.iph.ihl != 5 || (p.iph.frag_off & htons(~IP_DF)) ||
		    ((p.i_flags | p.o_flags) & (GRE_VERSION | GRE_ROUTING)))
		if (p->iph.version != 4 || p->iph.protocol != IPPROTO_GRE ||
		    p->iph.ihl != 5 || (p->iph.frag_off & htons(~IP_DF)) ||
		    ((p->i_flags | p->o_flags) & (GRE_VERSION | GRE_ROUTING)))
			return -EINVAL;
	}

	p.i_flags = gre_flags_to_tnl_flags(p.i_flags);
	p.o_flags = gre_flags_to_tnl_flags(p.o_flags);
	p->i_flags = gre_flags_to_tnl_flags(p->i_flags);
	p->o_flags = gre_flags_to_tnl_flags(p->o_flags);

	err = ip_tunnel_ioctl(dev, &p, cmd);
	err = ip_tunnel_ctl(dev, p, cmd);
	if (err)
		return err;

	if (cmd == SIOCCHGTUNNEL) {
		struct ip_tunnel *t = netdev_priv(dev);

		t->parms.i_flags = p.i_flags;
		t->parms.o_flags = p.o_flags;
		t->parms.i_flags = p->i_flags;
		t->parms.o_flags = p->o_flags;

		if (strcmp(dev->rtnl_link_ops->kind, "erspan"))
			ipgre_link_update(dev, true);
	}

	p.i_flags = gre_tnl_flags_to_gre_flags(p.i_flags);
	p.o_flags = gre_tnl_flags_to_gre_flags(p.o_flags);

	if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
		return -EFAULT;

	p->i_flags = gre_tnl_flags_to_gre_flags(p->i_flags);
	p->o_flags = gre_tnl_flags_to_gre_flags(p->o_flags);
	return 0;
}

@@ -924,10 +916,11 @@ static const struct net_device_ops ipgre_netdev_ops = {
	.ndo_stop		= ipgre_close,
#endif
	.ndo_start_xmit		= ipgre_xmit,
	.ndo_do_ioctl		= ipgre_tunnel_ioctl,
	.ndo_do_ioctl		= ip_tunnel_ioctl,
	.ndo_change_mtu		= ip_tunnel_change_mtu,
	.ndo_get_stats64	= ip_tunnel_get_stats64,
	.ndo_get_iflink		= ip_tunnel_get_iflink,
	.ndo_tunnel_ctl		= ipgre_tunnel_ctl,
};

#define GRE_FEATURES (NETIF_F_SG |		\
+15 −1
Original line number Diff line number Diff line
@@ -860,7 +860,7 @@ static void ip_tunnel_update(struct ip_tunnel_net *itn,
	netdev_state_change(dev);
}

int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd)
int ip_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd)
{
	int err = 0;
	struct ip_tunnel *t = netdev_priv(dev);
@@ -960,6 +960,20 @@ int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd)
done:
	return err;
}
EXPORT_SYMBOL_GPL(ip_tunnel_ctl);

int ip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
	struct ip_tunnel_parm p;
	int err;

	if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
		return -EFAULT;
	err = dev->netdev_ops->ndo_tunnel_ctl(dev, &p, cmd);
	if (!err && copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
		return -EFAULT;
	return err;
}
EXPORT_SYMBOL_GPL(ip_tunnel_ioctl);

int __ip_tunnel_change_mtu(struct net_device *dev, int new_mtu, bool strict)
+13 −19
Original line number Diff line number Diff line
@@ -378,38 +378,31 @@ static int vti4_err(struct sk_buff *skb, u32 info)
}

static int
vti_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
vti_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd)
{
	int err = 0;
	struct ip_tunnel_parm p;

	if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
		return -EFAULT;

	if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
		if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP ||
		    p.iph.ihl != 5)
		if (p->iph.version != 4 || p->iph.protocol != IPPROTO_IPIP ||
		    p->iph.ihl != 5)
			return -EINVAL;
	}

	if (!(p.i_flags & GRE_KEY))
		p.i_key = 0;
	if (!(p.o_flags & GRE_KEY))
		p.o_key = 0;
	if (!(p->i_flags & GRE_KEY))
		p->i_key = 0;
	if (!(p->o_flags & GRE_KEY))
		p->o_key = 0;

	p.i_flags = VTI_ISVTI;
	p->i_flags = VTI_ISVTI;

	err = ip_tunnel_ioctl(dev, &p, cmd);
	err = ip_tunnel_ctl(dev, p, cmd);
	if (err)
		return err;

	if (cmd != SIOCDELTUNNEL) {
		p.i_flags |= GRE_KEY;
		p.o_flags |= GRE_KEY;
		p->i_flags |= GRE_KEY;
		p->o_flags |= GRE_KEY;
	}

	if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
		return -EFAULT;
	return 0;
}

@@ -417,10 +410,11 @@ static const struct net_device_ops vti_netdev_ops = {
	.ndo_init	= vti_tunnel_init,
	.ndo_uninit	= ip_tunnel_uninit,
	.ndo_start_xmit	= vti_tunnel_xmit,
	.ndo_do_ioctl	= vti_tunnel_ioctl,
	.ndo_do_ioctl	= ip_tunnel_ioctl,
	.ndo_change_mtu	= ip_tunnel_change_mtu,
	.ndo_get_stats64 = ip_tunnel_get_stats64,
	.ndo_get_iflink = ip_tunnel_get_iflink,
	.ndo_tunnel_ctl	= vti_tunnel_ctl,
};

static void vti_tunnel_setup(struct net_device *dev)
Loading