static int ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { int err = 0; struct ip6_tnl_parm p; struct ip6_tnl *t = NULL; switch (cmd) { case SIOCGETTUNNEL: if (dev == ip6ip6_fb_tnl_dev) { if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { err = -EFAULT; break; } t = ip6ip6_tnl_locate(&p, 0); } if (t == NULL) t = netdev_priv(dev); memcpy(&p, &t->parms, sizeof (p)); if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { err = -EFAULT; } break; case SIOCADDTUNNEL: case SIOCCHGTUNNEL: err = -EPERM; if (!capable(CAP_NET_ADMIN)) break; err = -EFAULT; if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) break; err = -EINVAL; if (p.proto != IPPROTO_IPV6) break; t = ip6ip6_tnl_locate(&p, cmd == SIOCADDTUNNEL); if (dev != ip6ip6_fb_tnl_dev && cmd == SIOCCHGTUNNEL) { if (t != NULL) { if (t->dev != dev) { err = -EEXIST; break; } } else t = netdev_priv(dev); ip6ip6_tnl_unlink(t); err = ip6ip6_tnl_change(t, &p); ip6ip6_tnl_link(t); netdev_state_change(dev); } if (t) { err = 0; if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof (p))) err = -EFAULT; } else err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT); break; case SIOCDELTUNNEL: err = -EPERM; if (!capable(CAP_NET_ADMIN)) break; if (dev == ip6ip6_fb_tnl_dev) { err = -EFAULT; if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) break; err = -ENOENT; if ((t = ip6ip6_tnl_locate(&p, 0)) == NULL) break; err = -EPERM; if (t->dev == ip6ip6_fb_tnl_dev) break; dev = t->dev; } err = 0; unregister_netdevice(dev); break; default: err = -EINVAL; } return err; }
static int ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { int err = 0; int create; struct ip6_tnl_parm p; struct ip6_tnl *t = NULL; switch (cmd) { case SIOCGETTUNNEL: if (dev == ip6ip6_fb_tnl_dev) { if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { err = -EFAULT; break; } if ((err = ip6ip6_tnl_locate(&p, &t, 0)) == -ENODEV) t = netdev_priv(dev); else if (err) break; } else t = netdev_priv(dev); memcpy(&p, &t->parms, sizeof (p)); if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { err = -EFAULT; } break; case SIOCADDTUNNEL: case SIOCCHGTUNNEL: err = -EPERM; create = (cmd == SIOCADDTUNNEL); if (!capable(CAP_NET_ADMIN)) break; if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { err = -EFAULT; break; } if (!create && dev != ip6ip6_fb_tnl_dev) { t = netdev_priv(dev); } if (!t && (err = ip6ip6_tnl_locate(&p, &t, create))) { break; } if (cmd == SIOCCHGTUNNEL) { if (t->dev != dev) { err = -EEXIST; break; } ip6ip6_tnl_unlink(t); err = ip6ip6_tnl_change(t, &p); ip6ip6_tnl_link(t); netdev_state_change(dev); } if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof (p))) { err = -EFAULT; } else { err = 0; } break; case SIOCDELTUNNEL: err = -EPERM; if (!capable(CAP_NET_ADMIN)) break; if (dev == ip6ip6_fb_tnl_dev) { if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { err = -EFAULT; break; } err = ip6ip6_tnl_locate(&p, &t, 0); if (err) break; if (t == netdev_priv(ip6ip6_fb_tnl_dev)) { err = -EPERM; break; } } else { t = netdev_priv(dev); } err = unregister_netdevice(t->dev); break; default: err = -EINVAL; } return err; }