Merge tag 'v3.10.98' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / ipv6 / ip6_gre.c
index ecd60733e5e24afdb28a52c95686fec28e2e4d73..7eb7267861acbabaa6c96cb7c1f173b1bdc2a582 100644 (file)
@@ -359,6 +359,7 @@ static void ip6gre_tunnel_uninit(struct net_device *dev)
        struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
 
        ip6gre_tunnel_unlink(ign, netdev_priv(dev));
+       ip6_tnl_dst_reset(netdev_priv(dev));
        dev_put(dev);
 }
 
@@ -512,11 +513,11 @@ static int ip6gre_rcv(struct sk_buff *skb)
 
                skb->protocol = gre_proto;
                /* WCCP version 1 and 2 protocol decoding.
-                * - Change protocol to IP
+                * - Change protocol to IPv6
                 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
                 */
                if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) {
-                       skb->protocol = htons(ETH_P_IP);
+                       skb->protocol = htons(ETH_P_IPV6);
                        if ((*(h + offset) & 0xF0) != 0x40)
                                offset += 4;
                }
@@ -620,7 +621,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
        struct ip6_tnl *tunnel = netdev_priv(dev);
        struct net_device *tdev;    /* Device to other host */
        struct ipv6hdr  *ipv6h;     /* Our new IP header */
-       unsigned int max_headroom /* The extra header space needed */
+       unsigned int max_headroom = 0; /* The extra header space needed */
        int    gre_hlen;
        struct ipv6_tel_txoption opt;
        int    mtu;
@@ -693,7 +694,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
                        tunnel->err_count = 0;
        }
 
-       max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len;
+       max_headroom += LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len;
 
        if (skb_headroom(skb) < max_headroom || skb_shared(skb) ||
            (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
@@ -787,7 +788,7 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
                encap_limit = t->parms.encap_limit;
 
        memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
-       fl6.flowi6_proto = IPPROTO_IPIP;
+       fl6.flowi6_proto = IPPROTO_GRE;
 
        dsfield = ipv4_get_dsfield(iph);
 
@@ -837,7 +838,7 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
                encap_limit = t->parms.encap_limit;
 
        memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
-       fl6.flowi6_proto = IPPROTO_IPV6;
+       fl6.flowi6_proto = IPPROTO_GRE;
 
        dsfield = ipv6_get_dsfield(ipv6h);
        if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
@@ -962,8 +963,6 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
        else
                dev->flags &= ~IFF_POINTOPOINT;
 
-       dev->iflink = p->link;
-
        /* Precalculate GRE options length */
        if (t->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
                if (t->parms.o_flags&GRE_CSUM)
@@ -1267,6 +1266,8 @@ static int ip6gre_tunnel_init(struct net_device *dev)
        if (!dev->tstats)
                return -ENOMEM;
 
+       dev->iflink = tunnel->parms.link;
+
        return 0;
 }
 
@@ -1282,7 +1283,6 @@ static void ip6gre_fb_tunnel_init(struct net_device *dev)
        dev_hold(dev);
 }
 
-
 static struct inet6_protocol ip6gre_protocol __read_mostly = {
        .handler     = ip6gre_rcv,
        .err_handler = ip6gre_err,
@@ -1458,6 +1458,8 @@ static int ip6gre_tap_init(struct net_device *dev)
        if (!dev->tstats)
                return -ENOMEM;
 
+       dev->iflink = tunnel->parms.link;
+
        return 0;
 }
 
@@ -1539,16 +1541,23 @@ static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
                        return -EEXIST;
        } else {
                t = nt;
-
-               ip6gre_tunnel_unlink(ign, t);
-               ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]);
-               ip6gre_tunnel_link(ign, t);
-               netdev_state_change(dev);
        }
 
+       ip6gre_tunnel_unlink(ign, t);
+       ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]);
+       ip6gre_tunnel_link(ign, t);
        return 0;
 }
 
+static void ip6gre_dellink(struct net_device *dev, struct list_head *head)
+{
+       struct net *net = dev_net(dev);
+       struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+
+       if (dev != ign->fb_tunnel_dev)
+               unregister_netdevice_queue(dev, head);
+}
+
 static size_t ip6gre_get_size(const struct net_device *dev)
 {
        return
@@ -1626,6 +1635,7 @@ static struct rtnl_link_ops ip6gre_link_ops __read_mostly = {
        .validate       = ip6gre_tunnel_validate,
        .newlink        = ip6gre_newlink,
        .changelink     = ip6gre_changelink,
+       .dellink        = ip6gre_dellink,
        .get_size       = ip6gre_get_size,
        .fill_info      = ip6gre_fill_info,
 };