]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - net/ipv6/tcp_ipv6.c
[NET]: Split skb->csum
[linux-2.6.git] / net / ipv6 / tcp_ipv6.c
index b9c7003b7f8b895809a6b6a3a773cf5fd9e65836..517c50024bfcc1060780c41d8d3d7079c56ddf0e 100644 (file)
@@ -26,7 +26,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -47,6 +46,8 @@
 
 #include <net/tcp.h>
 #include <net/ndisc.h>
+#include <net/inet6_hashtables.h>
+#include <net/inet6_connection_sock.h>
 #include <net/ipv6.h>
 #include <net/transp_v6.h>
 #include <net/addrconf.h>
 #include <net/addrconf.h>
 #include <net/snmp.h>
 #include <net/dsfield.h>
+#include <net/timewait_sock.h>
 
 #include <asm/uaccess.h>
 
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 
-static void    tcp_v6_send_reset(struct sk_buff *skb);
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+
+/* Socket used for sending RSTs and ACKs */
+static struct socket *tcp6_socket;
+
+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, struct tcphdr *th, int len, 
+static void    tcp_v6_send_check(struct sock *sk, int len, 
                                  struct sk_buff *skb);
 
 static int     tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
-static int     tcp_v6_xmit(struct sk_buff *skb, int ipfragok);
-
-static struct tcp_func ipv6_mapped;
-static struct tcp_func ipv6_specific;
-
-/* I have no idea if this is a good hash for v6 or not. -DaveM */
-static __inline__ int tcp_v6_hashfn(struct in6_addr *laddr, u16 lport,
-                                   struct in6_addr *faddr, u16 fport)
-{
-       int hashent = (lport ^ fport);
-
-       hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]);
-       hashent ^= hashent>>16;
-       hashent ^= hashent>>8;
-       return (hashent & (tcp_hashinfo.ehash_size - 1));
-}
-
-static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
-{
-       struct inet_sock *inet = inet_sk(sk);
-       struct ipv6_pinfo *np = inet6_sk(sk);
-       struct in6_addr *laddr = &np->rcv_saddr;
-       struct in6_addr *faddr = &np->daddr;
-       __u16 lport = inet->num;
-       __u16 fport = inet->dport;
-       return tcp_v6_hashfn(laddr, lport, faddr, fport);
-}
-
-static inline int tcp_v6_bind_conflict(const struct sock *sk,
-                                      const struct inet_bind_bucket *tb)
-{
-       const struct sock *sk2;
-       const struct hlist_node *node;
-
-       /* We must walk the whole port owner list in this case. -DaveM */
-       sk_for_each_bound(sk2, node, &tb->owners) {
-               if (sk != sk2 &&
-                   (!sk->sk_bound_dev_if ||
-                    !sk2->sk_bound_dev_if ||
-                    sk->sk_bound_dev_if == sk2->sk_bound_dev_if) &&
-                   (!sk->sk_reuse || !sk2->sk_reuse ||
-                    sk2->sk_state == TCP_LISTEN) &&
-                    ipv6_rcv_saddr_equal(sk, sk2))
-                       break;
-       }
 
-       return node != NULL;
-}
+static struct inet_connection_sock_af_ops ipv6_mapped;
+static 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;
+#endif
 
-/* Grrr, addr_type already calculated by caller, but I don't want
- * to add some silly "cookie" argument to this method just for that.
- * But it doesn't matter, the recalculation is in the rarest path
- * this function ever takes.
- */
 static int tcp_v6_get_port(struct sock *sk, unsigned short snum)
 {
-       struct inet_bind_hashbucket *head;
-       struct inet_bind_bucket *tb;
-       struct hlist_node *node;
-       int ret;
-
-       local_bh_disable();
-       if (snum == 0) {
-               int low = sysctl_local_port_range[0];
-               int high = sysctl_local_port_range[1];
-               int remaining = (high - low) + 1;
-               int rover;
-
-               spin_lock(&tcp_hashinfo.portalloc_lock);
-               if (tcp_hashinfo.port_rover < low)
-                       rover = low;
-               else
-                       rover = tcp_hashinfo.port_rover;
-               do {    rover++;
-                       if (rover > high)
-                               rover = low;
-                       head = &tcp_hashinfo.bhash[inet_bhashfn(rover, tcp_hashinfo.bhash_size)];
-                       spin_lock(&head->lock);
-                       inet_bind_bucket_for_each(tb, node, &head->chain)
-                               if (tb->port == rover)
-                                       goto next;
-                       break;
-               next:
-                       spin_unlock(&head->lock);
-               } while (--remaining > 0);
-               tcp_hashinfo.port_rover = rover;
-               spin_unlock(&tcp_hashinfo.portalloc_lock);
-
-               /* Exhausted local port range during search?  It is not
-                * possible for us to be holding one of the bind hash
-                * locks if this test triggers, because if 'remaining'
-                * drops to zero, we broke out of the do/while loop at
-                * the top level, not from the 'break;' statement.
-                */
-               ret = 1;
-               if (unlikely(remaining <= 0))
-                       goto fail;
-
-               /* OK, here is the one we will use. */
-               snum = rover;
-       } else {
-               head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)];
-               spin_lock(&head->lock);
-               inet_bind_bucket_for_each(tb, node, &head->chain)
-                       if (tb->port == snum)
-                               goto tb_found;
-       }
-       tb = NULL;
-       goto tb_not_found;
-tb_found:
-       if (tb && !hlist_empty(&tb->owners)) {
-               if (tb->fastreuse > 0 && sk->sk_reuse &&
-                   sk->sk_state != TCP_LISTEN) {
-                       goto success;
-               } else {
-                       ret = 1;
-                       if (tcp_v6_bind_conflict(sk, tb))
-                               goto fail_unlock;
-               }
-       }
-tb_not_found:
-       ret = 1;
-       if (tb == NULL) {
-               tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, snum);
-               if (tb == NULL)
-                       goto fail_unlock;
-       }
-       if (hlist_empty(&tb->owners)) {
-               if (sk->sk_reuse && sk->sk_state != TCP_LISTEN)
-                       tb->fastreuse = 1;
-               else
-                       tb->fastreuse = 0;
-       } else if (tb->fastreuse &&
-                  (!sk->sk_reuse || sk->sk_state == TCP_LISTEN))
-               tb->fastreuse = 0;
-
-success:
-       if (!inet_csk(sk)->icsk_bind_hash)
-               inet_bind_hash(sk, tb, snum);
-       BUG_TRAP(inet_csk(sk)->icsk_bind_hash == tb);
-       ret = 0;
-
-fail_unlock:
-       spin_unlock(&head->lock);
-fail:
-       local_bh_enable();
-       return ret;
+       return inet_csk_get_port(&tcp_hashinfo, sk, snum,
+                                inet6_csk_bind_conflict);
 }
 
-static __inline__ void __tcp_v6_hash(struct sock *sk)
-{
-       struct hlist_head *list;
-       rwlock_t *lock;
-
-       BUG_TRAP(sk_unhashed(sk));
-
-       if (sk->sk_state == TCP_LISTEN) {
-               list = &tcp_hashinfo.listening_hash[inet_sk_listen_hashfn(sk)];
-               lock = &tcp_hashinfo.lhash_lock;
-               inet_listen_wlock(&tcp_hashinfo);
-       } else {
-               sk->sk_hashent = tcp_v6_sk_hashfn(sk);
-               list = &tcp_hashinfo.ehash[sk->sk_hashent].chain;
-               lock = &tcp_hashinfo.ehash[sk->sk_hashent].lock;
-               write_lock(lock);
-       }
-
-       __sk_add_node(sk, list);
-       sock_prot_inc_use(sk->sk_prot);
-       write_unlock(lock);
-}
-
-
 static void tcp_v6_hash(struct sock *sk)
 {
        if (sk->sk_state != TCP_CLOSE) {
-               struct tcp_sock *tp = tcp_sk(sk);
-
-               if (tp->af_specific == &ipv6_mapped) {
+               if (inet_csk(sk)->icsk_af_ops == &ipv6_mapped) {
                        tcp_prot.hash(sk);
                        return;
                }
                local_bh_disable();
-               __tcp_v6_hash(sk);
+               __inet6_hash(&tcp_hashinfo, sk);
                local_bh_enable();
        }
 }
 
-static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum, int dif)
-{
-       struct sock *sk;
-       struct hlist_node *node;
-       struct sock *result = NULL;
-       int score, hiscore;
-
-       hiscore=0;
-       read_lock(&tcp_hashinfo.lhash_lock);
-       sk_for_each(sk, node, &tcp_hashinfo.listening_hash[inet_lhashfn(hnum)]) {
-               if (inet_sk(sk)->num == hnum && sk->sk_family == PF_INET6) {
-                       struct ipv6_pinfo *np = inet6_sk(sk);
-                       
-                       score = 1;
-                       if (!ipv6_addr_any(&np->rcv_saddr)) {
-                               if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
-                                       continue;
-                               score++;
-                       }
-                       if (sk->sk_bound_dev_if) {
-                               if (sk->sk_bound_dev_if != dif)
-                                       continue;
-                               score++;
-                       }
-                       if (score == 3) {
-                               result = sk;
-                               break;
-                       }
-                       if (score > hiscore) {
-                               hiscore = score;
-                               result = sk;
-                       }
-               }
-       }
-       if (result)
-               sock_hold(result);
-       read_unlock(&tcp_hashinfo.lhash_lock);
-       return result;
-}
-
-/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
- * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
- *
- * The sockhash lock must be held as a reader here.
- */
-
-static inline struct sock *__tcp_v6_lookup_established(struct in6_addr *saddr, u16 sport,
-                                                      struct in6_addr *daddr, u16 hnum,
-                                                      int dif)
-{
-       struct sock *sk;
-       const struct hlist_node *node;
-       const __u32 ports = INET_COMBINED_PORTS(sport, hnum);
-       /* Optimize here for direct hit, only listening connections can
-        * have wildcards anyways.
-        */
-       const int hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
-       struct inet_ehash_bucket *head = &tcp_hashinfo.ehash[hash];
-
-       read_lock(&head->lock);
-       sk_for_each(sk, node, &head->chain) {
-               /* For IPV6 do the cheaper port and family tests first. */
-               if (INET6_MATCH(sk, saddr, daddr, ports, dif))
-                       goto hit; /* You sunk my battleship! */
-       }
-       /* Must check for a TIME_WAIT'er before going to listener hash. */
-       sk_for_each(sk, node, &(head + tcp_hashinfo.ehash_size)->chain) {
-               const struct inet_timewait_sock *tw = inet_twsk(sk);
-
-               if(*((__u32 *)&(tw->tw_dport))  == ports        &&
-                  sk->sk_family                == PF_INET6) {
-                       const struct tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk);
-
-                       if (ipv6_addr_equal(&tcp6tw->tw_v6_daddr, saddr)        &&
-                           ipv6_addr_equal(&tcp6tw->tw_v6_rcv_saddr, daddr)    &&
-                           (!sk->sk_bound_dev_if || sk->sk_bound_dev_if == dif))
-                               goto hit;
-               }
-       }
-       read_unlock(&head->lock);
-       return NULL;
-
-hit:
-       sock_hold(sk);
-       read_unlock(&head->lock);
-       return sk;
-}
-
-
-static inline struct sock *__tcp_v6_lookup(struct in6_addr *saddr, u16 sport,
-                                          struct in6_addr *daddr, u16 hnum,
-                                          int dif)
-{
-       struct sock *sk;
-
-       sk = __tcp_v6_lookup_established(saddr, sport, daddr, hnum, dif);
-
-       if (sk)
-               return sk;
-
-       return tcp_v6_lookup_listener(daddr, hnum, dif);
-}
-
-inline struct sock *tcp_v6_lookup(struct in6_addr *saddr, u16 sport,
-                                 struct in6_addr *daddr, u16 dport,
-                                 int dif)
-{
-       struct sock *sk;
-
-       local_bh_disable();
-       sk = __tcp_v6_lookup(saddr, sport, daddr, ntohs(dport), dif);
-       local_bh_enable();
-
-       return sk;
-}
-
-EXPORT_SYMBOL_GPL(tcp_v6_lookup);
-
-
-/*
- * Open request hash tables.
- */
-
-static u32 tcp_v6_synq_hash(const struct in6_addr *raddr, const u16 rport, const u32 rnd)
-{
-       u32 a, b, c;
-
-       a = raddr->s6_addr32[0];
-       b = raddr->s6_addr32[1];
-       c = raddr->s6_addr32[2];
-
-       a += JHASH_GOLDEN_RATIO;
-       b += JHASH_GOLDEN_RATIO;
-       c += rnd;
-       __jhash_mix(a, b, c);
-
-       a += raddr->s6_addr32[3];
-       b += (u32) rport;
-       __jhash_mix(a, b, c);
-
-       return c & (TCP_SYNQ_HSIZE - 1);
-}
-
-static struct request_sock *tcp_v6_search_req(const struct sock *sk,
-                                             struct request_sock ***prevp,
-                                             __u16 rport,
-                                             struct in6_addr *raddr,
-                                             struct in6_addr *laddr,
-                                             int iif)
-{
-       const struct inet_connection_sock *icsk = inet_csk(sk);
-       struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
-       struct request_sock *req, **prev;  
-
-       for (prev = &lopt->syn_table[tcp_v6_synq_hash(raddr, rport, lopt->hash_rnd)];
-            (req = *prev) != NULL;
-            prev = &req->dl_next) {
-               const struct tcp6_request_sock *treq = tcp6_rsk(req);
-
-               if (inet_rsk(req)->rmt_port == rport &&
-                   req->rsk_ops->family == AF_INET6 &&
-                   ipv6_addr_equal(&treq->rmt_addr, raddr) &&
-                   ipv6_addr_equal(&treq->loc_addr, laddr) &&
-                   (!treq->iif || treq->iif == iif)) {
-                       BUG_TRAP(req->sk == NULL);
-                       *prevp = prev;
-                       return req;
-               }
-       }
-
-       return NULL;
-}
-
-static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len,
+static __inline__ __sum16 tcp_v6_check(struct tcphdr *th, int len,
                                   struct in6_addr *saddr, 
                                   struct in6_addr *daddr, 
-                                  unsigned long base)
+                                  __wsum base)
 {
        return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, base);
 }
 
-static __u32 tcp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
-{
-       if (skb->protocol == htons(ETH_P_IPV6)) {
-               return secure_tcpv6_sequence_number(skb->nh.ipv6h->daddr.s6_addr32,
-                                                   skb->nh.ipv6h->saddr.s6_addr32,
-                                                   skb->h.th->dest,
-                                                   skb->h.th->source);
-       } else {
-               return secure_tcp_sequence_number(skb->nh.iph->daddr,
-                                                 skb->nh.iph->saddr,
-                                                 skb->h.th->dest,
-                                                 skb->h.th->source);
-       }
-}
-
-static int __tcp_v6_check_established(struct sock *sk, __u16 lport,
-                                     struct inet_timewait_sock **twp)
-{
-       struct inet_sock *inet = inet_sk(sk);
-       struct ipv6_pinfo *np = inet6_sk(sk);
-       struct in6_addr *daddr = &np->rcv_saddr;
-       struct in6_addr *saddr = &np->daddr;
-       int dif = sk->sk_bound_dev_if;
-       const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
-       const int hash = tcp_v6_hashfn(daddr, inet->num, saddr, inet->dport);
-       struct inet_ehash_bucket *head = &tcp_hashinfo.ehash[hash];
-       struct sock *sk2;
-       const struct hlist_node *node;
-       struct inet_timewait_sock *tw;
-
-       write_lock(&head->lock);
-
-       /* Check TIME-WAIT sockets first. */
-       sk_for_each(sk2, node, &(head + tcp_hashinfo.ehash_size)->chain) {
-               const struct tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk2);
-
-               tw = inet_twsk(sk2);
-
-               if(*((__u32 *)&(tw->tw_dport))  == ports        &&
-                  sk2->sk_family               == PF_INET6     &&
-                  ipv6_addr_equal(&tcp6tw->tw_v6_daddr, saddr) &&
-                  ipv6_addr_equal(&tcp6tw->tw_v6_rcv_saddr, daddr)     &&
-                  sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
-                       const struct tcp_timewait_sock *tcptw = tcp_twsk(sk2);
-                       struct tcp_sock *tp = tcp_sk(sk);
-
-                       if (tcptw->tw_ts_recent_stamp &&
-                           (!twp ||
-                            (sysctl_tcp_tw_reuse &&
-                             xtime.tv_sec - tcptw->tw_ts_recent_stamp > 1))) {
-                               /* See comment in tcp_ipv4.c */
-                               tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
-                               if (!tp->write_seq)
-                                       tp->write_seq = 1;
-                               tp->rx_opt.ts_recent       = tcptw->tw_ts_recent;
-                               tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
-                               sock_hold(sk2);
-                               goto unique;
-                       } else
-                               goto not_unique;
-               }
-       }
-       tw = NULL;
-
-       /* And established part... */
-       sk_for_each(sk2, node, &head->chain) {
-               if (INET6_MATCH(sk2, saddr, daddr, ports, dif))
-                       goto not_unique;
-       }
-
-unique:
-       BUG_TRAP(sk_unhashed(sk));
-       __sk_add_node(sk, &head->chain);
-       sk->sk_hashent = hash;
-       sock_prot_inc_use(sk->sk_prot);
-       write_unlock(&head->lock);
-
-       if (twp) {
-               *twp = tw;
-               NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
-       } else if (tw) {
-               /* Silly. Should hash-dance instead... */
-               tcp_tw_deschedule(tw);
-               NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
-
-               inet_twsk_put(tw);
-       }
-       return 0;
-
-not_unique:
-       write_unlock(&head->lock);
-       return -EADDRNOTAVAIL;
-}
-
-static inline u32 tcpv6_port_offset(const struct sock *sk)
+static __u32 tcp_v6_init_sequence(struct sk_buff *skb)
 {
-       const struct inet_sock *inet = inet_sk(sk);
-       const struct ipv6_pinfo *np = inet6_sk(sk);
-
-       return secure_tcpv6_port_ephemeral(np->rcv_saddr.s6_addr32,
-                                          np->daddr.s6_addr32,
-                                          inet->dport);
-}
-
-static int tcp_v6_hash_connect(struct sock *sk)
-{
-       unsigned short snum = inet_sk(sk)->num;
-       struct inet_bind_hashbucket *head;
-       struct inet_bind_bucket *tb;
-       int ret;
-
-       if (!snum) {
-               int low = sysctl_local_port_range[0];
-               int high = sysctl_local_port_range[1];
-               int range = high - low;
-               int i;
-               int port;
-               static u32 hint;
-               u32 offset = hint + tcpv6_port_offset(sk);
-               struct hlist_node *node;
-               struct inet_timewait_sock *tw = NULL;
-
-               local_bh_disable();
-               for (i = 1; i <= range; i++) {
-                       port = low + (i + offset) % range;
-                       head = &tcp_hashinfo.bhash[inet_bhashfn(port, tcp_hashinfo.bhash_size)];
-                       spin_lock(&head->lock);
-
-                       /* Does not bother with rcv_saddr checks,
-                        * because the established check is already
-                        * unique enough.
-                        */
-                       inet_bind_bucket_for_each(tb, node, &head->chain) {
-                               if (tb->port == port) {
-                                       BUG_TRAP(!hlist_empty(&tb->owners));
-                                       if (tb->fastreuse >= 0)
-                                               goto next_port;
-                                       if (!__tcp_v6_check_established(sk,
-                                                                       port,
-                                                                       &tw))
-                                               goto ok;
-                                       goto next_port;
-                               }
-                       }
-
-                       tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, port);
-                       if (!tb) {
-                               spin_unlock(&head->lock);
-                               break;
-                       }
-                       tb->fastreuse = -1;
-                       goto ok;
-
-               next_port:
-                       spin_unlock(&head->lock);
-               }
-               local_bh_enable();
-
-               return -EADDRNOTAVAIL;
-
-ok:
-               hint += i;
-
-               /* Head lock still held and bh's disabled */
-               inet_bind_hash(sk, tb, port);
-               if (sk_unhashed(sk)) {
-                       inet_sk(sk)->sport = htons(port);
-                       __tcp_v6_hash(sk);
-               }
-               spin_unlock(&head->lock);
-
-               if (tw) {
-                       tcp_tw_deschedule(tw);
-                       inet_twsk_put(tw);
-               }
-
-               ret = 0;
-               goto out;
-       }
-
-       head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)];
-       tb   = inet_csk(sk)->icsk_bind_hash;
-       spin_lock_bh(&head->lock);
-
-       if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
-               __tcp_v6_hash(sk);
-               spin_unlock_bh(&head->lock);
-               return 0;
-       } else {
-               spin_unlock(&head->lock);
-               /* No definite answer... Walk to established hash table */
-               ret = __tcp_v6_check_established(sk, snum, NULL);
-out:
-               local_bh_enable();
-               return ret;
-       }
-}
-
-static __inline__ int tcp_v6_iif(struct sk_buff *skb)
-{
-       return IP6CB(skb)->iif;
+       return secure_tcpv6_sequence_number(skb->nh.ipv6h->daddr.s6_addr32,
+                                           skb->nh.ipv6h->saddr.s6_addr32,
+                                           skb->h.th->dest,
+                                           skb->h.th->source);
 }
 
 static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, 
                          int addr_len)
 {
        struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
-       struct inet_sock *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
+       struct inet_connection_sock *icsk = inet_csk(sk);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
        struct in6_addr *saddr = NULL, *final_p = NULL, final;
@@ -721,7 +201,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
         */
 
        if (addr_type == IPV6_ADDR_MAPPED) {
-               u32 exthdrlen = tp->ext_header_len;
+               u32 exthdrlen = icsk->icsk_ext_hdr_len;
                struct sockaddr_in sin;
 
                SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
@@ -733,15 +213,21 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
                sin.sin_port = usin->sin6_port;
                sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
 
-               tp->af_specific = &ipv6_mapped;
+               icsk->icsk_af_ops = &ipv6_mapped;
                sk->sk_backlog_rcv = tcp_v4_do_rcv;
+#ifdef CONFIG_TCP_MD5SIG
+               tp->af_specific = &tcp_sock_ipv6_mapped_specific;
+#endif
 
                err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
 
                if (err) {
-                       tp->ext_header_len = exthdrlen;
-                       tp->af_specific = &ipv6_specific;
+                       icsk->icsk_ext_hdr_len = exthdrlen;
+                       icsk->icsk_af_ops = &ipv6_specific;
                        sk->sk_backlog_rcv = tcp_v6_do_rcv;
+#ifdef CONFIG_TCP_MD5SIG
+                       tp->af_specific = &tcp_sock_ipv6_specific;
+#endif
                        goto failure;
                } else {
                        ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000FFFF),
@@ -771,16 +257,16 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
                final_p = &final;
        }
 
+       security_sk_classify_flow(sk, &fl);
+
        err = ip6_dst_lookup(sk, &dst, &fl);
        if (err)
                goto failure;
        if (final_p)
                ipv6_addr_copy(&fl.fl6_dst, final_p);
 
-       if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
-               dst_release(dst);
+       if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
                goto failure;
-       }
 
        if (saddr == NULL) {
                saddr = &fl.fl6_src;
@@ -791,20 +277,20 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        ipv6_addr_copy(&np->saddr, saddr);
        inet->rcv_saddr = LOOPBACK4_IPV6;
 
-       ip6_dst_store(sk, dst, NULL);
-       sk->sk_route_caps = dst->dev->features &
-               ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
+       sk->sk_gso_type = SKB_GSO_TCPV6;
+       __ip6_dst_store(sk, dst, NULL, NULL);
 
-       tp->ext_header_len = 0;
+       icsk->icsk_ext_hdr_len = 0;
        if (np->opt)
-               tp->ext_header_len = np->opt->opt_flen + np->opt->opt_nflen;
+               icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
+                                         np->opt->opt_nflen);
 
        tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
 
        inet->dport = usin->sin6_port;
 
        tcp_set_state(sk, TCP_SYN_SENT);
-       err = tcp_v6_hash_connect(sk);
+       err = inet6_hash_connect(&tcp_death_row, sk);
        if (err)
                goto late_failure;
 
@@ -830,17 +316,18 @@ failure:
 }
 
 static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
-               int type, int code, int offset, __u32 info)
+               int type, int code, int offset, __be32 info)
 {
        struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data;
-       struct tcphdr *th = (struct tcphdr *)(skb->data+offset);
+       const struct tcphdr *th = (struct tcphdr *)(skb->data+offset);
        struct ipv6_pinfo *np;
        struct sock *sk;
        int err;
        struct tcp_sock *tp; 
        __u32 seq;
 
-       sk = tcp_v6_lookup(&hdr->daddr, th->dest, &hdr->saddr, th->source, skb->dev->ifindex);
+       sk = inet6_lookup(&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);
@@ -848,7 +335,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        }
 
        if (sk->sk_state == TCP_TIME_WAIT) {
-               inet_twsk_put((struct inet_timewait_sock *)sk);
+               inet_twsk_put(inet_twsk(sk));
                return;
        }
 
@@ -895,6 +382,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                        fl.oif = sk->sk_bound_dev_if;
                        fl.fl_ip_dport = inet->dport;
                        fl.fl_ip_sport = inet->sport;
+                       security_skb_classify_flow(skb, &fl);
 
                        if ((err = ip6_dst_lookup(sk, &dst, &fl))) {
                                sk->sk_err_soft = -err;
@@ -909,7 +397,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                } else
                        dst_hold(dst);
 
-               if (tp->pmtu_cookie > dst_mtu(dst)) {
+               if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) {
                        tcp_sync_mss(sk, dst_mtu(dst));
                        tcp_simple_retransmit(sk);
                } /* else let the usual retransmit timer handle it */
@@ -926,8 +414,8 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                if (sock_owned_by_user(sk))
                        goto out;
 
-               req = tcp_v6_search_req(sk, &prev, th->dest, &hdr->daddr,
-                                       &hdr->saddr, tcp_v6_iif(skb));
+               req = inet6_csk_search_req(sk, &prev, th->dest, &hdr->daddr,
+                                          &hdr->saddr, inet6_iif(skb));
                if (!req)
                        goto out;
 
@@ -948,7 +436,6 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        case TCP_SYN_RECV:  /* Cannot happen.
                               It can, it SYNs are crossed. --ANK */ 
                if (!sock_owned_by_user(sk)) {
-                       TCP_INC_STATS_BH(TCP_MIB_ATTEMPTFAILS);
                        sk->sk_err = err;
                        sk->sk_error_report(sk);                /* Wake people up to see the error (see connect in sock.c) */
 
@@ -973,7 +460,7 @@ out:
 static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
                              struct dst_entry *dst)
 {
-       struct tcp6_request_sock *treq = tcp6_rsk(req);
+       struct inet6_request_sock *treq = inet6_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct sk_buff * skb;
        struct ipv6_txoptions *opt = NULL;
@@ -989,11 +476,12 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
        fl.oif = treq->iif;
        fl.fl_ip_dport = inet_rsk(req)->rmt_port;
        fl.fl_ip_sport = inet_sk(sk)->sport;
+       security_req_classify_flow(req, &fl);
 
        if (dst == NULL) {
                opt = np->opt;
                if (opt == NULL &&
-                   np->rxopt.bits.srcrt == 2 &&
+                   np->rxopt.bits.osrcrt == 2 &&
                    treq->pktopts) {
                        struct sk_buff *pktopts = treq->pktopts;
                        struct inet6_skb_parm *rxopt = IP6CB(pktopts);
@@ -1027,24 +515,412 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
 
                ipv6_addr_copy(&fl.fl6_dst, &treq->rmt_addr);
                err = ip6_xmit(sk, skb, &fl, opt, 0);
-               if (err == NET_XMIT_CN)
-                       err = 0;
+               err = net_xmit_eval(err);
        }
 
 done:
-       dst_release(dst);
         if (opt && opt != np->opt)
                sock_kfree_s(sk, opt, opt->tot_len);
+       dst_release(dst);
        return err;
 }
 
 static void tcp_v6_reqsk_destructor(struct request_sock *req)
 {
-       if (tcp6_rsk(req)->pktopts)
-               kfree_skb(tcp6_rsk(req)->pktopts);
+       if (inet6_rsk(req)->pktopts)
+               kfree_skb(inet6_rsk(req)->pktopts);
+}
+
+#ifdef CONFIG_TCP_MD5SIG
+static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,
+                                                  struct in6_addr *addr)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       int i;
+
+       BUG_ON(tp == NULL);
+
+       if (!tp->md5sig_info || !tp->md5sig_info->entries6)
+               return NULL;
+
+       for (i = 0; i < tp->md5sig_info->entries6; i++) {
+               if (ipv6_addr_cmp(&tp->md5sig_info->keys6[i].addr, addr) == 0)
+                       return (struct tcp_md5sig_key *)&tp->md5sig_info->keys6[i];
+       }
+       return NULL;
+}
+
+static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk,
+                                               struct sock *addr_sk)
+{
+       return tcp_v6_md5_do_lookup(sk, &inet6_sk(addr_sk)->daddr);
+}
+
+static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk,
+                                                     struct request_sock *req)
+{
+       return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr);
+}
+
+static int tcp_v6_md5_do_add(struct sock *sk, struct in6_addr *peer,
+                            char *newkey, u8 newkeylen)
+{
+       /* Add key to the list */
+       struct tcp6_md5sig_key *key;
+       struct tcp_sock *tp = tcp_sk(sk);
+       struct tcp6_md5sig_key *keys;
+
+       key = (struct tcp6_md5sig_key*) tcp_v6_md5_do_lookup(sk, peer);
+       if (key) {
+               /* modify existing entry - just update that one */
+               kfree(key->key);
+               key->key = newkey;
+               key->keylen = newkeylen;
+       } else {
+               /* reallocate new list if current one is full. */
+               if (!tp->md5sig_info) {
+                       tp->md5sig_info = kzalloc(sizeof(*tp->md5sig_info), GFP_ATOMIC);
+                       if (!tp->md5sig_info) {
+                               kfree(newkey);
+                               return -ENOMEM;
+                       }
+               }
+               tcp_alloc_md5sig_pool();
+               if (tp->md5sig_info->alloced6 == tp->md5sig_info->entries6) {
+                       keys = kmalloc((sizeof (tp->md5sig_info->keys6[0]) *
+                                      (tp->md5sig_info->entries6 + 1)), GFP_ATOMIC);
+
+                       if (!keys) {
+                               tcp_free_md5sig_pool();
+                               kfree(newkey);
+                               return -ENOMEM;
+                       }
+
+                       if (tp->md5sig_info->entries6)
+                               memmove(keys, tp->md5sig_info->keys6,
+                                       (sizeof (tp->md5sig_info->keys6[0]) *
+                                        tp->md5sig_info->entries6));
+
+                       kfree(tp->md5sig_info->keys6);
+                       tp->md5sig_info->keys6 = keys;
+                       tp->md5sig_info->alloced6++;
+               }
+
+               ipv6_addr_copy(&tp->md5sig_info->keys6[tp->md5sig_info->entries6].addr,
+                              peer);
+               tp->md5sig_info->keys6[tp->md5sig_info->entries6].key = newkey;
+               tp->md5sig_info->keys6[tp->md5sig_info->entries6].keylen = newkeylen;
+
+               tp->md5sig_info->entries6++;
+       }
+       return 0;
+}
+
+static int tcp_v6_md5_add_func(struct sock *sk, struct sock *addr_sk,
+                              u8 *newkey, __u8 newkeylen)
+{
+       return tcp_v6_md5_do_add(sk, &inet6_sk(addr_sk)->daddr,
+                                newkey, newkeylen);
 }
 
-static struct request_sock_ops tcp6_request_sock_ops = {
+static int tcp_v6_md5_do_del(struct sock *sk, struct in6_addr *peer)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       int i;
+
+       for (i = 0; i < tp->md5sig_info->entries6; i++) {
+               if (ipv6_addr_cmp(&tp->md5sig_info->keys6[i].addr, peer) == 0) {
+                       /* Free the key */
+                       kfree(tp->md5sig_info->keys6[i].key);
+                       tp->md5sig_info->entries6--;
+
+                       if (tp->md5sig_info->entries6 == 0) {
+                               kfree(tp->md5sig_info->keys6);
+                               tp->md5sig_info->keys6 = NULL;
+
+                               tcp_free_md5sig_pool();
+
+                               return 0;
+                       } else {
+                               /* shrink the database */
+                               if (tp->md5sig_info->entries6 != i)
+                                       memmove(&tp->md5sig_info->keys6[i],
+                                               &tp->md5sig_info->keys6[i+1],
+                                               (tp->md5sig_info->entries6 - i)
+                                               * sizeof (tp->md5sig_info->keys6[0]));
+                       }
+               }
+       }
+       return -ENOENT;
+}
+
+static void tcp_v6_clear_md5_list (struct sock *sk)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+       int i;
+
+       if (tp->md5sig_info->entries6) {
+               for (i = 0; i < tp->md5sig_info->entries6; i++)
+                       kfree(tp->md5sig_info->keys6[i].key);
+               tp->md5sig_info->entries6 = 0;
+               tcp_free_md5sig_pool();
+       }
+
+       kfree(tp->md5sig_info->keys6);
+       tp->md5sig_info->keys6 = NULL;
+       tp->md5sig_info->alloced6 = 0;
+
+       if (tp->md5sig_info->entries4) {
+               for (i = 0; i < tp->md5sig_info->entries4; i++)
+                       kfree(tp->md5sig_info->keys4[i].key);
+               tp->md5sig_info->entries4 = 0;
+               tcp_free_md5sig_pool();
+       }
+
+       kfree(tp->md5sig_info->keys4);
+       tp->md5sig_info->keys4 = NULL;
+       tp->md5sig_info->alloced4 = 0;
+}
+
+static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
+                                 int optlen)
+{
+       struct tcp_md5sig cmd;
+       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr;
+       u8 *newkey;
+
+       if (optlen < sizeof(cmd))
+               return -EINVAL;
+
+       if (copy_from_user(&cmd, optval, sizeof(cmd)))
+               return -EFAULT;
+
+       if (sin6->sin6_family != AF_INET6)
+               return -EINVAL;
+
+       if (!cmd.tcpm_keylen) {
+               if (!tcp_sk(sk)->md5sig_info)
+                       return -ENOENT;
+               if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_MAPPED)
+                       return tcp_v4_md5_do_del(sk, sin6->sin6_addr.s6_addr32[3]);
+               return tcp_v6_md5_do_del(sk, &sin6->sin6_addr);
+       }
+
+       if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
+               return -EINVAL;
+
+       if (!tcp_sk(sk)->md5sig_info) {
+               struct tcp_sock *tp = tcp_sk(sk);
+               struct tcp_md5sig_info *p;
+
+               p = kzalloc(sizeof(struct tcp_md5sig_info), GFP_KERNEL);
+               if (!p)
+                       return -ENOMEM;
+
+               tp->md5sig_info = p;
+       }
+
+       newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
+       if (!newkey)
+               return -ENOMEM;
+       if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_MAPPED) {
+               return tcp_v4_md5_do_add(sk, sin6->sin6_addr.s6_addr32[3],
+                                        newkey, cmd.tcpm_keylen);
+       }
+       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, int protocol,
+                                  int tcplen)
+{
+       struct scatterlist sg[4];
+       __u16 data_len;
+       int block = 0;
+       __sum16 cksum;
+       struct tcp_md5sig_pool *hp;
+       struct tcp6_pseudohdr *bp;
+       struct hash_desc *desc;
+       int err;
+       unsigned int nbytes = 0;
+
+       hp = tcp_get_md5sig_pool();
+       if (!hp) {
+               printk(KERN_WARNING "%s(): hash pool not found...\n", __FUNCTION__);
+               goto clear_hash_noput;
+       }
+       bp = &hp->md5_blk.ip6;
+       desc = &hp->md5_desc;
+
+       /* 1. TCP pseudo-header (RFC2460) */
+       ipv6_addr_copy(&bp->saddr, saddr);
+       ipv6_addr_copy(&bp->daddr, daddr);
+       bp->len = htonl(tcplen);
+       bp->protocol = htonl(protocol);
+
+       sg_set_buf(&sg[block++], bp, sizeof(*bp));
+       nbytes += sizeof(*bp);
+
+       /* 2. TCP header, excluding options */
+       cksum = th->check;
+       th->check = 0;
+       sg_set_buf(&sg[block++], th, sizeof(*th));
+       nbytes += sizeof(*th);
+
+       /* 3. TCP segment data (if any) */
+       data_len = tcplen - (th->doff << 2);
+       if (data_len > 0) {
+               u8 *data = (u8 *)th + (th->doff << 2);
+               sg_set_buf(&sg[block++], data, data_len);
+               nbytes += data_len;
+       }
+
+       /* 4. shared key */
+       sg_set_buf(&sg[block++], key->key, key->keylen);
+       nbytes += key->keylen;
+
+       /* Now store the hash into the packet */
+       err = crypto_hash_init(desc);
+       if (err) {
+               printk(KERN_WARNING "%s(): hash_init failed\n", __FUNCTION__);
+               goto clear_hash;
+       }
+       err = crypto_hash_update(desc, sg, nbytes);
+       if (err) {
+               printk(KERN_WARNING "%s(): hash_update failed\n", __FUNCTION__);
+               goto clear_hash;
+       }
+       err = crypto_hash_final(desc, md5_hash);
+       if (err) {
+               printk(KERN_WARNING "%s(): hash_final failed\n", __FUNCTION__);
+               goto clear_hash;
+       }
+
+       /* Reset header, and free up the crypto */
+       tcp_put_md5sig_pool();
+       th->check = cksum;
+out:
+       return 0;
+clear_hash:
+       tcp_put_md5sig_pool();
+clear_hash_noput:
+       memset(md5_hash, 0, 16);
+       goto out;
+}
+
+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, int protocol,
+                               int tcplen)
+{
+       struct in6_addr *saddr, *daddr;
+
+       if (sk) {
+               saddr = &inet6_sk(sk)->saddr;
+               daddr = &inet6_sk(sk)->daddr;
+       } else {
+               saddr = &inet6_rsk(req)->loc_addr;
+               daddr = &inet6_rsk(req)->rmt_addr;
+       }
+       return tcp_v6_do_calc_md5_hash(md5_hash, key,
+                                      saddr, daddr,
+                                      th, protocol, tcplen);
+}
+
+static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb)
+{
+       __u8 *hash_location = NULL;
+       struct tcp_md5sig_key *hash_expected;
+       struct ipv6hdr *ip6h = skb->nh.ipv6h;
+       struct tcphdr *th = skb->h.th;
+       int length = (th->doff << 2) - sizeof (*th);
+       int genhash;
+       u8 *ptr;
+       u8 newhash[16];
+
+       hash_expected = tcp_v6_md5_do_lookup(sk, &ip6h->saddr);
+
+       /* If the TCP option is too short, we can short cut */
+       if (length < TCPOLEN_MD5SIG)
+               return hash_expected ? 1 : 0;
+
+       /* parse options */
+       ptr = (u8*)(th + 1);
+       while (length > 0) {
+               int opcode = *ptr++;
+               int opsize;
+
+               switch(opcode) {
+               case TCPOPT_EOL:
+                       goto done_opts;
+               case TCPOPT_NOP:
+                       length--;
+                       continue;
+               default:
+                       opsize = *ptr++;
+                       if (opsize < 2 || opsize > length)
+                               goto done_opts;
+                       if (opcode == TCPOPT_MD5SIG) {
+                               hash_location = ptr;
+                               goto done_opts;
+                       }
+               }
+               ptr += opsize - 2;
+               length -= opsize;
+       }
+
+done_opts:
+       /* 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));
+               }
+               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));
+               }
+               return 1;
+       }
+
+       /* check the signature */
+       genhash = tcp_v6_do_calc_md5_hash(newhash,
+                                         hash_expected,
+                                         &ip6h->saddr, &ip6h->daddr,
+                                         th, sk->sk_protocol,
+                                         skb->len);
+       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",
+                              genhash ? "failed" : "mismatch",
+                              NIP6(ip6h->saddr), ntohs(th->source),
+                              NIP6(ip6h->daddr), ntohs(th->dest));
+               }
+               return 1;
+       }
+       return 0;
+}
+#endif
+
+static struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
        .family         =       AF_INET6,
        .obj_size       =       sizeof(struct tcp6_request_sock),
        .rtx_syn_ack    =       tcp_v6_send_synack,
@@ -1053,31 +929,26 @@ static struct request_sock_ops tcp6_request_sock_ops = {
        .send_reset     =       tcp_v6_send_reset
 };
 
-static int ipv6_opt_accepted(struct sock *sk, struct sk_buff *skb)
-{
-       struct ipv6_pinfo *np = inet6_sk(sk);
-       struct inet6_skb_parm *opt = IP6CB(skb);
-
-       if (np->rxopt.all) {
-               if ((opt->hop && np->rxopt.bits.hopopts) ||
-                   ((IPV6_FLOWINFO_MASK&*(u32*)skb->nh.raw) &&
-                    np->rxopt.bits.rxflow) ||
-                   (opt->srcrt && np->rxopt.bits.srcrt) ||
-                   ((opt->dst1 || opt->dst0) && np->rxopt.bits.dstopts))
-                       return 1;
-       }
-       return 0;
-}
+struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
+#ifdef CONFIG_TCP_MD5SIG
+       .md5_lookup     =       tcp_v6_reqsk_md5_lookup,
+#endif
+};
 
+static struct timewait_sock_ops tcp6_timewait_sock_ops = {
+       .twsk_obj_size  = sizeof(struct tcp6_timewait_sock),
+       .twsk_unique    = tcp_twsk_unique,
+       .twsk_destructor= tcp_twsk_destructor,
+};
 
-static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len, 
-                             struct sk_buff *skb)
+static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
+       struct tcphdr *th = skb->h.th;
 
-       if (skb->ip_summed == CHECKSUM_HW) {
+       if (skb->ip_summed == CHECKSUM_PARTIAL) {
                th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,  0);
-               skb->csum = offsetof(struct tcphdr, check);
+               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, 
@@ -1085,12 +956,34 @@ static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
        }
 }
 
+static int tcp_v6_gso_send_check(struct sk_buff *skb)
+{
+       struct ipv6hdr *ipv6h;
+       struct tcphdr *th;
+
+       if (!pskb_may_pull(skb, sizeof(*th)))
+               return -EINVAL;
+
+       ipv6h = skb->nh.ipv6h;
+       th = skb->h.th;
+
+       th->check = 0;
+       th->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
+                                    IPPROTO_TCP, 0);
+       skb->csum_offset = offsetof(struct tcphdr, check);
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       return 0;
+}
 
-static void tcp_v6_send_reset(struct sk_buff *skb)
+static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
 {
        struct tcphdr *th = skb->h.th, *t1; 
        struct sk_buff *buff;
        struct flowi fl;
+       int tot_len = sizeof(*th);
+#ifdef CONFIG_TCP_MD5SIG
+       struct tcp_md5sig_key *key;
+#endif
 
        if (th->rst)
                return;
@@ -1098,25 +991,35 @@ static void tcp_v6_send_reset(struct sk_buff *skb)
        if (!ipv6_unicast_destination(skb))
                return; 
 
+#ifdef CONFIG_TCP_MD5SIG
+       if (sk)
+               key = tcp_v6_md5_do_lookup(sk, &skb->nh.ipv6h->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) + sizeof(struct tcphdr),
+       buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) + tot_len,
                         GFP_ATOMIC);
        if (buff == NULL) 
                return;
 
-       skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr) + sizeof(struct tcphdr));
+       skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr) + tot_len);
 
-       t1 = (struct tcphdr *) skb_push(buff,sizeof(struct tcphdr));
+       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 = sizeof(*t1)/4;
+       t1->doff = tot_len / 4;
        t1->rst = 1;
   
        if(th->ack) {
@@ -1127,6 +1030,22 @@ static void tcp_v6_send_reset(struct sk_buff *skb)
                                    + skb->len - (th->doff<<2));
        }
 
+#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,
+                                       &skb->nh.ipv6h->daddr,
+                                       &skb->nh.ipv6h->saddr,
+                                       t1, IPPROTO_TCP,
+                                       tot_len);
+       }
+#endif
+
        buff->csum = csum_partial((char *)t1, sizeof(*t1), 0);
 
        memset(&fl, 0, sizeof(fl));
@@ -1138,36 +1057,56 @@ static void tcp_v6_send_reset(struct sk_buff *skb)
                                    buff->csum);
 
        fl.proto = IPPROTO_TCP;
-       fl.oif = tcp_v6_iif(skb);
+       fl.oif = inet6_iif(skb);
        fl.fl_ip_dport = t1->dest;
        fl.fl_ip_sport = t1->source;
+       security_skb_classify_flow(skb, &fl);
 
        /* sk = NULL, but it is safe for now. RST socket required. */
        if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
 
-               if ((xfrm_lookup(&buff->dst, &fl, NULL, 0)) < 0) {
-                       dst_release(buff->dst);
+               if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {
+                       ip6_xmit(tcp6_socket->sk, buff, &fl, NULL, 0);
+                       TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
+                       TCP_INC_STATS_BH(TCP_MIB_OUTRSTS);
                        return;
                }
-
-               ip6_xmit(NULL, buff, &fl, NULL, 0);
-               TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
-               TCP_INC_STATS_BH(TCP_MIB_OUTRSTS);
-               return;
        }
 
        kfree_skb(buff);
 }
 
-static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts)
+static void tcp_v6_send_ack(struct tcp_timewait_sock *tw,
+                           struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts)
 {
        struct tcphdr *th = skb->h.th, *t1;
        struct sk_buff *buff;
        struct flowi fl;
        int tot_len = sizeof(struct tcphdr);
+       __be32 *topt;
+#ifdef CONFIG_TCP_MD5SIG
+       struct tcp_md5sig_key *key;
+       struct tcp_md5sig_key tw_key;
+#endif
+
+#ifdef CONFIG_TCP_MD5SIG
+       if (!tw && skb->sk) {
+               key = tcp_v6_md5_do_lookup(skb->sk, &skb->nh.ipv6h->daddr);
+       } else if (tw && tw->tw_md5_keylen) {
+               tw_key.key = tw->tw_md5_key;
+               tw_key.keylen = tw->tw_md5_keylen;
+               key = &tw_key;
+       } else {
+               key = NULL;
+       }
+#endif
 
        if (ts)
-               tot_len += 3*4;
+               tot_len += TCPOLEN_TSTAMP_ALIGNED;
+#ifdef CONFIG_TCP_MD5SIG
+       if (key)
+               tot_len += TCPOLEN_MD5SIG_ALIGNED;
+#endif
 
        buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) + tot_len,
                         GFP_ATOMIC);
@@ -1187,15 +1126,29 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32
        t1->ack_seq = htonl(ack);
        t1->ack = 1;
        t1->window = htons(win);
+
+       topt = (__be32 *)(t1 + 1);
        
        if (ts) {
-               u32 *ptr = (u32*)(t1 + 1);
-               *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
-                              (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
-               *ptr++ = htonl(tcp_time_stamp);
-               *ptr = htonl(ts);
+               *topt++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
+                               (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
+               *topt++ = htonl(tcp_time_stamp);
+               *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,
+                                       &skb->nh.ipv6h->daddr,
+                                       &skb->nh.ipv6h->saddr,
+                                       t1, IPPROTO_TCP,
+                                       tot_len);
+       }
+#endif
+
        buff->csum = csum_partial((char *)t1, tot_len, 0);
 
        memset(&fl, 0, sizeof(fl));
@@ -1207,18 +1160,17 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32
                                    buff->csum);
 
        fl.proto = IPPROTO_TCP;
-       fl.oif = tcp_v6_iif(skb);
+       fl.oif = inet6_iif(skb);
        fl.fl_ip_dport = t1->dest;
        fl.fl_ip_sport = t1->source;
+       security_skb_classify_flow(skb, &fl);
 
        if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
-               if ((xfrm_lookup(&buff->dst, &fl, NULL, 0)) < 0) {
-                       dst_release(buff->dst);
+               if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {
+                       ip6_xmit(tcp6_socket->sk, buff, &fl, NULL, 0);
+                       TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
                        return;
                }
-               ip6_xmit(NULL, buff, &fl, NULL, 0);
-               TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
-               return;
        }
 
        kfree_skb(buff);
@@ -1227,9 +1179,9 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32
 static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
 {
        struct inet_timewait_sock *tw = inet_twsk(sk);
-       const struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
+       struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
 
-       tcp_v6_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
+       tcp_v6_send_ack(tcptw, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
                        tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
                        tcptw->tw_ts_recent);
 
@@ -1238,34 +1190,33 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
 
 static void tcp_v6_reqsk_send_ack(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_send_ack(NULL, skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, req->ts_recent);
 }
 
 
 static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
 {
        struct request_sock *req, **prev;
-       struct tcphdr *th = skb->h.th;
+       const struct tcphdr *th = skb->h.th;
        struct sock *nsk;
 
        /* Find possible connection requests. */
-       req = tcp_v6_search_req(sk, &prev, th->source, &skb->nh.ipv6h->saddr,
-                               &skb->nh.ipv6h->daddr, tcp_v6_iif(skb));
+       req = inet6_csk_search_req(sk, &prev, th->source,
+                                  &skb->nh.ipv6h->saddr,
+                                  &skb->nh.ipv6h->daddr, inet6_iif(skb));
        if (req)
                return tcp_check_req(sk, skb, req, prev);
 
-       nsk = __tcp_v6_lookup_established(&skb->nh.ipv6h->saddr,
-                                         th->source,
-                                         &skb->nh.ipv6h->daddr,
-                                         ntohs(th->dest),
-                                         tcp_v6_iif(skb));
+       nsk = __inet6_lookup_established(&tcp_hashinfo, &skb->nh.ipv6h->saddr,
+                                        th->source, &skb->nh.ipv6h->daddr,
+                                        ntohs(th->dest), inet6_iif(skb));
 
        if (nsk) {
                if (nsk->sk_state != TCP_TIME_WAIT) {
                        bh_lock_sock(nsk);
                        return nsk;
                }
-               inet_twsk_put((struct inet_timewait_sock *)nsk);
+               inet_twsk_put(inet_twsk(nsk));
                return NULL;
        }
 
@@ -1276,23 +1227,12 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
        return sk;
 }
 
-static void tcp_v6_synq_add(struct sock *sk, struct request_sock *req)
-{
-       struct inet_connection_sock *icsk = inet_csk(sk);
-       struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt;
-       const u32 h = tcp_v6_synq_hash(&tcp6_rsk(req)->rmt_addr, inet_rsk(req)->rmt_port, lopt->hash_rnd);
-
-       reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, TCP_TIMEOUT_INIT);
-       inet_csk_reqsk_queue_added(sk, TCP_TIMEOUT_INIT);
-}
-
-
 /* FIXME: this is substantially similar to the ipv4 code.
  * Can some kind of merge be done? -- erics
  */
 static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 {
-       struct tcp6_request_sock *treq;
+       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);
@@ -1317,10 +1257,14 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
                goto drop;
 
-       req = reqsk_alloc(&tcp6_request_sock_ops);
+       req = inet6_reqsk_alloc(&tcp6_request_sock_ops);
        if (req == NULL)
                goto drop;
 
+#ifdef CONFIG_TCP_MD5SIG
+       tcp_rsk(req)->af_specific = &tcp_request_sock_ipv6_ops;
+#endif
+
        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;
@@ -1330,14 +1274,14 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        tmp_opt.tstamp_ok = tmp_opt.saw_tstamp;
        tcp_openreq_init(req, &tmp_opt, skb);
 
-       treq = tcp6_rsk(req);
+       treq = inet6_rsk(req);
        ipv6_addr_copy(&treq->rmt_addr, &skb->nh.ipv6h->saddr);
        ipv6_addr_copy(&treq->loc_addr, &skb->nh.ipv6h->daddr);
        TCP_ECN_create_request(req, skb->h.th);
        treq->pktopts = NULL;
        if (ipv6_opt_accepted(sk, skb) ||
-           np->rxopt.bits.rxinfo ||
-           np->rxopt.bits.rxhlim) {
+           np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
+           np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) {
                atomic_inc(&skb->users);
                treq->pktopts = skb;
        }
@@ -1346,25 +1290,25 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        /* So that link locals have meaning */
        if (!sk->sk_bound_dev_if &&
            ipv6_addr_type(&treq->rmt_addr) & IPV6_ADDR_LINKLOCAL)
-               treq->iif = tcp_v6_iif(skb);
+               treq->iif = inet6_iif(skb);
 
        if (isn == 0) 
-               isn = tcp_v6_init_sequence(sk,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, NULL))
                goto drop;
 
-       tcp_v6_synq_add(sk, req);
-
+       inet6_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);
        return 0;
 
 drop:
        if (req)
                reqsk_free(req);
 
-       TCP_INC_STATS_BH(TCP_MIB_ATTEMPTFAILS);
        return 0; /* don't send reset */
 }
 
@@ -1372,13 +1316,16 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                                          struct request_sock *req,
                                          struct dst_entry *dst)
 {
-       struct tcp6_request_sock *treq = tcp6_rsk(req);
+       struct inet6_request_sock *treq = inet6_rsk(req);
        struct ipv6_pinfo *newnp, *np = inet6_sk(sk);
        struct tcp6_sock *newtcp6sk;
        struct inet_sock *newinet;
        struct tcp_sock *newtp;
        struct sock *newsk;
        struct ipv6_txoptions *opt;
+#ifdef CONFIG_TCP_MD5SIG
+       struct tcp_md5sig_key *key;
+#endif
 
        if (skb->protocol == htons(ETH_P_IP)) {
                /*
@@ -1407,11 +1354,15 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 
                ipv6_addr_copy(&newnp->rcv_saddr, &newnp->saddr);
 
-               newtp->af_specific = &ipv6_mapped;
+               inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
                newsk->sk_backlog_rcv = tcp_v4_do_rcv;
+#ifdef CONFIG_TCP_MD5SIG
+               newtp->af_specific = &tcp_sock_ipv6_mapped_specific;
+#endif
+
                newnp->pktoptions  = NULL;
                newnp->opt         = NULL;
-               newnp->mcast_oif   = tcp_v6_iif(skb);
+               newnp->mcast_oif   = inet6_iif(skb);
                newnp->mcast_hops  = skb->nh.ipv6h->hop_limit;
 
                /*
@@ -1421,10 +1372,10 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                 */
 
                /* It is tricky place. Until this moment IPv4 tcp
-                  worked with IPv6 af_tcp.af_specific.
+                  worked with IPv6 icsk.icsk_af_ops.
                   Sync it now.
                 */
-               tcp_sync_mss(newsk, newtp->pmtu_cookie);
+               tcp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
 
                return newsk;
        }
@@ -1434,7 +1385,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        if (sk_acceptq_is_full(sk))
                goto out_overflow;
 
-       if (np->rxopt.bits.srcrt == 2 &&
+       if (np->rxopt.bits.osrcrt == 2 &&
            opt == NULL && treq->pktopts) {
                struct inet6_skb_parm *rxopt = IP6CB(treq->pktopts);
                if (rxopt->srcrt)
@@ -1458,6 +1409,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                fl.oif = sk->sk_bound_dev_if;
                fl.fl_ip_dport = inet_rsk(req)->rmt_port;
                fl.fl_ip_sport = inet_sk(sk)->sport;
+               security_req_classify_flow(req, &fl);
 
                if (ip6_dst_lookup(sk, &dst, &fl))
                        goto out;
@@ -1479,9 +1431,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
         * comment in that function for the gory details. -acme
         */
 
-       ip6_dst_store(newsk, dst, NULL);
-       newsk->sk_route_caps = dst->dev->features &
-               ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
+       newsk->sk_gso_type = SKB_GSO_TCPV6;
+       __ip6_dst_store(newsk, dst, NULL, NULL);
 
        newtcp6sk = (struct tcp6_sock *)newsk;
        inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
@@ -1516,7 +1467,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                        skb_set_owner_r(newnp->pktoptions, newsk);
        }
        newnp->opt        = NULL;
-       newnp->mcast_oif  = tcp_v6_iif(skb);
+       newnp->mcast_oif  = inet6_iif(skb);
        newnp->mcast_hops = skb->nh.ipv6h->hop_limit;
 
        /* Clone native IPv6 options from listening socket (if any)
@@ -1531,18 +1482,34 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                        sock_kfree_s(sk, opt, opt->tot_len);
        }
 
-       newtp->ext_header_len = 0;
+       inet_csk(newsk)->icsk_ext_hdr_len = 0;
        if (newnp->opt)
-               newtp->ext_header_len = newnp->opt->opt_nflen +
-                                       newnp->opt->opt_flen;
+               inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
+                                                    newnp->opt->opt_flen);
 
+       tcp_mtup_init(newsk);
        tcp_sync_mss(newsk, dst_mtu(dst));
        newtp->advmss = dst_metric(dst, RTAX_ADVMSS);
        tcp_initialize_rcv_mss(newsk);
 
        newinet->daddr = newinet->saddr = newinet->rcv_saddr = LOOPBACK4_IPV6;
 
-       __tcp_v6_hash(newsk);
+#ifdef CONFIG_TCP_MD5SIG
+       /* Copy over the MD5 key from the original socket */
+       if ((key = tcp_v6_md5_do_lookup(sk, &newnp->daddr)) != NULL) {
+               /* We're using one, so create a matching key
+                * on the newsk structure. If we fail to get
+                * memory, then we end up not copying the key
+                * across. Shucks.
+                */
+               char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC);
+               if (newkey != NULL)
+                       tcp_v6_md5_do_add(newsk, &inet6_sk(sk)->daddr,
+                                         newkey, key->keylen);
+       }
+#endif
+
+       __inet6_hash(&tcp_hashinfo, newsk);
        inet_inherit_port(&tcp_hashinfo, sk, newsk);
 
        return newsk;
@@ -1557,23 +1524,21 @@ out:
        return NULL;
 }
 
-static int tcp_v6_checksum_init(struct sk_buff *skb)
+static __sum16 tcp_v6_checksum_init(struct sk_buff *skb)
 {
-       if (skb->ip_summed == CHECKSUM_HW) {
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       if (skb->ip_summed == CHECKSUM_COMPLETE) {
                if (!tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
-                                 &skb->nh.ipv6h->daddr,skb->csum))
+                                 &skb->nh.ipv6h->daddr,skb->csum)) {
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
                        return 0;
-               LIMIT_NETDEBUG(printk(KERN_DEBUG "hw tcp v6 csum failed\n"));
+               }
        }
+
+       skb->csum = ~csum_unfold(tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
+                                 &skb->nh.ipv6h->daddr, 0));
+
        if (skb->len <= 76) {
-               if (tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
-                                &skb->nh.ipv6h->daddr,skb_checksum(skb, 0, skb->len, 0)))
-                       return -1;
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
-       } else {
-               skb->csum = ~tcp_v6_check(skb->h.th,skb->len,&skb->nh.ipv6h->saddr,
-                                         &skb->nh.ipv6h->daddr,0);
+               return __skb_checksum_complete(skb);
        }
        return 0;
 }
@@ -1603,7 +1568,12 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
        if (skb->protocol == htons(ETH_P_IP))
                return tcp_v4_do_rcv(sk, skb);
 
-       if (sk_filter(sk, skb, 0))
+#ifdef CONFIG_TCP_MD5SIG
+       if (tcp_v6_inbound_md5_hash (sk, skb))
+               goto discard;
+#endif
+
+       if (sk_filter(sk, skb))
                goto discard;
 
        /*
@@ -1668,7 +1638,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
        return 0;
 
 reset:
-       tcp_v6_send_reset(skb);
+       tcp_v6_send_reset(sk, skb);
 discard:
        if (opt_skb)
                __kfree_skb(opt_skb);
@@ -1690,9 +1660,9 @@ ipv6_pktoptions:
        tp = tcp_sk(sk);
        if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt &&
            !((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) {
-               if (np->rxopt.bits.rxinfo)
-                       np->mcast_oif = tcp_v6_iif(opt_skb);
-               if (np->rxopt.bits.rxhlim)
+               if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo)
+                       np->mcast_oif = inet6_iif(opt_skb);
+               if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
                        np->mcast_hops = opt_skb->nh.ipv6h->hop_limit;
                if (ipv6_opt_accepted(sk, opt_skb)) {
                        skb_set_owner_r(opt_skb, sk);
@@ -1708,7 +1678,7 @@ ipv6_pktoptions:
        return 0;
 }
 
-static int tcp_v6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
+static int tcp_v6_rcv(struct sk_buff **pskb)
 {
        struct sk_buff *skb = *pskb;
        struct tcphdr *th;      
@@ -1734,7 +1704,7 @@ static int tcp_v6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
                goto discard_it;
 
        if ((skb->ip_summed != CHECKSUM_UNNECESSARY &&
-            tcp_v6_checksum_init(skb) < 0))
+            tcp_v6_checksum_init(skb)))
                goto bad_packet;
 
        th = skb->h.th;
@@ -1746,8 +1716,9 @@ static int tcp_v6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
        TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(skb->nh.ipv6h);
        TCP_SKB_CB(skb)->sacked = 0;
 
-       sk = __tcp_v6_lookup(&skb->nh.ipv6h->saddr, th->source,
-                            &skb->nh.ipv6h->daddr, ntohs(th->dest), tcp_v6_iif(skb));
+       sk = __inet6_lookup(&tcp_hashinfo, &skb->nh.ipv6h->saddr, th->source,
+                           &skb->nh.ipv6h->daddr, ntohs(th->dest),
+                           inet6_iif(skb));
 
        if (!sk)
                goto no_tcp_socket;
@@ -1759,16 +1730,24 @@ process:
        if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
                goto discard_and_relse;
 
-       if (sk_filter(sk, skb, 0))
+       if (sk_filter(sk, skb))
                goto discard_and_relse;
 
        skb->dev = NULL;
 
-       bh_lock_sock(sk);
+       bh_lock_sock_nested(sk);
        ret = 0;
        if (!sock_owned_by_user(sk)) {
-               if (!tcp_prequeue(sk, skb))
-                       ret = tcp_v6_do_rcv(sk, skb);
+#ifdef CONFIG_NET_DMA
+                struct tcp_sock *tp = tcp_sk(sk);
+                if (tp->ucopy.dma_chan)
+                        ret = tcp_v6_do_rcv(sk, skb);
+                else
+#endif
+               {
+                       if (!tcp_prequeue(sk, skb))
+                               ret = tcp_v6_do_rcv(sk, skb);
+               }
        } else
                sk_add_backlog(sk, skb);
        bh_unlock_sock(sk);
@@ -1784,7 +1763,7 @@ no_tcp_socket:
 bad_packet:
                TCP_INC_STATS_BH(TCP_MIB_INERRS);
        } else {
-               tcp_v6_send_reset(skb);
+               tcp_v6_send_reset(NULL, skb);
        }
 
 discard_it:
@@ -1802,26 +1781,28 @@ discard_and_relse:
 
 do_time_wait:
        if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
-               inet_twsk_put((struct inet_timewait_sock *)sk);
+               inet_twsk_put(inet_twsk(sk));
                goto discard_it;
        }
 
        if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
                TCP_INC_STATS_BH(TCP_MIB_INERRS);
-               inet_twsk_put((struct inet_timewait_sock *)sk);
+               inet_twsk_put(inet_twsk(sk));
                goto discard_it;
        }
 
-       switch (tcp_timewait_state_process((struct inet_timewait_sock *)sk,
-                                          skb, th)) {
+       switch (tcp_timewait_state_process(inet_twsk(sk), skb, th)) {
        case TCP_TW_SYN:
        {
                struct sock *sk2;
 
-               sk2 = tcp_v6_lookup_listener(&skb->nh.ipv6h->daddr, ntohs(th->dest), tcp_v6_iif(skb));
+               sk2 = inet6_lookup_listener(&tcp_hashinfo,
+                                           &skb->nh.ipv6h->daddr,
+                                           ntohs(th->dest), inet6_iif(skb));
                if (sk2 != NULL) {
-                       tcp_tw_deschedule((struct inet_timewait_sock *)sk);
-                       inet_twsk_put((struct inet_timewait_sock *)sk);
+                       struct inet_timewait_sock *tw = inet_twsk(sk);
+                       inet_twsk_deschedule(tw, &tcp_death_row);
+                       inet_twsk_put(tw);
                        sk = sk2;
                        goto process;
                }
@@ -1837,185 +1818,83 @@ do_time_wait:
        goto discard_it;
 }
 
-static int tcp_v6_rebuild_header(struct sock *sk)
-{
-       int err;
-       struct dst_entry *dst;
-       struct ipv6_pinfo *np = inet6_sk(sk);
-
-       dst = __sk_dst_check(sk, np->dst_cookie);
-
-       if (dst == NULL) {
-               struct inet_sock *inet = inet_sk(sk);
-               struct in6_addr *final_p = NULL, final;
-               struct flowi fl;
-
-               memset(&fl, 0, sizeof(fl));
-               fl.proto = IPPROTO_TCP;
-               ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
-               ipv6_addr_copy(&fl.fl6_src, &np->saddr);
-               fl.fl6_flowlabel = np->flow_label;
-               fl.oif = sk->sk_bound_dev_if;
-               fl.fl_ip_dport = inet->dport;
-               fl.fl_ip_sport = inet->sport;
-
-               if (np->opt && np->opt->srcrt) {
-                       struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
-                       ipv6_addr_copy(&final, &fl.fl6_dst);
-                       ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
-                       final_p = &final;
-               }
-
-               err = ip6_dst_lookup(sk, &dst, &fl);
-               if (err) {
-                       sk->sk_route_caps = 0;
-                       return err;
-               }
-               if (final_p)
-                       ipv6_addr_copy(&fl.fl6_dst, final_p);
-
-               if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
-                       sk->sk_err_soft = -err;
-                       dst_release(dst);
-                       return err;
-               }
-
-               ip6_dst_store(sk, dst, NULL);
-               sk->sk_route_caps = dst->dev->features &
-                       ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
-       }
-
-       return 0;
-}
-
-static int tcp_v6_xmit(struct sk_buff *skb, int ipfragok)
-{
-       struct sock *sk = skb->sk;
-       struct inet_sock *inet = inet_sk(sk);
-       struct ipv6_pinfo *np = inet6_sk(sk);
-       struct flowi fl;
-       struct dst_entry *dst;
-       struct in6_addr *final_p = NULL, final;
-
-       memset(&fl, 0, sizeof(fl));
-       fl.proto = IPPROTO_TCP;
-       ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
-       ipv6_addr_copy(&fl.fl6_src, &np->saddr);
-       fl.fl6_flowlabel = np->flow_label;
-       IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel);
-       fl.oif = sk->sk_bound_dev_if;
-       fl.fl_ip_sport = inet->sport;
-       fl.fl_ip_dport = inet->dport;
-
-       if (np->opt && np->opt->srcrt) {
-               struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
-               ipv6_addr_copy(&final, &fl.fl6_dst);
-               ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
-               final_p = &final;
-       }
-
-       dst = __sk_dst_check(sk, np->dst_cookie);
-
-       if (dst == NULL) {
-               int err = ip6_dst_lookup(sk, &dst, &fl);
-
-               if (err) {
-                       sk->sk_err_soft = -err;
-                       return err;
-               }
-
-               if (final_p)
-                       ipv6_addr_copy(&fl.fl6_dst, final_p);
-
-               if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
-                       sk->sk_route_caps = 0;
-                       dst_release(dst);
-                       return err;
-               }
-
-               ip6_dst_store(sk, dst, NULL);
-               sk->sk_route_caps = dst->dev->features &
-                       ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
-       }
-
-       skb->dst = dst_clone(dst);
-
-       /* Restore final destination back after routing done */
-       ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
-
-       return ip6_xmit(sk, skb, &fl, np->opt, 0);
-}
-
-static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
-{
-       struct ipv6_pinfo *np = inet6_sk(sk);
-       struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) uaddr;
-
-       sin6->sin6_family = AF_INET6;
-       ipv6_addr_copy(&sin6->sin6_addr, &np->daddr);
-       sin6->sin6_port = inet_sk(sk)->dport;
-       /* We do not store received flowlabel for TCP */
-       sin6->sin6_flowinfo = 0;
-       sin6->sin6_scope_id = 0;
-       if (sk->sk_bound_dev_if &&
-           ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
-               sin6->sin6_scope_id = sk->sk_bound_dev_if;
-}
-
 static int tcp_v6_remember_stamp(struct sock *sk)
 {
        /* Alas, not yet... */
        return 0;
 }
 
-static struct tcp_func ipv6_specific = {
-       .queue_xmit     =       tcp_v6_xmit,
-       .send_check     =       tcp_v6_send_check,
-       .rebuild_header =       tcp_v6_rebuild_header,
-       .conn_request   =       tcp_v6_conn_request,
-       .syn_recv_sock  =       tcp_v6_syn_recv_sock,
-       .remember_stamp =       tcp_v6_remember_stamp,
-       .net_header_len =       sizeof(struct ipv6hdr),
-
-       .setsockopt     =       ipv6_setsockopt,
-       .getsockopt     =       ipv6_getsockopt,
-       .addr2sockaddr  =       v6_addr2sockaddr,
-       .sockaddr_len   =       sizeof(struct sockaddr_in6)
+static 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,
+       .conn_request      = tcp_v6_conn_request,
+       .syn_recv_sock     = tcp_v6_syn_recv_sock,
+       .remember_stamp    = tcp_v6_remember_stamp,
+       .net_header_len    = sizeof(struct ipv6hdr),
+       .setsockopt        = ipv6_setsockopt,
+       .getsockopt        = ipv6_getsockopt,
+       .addr2sockaddr     = inet6_csk_addr2sockaddr,
+       .sockaddr_len      = sizeof(struct sockaddr_in6),
+#ifdef CONFIG_COMPAT
+       .compat_setsockopt = compat_ipv6_setsockopt,
+       .compat_getsockopt = compat_ipv6_getsockopt,
+#endif
+};
+
+#ifdef CONFIG_TCP_MD5SIG
+static struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
+       .md5_lookup     =       tcp_v6_md5_lookup,
+       .calc_md5_hash  =       tcp_v6_calc_md5_hash,
+       .md5_add        =       tcp_v6_md5_add_func,
+       .md5_parse      =       tcp_v6_parse_md5_keys,
 };
+#endif
 
 /*
  *     TCP over IPv4 via INET6 API
  */
 
-static struct tcp_func ipv6_mapped = {
-       .queue_xmit     =       ip_queue_xmit,
-       .send_check     =       tcp_v4_send_check,
-       .rebuild_header =       inet_sk_rebuild_header,
-       .conn_request   =       tcp_v6_conn_request,
-       .syn_recv_sock  =       tcp_v6_syn_recv_sock,
-       .remember_stamp =       tcp_v4_remember_stamp,
-       .net_header_len =       sizeof(struct iphdr),
-
-       .setsockopt     =       ipv6_setsockopt,
-       .getsockopt     =       ipv6_getsockopt,
-       .addr2sockaddr  =       v6_addr2sockaddr,
-       .sockaddr_len   =       sizeof(struct sockaddr_in6)
+static 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,
+       .conn_request      = tcp_v6_conn_request,
+       .syn_recv_sock     = tcp_v6_syn_recv_sock,
+       .remember_stamp    = tcp_v4_remember_stamp,
+       .net_header_len    = sizeof(struct iphdr),
+       .setsockopt        = ipv6_setsockopt,
+       .getsockopt        = ipv6_getsockopt,
+       .addr2sockaddr     = inet6_csk_addr2sockaddr,
+       .sockaddr_len      = sizeof(struct sockaddr_in6),
+#ifdef CONFIG_COMPAT
+       .compat_setsockopt = compat_ipv6_setsockopt,
+       .compat_getsockopt = compat_ipv6_getsockopt,
+#endif
 };
 
-
+#ifdef CONFIG_TCP_MD5SIG
+static struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
+       .md5_lookup     =       tcp_v4_md5_lookup,
+       .calc_md5_hash  =       tcp_v4_calc_md5_hash,
+       .md5_add        =       tcp_v6_md5_add_func,
+       .md5_parse      =       tcp_v6_parse_md5_keys,
+};
+#endif
 
 /* NOTE: A lot of things set to zero explicitly by call to
  *       sk_alloc() so need not be done here.
  */
 static int tcp_v6_init_sock(struct sock *sk)
 {
+       struct inet_connection_sock *icsk = inet_csk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
 
        skb_queue_head_init(&tp->out_of_order_queue);
        tcp_init_xmit_timers(sk);
        tcp_prequeue_init(tp);
 
-       inet_csk(sk)->icsk_rto = TCP_TIMEOUT_INIT;
+       icsk->icsk_rto = TCP_TIMEOUT_INIT;
        tp->mdev = TCP_TIMEOUT_INIT;
 
        /* So many TCP implementations out there (incorrectly) count the
@@ -2036,11 +1915,16 @@ static int tcp_v6_init_sock(struct sock *sk)
 
        sk->sk_state = TCP_CLOSE;
 
-       tp->af_specific = &ipv6_specific;
-       tp->ca_ops = &tcp_init_congestion_ops;
+       icsk->icsk_af_ops = &ipv6_specific;
+       icsk->icsk_ca_ops = &tcp_init_congestion_ops;
+       icsk->icsk_sync_mss = tcp_sync_mss;
        sk->sk_write_space = sk_stream_write_space;
        sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
 
+#ifdef CONFIG_TCP_MD5SIG
+       tp->af_specific = &tcp_sock_ipv6_specific;
+#endif
+
        sk->sk_sndbuf = sysctl_tcp_wmem[1];
        sk->sk_rcvbuf = sysctl_tcp_rmem[1];
 
@@ -2051,8 +1935,11 @@ static int tcp_v6_init_sock(struct sock *sk)
 
 static int tcp_v6_destroy_sock(struct sock *sk)
 {
-       extern int tcp_v4_destroy_sock(struct sock *sk);
-
+#ifdef CONFIG_TCP_MD5SIG
+       /* Clean up the MD5 key list */
+       if (tcp_sk(sk)->md5sig_info)
+               tcp_v6_clear_md5_list(sk);
+#endif
        tcp_v4_destroy_sock(sk);
        return inet6_destroy_sock(sk);
 }
@@ -2061,14 +1948,13 @@ static int tcp_v6_destroy_sock(struct sock *sk)
 static void get_openreq6(struct seq_file *seq, 
                         struct sock *sk, struct request_sock *req, int i, int uid)
 {
-       struct in6_addr *dest, *src;
        int ttd = req->expires - jiffies;
+       struct in6_addr *src = &inet6_rsk(req)->loc_addr;
+       struct in6_addr *dest = &inet6_rsk(req)->rmt_addr;
 
        if (ttd < 0)
                ttd = 0;
 
-       src = &tcp6_rsk(req)->loc_addr;
-       dest = &tcp6_rsk(req)->rmt_addr;
        seq_printf(seq,
                   "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
                   "%02X %08X:%08X %02X:%08lX %08X %5d %8d %d %d %p\n",
@@ -2129,12 +2015,13 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
                   dest->s6_addr32[0], dest->s6_addr32[1],
                   dest->s6_addr32[2], dest->s6_addr32[3], destp,
                   sp->sk_state, 
-                  tp->write_seq-tp->snd_una, tp->rcv_nxt-tp->copied_seq,
+                  tp->write_seq-tp->snd_una,
+                  (sp->sk_state == TCP_LISTEN) ? sp->sk_ack_backlog : (tp->rcv_nxt - tp->copied_seq),
                   timer_active,
                   jiffies_to_clock_t(timer_expires - jiffies),
                   icsk->icsk_retransmits,
                   sock_i_uid(sp),
-                  tp->probes_out,
+                  icsk->icsk_probes_out,
                   sock_i_ino(sp),
                   atomic_read(&sp->sk_refcnt), sp,
                   icsk->icsk_rto,
@@ -2149,14 +2036,14 @@ static void get_timewait6_sock(struct seq_file *seq,
 {
        struct in6_addr *dest, *src;
        __u16 destp, srcp;
-       struct tcp6_timewait_sock *tcp6tw = tcp6_twsk((struct sock *)tw);
+       struct inet6_timewait_sock *tw6 = inet6_twsk((struct sock *)tw);
        int ttd = tw->tw_ttd - jiffies;
 
        if (ttd < 0)
                ttd = 0;
 
-       dest = &tcp6tw->tw_v6_daddr;
-       src  = &tcp6tw->tw_v6_rcv_saddr;
+       dest = &tw6->tw_v6_daddr;
+       src  = &tw6->tw_v6_rcv_saddr;
        destp = ntohs(tw->tw_dport);
        srcp  = ntohs(tw->tw_sport);
 
@@ -2248,23 +2135,28 @@ struct proto tcpv6_prot = {
        .sockets_allocated      = &tcp_sockets_allocated,
        .memory_allocated       = &tcp_memory_allocated,
        .memory_pressure        = &tcp_memory_pressure,
+       .orphan_count           = &tcp_orphan_count,
        .sysctl_mem             = sysctl_tcp_mem,
        .sysctl_wmem            = sysctl_tcp_wmem,
        .sysctl_rmem            = sysctl_tcp_rmem,
        .max_header             = MAX_TCP_HEADER,
        .obj_size               = sizeof(struct tcp6_sock),
-       .twsk_obj_size          = sizeof(struct tcp6_timewait_sock),
+       .twsk_prot              = &tcp6_timewait_sock_ops,
        .rsk_prot               = &tcp6_request_sock_ops,
+#ifdef CONFIG_COMPAT
+       .compat_setsockopt      = compat_tcp_setsockopt,
+       .compat_getsockopt      = compat_tcp_getsockopt,
+#endif
 };
 
 static 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,
        .flags          =       INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
-extern struct proto_ops inet6_stream_ops;
-
 static struct inet_protosw tcpv6_protosw = {
        .type           =       SOCK_STREAM,
        .protocol       =       IPPROTO_TCP,
@@ -2272,7 +2164,8 @@ static struct inet_protosw tcpv6_protosw = {
        .ops            =       &inet6_stream_ops,
        .capability     =       -1,
        .no_check       =       0,
-       .flags          =       INET_PROTOSW_PERMANENT,
+       .flags          =       INET_PROTOSW_PERMANENT |
+                               INET_PROTOSW_ICSK,
 };
 
 void __init tcpv6_init(void)
@@ -2281,4 +2174,8 @@ void __init tcpv6_init(void)
        if (inet6_add_protocol(&tcpv6_protocol, IPPROTO_TCP) < 0)
                printk(KERN_ERR "tcpv6_init: Could not register protocol\n");
        inet6_register_protosw(&tcpv6_protosw);
+
+       if (inet_csk_ctl_sock_create(&tcp6_socket, PF_INET6, SOCK_RAW,
+                                    IPPROTO_TCP) < 0)
+               panic("Failed to create the TCPv6 control socket.\n");
 }