]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - net/ipv6/tcp_ipv6.c
TCPCT part 1g: Responder Cookie => Initiator
[linux-2.6.git] / net / ipv6 / tcp_ipv6.c
index ebed5d3adb82069f080e71692edc039cd93959c6..fc0a4e5895ee10347338a5f441a0a9425a5664c3 100644 (file)
@@ -23,6 +23,7 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+#include <linux/bottom_half.h>
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/scatterlist.h>
 
 static void    tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb);
-static void    tcp_v6_reqsk_send_ack(struct sk_buff *skb, struct request_sock *req);
-static void    tcp_v6_send_check(struct sock *sk, int len,
-                                 struct sk_buff *skb);
+static void    tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
+                                     struct request_sock *req);
 
 static int     tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
 
-static struct inet_connection_sock_af_ops ipv6_mapped;
-static struct inet_connection_sock_af_ops ipv6_specific;
+static const struct inet_connection_sock_af_ops ipv6_mapped;
+static const struct inet_connection_sock_af_ops ipv6_specific;
 #ifdef CONFIG_TCP_MD5SIG
-static struct tcp_sock_af_ops tcp_sock_ipv6_specific;
-static struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific;
+static const struct tcp_sock_af_ops tcp_sock_ipv6_specific;
+static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific;
 #else
 static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,
                                                   struct in6_addr *addr)
@@ -101,7 +101,7 @@ static void tcp_v6_hash(struct sock *sk)
        }
 }
 
-static __inline__ __sum16 tcp_v6_check(struct tcphdr *th, int len,
+static __inline__ __sum16 tcp_v6_check(int len,
                                   struct in6_addr *saddr,
                                   struct in6_addr *daddr,
                                   __wsum base)
@@ -226,10 +226,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 #endif
                        goto failure;
                } else {
-                       ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000FFFF),
-                                     inet->saddr);
-                       ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000FFFF),
-                                     inet->rcv_saddr);
+                       ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr);
+                       ipv6_addr_set_v4mapped(inet->inet_rcv_saddr,
+                                              &np->rcv_saddr);
                }
 
                return err;
@@ -243,8 +242,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        ipv6_addr_copy(&fl.fl6_src,
                       (saddr ? saddr : &np->saddr));
        fl.oif = sk->sk_bound_dev_if;
+       fl.mark = sk->sk_mark;
        fl.fl_ip_dport = usin->sin6_port;
-       fl.fl_ip_sport = inet->sport;
+       fl.fl_ip_sport = inet->inet_sport;
 
        if (np->opt && np->opt->srcrt) {
                struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
@@ -261,7 +261,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        if (final_p)
                ipv6_addr_copy(&fl.fl6_dst, final_p);
 
-       if ((err = __xfrm_lookup(&dst, &fl, sk, XFRM_LOOKUP_WAIT)) < 0) {
+       err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT);
+       if (err < 0) {
                if (err == -EREMOTE)
                        err = ip6_dst_blackhole(sk, &dst, &fl);
                if (err < 0)
@@ -275,7 +276,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 
        /* set the source address */
        ipv6_addr_copy(&np->saddr, saddr);
-       inet->rcv_saddr = LOOPBACK4_IPV6;
+       inet->inet_rcv_saddr = LOOPBACK4_IPV6;
 
        sk->sk_gso_type = SKB_GSO_TCPV6;
        __ip6_dst_store(sk, dst, NULL, NULL);
@@ -287,7 +288,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 
        tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
 
-       inet->dport = usin->sin6_port;
+       inet->inet_dport = usin->sin6_port;
 
        tcp_set_state(sk, TCP_SYN_SENT);
        err = inet6_hash_connect(&tcp_death_row, sk);
@@ -297,8 +298,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        if (!tp->write_seq)
                tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32,
                                                             np->daddr.s6_addr32,
-                                                            inet->sport,
-                                                            inet->dport);
+                                                            inet->inet_sport,
+                                                            inet->inet_dport);
 
        err = tcp_connect(sk);
        if (err)
@@ -310,13 +311,13 @@ late_failure:
        tcp_set_state(sk, TCP_CLOSE);
        __sk_dst_reset(sk);
 failure:
-       inet->dport = 0;
+       inet->inet_dport = 0;
        sk->sk_route_caps = 0;
        return err;
 }
 
 static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
-               int type, int code, int offset, __be32 info)
+               u8 type, u8 code, int offset, __be32 info)
 {
        struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data;
        const struct tcphdr *th = (struct tcphdr *)(skb->data+offset);
@@ -325,12 +326,14 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        int err;
        struct tcp_sock *tp;
        __u32 seq;
+       struct net *net = dev_net(skb->dev);
 
-       sk = inet6_lookup(dev_net(skb->dev), &tcp_hashinfo, &hdr->daddr,
+       sk = inet6_lookup(net, &tcp_hashinfo, &hdr->daddr,
                        th->dest, &hdr->saddr, th->source, skb->dev->ifindex);
 
        if (sk == NULL) {
-               ICMP6_INC_STATS_BH(__in6_dev_get(skb->dev), ICMP6_MIB_INERRORS);
+               ICMP6_INC_STATS_BH(net, __in6_dev_get(skb->dev),
+                                  ICMP6_MIB_INERRORS);
                return;
        }
 
@@ -341,7 +344,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
        bh_lock_sock(sk);
        if (sock_owned_by_user(sk))
-               NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);
+               NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
 
        if (sk->sk_state == TCP_CLOSE)
                goto out;
@@ -350,7 +353,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        seq = ntohl(th->seq);
        if (sk->sk_state != TCP_LISTEN &&
            !between(seq, tp->snd_una, tp->snd_nxt)) {
-               NET_INC_STATS_BH(LINUX_MIB_OUTOFWINDOWICMPS);
+               NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
                goto out;
        }
 
@@ -380,8 +383,9 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                        ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
                        ipv6_addr_copy(&fl.fl6_src, &np->saddr);
                        fl.oif = sk->sk_bound_dev_if;
-                       fl.fl_ip_dport = inet->dport;
-                       fl.fl_ip_sport = inet->sport;
+                       fl.mark = sk->sk_mark;
+                       fl.fl_ip_dport = inet->inet_dport;
+                       fl.fl_ip_sport = inet->inet_sport;
                        security_skb_classify_flow(skb, &fl);
 
                        if ((err = ip6_dst_lookup(sk, &dst, &fl))) {
@@ -389,7 +393,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                                goto out;
                        }
 
-                       if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
+                       if ((err = xfrm_lookup(net, &dst, &fl, sk, 0)) < 0) {
                                sk->sk_err_soft = -err;
                                goto out;
                        }
@@ -422,10 +426,10 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                /* ICMPs are not backlogged, hence we cannot get
                 * an established socket here.
                 */
-               BUG_TRAP(req->sk == NULL);
+               WARN_ON(req->sk != NULL);
 
                if (seq != tcp_rsk(req)->snt_isn) {
-                       NET_INC_STATS_BH(LINUX_MIB_OUTOFWINDOWICMPS);
+                       NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
                        goto out;
                }
 
@@ -457,7 +461,8 @@ out:
 }
 
 
-static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req)
+static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
+                             struct request_values *rvp)
 {
        struct inet6_request_sock *treq = inet6_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -474,8 +479,9 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req)
        ipv6_addr_copy(&fl.fl6_src, &treq->loc_addr);
        fl.fl6_flowlabel = 0;
        fl.oif = treq->iif;
+       fl.mark = sk->sk_mark;
        fl.fl_ip_dport = inet_rsk(req)->rmt_port;
-       fl.fl_ip_sport = inet_sk(sk)->sport;
+       fl.fl_ip_sport = inet_rsk(req)->loc_port;
        security_req_classify_flow(req, &fl);
 
        opt = np->opt;
@@ -491,16 +497,16 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req)
                goto done;
        if (final_p)
                ipv6_addr_copy(&fl.fl6_dst, final_p);
-       if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+       if ((err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0)
                goto done;
 
-       skb = tcp_make_synack(sk, dst, req);
+       skb = tcp_make_synack(sk, dst, req, rvp);
        if (skb) {
                struct tcphdr *th = tcp_hdr(skb);
 
-               th->check = tcp_v6_check(th, skb->len,
+               th->check = tcp_v6_check(skb->len,
                                         &treq->loc_addr, &treq->rmt_addr,
-                                        csum_partial((char *)th, skb->len, skb->csum));
+                                        csum_partial(th, skb->len, skb->csum));
 
                ipv6_addr_copy(&fl.fl6_dst, &treq->rmt_addr);
                err = ip6_xmit(sk, skb, &fl, opt, 0);
@@ -530,8 +536,7 @@ static inline void syn_flood_warning(struct sk_buff *skb)
 
 static void tcp_v6_reqsk_destructor(struct request_sock *req)
 {
-       if (inet6_rsk(req)->pktopts)
-               kfree_skb(inet6_rsk(req)->pktopts);
+       kfree_skb(inet6_rsk(req)->pktopts);
 }
 
 #ifdef CONFIG_TCP_MD5SIG
@@ -589,7 +594,7 @@ static int tcp_v6_md5_do_add(struct sock *sk, struct in6_addr *peer,
                        }
                        sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
                }
-               if (tcp_alloc_md5sig_pool() == NULL) {
+               if (tcp_alloc_md5sig_pool(sk) == NULL) {
                        kfree(newkey);
                        return -ENOMEM;
                }
@@ -737,64 +742,105 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
        return tcp_v6_md5_do_add(sk, &sin6->sin6_addr, newkey, cmd.tcpm_keylen);
 }
 
-static int tcp_v6_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
-                                  struct in6_addr *saddr,
-                                  struct in6_addr *daddr,
-                                  struct tcphdr *th, unsigned int tcplen)
+static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
+                                       struct in6_addr *daddr,
+                                       struct in6_addr *saddr, int nbytes)
 {
-       struct tcp_md5sig_pool *hp;
        struct tcp6_pseudohdr *bp;
-       int err;
-
-       hp = tcp_get_md5sig_pool();
-       if (!hp) {
-               printk(KERN_WARNING "%s(): hash pool not found...\n", __func__);
-               goto clear_hash_noput;
-       }
+       struct scatterlist sg;
 
        bp = &hp->md5_blk.ip6;
-
        /* 1. TCP pseudo-header (RFC2460) */
        ipv6_addr_copy(&bp->saddr, saddr);
        ipv6_addr_copy(&bp->daddr, daddr);
-       bp->len = htonl(tcplen);
-       bp->protocol = htonl(IPPROTO_TCP);
+       bp->protocol = cpu_to_be32(IPPROTO_TCP);
+       bp->len = cpu_to_be32(nbytes);
 
-       err = tcp_calc_md5_hash(md5_hash, key, sizeof(*bp),
-                               th, tcplen, hp);
+       sg_init_one(&sg, bp, sizeof(*bp));
+       return crypto_hash_update(&hp->md5_desc, &sg, sizeof(*bp));
+}
 
-       if (err)
+static int tcp_v6_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
+                              struct in6_addr *daddr, struct in6_addr *saddr,
+                              struct tcphdr *th)
+{
+       struct tcp_md5sig_pool *hp;
+       struct hash_desc *desc;
+
+       hp = tcp_get_md5sig_pool();
+       if (!hp)
+               goto clear_hash_noput;
+       desc = &hp->md5_desc;
+
+       if (crypto_hash_init(desc))
+               goto clear_hash;
+       if (tcp_v6_md5_hash_pseudoheader(hp, daddr, saddr, th->doff << 2))
+               goto clear_hash;
+       if (tcp_md5_hash_header(hp, th))
+               goto clear_hash;
+       if (tcp_md5_hash_key(hp, key))
+               goto clear_hash;
+       if (crypto_hash_final(desc, md5_hash))
                goto clear_hash;
 
-       /* Free up the crypto pool */
        tcp_put_md5sig_pool();
-out:
        return 0;
+
 clear_hash:
        tcp_put_md5sig_pool();
 clear_hash_noput:
        memset(md5_hash, 0, 16);
-       goto out;
+       return 1;
 }
 
-static int tcp_v6_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
-                               struct sock *sk,
-                               struct dst_entry *dst,
-                               struct request_sock *req,
-                               struct tcphdr *th, unsigned int tcplen)
+static int tcp_v6_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
+                              struct sock *sk, struct request_sock *req,
+                              struct sk_buff *skb)
 {
        struct in6_addr *saddr, *daddr;
+       struct tcp_md5sig_pool *hp;
+       struct hash_desc *desc;
+       struct tcphdr *th = tcp_hdr(skb);
 
        if (sk) {
                saddr = &inet6_sk(sk)->saddr;
                daddr = &inet6_sk(sk)->daddr;
-       } else {
+       } else if (req) {
                saddr = &inet6_rsk(req)->loc_addr;
                daddr = &inet6_rsk(req)->rmt_addr;
+       } else {
+               struct ipv6hdr *ip6h = ipv6_hdr(skb);
+               saddr = &ip6h->saddr;
+               daddr = &ip6h->daddr;
        }
-       return tcp_v6_do_calc_md5_hash(md5_hash, key,
-                                      saddr, daddr,
-                                      th, tcplen);
+
+       hp = tcp_get_md5sig_pool();
+       if (!hp)
+               goto clear_hash_noput;
+       desc = &hp->md5_desc;
+
+       if (crypto_hash_init(desc))
+               goto clear_hash;
+
+       if (tcp_v6_md5_hash_pseudoheader(hp, daddr, saddr, skb->len))
+               goto clear_hash;
+       if (tcp_md5_hash_header(hp, th))
+               goto clear_hash;
+       if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2))
+               goto clear_hash;
+       if (tcp_md5_hash_key(hp, key))
+               goto clear_hash;
+       if (crypto_hash_final(desc, md5_hash))
+               goto clear_hash;
+
+       tcp_put_md5sig_pool();
+       return 0;
+
+clear_hash:
+       tcp_put_md5sig_pool();
+clear_hash_noput:
+       memset(md5_hash, 0, 16);
+       return 1;
 }
 
 static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb)
@@ -809,44 +855,31 @@ static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb)
        hash_expected = tcp_v6_md5_do_lookup(sk, &ip6h->saddr);
        hash_location = tcp_parse_md5sig_option(th);
 
-       /* do we have a hash as expected? */
-       if (!hash_expected) {
-               if (!hash_location)
-                       return 0;
-               if (net_ratelimit()) {
-                       printk(KERN_INFO "MD5 Hash NOT expected but found "
-                              "(" NIP6_FMT ", %u)->"
-                              "(" NIP6_FMT ", %u)\n",
-                              NIP6(ip6h->saddr), ntohs(th->source),
-                              NIP6(ip6h->daddr), ntohs(th->dest));
-               }
+       /* We've parsed the options - do we have a hash? */
+       if (!hash_expected && !hash_location)
+               return 0;
+
+       if (hash_expected && !hash_location) {
+               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
                return 1;
        }
 
-       if (!hash_location) {
-               if (net_ratelimit()) {
-                       printk(KERN_INFO "MD5 Hash expected but NOT found "
-                              "(" NIP6_FMT ", %u)->"
-                              "(" NIP6_FMT ", %u)\n",
-                              NIP6(ip6h->saddr), ntohs(th->source),
-                              NIP6(ip6h->daddr), ntohs(th->dest));
-               }
+       if (!hash_expected && hash_location) {
+               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED);
                return 1;
        }
 
        /* check the signature */
-       genhash = tcp_v6_do_calc_md5_hash(newhash,
-                                         hash_expected,
-                                         &ip6h->saddr, &ip6h->daddr,
-                                         th, skb->len);
+       genhash = tcp_v6_md5_hash_skb(newhash,
+                                     hash_expected,
+                                     NULL, NULL, skb);
+
        if (genhash || memcmp(hash_location, newhash, 16) != 0) {
                if (net_ratelimit()) {
-                       printk(KERN_INFO "MD5 Hash %s for "
-                              "(" NIP6_FMT ", %u)->"
-                              "(" NIP6_FMT ", %u)\n",
+                       printk(KERN_INFO "MD5 Hash %s for (%pI6, %u)->(%pI6, %u)\n",
                               genhash ? "failed" : "mismatch",
-                              NIP6(ip6h->saddr), ntohs(th->source),
-                              NIP6(ip6h->daddr), ntohs(th->dest));
+                              &ip6h->saddr, ntohs(th->source),
+                              &ip6h->daddr, ntohs(th->dest));
                }
                return 1;
        }
@@ -864,8 +897,9 @@ struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
 };
 
 #ifdef CONFIG_TCP_MD5SIG
-static struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
+static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
        .md5_lookup     =       tcp_v6_reqsk_md5_lookup,
+       .calc_md5_hash  =       tcp_v6_md5_hash_skb,
 };
 #endif
 
@@ -886,7 +920,7 @@ static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
                skb->csum_offset = offsetof(struct tcphdr, check);
        } else {
                th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,
-                                           csum_partial((char *)th, th->doff<<2,
+                                           csum_partial(th, th->doff<<2,
                                                         skb->csum));
        }
 }
@@ -911,119 +945,50 @@ static int tcp_v6_gso_send_check(struct sk_buff *skb)
        return 0;
 }
 
-static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
+static struct sk_buff **tcp6_gro_receive(struct sk_buff **head,
+                                        struct sk_buff *skb)
 {
-       struct tcphdr *th = tcp_hdr(skb), *t1;
-       struct sk_buff *buff;
-       struct flowi fl;
-       struct net *net = dev_net(skb->dst->dev);
-       struct sock *ctl_sk = net->ipv6.tcp_sk;
-       unsigned int tot_len = sizeof(*th);
-#ifdef CONFIG_TCP_MD5SIG
-       struct tcp_md5sig_key *key;
-#endif
-
-       if (th->rst)
-               return;
-
-       if (!ipv6_unicast_destination(skb))
-               return;
-
-#ifdef CONFIG_TCP_MD5SIG
-       if (sk)
-               key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr);
-       else
-               key = NULL;
-
-       if (key)
-               tot_len += TCPOLEN_MD5SIG_ALIGNED;
-#endif
-
-       /*
-        * We need to grab some memory, and put together an RST,
-        * and then put it into the queue to be sent.
-        */
-
-       buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) + tot_len,
-                        GFP_ATOMIC);
-       if (buff == NULL)
-               return;
-
-       skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr) + tot_len);
-
-       t1 = (struct tcphdr *) skb_push(buff, tot_len);
-
-       /* Swap the send and the receive. */
-       memset(t1, 0, sizeof(*t1));
-       t1->dest = th->source;
-       t1->source = th->dest;
-       t1->doff = tot_len / 4;
-       t1->rst = 1;
+       struct ipv6hdr *iph = skb_gro_network_header(skb);
 
-       if(th->ack) {
-               t1->seq = th->ack_seq;
-       } else {
-               t1->ack = 1;
-               t1->ack_seq = htonl(ntohl(th->seq) + th->syn + th->fin
-                                   + skb->len - (th->doff<<2));
-       }
+       switch (skb->ip_summed) {
+       case CHECKSUM_COMPLETE:
+               if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr,
+                                 skb->csum)) {
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+                       break;
+               }
 
-#ifdef CONFIG_TCP_MD5SIG
-       if (key) {
-               __be32 *opt = (__be32*)(t1 + 1);
-               opt[0] = htonl((TCPOPT_NOP << 24) |
-                              (TCPOPT_NOP << 16) |
-                              (TCPOPT_MD5SIG << 8) |
-                              TCPOLEN_MD5SIG);
-               tcp_v6_do_calc_md5_hash((__u8 *)&opt[1], key,
-                                       &ipv6_hdr(skb)->daddr,
-                                       &ipv6_hdr(skb)->saddr,
-                                       t1, tot_len);
+               /* fall through */
+       case CHECKSUM_NONE:
+               NAPI_GRO_CB(skb)->flush = 1;
+               return NULL;
        }
-#endif
 
-       buff->csum = csum_partial((char *)t1, sizeof(*t1), 0);
-
-       memset(&fl, 0, sizeof(fl));
-       ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr);
-       ipv6_addr_copy(&fl.fl6_src, &ipv6_hdr(skb)->daddr);
-
-       t1->check = csum_ipv6_magic(&fl.fl6_src, &fl.fl6_dst,
-                                   sizeof(*t1), IPPROTO_TCP,
-                                   buff->csum);
-
-       fl.proto = IPPROTO_TCP;
-       fl.oif = inet6_iif(skb);
-       fl.fl_ip_dport = t1->dest;
-       fl.fl_ip_sport = t1->source;
-       security_skb_classify_flow(skb, &fl);
+       return tcp_gro_receive(head, skb);
+}
 
-       /* Pass a socket to ip6_dst_lookup either it is for RST
-        * Underlying function will use this to retrieve the network
-        * namespace
-        */
-       if (!ip6_dst_lookup(ctl_sk, &buff->dst, &fl)) {
+static int tcp6_gro_complete(struct sk_buff *skb)
+{
+       struct ipv6hdr *iph = ipv6_hdr(skb);
+       struct tcphdr *th = tcp_hdr(skb);
 
-               if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {
-                       ip6_xmit(ctl_sk, buff, &fl, NULL, 0);
-                       TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
-                       TCP_INC_STATS_BH(TCP_MIB_OUTRSTS);
-                       return;
-               }
-       }
+       th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
+                                 &iph->saddr, &iph->daddr, 0);
+       skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
 
-       kfree_skb(buff);
+       return tcp_gro_complete(skb);
 }
 
-static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts,
-                           struct tcp_md5sig_key *key)
+static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
+                                u32 ts, struct tcp_md5sig_key *key, int rst)
 {
        struct tcphdr *th = tcp_hdr(skb), *t1;
        struct sk_buff *buff;
        struct flowi fl;
-       struct net *net = dev_net(skb->dev);
+       struct net *net = dev_net(skb_dst(skb)->dev);
        struct sock *ctl_sk = net->ipv6.tcp_sk;
        unsigned int tot_len = sizeof(struct tcphdr);
+       struct dst_entry *dst;
        __be32 *topt;
 
        if (ts)
@@ -1040,16 +1005,18 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32
 
        skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr) + tot_len);
 
-       t1 = (struct tcphdr *) skb_push(buff,tot_len);
+       t1 = (struct tcphdr *) skb_push(buff, tot_len);
+       skb_reset_transport_header(skb);
 
        /* Swap the send and the receive. */
        memset(t1, 0, sizeof(*t1));
        t1->dest = th->source;
        t1->source = th->dest;
-       t1->doff = tot_len/4;
+       t1->doff = tot_len / 4;
        t1->seq = htonl(seq);
        t1->ack_seq = htonl(ack);
-       t1->ack = 1;
+       t1->ack = !rst || !th->ack;
+       t1->rst = rst;
        t1->window = htons(win);
 
        topt = (__be32 *)(t1 + 1);
@@ -1058,21 +1025,20 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32
                *topt++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
                                (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
                *topt++ = htonl(tcp_time_stamp);
-               *topt = htonl(ts);
+               *topt++ = htonl(ts);
        }
 
 #ifdef CONFIG_TCP_MD5SIG
        if (key) {
                *topt++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
                                (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG);
-               tcp_v6_do_calc_md5_hash((__u8 *)topt, key,
-                                       &ipv6_hdr(skb)->daddr,
-                                       &ipv6_hdr(skb)->saddr,
-                                       t1, tot_len);
+               tcp_v6_md5_hash_hdr((__u8 *)topt, key,
+                                   &ipv6_hdr(skb)->saddr,
+                                   &ipv6_hdr(skb)->daddr, t1);
        }
 #endif
 
-       buff->csum = csum_partial((char *)t1, tot_len, 0);
+       buff->csum = csum_partial(t1, tot_len, 0);
 
        memset(&fl, 0, sizeof(fl));
        ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr);
@@ -1088,10 +1054,17 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32
        fl.fl_ip_sport = t1->source;
        security_skb_classify_flow(skb, &fl);
 
-       if (!ip6_dst_lookup(ctl_sk, &buff->dst, &fl)) {
-               if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {
+       /* Pass a socket to ip6_dst_lookup either it is for RST
+        * Underlying function will use this to retrieve the network
+        * namespace
+        */
+       if (!ip6_dst_lookup(ctl_sk, &dst, &fl)) {
+               if (xfrm_lookup(net, &dst, &fl, NULL, 0) >= 0) {
+                       skb_dst_set(buff, dst);
                        ip6_xmit(ctl_sk, buff, &fl, NULL, 0);
-                       TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
+                       TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
+                       if (rst)
+                               TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS);
                        return;
                }
        }
@@ -1099,6 +1072,38 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32
        kfree_skb(buff);
 }
 
+static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
+{
+       struct tcphdr *th = tcp_hdr(skb);
+       u32 seq = 0, ack_seq = 0;
+       struct tcp_md5sig_key *key = NULL;
+
+       if (th->rst)
+               return;
+
+       if (!ipv6_unicast_destination(skb))
+               return;
+
+#ifdef CONFIG_TCP_MD5SIG
+       if (sk)
+               key = tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr);
+#endif
+
+       if (th->ack)
+               seq = ntohl(th->ack_seq);
+       else
+               ack_seq = ntohl(th->seq) + th->syn + th->fin + skb->len -
+                         (th->doff << 2);
+
+       tcp_v6_send_response(skb, seq, ack_seq, 0, 0, key, 1);
+}
+
+static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts,
+                           struct tcp_md5sig_key *key)
+{
+       tcp_v6_send_response(skb, seq, ack, win, ts, key, 0);
+}
+
 static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
 {
        struct inet_timewait_sock *tw = inet_twsk(sk);
@@ -1111,10 +1116,11 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
        inet_twsk_put(tw);
 }
 
-static void tcp_v6_reqsk_send_ack(struct sk_buff *skb, struct request_sock *req)
+static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
+                                 struct request_sock *req)
 {
        tcp_v6_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, req->ts_recent,
-                       tcp_v6_md5_do_lookup(skb->sk, &ipv6_hdr(skb)->daddr));
+                       tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr));
 }
 
 
@@ -1156,11 +1162,14 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
  */
 static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 {
+       struct tcp_extend_values tmp_ext;
+       struct tcp_options_received tmp_opt;
+       u8 *hash_location;
+       struct request_sock *req;
        struct inet6_request_sock *treq;
        struct ipv6_pinfo *np = inet6_sk(sk);
-       struct tcp_options_received tmp_opt;
        struct tcp_sock *tp = tcp_sk(sk);
-       struct request_sock *req = NULL;
+       struct dst_entry *dst = __sk_dst_get(sk);
        __u32 isn = TCP_SKB_CB(skb)->when;
 #ifdef CONFIG_SYN_COOKIES
        int want_cookie = 0;
@@ -1199,8 +1208,52 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        tcp_clear_options(&tmp_opt);
        tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
        tmp_opt.user_mss = tp->rx_opt.user_mss;
+       tcp_parse_options(skb, &tmp_opt, &hash_location, 0, dst);
+
+       if (tmp_opt.cookie_plus > 0 &&
+           tmp_opt.saw_tstamp &&
+           !tp->rx_opt.cookie_out_never &&
+           (sysctl_tcp_cookie_size > 0 ||
+            (tp->cookie_values != NULL &&
+             tp->cookie_values->cookie_desired > 0))) {
+               u8 *c;
+               u32 *d;
+               u32 *mess = &tmp_ext.cookie_bakery[COOKIE_DIGEST_WORDS];
+               int l = tmp_opt.cookie_plus - TCPOLEN_COOKIE_BASE;
+
+               if (tcp_cookie_generator(&tmp_ext.cookie_bakery[0]) != 0)
+                       goto drop_and_free;
+
+               /* Secret recipe starts with IP addresses */
+               d = &ipv6_hdr(skb)->daddr.s6_addr32[0];
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+               d = &ipv6_hdr(skb)->saddr.s6_addr32[0];
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+               *mess++ ^= *d++;
+
+               /* plus variable length Initiator Cookie */
+               c = (u8 *)mess;
+               while (l-- > 0)
+                       *c++ ^= *hash_location++;
 
-       tcp_parse_options(skb, &tmp_opt, 0);
+#ifdef CONFIG_SYN_COOKIES
+               want_cookie = 0;        /* not our kind of cookie */
+#endif
+               tmp_ext.cookie_out_never = 0; /* false */
+               tmp_ext.cookie_plus = tmp_opt.cookie_plus;
+       } else if (!tp->rx_opt.cookie_in_always) {
+               /* redundant indications, but ensure initialization. */
+               tmp_ext.cookie_out_never = 1; /* true */
+               tmp_ext.cookie_plus = 0;
+       } else {
+               goto drop_and_free;
+       }
+       tmp_ext.cookie_in_always = tp->rx_opt.cookie_in_always;
 
        if (want_cookie && !tmp_opt.saw_tstamp)
                tcp_clear_options(&tmp_opt);
@@ -1233,23 +1286,21 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 
                isn = tcp_v6_init_sequence(skb);
        }
-
        tcp_rsk(req)->snt_isn = isn;
 
        security_inet_conn_request(sk, skb, req);
 
-       if (tcp_v6_send_synack(sk, req))
-               goto drop;
+       if (tcp_v6_send_synack(sk, req,
+                              (struct request_values *)&tmp_ext) ||
+           want_cookie)
+               goto drop_and_free;
 
-       if (!want_cookie) {
-               inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
-               return 0;
-       }
+       inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
+       return 0;
 
+drop_and_free:
+       reqsk_free(req);
 drop:
-       if (req)
-               reqsk_free(req);
-
        return 0; /* don't send reset */
 }
 
@@ -1257,7 +1308,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                                          struct request_sock *req,
                                          struct dst_entry *dst)
 {
-       struct inet6_request_sock *treq = inet6_rsk(req);
+       struct inet6_request_sock *treq;
        struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
        struct tcp6_sock *newtcp6sk;
        struct inet_sock *newinet;
@@ -1287,11 +1338,9 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 
                memcpy(newnp, np, sizeof(struct ipv6_pinfo));
 
-               ipv6_addr_set(&newnp->daddr, 0, 0, htonl(0x0000FFFF),
-                             newinet->daddr);
+               ipv6_addr_set_v4mapped(newinet->inet_daddr, &newnp->daddr);
 
-               ipv6_addr_set(&newnp->saddr, 0, 0, htonl(0x0000FFFF),
-                             newinet->saddr);
+               ipv6_addr_set_v4mapped(newinet->inet_saddr, &newnp->saddr);
 
                ipv6_addr_copy(&newnp->rcv_saddr, &newnp->saddr);
 
@@ -1321,6 +1370,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                return newsk;
        }
 
+       treq = inet6_rsk(req);
        opt = np->opt;
 
        if (sk_acceptq_is_full(sk))
@@ -1341,8 +1391,9 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                }
                ipv6_addr_copy(&fl.fl6_src, &treq->loc_addr);
                fl.oif = sk->sk_bound_dev_if;
+               fl.mark = sk->sk_mark;
                fl.fl_ip_dport = inet_rsk(req)->rmt_port;
-               fl.fl_ip_sport = inet_sk(sk)->sport;
+               fl.fl_ip_sport = inet_rsk(req)->loc_port;
                security_req_classify_flow(req, &fl);
 
                if (ip6_dst_lookup(sk, &dst, &fl))
@@ -1351,7 +1402,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                if (final_p)
                        ipv6_addr_copy(&fl.fl6_dst, final_p);
 
-               if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+               if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0)
                        goto out;
        }
 
@@ -1427,7 +1478,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        newtp->advmss = dst_metric(dst, RTAX_ADVMSS);
        tcp_initialize_rcv_mss(newsk);
 
-       newinet->daddr = newinet->saddr = newinet->rcv_saddr = LOOPBACK4_IPV6;
+       newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6;
+       newinet->inet_rcv_saddr = LOOPBACK4_IPV6;
 
 #ifdef CONFIG_TCP_MD5SIG
        /* Copy over the MD5 key from the original socket */
@@ -1439,7 +1491,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                 */
                char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC);
                if (newkey != NULL)
-                       tcp_v6_md5_do_add(newsk, &inet6_sk(sk)->daddr,
+                       tcp_v6_md5_do_add(newsk, &newnp->daddr,
                                          newkey, key->keylen);
        }
 #endif
@@ -1450,9 +1502,9 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        return newsk;
 
 out_overflow:
-       NET_INC_STATS_BH(LINUX_MIB_LISTENOVERFLOWS);
+       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
 out:
-       NET_INC_STATS_BH(LINUX_MIB_LISTENDROPS);
+       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
        if (opt && opt != np->opt)
                sock_kfree_s(sk, opt, opt->tot_len);
        dst_release(dst);
@@ -1462,14 +1514,14 @@ out:
 static __sum16 tcp_v6_checksum_init(struct sk_buff *skb)
 {
        if (skb->ip_summed == CHECKSUM_COMPLETE) {
-               if (!tcp_v6_check(tcp_hdr(skb), skb->len, &ipv6_hdr(skb)->saddr,
+               if (!tcp_v6_check(skb->len, &ipv6_hdr(skb)->saddr,
                                  &ipv6_hdr(skb)->daddr, skb->csum)) {
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
                        return 0;
                }
        }
 
-       skb->csum = ~csum_unfold(tcp_v6_check(tcp_hdr(skb), skb->len,
+       skb->csum = ~csum_unfold(tcp_v6_check(skb->len,
                                              &ipv6_hdr(skb)->saddr,
                                              &ipv6_hdr(skb)->daddr, 0));
 
@@ -1581,7 +1633,7 @@ discard:
        kfree_skb(skb);
        return 0;
 csum_err:
-       TCP_INC_STATS_BH(TCP_MIB_INERRS);
+       TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
        goto discard;
 
 
@@ -1609,8 +1661,7 @@ ipv6_pktoptions:
                }
        }
 
-       if (opt_skb)
-               kfree_skb(opt_skb);
+       kfree_skb(opt_skb);
        return 0;
 }
 
@@ -1619,6 +1670,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)
        struct tcphdr *th;
        struct sock *sk;
        int ret;
+       struct net *net = dev_net(skb->dev);
 
        if (skb->pkt_type != PACKET_HOST)
                goto discard_it;
@@ -1626,7 +1678,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)
        /*
         *      Count it even if it's bad.
         */
-       TCP_INC_STATS_BH(TCP_MIB_INSEGS);
+       TCP_INC_STATS_BH(net, TCP_MIB_INSEGS);
 
        if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
                goto discard_it;
@@ -1650,11 +1702,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)
        TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(ipv6_hdr(skb));
        TCP_SKB_CB(skb)->sacked = 0;
 
-       sk = __inet6_lookup(dev_net(skb->dev), &tcp_hashinfo,
-                       &ipv6_hdr(skb)->saddr, th->source,
-                       &ipv6_hdr(skb)->daddr, ntohs(th->dest),
-                       inet6_iif(skb));
-
+       sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
        if (!sk)
                goto no_tcp_socket;
 
@@ -1676,7 +1724,7 @@ process:
 #ifdef CONFIG_NET_DMA
                struct tcp_sock *tp = tcp_sk(sk);
                if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
-                       tp->ucopy.dma_chan = get_softnet_dma();
+                       tp->ucopy.dma_chan = dma_find_channel(DMA_MEMCPY);
                if (tp->ucopy.dma_chan)
                        ret = tcp_v6_do_rcv(sk, skb);
                else
@@ -1698,7 +1746,7 @@ no_tcp_socket:
 
        if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
 bad_packet:
-               TCP_INC_STATS_BH(TCP_MIB_INERRS);
+               TCP_INC_STATS_BH(net, TCP_MIB_INERRS);
        } else {
                tcp_v6_send_reset(NULL, skb);
        }
@@ -1723,7 +1771,7 @@ do_time_wait:
        }
 
        if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
-               TCP_INC_STATS_BH(TCP_MIB_INERRS);
+               TCP_INC_STATS_BH(net, TCP_MIB_INERRS);
                inet_twsk_put(inet_twsk(sk));
                goto discard_it;
        }
@@ -1761,7 +1809,7 @@ static int tcp_v6_remember_stamp(struct sock *sk)
        return 0;
 }
 
-static struct inet_connection_sock_af_ops ipv6_specific = {
+static const struct inet_connection_sock_af_ops ipv6_specific = {
        .queue_xmit        = inet6_csk_xmit,
        .send_check        = tcp_v6_send_check,
        .rebuild_header    = inet6_sk_rebuild_header,
@@ -1781,9 +1829,9 @@ static struct inet_connection_sock_af_ops ipv6_specific = {
 };
 
 #ifdef CONFIG_TCP_MD5SIG
-static struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
+static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
        .md5_lookup     =       tcp_v6_md5_lookup,
-       .calc_md5_hash  =       tcp_v6_calc_md5_hash,
+       .calc_md5_hash  =       tcp_v6_md5_hash_skb,
        .md5_add        =       tcp_v6_md5_add_func,
        .md5_parse      =       tcp_v6_parse_md5_keys,
 };
@@ -1793,7 +1841,7 @@ static struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
  *     TCP over IPv4 via INET6 API
  */
 
-static struct inet_connection_sock_af_ops ipv6_mapped = {
+static const struct inet_connection_sock_af_ops ipv6_mapped = {
        .queue_xmit        = ip_queue_xmit,
        .send_check        = tcp_v4_send_check,
        .rebuild_header    = inet_sk_rebuild_header,
@@ -1813,9 +1861,9 @@ static struct inet_connection_sock_af_ops ipv6_mapped = {
 };
 
 #ifdef CONFIG_TCP_MD5SIG
-static struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
+static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
        .md5_lookup     =       tcp_v4_md5_lookup,
-       .calc_md5_hash  =       tcp_v4_calc_md5_hash,
+       .calc_md5_hash  =       tcp_v4_md5_hash_skb,
        .md5_add        =       tcp_v6_md5_add_func,
        .md5_parse      =       tcp_v6_parse_md5_keys,
 };
@@ -1846,9 +1894,9 @@ static int tcp_v6_init_sock(struct sock *sk)
        /* See draft-stevens-tcpca-spec-01 for discussion of the
         * initialization of these values.
         */
-       tp->snd_ssthresh = 0x7fffffff;
+       tp->snd_ssthresh = TCP_INFINITE_SSTHRESH;
        tp->snd_cwnd_clamp = ~0;
-       tp->mss_cache = 536;
+       tp->mss_cache = TCP_MSS_DEFAULT;
 
        tp->reordering = sysctl_tcp_reordering;
 
@@ -1864,15 +1912,30 @@ static int tcp_v6_init_sock(struct sock *sk)
        tp->af_specific = &tcp_sock_ipv6_specific;
 #endif
 
+       /* TCP Cookie Transactions */
+       if (sysctl_tcp_cookie_size > 0) {
+               /* Default, cookies without s_data_payload. */
+               tp->cookie_values =
+                       kzalloc(sizeof(*tp->cookie_values),
+                               sk->sk_allocation);
+               if (tp->cookie_values != NULL)
+                       kref_init(&tp->cookie_values->kref);
+       }
+       /* Presumed zeroed, in order of appearance:
+        *      cookie_in_always, cookie_out_never,
+        *      s_data_constant, s_data_in, s_data_out
+        */
        sk->sk_sndbuf = sysctl_tcp_wmem[1];
        sk->sk_rcvbuf = sysctl_tcp_rmem[1];
 
-       atomic_inc(&tcp_sockets_allocated);
+       local_bh_disable();
+       percpu_counter_inc(&tcp_sockets_allocated);
+       local_bh_enable();
 
        return 0;
 }
 
-static int tcp_v6_destroy_sock(struct sock *sk)
+static void tcp_v6_destroy_sock(struct sock *sk)
 {
 #ifdef CONFIG_TCP_MD5SIG
        /* Clean up the MD5 key list */
@@ -1880,7 +1943,7 @@ static int tcp_v6_destroy_sock(struct sock *sk)
                tcp_v6_clear_md5_list(sk);
 #endif
        tcp_v4_destroy_sock(sk);
-       return inet6_destroy_sock(sk);
+       inet6_destroy_sock(sk);
 }
 
 #ifdef CONFIG_PROC_FS
@@ -1901,7 +1964,7 @@ static void get_openreq6(struct seq_file *seq,
                   i,
                   src->s6_addr32[0], src->s6_addr32[1],
                   src->s6_addr32[2], src->s6_addr32[3],
-                  ntohs(inet_sk(sk)->sport),
+                  ntohs(inet_rsk(req)->loc_port),
                   dest->s6_addr32[0], dest->s6_addr32[1],
                   dest->s6_addr32[2], dest->s6_addr32[3],
                   ntohs(inet_rsk(req)->rmt_port),
@@ -1929,8 +1992,8 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
 
        dest  = &np->daddr;
        src   = &np->rcv_saddr;
-       destp = ntohs(inet->dport);
-       srcp  = ntohs(inet->sport);
+       destp = ntohs(inet->inet_dport);
+       srcp  = ntohs(inet->inet_sport);
 
        if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
                timer_active    = 1;
@@ -1948,7 +2011,7 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
 
        seq_printf(seq,
                   "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
-                  "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %u %u %u %u %d\n",
+                  "%02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %lu %lu %u %u %d\n",
                   i,
                   src->s6_addr32[0], src->s6_addr32[1],
                   src->s6_addr32[2], src->s6_addr32[3], srcp,
@@ -1964,10 +2027,11 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
                   icsk->icsk_probes_out,
                   sock_i_ino(sp),
                   atomic_read(&sp->sk_refcnt), sp,
-                  icsk->icsk_rto,
-                  icsk->icsk_ack.ato,
+                  jiffies_to_clock_t(icsk->icsk_rto),
+                  jiffies_to_clock_t(icsk->icsk_ack.ato),
                   (icsk->icsk_ack.quick << 1 ) | icsk->icsk_ack.pingpong,
-                  tp->snd_cwnd, tp->snd_ssthresh>=0xFFFF?-1:tp->snd_ssthresh
+                  tp->snd_cwnd,
+                  tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh
                   );
 }
 
@@ -2081,6 +2145,7 @@ struct proto tcpv6_prot = {
        .sysctl_rmem            = sysctl_tcp_rmem,
        .max_header             = MAX_TCP_HEADER,
        .obj_size               = sizeof(struct tcp6_sock),
+       .slab_flags             = SLAB_DESTROY_BY_RCU,
        .twsk_prot              = &tcp6_timewait_sock_ops,
        .rsk_prot               = &tcp6_request_sock_ops,
        .h.hashinfo             = &tcp_hashinfo,
@@ -2090,11 +2155,13 @@ struct proto tcpv6_prot = {
 #endif
 };
 
-static struct inet6_protocol tcpv6_protocol = {
+static const struct inet6_protocol tcpv6_protocol = {
        .handler        =       tcp_v6_rcv,
        .err_handler    =       tcp_v6_err,
        .gso_send_check =       tcp_v6_gso_send_check,
        .gso_segment    =       tcp_tso_segment,
+       .gro_receive    =       tcp6_gro_receive,
+       .gro_complete   =       tcp6_gro_complete,
        .flags          =       INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
@@ -2103,7 +2170,6 @@ static struct inet_protosw tcpv6_protosw = {
        .protocol       =       IPPROTO_TCP,
        .prot           =       &tcpv6_prot,
        .ops            =       &inet6_stream_ops,
-       .capability     =       -1,
        .no_check       =       0,
        .flags          =       INET_PROTOSW_PERMANENT |
                                INET_PROTOSW_ICSK,
@@ -2118,6 +2184,7 @@ static int tcpv6_net_init(struct net *net)
 static void tcpv6_net_exit(struct net *net)
 {
        inet_ctl_sock_destroy(net->ipv6.tcp_sk);
+       inet_twsk_purge(net, &tcp_hashinfo, &tcp_death_row, AF_INET6);
 }
 
 static struct pernet_operations tcpv6_net_ops = {