net/tcp: Add TCP-AO sign to twsk

Add support for sockets in time-wait state.
ao_info as well as all keys are inherited on transition to time-wait
socket. The lifetime of ao_info is now protected by ref counter, so
that tcp_ao_destroy_sock() will destruct it only when the last user is
gone.

Co-developed-by: Francesco Ruggeri <fruggeri@arista.com>
Signed-off-by: Francesco Ruggeri <fruggeri@arista.com>
Co-developed-by: Salam Noureddine <noureddine@arista.com>
Signed-off-by: Salam Noureddine <noureddine@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
Acked-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Dmitry Safonov
2023-10-23 20:22:01 +01:00
committed by David S. Miller
parent ba7783ad45
commit decde2586b
7 changed files with 183 additions and 50 deletions

View File

@ -159,6 +159,7 @@ static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags)
if (!ao)
return NULL;
INIT_HLIST_HEAD(&ao->head);
refcount_set(&ao->refcnt, 1);
return ao;
}
@ -176,27 +177,54 @@ static void tcp_ao_key_free_rcu(struct rcu_head *head)
kfree_sensitive(key);
}
void tcp_ao_destroy_sock(struct sock *sk)
void tcp_ao_destroy_sock(struct sock *sk, bool twsk)
{
struct tcp_ao_info *ao;
struct tcp_ao_key *key;
struct hlist_node *n;
ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1);
tcp_sk(sk)->ao_info = NULL;
if (twsk) {
ao = rcu_dereference_protected(tcp_twsk(sk)->ao_info, 1);
tcp_twsk(sk)->ao_info = NULL;
} else {
ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1);
tcp_sk(sk)->ao_info = NULL;
}
if (!ao)
if (!ao || !refcount_dec_and_test(&ao->refcnt))
return;
hlist_for_each_entry_safe(key, n, &ao->head, node) {
hlist_del_rcu(&key->node);
atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
if (!twsk)
atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
call_rcu(&key->rcu, tcp_ao_key_free_rcu);
}
kfree_rcu(ao, rcu);
}
void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp)
{
struct tcp_ao_info *ao_info = rcu_dereference_protected(tp->ao_info, 1);
if (ao_info) {
struct tcp_ao_key *key;
struct hlist_node *n;
int omem = 0;
hlist_for_each_entry_safe(key, n, &ao_info->head, node) {
omem += tcp_ao_sizeof_key(key);
}
refcount_inc(&ao_info->refcnt);
atomic_sub(omem, &(((struct sock *)tp)->sk_omem_alloc));
rcu_assign_pointer(tcptw->ao_info, ao_info);
} else {
tcptw->ao_info = NULL;
}
}
/* 4 tuple and ISNs are expected in NBO */
static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
__be32 saddr, __be32 daddr,
@ -514,11 +542,13 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
if (!sk)
return -ENOTCONN;
if ((1 << sk->sk_state) &
(TCPF_LISTEN | TCPF_NEW_SYN_RECV | TCPF_TIME_WAIT))
if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) {
return -1;
ao_info = rcu_dereference(tcp_sk(sk)->ao_info);
if (sk->sk_state == TCP_TIME_WAIT)
ao_info = rcu_dereference(tcp_twsk(sk)->ao_info);
else
ao_info = rcu_dereference(tcp_sk(sk)->ao_info);
if (!ao_info)
return -ENOENT;
@ -910,6 +940,9 @@ static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk)
if (sk_fullsock(sk)) {
return rcu_dereference_protected(tcp_sk(sk)->ao_info,
lockdep_sock_is_held(sk));
} else if (sk->sk_state == TCP_TIME_WAIT) {
return rcu_dereference_protected(tcp_twsk(sk)->ao_info,
lockdep_sock_is_held(sk));
}
return ERR_PTR(-ESOCKTNOSUPPORT);
}