net: spread __net_init, __net_exit
[safe/jmp/linux-2.6] / net / ipv4 / ip_gre.c
index c0755e9..7631b20 100644 (file)
    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
-   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.
+   Current solution: HARD_TX_LOCK lock breaks dead loops.
 
 
 
 
 
 
@@ -126,11 +123,9 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev);
 
 /* Fallback tunnel: no source, no destination, no key, no options */
 
 
 /* Fallback tunnel: no source, no destination, no key, no options */
 
-static int ipgre_fb_tunnel_init(struct net_device *dev);
-
 #define HASH_SIZE  16
 
 #define HASH_SIZE  16
 
-static int ipgre_net_id;
+static int ipgre_net_id __read_mostly;
 struct ipgre_net {
        struct ip_tunnel *tunnels[4][HASH_SIZE];
 
 struct ipgre_net {
        struct ip_tunnel *tunnels[4][HASH_SIZE];
 
@@ -161,72 +156,135 @@ struct ipgre_net {
 #define tunnels_r      tunnels[2]
 #define tunnels_l      tunnels[1]
 #define tunnels_wc     tunnels[0]
 #define tunnels_r      tunnels[2]
 #define tunnels_l      tunnels[1]
 #define tunnels_wc     tunnels[0]
+/*
+ * Locking : hash tables are protected by RCU and a spinlock
+ */
+static DEFINE_SPINLOCK(ipgre_lock);
 
 
-static DEFINE_RWLOCK(ipgre_lock);
+#define for_each_ip_tunnel_rcu(start) \
+       for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
 
 /* Given src, dst and key, find appropriate for input tunnel. */
 
 
 /* Given src, dst and key, find appropriate for input tunnel. */
 
-static struct ip_tunnel * ipgre_tunnel_lookup(struct net *net,
+static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
                                              __be32 remote, __be32 local,
                                              __be32 key, __be16 gre_proto)
 {
                                              __be32 remote, __be32 local,
                                              __be32 key, __be16 gre_proto)
 {
+       struct net *net = dev_net(dev);
+       int link = dev->ifindex;
        unsigned h0 = HASH(remote);
        unsigned h1 = HASH(key);
        unsigned h0 = HASH(remote);
        unsigned h1 = HASH(key);
-       struct ip_tunnel *t;
-       struct ip_tunnel *t2 = NULL;
+       struct ip_tunnel *t, *cand = NULL;
        struct ipgre_net *ign = net_generic(net, ipgre_net_id);
        int dev_type = (gre_proto == htons(ETH_P_TEB)) ?
                       ARPHRD_ETHER : ARPHRD_IPGRE;
        struct ipgre_net *ign = net_generic(net, ipgre_net_id);
        int dev_type = (gre_proto == htons(ETH_P_TEB)) ?
                       ARPHRD_ETHER : ARPHRD_IPGRE;
-
-       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) {
-                               if (t->dev->type == dev_type)
-                                       return t;
-                               if (t->dev->type == ARPHRD_IPGRE && !t2)
-                                       t2 = t;
-                       }
+       int score, cand_score = 4;
+
+       for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) {
+               if (local != t->parms.iph.saddr ||
+                   remote != t->parms.iph.daddr ||
+                   key != t->parms.i_key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->dev->type != ARPHRD_IPGRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               score = 0;
+               if (t->parms.link != link)
+                       score |= 1;
+               if (t->dev->type != dev_type)
+                       score |= 2;
+               if (score == 0)
+                       return t;
+
+               if (score < cand_score) {
+                       cand = t;
+                       cand_score = score;
                }
        }
 
                }
        }
 
-       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) {
-                               if (t->dev->type == dev_type)
-                                       return t;
-                               if (t->dev->type == ARPHRD_IPGRE && !t2)
-                                       t2 = t;
-                       }
+       for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) {
+               if (remote != t->parms.iph.daddr ||
+                   key != t->parms.i_key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->dev->type != ARPHRD_IPGRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               score = 0;
+               if (t->parms.link != link)
+                       score |= 1;
+               if (t->dev->type != dev_type)
+                       score |= 2;
+               if (score == 0)
+                       return t;
+
+               if (score < cand_score) {
+                       cand = t;
+                       cand_score = score;
                }
        }
 
                }
        }
 
-       for (t = ign->tunnels_l[h1]; t; t = t->next) {
-               if (local == t->parms.iph.saddr ||
-                    (local == t->parms.iph.daddr &&
-                     ipv4_is_multicast(local))) {
-                       if (t->parms.i_key == key && t->dev->flags & IFF_UP) {
-                               if (t->dev->type == dev_type)
-                                       return t;
-                               if (t->dev->type == ARPHRD_IPGRE && !t2)
-                                       t2 = t;
-                       }
+       for_each_ip_tunnel_rcu(ign->tunnels_l[h1]) {
+               if ((local != t->parms.iph.saddr &&
+                    (local != t->parms.iph.daddr ||
+                     !ipv4_is_multicast(local))) ||
+                   key != t->parms.i_key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->dev->type != ARPHRD_IPGRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               score = 0;
+               if (t->parms.link != link)
+                       score |= 1;
+               if (t->dev->type != dev_type)
+                       score |= 2;
+               if (score == 0)
+                       return t;
+
+               if (score < cand_score) {
+                       cand = t;
+                       cand_score = score;
                }
        }
 
                }
        }
 
-       for (t = ign->tunnels_wc[h1]; t; t = t->next) {
-               if (t->parms.i_key == key && t->dev->flags & IFF_UP) {
-                       if (t->dev->type == dev_type)
-                               return t;
-                       if (t->dev->type == ARPHRD_IPGRE && !t2)
-                               t2 = t;
+       for_each_ip_tunnel_rcu(ign->tunnels_wc[h1]) {
+               if (t->parms.i_key != key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->dev->type != ARPHRD_IPGRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               score = 0;
+               if (t->parms.link != link)
+                       score |= 1;
+               if (t->dev->type != dev_type)
+                       score |= 2;
+               if (score == 0)
+                       return t;
+
+               if (score < cand_score) {
+                       cand = t;
+                       cand_score = score;
                }
        }
 
                }
        }
 
-       if (t2)
-               return t2;
+       if (cand != NULL)
+               return cand;
+
+       dev = ign->fb_tunnel_dev;
+       if (dev->flags & IFF_UP)
+               return netdev_priv(dev);
 
 
-       if (ign->fb_tunnel_dev->flags&IFF_UP)
-               return netdev_priv(ign->fb_tunnel_dev);
        return NULL;
 }
 
        return NULL;
 }
 
@@ -259,10 +317,10 @@ static void ipgre_tunnel_link(struct ipgre_net *ign, struct ip_tunnel *t)
 {
        struct ip_tunnel **tp = ipgre_bucket(ign, t);
 
 {
        struct ip_tunnel **tp = ipgre_bucket(ign, t);
 
+       spin_lock_bh(&ipgre_lock);
        t->next = *tp;
        t->next = *tp;
-       write_lock_bh(&ipgre_lock);
-       *tp = t;
-       write_unlock_bh(&ipgre_lock);
+       rcu_assign_pointer(*tp, t);
+       spin_unlock_bh(&ipgre_lock);
 }
 
 static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)
 }
 
 static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)
@@ -271,9 +329,9 @@ static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t)
 
        for (tp = ipgre_bucket(ign, t); *tp; tp = &(*tp)->next) {
                if (t == *tp) {
 
        for (tp = ipgre_bucket(ign, t); *tp; tp = &(*tp)->next) {
                if (t == *tp) {
-                       write_lock_bh(&ipgre_lock);
+                       spin_lock_bh(&ipgre_lock);
                        *tp = t->next;
                        *tp = t->next;
-                       write_unlock_bh(&ipgre_lock);
+                       spin_unlock_bh(&ipgre_lock);
                        break;
                }
        }
                        break;
                }
        }
@@ -286,6 +344,7 @@ static struct ip_tunnel *ipgre_tunnel_find(struct net *net,
        __be32 remote = parms->iph.daddr;
        __be32 local = parms->iph.saddr;
        __be32 key = parms->i_key;
        __be32 remote = parms->iph.daddr;
        __be32 local = parms->iph.saddr;
        __be32 key = parms->i_key;
+       int link = parms->link;
        struct ip_tunnel *t, **tp;
        struct ipgre_net *ign = net_generic(net, ipgre_net_id);
 
        struct ip_tunnel *t, **tp;
        struct ipgre_net *ign = net_generic(net, ipgre_net_id);
 
@@ -293,6 +352,7 @@ static struct ip_tunnel *ipgre_tunnel_find(struct net *net,
                if (local == t->parms.iph.saddr &&
                    remote == t->parms.iph.daddr &&
                    key == t->parms.i_key &&
                if (local == t->parms.iph.saddr &&
                    remote == t->parms.iph.daddr &&
                    key == t->parms.i_key &&
+                   link == t->parms.link &&
                    type == t->dev->type)
                        break;
 
                    type == t->dev->type)
                        break;
 
@@ -371,7 +431,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
    by themself???
  */
 
    by themself???
  */
 
-       struct iphdr *iph = (struct iphdr*)skb->data;
+       struct iphdr *iph = (struct iphdr *)skb->data;
        __be16       *p = (__be16*)(skb->data+(iph->ihl<<2));
        int grehlen = (iph->ihl<<2) + 4;
        const int type = icmp_hdr(skb)->type;
        __be16       *p = (__be16*)(skb->data+(iph->ihl<<2));
        int grehlen = (iph->ihl<<2) + 4;
        const int type = icmp_hdr(skb)->type;
@@ -422,8 +482,8 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
                break;
        }
 
                break;
        }
 
-       read_lock(&ipgre_lock);
-       t = ipgre_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr,
+       rcu_read_lock();
+       t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr,
                                flags & GRE_KEY ?
                                *(((__be32 *)p) + (grehlen / 4) - 1) : 0,
                                p[1]);
                                flags & GRE_KEY ?
                                *(((__be32 *)p) + (grehlen / 4) - 1) : 0,
                                p[1]);
@@ -434,13 +494,13 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
        if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
                goto out;
 
        if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
                goto out;
 
-       if (jiffies - t->err_time < IPTUNNEL_ERR_TIMEO)
+       if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
                t->err_count++;
        else
                t->err_count = 1;
        t->err_time = jiffies;
 out:
                t->err_count++;
        else
                t->err_count = 1;
        t->err_time = jiffies;
 out:
-       read_unlock(&ipgre_lock);
+       rcu_read_unlock();
        return;
 }
 
        return;
 }
 
@@ -519,8 +579,8 @@ static int ipgre_rcv(struct sk_buff *skb)
 
        gre_proto = *(__be16 *)(h + 2);
 
 
        gre_proto = *(__be16 *)(h + 2);
 
-       read_lock(&ipgre_lock);
-       if ((tunnel = ipgre_tunnel_lookup(dev_net(skb->dev),
+       rcu_read_lock();
+       if ((tunnel = ipgre_tunnel_lookup(skb->dev,
                                          iph->saddr, iph->daddr, key,
                                          gre_proto))) {
                struct net_device_stats *stats = &tunnel->dev->stats;
                                          iph->saddr, iph->daddr, key,
                                          gre_proto))) {
                struct net_device_stats *stats = &tunnel->dev->stats;
@@ -545,7 +605,7 @@ static int ipgre_rcv(struct sk_buff *skb)
 #ifdef CONFIG_NET_IPGRE_BROADCAST
                if (ipv4_is_multicast(iph->daddr)) {
                        /* Looped back packet, drop it! */
 #ifdef CONFIG_NET_IPGRE_BROADCAST
                if (ipv4_is_multicast(iph->daddr)) {
                        /* Looped back packet, drop it! */
-                       if (skb->rtable->fl.iif == 0)
+                       if (skb_rtable(skb)->fl.iif == 0)
                                goto drop;
                        stats->multicast++;
                        skb->pkt_type = PACKET_BROADCAST;
                                goto drop;
                        stats->multicast++;
                        skb->pkt_type = PACKET_BROADCAST;
@@ -586,30 +646,30 @@ static int ipgre_rcv(struct sk_buff *skb)
                stats->rx_packets++;
                stats->rx_bytes += len;
                skb->dev = tunnel->dev;
                stats->rx_packets++;
                stats->rx_bytes += len;
                skb->dev = tunnel->dev;
-               dst_release(skb->dst);
-               skb->dst = NULL;
+               skb_dst_drop(skb);
                nf_reset(skb);
 
                skb_reset_network_header(skb);
                ipgre_ecn_decapsulate(iph, skb);
 
                netif_rx(skb);
                nf_reset(skb);
 
                skb_reset_network_header(skb);
                ipgre_ecn_decapsulate(iph, skb);
 
                netif_rx(skb);
-               read_unlock(&ipgre_lock);
+               rcu_read_unlock();
                return(0);
        }
        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 
 drop:
                return(0);
        }
        icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
 
 drop:
-       read_unlock(&ipgre_lock);
+       rcu_read_unlock();
 drop_nolock:
        kfree_skb(skb);
        return(0);
 }
 
 drop_nolock:
        kfree_skb(skb);
        return(0);
 }
 
-static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
-       struct net_device_stats *stats = &tunnel->dev->stats;
+       struct net_device_stats *stats = &dev->stats;
+       struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
        struct iphdr  *old_iph = ip_hdr(skb);
        struct iphdr  *tiph;
        u8     tos;
        struct iphdr  *old_iph = ip_hdr(skb);
        struct iphdr  *tiph;
        u8     tos;
@@ -622,17 +682,12 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        __be32 dst;
        int    mtu;
 
        __be32 dst;
        int    mtu;
 
-       if (tunnel->recursion++) {
-               stats->collisions++;
-               goto tx_error;
-       }
-
        if (dev->type == ARPHRD_ETHER)
                IPCB(skb)->flags = 0;
 
        if (dev->header_ops && dev->type == ARPHRD_IPGRE) {
                gre_hlen = 0;
        if (dev->type == ARPHRD_ETHER)
                IPCB(skb)->flags = 0;
 
        if (dev->header_ops && dev->type == ARPHRD_IPGRE) {
                gre_hlen = 0;
-               tiph = (struct iphdr*)skb->data;
+               tiph = (struct iphdr *)skb->data;
        } else {
                gre_hlen = tunnel->hlen;
                tiph = &tunnel->parms.iph;
        } else {
                gre_hlen = tunnel->hlen;
                tiph = &tunnel->parms.iph;
@@ -641,13 +696,13 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        if ((dst = tiph->daddr) == 0) {
                /* NBMA tunnel */
 
        if ((dst = tiph->daddr) == 0) {
                /* NBMA tunnel */
 
-               if (skb->dst == NULL) {
+               if (skb_dst(skb) == NULL) {
                        stats->tx_fifo_errors++;
                        goto tx_error;
                }
 
                if (skb->protocol == htons(ETH_P_IP)) {
                        stats->tx_fifo_errors++;
                        goto tx_error;
                }
 
                if (skb->protocol == htons(ETH_P_IP)) {
-                       rt = skb->rtable;
+                       rt = skb_rtable(skb);
                        if ((dst = rt->rt_gateway) == 0)
                                goto tx_error_icmp;
                }
                        if ((dst = rt->rt_gateway) == 0)
                                goto tx_error_icmp;
                }
@@ -655,12 +710,12 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                else if (skb->protocol == htons(ETH_P_IPV6)) {
                        struct in6_addr *addr6;
                        int addr_type;
                else if (skb->protocol == htons(ETH_P_IPV6)) {
                        struct in6_addr *addr6;
                        int addr_type;
-                       struct neighbour *neigh = skb->dst->neighbour;
+                       struct neighbour *neigh = skb_dst(skb)->neighbour;
 
                        if (neigh == NULL)
                                goto tx_error;
 
 
                        if (neigh == NULL)
                                goto tx_error;
 
-                       addr6 = (struct in6_addr*)&neigh->primary_key;
+                       addr6 = (struct in6_addr *)&neigh->primary_key;
                        addr_type = ipv6_addr_type(addr6);
 
                        if (addr_type == IPV6_ADDR_ANY) {
                        addr_type = ipv6_addr_type(addr6);
 
                        if (addr_type == IPV6_ADDR_ANY) {
@@ -679,10 +734,10 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 
        tos = tiph->tos;
        }
 
        tos = tiph->tos;
-       if (tos&1) {
+       if (tos == 1) {
+               tos = 0;
                if (skb->protocol == htons(ETH_P_IP))
                        tos = old_iph->tos;
                if (skb->protocol == htons(ETH_P_IP))
                        tos = old_iph->tos;
-               tos &= ~1;
        }
 
        {
        }
 
        {
@@ -709,10 +764,10 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        if (df)
                mtu = dst_mtu(&rt->u.dst) - dev->hard_header_len - tunnel->hlen;
        else
        if (df)
                mtu = dst_mtu(&rt->u.dst) - dev->hard_header_len - tunnel->hlen;
        else
-               mtu = skb->dst ? dst_mtu(skb->dst) : dev->mtu;
+               mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;
 
 
-       if (skb->dst)
-               skb->dst->ops->update_pmtu(skb->dst, mtu);
+       if (skb_dst(skb))
+               skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu);
 
        if (skb->protocol == htons(ETH_P_IP)) {
                df |= (old_iph->frag_off&htons(IP_DF));
 
        if (skb->protocol == htons(ETH_P_IP)) {
                df |= (old_iph->frag_off&htons(IP_DF));
@@ -726,14 +781,14 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 #ifdef CONFIG_IPV6
        else if (skb->protocol == htons(ETH_P_IPV6)) {
        }
 #ifdef CONFIG_IPV6
        else if (skb->protocol == htons(ETH_P_IPV6)) {
-               struct rt6_info *rt6 = (struct rt6_info*)skb->dst;
+               struct rt6_info *rt6 = (struct rt6_info *)skb_dst(skb);
 
 
-               if (rt6 && mtu < dst_mtu(skb->dst) && mtu >= IPV6_MIN_MTU) {
+               if (rt6 && mtu < dst_mtu(skb_dst(skb)) && mtu >= IPV6_MIN_MTU) {
                        if ((tunnel->parms.iph.daddr &&
                             !ipv4_is_multicast(tunnel->parms.iph.daddr)) ||
                            rt6->rt6i_dst.plen == 128) {
                                rt6->rt6i_flags |= RTF_MODIFIED;
                        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;
+                               skb_dst(skb)->metrics[RTAX_MTU-1] = mtu;
                        }
                }
 
                        }
                }
 
@@ -746,7 +801,8 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
 #endif
 
        if (tunnel->err_count > 0) {
 #endif
 
        if (tunnel->err_count > 0) {
-               if (jiffies - tunnel->err_time < IPTUNNEL_ERR_TIMEO) {
+               if (time_before(jiffies,
+                               tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
                        tunnel->err_count--;
 
                        dst_link_failure(skb);
                        tunnel->err_count--;
 
                        dst_link_failure(skb);
@@ -761,10 +817,9 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                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++;
+                       txq->tx_dropped++;
                        dev_kfree_skb(skb);
                        dev_kfree_skb(skb);
-                       tunnel->recursion--;
-                       return 0;
+                       return NETDEV_TX_OK;
                }
                if (skb->sk)
                        skb_set_owner_w(new_skb, skb->sk);
                }
                if (skb->sk)
                        skb_set_owner_w(new_skb, skb->sk);
@@ -779,8 +834,8 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        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);
-       dst_release(skb->dst);
-       skb->dst = &rt->u.dst;
+       skb_dst_drop(skb);
+       skb_dst_set(skb, &rt->u.dst);
 
        /*
         *      Push down and install the IPIP header.
 
        /*
         *      Push down and install the IPIP header.
@@ -800,7 +855,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
                        iph->ttl = old_iph->ttl;
 #ifdef CONFIG_IPV6
                else if (skb->protocol == htons(ETH_P_IPV6))
                        iph->ttl = old_iph->ttl;
 #ifdef CONFIG_IPV6
                else if (skb->protocol == htons(ETH_P_IPV6))
-                       iph->ttl = ((struct ipv6hdr*)old_iph)->hop_limit;
+                       iph->ttl = ((struct ipv6hdr *)old_iph)->hop_limit;
 #endif
                else
                        iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
 #endif
                else
                        iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
@@ -831,8 +886,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
        nf_reset(skb);
 
        IPTUNNEL_XMIT();
        nf_reset(skb);
 
        IPTUNNEL_XMIT();
-       tunnel->recursion--;
-       return 0;
+       return NETDEV_TX_OK;
 
 tx_error_icmp:
        dst_link_failure(skb);
 
 tx_error_icmp:
        dst_link_failure(skb);
@@ -840,8 +894,7 @@ tx_error_icmp:
 tx_error:
        stats->tx_errors++;
        dev_kfree_skb(skb);
 tx_error:
        stats->tx_errors++;
        dev_kfree_skb(skb);
-       tunnel->recursion--;
-       return 0;
+       return NETDEV_TX_OK;
 }
 
 static int ipgre_tunnel_bind_dev(struct net_device *dev)
 }
 
 static int ipgre_tunnel_bind_dev(struct net_device *dev)
@@ -894,7 +947,7 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev)
                        addend += 4;
        }
        dev->needed_headroom = addend + hlen;
                        addend += 4;
        }
        dev->needed_headroom = addend + hlen;
-       mtu -= dev->hard_header_len - addend;
+       mtu -= dev->hard_header_len + addend;
 
        if (mtu < 68)
                mtu = 68;
 
        if (mtu < 68)
                mtu = 68;
@@ -962,7 +1015,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                                        break;
                                }
                        } else {
                                        break;
                                }
                        } else {
-                               unsigned nflags=0;
+                               unsigned nflags = 0;
 
                                t = netdev_priv(dev);
 
 
                                t = netdev_priv(dev);
 
@@ -1104,7 +1157,7 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev,
 
 static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
 {
 
 static int ipgre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
 {
-       struct iphdr *iph = (struct iphdr*) skb_mac_header(skb);
+       struct iphdr *iph = (struct iphdr *) skb_mac_header(skb);
        memcpy(haddr, &iph->saddr, 4);
        return 4;
 }
        memcpy(haddr, &iph->saddr, 4);
        return 4;
 }
@@ -1142,6 +1195,7 @@ 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 (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 (ipv4_is_multicast(t->parms.iph.daddr) && t->mlink) {
                struct in_device *in_dev;
                in_dev = inetdev_by_index(dev_net(dev), t->mlink);
@@ -1155,14 +1209,22 @@ static int ipgre_close(struct net_device *dev)
 
 #endif
 
 
 #endif
 
+static const struct net_device_ops ipgre_netdev_ops = {
+       .ndo_init               = ipgre_tunnel_init,
+       .ndo_uninit             = ipgre_tunnel_uninit,
+#ifdef CONFIG_NET_IPGRE_BROADCAST
+       .ndo_open               = ipgre_open,
+       .ndo_stop               = ipgre_close,
+#endif
+       .ndo_start_xmit         = ipgre_tunnel_xmit,
+       .ndo_do_ioctl           = ipgre_tunnel_ioctl,
+       .ndo_change_mtu         = ipgre_tunnel_change_mtu,
+};
+
 static void ipgre_tunnel_setup(struct net_device *dev)
 {
 static void ipgre_tunnel_setup(struct net_device *dev)
 {
-       dev->init               = ipgre_tunnel_init;
-       dev->uninit             = ipgre_tunnel_uninit;
+       dev->netdev_ops         = &ipgre_netdev_ops;
        dev->destructor         = free_netdev;
        dev->destructor         = free_netdev;
-       dev->hard_start_xmit    = ipgre_tunnel_xmit;
-       dev->do_ioctl           = ipgre_tunnel_ioctl;
-       dev->change_mtu         = ipgre_tunnel_change_mtu;
 
        dev->type               = ARPHRD_IPGRE;
        dev->needed_headroom    = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
 
        dev->type               = ARPHRD_IPGRE;
        dev->needed_headroom    = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
@@ -1171,6 +1233,7 @@ static void ipgre_tunnel_setup(struct net_device *dev)
        dev->iflink             = 0;
        dev->addr_len           = 4;
        dev->features           |= NETIF_F_NETNS_LOCAL;
        dev->iflink             = 0;
        dev->addr_len           = 4;
        dev->features           |= NETIF_F_NETNS_LOCAL;
+       dev->priv_flags         &= ~IFF_XMIT_DST_RELEASE;
 }
 
 static int ipgre_tunnel_init(struct net_device *dev)
 }
 
 static int ipgre_tunnel_init(struct net_device *dev)
@@ -1194,8 +1257,6 @@ static int ipgre_tunnel_init(struct net_device *dev)
                                return -EINVAL;
                        dev->flags = IFF_BROADCAST;
                        dev->header_ops = &ipgre_header_ops;
                                return -EINVAL;
                        dev->flags = IFF_BROADCAST;
                        dev->header_ops = &ipgre_header_ops;
-                       dev->open = ipgre_open;
-                       dev->stop = ipgre_close;
                }
 #endif
        } else
                }
 #endif
        } else
@@ -1204,7 +1265,7 @@ static int ipgre_tunnel_init(struct net_device *dev)
        return 0;
 }
 
        return 0;
 }
 
-static int ipgre_fb_tunnel_init(struct net_device *dev)
+static void 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;
@@ -1220,43 +1281,36 @@ static int ipgre_fb_tunnel_init(struct net_device *dev)
 
        dev_hold(dev);
        ign->tunnels_wc[0]      = tunnel;
 
        dev_hold(dev);
        ign->tunnels_wc[0]      = tunnel;
-       return 0;
 }
 
 
 }
 
 
-static struct net_protocol ipgre_protocol = {
+static const struct net_protocol ipgre_protocol = {
        .handler        =       ipgre_rcv,
        .err_handler    =       ipgre_err,
        .netns_ok       =       1,
 };
 
        .handler        =       ipgre_rcv,
        .err_handler    =       ipgre_err,
        .netns_ok       =       1,
 };
 
-static void ipgre_destroy_tunnels(struct ipgre_net *ign)
+static void ipgre_destroy_tunnels(struct ipgre_net *ign, struct list_head *head)
 {
        int prio;
 
        for (prio = 0; prio < 4; prio++) {
                int h;
                for (h = 0; h < HASH_SIZE; h++) {
 {
        int prio;
 
        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);
+                       struct ip_tunnel *t = ign->tunnels[prio][h];
+
+                       while (t != NULL) {
+                               unregister_netdevice_queue(t->dev, head);
+                               t = t->next;
+                       }
                }
        }
 }
 
                }
        }
 }
 
-static int ipgre_init_net(struct net *net)
+static int __net_init ipgre_init_net(struct net *net)
 {
 {
+       struct ipgre_net *ign = net_generic(net, ipgre_net_id);
        int err;
        int err;
-       struct ipgre_net *ign;
-
-       err = -ENOMEM;
-       ign = kzalloc(sizeof(struct ipgre_net), GFP_KERNEL);
-       if (ign == NULL)
-               goto err_alloc;
-
-       err = net_assign_generic(net, ipgre_net_id, ign);
-       if (err < 0)
-               goto err_assign;
 
        ign->fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "gre0",
                                           ipgre_tunnel_setup);
 
        ign->fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "gre0",
                                           ipgre_tunnel_setup);
@@ -1264,9 +1318,9 @@ static int ipgre_init_net(struct net *net)
                err = -ENOMEM;
                goto err_alloc_dev;
        }
                err = -ENOMEM;
                goto err_alloc_dev;
        }
-
-       ign->fb_tunnel_dev->init = ipgre_fb_tunnel_init;
        dev_net_set(ign->fb_tunnel_dev, net);
        dev_net_set(ign->fb_tunnel_dev, net);
+
+       ipgre_fb_tunnel_init(ign->fb_tunnel_dev);
        ign->fb_tunnel_dev->rtnl_link_ops = &ipgre_link_ops;
 
        if ((err = register_netdev(ign->fb_tunnel_dev)))
        ign->fb_tunnel_dev->rtnl_link_ops = &ipgre_link_ops;
 
        if ((err = register_netdev(ign->fb_tunnel_dev)))
@@ -1277,27 +1331,26 @@ static int ipgre_init_net(struct net *net)
 err_reg_dev:
        free_netdev(ign->fb_tunnel_dev);
 err_alloc_dev:
 err_reg_dev:
        free_netdev(ign->fb_tunnel_dev);
 err_alloc_dev:
-       /* nothing */
-err_assign:
-       kfree(ign);
-err_alloc:
        return err;
 }
 
        return err;
 }
 
-static void ipgre_exit_net(struct net *net)
+static void __net_exit ipgre_exit_net(struct net *net)
 {
        struct ipgre_net *ign;
 {
        struct ipgre_net *ign;
+       LIST_HEAD(list);
 
        ign = net_generic(net, ipgre_net_id);
        rtnl_lock();
 
        ign = net_generic(net, ipgre_net_id);
        rtnl_lock();
-       ipgre_destroy_tunnels(ign);
+       ipgre_destroy_tunnels(ign, &list);
+       unregister_netdevice_many(&list);
        rtnl_unlock();
        rtnl_unlock();
-       kfree(ign);
 }
 
 static struct pernet_operations ipgre_net_ops = {
        .init = ipgre_init_net,
        .exit = ipgre_exit_net,
 }
 
 static struct pernet_operations ipgre_net_ops = {
        .init = ipgre_init_net,
        .exit = ipgre_exit_net,
+       .id   = &ipgre_net_id,
+       .size = sizeof(struct ipgre_net),
 };
 
 static int ipgre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[])
 };
 
 static int ipgre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[])
@@ -1345,7 +1398,7 @@ out:
 static void ipgre_netlink_parms(struct nlattr *data[],
                                struct ip_tunnel_parm *parms)
 {
 static void ipgre_netlink_parms(struct nlattr *data[],
                                struct ip_tunnel_parm *parms)
 {
-       memset(parms, 0, sizeof(parms));
+       memset(parms, 0, sizeof(*parms));
 
        parms->iph.protocol = IPPROTO_GRE;
 
 
        parms->iph.protocol = IPPROTO_GRE;
 
@@ -1368,10 +1421,10 @@ static void ipgre_netlink_parms(struct nlattr *data[],
                parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]);
 
        if (data[IFLA_GRE_LOCAL])
                parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]);
 
        if (data[IFLA_GRE_LOCAL])
-               memcpy(&parms->iph.saddr, nla_data(data[IFLA_GRE_LOCAL]), 4);
+               parms->iph.saddr = nla_get_be32(data[IFLA_GRE_LOCAL]);
 
        if (data[IFLA_GRE_REMOTE])
 
        if (data[IFLA_GRE_REMOTE])
-               memcpy(&parms->iph.daddr, nla_data(data[IFLA_GRE_REMOTE]), 4);
+               parms->iph.daddr = nla_get_be32(data[IFLA_GRE_REMOTE]);
 
        if (data[IFLA_GRE_TTL])
                parms->iph.ttl = nla_get_u8(data[IFLA_GRE_TTL]);
 
        if (data[IFLA_GRE_TTL])
                parms->iph.ttl = nla_get_u8(data[IFLA_GRE_TTL]);
@@ -1397,22 +1450,28 @@ static int ipgre_tap_init(struct net_device *dev)
        return 0;
 }
 
        return 0;
 }
 
+static const struct net_device_ops ipgre_tap_netdev_ops = {
+       .ndo_init               = ipgre_tap_init,
+       .ndo_uninit             = ipgre_tunnel_uninit,
+       .ndo_start_xmit         = ipgre_tunnel_xmit,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_change_mtu         = ipgre_tunnel_change_mtu,
+};
+
 static void ipgre_tap_setup(struct net_device *dev)
 {
 
        ether_setup(dev);
 
 static void ipgre_tap_setup(struct net_device *dev)
 {
 
        ether_setup(dev);
 
-       dev->init               = ipgre_tap_init;
-       dev->uninit             = ipgre_tunnel_uninit;
+       dev->netdev_ops         = &ipgre_tap_netdev_ops;
        dev->destructor         = free_netdev;
        dev->destructor         = free_netdev;
-       dev->hard_start_xmit    = ipgre_tunnel_xmit;
-       dev->change_mtu         = ipgre_tunnel_change_mtu;
 
        dev->iflink             = 0;
        dev->features           |= NETIF_F_NETNS_LOCAL;
 }
 
 
        dev->iflink             = 0;
        dev->features           |= NETIF_F_NETNS_LOCAL;
 }
 
-static int ipgre_newlink(struct net_device *dev, struct nlattr *tb[],
+static int ipgre_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[],
                         struct nlattr *data[])
 {
        struct ip_tunnel *nt;
                         struct nlattr *data[])
 {
        struct ip_tunnel *nt;
@@ -1466,25 +1525,29 @@ static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[],
                if (t->dev != dev)
                        return -EEXIST;
        } else {
                if (t->dev != dev)
                        return -EEXIST;
        } else {
-               unsigned nflags = 0;
-
                t = nt;
 
                t = nt;
 
-               if (ipv4_is_multicast(p.iph.daddr))
-                       nflags = IFF_BROADCAST;
-               else if (p.iph.daddr)
-                       nflags = IFF_POINTOPOINT;
+               if (dev->type != ARPHRD_ETHER) {
+                       unsigned nflags = 0;
 
 
-               if ((dev->flags ^ nflags) &
-                   (IFF_POINTOPOINT | IFF_BROADCAST))
-                       return -EINVAL;
+                       if (ipv4_is_multicast(p.iph.daddr))
+                               nflags = IFF_BROADCAST;
+                       else if (p.iph.daddr)
+                               nflags = IFF_POINTOPOINT;
+
+                       if ((dev->flags ^ nflags) &
+                           (IFF_POINTOPOINT | IFF_BROADCAST))
+                               return -EINVAL;
+               }
 
                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;
 
                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;
-               memcpy(dev->dev_addr, &p.iph.saddr, 4);
-               memcpy(dev->broadcast, &p.iph.daddr, 4);
+               if (dev->type != ARPHRD_ETHER) {
+                       memcpy(dev->dev_addr, &p.iph.saddr, 4);
+                       memcpy(dev->broadcast, &p.iph.daddr, 4);
+               }
                ipgre_tunnel_link(ign, t);
                netdev_state_change(dev);
        }
                ipgre_tunnel_link(ign, t);
                netdev_state_change(dev);
        }
@@ -1541,8 +1604,8 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
        NLA_PUT_BE16(skb, IFLA_GRE_OFLAGS, p->o_flags);
        NLA_PUT_BE32(skb, IFLA_GRE_IKEY, p->i_key);
        NLA_PUT_BE32(skb, IFLA_GRE_OKEY, p->o_key);
        NLA_PUT_BE16(skb, IFLA_GRE_OFLAGS, p->o_flags);
        NLA_PUT_BE32(skb, IFLA_GRE_IKEY, p->i_key);
        NLA_PUT_BE32(skb, IFLA_GRE_OKEY, p->o_key);
-       NLA_PUT(skb, IFLA_GRE_LOCAL, 4, &p->iph.saddr);
-       NLA_PUT(skb, IFLA_GRE_REMOTE, 4, &p->iph.daddr);
+       NLA_PUT_BE32(skb, IFLA_GRE_LOCAL, p->iph.saddr);
+       NLA_PUT_BE32(skb, IFLA_GRE_REMOTE, p->iph.daddr);
        NLA_PUT_U8(skb, IFLA_GRE_TTL, p->iph.ttl);
        NLA_PUT_U8(skb, IFLA_GRE_TOS, p->iph.tos);
        NLA_PUT_U8(skb, IFLA_GRE_PMTUDISC, !!(p->iph.frag_off & htons(IP_DF)));
        NLA_PUT_U8(skb, IFLA_GRE_TTL, p->iph.ttl);
        NLA_PUT_U8(skb, IFLA_GRE_TOS, p->iph.tos);
        NLA_PUT_U8(skb, IFLA_GRE_PMTUDISC, !!(p->iph.frag_off & htons(IP_DF)));
@@ -1559,8 +1622,8 @@ static const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = {
        [IFLA_GRE_OFLAGS]       = { .type = NLA_U16 },
        [IFLA_GRE_IKEY]         = { .type = NLA_U32 },
        [IFLA_GRE_OKEY]         = { .type = NLA_U32 },
        [IFLA_GRE_OFLAGS]       = { .type = NLA_U16 },
        [IFLA_GRE_IKEY]         = { .type = NLA_U32 },
        [IFLA_GRE_OKEY]         = { .type = NLA_U32 },
-       [IFLA_GRE_LOCAL]        = { .len = 4 },
-       [IFLA_GRE_REMOTE]       = { .len = 4 },
+       [IFLA_GRE_LOCAL]        = { .len = FIELD_SIZEOF(struct iphdr, saddr) },
+       [IFLA_GRE_REMOTE]       = { .len = FIELD_SIZEOF(struct iphdr, daddr) },
        [IFLA_GRE_TTL]          = { .type = NLA_U8 },
        [IFLA_GRE_TOS]          = { .type = NLA_U8 },
        [IFLA_GRE_PMTUDISC]     = { .type = NLA_U8 },
        [IFLA_GRE_TTL]          = { .type = NLA_U8 },
        [IFLA_GRE_TOS]          = { .type = NLA_U8 },
        [IFLA_GRE_PMTUDISC]     = { .type = NLA_U8 },
@@ -1607,7 +1670,7 @@ static int __init ipgre_init(void)
                return -EAGAIN;
        }
 
                return -EAGAIN;
        }
 
-       err = register_pernet_gen_device(&ipgre_net_id, &ipgre_net_ops);
+       err = register_pernet_device(&ipgre_net_ops);
        if (err < 0)
                goto gen_device_failed;
 
        if (err < 0)
                goto gen_device_failed;
 
@@ -1625,7 +1688,7 @@ out:
 tap_ops_failed:
        rtnl_link_unregister(&ipgre_link_ops);
 rtnl_link_failed:
 tap_ops_failed:
        rtnl_link_unregister(&ipgre_link_ops);
 rtnl_link_failed:
-       unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops);
+       unregister_pernet_device(&ipgre_net_ops);
 gen_device_failed:
        inet_del_protocol(&ipgre_protocol, IPPROTO_GRE);
        goto out;
 gen_device_failed:
        inet_del_protocol(&ipgre_protocol, IPPROTO_GRE);
        goto out;
@@ -1635,7 +1698,7 @@ static void __exit ipgre_fini(void)
 {
        rtnl_link_unregister(&ipgre_tap_ops);
        rtnl_link_unregister(&ipgre_link_ops);
 {
        rtnl_link_unregister(&ipgre_tap_ops);
        rtnl_link_unregister(&ipgre_link_ops);
-       unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops);
+       unregister_pernet_device(&ipgre_net_ops);
        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");
 }
@@ -1643,5 +1706,5 @@ static void __exit ipgre_fini(void)
 module_init(ipgre_init);
 module_exit(ipgre_fini);
 MODULE_LICENSE("GPL");
 module_init(ipgre_init);
 module_exit(ipgre_fini);
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("rtnl-link-gre");
-MODULE_ALIAS("rtnl-link-gretap");
+MODULE_ALIAS_RTNL_LINK("gre");
+MODULE_ALIAS_RTNL_LINK("gretap");