Skip to content

Commit 459837b

Browse files
0x7f454c46kuba-moo
authored andcommitted
net/tcp: Disable TCP-MD5 static key on tcp_md5sig_info destruction
To do that, separate two scenarios: - where it's the first MD5 key on the system, which means that enabling of the static key may need to sleep; - copying of an existing key from a listening socket to the request socket upon receiving a signed TCP segment, where static key was already enabled (when the key was added to the listening socket). Now the life-time of the static branch for TCP-MD5 is until: - last tcp_md5sig_info is destroyed - last socket in time-wait state with MD5 key is closed. Which means that after all sockets with TCP-MD5 keys are gone, the system gets back the performance of disabled md5-key static branch. While at here, provide static_key_fast_inc() helper that does ref counter increment in atomic fashion (without grabbing cpus_read_lock() on CONFIG_JUMP_LABEL=y). This is needed to add a new user for a static_key when the caller controls the lifetime of another user. Signed-off-by: Dmitry Safonov <[email protected]> Acked-by: Jakub Kicinski <[email protected]> Reviewed-by: Eric Dumazet <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]>
1 parent f62c751 commit 459837b

File tree

6 files changed

+84
-32
lines changed

6 files changed

+84
-32
lines changed

include/net/tcp.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,23 +1675,27 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
16751675
const struct sock *sk, const struct sk_buff *skb);
16761676
int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
16771677
int family, u8 prefixlen, int l3index, u8 flags,
1678-
const u8 *newkey, u8 newkeylen, gfp_t gfp);
1678+
const u8 *newkey, u8 newkeylen);
1679+
int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr,
1680+
int family, u8 prefixlen, int l3index,
1681+
struct tcp_md5sig_key *key);
1682+
16791683
int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr,
16801684
int family, u8 prefixlen, int l3index, u8 flags);
16811685
struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
16821686
const struct sock *addr_sk);
16831687

16841688
#ifdef CONFIG_TCP_MD5SIG
16851689
#include <linux/jump_label.h>
1686-
extern struct static_key_false tcp_md5_needed;
1690+
extern struct static_key_false_deferred tcp_md5_needed;
16871691
struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,
16881692
const union tcp_md5_addr *addr,
16891693
int family);
16901694
static inline struct tcp_md5sig_key *
16911695
tcp_md5_do_lookup(const struct sock *sk, int l3index,
16921696
const union tcp_md5_addr *addr, int family)
16931697
{
1694-
if (!static_branch_unlikely(&tcp_md5_needed))
1698+
if (!static_branch_unlikely(&tcp_md5_needed.key))
16951699
return NULL;
16961700
return __tcp_md5_do_lookup(sk, l3index, addr, family);
16971701
}

net/ipv4/tcp.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4464,11 +4464,8 @@ bool tcp_alloc_md5sig_pool(void)
44644464
if (unlikely(!READ_ONCE(tcp_md5sig_pool_populated))) {
44654465
mutex_lock(&tcp_md5sig_mutex);
44664466

4467-
if (!tcp_md5sig_pool_populated) {
4467+
if (!tcp_md5sig_pool_populated)
44684468
__tcp_alloc_md5sig_pool();
4469-
if (tcp_md5sig_pool_populated)
4470-
static_branch_inc(&tcp_md5_needed);
4471-
}
44724469

44734470
mutex_unlock(&tcp_md5sig_mutex);
44744471
}

net/ipv4/tcp_ipv4.c

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,7 +1053,7 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req)
10531053
* We need to maintain these in the sk structure.
10541054
*/
10551055

1056-
DEFINE_STATIC_KEY_FALSE(tcp_md5_needed);
1056+
DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_md5_needed, HZ);
10571057
EXPORT_SYMBOL(tcp_md5_needed);
10581058

10591059
static bool better_md5_match(struct tcp_md5sig_key *old, struct tcp_md5sig_key *new)
@@ -1166,9 +1166,6 @@ static int tcp_md5sig_info_add(struct sock *sk, gfp_t gfp)
11661166
struct tcp_sock *tp = tcp_sk(sk);
11671167
struct tcp_md5sig_info *md5sig;
11681168

1169-
if (rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk)))
1170-
return 0;
1171-
11721169
md5sig = kmalloc(sizeof(*md5sig), gfp);
11731170
if (!md5sig)
11741171
return -ENOMEM;
@@ -1180,9 +1177,9 @@ static int tcp_md5sig_info_add(struct sock *sk, gfp_t gfp)
11801177
}
11811178

11821179
/* This can be called on a newly created socket, from other files */
1183-
int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
1184-
int family, u8 prefixlen, int l3index, u8 flags,
1185-
const u8 *newkey, u8 newkeylen, gfp_t gfp)
1180+
static int __tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
1181+
int family, u8 prefixlen, int l3index, u8 flags,
1182+
const u8 *newkey, u8 newkeylen, gfp_t gfp)
11861183
{
11871184
/* Add Key to the list */
11881185
struct tcp_md5sig_key *key;
@@ -1209,9 +1206,6 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
12091206
return 0;
12101207
}
12111208

1212-
if (tcp_md5sig_info_add(sk, gfp))
1213-
return -ENOMEM;
1214-
12151209
md5sig = rcu_dereference_protected(tp->md5sig_info,
12161210
lockdep_sock_is_held(sk));
12171211

@@ -1235,8 +1229,59 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
12351229
hlist_add_head_rcu(&key->node, &md5sig->head);
12361230
return 0;
12371231
}
1232+
1233+
int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
1234+
int family, u8 prefixlen, int l3index, u8 flags,
1235+
const u8 *newkey, u8 newkeylen)
1236+
{
1237+
struct tcp_sock *tp = tcp_sk(sk);
1238+
1239+
if (!rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) {
1240+
if (tcp_md5sig_info_add(sk, GFP_KERNEL))
1241+
return -ENOMEM;
1242+
1243+
if (!static_branch_inc(&tcp_md5_needed.key)) {
1244+
struct tcp_md5sig_info *md5sig;
1245+
1246+
md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk));
1247+
rcu_assign_pointer(tp->md5sig_info, NULL);
1248+
kfree_rcu(md5sig);
1249+
return -EUSERS;
1250+
}
1251+
}
1252+
1253+
return __tcp_md5_do_add(sk, addr, family, prefixlen, l3index, flags,
1254+
newkey, newkeylen, GFP_KERNEL);
1255+
}
12381256
EXPORT_SYMBOL(tcp_md5_do_add);
12391257

1258+
int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr,
1259+
int family, u8 prefixlen, int l3index,
1260+
struct tcp_md5sig_key *key)
1261+
{
1262+
struct tcp_sock *tp = tcp_sk(sk);
1263+
1264+
if (!rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) {
1265+
if (tcp_md5sig_info_add(sk, sk_gfp_mask(sk, GFP_ATOMIC)))
1266+
return -ENOMEM;
1267+
1268+
if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key)) {
1269+
struct tcp_md5sig_info *md5sig;
1270+
1271+
md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk));
1272+
net_warn_ratelimited("Too many TCP-MD5 keys in the system\n");
1273+
rcu_assign_pointer(tp->md5sig_info, NULL);
1274+
kfree_rcu(md5sig);
1275+
return -EUSERS;
1276+
}
1277+
}
1278+
1279+
return __tcp_md5_do_add(sk, addr, family, prefixlen, l3index,
1280+
key->flags, key->key, key->keylen,
1281+
sk_gfp_mask(sk, GFP_ATOMIC));
1282+
}
1283+
EXPORT_SYMBOL(tcp_md5_key_copy);
1284+
12401285
int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family,
12411286
u8 prefixlen, int l3index, u8 flags)
12421287
{
@@ -1323,7 +1368,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,
13231368
return -EINVAL;
13241369

13251370
return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags,
1326-
cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
1371+
cmd.tcpm_key, cmd.tcpm_keylen);
13271372
}
13281373

13291374
static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp,
@@ -1580,8 +1625,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
15801625
* memory, then we end up not copying the key
15811626
* across. Shucks.
15821627
*/
1583-
tcp_md5_do_add(newsk, addr, AF_INET, 32, l3index, key->flags,
1584-
key->key, key->keylen, GFP_ATOMIC);
1628+
tcp_md5_key_copy(newsk, addr, AF_INET, 32, l3index, key);
15851629
sk_gso_disable(newsk);
15861630
}
15871631
#endif
@@ -2273,6 +2317,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
22732317
tcp_clear_md5_list(sk);
22742318
kfree_rcu(rcu_dereference_protected(tp->md5sig_info, 1), rcu);
22752319
tp->md5sig_info = NULL;
2320+
static_branch_slow_dec_deferred(&tcp_md5_needed);
22762321
}
22772322
#endif
22782323

net/ipv4/tcp_minisocks.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -291,13 +291,19 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
291291
*/
292292
do {
293293
tcptw->tw_md5_key = NULL;
294-
if (static_branch_unlikely(&tcp_md5_needed)) {
294+
if (static_branch_unlikely(&tcp_md5_needed.key)) {
295295
struct tcp_md5sig_key *key;
296296

297297
key = tp->af_specific->md5_lookup(sk, sk);
298298
if (key) {
299299
tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC);
300-
BUG_ON(tcptw->tw_md5_key && !tcp_alloc_md5sig_pool());
300+
if (!tcptw->tw_md5_key)
301+
break;
302+
BUG_ON(!tcp_alloc_md5sig_pool());
303+
if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key)) {
304+
kfree(tcptw->tw_md5_key);
305+
tcptw->tw_md5_key = NULL;
306+
}
301307
}
302308
}
303309
} while (0);
@@ -337,11 +343,13 @@ EXPORT_SYMBOL(tcp_time_wait);
337343
void tcp_twsk_destructor(struct sock *sk)
338344
{
339345
#ifdef CONFIG_TCP_MD5SIG
340-
if (static_branch_unlikely(&tcp_md5_needed)) {
346+
if (static_branch_unlikely(&tcp_md5_needed.key)) {
341347
struct tcp_timewait_sock *twsk = tcp_twsk(sk);
342348

343-
if (twsk->tw_md5_key)
349+
if (twsk->tw_md5_key) {
344350
kfree_rcu(twsk->tw_md5_key, rcu);
351+
static_branch_slow_dec_deferred(&tcp_md5_needed);
352+
}
345353
}
346354
#endif
347355
}

net/ipv4/tcp_output.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
766766

767767
*md5 = NULL;
768768
#ifdef CONFIG_TCP_MD5SIG
769-
if (static_branch_unlikely(&tcp_md5_needed) &&
769+
if (static_branch_unlikely(&tcp_md5_needed.key) &&
770770
rcu_access_pointer(tp->md5sig_info)) {
771771
*md5 = tp->af_specific->md5_lookup(sk, sk);
772772
if (*md5) {
@@ -922,7 +922,7 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
922922

923923
*md5 = NULL;
924924
#ifdef CONFIG_TCP_MD5SIG
925-
if (static_branch_unlikely(&tcp_md5_needed) &&
925+
if (static_branch_unlikely(&tcp_md5_needed.key) &&
926926
rcu_access_pointer(tp->md5sig_info)) {
927927
*md5 = tp->af_specific->md5_lookup(sk, sk);
928928
if (*md5) {

net/ipv6/tcp_ipv6.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -665,12 +665,11 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,
665665
if (ipv6_addr_v4mapped(&sin6->sin6_addr))
666666
return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
667667
AF_INET, prefixlen, l3index, flags,
668-
cmd.tcpm_key, cmd.tcpm_keylen,
669-
GFP_KERNEL);
668+
cmd.tcpm_key, cmd.tcpm_keylen);
670669

671670
return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr,
672671
AF_INET6, prefixlen, l3index, flags,
673-
cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
672+
cmd.tcpm_key, cmd.tcpm_keylen);
674673
}
675674

676675
static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp,
@@ -1370,9 +1369,8 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
13701369
* memory, then we end up not copying the key
13711370
* across. Shucks.
13721371
*/
1373-
tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr,
1374-
AF_INET6, 128, l3index, key->flags, key->key, key->keylen,
1375-
sk_gfp_mask(sk, GFP_ATOMIC));
1372+
tcp_md5_key_copy(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr,
1373+
AF_INET6, 128, l3index, key);
13761374
}
13771375
#endif
13781376

0 commit comments

Comments
 (0)