]> err.no Git - linux-2.6/commitdiff
Merge branch 'net-next-2.6-misc-20080612a' of git://git.linux-ipv6.org/gitroot/yoshfu...
authorDavid S. Miller <davem@davemloft.net>
Thu, 12 Jun 2008 05:33:59 +0000 (22:33 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 12 Jun 2008 05:33:59 +0000 (22:33 -0700)
1  2 
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_output.c
net/ipv6/addrconf.c
net/ipv6/mcast.c
net/ipv6/tcp_ipv6.c

diff --combined net/ipv4/tcp.c
index ad66b09e0bcd9abe3392a7d4ecc95383181705e4,6efbae0e5512e7e0d29e8235a9fe9c5679452c4f..6d30ca559c64d1bb10dc81698fac93f1ad36caa5
@@@ -5,6 -5,8 +5,6 @@@
   *
   *            Implementation of the Transmission Control Protocol(TCP).
   *
 - * Version:   $Id: tcp.c,v 1.216 2002/02/01 22:01:04 davem Exp $
 - *
   * Authors:   Ross Biro
   *            Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
   *            Mark Evans, <evansmp@uhura.aston.ac.uk>
@@@ -2457,6 -2459,76 +2457,76 @@@ static unsigned long tcp_md5sig_users
  static struct tcp_md5sig_pool **tcp_md5sig_pool;
  static DEFINE_SPINLOCK(tcp_md5sig_pool_lock);
  
+ int tcp_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
+                     int bplen,
+                     struct tcphdr *th, unsigned int tcplen,
+                     struct tcp_md5sig_pool *hp)
+ {
+       struct scatterlist sg[4];
+       __u16 data_len;
+       int block = 0;
+       __sum16 cksum;
+       struct hash_desc *desc = &hp->md5_desc;
+       int err;
+       unsigned int nbytes = 0;
+       sg_init_table(sg, 4);
+       /* 1. The TCP pseudo-header */
+       sg_set_buf(&sg[block++], &hp->md5_blk, bplen);
+       nbytes += bplen;
+       /* 2. The TCP header, excluding options, and assuming a
+        * checksum of zero
+        */
+       cksum = th->check;
+       th->check = 0;
+       sg_set_buf(&sg[block++], th, sizeof(*th));
+       nbytes += sizeof(*th);
+       /* 3. The 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. an independently-specified key or password, known to both
+        * TCPs and presumably connection-specific
+        */
+       sg_set_buf(&sg[block++], key->key, key->keylen);
+       nbytes += key->keylen;
+       sg_mark_end(&sg[block - 1]);
+       /* Now store the hash into the packet */
+       err = crypto_hash_init(desc);
+       if (err) {
+               if (net_ratelimit())
+                       printk(KERN_WARNING "%s(): hash_init failed\n", __func__);
+               return -1;
+       }
+       err = crypto_hash_update(desc, sg, nbytes);
+       if (err) {
+               if (net_ratelimit())
+                       printk(KERN_WARNING "%s(): hash_update failed\n", __func__);
+               return -1;
+       }
+       err = crypto_hash_final(desc, md5_hash);
+       if (err) {
+               if (net_ratelimit())
+                       printk(KERN_WARNING "%s(): hash_final failed\n", __func__);
+               return -1;
+       }
+       /* Reset header */
+       th->check = cksum;
+       return 0;
+ }
+ EXPORT_SYMBOL(tcp_calc_md5_hash);
  static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool **pool)
  {
        int cpu;
diff --combined net/ipv4/tcp_input.c
index b68c3c7d906bde9c1d2b7f35534b8212e0abff23,f331e67f2328da8b080f221efa2d1e415085844d..bc7f62e2792b4039e6b6f033696a0263f5cb1a8f
@@@ -5,6 -5,8 +5,6 @@@
   *
   *            Implementation of the Transmission Control Protocol(TCP).
   *
 - * Version:   $Id: tcp_input.c,v 1.243 2002/02/01 22:01:04 davem Exp $
 - *
   * Authors:   Ross Biro
   *            Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
   *            Mark Evans, <evansmp@uhura.aston.ac.uk>
@@@ -3448,6 -3450,43 +3448,43 @@@ static int tcp_fast_parse_options(struc
        return 1;
  }
  
+ #ifdef CONFIG_TCP_MD5SIG
+ /*
+  * Parse MD5 Signature option
+  */
+ u8 *tcp_parse_md5sig_option(struct tcphdr *th)
+ {
+       int length = (th->doff << 2) - sizeof (*th);
+       u8 *ptr = (u8*)(th + 1);
+       /* If the TCP option is too short, we can short cut */
+       if (length < TCPOLEN_MD5SIG)
+               return NULL;
+       while (length > 0) {
+               int opcode = *ptr++;
+               int opsize;
+               switch(opcode) {
+               case TCPOPT_EOL:
+                       return NULL;
+               case TCPOPT_NOP:
+                       length--;
+                       continue;
+               default:
+                       opsize = *ptr++;
+                       if (opsize < 2 || opsize > length)
+                               return NULL;
+                       if (opcode == TCPOPT_MD5SIG)
+                               return ptr;
+               }
+               ptr += opsize - 2;
+               length -= opsize;
+       }
+       return NULL;
+ }
+ #endif
  static inline void tcp_store_ts_recent(struct tcp_sock *tp)
  {
        tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval;
@@@ -5465,6 -5504,9 +5502,9 @@@ EXPORT_SYMBOL(sysctl_tcp_ecn)
  EXPORT_SYMBOL(sysctl_tcp_reordering);
  EXPORT_SYMBOL(sysctl_tcp_adv_win_scale);
  EXPORT_SYMBOL(tcp_parse_options);
+ #ifdef CONFIG_TCP_MD5SIG
+ EXPORT_SYMBOL(tcp_parse_md5sig_option);
+ #endif
  EXPORT_SYMBOL(tcp_rcv_established);
  EXPORT_SYMBOL(tcp_rcv_state_process);
  EXPORT_SYMBOL(tcp_initialize_rcv_mss);
diff --combined net/ipv4/tcp_ipv4.c
index f2926ae1de57dee2821915c2e3f11b60e36927bf,f7ff2a64a7f054488cdaba6a32e0c986287642d3..9088d709725ea94fe3d3128508e2568adab30677
@@@ -5,6 -5,8 +5,6 @@@
   *
   *            Implementation of the Transmission Control Protocol(TCP).
   *
 - * Version:   $Id: tcp_ipv4.c,v 1.240 2002/02/01 22:01:04 davem Exp $
 - *
   *            IPv4 specific functions
   *
   *
@@@ -93,8 -95,13 +93,13 @@@ static struct tcp_md5sig_key *tcp_v4_md
                                                   __be32 addr);
  static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
                                   __be32 saddr, __be32 daddr,
-                                  struct tcphdr *th, int protocol,
-                                  unsigned int tcplen);
+                                  struct tcphdr *th, unsigned int tcplen);
+ #else
+ static inline
+ struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
+ {
+       return NULL;
+ }
  #endif
  
  struct inet_hashinfo __cacheline_aligned tcp_hashinfo = {
@@@ -584,8 -591,7 +589,7 @@@ static void tcp_v4_send_reset(struct so
                                        key,
                                        ip_hdr(skb)->daddr,
                                        ip_hdr(skb)->saddr,
-                                       &rep.th, IPPROTO_TCP,
-                                       arg.iov[0].iov_len);
+                                       &rep.th, arg.iov[0].iov_len);
        }
  #endif
        arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
     outside socket context is ugly, certainly. What can I do?
   */
  
- static void tcp_v4_send_ack(struct tcp_timewait_sock *twsk,
-                           struct sk_buff *skb, u32 seq, u32 ack,
-                           u32 win, u32 ts)
+ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
+                           u32 win, u32 ts, int oif,
+                           struct tcp_md5sig_key *key)
  {
        struct tcphdr *th = tcp_hdr(skb);
        struct {
                        ];
        } rep;
        struct ip_reply_arg arg;
- #ifdef CONFIG_TCP_MD5SIG
-       struct tcp_md5sig_key *key;
-       struct tcp_md5sig_key tw_key;
- #endif
  
        memset(&rep.th, 0, sizeof(struct tcphdr));
        memset(&arg, 0, sizeof(arg));
        rep.th.window  = htons(win);
  
  #ifdef CONFIG_TCP_MD5SIG
-       /*
-        * The SKB holds an imcoming packet, but may not have a valid ->sk
-        * pointer. This is especially the case when we're dealing with a
-        * TIME_WAIT ack, because the sk structure is long gone, and only
-        * the tcp_timewait_sock remains. So the md5 key is stashed in that
-        * structure, and we use it in preference.  I believe that (twsk ||
-        * skb->sk) holds true, but we program defensively.
-        */
-       if (!twsk && skb->sk) {
-               key = tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr);
-       } else if (twsk && twsk->tw_md5_keylen) {
-               tw_key.key = twsk->tw_md5_key;
-               tw_key.keylen = twsk->tw_md5_keylen;
-               key = &tw_key;
-       } else
-               key = NULL;
        if (key) {
                int offset = (ts) ? 3 : 0;
  
                                        key,
                                        ip_hdr(skb)->daddr,
                                        ip_hdr(skb)->saddr,
-                                       &rep.th, IPPROTO_TCP,
-                                       arg.iov[0].iov_len);
+                                       &rep.th, arg.iov[0].iov_len);
        }
  #endif
        arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
                                      ip_hdr(skb)->saddr, /* XXX */
                                      arg.iov[0].iov_len, IPPROTO_TCP, 0);
        arg.csumoffset = offsetof(struct tcphdr, check) / 2;
-       if (twsk)
-               arg.bound_dev_if = twsk->tw_sk.tw_bound_dev_if;
+       if (oif)
+               arg.bound_dev_if = oif;
  
        ip_send_reply(dev_net(skb->dev)->ipv4.tcp_sock, skb,
                      &arg, arg.iov[0].iov_len);
@@@ -700,9 -684,12 +682,12 @@@ static void tcp_v4_timewait_ack(struct 
        struct inet_timewait_sock *tw = inet_twsk(sk);
        struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
  
-       tcp_v4_send_ack(tcptw, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
+       tcp_v4_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
                        tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
-                       tcptw->tw_ts_recent);
+                       tcptw->tw_ts_recent,
+                       tw->tw_bound_dev_if,
+                       tcp_twsk_md5_key(tcptw)
+                       );
  
        inet_twsk_put(tw);
  }
  static void tcp_v4_reqsk_send_ack(struct sk_buff *skb,
                                  struct request_sock *req)
  {
-       tcp_v4_send_ack(NULL, skb, tcp_rsk(req)->snt_isn + 1,
+       tcp_v4_send_ack(skb, tcp_rsk(req)->snt_isn + 1,
                        tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd,
-                       req->ts_recent);
+                       req->ts_recent,
+                       0,
+                       tcp_v4_md5_do_lookup(skb->sk, ip_hdr(skb)->daddr));
  }
  
  /*
@@@ -1004,18 -993,12 +991,12 @@@ static int tcp_v4_parse_md5_keys(struc
  
  static int tcp_v4_do_calc_md5_hash(char *md5_hash, struct tcp_md5sig_key *key,
                                   __be32 saddr, __be32 daddr,
-                                  struct tcphdr *th, int protocol,
+                                  struct tcphdr *th,
                                   unsigned int tcplen)
  {
-       struct scatterlist sg[4];
-       __u16 data_len;
-       int block = 0;
-       __sum16 old_checksum;
        struct tcp_md5sig_pool *hp;
        struct tcp4_pseudohdr *bp;
-       struct hash_desc *desc;
        int err;
-       unsigned int nbytes = 0;
  
        /*
         * Okay, so RFC2385 is turned on for this connection,
                goto clear_hash_noput;
  
        bp = &hp->md5_blk.ip4;
-       desc = &hp->md5_desc;
  
        /*
-        * 1. the TCP pseudo-header (in the order: source IP address,
+        * The TCP pseudo-header (in the order: source IP address,
         * destination IP address, zero-padded protocol number, and
         * segment length)
         */
        bp->saddr = saddr;
        bp->daddr = daddr;
        bp->pad = 0;
-       bp->protocol = protocol;
+       bp->protocol = IPPROTO_TCP;
        bp->len = htons(tcplen);
  
-       sg_init_table(sg, 4);
-       sg_set_buf(&sg[block++], bp, sizeof(*bp));
-       nbytes += sizeof(*bp);
-       /* 2. the TCP header, excluding options, and assuming a
-        * checksum of zero/
-        */
-       old_checksum = th->check;
-       th->check = 0;
-       sg_set_buf(&sg[block++], th, sizeof(struct tcphdr));
-       nbytes += sizeof(struct tcphdr);
-       /* 3. the TCP segment data (if any) */
-       data_len = tcplen - (th->doff << 2);
-       if (data_len > 0) {
-               unsigned char *data = (unsigned char *)th + (th->doff << 2);
-               sg_set_buf(&sg[block++], data, data_len);
-               nbytes += data_len;
-       }
-       /* 4. an independently-specified key or password, known to both
-        * TCPs and presumably connection-specific
-        */
-       sg_set_buf(&sg[block++], key->key, key->keylen);
-       nbytes += key->keylen;
-       sg_mark_end(&sg[block - 1]);
-       /* Now store the Hash into the packet */
-       err = crypto_hash_init(desc);
-       if (err)
-               goto clear_hash;
-       err = crypto_hash_update(desc, sg, nbytes);
-       if (err)
-               goto clear_hash;
-       err = crypto_hash_final(desc, md5_hash);
+       err = tcp_calc_md5_hash(md5_hash, key, sizeof(*bp),
+                               th, tcplen, hp);
        if (err)
                goto clear_hash;
  
-       /* Reset header, and free up the crypto */
+       /* Free up the crypto pool */
        tcp_put_md5sig_pool();
-       th->check = old_checksum;
  out:
        return 0;
  clear_hash:
@@@ -1097,7 -1042,7 +1040,7 @@@ int tcp_v4_calc_md5_hash(char *md5_hash
                         struct sock *sk,
                         struct dst_entry *dst,
                         struct request_sock *req,
-                        struct tcphdr *th, int protocol,
+                        struct tcphdr *th,
                         unsigned int tcplen)
  {
        __be32 saddr, daddr;
        }
        return tcp_v4_do_calc_md5_hash(md5_hash, key,
                                       saddr, daddr,
-                                      th, protocol, tcplen);
+                                      th, tcplen);
  }
  
  EXPORT_SYMBOL(tcp_v4_calc_md5_hash);
@@@ -1132,52 -1077,12 +1075,12 @@@ static int tcp_v4_inbound_md5_hash(stru
        struct tcp_md5sig_key *hash_expected;
        const struct iphdr *iph = ip_hdr(skb);
        struct tcphdr *th = tcp_hdr(skb);
-       int length = (th->doff << 2) - sizeof(struct tcphdr);
        int genhash;
-       unsigned char *ptr;
        unsigned char newhash[16];
  
        hash_expected = tcp_v4_md5_do_lookup(sk, iph->saddr);
+       hash_location = tcp_parse_md5sig_option(th);
  
-       /*
-        * If the TCP option length is less than the TCP_MD5SIG
-        * option length, then we can shortcut
-        */
-       if (length < TCPOLEN_MD5SIG) {
-               if (hash_expected)
-                       return 1;
-               else
-                       return 0;
-       }
-       /* Okay, we can't shortcut - we have to grub through the options */
-       ptr = (unsigned char *)(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)
-                               goto done_opts;
-                       if (opsize > length)
-                               goto done_opts;
-                       if (opcode == TCPOPT_MD5SIG) {
-                               hash_location = ptr;
-                               goto done_opts;
-                       }
-               }
-               ptr += opsize-2;
-               length -= opsize;
-       }
- done_opts:
        /* We've parsed the options - do we have a hash? */
        if (!hash_expected && !hash_location)
                return 0;
        genhash = tcp_v4_do_calc_md5_hash(newhash,
                                          hash_expected,
                                          iph->saddr, iph->daddr,
-                                         th, sk->sk_protocol,
-                                         skb->len);
+                                         th, skb->len);
  
        if (genhash || memcmp(hash_location, newhash, 16) != 0) {
                if (net_ratelimit()) {
diff --combined net/ipv4/tcp_output.c
index b171ac65ccabfe65951347cc05fde2398cf65b73,f3ffd674edde4cd584420ed13d710c25c500529d..8f83ab4327052979138d1466028d42332bd2d832
@@@ -5,6 -5,8 +5,6 @@@
   *
   *            Implementation of the Transmission Control Protocol(TCP).
   *
 - * Version:   $Id: tcp_output.c,v 1.146 2002/02/01 22:01:04 davem Exp $
 - *
   * Authors:   Ross Biro
   *            Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
   *            Mark Evans, <evansmp@uhura.aston.ac.uk>
@@@ -605,7 -607,6 +605,6 @@@ static int tcp_transmit_skb(struct soc
                                               md5,
                                               sk, NULL, NULL,
                                               tcp_hdr(skb),
-                                              sk->sk_protocol,
                                               skb->len);
        }
  #endif
@@@ -2264,7 -2265,7 +2263,7 @@@ struct sk_buff *tcp_make_synack(struct 
                tp->af_specific->calc_md5_hash(md5_hash_location,
                                               md5,
                                               NULL, dst, req,
-                                              tcp_hdr(skb), sk->sk_protocol,
+                                              tcp_hdr(skb),
                                               skb->len);
        }
  #endif
diff --combined net/ipv6/addrconf.c
index deb38bf0337639ddaee709afb300feddaf5ceb84,fa43374e85c5aed988adbb03d618dc04d57e2dc1..9be6be3a7ff3cecad3fee2a98c904d3af3b9e852
@@@ -6,6 -6,8 +6,6 @@@
   *    Pedro Roque             <roque@di.fc.ul.pt>
   *    Alexey Kuznetsov        <kuznet@ms2.inr.ac.ru>
   *
 - *    $Id: addrconf.c,v 1.69 2001/10/31 21:55:54 davem Exp $
 - *
   *    This program is free software; you can redistribute it and/or
   *      modify it under the terms of the GNU General Public License
   *      as published by the Free Software Foundation; either version
@@@ -229,6 -231,12 +229,12 @@@ static inline int addrconf_qdisc_ok(str
        return (dev->qdisc != &noop_qdisc);
  }
  
+ /* Check if a route is valid prefix route */
+ static inline int addrconf_is_prefix_route(const struct rt6_info *rt)
+ {
+       return ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0);
+ }
  static void addrconf_del_timer(struct inet6_ifaddr *ifp)
  {
        if (del_timer(&ifp->timer))
@@@ -775,7 -783,7 +781,7 @@@ static void ipv6_del_addr(struct inet6_
                ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len);
                rt = rt6_lookup(net, &prefix, NULL, ifp->idev->dev->ifindex, 1);
  
-               if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
+               if (rt && addrconf_is_prefix_route(rt)) {
                        if (onlink == 0) {
                                ip6_del_rt(rt);
                                rt = NULL;
@@@ -956,7 -964,8 +962,8 @@@ static inline int ipv6_saddr_preferred(
        return 0;
  }
  
- static int ipv6_get_saddr_eval(struct ipv6_saddr_score *score,
+ static int ipv6_get_saddr_eval(struct net *net,
+                              struct ipv6_saddr_score *score,
                               struct ipv6_saddr_dst *dst,
                               int i)
  {
                break;
        case IPV6_SADDR_RULE_LABEL:
                /* Rule 6: Prefer matching label */
-               ret = ipv6_addr_label(&score->ifa->addr, score->addr_type,
+               ret = ipv6_addr_label(net,
+                                     &score->ifa->addr, score->addr_type,
                                      score->ifa->idev->dev->ifindex) == dst->label;
                break;
  #ifdef CONFIG_IPV6_PRIVACY
@@@ -1089,7 -1099,7 +1097,7 @@@ int ipv6_dev_get_saddr(struct net_devic
        dst.addr = daddr;
        dst.ifindex = dst_dev ? dst_dev->ifindex : 0;
        dst.scope = __ipv6_addr_src_scope(dst_type);
-       dst.label = ipv6_addr_label(daddr, dst_type, dst.ifindex);
+       dst.label = ipv6_addr_label(net, daddr, dst_type, dst.ifindex);
        dst.prefs = prefs;
  
        hiscore->rule = -1;
                        for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) {
                                int minihiscore, miniscore;
  
-                               minihiscore = ipv6_get_saddr_eval(hiscore, &dst, i);
-                               miniscore = ipv6_get_saddr_eval(score, &dst, i);
+                               minihiscore = ipv6_get_saddr_eval(net, hiscore, &dst, i);
+                               miniscore = ipv6_get_saddr_eval(net, score, &dst, i);
  
                                if (minihiscore > miniscore) {
                                        if (i == IPV6_SADDR_RULE_SCOPE &&
@@@ -1786,7 -1796,7 +1794,7 @@@ void addrconf_prefix_rcv(struct net_dev
                rt = rt6_lookup(dev_net(dev), &pinfo->prefix, NULL,
                                dev->ifindex, 1);
  
-               if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
+               if (rt && addrconf_is_prefix_route(rt)) {
                        /* Autoconf prefix route */
                        if (valid_lft == 0) {
                                ip6_del_rt(rt);
diff --combined net/ipv6/mcast.c
index fbb2d12c41bc25f5b97f00209c71e8943d99f5e5,ee30ec4c3a6e46a427441084ea4d6b08ed3dc295..bd2fe4cfafa7f3dfdaff9e6602ec296e62e34584
@@@ -5,6 -5,8 +5,6 @@@
   *    Authors:
   *    Pedro Roque             <roque@di.fc.ul.pt>
   *
 - *    $Id: mcast.c,v 1.40 2002/02/08 03:57:19 davem Exp $
 - *
   *    Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c
   *
   *    This program is free software; you can redistribute it and/or
@@@ -162,7 -164,6 +162,6 @@@ static int ip6_mc_leave_src(struct soc
        ((MLDV2_MASK(value, nbmant) | (1<<(nbmant))) << \
        (MLDV2_MASK((value) >> (nbmant), nbexp) + (nbexp))))
  
- #define MLDV2_QQIC(value) MLDV2_EXP(0x80, 4, 3, value)
  #define MLDV2_MRC(value) MLDV2_EXP(0x8000, 12, 3, value)
  
  #define IPV6_MLD_MAX_MSF      64
diff --combined net/ipv6/tcp_ipv6.c
index 155499197fc5618dfe614383dfdd24b7a3dfcd64,ecdbb9f46541ca1c2ef65a955248c62f12914b11..3fe736bead963f9c7168f20f9b7794420146fe7e
@@@ -5,6 -5,8 +5,6 @@@
   *    Authors:
   *    Pedro Roque             <roque@di.fc.ul.pt>
   *
 - *    $Id: tcp_ipv6.c,v 1.144 2002/02/01 22:01:04 davem Exp $
 - *
   *    Based on:
   *    linux/net/ipv4/tcp.c
   *    linux/net/ipv4/tcp_input.c
@@@ -80,6 -82,12 +80,12 @@@ static struct inet_connection_sock_af_o
  #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;
+ #else
+ static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,
+                                                  struct in6_addr *addr)
+ {
+       return NULL;
+ }
  #endif
  
  static void tcp_v6_hash(struct sock *sk)
@@@ -734,78 -742,34 +740,34 @@@ static int tcp_v6_parse_md5_keys (struc
  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,
-                                  unsigned int tcplen)
+                                  struct tcphdr *th, unsigned 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", __func__);
                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_init_table(sg, 4);
-       sg_set_buf(&sg[block++], bp, sizeof(*bp));
-       nbytes += sizeof(*bp);
+       bp->protocol = htonl(IPPROTO_TCP);
  
-       /* 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;
-       sg_mark_end(&sg[block - 1]);
+       err = tcp_calc_md5_hash(md5_hash, key, sizeof(*bp),
+                               th, tcplen, hp);
  
-       /* Now store the hash into the packet */
-       err = crypto_hash_init(desc);
-       if (err) {
-               printk(KERN_WARNING "%s(): hash_init failed\n", __func__);
-               goto clear_hash;
-       }
-       err = crypto_hash_update(desc, sg, nbytes);
-       if (err) {
-               printk(KERN_WARNING "%s(): hash_update failed\n", __func__);
-               goto clear_hash;
-       }
-       err = crypto_hash_final(desc, md5_hash);
-       if (err) {
-               printk(KERN_WARNING "%s(): hash_final failed\n", __func__);
+       if (err)
                goto clear_hash;
-       }
  
-       /* Reset header, and free up the crypto */
+       /* Free up the crypto pool */
        tcp_put_md5sig_pool();
-       th->check = cksum;
  out:
        return 0;
  clear_hash:
@@@ -819,8 -783,7 +781,7 @@@ static int tcp_v6_calc_md5_hash(char *m
                                struct sock *sk,
                                struct dst_entry *dst,
                                struct request_sock *req,
-                               struct tcphdr *th, int protocol,
-                               unsigned int tcplen)
+                               struct tcphdr *th, unsigned int tcplen)
  {
        struct in6_addr *saddr, *daddr;
  
        }
        return tcp_v6_do_calc_md5_hash(md5_hash, key,
                                       saddr, daddr,
-                                      th, protocol, tcplen);
+                                      th, tcplen);
  }
  
  static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb)
        struct tcp_md5sig_key *hash_expected;
        struct ipv6hdr *ip6h = ipv6_hdr(skb);
        struct tcphdr *th = tcp_hdr(skb);
-       int length = (th->doff << 2) - sizeof (*th);
        int genhash;
-       u8 *ptr;
        u8 newhash[16];
  
        hash_expected = tcp_v6_md5_do_lookup(sk, &ip6h->saddr);
+       hash_location = tcp_parse_md5sig_option(th);
  
-       /* 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)
        genhash = tcp_v6_do_calc_md5_hash(newhash,
                                          hash_expected,
                                          &ip6h->saddr, &ip6h->daddr,
-                                         th, sk->sk_protocol,
-                                         skb->len);
+                                         th, skb->len);
        if (genhash || memcmp(hash_location, newhash, 16) != 0) {
                if (net_ratelimit()) {
                        printk(KERN_INFO "MD5 Hash %s for "
@@@ -1049,7 -980,7 +978,7 @@@ static void tcp_v6_send_reset(struct so
                tcp_v6_do_calc_md5_hash((__u8 *)&opt[1], key,
                                        &ipv6_hdr(skb)->daddr,
                                        &ipv6_hdr(skb)->saddr,
-                                       t1, IPPROTO_TCP, tot_len);
+                                       t1, tot_len);
        }
  #endif
  
        kfree_skb(buff);
  }
  
- static void tcp_v6_send_ack(struct tcp_timewait_sock *tw,
-                           struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts)
+ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 ts,
+                           struct tcp_md5sig_key *key)
  {
        struct tcphdr *th = tcp_hdr(skb), *t1;
        struct sk_buff *buff;
        struct sock *ctl_sk = net->ipv6.tcp_sk;
        unsigned 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, &ipv6_hdr(skb)->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 += TCPOLEN_TSTAMP_ALIGNED;
                tcp_v6_do_calc_md5_hash((__u8 *)topt, key,
                                        &ipv6_hdr(skb)->daddr,
                                        &ipv6_hdr(skb)->saddr,
-                                       t1, IPPROTO_TCP, tot_len);
+                                       t1, tot_len);
        }
  #endif
  
@@@ -1191,16 -1106,17 +1104,17 @@@ static void tcp_v6_timewait_ack(struct 
        struct inet_timewait_sock *tw = inet_twsk(sk);
        struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
  
-       tcp_v6_send_ack(tcptw, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
+       tcp_v6_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
                        tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
-                       tcptw->tw_ts_recent);
+                       tcptw->tw_ts_recent, tcp_twsk_md5_key(tcptw));
  
        inet_twsk_put(tw);
  }
  
  static void tcp_v6_reqsk_send_ack(struct sk_buff *skb, struct request_sock *req)
  {
-       tcp_v6_send_ack(NULL, skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1, req->rcv_wnd, req->ts_recent);
+       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));
  }