All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dmitry Safonov <dima@arista.com>
To: David Ahern <dsahern@kernel.org>,
	Eric Dumazet <edumazet@google.com>,
	Paolo Abeni <pabeni@redhat.com>, Jakub Kicinski <kuba@kernel.org>,
	"David S. Miller" <davem@davemloft.net>
Cc: linux-kernel@vger.kernel.org, Dmitry Safonov <dima@arista.com>,
	Andy Lutomirski <luto@amacapital.net>,
	Ard Biesheuvel <ardb@kernel.org>,
	Bob Gilligan <gilligan@arista.com>,
	Dan Carpenter <error27@gmail.com>,
	David Laight <David.Laight@aculab.com>,
	Dmitry Safonov <0x7f454c46@gmail.com>,
	Donald Cassidy <dcassidy@redhat.com>,
	Eric Biggers <ebiggers@kernel.org>,
	"Eric W. Biederman" <ebiederm@xmission.com>,
	Francesco Ruggeri <fruggeri05@gmail.com>,
	Herbert Xu <herbert@gondor.apana.org.au>,
	Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>,
	Ivan Delalande <colona@arista.com>,
	Leonard Crestez <cdleonard@gmail.com>,
	Salam Noureddine <noureddine@arista.com>,
	netdev@vger.kernel.org
Subject: [PATCH v7 22/22] net/tcp: Add TCP_AO_REPAIR
Date: Thu, 15 Jun 2023 00:09:47 +0100	[thread overview]
Message-ID: <20230614230947.3954084-23-dima@arista.com> (raw)
In-Reply-To: <20230614230947.3954084-1-dima@arista.com>

Add TCP_AO_REPAIR setsockopt(), getsockopt(). They let a user to repair
TCP-AO ISNs/SNEs. Also let the user hack around when (tp->repair) is on
and add ao_info on a socket in any supported state.
As SNEs now can be read/written at any moment, use
WRITE_ONCE()/READ_ONCE() to set/read them.

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 include/net/tcp_ao.h     |  14 ++++++
 include/uapi/linux/tcp.h |  10 ++++
 net/ipv4/tcp.c           |  24 +++++++---
 net/ipv4/tcp_ao.c        | 100 ++++++++++++++++++++++++++++++++++++---
 net/ipv4/tcp_input.c     |  12 ++---
 net/ipv4/tcp_ipv4.c      |   4 +-
 net/ipv4/tcp_output.c    |   3 +-
 net/ipv6/tcp_ipv6.c      |   4 +-
 8 files changed, 147 insertions(+), 24 deletions(-)

diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index 3449d093143e..54eec4859b35 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -182,6 +182,8 @@ void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
 bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code);
 int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen);
 int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen);
+int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen);
+int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen);
 enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk,
 			const struct sk_buff *skb, unsigned short int family,
 			const struct request_sock *req, int l3index,
@@ -303,6 +305,18 @@ static inline int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockpt
 {
 	return -ENOPROTOOPT;
 }
+
+static inline int tcp_ao_get_repair(struct sock *sk,
+				    sockptr_t optval, sockptr_t optlen)
+{
+	return -ENOPROTOOPT;
+}
+
+static inline int tcp_ao_set_repair(struct sock *sk,
+				    sockptr_t optval, unsigned int optlen)
+{
+	return -ENOPROTOOPT;
+}
 #endif
 
 #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
index 979ff960fddb..a0dd4612a37c 100644
--- a/include/uapi/linux/tcp.h
+++ b/include/uapi/linux/tcp.h
@@ -133,6 +133,7 @@ enum {
 #define TCP_AO_DEL_KEY		39	/* Delete MKT */
 #define TCP_AO_INFO		40	/* Set/list TCP-AO per-socket options */
 #define TCP_AO_GET_KEYS		41	/* List MKT(s) */
+#define TCP_AO_REPAIR		42	/* Get/Set SNEs and ISNs */
 
 #define TCP_REPAIR_ON		1
 #define TCP_REPAIR_OFF		0
@@ -445,6 +446,15 @@ struct tcp_ao_getsockopt { /* getsockopt(TCP_AO_GET_KEYS) */
 	__u64	pkt_bad;		/* out: segments that failed verification */
 } __attribute__((aligned(8)));
 
+struct tcp_ao_repair { /* {s,g}etsockopt(TCP_AO_REPAIR) */
+	__be32			snt_isn;
+	__be32			rcv_isn;
+	__u32			snd_sne;
+	__u32			snd_sne_seq;
+	__u32			rcv_sne;
+	__u32			rcv_sne_seq;
+} __attribute__((aligned(8)));
+
 /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
 
 #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index c4143b67a67b..b814a3ce9e69 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -3714,20 +3714,28 @@ int do_tcp_setsockopt(struct sock *sk, int level, int optname,
 		__tcp_sock_set_quickack(sk, val);
 		break;
 
+	case TCP_AO_REPAIR:
+		err = tcp_ao_set_repair(sk, optval, optlen);
+		break;
 #ifdef CONFIG_TCP_AO
 	case TCP_AO_ADD_KEY:
 	case TCP_AO_DEL_KEY:
 	case TCP_AO_INFO: {
 		/* If this is the first TCP-AO setsockopt() on the socket,
-		 * sk_state has to be LISTEN or CLOSE
+		 * sk_state has to be LISTEN or CLOSE. Allow TCP_REPAIR
+		 * in any state.
 		 */
-		if (((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) ||
-		    rcu_dereference_protected(tcp_sk(sk)->ao_info,
+		if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))
+			goto ao_parse;
+		if (rcu_dereference_protected(tcp_sk(sk)->ao_info,
 					      lockdep_sock_is_held(sk)))
-			err = tp->af_specific->ao_parse(sk, optname, optval,
-							optlen);
-		else
-			err = -EISCONN;
+			goto ao_parse;
+		if (tp->repair)
+			goto ao_parse;
+		err = -EISCONN;
+		break;
+ao_parse:
+		err = tp->af_specific->ao_parse(sk, optname, optval, optlen);
 		break;
 	}
 #endif
@@ -4394,6 +4402,8 @@ int do_tcp_getsockopt(struct sock *sk, int level,
 		return err;
 	}
 #endif
+	case TCP_AO_REPAIR:
+		return tcp_ao_get_repair(sk, optval, optlen);
 	case TCP_AO_GET_KEYS:
 	case TCP_AO_INFO: {
 		int err;
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index d8a02cf0ba3c..6fe745e0b04c 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -1061,8 +1061,8 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
 
 	ao->risn = tcp_hdr(skb)->seq;
 
-	ao->rcv_sne = 0;
-	ao->rcv_sne_seq = ntohl(tcp_hdr(skb)->seq);
+	WRITE_ONCE(ao->rcv_sne, 0);
+	WRITE_ONCE(ao->rcv_sne_seq, ntohl(tcp_hdr(skb)->seq));
 
 	hlist_for_each_entry_rcu(key, &ao->head, node) {
 		tcp_ao_cache_traffic_keys(sk, ao, key);
@@ -1452,6 +1452,16 @@ static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk)
 	return ERR_PTR(-ESOCKTNOSUPPORT);
 }
 
+static struct tcp_ao_info *getsockopt_ao_info(struct sock *sk)
+{
+	if (sk_fullsock(sk))
+		return rcu_dereference(tcp_sk(sk)->ao_info);
+	else if (sk->sk_state == TCP_TIME_WAIT)
+		return rcu_dereference(tcp_twsk(sk)->ao_info);
+
+	return ERR_PTR(-ESOCKTNOSUPPORT);
+}
+
 #define TCP_AO_KEYF_ALL (TCP_AO_KEYF_IFINDEX | TCP_AO_KEYF_EXCLUDE_OPT)
 #define TCP_AO_GET_KEYF_VALID	(TCP_AO_KEYF_IFINDEX)
 
@@ -1586,11 +1596,13 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
 	if (ret < 0)
 		goto err_free_sock;
 
-	/* Change this condition if we allow adding keys in states
-	 * like close_wait, syn_sent or fin_wait...
-	 */
-	if (sk->sk_state == TCP_ESTABLISHED)
+	if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) {
 		tcp_ao_cache_traffic_keys(sk, ao_info, key);
+		if (first) {
+			ao_info->current_key = key;
+			ao_info->rnext_key = key;
+		}
+	}
 
 	tcp_ao_link_mkt(ao_info, key);
 	if (first) {
@@ -1837,6 +1849,8 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family,
 	if (IS_ERR(ao_info))
 		return PTR_ERR(ao_info);
 	if (!ao_info) {
+		if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)))
+			return -EINVAL;
 		ao_info = tcp_ao_alloc_info(GFP_KERNEL);
 		if (!ao_info)
 			return -ENOMEM;
@@ -2219,3 +2233,77 @@ int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen)
 	return 0;
 }
 
+int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct tcp_ao_repair cmd;
+	struct tcp_ao_key *key;
+	struct tcp_ao_info *ao;
+	int err;
+
+	if (optlen < sizeof(cmd))
+		return -EINVAL;
+
+	err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen);
+	if (err)
+		return err;
+
+	if (!tp->repair)
+		return -EPERM;
+
+	ao = setsockopt_ao_info(sk);
+	if (IS_ERR(ao))
+		return PTR_ERR(ao);
+	if (!ao)
+		return -ENOENT;
+
+	WRITE_ONCE(ao->lisn, cmd.snt_isn);
+	WRITE_ONCE(ao->risn, cmd.rcv_isn);
+	WRITE_ONCE(ao->snd_sne, cmd.snd_sne);
+	WRITE_ONCE(ao->snd_sne_seq, cmd.snd_sne_seq);
+	WRITE_ONCE(ao->rcv_sne, cmd.rcv_sne);
+	WRITE_ONCE(ao->rcv_sne_seq, cmd.rcv_sne_seq);
+
+	hlist_for_each_entry_rcu(key, &ao->head, node)
+		tcp_ao_cache_traffic_keys(sk, ao, key);
+
+	return 0;
+}
+
+int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct tcp_ao_repair opt;
+	struct tcp_ao_info *ao;
+	int len;
+
+	if (copy_from_sockptr(&len, optlen, sizeof(int)))
+		return -EFAULT;
+
+	if (len <= 0)
+		return -EINVAL;
+
+	if (!tp->repair)
+		return -EPERM;
+
+	rcu_read_lock();
+	ao = getsockopt_ao_info(sk);
+	if (IS_ERR(ao))
+		return PTR_ERR(ao);
+	if (!ao) {
+		rcu_read_unlock();
+		return -ENOENT;
+	}
+
+	opt.snt_isn	= ao->lisn;
+	opt.rcv_isn	= ao->risn;
+	opt.snd_sne	= READ_ONCE(ao->snd_sne);
+	opt.snd_sne_seq	= READ_ONCE(ao->snd_sne_seq);
+	opt.rcv_sne	= READ_ONCE(ao->rcv_sne);
+	opt.rcv_sne_seq	= READ_ONCE(ao->rcv_sne_seq);
+	rcu_read_unlock();
+
+	if (copy_to_sockptr(optval, &opt, len))
+		return -EFAULT;
+	return 0;
+}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index c0c18b05fd1c..1bd47cb8fb9a 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3536,8 +3536,8 @@ static void tcp_snd_sne_update(struct tcp_sock *tp, u32 ack)
 				       lockdep_sock_is_held((struct sock *)tp));
 	if (ao) {
 		if (ack < ao->snd_sne_seq)
-			ao->snd_sne++;
-		ao->snd_sne_seq = ack;
+			WRITE_ONCE(ao->snd_sne, ao->snd_sne + 1);
+		WRITE_ONCE(ao->snd_sne_seq, ack);
 	}
 #endif
 }
@@ -3565,8 +3565,8 @@ static void tcp_rcv_sne_update(struct tcp_sock *tp, u32 seq)
 				       lockdep_sock_is_held((struct sock *)tp));
 	if (ao) {
 		if (seq < ao->rcv_sne_seq)
-			ao->rcv_sne++;
-		ao->rcv_sne_seq = seq;
+			WRITE_ONCE(ao->rcv_sne, ao->rcv_sne + 1);
+		WRITE_ONCE(ao->rcv_sne_seq, seq);
 	}
 #endif
 }
@@ -6416,8 +6416,8 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
 					       lockdep_sock_is_held(sk));
 		if (ao) {
 			ao->risn = th->seq;
-			ao->rcv_sne = 0;
-			ao->rcv_sne_seq = ntohl(th->seq);
+			WRITE_ONCE(ao->rcv_sne, 0);
+			WRITE_ONCE(ao->rcv_sne_seq, ntohl(th->seq));
 		}
 #endif
 		tcp_set_state(sk, TCP_SYN_RECV);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 211d0edd6eea..229317e38a5e 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1040,8 +1040,8 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
 		 * below since sne probably doesn't change once we are
 		 * in timewait state.
 		 */
-		ao_sne = tcp_ao_compute_sne(ao_info->snd_sne,
-					    ao_info->snd_sne_seq,
+		ao_sne = tcp_ao_compute_sne(READ_ONCE(ao_info->snd_sne),
+					    READ_ONCE(ao_info->snd_sne_seq),
 					    tcptw->tw_snd_nxt);
 		rnext_key = READ_ONCE(ao_info->rnext_key);
 		rcv_next = rnext_key->rcvid;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index f055ef225c37..e68ae7b896b7 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1455,7 +1455,8 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
 		} else {
 			traffic_key = snd_other_key(ao_key);
 		}
-		sne = tcp_ao_compute_sne(ao->snd_sne, ao->snd_sne_seq,
+		sne = tcp_ao_compute_sne(READ_ONCE(ao->snd_sne),
+					 READ_ONCE(ao->snd_sne_seq),
 					 ntohl(th->seq));
 		tp->af_specific->calc_ao_hash(opts.hash_location, ao_key, sk, skb,
 					      traffic_key,
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 6b39da6c477e..ba9d4571b265 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1190,8 +1190,8 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
 		/* rcv_next switches to our rcv_next */
 		rnext_key = READ_ONCE(ao_info->rnext_key);
 		rcv_next = rnext_key->rcvid;
-		ao_sne = tcp_ao_compute_sne(ao_info->snd_sne,
-					    ao_info->snd_sne_seq,
+		ao_sne = tcp_ao_compute_sne(READ_ONCE(ao_info->snd_sne),
+					    READ_ONCE(ao_info->snd_sne_seq),
 					    tcptw->tw_snd_nxt);
 	}
 #endif
-- 
2.40.0


      parent reply	other threads:[~2023-06-14 23:12 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-14 23:09 [PATCH v7 00/22] net/tcp: Add TCP-AO support Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 01/22] net/tcp: Prepare tcp_md5sig_pool for TCP-AO Dmitry Safonov
2023-06-15 10:45   ` Steen Hegelund
2023-06-15 16:44     ` Dmitry Safonov
2023-06-16  7:00       ` Steen Hegelund
2023-06-14 23:09 ` [PATCH v7 02/22] net/tcp: Add TCP-AO config and structures Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 03/22] net/tcp: Introduce TCP_AO setsockopt()s Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 04/22] net/tcp: Prevent TCP-MD5 with TCP-AO being set Dmitry Safonov
2023-06-18 17:50   ` David Ahern
2023-06-19 16:31     ` Dmitry Safonov
2023-06-19 16:41       ` Dmitry Safonov
2023-06-19 16:59         ` Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 05/22] net/tcp: Calculate TCP-AO traffic keys Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 06/22] net/tcp: Add TCP-AO sign to outgoing packets Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 07/22] net/tcp: Add tcp_parse_auth_options() Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 08/22] net/tcp: Add AO sign to RST packets Dmitry Safonov
2023-06-15  1:22   ` kernel test robot
2023-06-15  1:23   ` kernel test robot
2023-06-15 11:47   ` kernel test robot
2023-06-14 23:09 ` [PATCH v7 09/22] net/tcp: Add TCP-AO sign to twsk Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 10/22] net/tcp: Wire TCP-AO to request sockets Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 11/22] net/tcp: Sign SYN-ACK segments with TCP-AO Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 12/22] net/tcp: Verify inbound TCP-AO signed segments Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 13/22] net/tcp: Add TCP-AO segments counters Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 14/22] net/tcp: Add TCP-AO SNE support Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 15/22] net/tcp: Add tcp_hash_fail() ratelimited logs Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 16/22] net/tcp: Ignore specific ICMPs for TCP-AO connections Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 17/22] net/tcp: Add option for TCP-AO to (not) hash header Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 18/22] net/tcp: Add TCP-AO getsockopt()s Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 19/22] net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs) Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 20/22] net/tcp: Add static_key for TCP-AO Dmitry Safonov
2023-06-14 23:09 ` [PATCH v7 21/22] net/tcp: Wire up l3index to TCP-AO Dmitry Safonov
2023-06-14 23:09 ` Dmitry Safonov [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230614230947.3954084-23-dima@arista.com \
    --to=dima@arista.com \
    --cc=0x7f454c46@gmail.com \
    --cc=David.Laight@aculab.com \
    --cc=ardb@kernel.org \
    --cc=cdleonard@gmail.com \
    --cc=colona@arista.com \
    --cc=davem@davemloft.net \
    --cc=dcassidy@redhat.com \
    --cc=dsahern@kernel.org \
    --cc=ebiederm@xmission.com \
    --cc=ebiggers@kernel.org \
    --cc=edumazet@google.com \
    --cc=error27@gmail.com \
    --cc=fruggeri05@gmail.com \
    --cc=gilligan@arista.com \
    --cc=herbert@gondor.apana.org.au \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luto@amacapital.net \
    --cc=netdev@vger.kernel.org \
    --cc=noureddine@arista.com \
    --cc=pabeni@redhat.com \
    --cc=yoshfuji@linux-ipv6.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.