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

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: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c7e36705
...@@ -83,6 +83,13 @@ struct sit_net { ...@@ -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) ...@@ -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 @@ ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a) ...@@ -441,6 +452,35 @@ ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a)
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, ...@@ -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) ...@@ -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)
{
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (!ipip6_valid_ip_proto(p->iph.protocol))
return -EINVAL;
if (p->iph.version != 4 ||
p->iph.ihl != 5 || (p->iph.frag_off & htons(~IP_DF)))
return -EINVAL;
if (p->iph.ttl)
p->iph.frag_off |= htons(IP_DF);
return 0;
}
static int
ipip6_tunnel_get(struct net_device *dev, struct ifreq *ifr)
{ {
int err = 0;
struct ip_tunnel_parm p;
struct ip_tunnel_prl prl;
struct ip_tunnel *t = netdev_priv(dev); struct ip_tunnel *t = netdev_priv(dev);
struct net *net = t->net; struct ip_tunnel_parm p;
struct sit_net *sitn = net_generic(net, sit_net_id);
#ifdef CONFIG_IPV6_SIT_6RD
struct ip_tunnel_6rd ip6rd;
#endif
switch (cmd) { if (dev == dev_to_sit_net(dev)->fb_tunnel_dev) {
case SIOCGETTUNNEL: if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
#ifdef CONFIG_IPV6_SIT_6RD return -EFAULT;
case SIOCGET6RD: t = ipip6_tunnel_locate(t->net, &p, 0);
#endif
if (dev == sitn->fb_tunnel_dev) {
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
err = -EFAULT;
break;
} }
t = ipip6_tunnel_locate(net, &p, 0);
if (!t) if (!t)
t = netdev_priv(dev); t = netdev_priv(dev);
}
err = -EFAULT; if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
if (cmd == SIOCGETTUNNEL) { return -EFAULT;
memcpy(&p, &t->parms, sizeof(p)); return 0;
if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, }
sizeof(p)))
goto done;
#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: static int
case SIOCCHGTUNNEL: ipip6_tunnel_add(struct net_device *dev, struct ifreq *ifr)
err = -EPERM; {
if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) struct ip_tunnel *t = netdev_priv(dev);
goto done; struct ip_tunnel_parm p;
int err;
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;
t = ipip6_tunnel_locate(t->net, &p, 1);
if (!t)
return -ENOBUFS;
err = -EINVAL; if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
if (!ipip6_valid_ip_proto(p.iph.protocol)) return -EFAULT;
goto done; return 0;
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); 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 (dev != sitn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) { 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) {
if (t->dev != dev) { if (t->dev != dev)
err = -EEXIST; return -EEXIST;
break;
}
} 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; }
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: case SIOCGETPRL:
err = -EINVAL; return ipip6_tunnel_get_prl(dev, ifr);
if (dev == sitn->fb_tunnel_dev)
goto done;
err = ipip6_tunnel_get_prl(t, ifr->ifr_ifru.ifru_data);
break;
case SIOCADDPRL: case SIOCADDPRL:
case SIOCDELPRL: case SIOCDELPRL:
case SIOCCHGPRL: case SIOCCHGPRL:
err = -EPERM; return ipip6_tunnel_prl_ctl(dev, ifr, cmd);
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;
#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 = {
......
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