[NETFILTER]: nf_nat: add DCCP protocol support
[safe/jmp/linux-2.6] / net / ipv4 / ip_gre.c
index a4c347c..50972b3 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)
  *
  *
  */
 
-#include <linux/config.h>
+#include <linux/capability.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>
@@ -28,6 +27,7 @@
 #include <linux/inetdevice.h>
 #include <linux/igmp.h>
 #include <linux/netfilter_ipv4.h>
+#include <linux/if_ether.h>
 
 #include <net/sock.h>
 #include <net/ip.h>
@@ -62,7 +62,7 @@
    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.
@@ -143,7 +143,7 @@ static struct net_device *ipgre_fb_tunnel_dev;
  */
 
 #define HASH_SIZE  16
-#define HASH(addr) ((addr^(addr>>4))&0xF)
+#define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF)
 
 static struct ip_tunnel *tunnels[4][HASH_SIZE];
 
@@ -156,7 +156,7 @@ 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(__be32 remote, __be32 local, __be32 key)
 {
        unsigned h0 = HASH(remote);
        unsigned h1 = HASH(key);
@@ -176,7 +176,8 @@ static struct ip_tunnel * ipgre_tunnel_lookup(u32 remote, u32 local, u32 key)
        }
        for (t = tunnels_l[h1]; t; t = t->next) {
                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;
                }
@@ -187,21 +188,21 @@ static struct ip_tunnel * ipgre_tunnel_lookup(u32 remote, u32 local, u32 key)
        }
 
        if (ipgre_fb_tunnel_dev->flags&IFF_UP)
-               return ipgre_fb_tunnel_dev->priv;
+               return netdev_priv(ipgre_fb_tunnel_dev);
        return NULL;
 }
 
-static struct ip_tunnel **ipgre_bucket(struct ip_tunnel *t)
+static struct ip_tunnel **__ipgre_bucket(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;
-       if (remote && !MULTICAST(remote)) {
+       if (remote && !ipv4_is_multicast(remote)) {
                prio |= 2;
                h ^= HASH(remote);
        }
@@ -209,6 +210,11 @@ static struct ip_tunnel **ipgre_bucket(struct ip_tunnel *t)
        return &tunnels[prio][h];
 }
 
+static inline struct ip_tunnel **ipgre_bucket(struct ip_tunnel *t)
+{
+       return __ipgre_bucket(&t->parms);
+}
+
 static void ipgre_tunnel_link(struct ip_tunnel *t)
 {
        struct ip_tunnel **tp = ipgre_bucket(t);
@@ -235,22 +241,14 @@ static void ipgre_tunnel_unlink(struct ip_tunnel *t)
 
 static struct ip_tunnel * ipgre_tunnel_locate(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;
-       unsigned h = HASH(key);
-       int prio = 0;
        char name[IFNAMSIZ];
 
-       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(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;
@@ -261,44 +259,37 @@ static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int
 
        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->init = ipgre_tunnel_init;
-       nt = dev->priv;
-       nt->parms = *parms;
-
-       if (register_netdevice(dev) < 0) {
-               free_netdev(dev);
-               goto failed;
+       if (strchr(name, '%')) {
+               if (dev_alloc_name(dev, name) < 0)
+                       goto failed_free;
        }
 
-       nt = dev->priv;
+       dev->init = ipgre_tunnel_init;
+       nt = netdev_priv(dev);
        nt->parms = *parms;
 
+       if (register_netdevice(dev) < 0)
+               goto failed_free;
+
        dev_hold(dev);
        ipgre_tunnel_link(nt);
        return nt;
 
-failed:
+failed_free:
+       free_netdev(dev);
        return NULL;
 }
 
 static void ipgre_tunnel_uninit(struct net_device *dev)
 {
-       ipgre_tunnel_unlink((struct ip_tunnel*)dev->priv);
+       ipgre_tunnel_unlink(netdev_priv(dev));
        dev_put(dev);
 }
 
@@ -321,12 +312,12 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
  */
 
        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 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;
-       u16 flags;
+       __be16 flags;
 
        flags = p[0];
        if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
@@ -372,8 +363,9 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
        }
 
        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(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)
@@ -390,13 +382,14 @@ out:
 #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;
+       __be16       *p = (__be16*)(dp+(iph->ihl<<2));
+       const int type = icmp_hdr(skb)->type;
+       const int code = icmp_hdr(skb)->code;
        int rel_type = 0;
        int rel_code = 0;
-       int rel_info = 0;
-       u16 flags;
+       __be32 rel_info = 0;
+       __u32 n = 0;
+       __be16 flags;
        int grehlen = (iph->ihl<<2) + 4;
        struct sk_buff *skb2;
        struct flowi fl;
@@ -424,14 +417,16 @@ out:
        default:
                return;
        case ICMP_PARAMETERPROB:
-               if (skb->h.icmph->un.gateway < (iph->ihl<<2))
+               n = ntohl(icmp_hdr(skb)->un.gateway) >> 24;
+               if (n < (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;
+               n -= grehlen;
+               rel_info = htonl(n << 24);
                break;
 
        case ICMP_DEST_UNREACH:
@@ -442,13 +437,14 @@ out:
                        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)
+                       n = ntohs(icmp_hdr(skb)->un.frag.mtu);
+                       if (n < grehlen+68)
                                return;
-                       rel_info -= grehlen;
+                       n -= grehlen;
                        /* BSD 4.2 MORE DOES NOT EXIST IN NATURE. */
-                       if (rel_info > ntohs(eiph->tot_len))
+                       if (n > ntohs(eiph->tot_len))
                                return;
+                       rel_info = htonl(n);
                        break;
                default:
                        /* All others are translated to HOST_UNREACH.
@@ -473,14 +469,14 @@ out:
        dst_release(skb2->dst);
        skb2->dst = NULL;
        skb_pull(skb2, skb->data - (u8*)eiph);
-       skb2->nh.raw = skb2->data;
+       skb_reset_network_header(skb2);
 
        /* 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)) {
+       if (ip_route_output_key(&init_net, &rt, &fl)) {
                kfree_skb(skb2);
                return;
        }
@@ -493,7 +489,7 @@ out:
                fl.fl4_dst = eiph->daddr;
                fl.fl4_src = eiph->saddr;
                fl.fl4_tos = eiph->tos;
-               if (ip_route_output_key(&rt, &fl) ||
+               if (ip_route_output_key(&init_net, &rt, &fl) ||
                    rt->u.dst.dev->type != ARPHRD_IPGRE) {
                        ip_rt_put(rt);
                        kfree_skb(skb2);
@@ -510,14 +506,13 @@ out:
 
        /* change mtu on this route */
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
-               if (rel_info > dst_mtu(skb2->dst)) {
+               if (n > dst_mtu(skb2->dst)) {
                        kfree_skb(skb2);
                        return;
                }
-               skb2->dst->ops->update_pmtu(skb2->dst, rel_info);
-               rel_info = htonl(rel_info);
+               skb2->dst->ops->update_pmtu(skb2->dst, n);
        } else if (type == ICMP_TIME_EXCEEDED) {
-               struct ip_tunnel *t = (struct ip_tunnel*)skb2->dev->priv;
+               struct ip_tunnel *t = netdev_priv(skb2->dev);
                if (t->parms.iph.ttl) {
                        rel_type = ICMP_DEST_UNREACH;
                        rel_code = ICMP_HOST_UNREACH;
@@ -533,9 +528,9 @@ 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)) {
-                       IP6_ECN_set_ce(skb->nh.ipv6h);
+                       IP6_ECN_set_ce(ipv6_hdr(skb));
                }
        }
 }
@@ -555,9 +550,9 @@ static int ipgre_rcv(struct sk_buff *skb)
 {
        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;
@@ -565,9 +560,9 @@ static int ipgre_rcv(struct sk_buff *skb)
        if (!pskb_may_pull(skb, 16))
                goto drop_nolock;
 
-       iph = skb->nh.iph;
+       iph = ip_hdr(skb);
        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.
@@ -578,24 +573,24 @@ static int ipgre_rcv(struct sk_buff *skb)
 
                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);
-                               skb->ip_summed = CHECKSUM_HW;
+                               skb->ip_summed = CHECKSUM_COMPLETE;
                        }
                        offset += 4;
                }
                if (flags&GRE_KEY) {
-                       key = *(u32*)(h + offset);
+                       key = *(__be32*)(h + offset);
                        offset += 4;
                }
                if (flags&GRE_SEQ) {
-                       seqno = ntohl(*(u32*)(h + offset));
+                       seqno = ntohl(*(__be32*)(h + offset));
                        offset += 4;
                }
        }
@@ -604,27 +599,27 @@ static int ipgre_rcv(struct sk_buff *skb)
        if ((tunnel = ipgre_tunnel_lookup(iph->saddr, iph->daddr, key)) != NULL) {
                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 &&
-                   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;
                }
 
-               skb->mac.raw = skb->nh.raw;
-               skb->nh.raw = __pskb_pull(skb, offset);
-               skb_postpull_rcsum(skb, skb->mac.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
-               if (MULTICAST(iph->daddr)) {
+               if (ipv4_is_multicast(iph->daddr)) {
                        /* Looped back packet, drop it! */
-                       if (((struct rtable*)skb->dst)->fl.iif == 0)
+                       if (skb->rtable->fl.iif == 0)
                                goto drop;
                        tunnel->stat.multicast++;
                        skb->pkt_type = PACKET_BROADCAST;
@@ -657,7 +652,7 @@ static int ipgre_rcv(struct sk_buff *skb)
                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);
@@ -668,18 +663,18 @@ drop_nolock:
 
 static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-       struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
+       struct ip_tunnel *tunnel = netdev_priv(dev);
        struct net_device_stats *stats = &tunnel->stat;
-       struct iphdr  *old_iph = skb->nh.iph;
+       struct iphdr  *old_iph = ip_hdr(skb);
        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 */
-       int    max_headroom;                    /* The extra header space needed */
+       unsigned int max_headroom;              /* The extra header space needed */
        int    gre_hlen;
-       u32    dst;
+       __be32 dst;
        int    mtu;
 
        if (tunnel->recursion++) {
@@ -687,7 +682,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                goto tx_error;
        }
 
-       if (dev->hard_header) {
+       if (dev->header_ops) {
                gre_hlen = 0;
                tiph = (struct iphdr*)skb->data;
        } else {
@@ -704,7 +699,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                }
 
                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;
                }
@@ -721,7 +716,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) {
-                               addr6 = &skb->nh.ipv6h->daddr;
+                               addr6 = &ipv6_hdr(skb)->daddr;
                                addr_type = ipv6_addr_type(addr6);
                        }
 
@@ -749,7 +744,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                                                .saddr = tiph->saddr,
                                                .tos = RT_TOS(tos) } },
                                    .proto = IPPROTO_GRE };
-               if (ip_route_output_key(&rt, &fl)) {
+               if (ip_route_output_key(&init_net, &rt, &fl)) {
                        tunnel->stat.tx_carrier_errors++;
                        goto tx_error;
                }
@@ -786,7 +781,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) {
-                       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;
@@ -812,11 +808,12 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 
        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);
-                       stats->tx_dropped++;
+                       stats->tx_dropped++;
                        dev_kfree_skb(skb);
                        tunnel->recursion--;
                        return 0;
@@ -825,12 +822,15 @@ 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;
-               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);
        dst_release(skb->dst);
        skb->dst = &rt->u.dst;
 
@@ -838,7 +838,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
         *      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;
@@ -858,11 +858,11 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                        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)) {
-               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;
@@ -875,7 +875,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                }
                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));
                }
        }
 
@@ -895,6 +895,59 @@ tx_error:
        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(&init_net, &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(&init_net, 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)
 {
@@ -913,7 +966,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                        t = ipgre_tunnel_locate(&p, 0);
                }
                if (t == NULL)
-                       t = (struct ip_tunnel*)dev->priv;
+                       t = netdev_priv(dev);
                memcpy(&p, &t->parms, sizeof(p));
                if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
                        err = -EFAULT;
@@ -953,9 +1006,9 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                        } else {
                                unsigned nflags=0;
 
-                               t = (struct ip_tunnel*)dev->priv;
+                               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;
@@ -982,6 +1035,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;
+                               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;
@@ -1002,11 +1060,12 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                        if ((t = ipgre_tunnel_locate(&p, 0)) == NULL)
                                goto done;
                        err = -EPERM;
-                       if (t == ipgre_fb_tunnel_dev->priv)
+                       if (t == netdev_priv(ipgre_fb_tunnel_dev))
                                goto done;
                        dev = t->dev;
                }
-               err = unregister_netdevice(dev);
+               unregister_netdevice(dev);
+               err = 0;
                break;
 
        default:
@@ -1019,19 +1078,18 @@ done:
 
 static struct net_device_stats *ipgre_tunnel_get_stats(struct net_device *dev)
 {
-       return &(((struct ip_tunnel*)dev->priv)->stat);
+       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 = (struct ip_tunnel*)dev->priv;
+       struct ip_tunnel *tunnel = netdev_priv(dev);
        if (new_mtu < 68 || new_mtu > 0xFFF8 - tunnel->hlen)
                return -EINVAL;
        dev->mtu = new_mtu;
        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.
@@ -1041,7 +1099,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).
-   
+
    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 :-)
 
@@ -1061,21 +1119,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 = (struct ip_tunnel*)dev->priv;
+       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);
 
        /*
-        *      Set the source hardware address. 
+        *      Set the source hardware address.
         */
-        
+
        if (saddr)
                memcpy(&iph->saddr, saddr, 4);
 
@@ -1083,17 +1142,30 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev, unsigned sh
                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;
 }
 
+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 = (struct ip_tunnel*)dev->priv;
+       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,
@@ -1101,7 +1173,7 @@ static int ipgre_open(struct net_device *dev)
                                                .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(&init_net, &rt, &fl))
                        return -EADDRNOTAVAIL;
                dev = rt->u.dst.dev;
                ip_rt_put(rt);
@@ -1115,9 +1187,10 @@ static int ipgre_open(struct net_device *dev)
 
 static int ipgre_close(struct net_device *dev)
 {
-       struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
-       if (MULTICAST(t->parms.iph.daddr) && t->mlink) {
-               struct in_device *in_dev = inetdev_by_index(t->mlink);
+       struct ip_tunnel *t = netdev_priv(dev);
+       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);
@@ -1130,7 +1203,6 @@ static int ipgre_close(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;
@@ -1140,7 +1212,7 @@ static void ipgre_tunnel_setup(struct net_device *dev)
 
        dev->type               = ARPHRD_IPGRE;
        dev->hard_header_len    = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
-       dev->mtu                = 1500 - sizeof(struct iphdr) - 4;
+       dev->mtu                = ETH_DATA_LEN - sizeof(struct iphdr) - 4;
        dev->flags              = IFF_NOARP;
        dev->iflink             = 0;
        dev->addr_len           = 4;
@@ -1148,14 +1220,10 @@ static void ipgre_tunnel_setup(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;
-       int hlen = LL_MAX_HEADER;
-       int mtu = 1500;
-       int addend = sizeof(struct iphdr) + 4;
 
-       tunnel = (struct ip_tunnel*)dev->priv;
+       tunnel = netdev_priv(dev);
        iph = &tunnel->parms.iph;
 
        tunnel->dev = dev;
@@ -1164,62 +1232,28 @@ 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);
 
-       /* Guess output device to choose reasonable mtu and hard_header_len */
+       ipgre_tunnel_bind_dev(dev);
 
        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
-               if (MULTICAST(iph->daddr)) {
+               if (ipv4_is_multicast(iph->daddr)) {
                        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
-       }
-
-       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;
 }
 
 static int __init ipgre_fb_tunnel_init(struct net_device *dev)
 {
-       struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
+       struct ip_tunnel *tunnel = netdev_priv(dev);
        struct iphdr *iph = &tunnel->parms.iph;
 
        tunnel->dev = dev;