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>,
"Gaillardetz, Dominik" <dgaillar@ciena.com>,
Herbert Xu <herbert@gondor.apana.org.au>,
Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>,
Ivan Delalande <colona@arista.com>,
Leonard Crestez <cdleonard@gmail.com>,
"Nassiri, Mohammad" <mnassiri@ciena.com>,
Salam Noureddine <noureddine@arista.com>,
Simon Horman <simon.horman@corigine.com>,
"Tetreault, Francois" <ftetreau@ciena.com>,
netdev@vger.kernel.org
Subject: [PATCH v13 net-next 14/23] net/tcp: Add TCP-AO SNE support
Date: Wed, 4 Oct 2023 23:36:18 +0100 [thread overview]
Message-ID: <20231004223629.166300-15-dima@arista.com> (raw)
In-Reply-To: <20231004223629.166300-1-dima@arista.com>
Add Sequence Number Extension (SNE) for TCP-AO.
This is needed to protect long-living TCP-AO connections from replaying
attacks after sequence number roll-over, see RFC5925 (6.2).
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>
---
include/net/tcp_ao.h | 22 ++++++++++++++++++-
net/ipv4/tcp_ao.c | 46 ++++++++++++++++++++++++++++++++--------
net/ipv4/tcp_input.c | 28 ++++++++++++++++++++++++
net/ipv4/tcp_ipv4.c | 3 ++-
net/ipv4/tcp_minisocks.c | 15 ++++++++++++-
net/ipv6/tcp_ipv6.c | 3 ++-
6 files changed, 104 insertions(+), 13 deletions(-)
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index b34b9a37c81b..9e37193fe805 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -95,6 +95,25 @@ struct tcp_ao_info {
__unused :31;
__be32 lisn;
__be32 risn;
+ /* Sequence Number Extension (SNE) are upper 4 bytes for SEQ,
+ * that protect TCP-AO connection from replayed old TCP segments.
+ * See RFC5925 (6.2).
+ * In order to get correct SNE, there's a helper tcp_ao_compute_sne().
+ * It needs SEQ basis to understand whereabouts are lower SEQ numbers.
+ * According to that basis vector, it can provide incremented SNE
+ * when SEQ rolls over or provide decremented SNE when there's
+ * a retransmitted segment from before-rolling over.
+ * - for request sockets such basis is rcv_isn/snt_isn, which seems
+ * good enough as it's unexpected to receive 4 Gbytes on reqsk.
+ * - for full sockets the basis is rcv_nxt/snd_una. snd_una is
+ * taken instead of snd_nxt as currently it's easier to track
+ * in tcp_snd_una_update(), rather than updating SNE in all
+ * WRITE_ONCE(tp->snd_nxt, ...)
+ * - for time-wait sockets the basis is tw_rcv_nxt/tw_snd_nxt.
+ * tw_snd_nxt is not expected to change, while tw_rcv_nxt may.
+ */
+ u32 snd_sne;
+ u32 rcv_sne;
atomic_t refcnt; /* Protects twsk destruction */
struct rcu_head rcu;
};
@@ -147,6 +166,7 @@ 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,
const struct tcp_ao_hdr *aoh);
+u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq);
struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
const union tcp_ao_addr *addr,
int family, int sndid, int rcvid);
@@ -156,7 +176,7 @@ int tcp_ao_hash_hdr(unsigned short family, char *ao_hash,
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,
+ const struct tcp_ao_hdr *aoh, int l3index, u32 seq,
struct tcp_ao_key **key, char **traffic_key,
bool *allocated_traffic_key, u8 *keyid, u32 *sne);
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index f7ee2aecf8fc..deae7209f8a1 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -401,6 +401,21 @@ static int tcp_ao_hash_pseudoheader(unsigned short int family,
return -EAFNOSUPPORT;
}
+u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq)
+{
+ u32 sne = next_sne;
+
+ if (before(seq, next_seq)) {
+ if (seq > next_seq)
+ sne--;
+ } else {
+ if (seq < next_seq)
+ sne++;
+ }
+
+ return sne;
+}
+
/* tcp_ao_hash_sne(struct tcp_sigpool *hp)
* @hp - used for hashing
* @sne - sne value
@@ -611,7 +626,7 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
}
int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
- const struct tcp_ao_hdr *aoh, int l3index,
+ const struct tcp_ao_hdr *aoh, int l3index, u32 seq,
struct tcp_ao_key **key, char **traffic_key,
bool *allocated_traffic_key, u8 *keyid, u32 *sne)
{
@@ -639,7 +654,7 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
sisn = htonl(tcp_rsk(req)->rcv_isn);
disn = htonl(tcp_rsk(req)->snt_isn);
- *sne = 0;
+ *sne = tcp_ao_compute_sne(0, tcp_rsk(req)->snt_isn, seq);
} else {
sisn = th->seq;
disn = 0;
@@ -670,11 +685,15 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
*keyid = (*key)->rcvid;
} else {
struct tcp_ao_key *rnext_key;
+ u32 snd_basis;
- if (sk->sk_state == TCP_TIME_WAIT)
+ if (sk->sk_state == TCP_TIME_WAIT) {
ao_info = rcu_dereference(tcp_twsk(sk)->ao_info);
- else
+ snd_basis = tcp_twsk(sk)->tw_snd_nxt;
+ } else {
ao_info = rcu_dereference(tcp_sk(sk)->ao_info);
+ snd_basis = tcp_sk(sk)->snd_una;
+ }
if (!ao_info)
return -ENOENT;
@@ -684,7 +703,8 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
*traffic_key = snd_other_key(*key);
rnext_key = READ_ONCE(ao_info->rnext_key);
*keyid = rnext_key->rcvid;
- *sne = 0;
+ *sne = tcp_ao_compute_sne(READ_ONCE(ao_info->snd_sne),
+ snd_basis, seq);
}
return 0;
}
@@ -698,6 +718,7 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
struct tcp_ao_info *ao;
void *tkey_buf = NULL;
u8 *traffic_key;
+ u32 sne;
ao = rcu_dereference_protected(tcp_sk(sk)->ao_info,
lockdep_sock_is_held(sk));
@@ -717,8 +738,10 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
tp->af_specific->ao_calc_key_sk(key, traffic_key,
sk, ao->lisn, disn, true);
}
+ sne = tcp_ao_compute_sne(READ_ONCE(ao->snd_sne), READ_ONCE(tp->snd_una),
+ ntohl(th->seq));
tp->af_specific->calc_ao_hash(hash_location, key, sk, skb, traffic_key,
- hash_location - (u8 *)th, 0);
+ hash_location - (u8 *)th, sne);
kfree(tkey_buf);
return 0;
}
@@ -846,7 +869,8 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
if (unlikely(th->syn && !th->ack))
goto verify_hash;
- sne = 0;
+ sne = tcp_ao_compute_sne(info->rcv_sne, tcp_sk(sk)->rcv_nxt,
+ ntohl(th->seq));
/* Established socket, traffic key are cached */
traffic_key = rcv_other_key(key);
err = tcp_ao_verify_hash(sk, skb, family, info, aoh, key,
@@ -881,14 +905,16 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) {
/* Make the initial syn the likely case here */
if (unlikely(req)) {
- sne = 0;
+ sne = tcp_ao_compute_sne(0, tcp_rsk(req)->rcv_isn,
+ ntohl(th->seq));
sisn = htonl(tcp_rsk(req)->rcv_isn);
disn = htonl(tcp_rsk(req)->snt_isn);
} else if (unlikely(th->ack && !th->syn)) {
/* Possible syncookie packet */
sisn = htonl(ntohl(th->seq) - 1);
disn = htonl(ntohl(th->ack_seq) - 1);
- sne = 0;
+ sne = tcp_ao_compute_sne(0, ntohl(sisn),
+ ntohl(th->seq));
} else if (unlikely(!th->syn)) {
/* no way to figure out initial sisn/disn - drop */
return SKB_DROP_REASON_TCP_FLAGS;
@@ -986,6 +1012,7 @@ void tcp_ao_connect_init(struct sock *sk)
tp->tcp_header_len += tcp_ao_len(key);
ao_info->lisn = htonl(tp->write_seq);
+ ao_info->snd_sne = 0;
} else {
/* TODO: probably, it should fail to connect() here */
rcu_assign_pointer(tp->ao_info, NULL);
@@ -1018,6 +1045,7 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
return;
WRITE_ONCE(ao->risn, tcp_hdr(skb)->seq);
+ ao->rcv_sne = 0;
hlist_for_each_entry_rcu(key, &ao->head, node)
tcp_ao_cache_traffic_keys(sk, ao, key);
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index df137d5668f7..2ce768e1d914 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3546,9 +3546,18 @@ static inline bool tcp_may_update_window(const struct tcp_sock *tp,
static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack)
{
u32 delta = ack - tp->snd_una;
+#ifdef CONFIG_TCP_AO
+ struct tcp_ao_info *ao;
+#endif
sock_owned_by_me((struct sock *)tp);
tp->bytes_acked += delta;
+#ifdef CONFIG_TCP_AO
+ ao = rcu_dereference_protected(tp->ao_info,
+ lockdep_sock_is_held((struct sock *)tp));
+ if (ao && ack < tp->snd_una)
+ ao->snd_sne++;
+#endif
tp->snd_una = ack;
}
@@ -3556,9 +3565,18 @@ static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack)
static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq)
{
u32 delta = seq - tp->rcv_nxt;
+#ifdef CONFIG_TCP_AO
+ struct tcp_ao_info *ao;
+#endif
sock_owned_by_me((struct sock *)tp);
tp->bytes_received += delta;
+#ifdef CONFIG_TCP_AO
+ ao = rcu_dereference_protected(tp->ao_info,
+ lockdep_sock_is_held((struct sock *)tp));
+ if (ao && seq < tp->rcv_nxt)
+ ao->rcv_sne++;
+#endif
WRITE_ONCE(tp->rcv_nxt, seq);
}
@@ -6414,6 +6432,16 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
* simultaneous connect with crossed SYNs.
* Particularly, it can be connect to self.
*/
+#ifdef CONFIG_TCP_AO
+ struct tcp_ao_info *ao;
+
+ ao = rcu_dereference_protected(tp->ao_info,
+ lockdep_sock_is_held(sk));
+ if (ao) {
+ WRITE_ONCE(ao->risn, th->seq);
+ ao->rcv_sne = 0;
+ }
+#endif
tcp_set_state(sk, TCP_SYN_RECV);
if (tp->rx_opt.saw_tstamp) {
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 1e714b26d9ce..48fb81a8a712 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -675,7 +675,7 @@ static bool tcp_v4_ao_sign_reset(const struct sock *sk, struct sk_buff *skb,
u8 keyid;
rcu_read_lock();
- if (tcp_ao_prepare_reset(sk, skb, aoh, l3index,
+ if (tcp_ao_prepare_reset(sk, skb, aoh, l3index, ntohl(reply->seq),
&key, &traffic_key, &allocated_traffic_key,
&keyid, &ao_sne))
goto out;
@@ -1033,6 +1033,7 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
struct tcp_ao_key *rnext_key;
key.traffic_key = snd_other_key(key.ao_key);
+ key.sne = READ_ONCE(ao_info->snd_sne);
rnext_key = READ_ONCE(ao_info->rnext_key);
key.rcv_next = rnext_key->rcvid;
key.type = TCP_KEY_AO;
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index a6ece0edf4d7..96963c15a05e 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -51,6 +51,18 @@ tcp_timewait_check_oow_rate_limit(struct inet_timewait_sock *tw,
return TCP_TW_SUCCESS;
}
+static void twsk_rcv_nxt_update(struct tcp_timewait_sock *tcptw, u32 seq)
+{
+#ifdef CONFIG_TCP_AO
+ struct tcp_ao_info *ao;
+
+ ao = rcu_dereference(tcptw->ao_info);
+ if (unlikely(ao && seq < tcptw->tw_rcv_nxt))
+ WRITE_ONCE(ao->rcv_sne, ao->rcv_sne + 1);
+#endif
+ tcptw->tw_rcv_nxt = seq;
+}
+
/*
* * Main purpose of TIME-WAIT state is to close connection gracefully,
* when one of ends sits in LAST-ACK or CLOSING retransmitting FIN
@@ -136,7 +148,8 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
/* FIN arrived, enter true time-wait state. */
tw->tw_substate = TCP_TIME_WAIT;
- tcptw->tw_rcv_nxt = TCP_SKB_CB(skb)->end_seq;
+ twsk_rcv_nxt_update(tcptw, TCP_SKB_CB(skb)->end_seq);
+
if (tmp_opt.saw_tstamp) {
tcptw->tw_ts_recent_stamp = ktime_get_seconds();
tcptw->tw_ts_recent = tmp_opt.rcv_tsval;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 05f3421f26ea..7d375405fd94 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1087,7 +1087,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
int l3index;
l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0;
- if (tcp_ao_prepare_reset(sk, skb, aoh, l3index,
+ if (tcp_ao_prepare_reset(sk, skb, aoh, l3index, seq,
&key.ao_key, &key.traffic_key,
&allocated_traffic_key,
&key.rcv_next, &key.sne))
@@ -1164,6 +1164,7 @@ 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);
key.rcv_next = rnext_key->rcvid;
+ key.sne = READ_ONCE(ao_info->snd_sne);
key.type = TCP_KEY_AO;
#else
if (0) {
--
2.42.0
next prev parent reply other threads:[~2023-10-04 22:37 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-04 22:36 [PATCH v13 net-next 00/23] net/tcp: Add TCP-AO support Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 01/23] net/tcp: Prepare tcp_md5sig_pool for TCP-AO Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 02/23] net/tcp: Add TCP-AO config and structures Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 03/23] net/tcp: Introduce TCP_AO setsockopt()s Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 04/23] net/tcp: Prevent TCP-MD5 with TCP-AO being set Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 05/23] net/tcp: Calculate TCP-AO traffic keys Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 06/23] net/tcp: Add TCP-AO sign to outgoing packets Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 07/23] net/tcp: Add tcp_parse_auth_options() Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 08/23] net/tcp: Add AO sign to RST packets Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 09/23] net/tcp: Add TCP-AO sign to twsk Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 10/23] net/tcp: Wire TCP-AO to request sockets Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 11/23] net/tcp: Sign SYN-ACK segments with TCP-AO Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 12/23] net/tcp: Verify inbound TCP-AO signed segments Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 13/23] net/tcp: Add TCP-AO segments counters Dmitry Safonov
2023-10-04 22:36 ` Dmitry Safonov [this message]
2023-10-04 22:36 ` [PATCH v13 net-next 15/23] net/tcp: Add tcp_hash_fail() ratelimited logs Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 16/23] net/tcp: Ignore specific ICMPs for TCP-AO connections Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 17/23] net/tcp: Add option for TCP-AO to (not) hash header Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 18/23] net/tcp: Add TCP-AO getsockopt()s Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 19/23] net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs) Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 20/23] net/tcp: Add static_key for TCP-AO Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 21/23] net/tcp: Wire up l3index to TCP-AO Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 22/23] net/tcp: Add TCP_AO_REPAIR Dmitry Safonov
2023-10-04 22:36 ` [PATCH v13 net-next 23/23] Documentation/tcp: Add TCP-AO documentation Dmitry Safonov
2023-10-04 22:56 ` Jonathan Corbet
2023-10-05 17:10 ` Dmitry Safonov
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=20231004223629.166300-15-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=dgaillar@ciena.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=ftetreau@ciena.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=mnassiri@ciena.com \
--cc=netdev@vger.kernel.org \
--cc=noureddine@arista.com \
--cc=pabeni@redhat.com \
--cc=simon.horman@corigine.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.