Re: [PATCH v4 4/7] net/tcp: Allow removing current/rnext TCP-AO keys on TCP_LISTEN sockets

From: Dmitry Safonov
Date: Wed Nov 29 2023 - 13:11:29 EST


On 11/29/23 17:53, Eric Dumazet wrote:
> On Wed, Nov 29, 2023 at 5:57 PM Dmitry Safonov <dima@xxxxxxxxxx> wrote:
>>
>> TCP_LISTEN sockets are not connected to any peer, so having
>> current_key/rnext_key doesn't make sense.
>
> I do not understand this patch.
>
> This seems that the clearing should happen at disconnect time, from
> tcp_disconnect() ?

Yeah, probably the patch description could have been better.

The key here is that TCP_CLOSE may have current/rnext keys: they will be
the ones that are used on connect() for 3way handshake. While for
TCP_LISTEN it doesn't make any sense to have them (as they otherwise
should be per-peer ip/netmask).

So, initially I thought of cleaning them up on listen() syscall [1].
But obviously the result was a bit gross.

So, I decided to just let userspace delete any keys on TCP_LISTEN by
cleaning current/rnext pointers before the checks that don't allow
removing current/rnext keys as they are in use by connection.

For TCP_CLOSE it's a lesser deal:
- the socket may just be closed
- alternatively, the user may add a new key and set it as current/rnext
and then remove the old key (as it won't be current/rnext anymore).

I also should note that currently it's not possible to set/change
current/rnext key on TCP_LISTEN, this situation is only a theoretical
issue that may be encountered by userspace if it sets those keys by any
random reason before listen():

static bool tcp_ao_can_set_current_rnext(struct sock *sk)
{
/* There aren't current/rnext keys on TCP_LISTEN sockets */
if (sk->sk_state == TCP_LISTEN)
return false;
return true;
}

> Why forcing user to set a socket option to clear these fields ?

It's just before the checks that disallow removing keys in use:

static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info,
bool del_async, struct tcp_ao_key *key,
struct tcp_ao_key *new_current,
struct tcp_ao_key *new_rnext)
{
...
if (unlikely(READ_ONCE(ao_info->current_key) == key ||
READ_ONCE(ao_info->rnext_key) == key)) {
err = -EBUSY;
goto add_key;
}


>> The userspace may falter over this issue by setting current or rnext
>> TCP-AO key before listen() syscall. setsockopt(TCP_AO_DEL_KEY) doesn't
>> allow removing a key that is in use (in accordance to RFC 5925), so
>> it might be inconvenient to have keys that can be destroyed only with
>> listener socket.
>>
>> Fixes: 4954f17ddefc ("net/tcp: Introduce TCP_AO setsockopt()s")
>> Signed-off-by: Dmitry Safonov <dima@xxxxxxxxxx>
>> ---
>> net/ipv4/tcp_ao.c | 12 ++++++++++--
>> 1 file changed, 10 insertions(+), 2 deletions(-)
>>
>> diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
>> index c8be1d526eac..bf41be6d4721 100644
>> --- a/net/ipv4/tcp_ao.c
>> +++ b/net/ipv4/tcp_ao.c
>> @@ -1818,8 +1818,16 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family,
>> if (!new_rnext)
>> return -ENOENT;
>> }
>> - if (cmd.del_async && sk->sk_state != TCP_LISTEN)
>> - return -EINVAL;
>> + if (sk->sk_state == TCP_LISTEN) {
>> + /* Cleaning up possible "stale" current/rnext keys state,
>> + * that may have preserved from TCP_CLOSE, before sys_listen()
>> + */
>> + ao_info->current_key = NULL;
>> + ao_info->rnext_key = NULL;
>> + } else {
>> + if (cmd.del_async)
>> + return -EINVAL;
>> + }
>>
>> if (family == AF_INET) {
>> struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.addr;
>> --
>> 2.43.0
>>

[1]
https://lore.kernel.org/all/CANn89i+xvBQY5HLXNkjW0o9R4SX1hqRisJnr54ZqwuOpEJdHeA@xxxxxxxxxxxxxx/T/#mfd4461bf1dabf6e4b994d85f5191b6cefce337cd

Thanks,
Dmitry