Skip to content

Commit 37a183d

Browse files
Eric Biggerskuba-moo
authored andcommitted
tcp: Convert tcp-md5 to use MD5 library instead of crypto_ahash
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 <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent f578ff4 commit 37a183d

File tree

6 files changed

+121
-240
lines changed

6 files changed

+121
-240
lines changed

include/net/tcp.h

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1898,13 +1898,6 @@ struct tcp6_pseudohdr {
18981898
__be32 protocol; /* including padding */
18991899
};
19001900

1901-
union tcp_md5sum_block {
1902-
struct tcp4_pseudohdr ip4;
1903-
#if IS_ENABLED(CONFIG_IPV6)
1904-
struct tcp6_pseudohdr ip6;
1905-
#endif
1906-
};
1907-
19081901
/*
19091902
* struct tcp_sigpool - per-CPU pool of ahash_requests
19101903
* @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);
19391932
void tcp_sigpool_end(struct tcp_sigpool *c);
19401933
size_t tcp_sigpool_algo(unsigned int id, char *buf, size_t buf_len);
19411934
/* - functions */
1942-
int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
1943-
const struct sock *sk, const struct sk_buff *skb);
1935+
void tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
1936+
const struct sock *sk, const struct sk_buff *skb);
19441937
int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
19451938
int family, u8 prefixlen, int l3index, u8 flags,
19461939
const u8 *newkey, u8 newkeylen);
@@ -1999,13 +1992,10 @@ static inline void tcp_md5_destruct_sock(struct sock *sk)
19991992
}
20001993
#endif
20011994

2002-
int tcp_md5_alloc_sigpool(void);
2003-
void tcp_md5_release_sigpool(void);
2004-
void tcp_md5_add_sigpool(void);
2005-
extern int tcp_md5_sigpool_id;
2006-
2007-
int tcp_md5_hash_key(struct tcp_sigpool *hp,
2008-
const struct tcp_md5sig_key *key);
1995+
struct md5_ctx;
1996+
void tcp_md5_hash_skb_data(struct md5_ctx *ctx, const struct sk_buff *skb,
1997+
unsigned int header_len);
1998+
void tcp_md5_hash_key(struct md5_ctx *ctx, const struct tcp_md5sig_key *key);
20091999

20102000
/* From tcp_fastopen.c */
20112001
void tcp_fastopen_cache_get(struct sock *sk, u16 *mss,
@@ -2355,7 +2345,7 @@ struct tcp_sock_af_ops {
23552345
#ifdef CONFIG_TCP_MD5SIG
23562346
struct tcp_md5sig_key *(*md5_lookup) (const struct sock *sk,
23572347
const struct sock *addr_sk);
2358-
int (*calc_md5_hash)(char *location,
2348+
void (*calc_md5_hash)(char *location,
23592349
const struct tcp_md5sig_key *md5,
23602350
const struct sock *sk,
23612351
const struct sk_buff *skb);
@@ -2383,7 +2373,7 @@ struct tcp_request_sock_ops {
23832373
#ifdef CONFIG_TCP_MD5SIG
23842374
struct tcp_md5sig_key *(*req_md5_lookup)(const struct sock *sk,
23852375
const struct sock *addr_sk);
2386-
int (*calc_md5_hash) (char *location,
2376+
void (*calc_md5_hash) (char *location,
23872377
const struct tcp_md5sig_key *md5,
23882378
const struct sock *sk,
23892379
const struct sk_buff *skb);

net/ipv4/Kconfig

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -760,9 +760,7 @@ config TCP_AO
760760

761761
config TCP_MD5SIG
762762
bool "TCP: MD5 Signature Option support (RFC2385)"
763-
select CRYPTO
764-
select CRYPTO_MD5
765-
select TCP_SIGPOOL
763+
select CRYPTO_LIB_MD5
766764
help
767765
RFC2385 specifies a method of giving MD5 protection to TCP sessions.
768766
Its main (only?) use is to protect BGP sessions between core routers

net/ipv4/tcp.c

Lines changed: 31 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@
243243

244244
#define pr_fmt(fmt) "TCP: " fmt
245245

246-
#include <crypto/hash.h>
246+
#include <crypto/md5.h>
247247
#include <linux/kernel.h>
248248
#include <linux/module.h>
249249
#include <linux/types.h>
@@ -253,7 +253,6 @@
253253
#include <linux/init.h>
254254
#include <linux/fs.h>
255255
#include <linux/skbuff.h>
256-
#include <linux/scatterlist.h>
257256
#include <linux/splice.h>
258257
#include <linux/net.h>
259258
#include <linux/socket.h>
@@ -425,7 +424,6 @@ void tcp_md5_destruct_sock(struct sock *sk)
425424
tcp_clear_md5_list(sk);
426425
kfree(rcu_replace_pointer(tp->md5sig_info, NULL, 1));
427426
static_branch_slow_dec_deferred(&tcp_md5_needed);
428-
tcp_md5_release_sigpool();
429427
}
430428
}
431429
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,
48384836
EXPORT_IPV6_MOD(tcp_getsockopt);
48394837

48404838
#ifdef CONFIG_TCP_MD5SIG
4841-
int tcp_md5_sigpool_id = -1;
4842-
EXPORT_IPV6_MOD_GPL(tcp_md5_sigpool_id);
4843-
4844-
int tcp_md5_alloc_sigpool(void)
4839+
void tcp_md5_hash_skb_data(struct md5_ctx *ctx, const struct sk_buff *skb,
4840+
unsigned int header_len)
48454841
{
4846-
size_t scratch_size;
4847-
int ret;
4842+
const unsigned int head_data_len = skb_headlen(skb) > header_len ?
4843+
skb_headlen(skb) - header_len : 0;
4844+
const struct skb_shared_info *shi = skb_shinfo(skb);
4845+
struct sk_buff *frag_iter;
4846+
unsigned int i;
48484847

4849-
scratch_size = sizeof(union tcp_md5sum_block) + sizeof(struct tcphdr);
4850-
ret = tcp_sigpool_alloc_ahash("md5", scratch_size);
4851-
if (ret >= 0) {
4852-
/* As long as any md5 sigpool was allocated, the return
4853-
* id would stay the same. Re-write the id only for the case
4854-
* when previously all MD5 keys were deleted and this call
4855-
* allocates the first MD5 key, which may return a different
4856-
* sigpool id than was used previously.
4857-
*/
4858-
WRITE_ONCE(tcp_md5_sigpool_id, ret); /* Avoids the compiler potentially being smart here */
4859-
return 0;
4860-
}
4861-
return ret;
4862-
}
4848+
md5_update(ctx, (const u8 *)tcp_hdr(skb) + header_len, head_data_len);
48634849

4864-
void tcp_md5_release_sigpool(void)
4865-
{
4866-
tcp_sigpool_release(READ_ONCE(tcp_md5_sigpool_id));
4867-
}
4850+
for (i = 0; i < shi->nr_frags; ++i) {
4851+
const skb_frag_t *f = &shi->frags[i];
4852+
u32 p_off, p_len, copied;
4853+
const void *vaddr;
4854+
struct page *p;
48684855

4869-
void tcp_md5_add_sigpool(void)
4870-
{
4871-
tcp_sigpool_get(READ_ONCE(tcp_md5_sigpool_id));
4856+
skb_frag_foreach_page(f, skb_frag_off(f), skb_frag_size(f),
4857+
p, p_off, p_len, copied) {
4858+
vaddr = kmap_local_page(p);
4859+
md5_update(ctx, vaddr + p_off, p_len);
4860+
kunmap_local(vaddr);
4861+
}
4862+
}
4863+
4864+
skb_walk_frags(skb, frag_iter)
4865+
tcp_md5_hash_skb_data(ctx, frag_iter, 0);
48724866
}
4867+
EXPORT_IPV6_MOD(tcp_md5_hash_skb_data);
48734868

4874-
int tcp_md5_hash_key(struct tcp_sigpool *hp,
4875-
const struct tcp_md5sig_key *key)
4869+
void tcp_md5_hash_key(struct md5_ctx *ctx,
4870+
const struct tcp_md5sig_key *key)
48764871
{
48774872
u8 keylen = READ_ONCE(key->keylen); /* paired with WRITE_ONCE() in tcp_md5_do_add */
4878-
struct scatterlist sg;
4879-
4880-
sg_init_one(&sg, key->key, keylen);
4881-
ahash_request_set_crypt(hp->req, &sg, NULL, keylen);
48824873

48834874
/* We use data_race() because tcp_md5_do_add() might change
48844875
* key->key under us
48854876
*/
4886-
return data_race(crypto_ahash_update(hp->req));
4877+
data_race(({ md5_update(ctx, key->key, keylen), 0; }));
48874878
}
48884879
EXPORT_IPV6_MOD(tcp_md5_hash_key);
48894880

@@ -4902,7 +4893,6 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
49024893
const struct tcp_sock *tp = tcp_sk(sk);
49034894
struct tcp_md5sig_key *key;
49044895
u8 newhash[16];
4905-
int genhash;
49064896

49074897
key = tcp_md5_do_lookup(sk, l3index, saddr, family);
49084898

@@ -4917,11 +4907,10 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
49174907
* IPv4-mapped case.
49184908
*/
49194909
if (family == AF_INET)
4920-
genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
4910+
tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
49214911
else
4922-
genhash = tp->af_specific->calc_md5_hash(newhash, key,
4923-
NULL, skb);
4924-
if (genhash || memcmp(hash_location, newhash, 16) != 0) {
4912+
tp->af_specific->calc_md5_hash(newhash, key, NULL, skb);
4913+
if (memcmp(hash_location, newhash, 16) != 0) {
49254914
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE);
49264915
trace_tcp_hash_md5_mismatch(sk, skb);
49274916
return SKB_DROP_REASON_TCP_MD5FAILURE;

0 commit comments

Comments
 (0)