static struct ip_tunnel * ipip6_tunnel_locate(struct net *net, struct ip_tunnel_parm *parms, int create) { __be32 remote = parms->iph.daddr; __be32 local = parms->iph.saddr; struct ip_tunnel *t, **tp, *nt; struct net_device *dev; char name[IFNAMSIZ]; struct sit_net *sitn = net_generic(net, sit_net_id); for (tp = __ipip6_bucket(sitn, parms); (t = *tp) != NULL; tp = &t->next) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr && parms->link == t->parms.link) { if (create) return NULL; else return t; } } if (!create) goto failed; if (parms->name[0]) strlcpy(name, parms->name, IFNAMSIZ); else sprintf(name, "sit%%d"); dev = alloc_netdev(sizeof(*t), name, ipip6_tunnel_setup); if (dev == NULL) return NULL; dev_net_set(dev, net); if (strchr(name, '%')) { if (dev_alloc_name(dev, name) < 0) goto failed_free; } nt = netdev_priv(dev); nt->parms = *parms; ipip6_tunnel_init(dev); if (parms->i_flags & SIT_ISATAP) dev->priv_flags |= IFF_ISATAP; if (register_netdevice(dev) < 0) goto failed_free; dev_hold(dev); ipip6_tunnel_link(sitn, nt); return nt; failed_free: free_netdev(dev); failed: return NULL; }
static struct ip_tunnel * ipip6_tunnel_locate(struct ip_tunnel_parm *parms, int create) { u32 remote = parms->iph.daddr; u32 local = parms->iph.saddr; struct ip_tunnel *t, **tp, *nt; struct net_device *dev; unsigned h = 0; int prio = 0; char name[IFNAMSIZ]; if (remote) { prio |= 2; h ^= HASH(remote); } if (local) { prio |= 1; h ^= HASH(local); } for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) return t; } if (!create) goto failed; if (parms->name[0]) strlcpy(name, parms->name, IFNAMSIZ); else { int i; for (i=1; i<100; i++) { sprintf(name, "sit%d", i); if (__dev_get_by_name(name) == NULL) break; } if (i==100) goto failed; } dev = alloc_netdev(sizeof(*t), name, ipip6_tunnel_setup); if (dev == NULL) return NULL; nt = netdev_priv(dev); dev->init = ipip6_tunnel_init; nt->parms = *parms; if (register_netdevice(dev) < 0) { free_netdev(dev); goto failed; } dev_hold(dev); ipip6_tunnel_link(nt); return nt; failed: return NULL; }
struct ip_tunnel * ipip6_tunnel_locate(struct ip_tunnel_parm *parms, int create) { u32 remote = parms->iph.daddr; u32 local = parms->iph.saddr; struct ip_tunnel *t, **tp, *nt; struct net_device *dev; for (tp = __ipip6_bucket(parms); (t = *tp) != NULL; tp = &t->next) { if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) return t; } if (!create) return NULL; MOD_INC_USE_COUNT; dev = kmalloc(sizeof(*dev) + sizeof(*t), GFP_KERNEL); if (dev == NULL) { MOD_DEC_USE_COUNT; return NULL; } memset(dev, 0, sizeof(*dev) + sizeof(*t)); dev->priv = (void*)(dev+1); nt = (struct ip_tunnel*)dev->priv; nt->dev = dev; dev->init = ipip6_tunnel_init; dev->features |= NETIF_F_DYNALLOC; memcpy(&nt->parms, parms, sizeof(*parms)); nt->parms.name[IFNAMSIZ-1] = '\0'; strcpy(dev->name, nt->parms.name); if (dev->name[0] == 0) { int i; for (i=1; i<100; i++) { sprintf(dev->name, "sit%d", i); if (__dev_get_by_name(dev->name) == NULL) break; } if (i==100) goto failed; memcpy(nt->parms.name, dev->name, IFNAMSIZ); } if (register_netdevice(dev) < 0) goto failed; dev_hold(dev); ipip6_tunnel_link(nt); /* Do not decrement MOD_USE_COUNT here. */ return nt; failed: kfree(dev); MOD_DEC_USE_COUNT; return NULL; }
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; }
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; }
static int sit_rst(loff_t start, struct cpt_netdev_image *di, struct rst_ops *ops, struct cpt_context *ctx) { int err = -ENODEV; struct cpt_tunnel_image v; struct net_device *dev; struct ip_tunnel *t; loff_t pos; int fbdev; struct sit_net *sitn; sitn = net_generic(get_exec_env()->ve_netns, sit_net_id); if (sitn == NULL) return -EOPNOTSUPP; pos = start + di->cpt_hdrlen; err = ops->get_object(CPT_OBJ_NET_IPIP_TUNNEL, pos, &v, sizeof(v), ctx); if (err) return err; /* some sanity */ if (v.cpt_content != CPT_CONTENT_VOID) return -EINVAL; if (!(v.cpt_tnl_flags & CPT_TUNNEL_SIT)) return 1; if (v.cpt_tnl_flags & CPT_TUNNEL_FBDEV) { fbdev = 1; err = 0; dev = sitn->fb_tunnel_dev; } else { fbdev = 0; err = -ENOMEM; dev = alloc_netdev(sizeof(struct ip_tunnel), di->cpt_name, ipip6_tunnel_setup); if (!dev) goto out; } t = netdev_priv(dev); t->parms.i_flags = v.cpt_i_flags; t->parms.o_flags = v.cpt_o_flags; t->parms.i_key = v.cpt_i_key; t->parms.o_key = v.cpt_o_key; BUILD_BUG_ON(sizeof(v.cpt_iphdr) != sizeof(t->parms.iph)); memcpy(&t->parms.iph, &v.cpt_iphdr, sizeof(t->parms.iph)); if (!fbdev) { ipip6_tunnel_init(dev); err = register_netdevice(dev); if (err) { free_netdev(dev); goto out; } dev_hold(dev); ipip6_tunnel_link(sitn, t); } out: return err; }
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; }