mac80211: rx.c use new frame control helpers
[safe/jmp/linux-2.6] / net / ipv4 / ip_gre.c
index 9981dcd..2a61158 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- *     Linux NET3:     GRE over IP protocol decoder. 
+ *     Linux NET3:     GRE over IP protocol decoder.
  *
  *     Authors: Alexey Kuznetsov (kuznet@ms2.inr.ac.ru)
  *
  *
  *     Authors: Alexey Kuznetsov (kuznet@ms2.inr.ac.ru)
  *
  */
 
 #include <linux/capability.h>
  */
 
 #include <linux/capability.h>
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/types.h>
-#include <linux/sched.h>
 #include <linux/kernel.h>
 #include <asm/uaccess.h>
 #include <linux/skbuff.h>
 #include <linux/kernel.h>
 #include <asm/uaccess.h>
 #include <linux/skbuff.h>
@@ -41,6 +39,8 @@
 #include <net/dsfield.h>
 #include <net/inet_ecn.h>
 #include <net/xfrm.h>
 #include <net/dsfield.h>
 #include <net/inet_ecn.h>
 #include <net/xfrm.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
 
 #ifdef CONFIG_IPV6
 #include <net/ipv6.h>
 
 #ifdef CONFIG_IPV6
 #include <net/ipv6.h>
@@ -64,7 +64,7 @@
    solution, but it supposes maintaing new variable in ALL
    skb, even if no tunneling is used.
 
    solution, but it supposes maintaing new variable in ALL
    skb, even if no tunneling is used.
 
-   Current solution: t->recursion lock breaks dead loops. It looks 
+   Current solution: t->recursion lock breaks dead loops. It looks
    like dev->tbusy flag, but I preferred new variable, because
    the semantics is different. One day, when hard_start_xmit
    will be multithreaded we will have to use skb->encapsulation.
    like dev->tbusy flag, but I preferred new variable, because
    the semantics is different. One day, when hard_start_xmit
    will be multithreaded we will have to use skb->encapsulation.
@@ -124,7 +124,14 @@ static void ipgre_tunnel_setup(struct net_device *dev);
 
 static int ipgre_fb_tunnel_init(struct net_device *dev);
 
 
 static int ipgre_fb_tunnel_init(struct net_device *dev);
 
-static struct net_device *ipgre_fb_tunnel_dev;
+#define HASH_SIZE  16
+
+static int ipgre_net_id;
+struct ipgre_net {
+       struct ip_tunnel *tunnels[4][HASH_SIZE];
+
+       struct net_device *fb_tunnel_dev;
+};
 
 /* Tunnel hash table */
 
 
 /* Tunnel hash table */
 
@@ -144,76 +151,83 @@ static struct net_device *ipgre_fb_tunnel_dev;
    will match fallback tunnel.
  */
 
    will match fallback tunnel.
  */
 
-#define HASH_SIZE  16
-#define HASH(addr) ((addr^(addr>>4))&0xF)
-
-static struct ip_tunnel *tunnels[4][HASH_SIZE];
+#define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF)
 
 
-#define tunnels_r_l    (tunnels[3])
-#define tunnels_r      (tunnels[2])
-#define tunnels_l      (tunnels[1])
-#define tunnels_wc     (tunnels[0])
+#define tunnels_r_l    tunnels[3]
+#define tunnels_r      tunnels[2]
+#define tunnels_l      tunnels[1]
+#define tunnels_wc     tunnels[0]
 
 static DEFINE_RWLOCK(ipgre_lock);
 
 /* Given src, dst and key, find appropriate for input tunnel. */
 
 
 static DEFINE_RWLOCK(ipgre_lock);
 
 /* Given src, dst and key, find appropriate for input tunnel. */
 
-static struct ip_tunnel * ipgre_tunnel_lookup(u32 remote, u32 local, u32 key)
+static struct ip_tunnel * ipgre_tunnel_lookup(struct net *net,
+               __be32 remote, __be32 local, __be32 key)
 {
        unsigned h0 = HASH(remote);
        unsigned h1 = HASH(key);
        struct ip_tunnel *t;
 {
        unsigned h0 = HASH(remote);
        unsigned h1 = HASH(key);
        struct ip_tunnel *t;
+       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
 
 
-       for (t = tunnels_r_l[h0^h1]; t; t = t->next) {
+       for (t = ign->tunnels_r_l[h0^h1]; t; t = t->next) {
                if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) {
                        if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
                                return t;
                }
        }
                if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) {
                        if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
                                return t;
                }
        }
-       for (t = tunnels_r[h0^h1]; t; t = t->next) {
+       for (t = ign->tunnels_r[h0^h1]; t; t = t->next) {
                if (remote == t->parms.iph.daddr) {
                        if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
                                return t;
                }
        }
                if (remote == t->parms.iph.daddr) {
                        if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
                                return t;
                }
        }
-       for (t = tunnels_l[h1]; t; t = t->next) {
+       for (t = ign->tunnels_l[h1]; t; t = t->next) {
                if (local == t->parms.iph.saddr ||
                if (local == t->parms.iph.saddr ||
-                    (local == t->parms.iph.daddr && MULTICAST(local))) {
+                    (local == t->parms.iph.daddr &&
+                     ipv4_is_multicast(local))) {
                        if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
                                return t;
                }
        }
                        if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
                                return t;
                }
        }
-       for (t = tunnels_wc[h1]; t; t = t->next) {
+       for (t = ign->tunnels_wc[h1]; t; t = t->next) {
                if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
                        return t;
        }
 
                if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
                        return t;
        }
 
-       if (ipgre_fb_tunnel_dev->flags&IFF_UP)
-               return netdev_priv(ipgre_fb_tunnel_dev);
+       if (ign->fb_tunnel_dev->flags&IFF_UP)
+               return netdev_priv(ign->fb_tunnel_dev);
        return NULL;
 }
 
        return NULL;
 }
 
-static struct ip_tunnel **ipgre_bucket(struct ip_tunnel *t)
+static struct ip_tunnel **__ipgre_bucket(struct ipgre_net *ign,
+               struct ip_tunnel_parm *parms)
 {
 {
-       u32 remote = t->parms.iph.daddr;
-       u32 local = t->parms.iph.saddr;
-       u32 key = t->parms.i_key;
+       __be32 remote = parms->iph.daddr;
+       __be32 local = parms->iph.saddr;
+       __be32 key = parms->i_key;
        unsigned h = HASH(key);
        int prio = 0;
 
        if (local)
                prio |= 1;
        unsigned h = HASH(key);
        int prio = 0;
 
        if (local)
                prio |= 1;
-       if (remote && !MULTICAST(remote)) {
+       if (remote && !ipv4_is_multicast(remote)) {
                prio |= 2;
                h ^= HASH(remote);
        }
 
                prio |= 2;
                h ^= HASH(remote);
        }
 
-       return &tunnels[prio][h];
+       return &ign->tunnels[prio][h];
+}
+
+static inline struct ip_tunnel **ipgre_bucket(struct ipgre_net *ign,
+               struct ip_tunnel *t)
+{
+       return __ipgre_bucket(ign, &t->parms);
 }
 
 }
 
-static void ipgre_tunnel_link(struct ip_tunnel *t)
+static void ipgre_tunnel_link(struct ipgre_net *ign, struct ip_tunnel *t)
 {
 {
-       struct ip_tunnel **tp = ipgre_bucket(t);
+       struct ip_tunnel **tp = ipgre_bucket(ign, t);
 
        t->next = *tp;
        write_lock_bh(&ipgre_lock);
 
        t->next = *tp;
        write_lock_bh(&ipgre_lock);
@@ -221,11 +235,11 @@ static void ipgre_tunnel_link(struct ip_tunnel *t)
        write_unlock_bh(&ipgre_lock);
 }
 
        write_unlock_bh(&ipgre_lock);
 }
 
-static void ipgre_tunnel_unlink(struct ip_tunnel *t)
+static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)
 {
        struct ip_tunnel **tp;
 
 {
        struct ip_tunnel **tp;
 
-       for (tp = ipgre_bucket(t); *tp; tp = &(*tp)->next) {
+       for (tp = ipgre_bucket(ign, t); *tp; tp = &(*tp)->next) {
                if (t == *tp) {
                        write_lock_bh(&ipgre_lock);
                        *tp = t->next;
                if (t == *tp) {
                        write_lock_bh(&ipgre_lock);
                        *tp = t->next;
@@ -235,24 +249,18 @@ static void ipgre_tunnel_unlink(struct ip_tunnel *t)
        }
 }
 
        }
 }
 
-static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int create)
+static struct ip_tunnel * ipgre_tunnel_locate(struct net *net,
+               struct ip_tunnel_parm *parms, int create)
 {
 {
-       u32 remote = parms->iph.daddr;
-       u32 local = parms->iph.saddr;
-       u32 key = parms->i_key;
+       __be32 remote = parms->iph.daddr;
+       __be32 local = parms->iph.saddr;
+       __be32 key = parms->i_key;
        struct ip_tunnel *t, **tp, *nt;
        struct net_device *dev;
        struct ip_tunnel *t, **tp, *nt;
        struct net_device *dev;
-       unsigned h = HASH(key);
-       int prio = 0;
        char name[IFNAMSIZ];
        char name[IFNAMSIZ];
+       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
 
 
-       if (local)
-               prio |= 1;
-       if (remote && !MULTICAST(remote)) {
-               prio |= 2;
-               h ^= HASH(remote);
-       }
-       for (tp = &tunnels[prio][h]; (t = *tp) != NULL; tp = &t->next) {
+       for (tp = __ipgre_bucket(ign, parms); (t = *tp) != NULL; tp = &t->next) {
                if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) {
                        if (key == t->parms.i_key)
                                return t;
                if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) {
                        if (key == t->parms.i_key)
                                return t;
@@ -263,50 +271,50 @@ static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int
 
        if (parms->name[0])
                strlcpy(name, parms->name, IFNAMSIZ);
 
        if (parms->name[0])
                strlcpy(name, parms->name, IFNAMSIZ);
-       else {
-               int i;
-               for (i=1; i<100; i++) {
-                       sprintf(name, "gre%d", i);
-                       if (__dev_get_by_name(name) == NULL)
-                               break;
-               }
-               if (i==100)
-                       goto failed;
-       }
+       else
+               sprintf(name, "gre%%d");
 
        dev = alloc_netdev(sizeof(*t), name, ipgre_tunnel_setup);
        if (!dev)
          return NULL;
 
 
        dev = alloc_netdev(sizeof(*t), name, ipgre_tunnel_setup);
        if (!dev)
          return NULL;
 
+       dev_net_set(dev, net);
+
+       if (strchr(name, '%')) {
+               if (dev_alloc_name(dev, name) < 0)
+                       goto failed_free;
+       }
+
        dev->init = ipgre_tunnel_init;
        nt = netdev_priv(dev);
        nt->parms = *parms;
 
        dev->init = ipgre_tunnel_init;
        nt = netdev_priv(dev);
        nt->parms = *parms;
 
-       if (register_netdevice(dev) < 0) {
-               free_netdev(dev);
-               goto failed;
-       }
+       if (register_netdevice(dev) < 0)
+               goto failed_free;
 
        dev_hold(dev);
 
        dev_hold(dev);
-       ipgre_tunnel_link(nt);
+       ipgre_tunnel_link(ign, nt);
        return nt;
 
        return nt;
 
-failed:
+failed_free:
+       free_netdev(dev);
        return NULL;
 }
 
 static void ipgre_tunnel_uninit(struct net_device *dev)
 {
        return NULL;
 }
 
 static void ipgre_tunnel_uninit(struct net_device *dev)
 {
-       ipgre_tunnel_unlink(netdev_priv(dev));
+       struct net *net = dev_net(dev);
+       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
+
+       ipgre_tunnel_unlink(ign, netdev_priv(dev));
        dev_put(dev);
 }
 
 
 static void ipgre_err(struct sk_buff *skb, u32 info)
 {
        dev_put(dev);
 }
 
 
 static void ipgre_err(struct sk_buff *skb, u32 info)
 {
-#ifndef I_WISH_WORLD_WERE_PERFECT
 
 
-/* It is not :-( All the routers (except for Linux) return only
+/* All the routers (except for Linux) return only
    8 bytes of packet payload. It means, that precise relaying of
    ICMP in the real Internet is absolutely infeasible.
 
    8 bytes of packet payload. It means, that precise relaying of
    ICMP in the real Internet is absolutely infeasible.
 
@@ -320,12 +328,12 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
  */
 
        struct iphdr *iph = (struct iphdr*)skb->data;
  */
 
        struct iphdr *iph = (struct iphdr*)skb->data;
-       u16          *p = (u16*)(skb->data+(iph->ihl<<2));
+       __be16       *p = (__be16*)(skb->data+(iph->ihl<<2));
        int grehlen = (iph->ihl<<2) + 4;
        int grehlen = (iph->ihl<<2) + 4;
-       int type = skb->h.icmph->type;
-       int code = skb->h.icmph->code;
+       const int type = icmp_hdr(skb)->type;
+       const int code = icmp_hdr(skb)->code;
        struct ip_tunnel *t;
        struct ip_tunnel *t;
-       u16 flags;
+       __be16 flags;
 
        flags = p[0];
        if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
 
        flags = p[0];
        if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
@@ -371,8 +379,11 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
        }
 
        read_lock(&ipgre_lock);
        }
 
        read_lock(&ipgre_lock);
-       t = ipgre_tunnel_lookup(iph->daddr, iph->saddr, (flags&GRE_KEY) ? *(((u32*)p) + (grehlen>>2) - 1) : 0);
-       if (t == NULL || t->parms.iph.daddr == 0 || MULTICAST(t->parms.iph.daddr))
+       t = ipgre_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr,
+                       (flags&GRE_KEY) ?
+                       *(((__be32*)p) + (grehlen>>2) - 1) : 0);
+       if (t == NULL || t->parms.iph.daddr == 0 ||
+           ipv4_is_multicast(t->parms.iph.daddr))
                goto out;
 
        if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
                goto out;
 
        if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
@@ -386,155 +397,15 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
 out:
        read_unlock(&ipgre_lock);
        return;
 out:
        read_unlock(&ipgre_lock);
        return;
-#else
-       struct iphdr *iph = (struct iphdr*)dp;
-       struct iphdr *eiph;
-       u16          *p = (u16*)(dp+(iph->ihl<<2));
-       int type = skb->h.icmph->type;
-       int code = skb->h.icmph->code;
-       int rel_type = 0;
-       int rel_code = 0;
-       int rel_info = 0;
-       u16 flags;
-       int grehlen = (iph->ihl<<2) + 4;
-       struct sk_buff *skb2;
-       struct flowi fl;
-       struct rtable *rt;
-
-       if (p[1] != htons(ETH_P_IP))
-               return;
-
-       flags = p[0];
-       if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
-               if (flags&(GRE_VERSION|GRE_ROUTING))
-                       return;
-               if (flags&GRE_CSUM)
-                       grehlen += 4;
-               if (flags&GRE_KEY)
-                       grehlen += 4;
-               if (flags&GRE_SEQ)
-                       grehlen += 4;
-       }
-       if (len < grehlen + sizeof(struct iphdr))
-               return;
-       eiph = (struct iphdr*)(dp + grehlen);
-
-       switch (type) {
-       default:
-               return;
-       case ICMP_PARAMETERPROB:
-               if (skb->h.icmph->un.gateway < (iph->ihl<<2))
-                       return;
-
-               /* So... This guy found something strange INSIDE encapsulated
-                  packet. Well, he is fool, but what can we do ?
-                */
-               rel_type = ICMP_PARAMETERPROB;
-               rel_info = skb->h.icmph->un.gateway - grehlen;
-               break;
-
-       case ICMP_DEST_UNREACH:
-               switch (code) {
-               case ICMP_SR_FAILED:
-               case ICMP_PORT_UNREACH:
-                       /* Impossible event. */
-                       return;
-               case ICMP_FRAG_NEEDED:
-                       /* And it is the only really necessary thing :-) */
-                       rel_info = ntohs(skb->h.icmph->un.frag.mtu);
-                       if (rel_info < grehlen+68)
-                               return;
-                       rel_info -= grehlen;
-                       /* BSD 4.2 MORE DOES NOT EXIST IN NATURE. */
-                       if (rel_info > ntohs(eiph->tot_len))
-                               return;
-                       break;
-               default:
-                       /* All others are translated to HOST_UNREACH.
-                          rfc2003 contains "deep thoughts" about NET_UNREACH,
-                          I believe, it is just ether pollution. --ANK
-                        */
-                       rel_type = ICMP_DEST_UNREACH;
-                       rel_code = ICMP_HOST_UNREACH;
-                       break;
-               }
-               break;
-       case ICMP_TIME_EXCEEDED:
-               if (code != ICMP_EXC_TTL)
-                       return;
-               break;
-       }
-
-       /* Prepare fake skb to feed it to icmp_send */
-       skb2 = skb_clone(skb, GFP_ATOMIC);
-       if (skb2 == NULL)
-               return;
-       dst_release(skb2->dst);
-       skb2->dst = NULL;
-       skb_pull(skb2, skb->data - (u8*)eiph);
-       skb2->nh.raw = skb2->data;
-
-       /* Try to guess incoming interface */
-       memset(&fl, 0, sizeof(fl));
-       fl.fl4_dst = eiph->saddr;
-       fl.fl4_tos = RT_TOS(eiph->tos);
-       fl.proto = IPPROTO_GRE;
-       if (ip_route_output_key(&rt, &fl)) {
-               kfree_skb(skb2);
-               return;
-       }
-       skb2->dev = rt->u.dst.dev;
-
-       /* route "incoming" packet */
-       if (rt->rt_flags&RTCF_LOCAL) {
-               ip_rt_put(rt);
-               rt = NULL;
-               fl.fl4_dst = eiph->daddr;
-               fl.fl4_src = eiph->saddr;
-               fl.fl4_tos = eiph->tos;
-               if (ip_route_output_key(&rt, &fl) ||
-                   rt->u.dst.dev->type != ARPHRD_IPGRE) {
-                       ip_rt_put(rt);
-                       kfree_skb(skb2);
-                       return;
-               }
-       } else {
-               ip_rt_put(rt);
-               if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos, skb2->dev) ||
-                   skb2->dst->dev->type != ARPHRD_IPGRE) {
-                       kfree_skb(skb2);
-                       return;
-               }
-       }
-
-       /* change mtu on this route */
-       if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
-               if (rel_info > dst_mtu(skb2->dst)) {
-                       kfree_skb(skb2);
-                       return;
-               }
-               skb2->dst->ops->update_pmtu(skb2->dst, rel_info);
-               rel_info = htonl(rel_info);
-       } else if (type == ICMP_TIME_EXCEEDED) {
-               struct ip_tunnel *t = netdev_priv(skb2->dev);
-               if (t->parms.iph.ttl) {
-                       rel_type = ICMP_DEST_UNREACH;
-                       rel_code = ICMP_HOST_UNREACH;
-               }
-       }
-
-       icmp_send(skb2, rel_type, rel_code, rel_info);
-       kfree_skb(skb2);
-#endif
 }
 
 static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
 {
        if (INET_ECN_is_ce(iph->tos)) {
                if (skb->protocol == htons(ETH_P_IP)) {
 }
 
 static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
 {
        if (INET_ECN_is_ce(iph->tos)) {
                if (skb->protocol == htons(ETH_P_IP)) {
-                       IP_ECN_set_ce(skb->nh.iph);
+                       IP_ECN_set_ce(ip_hdr(skb));
                } else if (skb->protocol == htons(ETH_P_IPV6)) {
                } else if (skb->protocol == htons(ETH_P_IPV6)) {
-                       IP6_ECN_set_ce(skb->nh.ipv6h);
+                       IP6_ECN_set_ce(ipv6_hdr(skb));
                }
        }
 }
                }
        }
 }
@@ -554,9 +425,9 @@ static int ipgre_rcv(struct sk_buff *skb)
 {
        struct iphdr *iph;
        u8     *h;
 {
        struct iphdr *iph;
        u8     *h;
-       u16    flags;
-       u16    csum = 0;
-       u32    key = 0;
+       __be16    flags;
+       __sum16   csum = 0;
+       __be32 key = 0;
        u32    seqno = 0;
        struct ip_tunnel *tunnel;
        int    offset = 4;
        u32    seqno = 0;
        struct ip_tunnel *tunnel;
        int    offset = 4;
@@ -564,9 +435,9 @@ static int ipgre_rcv(struct sk_buff *skb)
        if (!pskb_may_pull(skb, 16))
                goto drop_nolock;
 
        if (!pskb_may_pull(skb, 16))
                goto drop_nolock;
 
-       iph = skb->nh.iph;
+       iph = ip_hdr(skb);
        h = skb->data;
        h = skb->data;
-       flags = *(u16*)h;
+       flags = *(__be16*)h;
 
        if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) {
                /* - Version must be 0.
 
        if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) {
                /* - Version must be 0.
@@ -577,76 +448,79 @@ static int ipgre_rcv(struct sk_buff *skb)
 
                if (flags&GRE_CSUM) {
                        switch (skb->ip_summed) {
 
                if (flags&GRE_CSUM) {
                        switch (skb->ip_summed) {
-                       case CHECKSUM_HW:
-                               csum = (u16)csum_fold(skb->csum);
+                       case CHECKSUM_COMPLETE:
+                               csum = csum_fold(skb->csum);
                                if (!csum)
                                        break;
                                /* fall through */
                        case CHECKSUM_NONE:
                                skb->csum = 0;
                                csum = __skb_checksum_complete(skb);
                                if (!csum)
                                        break;
                                /* fall through */
                        case CHECKSUM_NONE:
                                skb->csum = 0;
                                csum = __skb_checksum_complete(skb);
-                               skb->ip_summed = CHECKSUM_HW;
+                               skb->ip_summed = CHECKSUM_COMPLETE;
                        }
                        offset += 4;
                }
                if (flags&GRE_KEY) {
                        }
                        offset += 4;
                }
                if (flags&GRE_KEY) {
-                       key = *(u32*)(h + offset);
+                       key = *(__be32*)(h + offset);
                        offset += 4;
                }
                if (flags&GRE_SEQ) {
                        offset += 4;
                }
                if (flags&GRE_SEQ) {
-                       seqno = ntohl(*(u32*)(h + offset));
+                       seqno = ntohl(*(__be32*)(h + offset));
                        offset += 4;
                }
        }
 
        read_lock(&ipgre_lock);
                        offset += 4;
                }
        }
 
        read_lock(&ipgre_lock);
-       if ((tunnel = ipgre_tunnel_lookup(iph->saddr, iph->daddr, key)) != NULL) {
+       if ((tunnel = ipgre_tunnel_lookup(dev_net(skb->dev),
+                                       iph->saddr, iph->daddr, key)) != NULL) {
+               struct net_device_stats *stats = &tunnel->dev->stats;
+
                secpath_reset(skb);
 
                secpath_reset(skb);
 
-               skb->protocol = *(u16*)(h + 2);
+               skb->protocol = *(__be16*)(h + 2);
                /* WCCP version 1 and 2 protocol decoding.
                 * - Change protocol to IP
                 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
                 */
                if (flags == 0 &&
                /* WCCP version 1 and 2 protocol decoding.
                 * - Change protocol to IP
                 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
                 */
                if (flags == 0 &&
-                   skb->protocol == __constant_htons(ETH_P_WCCP)) {
-                       skb->protocol = __constant_htons(ETH_P_IP);
-                       if ((*(h + offset) & 0xF0) != 0x40) 
+                   skb->protocol == htons(ETH_P_WCCP)) {
+                       skb->protocol = htons(ETH_P_IP);
+                       if ((*(h + offset) & 0xF0) != 0x40)
                                offset += 4;
                }
 
                                offset += 4;
                }
 
-               skb->mac.raw = skb->nh.raw;
-               skb->nh.raw = __pskb_pull(skb, offset);
-               skb_postpull_rcsum(skb, skb->h.raw, offset);
-               memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+               skb->mac_header = skb->network_header;
+               __pskb_pull(skb, offset);
+               skb_reset_network_header(skb);
+               skb_postpull_rcsum(skb, skb_transport_header(skb), offset);
                skb->pkt_type = PACKET_HOST;
 #ifdef CONFIG_NET_IPGRE_BROADCAST
                skb->pkt_type = PACKET_HOST;
 #ifdef CONFIG_NET_IPGRE_BROADCAST
-               if (MULTICAST(iph->daddr)) {
+               if (ipv4_is_multicast(iph->daddr)) {
                        /* Looped back packet, drop it! */
                        /* Looped back packet, drop it! */
-                       if (((struct rtable*)skb->dst)->fl.iif == 0)
+                       if (skb->rtable->fl.iif == 0)
                                goto drop;
                                goto drop;
-                       tunnel->stat.multicast++;
+                       stats->multicast++;
                        skb->pkt_type = PACKET_BROADCAST;
                }
 #endif
 
                if (((flags&GRE_CSUM) && csum) ||
                    (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) {
                        skb->pkt_type = PACKET_BROADCAST;
                }
 #endif
 
                if (((flags&GRE_CSUM) && csum) ||
                    (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) {
-                       tunnel->stat.rx_crc_errors++;
-                       tunnel->stat.rx_errors++;
+                       stats->rx_crc_errors++;
+                       stats->rx_errors++;
                        goto drop;
                }
                if (tunnel->parms.i_flags&GRE_SEQ) {
                        if (!(flags&GRE_SEQ) ||
                            (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0)) {
                        goto drop;
                }
                if (tunnel->parms.i_flags&GRE_SEQ) {
                        if (!(flags&GRE_SEQ) ||
                            (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0)) {
-                               tunnel->stat.rx_fifo_errors++;
-                               tunnel->stat.rx_errors++;
+                               stats->rx_fifo_errors++;
+                               stats->rx_errors++;
                                goto drop;
                        }
                        tunnel->i_seqno = seqno + 1;
                }
                                goto drop;
                        }
                        tunnel->i_seqno = seqno + 1;
                }
-               tunnel->stat.rx_packets++;
-               tunnel->stat.rx_bytes += skb->len;
+               stats->rx_packets++;
+               stats->rx_bytes += skb->len;
                skb->dev = tunnel->dev;
                dst_release(skb->dst);
                skb->dst = NULL;
                skb->dev = tunnel->dev;
                dst_release(skb->dst);
                skb->dst = NULL;
@@ -656,7 +530,7 @@ static int ipgre_rcv(struct sk_buff *skb)
                read_unlock(&ipgre_lock);
                return(0);
        }
                read_unlock(&ipgre_lock);
                return(0);
        }
-       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
+       icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 
 drop:
        read_unlock(&ipgre_lock);
 
 drop:
        read_unlock(&ipgre_lock);
@@ -668,25 +542,25 @@ drop_nolock:
 static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
 static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
-       struct net_device_stats *stats = &tunnel->stat;
-       struct iphdr  *old_iph = skb->nh.iph;
+       struct net_device_stats *stats = &tunnel->dev->stats;
+       struct iphdr  *old_iph = ip_hdr(skb);
        struct iphdr  *tiph;
        u8     tos;
        struct iphdr  *tiph;
        u8     tos;
-       u16    df;
+       __be16 df;
        struct rtable *rt;                      /* Route to the other host */
        struct net_device *tdev;                        /* Device to other host */
        struct iphdr  *iph;                     /* Our new IP header */
        struct rtable *rt;                      /* Route to the other host */
        struct net_device *tdev;                        /* Device to other host */
        struct iphdr  *iph;                     /* Our new IP header */
-       int    max_headroom;                    /* The extra header space needed */
+       unsigned int max_headroom;              /* The extra header space needed */
        int    gre_hlen;
        int    gre_hlen;
-       u32    dst;
+       __be32 dst;
        int    mtu;
 
        if (tunnel->recursion++) {
        int    mtu;
 
        if (tunnel->recursion++) {
-               tunnel->stat.collisions++;
+               stats->collisions++;
                goto tx_error;
        }
 
                goto tx_error;
        }
 
-       if (dev->hard_header) {
+       if (dev->header_ops) {
                gre_hlen = 0;
                tiph = (struct iphdr*)skb->data;
        } else {
                gre_hlen = 0;
                tiph = (struct iphdr*)skb->data;
        } else {
@@ -698,12 +572,12 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                /* NBMA tunnel */
 
                if (skb->dst == NULL) {
                /* NBMA tunnel */
 
                if (skb->dst == NULL) {
-                       tunnel->stat.tx_fifo_errors++;
+                       stats->tx_fifo_errors++;
                        goto tx_error;
                }
 
                if (skb->protocol == htons(ETH_P_IP)) {
                        goto tx_error;
                }
 
                if (skb->protocol == htons(ETH_P_IP)) {
-                       rt = (struct rtable*)skb->dst;
+                       rt = skb->rtable;
                        if ((dst = rt->rt_gateway) == 0)
                                goto tx_error_icmp;
                }
                        if ((dst = rt->rt_gateway) == 0)
                                goto tx_error_icmp;
                }
@@ -720,7 +594,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                        addr_type = ipv6_addr_type(addr6);
 
                        if (addr_type == IPV6_ADDR_ANY) {
                        addr_type = ipv6_addr_type(addr6);
 
                        if (addr_type == IPV6_ADDR_ANY) {
-                               addr6 = &skb->nh.ipv6h->daddr;
+                               addr6 = &ipv6_hdr(skb)->daddr;
                                addr_type = ipv6_addr_type(addr6);
                        }
 
                                addr_type = ipv6_addr_type(addr6);
                        }
 
@@ -748,8 +622,8 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                                                .saddr = tiph->saddr,
                                                .tos = RT_TOS(tos) } },
                                    .proto = IPPROTO_GRE };
                                                .saddr = tiph->saddr,
                                                .tos = RT_TOS(tos) } },
                                    .proto = IPPROTO_GRE };
-               if (ip_route_output_key(&rt, &fl)) {
-                       tunnel->stat.tx_carrier_errors++;
+               if (ip_route_output_key(dev_net(dev), &rt, &fl)) {
+                       stats->tx_carrier_errors++;
                        goto tx_error;
                }
        }
                        goto tx_error;
                }
        }
@@ -757,7 +631,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 
        if (tdev == dev) {
                ip_rt_put(rt);
 
        if (tdev == dev) {
                ip_rt_put(rt);
-               tunnel->stat.collisions++;
+               stats->collisions++;
                goto tx_error;
        }
 
                goto tx_error;
        }
 
@@ -785,7 +659,8 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                struct rt6_info *rt6 = (struct rt6_info*)skb->dst;
 
                if (rt6 && mtu < dst_mtu(skb->dst) && mtu >= IPV6_MIN_MTU) {
                struct rt6_info *rt6 = (struct rt6_info*)skb->dst;
 
                if (rt6 && mtu < dst_mtu(skb->dst) && mtu >= IPV6_MIN_MTU) {
-                       if ((tunnel->parms.iph.daddr && !MULTICAST(tunnel->parms.iph.daddr)) ||
+                       if ((tunnel->parms.iph.daddr &&
+                            !ipv4_is_multicast(tunnel->parms.iph.daddr)) ||
                            rt6->rt6i_dst.plen == 128) {
                                rt6->rt6i_flags |= RTF_MODIFIED;
                                skb->dst->metrics[RTAX_MTU-1] = mtu;
                            rt6->rt6i_dst.plen == 128) {
                                rt6->rt6i_flags |= RTF_MODIFIED;
                                skb->dst->metrics[RTAX_MTU-1] = mtu;
@@ -811,11 +686,12 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 
        max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen;
 
 
        max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen;
 
-       if (skb_headroom(skb) < max_headroom || skb_cloned(skb) || skb_shared(skb)) {
+       if (skb_headroom(skb) < max_headroom || skb_shared(skb)||
+           (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
                struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
                if (!new_skb) {
                        ip_rt_put(rt);
                struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
                if (!new_skb) {
                        ip_rt_put(rt);
-                       stats->tx_dropped++;
+                       stats->tx_dropped++;
                        dev_kfree_skb(skb);
                        tunnel->recursion--;
                        return 0;
                        dev_kfree_skb(skb);
                        tunnel->recursion--;
                        return 0;
@@ -824,11 +700,12 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                        skb_set_owner_w(new_skb, skb->sk);
                dev_kfree_skb(skb);
                skb = new_skb;
                        skb_set_owner_w(new_skb, skb->sk);
                dev_kfree_skb(skb);
                skb = new_skb;
-               old_iph = skb->nh.iph;
+               old_iph = ip_hdr(skb);
        }
 
        }
 
-       skb->h.raw = skb->nh.raw;
-       skb->nh.raw = skb_push(skb, gre_hlen);
+       skb->transport_header = skb->network_header;
+       skb_push(skb, gre_hlen);
+       skb_reset_network_header(skb);
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
        IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
                              IPSKB_REROUTED);
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
        IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
                              IPSKB_REROUTED);
@@ -839,7 +716,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
         *      Push down and install the IPIP header.
         */
 
         *      Push down and install the IPIP header.
         */
 
-       iph                     =       skb->nh.iph;
+       iph                     =       ip_hdr(skb);
        iph->version            =       4;
        iph->ihl                =       sizeof(struct iphdr) >> 2;
        iph->frag_off           =       df;
        iph->version            =       4;
        iph->ihl                =       sizeof(struct iphdr) >> 2;
        iph->frag_off           =       df;
@@ -859,11 +736,11 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                        iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
        }
 
                        iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
        }
 
-       ((u16*)(iph+1))[0] = tunnel->parms.o_flags;
-       ((u16*)(iph+1))[1] = skb->protocol;
+       ((__be16*)(iph+1))[0] = tunnel->parms.o_flags;
+       ((__be16*)(iph+1))[1] = skb->protocol;
 
        if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) {
 
        if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) {
-               u32 *ptr = (u32*)(((u8*)iph) + tunnel->hlen - 4);
+               __be32 *ptr = (__be32*)(((u8*)iph) + tunnel->hlen - 4);
 
                if (tunnel->parms.o_flags&GRE_SEQ) {
                        ++tunnel->o_seqno;
 
                if (tunnel->parms.o_flags&GRE_SEQ) {
                        ++tunnel->o_seqno;
@@ -876,7 +753,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                }
                if (tunnel->parms.o_flags&GRE_CSUM) {
                        *ptr = 0;
                }
                if (tunnel->parms.o_flags&GRE_CSUM) {
                        *ptr = 0;
-                       *(__u16*)ptr = ip_compute_csum((void*)(iph+1), skb->len - sizeof(struct iphdr));
+                       *(__sum16*)ptr = ip_compute_csum((void*)(iph+1), skb->len - sizeof(struct iphdr));
                }
        }
 
                }
        }
 
@@ -896,22 +773,77 @@ tx_error:
        return 0;
 }
 
        return 0;
 }
 
+static void ipgre_tunnel_bind_dev(struct net_device *dev)
+{
+       struct net_device *tdev = NULL;
+       struct ip_tunnel *tunnel;
+       struct iphdr *iph;
+       int hlen = LL_MAX_HEADER;
+       int mtu = ETH_DATA_LEN;
+       int addend = sizeof(struct iphdr) + 4;
+
+       tunnel = netdev_priv(dev);
+       iph = &tunnel->parms.iph;
+
+       /* Guess output device to choose reasonable mtu and hard_header_len */
+
+       if (iph->daddr) {
+               struct flowi fl = { .oif = tunnel->parms.link,
+                                   .nl_u = { .ip4_u =
+                                             { .daddr = iph->daddr,
+                                               .saddr = iph->saddr,
+                                               .tos = RT_TOS(iph->tos) } },
+                                   .proto = IPPROTO_GRE };
+               struct rtable *rt;
+               if (!ip_route_output_key(dev_net(dev), &rt, &fl)) {
+                       tdev = rt->u.dst.dev;
+                       ip_rt_put(rt);
+               }
+               dev->flags |= IFF_POINTOPOINT;
+       }
+
+       if (!tdev && tunnel->parms.link)
+               tdev = __dev_get_by_index(dev_net(dev), tunnel->parms.link);
+
+       if (tdev) {
+               hlen = tdev->hard_header_len;
+               mtu = tdev->mtu;
+       }
+       dev->iflink = tunnel->parms.link;
+
+       /* Precalculate GRE options length */
+       if (tunnel->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
+               if (tunnel->parms.o_flags&GRE_CSUM)
+                       addend += 4;
+               if (tunnel->parms.o_flags&GRE_KEY)
+                       addend += 4;
+               if (tunnel->parms.o_flags&GRE_SEQ)
+                       addend += 4;
+       }
+       dev->hard_header_len = hlen + addend;
+       dev->mtu = mtu - addend;
+       tunnel->hlen = addend;
+
+}
+
 static int
 ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        int err = 0;
        struct ip_tunnel_parm p;
        struct ip_tunnel *t;
 static int
 ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        int err = 0;
        struct ip_tunnel_parm p;
        struct ip_tunnel *t;
+       struct net *net = dev_net(dev);
+       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
 
        switch (cmd) {
        case SIOCGETTUNNEL:
                t = NULL;
 
        switch (cmd) {
        case SIOCGETTUNNEL:
                t = NULL;
-               if (dev == ipgre_fb_tunnel_dev) {
+               if (dev == ign->fb_tunnel_dev) {
                        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
                                err = -EFAULT;
                                break;
                        }
                        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
                                err = -EFAULT;
                                break;
                        }
-                       t = ipgre_tunnel_locate(&p, 0);
+                       t = ipgre_tunnel_locate(net, &p, 0);
                }
                if (t == NULL)
                        t = netdev_priv(dev);
                }
                if (t == NULL)
                        t = netdev_priv(dev);
@@ -943,9 +875,9 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                if (!(p.o_flags&GRE_KEY))
                        p.o_key = 0;
 
                if (!(p.o_flags&GRE_KEY))
                        p.o_key = 0;
 
-               t = ipgre_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
+               t = ipgre_tunnel_locate(net, &p, cmd == SIOCADDTUNNEL);
 
 
-               if (dev != ipgre_fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
+               if (dev != ign->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
                        if (t != NULL) {
                                if (t->dev != dev) {
                                        err = -EEXIST;
                        if (t != NULL) {
                                if (t->dev != dev) {
                                        err = -EEXIST;
@@ -956,7 +888,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
 
                                t = netdev_priv(dev);
 
 
                                t = netdev_priv(dev);
 
-                               if (MULTICAST(p.iph.daddr))
+                               if (ipv4_is_multicast(p.iph.daddr))
                                        nflags = IFF_BROADCAST;
                                else if (p.iph.daddr)
                                        nflags = IFF_POINTOPOINT;
                                        nflags = IFF_BROADCAST;
                                else if (p.iph.daddr)
                                        nflags = IFF_POINTOPOINT;
@@ -965,14 +897,14 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                                        err = -EINVAL;
                                        break;
                                }
                                        err = -EINVAL;
                                        break;
                                }
-                               ipgre_tunnel_unlink(t);
+                               ipgre_tunnel_unlink(ign, t);
                                t->parms.iph.saddr = p.iph.saddr;
                                t->parms.iph.daddr = p.iph.daddr;
                                t->parms.i_key = p.i_key;
                                t->parms.o_key = p.o_key;
                                memcpy(dev->dev_addr, &p.iph.saddr, 4);
                                memcpy(dev->broadcast, &p.iph.daddr, 4);
                                t->parms.iph.saddr = p.iph.saddr;
                                t->parms.iph.daddr = p.iph.daddr;
                                t->parms.i_key = p.i_key;
                                t->parms.o_key = p.o_key;
                                memcpy(dev->dev_addr, &p.iph.saddr, 4);
                                memcpy(dev->broadcast, &p.iph.daddr, 4);
-                               ipgre_tunnel_link(t);
+                               ipgre_tunnel_link(ign, t);
                                netdev_state_change(dev);
                        }
                }
                                netdev_state_change(dev);
                        }
                }
@@ -983,6 +915,11 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                                t->parms.iph.ttl = p.iph.ttl;
                                t->parms.iph.tos = p.iph.tos;
                                t->parms.iph.frag_off = p.iph.frag_off;
                                t->parms.iph.ttl = p.iph.ttl;
                                t->parms.iph.tos = p.iph.tos;
                                t->parms.iph.frag_off = p.iph.frag_off;
+                               if (t->parms.link != p.link) {
+                                       t->parms.link = p.link;
+                                       ipgre_tunnel_bind_dev(dev);
+                                       netdev_state_change(dev);
+                               }
                        }
                        if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
                                err = -EFAULT;
                        }
                        if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
                                err = -EFAULT;
@@ -995,19 +932,20 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                if (!capable(CAP_NET_ADMIN))
                        goto done;
 
                if (!capable(CAP_NET_ADMIN))
                        goto done;
 
-               if (dev == ipgre_fb_tunnel_dev) {
+               if (dev == ign->fb_tunnel_dev) {
                        err = -EFAULT;
                        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
                                goto done;
                        err = -ENOENT;
                        err = -EFAULT;
                        if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
                                goto done;
                        err = -ENOENT;
-                       if ((t = ipgre_tunnel_locate(&p, 0)) == NULL)
+                       if ((t = ipgre_tunnel_locate(net, &p, 0)) == NULL)
                                goto done;
                        err = -EPERM;
                                goto done;
                        err = -EPERM;
-                       if (t == netdev_priv(ipgre_fb_tunnel_dev))
+                       if (t == netdev_priv(ign->fb_tunnel_dev))
                                goto done;
                        dev = t->dev;
                }
                                goto done;
                        dev = t->dev;
                }
-               err = unregister_netdevice(dev);
+               unregister_netdevice(dev);
+               err = 0;
                break;
 
        default:
                break;
 
        default:
@@ -1018,11 +956,6 @@ done:
        return err;
 }
 
        return err;
 }
 
-static struct net_device_stats *ipgre_tunnel_get_stats(struct net_device *dev)
-{
-       return &(((struct ip_tunnel*)netdev_priv(dev))->stat);
-}
-
 static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
 static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
@@ -1032,7 +965,6 @@ static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
        return 0;
 }
 
        return 0;
 }
 
-#ifdef CONFIG_NET_IPGRE_BROADCAST
 /* Nice toy. Unfortunately, useless in real life :-)
    It allows to construct virtual multiprotocol broadcast "LAN"
    over the Internet, provided multicast routing is tuned.
 /* Nice toy. Unfortunately, useless in real life :-)
    It allows to construct virtual multiprotocol broadcast "LAN"
    over the Internet, provided multicast routing is tuned.
@@ -1042,7 +974,7 @@ static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
    so that I had to set ARPHRD_IPGRE to a random value.
    I have an impression, that Cisco could make something similar,
    but this feature is apparently missing in IOS<=11.2(8).
    so that I had to set ARPHRD_IPGRE to a random value.
    I have an impression, that Cisco could make something similar,
    but this feature is apparently missing in IOS<=11.2(8).
-   
+
    I set up 10.66.66/24 and fec0:6666:6666::0/96 as virtual networks
    with broadcast 224.66.66.66. If you have access to mbone, play with me :-)
 
    I set up 10.66.66/24 and fec0:6666:6666::0/96 as virtual networks
    with broadcast 224.66.66.66. If you have access to mbone, play with me :-)
 
@@ -1062,21 +994,22 @@ static int ipgre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 
  */
 
 
  */
 
-static int ipgre_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
-                       void *daddr, void *saddr, unsigned len)
+static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
+                       unsigned short type,
+                       const void *daddr, const void *saddr, unsigned len)
 {
        struct ip_tunnel *t = netdev_priv(dev);
        struct iphdr *iph = (struct iphdr *)skb_push(skb, t->hlen);
 {
        struct ip_tunnel *t = netdev_priv(dev);
        struct iphdr *iph = (struct iphdr *)skb_push(skb, t->hlen);
-       u16 *p = (u16*)(iph+1);
+       __be16 *p = (__be16*)(iph+1);
 
        memcpy(iph, &t->parms.iph, sizeof(struct iphdr));
        p[0]            = t->parms.o_flags;
        p[1]            = htons(type);
 
        /*
 
        memcpy(iph, &t->parms.iph, sizeof(struct iphdr));
        p[0]            = t->parms.o_flags;
        p[1]            = htons(type);
 
        /*
-        *      Set the source hardware address. 
+        *      Set the source hardware address.
         */
         */
-        
+
        if (saddr)
                memcpy(&iph->saddr, saddr, 4);
 
        if (saddr)
                memcpy(&iph->saddr, saddr, 4);
 
@@ -1084,17 +1017,30 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev, unsigned sh
                memcpy(&iph->daddr, daddr, 4);
                return t->hlen;
        }
                memcpy(&iph->daddr, daddr, 4);
                return t->hlen;
        }
-       if (iph->daddr && !MULTICAST(iph->daddr))
+       if (iph->daddr && !ipv4_is_multicast(iph->daddr))
                return t->hlen;
                return t->hlen;
-       
+
        return -t->hlen;
 }
 
        return -t->hlen;
 }
 
+static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+       struct iphdr *iph = (struct iphdr*) skb_mac_header(skb);
+       memcpy(haddr, &iph->saddr, 4);
+       return 4;
+}
+
+static const struct header_ops ipgre_header_ops = {
+       .create = ipgre_header,
+       .parse  = ipgre_header_parse,
+};
+
+#ifdef CONFIG_NET_IPGRE_BROADCAST
 static int ipgre_open(struct net_device *dev)
 {
        struct ip_tunnel *t = netdev_priv(dev);
 
 static int ipgre_open(struct net_device *dev)
 {
        struct ip_tunnel *t = netdev_priv(dev);
 
-       if (MULTICAST(t->parms.iph.daddr)) {
+       if (ipv4_is_multicast(t->parms.iph.daddr)) {
                struct flowi fl = { .oif = t->parms.link,
                                    .nl_u = { .ip4_u =
                                              { .daddr = t->parms.iph.daddr,
                struct flowi fl = { .oif = t->parms.link,
                                    .nl_u = { .ip4_u =
                                              { .daddr = t->parms.iph.daddr,
@@ -1102,7 +1048,7 @@ static int ipgre_open(struct net_device *dev)
                                                .tos = RT_TOS(t->parms.iph.tos) } },
                                    .proto = IPPROTO_GRE };
                struct rtable *rt;
                                                .tos = RT_TOS(t->parms.iph.tos) } },
                                    .proto = IPPROTO_GRE };
                struct rtable *rt;
-               if (ip_route_output_key(&rt, &fl))
+               if (ip_route_output_key(dev_net(dev), &rt, &fl))
                        return -EADDRNOTAVAIL;
                dev = rt->u.dst.dev;
                ip_rt_put(rt);
                        return -EADDRNOTAVAIL;
                dev = rt->u.dst.dev;
                ip_rt_put(rt);
@@ -1117,8 +1063,9 @@ static int ipgre_open(struct net_device *dev)
 static int ipgre_close(struct net_device *dev)
 {
        struct ip_tunnel *t = netdev_priv(dev);
 static int ipgre_close(struct net_device *dev)
 {
        struct ip_tunnel *t = netdev_priv(dev);
-       if (MULTICAST(t->parms.iph.daddr) && t->mlink) {
-               struct in_device *in_dev = inetdev_by_index(t->mlink);
+       if (ipv4_is_multicast(t->parms.iph.daddr) && t->mlink) {
+               struct in_device *in_dev;
+               in_dev = inetdev_by_index(dev_net(dev), t->mlink);
                if (in_dev) {
                        ip_mc_dec_group(in_dev, t->parms.iph.daddr);
                        in_dev_put(in_dev);
                if (in_dev) {
                        ip_mc_dec_group(in_dev, t->parms.iph.daddr);
                        in_dev_put(in_dev);
@@ -1131,11 +1078,9 @@ static int ipgre_close(struct net_device *dev)
 
 static void ipgre_tunnel_setup(struct net_device *dev)
 {
 
 static void ipgre_tunnel_setup(struct net_device *dev)
 {
-       SET_MODULE_OWNER(dev);
        dev->uninit             = ipgre_tunnel_uninit;
        dev->destructor         = free_netdev;
        dev->hard_start_xmit    = ipgre_tunnel_xmit;
        dev->uninit             = ipgre_tunnel_uninit;
        dev->destructor         = free_netdev;
        dev->hard_start_xmit    = ipgre_tunnel_xmit;
-       dev->get_stats          = ipgre_tunnel_get_stats;
        dev->do_ioctl           = ipgre_tunnel_ioctl;
        dev->change_mtu         = ipgre_tunnel_change_mtu;
 
        dev->do_ioctl           = ipgre_tunnel_ioctl;
        dev->change_mtu         = ipgre_tunnel_change_mtu;
 
@@ -1145,16 +1090,13 @@ static void ipgre_tunnel_setup(struct net_device *dev)
        dev->flags              = IFF_NOARP;
        dev->iflink             = 0;
        dev->addr_len           = 4;
        dev->flags              = IFF_NOARP;
        dev->iflink             = 0;
        dev->addr_len           = 4;
+       dev->features           |= NETIF_F_NETNS_LOCAL;
 }
 
 static int ipgre_tunnel_init(struct net_device *dev)
 {
 }
 
 static int ipgre_tunnel_init(struct net_device *dev)
 {
-       struct net_device *tdev = NULL;
        struct ip_tunnel *tunnel;
        struct iphdr *iph;
        struct ip_tunnel *tunnel;
        struct iphdr *iph;
-       int hlen = LL_MAX_HEADER;
-       int mtu = ETH_DATA_LEN;
-       int addend = sizeof(struct iphdr) + 4;
 
        tunnel = netdev_priv(dev);
        iph = &tunnel->parms.iph;
 
        tunnel = netdev_priv(dev);
        iph = &tunnel->parms.iph;
@@ -1165,63 +1107,30 @@ static int ipgre_tunnel_init(struct net_device *dev)
        memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
        memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
 
        memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
        memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
 
-       /* Guess output device to choose reasonable mtu and hard_header_len */
+       ipgre_tunnel_bind_dev(dev);
 
        if (iph->daddr) {
 
        if (iph->daddr) {
-               struct flowi fl = { .oif = tunnel->parms.link,
-                                   .nl_u = { .ip4_u =
-                                             { .daddr = iph->daddr,
-                                               .saddr = iph->saddr,
-                                               .tos = RT_TOS(iph->tos) } },
-                                   .proto = IPPROTO_GRE };
-               struct rtable *rt;
-               if (!ip_route_output_key(&rt, &fl)) {
-                       tdev = rt->u.dst.dev;
-                       ip_rt_put(rt);
-               }
-
-               dev->flags |= IFF_POINTOPOINT;
-
 #ifdef CONFIG_NET_IPGRE_BROADCAST
 #ifdef CONFIG_NET_IPGRE_BROADCAST
-               if (MULTICAST(iph->daddr)) {
+               if (ipv4_is_multicast(iph->daddr)) {
                        if (!iph->saddr)
                                return -EINVAL;
                        dev->flags = IFF_BROADCAST;
                        if (!iph->saddr)
                                return -EINVAL;
                        dev->flags = IFF_BROADCAST;
-                       dev->hard_header = ipgre_header;
+                       dev->header_ops = &ipgre_header_ops;
                        dev->open = ipgre_open;
                        dev->stop = ipgre_close;
                }
 #endif
                        dev->open = ipgre_open;
                        dev->stop = ipgre_close;
                }
 #endif
-       }
-
-       if (!tdev && tunnel->parms.link)
-               tdev = __dev_get_by_index(tunnel->parms.link);
+       } else
+               dev->header_ops = &ipgre_header_ops;
 
 
-       if (tdev) {
-               hlen = tdev->hard_header_len;
-               mtu = tdev->mtu;
-       }
-       dev->iflink = tunnel->parms.link;
-
-       /* Precalculate GRE options length */
-       if (tunnel->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
-               if (tunnel->parms.o_flags&GRE_CSUM)
-                       addend += 4;
-               if (tunnel->parms.o_flags&GRE_KEY)
-                       addend += 4;
-               if (tunnel->parms.o_flags&GRE_SEQ)
-                       addend += 4;
-       }
-       dev->hard_header_len = hlen + addend;
-       dev->mtu = mtu - addend;
-       tunnel->hlen = addend;
        return 0;
 }
 
        return 0;
 }
 
-static int __init ipgre_fb_tunnel_init(struct net_device *dev)
+static int ipgre_fb_tunnel_init(struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
        struct iphdr *iph = &tunnel->parms.iph;
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
        struct iphdr *iph = &tunnel->parms.iph;
+       struct ipgre_net *ign = net_generic(dev_net(dev), ipgre_net_id);
 
        tunnel->dev = dev;
        strcpy(tunnel->parms.name, dev->name);
 
        tunnel->dev = dev;
        strcpy(tunnel->parms.name, dev->name);
@@ -1232,7 +1141,7 @@ static int __init ipgre_fb_tunnel_init(struct net_device *dev)
        tunnel->hlen            = sizeof(struct iphdr) + 4;
 
        dev_hold(dev);
        tunnel->hlen            = sizeof(struct iphdr) + 4;
 
        dev_hold(dev);
-       tunnels_wc[0]           = tunnel;
+       ign->tunnels_wc[0]      = tunnel;
        return 0;
 }
 
        return 0;
 }
 
@@ -1240,56 +1149,98 @@ static int __init ipgre_fb_tunnel_init(struct net_device *dev)
 static struct net_protocol ipgre_protocol = {
        .handler        =       ipgre_rcv,
        .err_handler    =       ipgre_err,
 static struct net_protocol ipgre_protocol = {
        .handler        =       ipgre_rcv,
        .err_handler    =       ipgre_err,
+       .netns_ok       =       1,
 };
 
 };
 
+static void ipgre_destroy_tunnels(struct ipgre_net *ign)
+{
+       int prio;
 
 
-/*
- *     And now the modules code and kernel interface.
- */
+       for (prio = 0; prio < 4; prio++) {
+               int h;
+               for (h = 0; h < HASH_SIZE; h++) {
+                       struct ip_tunnel *t;
+                       while ((t = ign->tunnels[prio][h]) != NULL)
+                               unregister_netdevice(t->dev);
+               }
+       }
+}
 
 
-static int __init ipgre_init(void)
+static int ipgre_init_net(struct net *net)
 {
        int err;
 {
        int err;
+       struct ipgre_net *ign;
 
 
-       printk(KERN_INFO "GRE over IPv4 tunneling driver\n");
+       err = -ENOMEM;
+       ign = kzalloc(sizeof(struct ipgre_net), GFP_KERNEL);
+       if (ign == NULL)
+               goto err_alloc;
 
 
-       if (inet_add_protocol(&ipgre_protocol, IPPROTO_GRE) < 0) {
-               printk(KERN_INFO "ipgre init: can't add protocol\n");
-               return -EAGAIN;
-       }
+       err = net_assign_generic(net, ipgre_net_id, ign);
+       if (err < 0)
+               goto err_assign;
 
 
-       ipgre_fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "gre0",
+       ign->fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "gre0",
                                           ipgre_tunnel_setup);
                                           ipgre_tunnel_setup);
-       if (!ipgre_fb_tunnel_dev) {
+       if (!ign->fb_tunnel_dev) {
                err = -ENOMEM;
                err = -ENOMEM;
-               goto err1;
+               goto err_alloc_dev;
        }
 
        }
 
-       ipgre_fb_tunnel_dev->init = ipgre_fb_tunnel_init;
+       ign->fb_tunnel_dev->init = ipgre_fb_tunnel_init;
+       dev_net_set(ign->fb_tunnel_dev, net);
 
 
-       if ((err = register_netdev(ipgre_fb_tunnel_dev)))
-               goto err2;
-out:
+       if ((err = register_netdev(ign->fb_tunnel_dev)))
+               goto err_reg_dev;
+
+       return 0;
+
+err_reg_dev:
+       free_netdev(ign->fb_tunnel_dev);
+err_alloc_dev:
+       /* nothing */
+err_assign:
+       kfree(ign);
+err_alloc:
        return err;
        return err;
-err2:
-       free_netdev(ipgre_fb_tunnel_dev);
-err1:
-       inet_del_protocol(&ipgre_protocol, IPPROTO_GRE);
-       goto out;
 }
 
 }
 
-static void __exit ipgre_destroy_tunnels(void)
+static void ipgre_exit_net(struct net *net)
 {
 {
-       int prio;
+       struct ipgre_net *ign;
 
 
-       for (prio = 0; prio < 4; prio++) {
-               int h;
-               for (h = 0; h < HASH_SIZE; h++) {
-                       struct ip_tunnel *t;
-                       while ((t = tunnels[prio][h]) != NULL)
-                               unregister_netdevice(t->dev);
-               }
+       ign = net_generic(net, ipgre_net_id);
+       rtnl_lock();
+       ipgre_destroy_tunnels(ign);
+       rtnl_unlock();
+       kfree(ign);
+}
+
+static struct pernet_operations ipgre_net_ops = {
+       .init = ipgre_init_net,
+       .exit = ipgre_exit_net,
+};
+
+/*
+ *     And now the modules code and kernel interface.
+ */
+
+static int __init ipgre_init(void)
+{
+       int err;
+
+       printk(KERN_INFO "GRE over IPv4 tunneling driver\n");
+
+       if (inet_add_protocol(&ipgre_protocol, IPPROTO_GRE) < 0) {
+               printk(KERN_INFO "ipgre init: can't add protocol\n");
+               return -EAGAIN;
        }
        }
+
+       err = register_pernet_gen_device(&ipgre_net_id, &ipgre_net_ops);
+       if (err < 0)
+               inet_del_protocol(&ipgre_protocol, IPPROTO_GRE);
+
+       return err;
 }
 
 static void __exit ipgre_fini(void)
 }
 
 static void __exit ipgre_fini(void)
@@ -1297,9 +1248,7 @@ static void __exit ipgre_fini(void)
        if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0)
                printk(KERN_INFO "ipgre close: can't remove protocol\n");
 
        if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0)
                printk(KERN_INFO "ipgre close: can't remove protocol\n");
 
-       rtnl_lock();
-       ipgre_destroy_tunnels();
-       rtnl_unlock();
+       unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops);
 }
 
 module_init(ipgre_init);
 }
 
 module_init(ipgre_init);