Example #1
0
static int
ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
{
	int err = 0;
	struct ip_tunnel_parm p;
	struct ip_tunnel_prl prl;
	struct ip_tunnel *t;
	struct net *net = dev_net(dev);
	struct sit_net *sitn = net_generic(net, sit_net_id);

	switch (cmd) {
	case SIOCGETTUNNEL:
		t = NULL;
		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 == 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))
			goto done;

		err = -EFAULT;
		if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
			goto done;

		err = -EINVAL;
		if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPV6 ||
		    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 (dev != sitn->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
			if (t != NULL) {
				if (t->dev != dev) {
					err = -EEXIST;
					break;
				}
			} else {
				if (((dev->flags&IFF_POINTOPOINT) && !p.iph.daddr) ||
				    (!(dev->flags&IFF_POINTOPOINT) && p.iph.daddr)) {
					err = -EINVAL;
					break;
				}
				t = netdev_priv(dev);
				ipip6_tunnel_unlink(sitn, t);
				t->parms.iph.saddr = p.iph.saddr;
				t->parms.iph.daddr = p.iph.daddr;
				memcpy(dev->dev_addr, &p.iph.saddr, 4);
				memcpy(dev->broadcast, &p.iph.daddr, 4);
				ipip6_tunnel_link(sitn, t);
				netdev_state_change(dev);
			}
		}

		if (t) {
			err = 0;
			if (cmd == SIOCCHGTUNNEL) {
				t->parms.iph.ttl = p.iph.ttl;
				t->parms.iph.tos = p.iph.tos;
				if (t->parms.link != p.link) {
					t->parms.link = p.link;
					ipip6_tunnel_bind_dev(dev);
					netdev_state_change(dev);
				}
			}
			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))
			goto done;

		if (dev == sitn->fb_tunnel_dev) {
			err = -EFAULT;
			if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
				goto done;
			err = -ENOENT;
			if ((t = ipip6_tunnel_locate(net, &p, 0)) == NULL)
				goto done;
			err = -EPERM;
			if (t == netdev_priv(sitn->fb_tunnel_dev))
				goto done;
			dev = t->dev;
		}
		unregister_netdevice(dev);
		err = 0;
		break;

	case SIOCGETPRL:
		err = -EINVAL;
		if (dev == sitn->fb_tunnel_dev)
			goto done;
		err = -ENOENT;
		if (!(t = netdev_priv(dev)))
			goto done;
		err = ipip6_tunnel_get_prl(t, ifr->ifr_ifru.ifru_data);
		break;

	case SIOCADDPRL:
	case SIOCDELPRL:
	case SIOCCHGPRL:
		err = -EPERM;
		if (!capable(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;
		err = -ENOENT;
		if (!(t = netdev_priv(dev)))
			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;
		}
		netdev_state_change(dev);
		break;

	default:
		err = -EINVAL;
	}

done:
	return err;
}
Example #2
0
static int
ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
{
    int err = 0;
    struct ip_tunnel_parm p;
    struct ip_tunnel *t;

    MOD_INC_USE_COUNT;

    switch (cmd) {
    case SIOCGETTUNNEL:
        t = NULL;
        if (dev == &ipip6_fb_tunnel_dev) {
            if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
                err = -EFAULT;
                break;
            }
            t = ipip6_tunnel_locate(&p, 0);
        }
        if (t == NULL)
            t = (struct ip_tunnel*)dev->priv;
        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))
            goto done;

        err = -EFAULT;
        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
            goto done;

        err = -EINVAL;
        if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPV6 ||
                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(&p, cmd == SIOCADDTUNNEL);

        if (dev != &ipip6_fb_tunnel_dev && cmd == SIOCCHGTUNNEL &&
                t != &ipip6_fb_tunnel) {
            if (t != NULL) {
                if (t->dev != dev) {
                    err = -EEXIST;
                    break;
                }
            } else {
                if (((dev->flags&IFF_POINTOPOINT) && !p.iph.daddr) ||
                        (!(dev->flags&IFF_POINTOPOINT) && p.iph.daddr)) {
                    err = -EINVAL;
                    break;
                }
                t = (struct ip_tunnel*)dev->priv;
                ipip6_tunnel_unlink(t);
                t->parms.iph.saddr = p.iph.saddr;
                t->parms.iph.daddr = p.iph.daddr;
                memcpy(dev->dev_addr, &p.iph.saddr, 4);
                memcpy(dev->broadcast, &p.iph.daddr, 4);
                ipip6_tunnel_link(t);
                netdev_state_change(dev);
            }
        }

        if (t) {
            err = 0;
            if (cmd == SIOCCHGTUNNEL) {
                t->parms.iph.ttl = p.iph.ttl;
                t->parms.iph.tos = p.iph.tos;
            }
            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))
            goto done;

        if (dev == &ipip6_fb_tunnel_dev) {
            err = -EFAULT;
            if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
                goto done;
            err = -ENOENT;
            if ((t = ipip6_tunnel_locate(&p, 0)) == NULL)
                goto done;
            err = -EPERM;
            if (t == &ipip6_fb_tunnel)
                goto done;
            dev = t->dev;
        }
        err = unregister_netdevice(dev);
        break;

    default:
        err = -EINVAL;
    }

done:
    MOD_DEC_USE_COUNT;
    return err;
}
Example #3
0
static int
ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
{
	int err = 0;
	struct ip_tunnel_parm p;
	struct ip_tunnel *t;

	switch (cmd) {
	case SIOCGETTUNNEL:
		t = NULL;
		if (dev == ipip6_fb_tunnel_dev) {
			if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
				err = -EFAULT;
				break;
			}
			t = ipip6_tunnel_locate(&p, 0);
		}
		if (t == NULL)
			t = (struct ip_tunnel*)dev->priv;
		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))
		{
			printk("#### 1 #####\n");
			goto done;
		}
		err = -EFAULT;
		if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
		{
			printk("#### 2 #####\n");
			goto done;
		}

		err = -EINVAL;
		printk("#### p.iph.version = %d #####\n",p.iph.version);
		printk("#### p.iph.protocol = %d #####\n",p.iph.protocol);
		printk("#### p.iph.ihl = %d #####\n",p.iph.ihl);

/* There is a bug. I have no time to debug why p.iph.version == 5 & p.iph.ihl == 4
   When i use command in user space: 
	ip tunnel add tun6to4 mode sit ttl 64 remote any local $(WAN_PUBIC_IP)
   The kernel got wrong value for both p.iph.version and p.iph.ihl.
   So, i just modify the values to avoid setting device tun6to4 fail. 
    		
		 if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPV6 ||
                    p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))

*/
		if (p.iph.version != 5 || p.iph.protocol != IPPROTO_IPV6 ||
		    p.iph.ihl != 4 || (p.iph.frag_off&htons(~IP_DF)))
		{
			printk("#### 3 #####\n");
			goto done;
		}
		if (p.iph.ttl)
			p.iph.frag_off |= htons(IP_DF);

		t = ipip6_tunnel_locate(&p, cmd == SIOCADDTUNNEL);

		if (dev != ipip6_fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
			if (t != NULL) {
				if (t->dev != dev) {
					err = -EEXIST;
					break;
				}
			} else {
				if (((dev->flags&IFF_POINTOPOINT) && !p.iph.daddr) ||
				    (!(dev->flags&IFF_POINTOPOINT) && p.iph.daddr)) {
					err = -EINVAL;
					printk("#### 4 #####\n");
					break;
				}
				t = (struct ip_tunnel*)dev->priv;
				ipip6_tunnel_unlink(t);
				t->parms.iph.saddr = p.iph.saddr;
				t->parms.iph.daddr = p.iph.daddr;
				memcpy(dev->dev_addr, &p.iph.saddr, 4);
				memcpy(dev->broadcast, &p.iph.daddr, 4);
				ipip6_tunnel_link(t);
				netdev_state_change(dev);
			}
		}

		if (t) {
			err = 0;
			if (cmd == SIOCCHGTUNNEL) {
				t->parms.iph.ttl = p.iph.ttl;
				t->parms.iph.tos = p.iph.tos;
			}
			if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
			{
				printk("#### 5 #####\n");
				err = -EFAULT;
			}
		} else
			err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
		break;

	case SIOCDELTUNNEL:
		err = -EPERM;
		if (!capable(CAP_NET_ADMIN))
			goto done;

		if (dev == ipip6_fb_tunnel_dev) {
			err = -EFAULT;
			if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
				goto done;
			err = -ENOENT;
			if ((t = ipip6_tunnel_locate(&p, 0)) == NULL)
				goto done;
			err = -EPERM;
			if (t == ipip6_fb_tunnel_dev->priv)
				goto done;
			dev = t->dev;
		}
		err = unregister_netdevice(dev);
		break;

	default:
		printk("#### 6 #####\n");
		err = -EINVAL;
	}

done:
	printk("#### 7 #####\n");
	return err;
}