]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
tcp: Convert tcp-md5 to use MD5 library instead of crypto_ahash
authorEric Biggers <ebiggers@kernel.org>
Tue, 14 Oct 2025 21:58:36 +0000 (14:58 -0700)
committerJakub Kicinski <kuba@kernel.org>
Sat, 18 Oct 2025 00:14:54 +0000 (17:14 -0700)
Make tcp-md5 use the MD5 library API (added in 6.18) instead of the
crypto_ahash API.  This is much simpler and also more efficient:

- The library API just operates on struct md5_ctx.  Just allocate this
  struct on the stack instead of using a pool of pre-allocated
  crypto_ahash and ahash_request objects.

- The library API accepts standard pointers and doesn't require
  scatterlists.  So, for hashing the headers just use an on-stack buffer
  instead of a pool of pre-allocated kmalloc'ed scratch buffers.

- The library API never fails.  Therefore, checking for MD5 hashing
  errors is no longer necessary.  Update tcp_v4_md5_hash_skb(),
  tcp_v6_md5_hash_skb(), tcp_v4_md5_hash_hdr(), tcp_v6_md5_hash_hdr(),
  tcp_md5_hash_key(), tcp_sock_af_ops::calc_md5_hash, and
  tcp_request_sock_ops::calc_md5_hash to return void instead of int.

- The library API provides direct access to the MD5 code, eliminating
  unnecessary overhead such as indirect function calls and scatterlist
  management.  Microbenchmarks of tcp_v4_md5_hash_skb() on x86_64 show a
  speedup from 7518 to 7041 cycles (6% fewer) with skb->len == 1440, or
  from 1020 to 678 cycles (33% fewer) with skb->len == 140.

Since tcp_sigpool_hash_skb_data() can no longer be used, add a function
tcp_md5_hash_skb_data() which is specialized to MD5.  Of course, to the
extent that this duplicates any code, it's well worth it.

To preserve the existing behavior of TCP-MD5 support being disabled when
the kernel is booted with "fips=1", make tcp_md5_do_add() check
fips_enabled itself.  Previously it relied on the error from
crypto_alloc_ahash("md5") being bubbled up.  I don't know for sure that
this is actually needed, but this preserves the existing behavior.

Tested with bidirectional TCP-MD5, both IPv4 and IPv6, between a kernel
that includes this commit and a kernel that doesn't include this commit.

(Side note: please don't use TCP-MD5!  It's cryptographically weak.  But
as long as Linux supports it, it might as well be implemented properly.)

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Link: https://patch.msgid.link/20251014215836.115616-1-ebiggers@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/tcp.h
net/ipv4/Kconfig
net/ipv4/tcp.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_minisocks.c
net/ipv6/tcp_ipv6.c

index 1e547138f4fb7f5c47d15990954d4d135f465f73..67fdd2523d929cec47655dd55bf449a1299ad621 100644 (file)
@@ -1898,13 +1898,6 @@ struct tcp6_pseudohdr {
        __be32          protocol;       /* including padding */
 };
 
-union tcp_md5sum_block {
-       struct tcp4_pseudohdr ip4;
-#if IS_ENABLED(CONFIG_IPV6)
-       struct tcp6_pseudohdr ip6;
-#endif
-};
-
 /*
  * struct tcp_sigpool - per-CPU pool of ahash_requests
  * @scratch: per-CPU temporary area, that can be used between
@@ -1939,8 +1932,8 @@ int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c);
 void tcp_sigpool_end(struct tcp_sigpool *c);
 size_t tcp_sigpool_algo(unsigned int id, char *buf, size_t buf_len);
 /* - functions */
-int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
-                       const struct sock *sk, const struct sk_buff *skb);
+void tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
+                        const struct sock *sk, const struct sk_buff *skb);
 int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
                   int family, u8 prefixlen, int l3index, u8 flags,
                   const u8 *newkey, u8 newkeylen);
@@ -1999,13 +1992,10 @@ static inline void tcp_md5_destruct_sock(struct sock *sk)
 }
 #endif
 
-int tcp_md5_alloc_sigpool(void);
-void tcp_md5_release_sigpool(void);
-void tcp_md5_add_sigpool(void);
-extern int tcp_md5_sigpool_id;
-
-int tcp_md5_hash_key(struct tcp_sigpool *hp,
-                    const struct tcp_md5sig_key *key);
+struct md5_ctx;
+void tcp_md5_hash_skb_data(struct md5_ctx *ctx, const struct sk_buff *skb,
+                          unsigned int header_len);
+void tcp_md5_hash_key(struct md5_ctx *ctx, const struct tcp_md5sig_key *key);
 
 /* From tcp_fastopen.c */
 void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
@@ -2355,7 +2345,7 @@ struct tcp_sock_af_ops {
 #ifdef CONFIG_TCP_MD5SIG
        struct tcp_md5sig_key   *(*md5_lookup) (const struct sock *sk,
                                                const struct sock *addr_sk);
-       int             (*calc_md5_hash)(char *location,
+       void            (*calc_md5_hash)(char *location,
                                         const struct tcp_md5sig_key *md5,
                                         const struct sock *sk,
                                         const struct sk_buff *skb);
@@ -2383,7 +2373,7 @@ struct tcp_request_sock_ops {
 #ifdef CONFIG_TCP_MD5SIG
        struct tcp_md5sig_key *(*req_md5_lookup)(const struct sock *sk,
                                                 const struct sock *addr_sk);
-       int             (*calc_md5_hash) (char *location,
+       void            (*calc_md5_hash) (char *location,
                                          const struct tcp_md5sig_key *md5,
                                          const struct sock *sk,
                                          const struct sk_buff *skb);
index 12850a277251dde053b93d8a3664d78e617b882d..b71c22475c515ffc64fd886fab2f748d8ab9b253 100644 (file)
@@ -760,9 +760,7 @@ config TCP_AO
 
 config TCP_MD5SIG
        bool "TCP: MD5 Signature Option support (RFC2385)"
-       select CRYPTO
-       select CRYPTO_MD5
-       select TCP_SIGPOOL
+       select CRYPTO_LIB_MD5
        help
          RFC2385 specifies a method of giving MD5 protection to TCP sessions.
          Its main (only?) use is to protect BGP sessions between core routers
index 4d720aa09a4c67f0f068ad7cbf72b306a65a3ee2..0ccc5405e740842fff2f05ae0458f025cf77501a 100644 (file)
 
 #define pr_fmt(fmt) "TCP: " fmt
 
-#include <crypto/hash.h>
+#include <crypto/md5.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/skbuff.h>
-#include <linux/scatterlist.h>
 #include <linux/splice.h>
 #include <linux/net.h>
 #include <linux/socket.h>
@@ -425,7 +424,6 @@ void tcp_md5_destruct_sock(struct sock *sk)
                tcp_clear_md5_list(sk);
                kfree(rcu_replace_pointer(tp->md5sig_info, NULL, 1));
                static_branch_slow_dec_deferred(&tcp_md5_needed);
-               tcp_md5_release_sigpool();
        }
 }
 EXPORT_IPV6_MOD_GPL(tcp_md5_destruct_sock);
@@ -4838,52 +4836,45 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
 EXPORT_IPV6_MOD(tcp_getsockopt);
 
 #ifdef CONFIG_TCP_MD5SIG
-int tcp_md5_sigpool_id = -1;
-EXPORT_IPV6_MOD_GPL(tcp_md5_sigpool_id);
-
-int tcp_md5_alloc_sigpool(void)
+void tcp_md5_hash_skb_data(struct md5_ctx *ctx, const struct sk_buff *skb,
+                          unsigned int header_len)
 {
-       size_t scratch_size;
-       int ret;
+       const unsigned int head_data_len = skb_headlen(skb) > header_len ?
+                                          skb_headlen(skb) - header_len : 0;
+       const struct skb_shared_info *shi = skb_shinfo(skb);
+       struct sk_buff *frag_iter;
+       unsigned int i;
 
-       scratch_size = sizeof(union tcp_md5sum_block) + sizeof(struct tcphdr);
-       ret = tcp_sigpool_alloc_ahash("md5", scratch_size);
-       if (ret >= 0) {
-               /* As long as any md5 sigpool was allocated, the return
-                * id would stay the same. Re-write the id only for the case
-                * when previously all MD5 keys were deleted and this call
-                * allocates the first MD5 key, which may return a different
-                * sigpool id than was used previously.
-                */
-               WRITE_ONCE(tcp_md5_sigpool_id, ret); /* Avoids the compiler potentially being smart here */
-               return 0;
-       }
-       return ret;
-}
+       md5_update(ctx, (const u8 *)tcp_hdr(skb) + header_len, head_data_len);
 
-void tcp_md5_release_sigpool(void)
-{
-       tcp_sigpool_release(READ_ONCE(tcp_md5_sigpool_id));
-}
+       for (i = 0; i < shi->nr_frags; ++i) {
+               const skb_frag_t *f = &shi->frags[i];
+               u32 p_off, p_len, copied;
+               const void *vaddr;
+               struct page *p;
 
-void tcp_md5_add_sigpool(void)
-{
-       tcp_sigpool_get(READ_ONCE(tcp_md5_sigpool_id));
+               skb_frag_foreach_page(f, skb_frag_off(f), skb_frag_size(f),
+                                     p, p_off, p_len, copied) {
+                       vaddr = kmap_local_page(p);
+                       md5_update(ctx, vaddr + p_off, p_len);
+                       kunmap_local(vaddr);
+               }
+       }
+
+       skb_walk_frags(skb, frag_iter)
+               tcp_md5_hash_skb_data(ctx, frag_iter, 0);
 }
+EXPORT_IPV6_MOD(tcp_md5_hash_skb_data);
 
-int tcp_md5_hash_key(struct tcp_sigpool *hp,
-                    const struct tcp_md5sig_key *key)
+void tcp_md5_hash_key(struct md5_ctx *ctx,
+                     const struct tcp_md5sig_key *key)
 {
        u8 keylen = READ_ONCE(key->keylen); /* paired with WRITE_ONCE() in tcp_md5_do_add */
-       struct scatterlist sg;
-
-       sg_init_one(&sg, key->key, keylen);
-       ahash_request_set_crypt(hp->req, &sg, NULL, keylen);
 
        /* We use data_race() because tcp_md5_do_add() might change
         * key->key under us
         */
-       return data_race(crypto_ahash_update(hp->req));
+       data_race(({ md5_update(ctx, key->key, keylen), 0; }));
 }
 EXPORT_IPV6_MOD(tcp_md5_hash_key);
 
@@ -4902,7 +4893,6 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
        const struct tcp_sock *tp = tcp_sk(sk);
        struct tcp_md5sig_key *key;
        u8 newhash[16];
-       int genhash;
 
        key = tcp_md5_do_lookup(sk, l3index, saddr, family);
 
@@ -4917,11 +4907,10 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
         * IPv4-mapped case.
         */
        if (family == AF_INET)
-               genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
+               tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
        else
-               genhash = tp->af_specific->calc_md5_hash(newhash, key,
-                                                        NULL, skb);
-       if (genhash || memcmp(hash_location, newhash, 16) != 0) {
+               tp->af_specific->calc_md5_hash(newhash, key, NULL, skb);
+       if (memcmp(hash_location, newhash, 16) != 0) {
                NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE);
                trace_tcp_hash_md5_mismatch(sk, skb);
                return SKB_DROP_REASON_TCP_MD5FAILURE;
index b1fcf3e4e1ce001f34907ce183390a7bae9fe48a..40a76da5364a1ccdef521f3d8bb3bcc2c8aff053 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/module.h>
 #include <linux/random.h>
 #include <linux/cache.h>
+#include <linux/fips.h>
 #include <linux/jhash.h>
 #include <linux/init.h>
 #include <linux/times.h>
 #include <linux/btf_ids.h>
 #include <linux/skbuff_ref.h>
 
-#include <crypto/hash.h>
-#include <linux/scatterlist.h>
+#include <crypto/md5.h>
 
 #include <trace/events/tcp.h>
 
 #ifdef CONFIG_TCP_MD5SIG
-static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
-                              __be32 daddr, __be32 saddr, const struct tcphdr *th);
+static void tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
+                               __be32 daddr, __be32 saddr, const struct tcphdr *th);
 #endif
 
 struct inet_hashinfo tcp_hashinfo;
@@ -754,7 +754,6 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb,
        struct tcp_md5sig_key *key = NULL;
        unsigned char newhash[16];
        struct sock *sk1 = NULL;
-       int genhash;
 #endif
        u64 transmit_time = 0;
        struct sock *ctl_sk;
@@ -840,11 +839,9 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb,
                if (!key)
                        goto out;
 
-
-               genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
-               if (genhash || memcmp(md5_hash_location, newhash, 16) != 0)
+               tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
+               if (memcmp(md5_hash_location, newhash, 16) != 0)
                        goto out;
-
        }
 
        if (key) {
@@ -1425,13 +1422,13 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
        struct tcp_sock *tp = tcp_sk(sk);
 
        if (!rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) {
-               if (tcp_md5_alloc_sigpool())
-                       return -ENOMEM;
+               if (fips_enabled) {
+                       pr_warn_once("TCP-MD5 support is disabled due to FIPS\n");
+                       return -EOPNOTSUPP;
+               }
 
-               if (tcp_md5sig_info_add(sk, GFP_KERNEL)) {
-                       tcp_md5_release_sigpool();
+               if (tcp_md5sig_info_add(sk, GFP_KERNEL))
                        return -ENOMEM;
-               }
 
                if (!static_branch_inc(&tcp_md5_needed.key)) {
                        struct tcp_md5sig_info *md5sig;
@@ -1439,7 +1436,6 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
                        md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk));
                        rcu_assign_pointer(tp->md5sig_info, NULL);
                        kfree_rcu(md5sig, rcu);
-                       tcp_md5_release_sigpool();
                        return -EUSERS;
                }
        }
@@ -1456,12 +1452,9 @@ int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr,
        struct tcp_sock *tp = tcp_sk(sk);
 
        if (!rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) {
-               tcp_md5_add_sigpool();
 
-               if (tcp_md5sig_info_add(sk, sk_gfp_mask(sk, GFP_ATOMIC))) {
-                       tcp_md5_release_sigpool();
+               if (tcp_md5sig_info_add(sk, sk_gfp_mask(sk, GFP_ATOMIC)))
                        return -ENOMEM;
-               }
 
                if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key)) {
                        struct tcp_md5sig_info *md5sig;
@@ -1470,7 +1463,6 @@ int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr,
                        net_warn_ratelimited("Too many TCP-MD5 keys in the system\n");
                        rcu_assign_pointer(tp->md5sig_info, NULL);
                        kfree_rcu(md5sig, rcu);
-                       tcp_md5_release_sigpool();
                        return -EUSERS;
                }
        }
@@ -1578,66 +1570,44 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,
                              cmd.tcpm_key, cmd.tcpm_keylen);
 }
 
-static int tcp_v4_md5_hash_headers(struct tcp_sigpool *hp,
-                                  __be32 daddr, __be32 saddr,
-                                  const struct tcphdr *th, int nbytes)
+static void tcp_v4_md5_hash_headers(struct md5_ctx *ctx,
+                                   __be32 daddr, __be32 saddr,
+                                   const struct tcphdr *th, int nbytes)
 {
-       struct tcp4_pseudohdr *bp;
-       struct scatterlist sg;
-       struct tcphdr *_th;
-
-       bp = hp->scratch;
-       bp->saddr = saddr;
-       bp->daddr = daddr;
-       bp->pad = 0;
-       bp->protocol = IPPROTO_TCP;
-       bp->len = cpu_to_be16(nbytes);
-
-       _th = (struct tcphdr *)(bp + 1);
-       memcpy(_th, th, sizeof(*th));
-       _th->check = 0;
+       struct {
+               struct tcp4_pseudohdr ip;
+               struct tcphdr tcp;
+       } h;
 
-       sg_init_one(&sg, bp, sizeof(*bp) + sizeof(*th));
-       ahash_request_set_crypt(hp->req, &sg, NULL,
-                               sizeof(*bp) + sizeof(*th));
-       return crypto_ahash_update(hp->req);
+       h.ip.saddr = saddr;
+       h.ip.daddr = daddr;
+       h.ip.pad = 0;
+       h.ip.protocol = IPPROTO_TCP;
+       h.ip.len = cpu_to_be16(nbytes);
+       h.tcp = *th;
+       h.tcp.check = 0;
+       md5_update(ctx, (const u8 *)&h, sizeof(h.ip) + sizeof(h.tcp));
 }
 
-static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
-                              __be32 daddr, __be32 saddr, const struct tcphdr *th)
+static noinline_for_stack void
+tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
+                   __be32 daddr, __be32 saddr, const struct tcphdr *th)
 {
-       struct tcp_sigpool hp;
+       struct md5_ctx ctx;
 
-       if (tcp_sigpool_start(tcp_md5_sigpool_id, &hp))
-               goto clear_hash_nostart;
-
-       if (crypto_ahash_init(hp.req))
-               goto clear_hash;
-       if (tcp_v4_md5_hash_headers(&hp, daddr, saddr, th, th->doff << 2))
-               goto clear_hash;
-       if (tcp_md5_hash_key(&hp, key))
-               goto clear_hash;
-       ahash_request_set_crypt(hp.req, NULL, md5_hash, 0);
-       if (crypto_ahash_final(hp.req))
-               goto clear_hash;
-
-       tcp_sigpool_end(&hp);
-       return 0;
-
-clear_hash:
-       tcp_sigpool_end(&hp);
-clear_hash_nostart:
-       memset(md5_hash, 0, 16);
-       return 1;
+       md5_init(&ctx);
+       tcp_v4_md5_hash_headers(&ctx, daddr, saddr, th, th->doff << 2);
+       tcp_md5_hash_key(&ctx, key);
+       md5_final(&ctx, md5_hash);
 }
 
-int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
-                       const struct sock *sk,
-                       const struct sk_buff *skb)
+noinline_for_stack void
+tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
+                   const struct sock *sk, const struct sk_buff *skb)
 {
        const struct tcphdr *th = tcp_hdr(skb);
-       struct tcp_sigpool hp;
        __be32 saddr, daddr;
+       struct md5_ctx ctx;
 
        if (sk) { /* valid for establish/request sockets */
                saddr = sk->sk_rcv_saddr;
@@ -1648,30 +1618,11 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
                daddr = iph->daddr;
        }
 
-       if (tcp_sigpool_start(tcp_md5_sigpool_id, &hp))
-               goto clear_hash_nostart;
-
-       if (crypto_ahash_init(hp.req))
-               goto clear_hash;
-
-       if (tcp_v4_md5_hash_headers(&hp, daddr, saddr, th, skb->len))
-               goto clear_hash;
-       if (tcp_sigpool_hash_skb_data(&hp, skb, th->doff << 2))
-               goto clear_hash;
-       if (tcp_md5_hash_key(&hp, key))
-               goto clear_hash;
-       ahash_request_set_crypt(hp.req, NULL, md5_hash, 0);
-       if (crypto_ahash_final(hp.req))
-               goto clear_hash;
-
-       tcp_sigpool_end(&hp);
-       return 0;
-
-clear_hash:
-       tcp_sigpool_end(&hp);
-clear_hash_nostart:
-       memset(md5_hash, 0, 16);
-       return 1;
+       md5_init(&ctx);
+       tcp_v4_md5_hash_headers(&ctx, daddr, saddr, th, skb->len);
+       tcp_md5_hash_skb_data(&ctx, skb, th->doff << 2);
+       tcp_md5_hash_key(&ctx, key);
+       md5_final(&ctx, md5_hash);
 }
 EXPORT_IPV6_MOD(tcp_v4_md5_hash_skb);
 
index 2ec8c6f1cdccc0e372ee92c9e47049364b77b6d7..ded2cf1f600672bcae30f46b52cb3e61bdf70d90 100644 (file)
@@ -312,7 +312,6 @@ static void tcp_time_wait_init(struct sock *sk, struct tcp_timewait_sock *tcptw)
                        return;
                if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key))
                        goto out_free;
-               tcp_md5_add_sigpool();
        }
        return;
 out_free:
@@ -406,7 +405,6 @@ void tcp_twsk_destructor(struct sock *sk)
                if (twsk->tw_md5_key) {
                        kfree(twsk->tw_md5_key);
                        static_branch_slow_dec_deferred(&tcp_md5_needed);
-                       tcp_md5_release_sigpool();
                }
        }
 #endif
index 6197dd4e6261cbf8a38165e4586b3e6600796321..06eb90e4078e563bb4c7b5ed95980d70c643414a 100644 (file)
@@ -67,8 +67,7 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 
-#include <crypto/hash.h>
-#include <linux/scatterlist.h>
+#include <crypto/md5.h>
 
 #include <trace/events/tcp.h>
 
@@ -691,69 +690,45 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,
                              cmd.tcpm_key, cmd.tcpm_keylen);
 }
 
-static int tcp_v6_md5_hash_headers(struct tcp_sigpool *hp,
-                                  const struct in6_addr *daddr,
-                                  const struct in6_addr *saddr,
-                                  const struct tcphdr *th, int nbytes)
+static void tcp_v6_md5_hash_headers(struct md5_ctx *ctx,
+                                   const struct in6_addr *daddr,
+                                   const struct in6_addr *saddr,
+                                   const struct tcphdr *th, int nbytes)
 {
-       struct tcp6_pseudohdr *bp;
-       struct scatterlist sg;
-       struct tcphdr *_th;
-
-       bp = hp->scratch;
-       /* 1. TCP pseudo-header (RFC2460) */
-       bp->saddr = *saddr;
-       bp->daddr = *daddr;
-       bp->protocol = cpu_to_be32(IPPROTO_TCP);
-       bp->len = cpu_to_be32(nbytes);
-
-       _th = (struct tcphdr *)(bp + 1);
-       memcpy(_th, th, sizeof(*th));
-       _th->check = 0;
-
-       sg_init_one(&sg, bp, sizeof(*bp) + sizeof(*th));
-       ahash_request_set_crypt(hp->req, &sg, NULL,
-                               sizeof(*bp) + sizeof(*th));
-       return crypto_ahash_update(hp->req);
+       struct {
+               struct tcp6_pseudohdr ip; /* TCP pseudo-header (RFC2460) */
+               struct tcphdr tcp;
+       } h;
+
+       h.ip.saddr = *saddr;
+       h.ip.daddr = *daddr;
+       h.ip.protocol = cpu_to_be32(IPPROTO_TCP);
+       h.ip.len = cpu_to_be32(nbytes);
+       h.tcp = *th;
+       h.tcp.check = 0;
+       md5_update(ctx, (const u8 *)&h, sizeof(h.ip) + sizeof(h.tcp));
 }
 
-static int tcp_v6_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
-                              const struct in6_addr *daddr, struct in6_addr *saddr,
-                              const struct tcphdr *th)
+static noinline_for_stack void
+tcp_v6_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
+                   const struct in6_addr *daddr, struct in6_addr *saddr,
+                   const struct tcphdr *th)
 {
-       struct tcp_sigpool hp;
-
-       if (tcp_sigpool_start(tcp_md5_sigpool_id, &hp))
-               goto clear_hash_nostart;
-
-       if (crypto_ahash_init(hp.req))
-               goto clear_hash;
-       if (tcp_v6_md5_hash_headers(&hp, daddr, saddr, th, th->doff << 2))
-               goto clear_hash;
-       if (tcp_md5_hash_key(&hp, key))
-               goto clear_hash;
-       ahash_request_set_crypt(hp.req, NULL, md5_hash, 0);
-       if (crypto_ahash_final(hp.req))
-               goto clear_hash;
-
-       tcp_sigpool_end(&hp);
-       return 0;
+       struct md5_ctx ctx;
 
-clear_hash:
-       tcp_sigpool_end(&hp);
-clear_hash_nostart:
-       memset(md5_hash, 0, 16);
-       return 1;
+       md5_init(&ctx);
+       tcp_v6_md5_hash_headers(&ctx, daddr, saddr, th, th->doff << 2);
+       tcp_md5_hash_key(&ctx, key);
+       md5_final(&ctx, md5_hash);
 }
 
-static int tcp_v6_md5_hash_skb(char *md5_hash,
-                              const struct tcp_md5sig_key *key,
-                              const struct sock *sk,
-                              const struct sk_buff *skb)
+static noinline_for_stack void
+tcp_v6_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
+                   const struct sock *sk, const struct sk_buff *skb)
 {
        const struct tcphdr *th = tcp_hdr(skb);
        const struct in6_addr *saddr, *daddr;
-       struct tcp_sigpool hp;
+       struct md5_ctx ctx;
 
        if (sk) { /* valid for establish/request sockets */
                saddr = &sk->sk_v6_rcv_saddr;
@@ -764,30 +739,11 @@ static int tcp_v6_md5_hash_skb(char *md5_hash,
                daddr = &ip6h->daddr;
        }
 
-       if (tcp_sigpool_start(tcp_md5_sigpool_id, &hp))
-               goto clear_hash_nostart;
-
-       if (crypto_ahash_init(hp.req))
-               goto clear_hash;
-
-       if (tcp_v6_md5_hash_headers(&hp, daddr, saddr, th, skb->len))
-               goto clear_hash;
-       if (tcp_sigpool_hash_skb_data(&hp, skb, th->doff << 2))
-               goto clear_hash;
-       if (tcp_md5_hash_key(&hp, key))
-               goto clear_hash;
-       ahash_request_set_crypt(hp.req, NULL, md5_hash, 0);
-       if (crypto_ahash_final(hp.req))
-               goto clear_hash;
-
-       tcp_sigpool_end(&hp);
-       return 0;
-
-clear_hash:
-       tcp_sigpool_end(&hp);
-clear_hash_nostart:
-       memset(md5_hash, 0, 16);
-       return 1;
+       md5_init(&ctx);
+       tcp_v6_md5_hash_headers(&ctx, daddr, saddr, th, skb->len);
+       tcp_md5_hash_skb_data(&ctx, skb, th->doff << 2);
+       tcp_md5_hash_key(&ctx, key);
+       md5_final(&ctx, md5_hash);
 }
 #endif
 
@@ -1032,7 +988,6 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb,
        int oif = 0;
 #ifdef CONFIG_TCP_MD5SIG
        unsigned char newhash[16];
-       int genhash;
        struct sock *sk1 = NULL;
 #endif
 
@@ -1091,8 +1046,8 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb,
                        goto out;
                key.type = TCP_KEY_MD5;
 
-               genhash = tcp_v6_md5_hash_skb(newhash, key.md5_key, NULL, skb);
-               if (genhash || memcmp(md5_hash_location, newhash, 16) != 0)
+               tcp_v6_md5_hash_skb(newhash, key.md5_key, NULL, skb);
+               if (memcmp(md5_hash_location, newhash, 16) != 0)
                        goto out;
        }
 #endif