sit: refactor ipip6_tunnel_ioctl
Split the ioctl handler into one function per command instead of having a all the logic sit in one giant switch statement. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
c7e3670516
commit
fd5d687b76
342
net/ipv6/sit.c
342
net/ipv6/sit.c
@@ -83,6 +83,13 @@ struct sit_net {
|
|||||||
struct net_device *fb_tunnel_dev;
|
struct net_device *fb_tunnel_dev;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline struct sit_net *dev_to_sit_net(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct ip_tunnel *t = netdev_priv(dev);
|
||||||
|
|
||||||
|
return net_generic(t->net, sit_net_id);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Must be invoked with rcu_read_lock
|
* Must be invoked with rcu_read_lock
|
||||||
*/
|
*/
|
||||||
@@ -291,14 +298,18 @@ __ipip6_tunnel_locate_prl(struct ip_tunnel *t, __be32 addr)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ipip6_tunnel_get_prl(struct ip_tunnel *t,
|
static int ipip6_tunnel_get_prl(struct net_device *dev, struct ifreq *ifr)
|
||||||
struct ip_tunnel_prl __user *a)
|
|
||||||
{
|
{
|
||||||
|
struct ip_tunnel_prl __user *a = ifr->ifr_ifru.ifru_data;
|
||||||
|
struct ip_tunnel *t = netdev_priv(dev);
|
||||||
struct ip_tunnel_prl kprl, *kp;
|
struct ip_tunnel_prl kprl, *kp;
|
||||||
struct ip_tunnel_prl_entry *prl;
|
struct ip_tunnel_prl_entry *prl;
|
||||||
unsigned int cmax, c = 0, ca, len;
|
unsigned int cmax, c = 0, ca, len;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (dev == dev_to_sit_net(dev)->fb_tunnel_dev)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (copy_from_user(&kprl, a, sizeof(kprl)))
|
if (copy_from_user(&kprl, a, sizeof(kprl)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
cmax = kprl.datalen / sizeof(kprl);
|
cmax = kprl.datalen / sizeof(kprl);
|
||||||
@@ -441,6 +452,35 @@ out:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ipip6_tunnel_prl_ctl(struct net_device *dev, struct ifreq *ifr,
|
||||||
|
int cmd)
|
||||||
|
{
|
||||||
|
struct ip_tunnel *t = netdev_priv(dev);
|
||||||
|
struct ip_tunnel_prl prl;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!ns_capable(t->net->user_ns, CAP_NET_ADMIN))
|
||||||
|
return -EPERM;
|
||||||
|
if (dev == dev_to_sit_net(dev)->fb_tunnel_dev)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (copy_from_user(&prl, ifr->ifr_ifru.ifru_data, sizeof(prl)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case SIOCDELPRL:
|
||||||
|
err = ipip6_tunnel_del_prl(t, &prl);
|
||||||
|
break;
|
||||||
|
case SIOCADDPRL:
|
||||||
|
case SIOCCHGPRL:
|
||||||
|
err = ipip6_tunnel_add_prl(t, &prl, cmd == SIOCCHGPRL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dst_cache_reset(&t->dst_cache);
|
||||||
|
netdev_state_change(dev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
isatap_chksrc(struct sk_buff *skb, const struct iphdr *iph, struct ip_tunnel *t)
|
isatap_chksrc(struct sk_buff *skb, const struct iphdr *iph, struct ip_tunnel *t)
|
||||||
{
|
{
|
||||||
@@ -1151,7 +1191,53 @@ static int ipip6_tunnel_update_6rd(struct ip_tunnel *t,
|
|||||||
netdev_state_change(t->dev);
|
netdev_state_change(t->dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
static int
|
||||||
|
ipip6_tunnel_get6rd(struct net_device *dev, struct ifreq *ifr)
|
||||||
|
{
|
||||||
|
struct ip_tunnel *t = netdev_priv(dev);
|
||||||
|
struct ip_tunnel_6rd ip6rd;
|
||||||
|
struct ip_tunnel_parm p;
|
||||||
|
|
||||||
|
if (dev == dev_to_sit_net(dev)->fb_tunnel_dev) {
|
||||||
|
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
|
||||||
|
return -EFAULT;
|
||||||
|
t = ipip6_tunnel_locate(t->net, &p, 0);
|
||||||
|
}
|
||||||
|
if (!t)
|
||||||
|
t = netdev_priv(dev);
|
||||||
|
|
||||||
|
ip6rd.prefix = t->ip6rd.prefix;
|
||||||
|
ip6rd.relay_prefix = t->ip6rd.relay_prefix;
|
||||||
|
ip6rd.prefixlen = t->ip6rd.prefixlen;
|
||||||
|
ip6rd.relay_prefixlen = t->ip6rd.relay_prefixlen;
|
||||||
|
if (copy_to_user(ifr->ifr_ifru.ifru_data, &ip6rd, sizeof(ip6rd)))
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ipip6_tunnel_6rdctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||||
|
{
|
||||||
|
struct ip_tunnel *t = netdev_priv(dev);
|
||||||
|
struct ip_tunnel_6rd ip6rd;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!ns_capable(t->net->user_ns, CAP_NET_ADMIN))
|
||||||
|
return -EPERM;
|
||||||
|
if (copy_from_user(&ip6rd, ifr->ifr_ifru.ifru_data, sizeof(ip6rd)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (cmd != SIOCDEL6RD) {
|
||||||
|
err = ipip6_tunnel_update_6rd(t, &ip6rd);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
} else
|
||||||
|
ipip6_tunnel_clone_6rd(dev, dev_to_sit_net(dev));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_IPV6_SIT_6RD */
|
||||||
|
|
||||||
static bool ipip6_valid_ip_proto(u8 ipproto)
|
static bool ipip6_valid_ip_proto(u8 ipproto)
|
||||||
{
|
{
|
||||||
@@ -1164,185 +1250,151 @@ static bool ipip6_valid_ip_proto(u8 ipproto)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ipip6_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
__ipip6_tunnel_ioctl_validate(struct net *net, struct ip_tunnel_parm *p)
|
||||||
{
|
{
|
||||||
int err = 0;
|
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
||||||
struct ip_tunnel_parm p;
|
return -EPERM;
|
||||||
struct ip_tunnel_prl prl;
|
|
||||||
struct ip_tunnel *t = netdev_priv(dev);
|
|
||||||
struct net *net = t->net;
|
|
||||||
struct sit_net *sitn = net_generic(net, sit_net_id);
|
|
||||||
#ifdef CONFIG_IPV6_SIT_6RD
|
|
||||||
struct ip_tunnel_6rd ip6rd;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
switch (cmd) {
|
if (!ipip6_valid_ip_proto(p->iph.protocol))
|
||||||
case SIOCGETTUNNEL:
|
return -EINVAL;
|
||||||
#ifdef CONFIG_IPV6_SIT_6RD
|
if (p->iph.version != 4 ||
|
||||||
case SIOCGET6RD:
|
p->iph.ihl != 5 || (p->iph.frag_off & htons(~IP_DF)))
|
||||||
#endif
|
return -EINVAL;
|
||||||
if (dev == sitn->fb_tunnel_dev) {
|
|
||||||
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
|
if (p->iph.ttl)
|
||||||
err = -EFAULT;
|
p->iph.frag_off |= htons(IP_DF);
|
||||||
break;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ipip6_tunnel_get(struct net_device *dev, struct ifreq *ifr)
|
||||||
|
{
|
||||||
|
struct ip_tunnel *t = netdev_priv(dev);
|
||||||
|
struct ip_tunnel_parm p;
|
||||||
|
|
||||||
|
if (dev == dev_to_sit_net(dev)->fb_tunnel_dev) {
|
||||||
|
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
|
||||||
|
return -EFAULT;
|
||||||
|
t = ipip6_tunnel_locate(t->net, &p, 0);
|
||||||
}
|
}
|
||||||
t = ipip6_tunnel_locate(net, &p, 0);
|
|
||||||
if (!t)
|
if (!t)
|
||||||
t = netdev_priv(dev);
|
t = netdev_priv(dev);
|
||||||
|
|
||||||
|
if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = -EFAULT;
|
static int
|
||||||
if (cmd == SIOCGETTUNNEL) {
|
ipip6_tunnel_add(struct net_device *dev, struct ifreq *ifr)
|
||||||
memcpy(&p, &t->parms, sizeof(p));
|
{
|
||||||
if (copy_to_user(ifr->ifr_ifru.ifru_data, &p,
|
struct ip_tunnel *t = netdev_priv(dev);
|
||||||
sizeof(p)))
|
struct ip_tunnel_parm p;
|
||||||
goto done;
|
int err;
|
||||||
#ifdef CONFIG_IPV6_SIT_6RD
|
|
||||||
} else {
|
|
||||||
ip6rd.prefix = t->ip6rd.prefix;
|
|
||||||
ip6rd.relay_prefix = t->ip6rd.relay_prefix;
|
|
||||||
ip6rd.prefixlen = t->ip6rd.prefixlen;
|
|
||||||
ip6rd.relay_prefixlen = t->ip6rd.relay_prefixlen;
|
|
||||||
if (copy_to_user(ifr->ifr_ifru.ifru_data, &ip6rd,
|
|
||||||
sizeof(ip6rd)))
|
|
||||||
goto done;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
err = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SIOCADDTUNNEL:
|
|
||||||
case SIOCCHGTUNNEL:
|
|
||||||
err = -EPERM;
|
|
||||||
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
err = -EFAULT;
|
|
||||||
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
|
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
|
||||||
goto done;
|
return -EFAULT;
|
||||||
|
err = __ipip6_tunnel_ioctl_validate(t->net, &p);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
err = -EINVAL;
|
t = ipip6_tunnel_locate(t->net, &p, 1);
|
||||||
if (!ipip6_valid_ip_proto(p.iph.protocol))
|
if (!t)
|
||||||
goto done;
|
return -ENOBUFS;
|
||||||
if (p.iph.version != 4 ||
|
|
||||||
p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
|
|
||||||
goto done;
|
|
||||||
if (p.iph.ttl)
|
|
||||||
p.iph.frag_off |= htons(IP_DF);
|
|
||||||
|
|
||||||
t = ipip6_tunnel_locate(net, &p, cmd == SIOCADDTUNNEL);
|
if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
|
||||||
|
return -EFAULT;
|
||||||
if (dev != sitn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
|
return 0;
|
||||||
if (t) {
|
|
||||||
if (t->dev != dev) {
|
|
||||||
err = -EEXIST;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ipip6_tunnel_change(struct net_device *dev, struct ifreq *ifr)
|
||||||
|
{
|
||||||
|
struct ip_tunnel *t = netdev_priv(dev);
|
||||||
|
struct ip_tunnel_parm p;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
|
||||||
|
return -EFAULT;
|
||||||
|
err = __ipip6_tunnel_ioctl_validate(t->net, &p);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
t = ipip6_tunnel_locate(t->net, &p, 0);
|
||||||
|
if (dev == dev_to_sit_net(dev)->fb_tunnel_dev) {
|
||||||
|
if (!t)
|
||||||
|
return -ENOENT;
|
||||||
|
} else {
|
||||||
|
if (t) {
|
||||||
|
if (t->dev != dev)
|
||||||
|
return -EEXIST;
|
||||||
} else {
|
} else {
|
||||||
if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) ||
|
if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) ||
|
||||||
(!(dev->flags&IFF_POINTOPOINT) && p.iph.daddr)) {
|
(!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr))
|
||||||
err = -EINVAL;
|
return -EINVAL;
|
||||||
break;
|
|
||||||
}
|
|
||||||
t = netdev_priv(dev);
|
t = netdev_priv(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
ipip6_tunnel_update(t, &p, t->fwmark);
|
ipip6_tunnel_update(t, &p, t->fwmark);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t) {
|
|
||||||
err = 0;
|
|
||||||
if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
|
if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
|
||||||
err = -EFAULT;
|
return -EFAULT;
|
||||||
} else
|
return 0;
|
||||||
err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case SIOCDELTUNNEL:
|
static int
|
||||||
err = -EPERM;
|
ipip6_tunnel_del(struct net_device *dev, struct ifreq *ifr)
|
||||||
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
{
|
||||||
goto done;
|
struct ip_tunnel *t = netdev_priv(dev);
|
||||||
|
struct ip_tunnel_parm p;
|
||||||
|
|
||||||
if (dev == sitn->fb_tunnel_dev) {
|
if (!ns_capable(t->net->user_ns, CAP_NET_ADMIN))
|
||||||
err = -EFAULT;
|
return -EPERM;
|
||||||
|
|
||||||
|
if (dev == dev_to_sit_net(dev)->fb_tunnel_dev) {
|
||||||
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
|
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
|
||||||
goto done;
|
return -EFAULT;
|
||||||
err = -ENOENT;
|
t = ipip6_tunnel_locate(t->net, &p, 0);
|
||||||
t = ipip6_tunnel_locate(net, &p, 0);
|
|
||||||
if (!t)
|
if (!t)
|
||||||
goto done;
|
return -ENOENT;
|
||||||
err = -EPERM;
|
if (t == netdev_priv(dev_to_sit_net(dev)->fb_tunnel_dev))
|
||||||
if (t == netdev_priv(sitn->fb_tunnel_dev))
|
return -EPERM;
|
||||||
goto done;
|
|
||||||
dev = t->dev;
|
dev = t->dev;
|
||||||
}
|
}
|
||||||
unregister_netdevice(dev);
|
unregister_netdevice(dev);
|
||||||
err = 0;
|
return 0;
|
||||||
break;
|
|
||||||
|
|
||||||
case SIOCGETPRL:
|
|
||||||
err = -EINVAL;
|
|
||||||
if (dev == sitn->fb_tunnel_dev)
|
|
||||||
goto done;
|
|
||||||
err = ipip6_tunnel_get_prl(t, ifr->ifr_ifru.ifru_data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SIOCADDPRL:
|
|
||||||
case SIOCDELPRL:
|
|
||||||
case SIOCCHGPRL:
|
|
||||||
err = -EPERM;
|
|
||||||
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
|
||||||
goto done;
|
|
||||||
err = -EINVAL;
|
|
||||||
if (dev == sitn->fb_tunnel_dev)
|
|
||||||
goto done;
|
|
||||||
err = -EFAULT;
|
|
||||||
if (copy_from_user(&prl, ifr->ifr_ifru.ifru_data, sizeof(prl)))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case SIOCDELPRL:
|
|
||||||
err = ipip6_tunnel_del_prl(t, &prl);
|
|
||||||
break;
|
|
||||||
case SIOCADDPRL:
|
|
||||||
case SIOCCHGPRL:
|
|
||||||
err = ipip6_tunnel_add_prl(t, &prl, cmd == SIOCCHGPRL);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
dst_cache_reset(&t->dst_cache);
|
|
||||||
netdev_state_change(dev);
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
ipip6_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||||
|
{
|
||||||
|
switch (cmd) {
|
||||||
|
case SIOCGETTUNNEL:
|
||||||
|
return ipip6_tunnel_get(dev, ifr);
|
||||||
|
case SIOCADDTUNNEL:
|
||||||
|
return ipip6_tunnel_add(dev, ifr);
|
||||||
|
case SIOCCHGTUNNEL:
|
||||||
|
return ipip6_tunnel_change(dev, ifr);
|
||||||
|
case SIOCDELTUNNEL:
|
||||||
|
return ipip6_tunnel_del(dev, ifr);
|
||||||
|
case SIOCGETPRL:
|
||||||
|
return ipip6_tunnel_get_prl(dev, ifr);
|
||||||
|
case SIOCADDPRL:
|
||||||
|
case SIOCDELPRL:
|
||||||
|
case SIOCCHGPRL:
|
||||||
|
return ipip6_tunnel_prl_ctl(dev, ifr, cmd);
|
||||||
#ifdef CONFIG_IPV6_SIT_6RD
|
#ifdef CONFIG_IPV6_SIT_6RD
|
||||||
|
case SIOCGET6RD:
|
||||||
|
return ipip6_tunnel_get6rd(dev, ifr);
|
||||||
case SIOCADD6RD:
|
case SIOCADD6RD:
|
||||||
case SIOCCHG6RD:
|
case SIOCCHG6RD:
|
||||||
case SIOCDEL6RD:
|
case SIOCDEL6RD:
|
||||||
err = -EPERM;
|
return ipip6_tunnel_6rdctl(dev, ifr, cmd);
|
||||||
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
err = -EFAULT;
|
|
||||||
if (copy_from_user(&ip6rd, ifr->ifr_ifru.ifru_data,
|
|
||||||
sizeof(ip6rd)))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (cmd != SIOCDEL6RD) {
|
|
||||||
err = ipip6_tunnel_update_6rd(t, &ip6rd);
|
|
||||||
if (err < 0)
|
|
||||||
goto done;
|
|
||||||
} else
|
|
||||||
ipip6_tunnel_clone_6rd(dev, sitn);
|
|
||||||
|
|
||||||
err = 0;
|
|
||||||
break;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct net_device_ops ipip6_netdev_ops = {
|
static const struct net_device_ops ipip6_netdev_ops = {
|
||||||
|
|||||||
Reference in New Issue
Block a user