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

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
...@@ -53,6 +53,7 @@ struct netpoll_info; ...@@ -53,6 +53,7 @@ struct netpoll_info;
struct device; struct device;
struct phy_device; struct phy_device;
struct dsa_port; struct dsa_port;
struct ip_tunnel_parm;
struct macsec_context; struct macsec_context;
struct macsec_ops; struct macsec_ops;
...@@ -1274,6 +1275,9 @@ struct netdev_net_notifier { ...@@ -1274,6 +1275,9 @@ struct netdev_net_notifier {
* Get devlink port instance associated with a given netdev. * Get devlink port instance associated with a given netdev.
* Called with a reference on the netdevice and devlink locks only, * Called with a reference on the netdevice and devlink locks only,
* rtnl_lock is not held. * 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 { struct net_device_ops {
int (*ndo_init)(struct net_device *dev); int (*ndo_init)(struct net_device *dev);
...@@ -1479,6 +1483,8 @@ struct net_device_ops { ...@@ -1479,6 +1483,8 @@ struct net_device_ops {
int (*ndo_xsk_wakeup)(struct net_device *dev, int (*ndo_xsk_wakeup)(struct net_device *dev,
u32 queue_id, u32 flags); u32 queue_id, u32 flags);
struct devlink_port * (*ndo_get_devlink_port)(struct net_device *dev); 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);
}; };
/** /**
......
...@@ -269,7 +269,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, ...@@ -269,7 +269,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
const struct iphdr *tnl_params, const u8 protocol); const struct iphdr *tnl_params, const u8 protocol);
void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
const u8 proto, int tunnel_hlen); 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, bool strict);
int ip_tunnel_change_mtu(struct net_device *dev, int new_mtu); int ip_tunnel_change_mtu(struct net_device *dev, int new_mtu);
......
...@@ -768,45 +768,37 @@ static void ipgre_link_update(struct net_device *dev, bool set_mtu) ...@@ -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, static int ipgre_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm *p,
struct ifreq *ifr, int cmd) int cmd)
{ {
struct ip_tunnel_parm p;
int err; int err;
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
return -EFAULT;
if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) { if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
if (p.iph.version != 4 || p.iph.protocol != IPPROTO_GRE || if (p->iph.version != 4 || p->iph.protocol != IPPROTO_GRE ||
p.iph.ihl != 5 || (p.iph.frag_off & htons(~IP_DF)) || p->iph.ihl != 5 || (p->iph.frag_off & htons(~IP_DF)) ||
((p.i_flags | p.o_flags) & (GRE_VERSION | GRE_ROUTING))) ((p->i_flags | p->o_flags) & (GRE_VERSION | GRE_ROUTING)))
return -EINVAL; return -EINVAL;
} }
p.i_flags = gre_flags_to_tnl_flags(p.i_flags); p->i_flags = gre_flags_to_tnl_flags(p->i_flags);
p.o_flags = gre_flags_to_tnl_flags(p.o_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) if (err)
return err; return err;
if (cmd == SIOCCHGTUNNEL) { if (cmd == SIOCCHGTUNNEL) {
struct ip_tunnel *t = netdev_priv(dev); struct ip_tunnel *t = netdev_priv(dev);
t->parms.i_flags = p.i_flags; t->parms.i_flags = p->i_flags;
t->parms.o_flags = p.o_flags; t->parms.o_flags = p->o_flags;
if (strcmp(dev->rtnl_link_ops->kind, "erspan")) if (strcmp(dev->rtnl_link_ops->kind, "erspan"))
ipgre_link_update(dev, true); ipgre_link_update(dev, true);
} }
p.i_flags = gre_tnl_flags_to_gre_flags(p.i_flags); p->i_flags = gre_tnl_flags_to_gre_flags(p->i_flags);
p.o_flags = gre_tnl_flags_to_gre_flags(p.o_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;
return 0; return 0;
} }
...@@ -924,10 +916,11 @@ static const struct net_device_ops ipgre_netdev_ops = { ...@@ -924,10 +916,11 @@ static const struct net_device_ops ipgre_netdev_ops = {
.ndo_stop = ipgre_close, .ndo_stop = ipgre_close,
#endif #endif
.ndo_start_xmit = ipgre_xmit, .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_change_mtu = ip_tunnel_change_mtu,
.ndo_get_stats64 = ip_tunnel_get_stats64, .ndo_get_stats64 = ip_tunnel_get_stats64,
.ndo_get_iflink = ip_tunnel_get_iflink, .ndo_get_iflink = ip_tunnel_get_iflink,
.ndo_tunnel_ctl = ipgre_tunnel_ctl,
}; };
#define GRE_FEATURES (NETIF_F_SG | \ #define GRE_FEATURES (NETIF_F_SG | \
......
...@@ -860,7 +860,7 @@ static void ip_tunnel_update(struct ip_tunnel_net *itn, ...@@ -860,7 +860,7 @@ static void ip_tunnel_update(struct ip_tunnel_net *itn,
netdev_state_change(dev); 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; int err = 0;
struct ip_tunnel *t = netdev_priv(dev); 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) ...@@ -960,6 +960,20 @@ int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd)
done: done:
return err; 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); EXPORT_SYMBOL_GPL(ip_tunnel_ioctl);
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, bool strict)
......
...@@ -378,38 +378,31 @@ static int vti4_err(struct sk_buff *skb, u32 info) ...@@ -378,38 +378,31 @@ static int vti4_err(struct sk_buff *skb, u32 info)
} }
static int 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; 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 (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP || if (p->iph.version != 4 || p->iph.protocol != IPPROTO_IPIP ||
p.iph.ihl != 5) p->iph.ihl != 5)
return -EINVAL; return -EINVAL;
} }
if (!(p.i_flags & GRE_KEY)) if (!(p->i_flags & GRE_KEY))
p.i_key = 0; p->i_key = 0;
if (!(p.o_flags & GRE_KEY)) if (!(p->o_flags & GRE_KEY))
p.o_key = 0; 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) if (err)
return err; return err;
if (cmd != SIOCDELTUNNEL) { if (cmd != SIOCDELTUNNEL) {
p.i_flags |= GRE_KEY; p->i_flags |= GRE_KEY;
p.o_flags |= GRE_KEY; p->o_flags |= GRE_KEY;
} }
if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
return -EFAULT;
return 0; return 0;
} }
...@@ -417,10 +410,11 @@ static const struct net_device_ops vti_netdev_ops = { ...@@ -417,10 +410,11 @@ static const struct net_device_ops vti_netdev_ops = {
.ndo_init = vti_tunnel_init, .ndo_init = vti_tunnel_init,
.ndo_uninit = ip_tunnel_uninit, .ndo_uninit = ip_tunnel_uninit,
.ndo_start_xmit = vti_tunnel_xmit, .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_change_mtu = ip_tunnel_change_mtu,
.ndo_get_stats64 = ip_tunnel_get_stats64, .ndo_get_stats64 = ip_tunnel_get_stats64,
.ndo_get_iflink = ip_tunnel_get_iflink, .ndo_get_iflink = ip_tunnel_get_iflink,
.ndo_tunnel_ctl = vti_tunnel_ctl,
}; };
static void vti_tunnel_setup(struct net_device *dev) static void vti_tunnel_setup(struct net_device *dev)
......
...@@ -327,41 +327,29 @@ static bool ipip_tunnel_ioctl_verify_protocol(u8 ipproto) ...@@ -327,41 +327,29 @@ static bool ipip_tunnel_ioctl_verify_protocol(u8 ipproto)
} }
static int static int
ipip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ipip_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 (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
if (p.iph.version != 4 || if (p->iph.version != 4 ||
!ipip_tunnel_ioctl_verify_protocol(p.iph.protocol) || !ipip_tunnel_ioctl_verify_protocol(p->iph.protocol) ||
p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF))) p->iph.ihl != 5 || (p->iph.frag_off & htons(~IP_DF)))
return -EINVAL; return -EINVAL;
} }
p.i_key = p.o_key = 0; p->i_key = p->o_key = 0;
p.i_flags = p.o_flags = 0; p->i_flags = p->o_flags = 0;
err = ip_tunnel_ioctl(dev, &p, cmd); return ip_tunnel_ctl(dev, p, cmd);
if (err)
return err;
if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
return -EFAULT;
return 0;
} }
static const struct net_device_ops ipip_netdev_ops = { static const struct net_device_ops ipip_netdev_ops = {
.ndo_init = ipip_tunnel_init, .ndo_init = ipip_tunnel_init,
.ndo_uninit = ip_tunnel_uninit, .ndo_uninit = ip_tunnel_uninit,
.ndo_start_xmit = ipip_tunnel_xmit, .ndo_start_xmit = ipip_tunnel_xmit,
.ndo_do_ioctl = ipip_tunnel_ioctl, .ndo_do_ioctl = ip_tunnel_ioctl,
.ndo_change_mtu = ip_tunnel_change_mtu, .ndo_change_mtu = ip_tunnel_change_mtu,
.ndo_get_stats64 = ip_tunnel_get_stats64, .ndo_get_stats64 = ip_tunnel_get_stats64,
.ndo_get_iflink = ip_tunnel_get_iflink, .ndo_get_iflink = ip_tunnel_get_iflink,
.ndo_tunnel_ctl = ipip_tunnel_ctl,
}; };
#define IPIP_FEATURES (NETIF_F_SG | \ #define IPIP_FEATURES (NETIF_F_SG | \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment