Re: [PATCH v14 net-next 08/23] net/tcp: Add AO sign to RST packets

From: Eric Dumazet
Date: Wed Oct 11 2023 - 14:04:41 EST


On Tue, Oct 10, 2023 at 1:07 AM Dmitry Safonov <dima@xxxxxxxxxx> wrote:
>
> Wire up sending resets to TCP-AO hashing.
>
> Co-developed-by: Francesco Ruggeri <fruggeri@xxxxxxxxxx>
> Signed-off-by: Francesco Ruggeri <fruggeri@xxxxxxxxxx>
> Co-developed-by: Salam Noureddine <noureddine@xxxxxxxxxx>
> Signed-off-by: Salam Noureddine <noureddine@xxxxxxxxxx>
> Signed-off-by: Dmitry Safonov <dima@xxxxxxxxxx>
> Acked-by: David Ahern <dsahern@xxxxxxxxxx>
> ---
> include/net/tcp.h | 7 ++-
> include/net/tcp_ao.h | 12 +++++
> net/ipv4/tcp_ao.c | 104 ++++++++++++++++++++++++++++++++++++++++++-
> net/ipv4/tcp_ipv4.c | 69 ++++++++++++++++++++++------
> net/ipv6/tcp_ipv6.c | 96 ++++++++++++++++++++++++++++-----------
> 5 files changed, 245 insertions(+), 43 deletions(-)
>
> diff --git a/include/net/tcp.h b/include/net/tcp.h
> index a619c429a8bd..dc74908ffa5a 100644
> --- a/include/net/tcp.h
> +++ b/include/net/tcp.h
> @@ -2220,7 +2220,12 @@ static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops,
>
> struct tcp_key {
> union {
> - struct tcp_ao_key *ao_key;
> + struct {
> + struct tcp_ao_key *ao_key;
> + u32 sne;
> + char *traffic_key;

Move sne after traffic_key to avoid a hole on 64bit arches.

> + u8 rcv_next;
> + };
> struct tcp_md5sig_key *md5_key;
> };
> enum {
> diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
> index fdd2f5091b98..629ab0365b83 100644
> --- a/include/net/tcp_ao.h
> +++ b/include/net/tcp_ao.h
> @@ -120,12 +120,24 @@ int tcp_ao_hash_skb(unsigned short int family,
> const u8 *tkey, int hash_offset, u32 sne);
> int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
> sockptr_t optval, int optlen);
> +struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao,
> + int sndid, int rcvid);
> int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
> unsigned int len, struct tcp_sigpool *hp);
> void tcp_ao_destroy_sock(struct sock *sk);
> struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
> const union tcp_ao_addr *addr,
> int family, int sndid, int rcvid);
> +int tcp_ao_hash_hdr(unsigned short family, char *ao_hash,
> + struct tcp_ao_key *key, const u8 *tkey,
> + const union tcp_ao_addr *daddr,
> + const union tcp_ao_addr *saddr,
> + const struct tcphdr *th, u32 sne);
> +int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
> + const struct tcp_ao_hdr *aoh, int l3index,
> + struct tcp_ao_key **key, char **traffic_key,
> + bool *allocated_traffic_key, u8 *keyid, u32 *sne);
> +
> /* ipv4 specific functions */
> int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen);
> struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
> diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
> index 6eb9241d14a3..df59924c3828 100644
> --- a/net/ipv4/tcp_ao.c
> +++ b/net/ipv4/tcp_ao.c
> @@ -48,8 +48,8 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
> * it's known that the keys in ao_info are matching peer's
> * family/address/VRF/etc.
> */
> -static struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao,
> - int sndid, int rcvid)
> +struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao,
> + int sndid, int rcvid)
> {
> struct tcp_ao_key *key;
>
> @@ -369,6 +369,66 @@ static int tcp_ao_hash_header(struct tcp_sigpool *hp,
> return err;
> }
>
> +int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash,
> + struct tcp_ao_key *key, const u8 *tkey,
> + const union tcp_ao_addr *daddr,
> + const union tcp_ao_addr *saddr,
> + const struct tcphdr *th, u32 sne)
> +{
> + int tkey_len = tcp_ao_digest_size(key);
> + int hash_offset = ao_hash - (char *)th;
> + struct tcp_sigpool hp;
> + void *hash_buf = NULL;
> +
> + hash_buf = kmalloc(tkey_len, GFP_ATOMIC);
> + if (!hash_buf)
> + goto clear_hash_noput;
> +
> + if (tcp_sigpool_start(key->tcp_sigpool_id, &hp))
> + goto clear_hash_noput;
> +
> + if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len))
> + goto clear_hash;
> +
> + if (crypto_ahash_init(hp.req))
> + goto clear_hash;
> +
> + if (tcp_ao_hash_sne(&hp, sne))
> + goto clear_hash;
> + if (family == AF_INET) {
> + if (tcp_v4_ao_hash_pseudoheader(&hp, daddr->a4.s_addr,
> + saddr->a4.s_addr, th->doff * 4))
> + goto clear_hash;
> +#if IS_ENABLED(CONFIG_IPV6)
> + } else if (family == AF_INET6) {
> + if (tcp_v6_ao_hash_pseudoheader(&hp, &daddr->a6,
> + &saddr->a6, th->doff * 4))
> + goto clear_hash;
> +#endif
> + } else {
> + WARN_ON_ONCE(1);
> + goto clear_hash;
> + }
> + if (tcp_ao_hash_header(&hp, th, false,
> + ao_hash, hash_offset, tcp_ao_maclen(key)))
> + goto clear_hash;
> + ahash_request_set_crypt(hp.req, NULL, hash_buf, 0);
> + if (crypto_ahash_final(hp.req))
> + goto clear_hash;
> +
> + memcpy(ao_hash, hash_buf, tcp_ao_maclen(key));
> + tcp_sigpool_end(&hp);
> + kfree(hash_buf);
> + return 0;
> +
> +clear_hash:
> + tcp_sigpool_end(&hp);
> +clear_hash_noput:
> + memset(ao_hash, 0, tcp_ao_maclen(key));
> + kfree(hash_buf);
> + return 1;
> +}
> +
> int tcp_ao_hash_skb(unsigned short int family,
> char *ao_hash, struct tcp_ao_key *key,
> const struct sock *sk, const struct sk_buff *skb,
> @@ -435,6 +495,46 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
> return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid);
> }
>
> +int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
> + const struct tcp_ao_hdr *aoh, int l3index,
> + struct tcp_ao_key **key, char **traffic_key,
> + bool *allocated_traffic_key, u8 *keyid, u32 *sne)
> +{
> + struct tcp_ao_info *ao_info;
> +
> + *allocated_traffic_key = false;
> + /* If there's no socket - than initial sisn/disn are unknown.
> + * Drop the segment. RFC5925 (7.7) advises to require graceful
> + * restart [RFC4724]. Alternatively, the RFC5925 advises to
> + * save/restore traffic keys before/after reboot.
> + * Linux TCP-AO support provides TCP_AO_ADD_KEY and TCP_AO_REPAIR
> + * options to restore a socket post-reboot.
> + */
> + if (!sk)
> + return -ENOTCONN;
> +
> + if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) {
> + return -1;
> + } else {
> + struct tcp_ao_key *rnext_key;
> +
> + if (sk->sk_state == TCP_TIME_WAIT)

Why not adding TCPF_TIME_WAIT in the prior test ?

> + return -1;
> + ao_info = rcu_dereference(tcp_sk(sk)->ao_info);
> + if (!ao_info)
> + return -ENOENT;
> +
> + *key = tcp_ao_established_key(ao_info, aoh->rnext_keyid, -1);
> + if (!*key)
> + return -ENOENT;
> + *traffic_key = snd_other_key(*key);
> + rnext_key = READ_ONCE(ao_info->rnext_key);
> + *keyid = rnext_key->rcvid;
> + *sne = 0;
> + }
> + return 0;
> +}
> +
>