All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/31] net/tcp: Add TCP-AO support
@ 2022-08-18 16:59 Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 01/31] crypto: Introduce crypto_pool Dmitry Safonov
                   ` (31 more replies)
  0 siblings, 32 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

This patchset implements the TCP-AO option as described in RFC5925. There
is a request from industry to move away from TCP-MD5SIG and it seems the time
is right to have a TCP-AO upstreamed. This TCP option is meant to replace
the TCP MD5 option and address its shortcomings. Specifically, it provides
more secure hashing, key rotation and support for long-lived connections
(see the summary of TCP-AO advantages over TCP-MD5 in (1.3) of RFC5925).
The patch series starts with six patches that are not specific to TCP-AO
but implement a general crypto facility that we thought is useful
to eliminate code duplication between TCP-MD5SIG and TCP-AO as well as other
crypto users. These six patches are being submitted separately in
a different patchset [1]. Including them here will show better the gain
in code sharing. Next are 18 patches that implement the actual TCP-AO option,
followed by patches implementing selftests.

The patch set was written as a collaboration of three authors (in alphabetical
order): Dmitry Safonov, Francesco Ruggeri and Salam Noureddine. Additional
credits should be given to Prasad Koya, who was involved in early prototyping
a few years back. There is also a separate submission done by Leonard Crestez
whom we thank for his efforts getting an implementation of RFC5925 submitted
for review upstream [2]. This is an independent implementation that makes
different design decisions.

For example, we chose a similar design to the TCP-MD5SIG implementation and
used setsockopt()s to program per-socket keys, avoiding the extra complexity
of managing a centralized key database in the kernel. A centralized database
in the kernel has dubious benefits since it doesn’t eliminate per-socket
setsockopts needed to specify which sockets need TCP-AO and what are the
currently preferred keys. It also complicates traffic key caching and
preventing deletion of in-use keys.

In this implementation, a centralized database of keys can be thought of
as living in user space and user applications would have to program those
keys on matching sockets. On the server side, the user application programs
keys (MKTS in TCP-AO nomenclature) on the listening socket for all peers that
are expected to connect. Prefix matching on the peer address is supported.
When a peer issues a successful connect, all the MKTs matching the IP address
of the peer are copied to the newly created socket. On the active side,
when a connect() is issued all MKTs that do not match the peer are deleted
from the socket since they will never match the peer. This implementation
uses three setsockopt()s for adding, deleting and modifying keys on a socket.
All three setsockopt()s have extensive sanity checks that prevent
inconsistencies in the keys on a given socket. A getsockopt() is provided
to get key information from any given socket.

Few things to note about this implementation:
- Traffic keys are cached for established connections avoiding the cost of
  such calculation for each packet received or sent.
- Great care has been taken to avoid deleting in-use MKTs
  as required by the RFC.
- Any crypto algorithm supported by the Linux kernel can be used
  to calculate packet hashes.
- Fastopen works with TCP-AO but hasn’t been tested extensively.
- Tested for interop with other major networking vendors (on linux-4.19),
  including testing for key rotation and long lived connections.

There are a couple of limitations that we’re aware of, including (but not
limited to) the following:
- setsockopt(TCP_REPAIR) not supported yet
- IPv4-mapped-IPv6 addresses not tested
- static key not implemented yet
- CONFIG_TCP_AO depends on CONFIG_TCP_MD5SIG
- A small window for a race condition exists between accept and key
  adding/deletion on a listening socket but can be easily overcome by using
  the getsockopt() to make sure the right keys are there on a newly accepted
  connection
- Key matching by TCP port numbers, peer ranges, asterisks is unsupported
  as it’s unlikely to be useful

[1]: https://lore.kernel.org/all/20220726201600.1715505-1-dima@arista.com/
[2]: https://lore.kernel.org/all/cover.1658815925.git.cdleonard@gmail.com/

Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Bob Gilligan <gilligan@arista.com>
Cc: David Ahern <dsahern@kernel.org>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Dmitry Safonov <0x7f454c46@gmail.com>
Cc: Eric Biggers <ebiggers@kernel.org>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Francesco Ruggeri <fruggeri@arista.com>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
Cc: Ivan Delalande <colona@arista.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Leonard Crestez <cdleonard@gmail.com>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Salam Noureddine <noureddine@arista.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: netdev@vger.kernel.org
Cc: linux-crypto@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

Dmitry Safonov (31):
  crypto: Introduce crypto_pool
  crypto_pool: Add crypto_pool_reserve_scratch()
  net/tcp: Separate tcp_md5sig_info allocation into tcp_md5sig_info_add()
  net/tcp: Disable TCP-MD5 static key on tcp_md5sig_info destruction
  net/tcp: Use crypto_pool for TCP-MD5
  net/ipv6: sr: Switch to using crypto_pool
  tcp: Add TCP-AO config and structures
  net/tcp: Introduce TCP_AO setsockopt()s
  net/tcp: Prevent TCP-MD5 with TCP-AO being set
  net/tcp: Calculate TCP-AO traffic keys
  net/tcp: Add TCP-AO sign to outgoing packets
  net/tcp: Add tcp_parse_auth_options()
  net/tcp: Add AO sign to RST packets
  net/tcp: Add TCP-AO sign to twsk
  net/tcp: Wire TCP-AO to request sockets
  net/tcp: Sign SYN-ACK segments with TCP-AO
  net/tcp: Verify inbound TCP-AO signed segments
  net/tcp: Add TCP-AO segments counters
  net/tcp: Add TCP-AO SNE support
  net/tcp: Add tcp_hash_fail() ratelimited logs
  net/tcp: Ignore specific ICMPs for TCP-AO connections
  net/tcp: Add option for TCP-AO to (not) hash header
  net/tcp: Add getsockopt(TCP_AO_GET)
  net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs)
  selftests/net: Add TCP-AO library
  selftests/net: Verify that TCP-AO complies with ignoring ICMPs
  selftest/net: Add TCP-AO ICMPs accept test
  selftest/tcp-ao: Add a test for MKT matching
  selftest/tcp-ao: Add test for TCP-AO add setsockopt() command
  selftests/tcp-ao: Add TCP-AO + TCP-MD5 + no sign listen socket tests
  selftests/aolib: Add test/benchmark for removing MKTs

 crypto/Kconfig                                |   12 +
 crypto/Makefile                               |    1 +
 crypto/crypto_pool.c                          |  323 +++
 include/crypto/pool.h                         |   33 +
 include/linux/sockptr.h                       |   23 +
 include/linux/tcp.h                           |   24 +
 include/net/dropreason.h                      |   25 +
 include/net/seg6_hmac.h                       |    7 -
 include/net/tcp.h                             |  193 +-
 include/net/tcp_ao.h                          |  283 +++
 include/uapi/linux/snmp.h                     |    5 +
 include/uapi/linux/tcp.h                      |   62 +
 net/ipv4/Kconfig                              |   15 +-
 net/ipv4/Makefile                             |    1 +
 net/ipv4/proc.c                               |    5 +
 net/ipv4/tcp.c                                |  191 +-
 net/ipv4/tcp_ao.c                             | 1939 +++++++++++++++++
 net/ipv4/tcp_input.c                          |   94 +-
 net/ipv4/tcp_ipv4.c                           |  385 +++-
 net/ipv4/tcp_minisocks.c                      |   37 +-
 net/ipv4/tcp_output.c                         |  188 +-
 net/ipv6/Kconfig                              |    2 +-
 net/ipv6/Makefile                             |    1 +
 net/ipv6/seg6.c                               |    3 -
 net/ipv6/seg6_hmac.c                          |  204 +-
 net/ipv6/tcp_ao.c                             |  151 ++
 net/ipv6/tcp_ipv6.c                           |  327 ++-
 tools/testing/selftests/Makefile              |    1 +
 tools/testing/selftests/net/tcp_ao/.gitignore |    2 +
 tools/testing/selftests/net/tcp_ao/Makefile   |   50 +
 .../selftests/net/tcp_ao/bench-lookups.c      |  403 ++++
 .../selftests/net/tcp_ao/connect-deny.c       |  217 ++
 tools/testing/selftests/net/tcp_ao/connect.c  |   81 +
 .../selftests/net/tcp_ao/icmps-accept.c       |    1 +
 .../selftests/net/tcp_ao/icmps-discard.c      |  447 ++++
 .../testing/selftests/net/tcp_ao/lib/aolib.h  |  333 +++
 .../selftests/net/tcp_ao/lib/netlink.c        |  341 +++
 tools/testing/selftests/net/tcp_ao/lib/proc.c |  267 +++
 .../testing/selftests/net/tcp_ao/lib/setup.c  |  297 +++
 tools/testing/selftests/net/tcp_ao/lib/sock.c |  294 +++
 .../testing/selftests/net/tcp_ao/lib/utils.c  |   30 +
 .../selftests/net/tcp_ao/setsockopt-closed.c  |  191 ++
 .../selftests/net/tcp_ao/unsigned-md5.c       |  483 ++++
 43 files changed, 7516 insertions(+), 456 deletions(-)
 create mode 100644 crypto/crypto_pool.c
 create mode 100644 include/crypto/pool.h
 create mode 100644 include/net/tcp_ao.h
 create mode 100644 net/ipv4/tcp_ao.c
 create mode 100644 net/ipv6/tcp_ao.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/.gitignore
 create mode 100644 tools/testing/selftests/net/tcp_ao/Makefile
 create mode 100644 tools/testing/selftests/net/tcp_ao/bench-lookups.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/connect-deny.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/connect.c
 create mode 120000 tools/testing/selftests/net/tcp_ao/icmps-accept.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/icmps-discard.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/aolib.h
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/netlink.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/proc.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/setup.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/sock.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/utils.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/setsockopt-closed.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/unsigned-md5.c


base-commit: e34cfee65ec891a319ce79797dda18083af33a76
-- 
2.37.2


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCH 01/31] crypto: Introduce crypto_pool
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 02/31] crypto_pool: Add crypto_pool_reserve_scratch() Dmitry Safonov
                   ` (30 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Introduce a per-CPU pool of async crypto requests that can be used
in bh-disabled contexts (designed with net RX/TX softirqs as users in
mind). Allocation can sleep and is a slow-path.
Initial implementation has only ahash as a backend and a fix-sized array
of possible algorithms used in parallel.

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 crypto/Kconfig        |   6 +
 crypto/Makefile       |   1 +
 crypto/crypto_pool.c  | 291 ++++++++++++++++++++++++++++++++++++++++++
 include/crypto/pool.h |  34 +++++
 4 files changed, 332 insertions(+)
 create mode 100644 crypto/crypto_pool.c
 create mode 100644 include/crypto/pool.h

diff --git a/crypto/Kconfig b/crypto/Kconfig
index bb427a835e44..aeddaa3dcc77 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -2128,6 +2128,12 @@ config CRYPTO_STATS
 config CRYPTO_HASH_INFO
 	bool
 
+config CRYPTO_POOL
+	tristate "Per-CPU crypto pool"
+	default n
+	help
+	  Per-CPU pool of crypto requests ready for usage in atomic contexts.
+
 source "drivers/crypto/Kconfig"
 source "crypto/asymmetric_keys/Kconfig"
 source "certs/Kconfig"
diff --git a/crypto/Makefile b/crypto/Makefile
index a6f94e04e1da..b0f54cb9cea1 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_CRYPTO_ACOMP2) += crypto_acompress.o
 cryptomgr-y := algboss.o testmgr.o
 
 obj-$(CONFIG_CRYPTO_MANAGER2) += cryptomgr.o
+obj-$(CONFIG_CRYPTO_POOL) += crypto_pool.o
 obj-$(CONFIG_CRYPTO_USER) += crypto_user.o
 crypto_user-y := crypto_user_base.o
 crypto_user-$(CONFIG_CRYPTO_STATS) += crypto_user_stat.o
diff --git a/crypto/crypto_pool.c b/crypto/crypto_pool.c
new file mode 100644
index 000000000000..a5b6e6cf818a
--- /dev/null
+++ b/crypto/crypto_pool.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <crypto/pool.h>
+#include <linux/kref.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/percpu.h>
+#include <linux/workqueue.h>
+
+static unsigned long scratch_size = DEFAULT_CRYPTO_POOL_SCRATCH_SZ;
+static DEFINE_PER_CPU(void *, crypto_pool_scratch);
+
+struct crypto_pool_entry {
+	struct ahash_request * __percpu *req;
+	const char			*alg;
+	struct kref			kref;
+	bool				needs_key;
+};
+
+#define CPOOL_SIZE (PAGE_SIZE/sizeof(struct crypto_pool_entry))
+static struct crypto_pool_entry cpool[CPOOL_SIZE];
+static int last_allocated;
+static DEFINE_MUTEX(cpool_mutex);
+
+static int crypto_pool_scratch_alloc(void)
+{
+	int cpu;
+
+	lockdep_assert_held(&cpool_mutex);
+
+	for_each_possible_cpu(cpu) {
+		void *scratch = per_cpu(crypto_pool_scratch, cpu);
+
+		if (scratch)
+			continue;
+
+		scratch = kmalloc_node(scratch_size, GFP_KERNEL,
+				       cpu_to_node(cpu));
+		if (!scratch)
+			return -ENOMEM;
+		per_cpu(crypto_pool_scratch, cpu) = scratch;
+	}
+	return 0;
+}
+
+static void crypto_pool_scratch_free(void)
+{
+	int cpu;
+
+	lockdep_assert_held(&cpool_mutex);
+
+	for_each_possible_cpu(cpu) {
+		void *scratch = per_cpu(crypto_pool_scratch, cpu);
+
+		if (!scratch)
+			continue;
+		per_cpu(crypto_pool_scratch, cpu) = NULL;
+		kfree(scratch);
+	}
+}
+
+static int __cpool_alloc_ahash(struct crypto_pool_entry *e, const char *alg)
+{
+	struct crypto_ahash *hash;
+	int cpu, ret = -ENOMEM;
+
+	e->alg = kstrdup(alg, GFP_KERNEL);
+	if (!e->alg)
+		return -ENOMEM;
+
+	e->req = alloc_percpu(struct ahash_request *);
+	if (!e->req)
+		goto out_free_alg;
+
+	hash = crypto_alloc_ahash(alg, 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(hash)) {
+		ret = PTR_ERR(hash);
+		goto out_free_req;
+	}
+
+	/* If hash has .setkey(), allocate ahash per-cpu, not only request */
+	e->needs_key = crypto_ahash_get_flags(hash) & CRYPTO_TFM_NEED_KEY;
+
+	for_each_possible_cpu(cpu) {
+		struct ahash_request *req;
+
+		if (!hash)
+			hash = crypto_alloc_ahash(alg, 0, CRYPTO_ALG_ASYNC);
+		if (IS_ERR(hash))
+			goto out_free;
+
+		req = ahash_request_alloc(hash, GFP_KERNEL);
+		if (!req)
+			goto out_free;
+
+		ahash_request_set_callback(req, 0, NULL, NULL);
+
+		*per_cpu_ptr(e->req, cpu) = req;
+
+		if (e->needs_key)
+			hash = NULL;
+	}
+	kref_init(&e->kref);
+	return 0;
+
+out_free:
+	if (!IS_ERR_OR_NULL(hash) && e->needs_key)
+		crypto_free_ahash(hash);
+
+	for_each_possible_cpu(cpu) {
+		if (*per_cpu_ptr(e->req, cpu) == NULL)
+			break;
+		hash = crypto_ahash_reqtfm(*per_cpu_ptr(e->req, cpu));
+		ahash_request_free(*per_cpu_ptr(e->req, cpu));
+		if (e->needs_key) {
+			crypto_free_ahash(hash);
+			hash = NULL;
+		}
+	}
+
+	if (hash)
+		crypto_free_ahash(hash);
+out_free_req:
+	free_percpu(e->req);
+out_free_alg:
+	kfree(e->alg);
+	e->alg = NULL;
+	return ret;
+}
+
+/**
+ * crypto_pool_alloc_ahash - allocates pool for ahash requests
+ * @alg: name of async hash algorithm
+ */
+int crypto_pool_alloc_ahash(const char *alg)
+{
+	unsigned int i;
+	int err;
+
+	/* slow-path */
+	mutex_lock(&cpool_mutex);
+	err = crypto_pool_scratch_alloc();
+	if (err)
+		goto out;
+
+	for (i = 0; i < last_allocated; i++) {
+		if (cpool[i].alg && !strcmp(cpool[i].alg, alg)) {
+			if (kref_read(&cpool[i].kref) > 0) {
+				kref_get(&cpool[i].kref);
+				goto out;
+			} else {
+				break;
+			}
+		}
+	}
+
+	for (i = 0; i < last_allocated; i++) {
+		if (!cpool[i].alg)
+			break;
+	}
+	if (i >= CPOOL_SIZE) {
+		err = -ENOSPC;
+		goto out;
+	}
+
+	err = __cpool_alloc_ahash(&cpool[i], alg);
+	if (!err && last_allocated <= i)
+		last_allocated++;
+out:
+	mutex_unlock(&cpool_mutex);
+	return err ?: (int)i;
+}
+EXPORT_SYMBOL_GPL(crypto_pool_alloc_ahash);
+
+static void __cpool_free_entry(struct crypto_pool_entry *e)
+{
+	struct crypto_ahash *hash = NULL;
+	int cpu;
+
+	for_each_possible_cpu(cpu) {
+		if (*per_cpu_ptr(e->req, cpu) == NULL)
+			continue;
+
+		hash = crypto_ahash_reqtfm(*per_cpu_ptr(e->req, cpu));
+		ahash_request_free(*per_cpu_ptr(e->req, cpu));
+		if (e->needs_key) {
+			crypto_free_ahash(hash);
+			hash = NULL;
+		}
+	}
+	if (hash)
+		crypto_free_ahash(hash);
+	free_percpu(e->req);
+	kfree(e->alg);
+	memset(e, 0, sizeof(*e));
+}
+
+static void cpool_cleanup_work_cb(struct work_struct *work)
+{
+	unsigned int i;
+	bool free_scratch = true;
+
+	mutex_lock(&cpool_mutex);
+	for (i = 0; i < last_allocated; i++) {
+		if (kref_read(&cpool[i].kref) > 0) {
+			free_scratch = false;
+			continue;
+		}
+		if (!cpool[i].alg)
+			continue;
+		__cpool_free_entry(&cpool[i]);
+	}
+	if (free_scratch)
+		crypto_pool_scratch_free();
+	mutex_unlock(&cpool_mutex);
+}
+
+static DECLARE_WORK(cpool_cleanup_work, cpool_cleanup_work_cb);
+static void cpool_schedule_cleanup(struct kref *kref)
+{
+	schedule_work(&cpool_cleanup_work);
+}
+
+/**
+ * crypto_pool_release - decreases number of users for a pool. If it was
+ * the last user of the pool, releases any memory that was consumed.
+ * @id: crypto_pool that was previously allocated by crypto_pool_alloc_ahash()
+ */
+void crypto_pool_release(unsigned int id)
+{
+	if (WARN_ON_ONCE(id > last_allocated || !cpool[id].alg))
+		return;
+
+	/* slow-path */
+	kref_put(&cpool[id].kref, cpool_schedule_cleanup);
+}
+EXPORT_SYMBOL_GPL(crypto_pool_release);
+
+/**
+ * crypto_pool_add - increases number of users (refcounter) for a pool
+ * @id: crypto_pool that was previously allocated by crypto_pool_alloc_ahash()
+ */
+void crypto_pool_add(unsigned int id)
+{
+	if (WARN_ON_ONCE(id > last_allocated || !cpool[id].alg))
+		return;
+	kref_get(&cpool[id].kref);
+}
+EXPORT_SYMBOL_GPL(crypto_pool_add);
+
+/**
+ * crypto_pool_get - disable bh and start using crypto_pool
+ * @id: crypto_pool that was previously allocated by crypto_pool_alloc_ahash()
+ * @c: returned crypto_pool for usage (uninitialized on failure)
+ */
+int crypto_pool_get(unsigned int id, struct crypto_pool *c)
+{
+	struct crypto_pool_ahash *ret = (struct crypto_pool_ahash *)c;
+
+	local_bh_disable();
+	if (WARN_ON_ONCE(id > last_allocated || !cpool[id].alg)) {
+		local_bh_enable();
+		return -EINVAL;
+	}
+	ret->req = *this_cpu_ptr(cpool[id].req);
+	ret->base.scratch = this_cpu_read(crypto_pool_scratch);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_pool_get);
+
+/**
+ * crypto_pool_algo - return algorithm of crypto_pool
+ * @id: crypto_pool that was previously allocated by crypto_pool_alloc_ahash()
+ * @buf: buffer to return name of algorithm
+ * @buf_len: size of @buf
+ */
+size_t crypto_pool_algo(unsigned int id, char *buf, size_t buf_len)
+{
+	size_t ret = 0;
+
+	/* slow-path */
+	mutex_lock(&cpool_mutex);
+	if (cpool[id].alg)
+		ret = strscpy(buf, cpool[id].alg, buf_len);
+	mutex_unlock(&cpool_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(crypto_pool_algo);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Per-CPU pool of crypto requests");
diff --git a/include/crypto/pool.h b/include/crypto/pool.h
new file mode 100644
index 000000000000..2c61aa45faff
--- /dev/null
+++ b/include/crypto/pool.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _CRYPTO_POOL_H
+#define _CRYPTO_POOL_H
+
+#include <crypto/hash.h>
+
+#define DEFAULT_CRYPTO_POOL_SCRATCH_SZ	128
+
+struct crypto_pool {
+	void *scratch;
+};
+
+/*
+ * struct crypto_pool_ahash - per-CPU pool of ahash_requests
+ * @base: common members that can be used by any async crypto ops
+ * @req: pre-allocated ahash request
+ */
+struct crypto_pool_ahash {
+	struct crypto_pool base;
+	struct ahash_request *req;
+};
+
+int crypto_pool_alloc_ahash(const char *alg);
+void crypto_pool_add(unsigned int id);
+void crypto_pool_release(unsigned int id);
+
+int crypto_pool_get(unsigned int id, struct crypto_pool *c);
+static inline void crypto_pool_put(void)
+{
+	local_bh_enable();
+}
+size_t crypto_pool_algo(unsigned int id, char *buf, size_t buf_len);
+
+#endif /* _CRYPTO_POOL_H */
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 02/31] crypto_pool: Add crypto_pool_reserve_scratch()
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 01/31] crypto: Introduce crypto_pool Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 03/31] net/tcp: Separate tcp_md5sig_info allocation into tcp_md5sig_info_add() Dmitry Safonov
                   ` (29 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Instead of having build-time hardcoded constant, reallocate scratch
area, if needed by user. Different algos, different users may need
different size of temp per-CPU buffer. Only up-sizing supported for
simplicity.

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 crypto/Kconfig        |  6 ++++
 crypto/crypto_pool.c  | 68 +++++++++++++++++++++++++++++++------------
 include/crypto/pool.h |  3 +-
 3 files changed, 57 insertions(+), 20 deletions(-)

diff --git a/crypto/Kconfig b/crypto/Kconfig
index aeddaa3dcc77..e5865be483be 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -2134,6 +2134,12 @@ config CRYPTO_POOL
 	help
 	  Per-CPU pool of crypto requests ready for usage in atomic contexts.
 
+config CRYPTO_POOL_DEFAULT_SCRATCH_SIZE
+	hex "Per-CPU default scratch area size"
+	depends on CRYPTO_POOL
+	default 0x100
+	range 0x100 0x10000
+
 source "drivers/crypto/Kconfig"
 source "crypto/asymmetric_keys/Kconfig"
 source "certs/Kconfig"
diff --git a/crypto/crypto_pool.c b/crypto/crypto_pool.c
index a5b6e6cf818a..9e2ac4eb1138 100644
--- a/crypto/crypto_pool.c
+++ b/crypto/crypto_pool.c
@@ -1,13 +1,14 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #include <crypto/pool.h>
+#include <linux/cpu.h>
 #include <linux/kref.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/percpu.h>
 #include <linux/workqueue.h>
 
-static unsigned long scratch_size = DEFAULT_CRYPTO_POOL_SCRATCH_SZ;
+static unsigned long scratch_size = CONFIG_CRYPTO_POOL_DEFAULT_SCRATCH_SIZE;
 static DEFINE_PER_CPU(void *, crypto_pool_scratch);
 
 struct crypto_pool_entry {
@@ -19,29 +20,64 @@ struct crypto_pool_entry {
 
 #define CPOOL_SIZE (PAGE_SIZE/sizeof(struct crypto_pool_entry))
 static struct crypto_pool_entry cpool[CPOOL_SIZE];
-static int last_allocated;
+static unsigned int last_allocated;
 static DEFINE_MUTEX(cpool_mutex);
 
-static int crypto_pool_scratch_alloc(void)
+static void __set_scratch(void *scratch)
 {
-	int cpu;
+	kfree(this_cpu_read(crypto_pool_scratch));
+	this_cpu_write(crypto_pool_scratch, scratch);
+}
 
-	lockdep_assert_held(&cpool_mutex);
+/* Slow-path */
+/**
+ * crypto_pool_reserve_scratch - re-allocates scratch buffer, slow-path
+ * @size: request size for the scratch/temp buffer
+ */
+int crypto_pool_reserve_scratch(unsigned long size)
+{
+	int cpu, err = 0;
 
+	mutex_lock(&cpool_mutex);
+	if (size == scratch_size) {
+		for_each_possible_cpu(cpu) {
+			if (per_cpu(crypto_pool_scratch, cpu))
+				continue;
+			goto allocate_scratch;
+		}
+		mutex_unlock(&cpool_mutex);
+		return 0;
+	}
+allocate_scratch:
+	size = max(size, scratch_size);
+	cpus_read_lock();
 	for_each_possible_cpu(cpu) {
-		void *scratch = per_cpu(crypto_pool_scratch, cpu);
+		void *scratch;
 
-		if (scratch)
-			continue;
+		scratch = kmalloc_node(size, GFP_KERNEL, cpu_to_node(cpu));
+		if (!scratch) {
+			err = -ENOMEM;
+			break;
+		}
 
-		scratch = kmalloc_node(scratch_size, GFP_KERNEL,
-				       cpu_to_node(cpu));
-		if (!scratch)
-			return -ENOMEM;
-		per_cpu(crypto_pool_scratch, cpu) = scratch;
+		if (!cpu_online(cpu)) {
+			kfree(per_cpu(crypto_pool_scratch, cpu));
+			per_cpu(crypto_pool_scratch, cpu) = scratch;
+			continue;
+		}
+		err = smp_call_function_single(cpu, __set_scratch, scratch, 1);
+		if (err) {
+			kfree(scratch);
+			break;
+		}
 	}
-	return 0;
+
+	cpus_read_unlock();
+	scratch_size = size;
+	mutex_unlock(&cpool_mutex);
+	return err;
 }
+EXPORT_SYMBOL_GPL(crypto_pool_reserve_scratch);
 
 static void crypto_pool_scratch_free(void)
 {
@@ -139,10 +175,6 @@ int crypto_pool_alloc_ahash(const char *alg)
 
 	/* slow-path */
 	mutex_lock(&cpool_mutex);
-	err = crypto_pool_scratch_alloc();
-	if (err)
-		goto out;
-
 	for (i = 0; i < last_allocated; i++) {
 		if (cpool[i].alg && !strcmp(cpool[i].alg, alg)) {
 			if (kref_read(&cpool[i].kref) > 0) {
diff --git a/include/crypto/pool.h b/include/crypto/pool.h
index 2c61aa45faff..c7d817860cc3 100644
--- a/include/crypto/pool.h
+++ b/include/crypto/pool.h
@@ -4,8 +4,6 @@
 
 #include <crypto/hash.h>
 
-#define DEFAULT_CRYPTO_POOL_SCRATCH_SZ	128
-
 struct crypto_pool {
 	void *scratch;
 };
@@ -20,6 +18,7 @@ struct crypto_pool_ahash {
 	struct ahash_request *req;
 };
 
+int crypto_pool_reserve_scratch(unsigned long size);
 int crypto_pool_alloc_ahash(const char *alg);
 void crypto_pool_add(unsigned int id);
 void crypto_pool_release(unsigned int id);
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 03/31] net/tcp: Separate tcp_md5sig_info allocation into tcp_md5sig_info_add()
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 01/31] crypto: Introduce crypto_pool Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 02/31] crypto_pool: Add crypto_pool_reserve_scratch() Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 04/31] net/tcp: Disable TCP-MD5 static key on tcp_md5sig_info destruction Dmitry Safonov
                   ` (28 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Add a helper to allocate tcp_md5sig_info, that will help later to
do/allocate things when info allocated, once per socket.

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 net/ipv4/tcp_ipv4.c | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 0c83780dc9bf..55e4092209a5 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1152,6 +1152,24 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
 }
 EXPORT_SYMBOL(tcp_v4_md5_lookup);
 
+static int tcp_md5sig_info_add(struct sock *sk, gfp_t gfp)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct tcp_md5sig_info *md5sig;
+
+	if (rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk)))
+		return 0;
+
+	md5sig = kmalloc(sizeof(*md5sig), gfp);
+	if (!md5sig)
+		return -ENOMEM;
+
+	sk_gso_disable(sk);
+	INIT_HLIST_HEAD(&md5sig->head);
+	rcu_assign_pointer(tp->md5sig_info, md5sig);
+	return 0;
+}
+
 /* This can be called on a newly created socket, from other files */
 int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
 		   int family, u8 prefixlen, int l3index, u8 flags,
@@ -1182,17 +1200,11 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
 		return 0;
 	}
 
+	if (tcp_md5sig_info_add(sk, gfp))
+		return -ENOMEM;
+
 	md5sig = rcu_dereference_protected(tp->md5sig_info,
 					   lockdep_sock_is_held(sk));
-	if (!md5sig) {
-		md5sig = kmalloc(sizeof(*md5sig), gfp);
-		if (!md5sig)
-			return -ENOMEM;
-
-		sk_gso_disable(sk);
-		INIT_HLIST_HEAD(&md5sig->head);
-		rcu_assign_pointer(tp->md5sig_info, md5sig);
-	}
 
 	key = sock_kmalloc(sk, sizeof(*key), gfp | __GFP_ZERO);
 	if (!key)
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 04/31] net/tcp: Disable TCP-MD5 static key on tcp_md5sig_info destruction
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (2 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 03/31] net/tcp: Separate tcp_md5sig_info allocation into tcp_md5sig_info_add() Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 05/31] net/tcp: Use crypto_pool for TCP-MD5 Dmitry Safonov
                   ` (27 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

To do that, separate two scenarios:
- where it's the first MD5 key on the system, which means that enabling
  of the static key may need to sleep;
- copying of an existing key from a listening socket to the request
  socket upon receiving a signed TCP segment, where static key was
  already enabled (when the key was added to the listening socket).

Now the life-time of the static branch for TCP-MD5 is until:
- last tcp_md5sig_info is destroyed
- last socket in time-wait state with MD5 key is closed.

Which means that after all sockets with TCP-MD5 keys are gone, the
system gets back the performance of disabled md5-key static branch.

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 include/net/tcp.h        | 10 ++++++---
 net/ipv4/tcp.c           |  5 +----
 net/ipv4/tcp_ipv4.c      | 45 +++++++++++++++++++++++++++++++---------
 net/ipv4/tcp_minisocks.c |  9 +++++---
 net/ipv4/tcp_output.c    |  4 ++--
 net/ipv6/tcp_ipv6.c      | 10 ++++-----
 6 files changed, 55 insertions(+), 28 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index d10962b9f0d0..831cd1e24687 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1665,7 +1665,11 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
 			const struct sock *sk, const struct sk_buff *skb);
 int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
 		   int family, u8 prefixlen, int l3index, u8 flags,
-		   const u8 *newkey, u8 newkeylen, gfp_t gfp);
+		   const u8 *newkey, u8 newkeylen);
+int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr,
+		     int family, u8 prefixlen, int l3index,
+		     struct tcp_md5sig_key *key);
+
 int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr,
 		   int family, u8 prefixlen, int l3index, u8 flags);
 struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
@@ -1673,7 +1677,7 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
 
 #ifdef CONFIG_TCP_MD5SIG
 #include <linux/jump_label.h>
-extern struct static_key_false tcp_md5_needed;
+extern struct static_key_false_deferred tcp_md5_needed;
 struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,
 					   const union tcp_md5_addr *addr,
 					   int family);
@@ -1681,7 +1685,7 @@ static inline struct tcp_md5sig_key *
 tcp_md5_do_lookup(const struct sock *sk, int l3index,
 		  const union tcp_md5_addr *addr, int family)
 {
-	if (!static_branch_unlikely(&tcp_md5_needed))
+	if (!static_branch_unlikely(&tcp_md5_needed.key))
 		return NULL;
 	return __tcp_md5_do_lookup(sk, l3index, addr, family);
 }
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 970e9a2cca4a..a4a171656d7d 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -4444,11 +4444,8 @@ bool tcp_alloc_md5sig_pool(void)
 	if (unlikely(!tcp_md5sig_pool_populated)) {
 		mutex_lock(&tcp_md5sig_mutex);
 
-		if (!tcp_md5sig_pool_populated) {
+		if (!tcp_md5sig_pool_populated)
 			__tcp_alloc_md5sig_pool();
-			if (tcp_md5sig_pool_populated)
-				static_branch_inc(&tcp_md5_needed);
-		}
 
 		mutex_unlock(&tcp_md5sig_mutex);
 	}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 55e4092209a5..72feb74706e6 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1044,7 +1044,7 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req)
  * We need to maintain these in the sk structure.
  */
 
-DEFINE_STATIC_KEY_FALSE(tcp_md5_needed);
+DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_md5_needed, HZ);
 EXPORT_SYMBOL(tcp_md5_needed);
 
 static bool better_md5_match(struct tcp_md5sig_key *old, struct tcp_md5sig_key *new)
@@ -1171,9 +1171,9 @@ static int tcp_md5sig_info_add(struct sock *sk, gfp_t gfp)
 }
 
 /* This can be called on a newly created socket, from other files */
-int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
-		   int family, u8 prefixlen, int l3index, u8 flags,
-		   const u8 *newkey, u8 newkeylen, gfp_t gfp)
+static int __tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
+			    int family, u8 prefixlen, int l3index, u8 flags,
+			    const u8 *newkey, u8 newkeylen, gfp_t gfp)
 {
 	/* Add Key to the list */
 	struct tcp_md5sig_key *key;
@@ -1200,9 +1200,6 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
 		return 0;
 	}
 
-	if (tcp_md5sig_info_add(sk, gfp))
-		return -ENOMEM;
-
 	md5sig = rcu_dereference_protected(tp->md5sig_info,
 					   lockdep_sock_is_held(sk));
 
@@ -1226,8 +1223,36 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
 	hlist_add_head_rcu(&key->node, &md5sig->head);
 	return 0;
 }
+
+int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
+		   int family, u8 prefixlen, int l3index, u8 flags,
+		   const u8 *newkey, u8 newkeylen)
+{
+	if (tcp_md5sig_info_add(sk, GFP_KERNEL))
+		return -ENOMEM;
+
+	static_branch_inc(&tcp_md5_needed.key);
+
+	return __tcp_md5_do_add(sk, addr, family, prefixlen, l3index, flags,
+				newkey, newkeylen, GFP_KERNEL);
+}
 EXPORT_SYMBOL(tcp_md5_do_add);
 
+int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr,
+		     int family, u8 prefixlen, int l3index,
+		     struct tcp_md5sig_key *key)
+{
+	if (tcp_md5sig_info_add(sk, sk_gfp_mask(sk, GFP_ATOMIC)))
+		return -ENOMEM;
+
+	atomic_inc(&tcp_md5_needed.key.key.enabled);
+
+	return __tcp_md5_do_add(sk, addr, family, prefixlen, l3index,
+				key->flags, key->key, key->keylen,
+				sk_gfp_mask(sk, GFP_ATOMIC));
+}
+EXPORT_SYMBOL(tcp_md5_key_copy);
+
 int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family,
 		   u8 prefixlen, int l3index, u8 flags)
 {
@@ -1314,7 +1339,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,
 		return -EINVAL;
 
 	return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags,
-			      cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
+			      cmd.tcpm_key, cmd.tcpm_keylen);
 }
 
 static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp,
@@ -1571,8 +1596,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
 		 * memory, then we end up not copying the key
 		 * across. Shucks.
 		 */
-		tcp_md5_do_add(newsk, addr, AF_INET, 32, l3index, key->flags,
-			       key->key, key->keylen, GFP_ATOMIC);
+		tcp_md5_key_copy(newsk, addr, AF_INET, 32, l3index, key);
 		sk_gso_disable(newsk);
 	}
 #endif
@@ -2260,6 +2284,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
 		tcp_clear_md5_list(sk);
 		kfree_rcu(rcu_dereference_protected(tp->md5sig_info, 1), rcu);
 		tp->md5sig_info = NULL;
+		static_branch_slow_dec_deferred(&tcp_md5_needed);
 	}
 #endif
 
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index cb95d88497ae..5d475a45a478 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -291,13 +291,14 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
 		 */
 		do {
 			tcptw->tw_md5_key = NULL;
-			if (static_branch_unlikely(&tcp_md5_needed)) {
+			if (static_branch_unlikely(&tcp_md5_needed.key)) {
 				struct tcp_md5sig_key *key;
 
 				key = tp->af_specific->md5_lookup(sk, sk);
 				if (key) {
 					tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC);
 					BUG_ON(tcptw->tw_md5_key && !tcp_alloc_md5sig_pool());
+					atomic_inc(&tcp_md5_needed.key.key.enabled);
 				}
 			}
 		} while (0);
@@ -337,11 +338,13 @@ EXPORT_SYMBOL(tcp_time_wait);
 void tcp_twsk_destructor(struct sock *sk)
 {
 #ifdef CONFIG_TCP_MD5SIG
-	if (static_branch_unlikely(&tcp_md5_needed)) {
+	if (static_branch_unlikely(&tcp_md5_needed.key)) {
 		struct tcp_timewait_sock *twsk = tcp_twsk(sk);
 
-		if (twsk->tw_md5_key)
+		if (twsk->tw_md5_key) {
 			kfree_rcu(twsk->tw_md5_key, rcu);
+			static_branch_slow_dec_deferred(&tcp_md5_needed);
+		}
 	}
 #endif
 }
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 78b654ff421b..9e12845a8758 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -766,7 +766,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
 
 	*md5 = NULL;
 #ifdef CONFIG_TCP_MD5SIG
-	if (static_branch_unlikely(&tcp_md5_needed) &&
+	if (static_branch_unlikely(&tcp_md5_needed.key) &&
 	    rcu_access_pointer(tp->md5sig_info)) {
 		*md5 = tp->af_specific->md5_lookup(sk, sk);
 		if (*md5) {
@@ -922,7 +922,7 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
 
 	*md5 = NULL;
 #ifdef CONFIG_TCP_MD5SIG
-	if (static_branch_unlikely(&tcp_md5_needed) &&
+	if (static_branch_unlikely(&tcp_md5_needed.key) &&
 	    rcu_access_pointer(tp->md5sig_info)) {
 		*md5 = tp->af_specific->md5_lookup(sk, sk);
 		if (*md5) {
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index e54eee80ce5f..cb891a71db0d 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -658,12 +658,11 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,
 	if (ipv6_addr_v4mapped(&sin6->sin6_addr))
 		return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
 				      AF_INET, prefixlen, l3index, flags,
-				      cmd.tcpm_key, cmd.tcpm_keylen,
-				      GFP_KERNEL);
+				      cmd.tcpm_key, cmd.tcpm_keylen);
 
 	return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr,
 			      AF_INET6, prefixlen, l3index, flags,
-			      cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
+			      cmd.tcpm_key, cmd.tcpm_keylen);
 }
 
 static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp,
@@ -1359,9 +1358,8 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
 		 * memory, then we end up not copying the key
 		 * across. Shucks.
 		 */
-		tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr,
-			       AF_INET6, 128, l3index, key->flags, key->key, key->keylen,
-			       sk_gfp_mask(sk, GFP_ATOMIC));
+		tcp_md5_key_copy(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr,
+			       AF_INET6, 128, l3index, key);
 	}
 #endif
 
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 05/31] net/tcp: Use crypto_pool for TCP-MD5
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (3 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 04/31] net/tcp: Disable TCP-MD5 static key on tcp_md5sig_info destruction Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 06/31] net/ipv6: sr: Switch to using crypto_pool Dmitry Safonov
                   ` (26 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Use crypto_pool API that was designed with tcp_md5sig_pool in mind.
The conversion to use crypto_pool will allow:
- to reuse ahash_request(s) for different users
- to allocate only one per-CPU scratch buffer rather than a new one for
  each user
- to have a common API for net/ users that need ahash on RX/TX fast path

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 include/net/tcp.h        | 22 +++------
 net/ipv4/Kconfig         |  2 +-
 net/ipv4/tcp.c           | 99 +++++++++++-----------------------------
 net/ipv4/tcp_ipv4.c      | 90 +++++++++++++++++++++---------------
 net/ipv4/tcp_minisocks.c | 22 +++++++--
 net/ipv6/tcp_ipv6.c      | 53 ++++++++++-----------
 6 files changed, 128 insertions(+), 160 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index 831cd1e24687..75bb817928c1 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1654,12 +1654,6 @@ union tcp_md5sum_block {
 #endif
 };
 
-/* - pool: digest algorithm, hash description and scratch buffer */
-struct tcp_md5sig_pool {
-	struct ahash_request	*md5_req;
-	void			*scratch;
-};
-
 /* - functions */
 int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
 			const struct sock *sk, const struct sk_buff *skb);
@@ -1715,17 +1709,15 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 #define tcp_twsk_md5_key(twsk)	NULL
 #endif
 
-bool tcp_alloc_md5sig_pool(void);
-
-struct tcp_md5sig_pool *tcp_get_md5sig_pool(void);
-static inline void tcp_put_md5sig_pool(void)
-{
-	local_bh_enable();
-}
+struct crypto_pool_ahash;
+int tcp_md5_alloc_crypto_pool(void);
+void tcp_md5_release_crypto_pool(void);
+void tcp_md5_add_crypto_pool(void);
+extern int tcp_md5_crypto_pool_id;
 
-int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *,
+int tcp_md5_hash_skb_data(struct crypto_pool_ahash *, const struct sk_buff *,
 			  unsigned int header_len);
-int tcp_md5_hash_key(struct tcp_md5sig_pool *hp,
+int tcp_md5_hash_key(struct crypto_pool_ahash *hp,
 		     const struct tcp_md5sig_key *key);
 
 /* From tcp_fastopen.c */
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index e983bb0c5012..c341864e4398 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -733,7 +733,7 @@ config DEFAULT_TCP_CONG
 
 config TCP_MD5SIG
 	bool "TCP: MD5 Signature Option support (RFC2385)"
-	select CRYPTO
+	select CRYPTO_POOL
 	select CRYPTO_MD5
 	help
 	  RFC2385 specifies a method of giving MD5 protection to TCP sessions.
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index a4a171656d7d..7c54b47e848f 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -244,6 +244,7 @@
 #define pr_fmt(fmt) "TCP: " fmt
 
 #include <crypto/hash.h>
+#include <crypto/pool.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/types.h>
@@ -4395,92 +4396,45 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
 EXPORT_SYMBOL(tcp_getsockopt);
 
 #ifdef CONFIG_TCP_MD5SIG
-static DEFINE_PER_CPU(struct tcp_md5sig_pool, tcp_md5sig_pool);
-static DEFINE_MUTEX(tcp_md5sig_mutex);
-static bool tcp_md5sig_pool_populated = false;
+int tcp_md5_crypto_pool_id = -1;
+EXPORT_SYMBOL(tcp_md5_crypto_pool_id);
 
-static void __tcp_alloc_md5sig_pool(void)
+int tcp_md5_alloc_crypto_pool(void)
 {
-	struct crypto_ahash *hash;
-	int cpu;
-
-	hash = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC);
-	if (IS_ERR(hash))
-		return;
-
-	for_each_possible_cpu(cpu) {
-		void *scratch = per_cpu(tcp_md5sig_pool, cpu).scratch;
-		struct ahash_request *req;
-
-		if (!scratch) {
-			scratch = kmalloc_node(sizeof(union tcp_md5sum_block) +
-					       sizeof(struct tcphdr),
-					       GFP_KERNEL,
-					       cpu_to_node(cpu));
-			if (!scratch)
-				return;
-			per_cpu(tcp_md5sig_pool, cpu).scratch = scratch;
-		}
-		if (per_cpu(tcp_md5sig_pool, cpu).md5_req)
-			continue;
-
-		req = ahash_request_alloc(hash, GFP_KERNEL);
-		if (!req)
-			return;
+	int ret;
 
-		ahash_request_set_callback(req, 0, NULL, NULL);
+	ret = crypto_pool_reserve_scratch(sizeof(union tcp_md5sum_block) +
+					  sizeof(struct tcphdr));
+	if (ret)
+		return ret;
 
-		per_cpu(tcp_md5sig_pool, cpu).md5_req = req;
+	ret = crypto_pool_alloc_ahash("md5");
+	if (ret >= 0) {
+		tcp_md5_crypto_pool_id = ret;
+		return 0;
 	}
-	/* before setting tcp_md5sig_pool_populated, we must commit all writes
-	 * to memory. See smp_rmb() in tcp_get_md5sig_pool()
-	 */
-	smp_wmb();
-	tcp_md5sig_pool_populated = true;
+	return ret;
 }
+EXPORT_SYMBOL(tcp_md5_alloc_crypto_pool);
 
-bool tcp_alloc_md5sig_pool(void)
+void tcp_md5_release_crypto_pool(void)
 {
-	if (unlikely(!tcp_md5sig_pool_populated)) {
-		mutex_lock(&tcp_md5sig_mutex);
-
-		if (!tcp_md5sig_pool_populated)
-			__tcp_alloc_md5sig_pool();
-
-		mutex_unlock(&tcp_md5sig_mutex);
-	}
-	return tcp_md5sig_pool_populated;
+	crypto_pool_release(tcp_md5_crypto_pool_id);
 }
-EXPORT_SYMBOL(tcp_alloc_md5sig_pool);
+EXPORT_SYMBOL(tcp_md5_release_crypto_pool);
 
-
-/**
- *	tcp_get_md5sig_pool - get md5sig_pool for this user
- *
- *	We use percpu structure, so if we succeed, we exit with preemption
- *	and BH disabled, to make sure another thread or softirq handling
- *	wont try to get same context.
- */
-struct tcp_md5sig_pool *tcp_get_md5sig_pool(void)
+void tcp_md5_add_crypto_pool(void)
 {
-	local_bh_disable();
-
-	if (tcp_md5sig_pool_populated) {
-		/* coupled with smp_wmb() in __tcp_alloc_md5sig_pool() */
-		smp_rmb();
-		return this_cpu_ptr(&tcp_md5sig_pool);
-	}
-	local_bh_enable();
-	return NULL;
+	crypto_pool_add(tcp_md5_crypto_pool_id);
 }
-EXPORT_SYMBOL(tcp_get_md5sig_pool);
+EXPORT_SYMBOL(tcp_md5_add_crypto_pool);
 
-int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp,
+int tcp_md5_hash_skb_data(struct crypto_pool_ahash *hp,
 			  const struct sk_buff *skb, unsigned int header_len)
 {
 	struct scatterlist sg;
 	const struct tcphdr *tp = tcp_hdr(skb);
-	struct ahash_request *req = hp->md5_req;
+	struct ahash_request *req = hp->req;
 	unsigned int i;
 	const unsigned int head_data_len = skb_headlen(skb) > header_len ?
 					   skb_headlen(skb) - header_len : 0;
@@ -4514,16 +4468,17 @@ int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp,
 }
 EXPORT_SYMBOL(tcp_md5_hash_skb_data);
 
-int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *key)
+int tcp_md5_hash_key(struct crypto_pool_ahash *hp,
+		     const struct tcp_md5sig_key *key)
 {
 	u8 keylen = READ_ONCE(key->keylen); /* paired with WRITE_ONCE() in tcp_md5_do_add */
 	struct scatterlist sg;
 
 	sg_init_one(&sg, key->key, keylen);
-	ahash_request_set_crypt(hp->md5_req, &sg, NULL, keylen);
+	ahash_request_set_crypt(hp->req, &sg, NULL, keylen);
 
 	/* We use data_race() because tcp_md5_do_add() might change key->key under us */
-	return data_race(crypto_ahash_update(hp->md5_req));
+	return data_race(crypto_ahash_update(hp->req));
 }
 EXPORT_SYMBOL(tcp_md5_hash_key);
 
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 72feb74706e6..6bafe7429902 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -79,6 +79,7 @@
 #include <linux/btf_ids.h>
 
 #include <crypto/hash.h>
+#include <crypto/pool.h>
 #include <linux/scatterlist.h>
 
 #include <trace/events/tcp.h>
@@ -1206,10 +1207,6 @@ static int __tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
 	key = sock_kmalloc(sk, sizeof(*key), gfp | __GFP_ZERO);
 	if (!key)
 		return -ENOMEM;
-	if (!tcp_alloc_md5sig_pool()) {
-		sock_kfree_s(sk, key, sizeof(*key));
-		return -ENOMEM;
-	}
 
 	memcpy(key->key, newkey, newkeylen);
 	key->keylen = newkeylen;
@@ -1228,8 +1225,13 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
 		   int family, u8 prefixlen, int l3index, u8 flags,
 		   const u8 *newkey, u8 newkeylen)
 {
-	if (tcp_md5sig_info_add(sk, GFP_KERNEL))
+	if (tcp_md5_alloc_crypto_pool())
+		return -ENOMEM;
+
+	if (tcp_md5sig_info_add(sk, GFP_KERNEL)) {
+		tcp_md5_release_crypto_pool();
 		return -ENOMEM;
+	}
 
 	static_branch_inc(&tcp_md5_needed.key);
 
@@ -1242,8 +1244,12 @@ int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr,
 		     int family, u8 prefixlen, int l3index,
 		     struct tcp_md5sig_key *key)
 {
-	if (tcp_md5sig_info_add(sk, sk_gfp_mask(sk, GFP_ATOMIC)))
+	tcp_md5_add_crypto_pool();
+
+	if (tcp_md5sig_info_add(sk, sk_gfp_mask(sk, GFP_ATOMIC))) {
+		tcp_md5_release_crypto_pool();
 		return -ENOMEM;
+	}
 
 	atomic_inc(&tcp_md5_needed.key.key.enabled);
 
@@ -1342,7 +1348,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,
 			      cmd.tcpm_key, cmd.tcpm_keylen);
 }
 
-static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp,
+static int tcp_v4_md5_hash_headers(struct crypto_pool_ahash *hp,
 				   __be32 daddr, __be32 saddr,
 				   const struct tcphdr *th, int nbytes)
 {
@@ -1350,7 +1356,7 @@ static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp,
 	struct scatterlist sg;
 	struct tcphdr *_th;
 
-	bp = hp->scratch;
+	bp = hp->base.scratch;
 	bp->saddr = saddr;
 	bp->daddr = daddr;
 	bp->pad = 0;
@@ -1362,37 +1368,34 @@ static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp,
 	_th->check = 0;
 
 	sg_init_one(&sg, bp, sizeof(*bp) + sizeof(*th));
-	ahash_request_set_crypt(hp->md5_req, &sg, NULL,
+	ahash_request_set_crypt(hp->req, &sg, NULL,
 				sizeof(*bp) + sizeof(*th));
-	return crypto_ahash_update(hp->md5_req);
+	return crypto_ahash_update(hp->req);
 }
 
 static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
 			       __be32 daddr, __be32 saddr, const struct tcphdr *th)
 {
-	struct tcp_md5sig_pool *hp;
-	struct ahash_request *req;
+	struct crypto_pool_ahash hp;
 
-	hp = tcp_get_md5sig_pool();
-	if (!hp)
+	if (crypto_pool_get(tcp_md5_crypto_pool_id, (struct crypto_pool *)&hp))
 		goto clear_hash_noput;
-	req = hp->md5_req;
 
-	if (crypto_ahash_init(req))
+	if (crypto_ahash_init(hp.req))
 		goto clear_hash;
-	if (tcp_v4_md5_hash_headers(hp, daddr, saddr, th, th->doff << 2))
+	if (tcp_v4_md5_hash_headers(&hp, daddr, saddr, th, th->doff << 2))
 		goto clear_hash;
-	if (tcp_md5_hash_key(hp, key))
+	if (tcp_md5_hash_key(&hp, key))
 		goto clear_hash;
-	ahash_request_set_crypt(req, NULL, md5_hash, 0);
-	if (crypto_ahash_final(req))
+	ahash_request_set_crypt(hp.req, NULL, md5_hash, 0);
+	if (crypto_ahash_final(hp.req))
 		goto clear_hash;
 
-	tcp_put_md5sig_pool();
+	crypto_pool_put();
 	return 0;
 
 clear_hash:
-	tcp_put_md5sig_pool();
+	crypto_pool_put();
 clear_hash_noput:
 	memset(md5_hash, 0, 16);
 	return 1;
@@ -1402,8 +1405,7 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
 			const struct sock *sk,
 			const struct sk_buff *skb)
 {
-	struct tcp_md5sig_pool *hp;
-	struct ahash_request *req;
+	struct crypto_pool_ahash hp;
 	const struct tcphdr *th = tcp_hdr(skb);
 	__be32 saddr, daddr;
 
@@ -1416,29 +1418,27 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
 		daddr = iph->daddr;
 	}
 
-	hp = tcp_get_md5sig_pool();
-	if (!hp)
+	if (crypto_pool_get(tcp_md5_crypto_pool_id, (struct crypto_pool *)&hp))
 		goto clear_hash_noput;
-	req = hp->md5_req;
 
-	if (crypto_ahash_init(req))
+	if (crypto_ahash_init(hp.req))
 		goto clear_hash;
 
-	if (tcp_v4_md5_hash_headers(hp, daddr, saddr, th, skb->len))
+	if (tcp_v4_md5_hash_headers(&hp, daddr, saddr, th, skb->len))
 		goto clear_hash;
-	if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2))
+	if (tcp_md5_hash_skb_data(&hp, skb, th->doff << 2))
 		goto clear_hash;
-	if (tcp_md5_hash_key(hp, key))
+	if (tcp_md5_hash_key(&hp, key))
 		goto clear_hash;
-	ahash_request_set_crypt(req, NULL, md5_hash, 0);
-	if (crypto_ahash_final(req))
+	ahash_request_set_crypt(hp.req, NULL, md5_hash, 0);
+	if (crypto_ahash_final(hp.req))
 		goto clear_hash;
 
-	tcp_put_md5sig_pool();
+	crypto_pool_put();
 	return 0;
 
 clear_hash:
-	tcp_put_md5sig_pool();
+	crypto_pool_put();
 clear_hash_noput:
 	memset(md5_hash, 0, 16);
 	return 1;
@@ -2257,6 +2257,18 @@ static int tcp_v4_init_sock(struct sock *sk)
 	return 0;
 }
 
+#ifdef CONFIG_TCP_MD5SIG
+static void tcp_md5sig_info_free_rcu(struct rcu_head *head)
+{
+	struct tcp_md5sig_info *md5sig;
+
+	md5sig = container_of(head, struct tcp_md5sig_info, rcu);
+	kfree(md5sig);
+	static_branch_slow_dec_deferred(&tcp_md5_needed);
+	tcp_md5_release_crypto_pool();
+}
+#endif
+
 void tcp_v4_destroy_sock(struct sock *sk)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
@@ -2281,10 +2293,12 @@ void tcp_v4_destroy_sock(struct sock *sk)
 #ifdef CONFIG_TCP_MD5SIG
 	/* Clean up the MD5 key list, if any */
 	if (tp->md5sig_info) {
+		struct tcp_md5sig_info *md5sig;
+
+		md5sig = rcu_dereference_protected(tp->md5sig_info, 1);
 		tcp_clear_md5_list(sk);
-		kfree_rcu(rcu_dereference_protected(tp->md5sig_info, 1), rcu);
-		tp->md5sig_info = NULL;
-		static_branch_slow_dec_deferred(&tcp_md5_needed);
+		call_rcu(&md5sig->rcu, tcp_md5sig_info_free_rcu);
+		rcu_assign_pointer(tp->md5sig_info, NULL);
 	}
 #endif
 
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 5d475a45a478..d1d30337ffec 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -297,8 +297,10 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
 				key = tp->af_specific->md5_lookup(sk, sk);
 				if (key) {
 					tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC);
-					BUG_ON(tcptw->tw_md5_key && !tcp_alloc_md5sig_pool());
+					if (WARN_ON(!tcptw->tw_md5_key))
+						break;
 					atomic_inc(&tcp_md5_needed.key.key.enabled);
+					tcp_md5_add_crypto_pool();
 				}
 			}
 		} while (0);
@@ -335,16 +337,26 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
 }
 EXPORT_SYMBOL(tcp_time_wait);
 
+#ifdef CONFIG_TCP_MD5SIG
+static void tcp_md5_twsk_free_rcu(struct rcu_head *head)
+{
+	struct tcp_md5sig_key *key;
+
+	key = container_of(head, struct tcp_md5sig_key, rcu);
+	kfree(key);
+	static_branch_slow_dec_deferred(&tcp_md5_needed);
+	tcp_md5_release_crypto_pool();
+}
+#endif
+
 void tcp_twsk_destructor(struct sock *sk)
 {
 #ifdef CONFIG_TCP_MD5SIG
 	if (static_branch_unlikely(&tcp_md5_needed.key)) {
 		struct tcp_timewait_sock *twsk = tcp_twsk(sk);
 
-		if (twsk->tw_md5_key) {
-			kfree_rcu(twsk->tw_md5_key, rcu);
-			static_branch_slow_dec_deferred(&tcp_md5_needed);
-		}
+		if (twsk->tw_md5_key)
+			call_rcu(&twsk->tw_md5_key->rcu, tcp_md5_twsk_free_rcu);
 	}
 #endif
 }
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index cb891a71db0d..f75569f889e7 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -64,6 +64,7 @@
 #include <linux/seq_file.h>
 
 #include <crypto/hash.h>
+#include <crypto/pool.h>
 #include <linux/scatterlist.h>
 
 #include <trace/events/tcp.h>
@@ -665,7 +666,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,
 			      cmd.tcpm_key, cmd.tcpm_keylen);
 }
 
-static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp,
+static int tcp_v6_md5_hash_headers(struct crypto_pool_ahash *hp,
 				   const struct in6_addr *daddr,
 				   const struct in6_addr *saddr,
 				   const struct tcphdr *th, int nbytes)
@@ -674,7 +675,7 @@ static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp,
 	struct scatterlist sg;
 	struct tcphdr *_th;
 
-	bp = hp->scratch;
+	bp = hp->base.scratch;
 	/* 1. TCP pseudo-header (RFC2460) */
 	bp->saddr = *saddr;
 	bp->daddr = *daddr;
@@ -686,38 +687,35 @@ static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp,
 	_th->check = 0;
 
 	sg_init_one(&sg, bp, sizeof(*bp) + sizeof(*th));
-	ahash_request_set_crypt(hp->md5_req, &sg, NULL,
+	ahash_request_set_crypt(hp->req, &sg, NULL,
 				sizeof(*bp) + sizeof(*th));
-	return crypto_ahash_update(hp->md5_req);
+	return crypto_ahash_update(hp->req);
 }
 
 static int tcp_v6_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
 			       const struct in6_addr *daddr, struct in6_addr *saddr,
 			       const struct tcphdr *th)
 {
-	struct tcp_md5sig_pool *hp;
-	struct ahash_request *req;
+	struct crypto_pool_ahash hp;
 
-	hp = tcp_get_md5sig_pool();
-	if (!hp)
+	if (crypto_pool_get(tcp_md5_crypto_pool_id, (struct crypto_pool *)&hp))
 		goto clear_hash_noput;
-	req = hp->md5_req;
 
-	if (crypto_ahash_init(req))
+	if (crypto_ahash_init(hp.req))
 		goto clear_hash;
-	if (tcp_v6_md5_hash_headers(hp, daddr, saddr, th, th->doff << 2))
+	if (tcp_v6_md5_hash_headers(&hp, daddr, saddr, th, th->doff << 2))
 		goto clear_hash;
-	if (tcp_md5_hash_key(hp, key))
+	if (tcp_md5_hash_key(&hp, key))
 		goto clear_hash;
-	ahash_request_set_crypt(req, NULL, md5_hash, 0);
-	if (crypto_ahash_final(req))
+	ahash_request_set_crypt(hp.req, NULL, md5_hash, 0);
+	if (crypto_ahash_final(hp.req))
 		goto clear_hash;
 
-	tcp_put_md5sig_pool();
+	crypto_pool_put();
 	return 0;
 
 clear_hash:
-	tcp_put_md5sig_pool();
+	crypto_pool_put();
 clear_hash_noput:
 	memset(md5_hash, 0, 16);
 	return 1;
@@ -729,8 +727,7 @@ static int tcp_v6_md5_hash_skb(char *md5_hash,
 			       const struct sk_buff *skb)
 {
 	const struct in6_addr *saddr, *daddr;
-	struct tcp_md5sig_pool *hp;
-	struct ahash_request *req;
+	struct crypto_pool_ahash hp;
 	const struct tcphdr *th = tcp_hdr(skb);
 
 	if (sk) { /* valid for establish/request sockets */
@@ -742,29 +739,27 @@ static int tcp_v6_md5_hash_skb(char *md5_hash,
 		daddr = &ip6h->daddr;
 	}
 
-	hp = tcp_get_md5sig_pool();
-	if (!hp)
+	if (crypto_pool_get(tcp_md5_crypto_pool_id, (struct crypto_pool *)&hp))
 		goto clear_hash_noput;
-	req = hp->md5_req;
 
-	if (crypto_ahash_init(req))
+	if (crypto_ahash_init(hp.req))
 		goto clear_hash;
 
-	if (tcp_v6_md5_hash_headers(hp, daddr, saddr, th, skb->len))
+	if (tcp_v6_md5_hash_headers(&hp, daddr, saddr, th, skb->len))
 		goto clear_hash;
-	if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2))
+	if (tcp_md5_hash_skb_data(&hp, skb, th->doff << 2))
 		goto clear_hash;
-	if (tcp_md5_hash_key(hp, key))
+	if (tcp_md5_hash_key(&hp, key))
 		goto clear_hash;
-	ahash_request_set_crypt(req, NULL, md5_hash, 0);
-	if (crypto_ahash_final(req))
+	ahash_request_set_crypt(hp.req, NULL, md5_hash, 0);
+	if (crypto_ahash_final(hp.req))
 		goto clear_hash;
 
-	tcp_put_md5sig_pool();
+	crypto_pool_put();
 	return 0;
 
 clear_hash:
-	tcp_put_md5sig_pool();
+	crypto_pool_put();
 clear_hash_noput:
 	memset(md5_hash, 0, 16);
 	return 1;
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 06/31] net/ipv6: sr: Switch to using crypto_pool
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (4 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 05/31] net/tcp: Use crypto_pool for TCP-MD5 Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 07/31] tcp: Add TCP-AO config and structures Dmitry Safonov
                   ` (25 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

The conversion to use crypto_pool has the following upsides:
- now SR uses asynchronous API which may potentially free CPU cycles and
  improve performance for of CPU crypto algorithm providers;
- hash descriptors now don't have to be allocated on boot, but only at
  the moment SR starts using HMAC and until the last HMAC secret is
  deleted;
- potentially reuse ahash_request(s) for different users
- allocate only one per-CPU scratch buffer rather than a new one for
  each user
- have a common API for net/ users that need ahash on RX/TX fast path

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 include/net/seg6_hmac.h |   7 --
 net/ipv6/Kconfig        |   2 +-
 net/ipv6/seg6.c         |   3 -
 net/ipv6/seg6_hmac.c    | 204 ++++++++++++++++------------------------
 4 files changed, 80 insertions(+), 136 deletions(-)

diff --git a/include/net/seg6_hmac.h b/include/net/seg6_hmac.h
index 2b5d2ee5613e..d6b7820ecda2 100644
--- a/include/net/seg6_hmac.h
+++ b/include/net/seg6_hmac.h
@@ -32,13 +32,6 @@ struct seg6_hmac_info {
 	u8 alg_id;
 };
 
-struct seg6_hmac_algo {
-	u8 alg_id;
-	char name[64];
-	struct crypto_shash * __percpu *tfms;
-	struct shash_desc * __percpu *shashs;
-};
-
 extern int seg6_hmac_compute(struct seg6_hmac_info *hinfo,
 			     struct ipv6_sr_hdr *hdr, struct in6_addr *saddr,
 			     u8 *output);
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index 658bfed1df8b..5be1dab0f178 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -304,7 +304,7 @@ config IPV6_SEG6_LWTUNNEL
 config IPV6_SEG6_HMAC
 	bool "IPv6: Segment Routing HMAC support"
 	depends on IPV6
-	select CRYPTO
+	select CRYPTO_POOL
 	select CRYPTO_HMAC
 	select CRYPTO_SHA1
 	select CRYPTO_SHA256
diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
index 73aaabf0e966..96b80e1d04c1 100644
--- a/net/ipv6/seg6.c
+++ b/net/ipv6/seg6.c
@@ -552,9 +552,6 @@ int __init seg6_init(void)
 
 void seg6_exit(void)
 {
-#ifdef CONFIG_IPV6_SEG6_HMAC
-	seg6_hmac_exit();
-#endif
 #ifdef CONFIG_IPV6_SEG6_LWTUNNEL
 	seg6_iptunnel_exit();
 #endif
diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c
index d43c50a7310d..3732dd993925 100644
--- a/net/ipv6/seg6_hmac.c
+++ b/net/ipv6/seg6_hmac.c
@@ -35,6 +35,7 @@
 #include <net/xfrm.h>
 
 #include <crypto/hash.h>
+#include <crypto/pool.h>
 #include <net/seg6.h>
 #include <net/genetlink.h>
 #include <net/seg6_hmac.h>
@@ -70,6 +71,12 @@ static const struct rhashtable_params rht_params = {
 	.obj_cmpfn		= seg6_hmac_cmpfn,
 };
 
+struct seg6_hmac_algo {
+	u8 alg_id;
+	char name[64];
+	int crypto_pool_id;
+};
+
 static struct seg6_hmac_algo hmac_algos[] = {
 	{
 		.alg_id = SEG6_HMAC_ALGO_SHA1,
@@ -115,55 +122,17 @@ static struct seg6_hmac_algo *__hmac_get_algo(u8 alg_id)
 	return NULL;
 }
 
-static int __do_hmac(struct seg6_hmac_info *hinfo, const char *text, u8 psize,
-		     u8 *output, int outlen)
-{
-	struct seg6_hmac_algo *algo;
-	struct crypto_shash *tfm;
-	struct shash_desc *shash;
-	int ret, dgsize;
-
-	algo = __hmac_get_algo(hinfo->alg_id);
-	if (!algo)
-		return -ENOENT;
-
-	tfm = *this_cpu_ptr(algo->tfms);
-
-	dgsize = crypto_shash_digestsize(tfm);
-	if (dgsize > outlen) {
-		pr_debug("sr-ipv6: __do_hmac: digest size too big (%d / %d)\n",
-			 dgsize, outlen);
-		return -ENOMEM;
-	}
-
-	ret = crypto_shash_setkey(tfm, hinfo->secret, hinfo->slen);
-	if (ret < 0) {
-		pr_debug("sr-ipv6: crypto_shash_setkey failed: err %d\n", ret);
-		goto failed;
-	}
-
-	shash = *this_cpu_ptr(algo->shashs);
-	shash->tfm = tfm;
-
-	ret = crypto_shash_digest(shash, text, psize, output);
-	if (ret < 0) {
-		pr_debug("sr-ipv6: crypto_shash_digest failed: err %d\n", ret);
-		goto failed;
-	}
-
-	return dgsize;
-
-failed:
-	return ret;
-}
-
 int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr,
 		      struct in6_addr *saddr, u8 *output)
 {
 	__be32 hmackeyid = cpu_to_be32(hinfo->hmackeyid);
-	u8 tmp_out[SEG6_HMAC_MAX_DIGESTSIZE];
+	struct crypto_pool_ahash hp;
+	struct seg6_hmac_algo *algo;
 	int plen, i, dgsize, wrsize;
+	struct crypto_ahash *tfm;
+	struct scatterlist sg;
 	char *ring, *off;
+	int err;
 
 	/* a 160-byte buffer for digest output allows to store highest known
 	 * hash function (RadioGatun) with up to 1216 bits
@@ -176,6 +145,10 @@ int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr,
 	if (plen >= SEG6_HMAC_RING_SIZE)
 		return -EMSGSIZE;
 
+	algo = __hmac_get_algo(hinfo->alg_id);
+	if (!algo)
+		return -ENOENT;
+
 	/* Let's build the HMAC text on the ring buffer. The text is composed
 	 * as follows, in order:
 	 *
@@ -186,8 +159,36 @@ int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr,
 	 * 5. All segments in the segments list (n * 128 bits)
 	 */
 
-	local_bh_disable();
+	err = crypto_pool_get(algo->crypto_pool_id, (struct crypto_pool *)&hp);
+	if (err)
+		return err;
+
 	ring = this_cpu_ptr(hmac_ring);
+
+	sg_init_one(&sg, ring, plen);
+
+	tfm = crypto_ahash_reqtfm(hp.req);
+	dgsize = crypto_ahash_digestsize(tfm);
+	if (dgsize > SEG6_HMAC_MAX_DIGESTSIZE) {
+		pr_debug("digest size too big (%d / %d)\n",
+			 dgsize, SEG6_HMAC_MAX_DIGESTSIZE);
+		err = -ENOMEM;
+		goto err_put_pool;
+	}
+
+	err = crypto_ahash_setkey(tfm, hinfo->secret, hinfo->slen);
+	if (err) {
+		pr_debug("crypto_ahash_setkey failed: err %d\n", err);
+		goto err_put_pool;
+	}
+
+	err = crypto_ahash_init(hp.req);
+	if (err)
+		goto err_put_pool;
+
+	ahash_request_set_crypt(hp.req, &sg,
+				hp.base.scratch, SEG6_HMAC_MAX_DIGESTSIZE);
+
 	off = ring;
 
 	/* source address */
@@ -210,21 +211,25 @@ int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr,
 		off += 16;
 	}
 
-	dgsize = __do_hmac(hinfo, ring, plen, tmp_out,
-			   SEG6_HMAC_MAX_DIGESTSIZE);
-	local_bh_enable();
+	err = crypto_ahash_update(hp.req);
+	if (err)
+		goto err_put_pool;
 
-	if (dgsize < 0)
-		return dgsize;
+	err = crypto_ahash_final(hp.req);
+	if (err)
+		goto err_put_pool;
 
 	wrsize = SEG6_HMAC_FIELD_LEN;
 	if (wrsize > dgsize)
 		wrsize = dgsize;
 
 	memset(output, 0, SEG6_HMAC_FIELD_LEN);
-	memcpy(output, tmp_out, wrsize);
+	memcpy(output, hp.base.scratch, wrsize);
 
-	return 0;
+err_put_pool:
+	crypto_pool_put();
+
+	return err;
 }
 EXPORT_SYMBOL(seg6_hmac_compute);
 
@@ -291,12 +296,24 @@ EXPORT_SYMBOL(seg6_hmac_info_lookup);
 int seg6_hmac_info_add(struct net *net, u32 key, struct seg6_hmac_info *hinfo)
 {
 	struct seg6_pernet_data *sdata = seg6_pernet(net);
-	int err;
+	struct seg6_hmac_algo *algo;
+	int ret;
 
-	err = rhashtable_lookup_insert_fast(&sdata->hmac_infos, &hinfo->node,
+	algo = __hmac_get_algo(hinfo->alg_id);
+	if (!algo)
+		return -ENOENT;
+
+	ret = crypto_pool_alloc_ahash(algo->name);
+	if (ret < 0)
+		return ret;
+	algo->crypto_pool_id = ret;
+
+	ret = rhashtable_lookup_insert_fast(&sdata->hmac_infos, &hinfo->node,
 					    rht_params);
+	if (ret)
+		crypto_pool_release(algo->crypto_pool_id);
 
-	return err;
+	return ret;
 }
 EXPORT_SYMBOL(seg6_hmac_info_add);
 
@@ -304,6 +321,7 @@ int seg6_hmac_info_del(struct net *net, u32 key)
 {
 	struct seg6_pernet_data *sdata = seg6_pernet(net);
 	struct seg6_hmac_info *hinfo;
+	struct seg6_hmac_algo *algo;
 	int err = -ENOENT;
 
 	hinfo = rhashtable_lookup_fast(&sdata->hmac_infos, &key, rht_params);
@@ -315,6 +333,12 @@ int seg6_hmac_info_del(struct net *net, u32 key)
 	if (err)
 		goto out;
 
+	algo = __hmac_get_algo(hinfo->alg_id);
+	if (algo)
+		crypto_pool_release(algo->crypto_pool_id);
+	else
+		WARN_ON_ONCE(1);
+
 	seg6_hinfo_release(hinfo);
 
 out:
@@ -348,56 +372,9 @@ int seg6_push_hmac(struct net *net, struct in6_addr *saddr,
 }
 EXPORT_SYMBOL(seg6_push_hmac);
 
-static int seg6_hmac_init_algo(void)
-{
-	struct seg6_hmac_algo *algo;
-	struct crypto_shash *tfm;
-	struct shash_desc *shash;
-	int i, alg_count, cpu;
-
-	alg_count = ARRAY_SIZE(hmac_algos);
-
-	for (i = 0; i < alg_count; i++) {
-		struct crypto_shash **p_tfm;
-		int shsize;
-
-		algo = &hmac_algos[i];
-		algo->tfms = alloc_percpu(struct crypto_shash *);
-		if (!algo->tfms)
-			return -ENOMEM;
-
-		for_each_possible_cpu(cpu) {
-			tfm = crypto_alloc_shash(algo->name, 0, 0);
-			if (IS_ERR(tfm))
-				return PTR_ERR(tfm);
-			p_tfm = per_cpu_ptr(algo->tfms, cpu);
-			*p_tfm = tfm;
-		}
-
-		p_tfm = raw_cpu_ptr(algo->tfms);
-		tfm = *p_tfm;
-
-		shsize = sizeof(*shash) + crypto_shash_descsize(tfm);
-
-		algo->shashs = alloc_percpu(struct shash_desc *);
-		if (!algo->shashs)
-			return -ENOMEM;
-
-		for_each_possible_cpu(cpu) {
-			shash = kzalloc_node(shsize, GFP_KERNEL,
-					     cpu_to_node(cpu));
-			if (!shash)
-				return -ENOMEM;
-			*per_cpu_ptr(algo->shashs, cpu) = shash;
-		}
-	}
-
-	return 0;
-}
-
 int __init seg6_hmac_init(void)
 {
-	return seg6_hmac_init_algo();
+	return crypto_pool_reserve_scratch(SEG6_HMAC_MAX_DIGESTSIZE);
 }
 
 int __net_init seg6_hmac_net_init(struct net *net)
@@ -407,29 +384,6 @@ int __net_init seg6_hmac_net_init(struct net *net)
 	return rhashtable_init(&sdata->hmac_infos, &rht_params);
 }
 
-void seg6_hmac_exit(void)
-{
-	struct seg6_hmac_algo *algo = NULL;
-	int i, alg_count, cpu;
-
-	alg_count = ARRAY_SIZE(hmac_algos);
-	for (i = 0; i < alg_count; i++) {
-		algo = &hmac_algos[i];
-		for_each_possible_cpu(cpu) {
-			struct crypto_shash *tfm;
-			struct shash_desc *shash;
-
-			shash = *per_cpu_ptr(algo->shashs, cpu);
-			kfree(shash);
-			tfm = *per_cpu_ptr(algo->tfms, cpu);
-			crypto_free_shash(tfm);
-		}
-		free_percpu(algo->tfms);
-		free_percpu(algo->shashs);
-	}
-}
-EXPORT_SYMBOL(seg6_hmac_exit);
-
 void __net_exit seg6_hmac_net_exit(struct net *net)
 {
 	struct seg6_pernet_data *sdata = seg6_pernet(net);
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 07/31] tcp: Add TCP-AO config and structures
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (5 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 06/31] net/ipv6: sr: Switch to using crypto_pool Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s Dmitry Safonov
                   ` (24 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Introduce new kernel config option and common structures as well as
helpers to be used by TCP-AO code.

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>
---
 include/linux/tcp.h      |  3 ++
 include/net/tcp.h        |  8 +---
 include/net/tcp_ao.h     | 90 ++++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/tcp.h |  2 +
 net/ipv4/Kconfig         | 13 ++++++
 5 files changed, 110 insertions(+), 6 deletions(-)
 create mode 100644 include/net/tcp_ao.h

diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index a9fbe22732c3..c8a8aaaf725b 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -435,6 +435,9 @@ struct tcp_sock {
 /* TCP MD5 Signature Option information */
 	struct tcp_md5sig_info	__rcu *md5sig_info;
 #endif
+#ifdef CONFIG_TCP_AO
+	struct tcp_ao_info	__rcu *ao_info;
+#endif
 
 /* TCP fastopen related information */
 	struct tcp_fastopen_request *fastopen_req;
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 75bb817928c1..b4b009094bf6 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -37,6 +37,7 @@
 #include <net/snmp.h>
 #include <net/ip.h>
 #include <net/tcp_states.h>
+#include <net/tcp_ao.h>
 #include <net/inet_ecn.h>
 #include <net/dst.h>
 #include <net/mptcp.h>
@@ -1605,12 +1606,7 @@ static inline void tcp_clear_all_retrans_hints(struct tcp_sock *tp)
 	tp->retransmit_skb_hint = NULL;
 }
 
-union tcp_md5_addr {
-	struct in_addr  a4;
-#if IS_ENABLED(CONFIG_IPV6)
-	struct in6_addr	a6;
-#endif
-};
+#define tcp_md5_addr tcp_ao_addr
 
 /* - key database */
 struct tcp_md5sig_key {
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
new file mode 100644
index 000000000000..39b3fc31e5a1
--- /dev/null
+++ b/include/net/tcp_ao.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _TCP_AO_H
+#define _TCP_AO_H
+
+#define TCP_AO_MAX_HASH_SIZE	64
+#define TCP_AO_KEY_ALIGN	1
+#define __tcp_ao_key_align __aligned(TCP_AO_KEY_ALIGN)
+
+union tcp_ao_addr {
+	struct in_addr  a4;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct in6_addr	a6;
+#endif
+};
+
+struct tcp_ao_hdr {
+	u8	kind;
+	u8	length;
+	u8	keyid;
+	u8	rnext_keyid;
+};
+
+struct tcp_ao_key {
+	struct hlist_node	node;
+	union tcp_ao_addr	addr;
+	u8			key[TCP_AO_MAXKEYLEN] __tcp_ao_key_align;
+	unsigned int		crypto_pool_id;
+	u16			port;
+	u8			prefixlen;
+	u8			family;
+	u8			keylen;
+	u8			keyflags;
+	u8			sndid;
+	u8			rcvid;
+	u8			maclen;
+	u8			digest_size;
+	struct rcu_head		rcu;
+	u8			traffic_keys[];
+};
+
+static inline u8 *rcv_other_key(struct tcp_ao_key *key)
+{
+	return key->traffic_keys;
+}
+
+static inline u8 *snd_other_key(struct tcp_ao_key *key)
+{
+	return key->traffic_keys + key->digest_size;
+}
+
+static inline int tcp_ao_maclen(const struct tcp_ao_key *key)
+{
+	return key->maclen;
+}
+
+static inline int tcp_ao_sizeof_key(const struct tcp_ao_key *key)
+{
+	return sizeof(struct tcp_ao_key) + (TCP_AO_MAX_HASH_SIZE << 1);
+}
+
+static inline int tcp_ao_len(const struct tcp_ao_key *key)
+{
+	return tcp_ao_maclen(key) + sizeof(struct tcp_ao_hdr);
+}
+
+static inline unsigned int tcp_ao_digest_size(struct tcp_ao_key *key)
+{
+	return key->digest_size;
+}
+
+struct tcp_ao_info {
+	struct hlist_head	head;
+	struct rcu_head		rcu;
+	/* current_key and rnext_key aren't maintained on listen sockets.
+	 * Their purpose is to cache keys on established connections,
+	 * saving needless lookups. Never dereference any of them from
+	 * listen sockets.
+	 */
+	struct tcp_ao_key	*volatile current_key;
+	struct tcp_ao_key	*rnext_key;
+	u8			ao_flags;
+	__be32			lisn;
+	__be32			risn;
+	u32			snd_sne;
+	u32			snd_sne_seq;
+	u32			rcv_sne;
+	u32			rcv_sne_seq;
+};
+
+#endif /* _TCP_AO_H */
diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
index 8fc09e8638b3..849bbf2d3c38 100644
--- a/include/uapi/linux/tcp.h
+++ b/include/uapi/linux/tcp.h
@@ -342,6 +342,8 @@ struct tcp_diag_md5sig {
 	__u8	tcpm_key[TCP_MD5SIG_MAXKEYLEN];
 };
 
+#define TCP_AO_MAXKEYLEN	80
+
 /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
 
 #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index c341864e4398..89bd0e9d97fe 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -731,6 +731,19 @@ config DEFAULT_TCP_CONG
 	default "bbr" if DEFAULT_BBR
 	default "cubic"
 
+config TCP_AO
+	bool "TCP: Authentication Option (rfc5925)"
+	select CRYPTO
+	select CRYPTO_POOL
+	select TCP_MD5SIG
+	depends on 64BIT # seq-number extension needs WRITE_ONCE(u64)
+	default y
+	help
+	  TCP-AO specifies the use of stronger Message Authentication Codes (MACs),
+	  protects against replays for long-lived TCP connections, and
+	  provides more details on the association of security with TCP
+	  connections than TCP MD5 (See RFC5925)
+
 config TCP_MD5SIG
 	bool "TCP: MD5 Signature Option support (RFC2385)"
 	select CRYPTO_POOL
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (6 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 07/31] tcp: Add TCP-AO config and structures Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 18:50   ` kernel test robot
                     ` (3 more replies)
  2022-08-18 16:59 ` [PATCH 09/31] net/tcp: Prevent TCP-MD5 with TCP-AO being set Dmitry Safonov
                   ` (23 subsequent siblings)
  31 siblings, 4 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Add 3 setsockopt()s:
1. to add a new Master Key Tuple (MKT) on a socket
2. to delete present MKT from a socket
3. to change flags of an MKT

Userspace has to introduce keys on every socket it wants to use TCP-AO
option on, similarly to TCP_MD5SIG/TCP_MD5SIG_EXT.
RFC5925 prohibits definition of MKTs that would match the same peer,
so do sanity checks on the data provided by userspace. Be as
conservative as possible, including refusal of defining MKT on
an established connection with no AO, removing the key in-use and etc.

(1) and (2) are to be used by userspace key manager to add/remove keys.
(3) main purpose is to set rnext_key, which (as prescribed by RFC5925)
is the key id that will be requested in TCP-AO header from the peer to
sign their segments with.

At this moment the life of ao_info ends in tcp_v4_destroy_sock().

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>
---
 include/linux/sockptr.h  |  23 ++
 include/net/tcp.h        |   6 +
 include/net/tcp_ao.h     |  15 +
 include/uapi/linux/tcp.h |  35 ++
 net/ipv4/Makefile        |   1 +
 net/ipv4/tcp.c           |  17 +
 net/ipv4/tcp_ao.c        | 828 +++++++++++++++++++++++++++++++++++++++
 net/ipv4/tcp_ipv4.c      |   8 +-
 net/ipv6/Makefile        |   1 +
 net/ipv6/tcp_ao.c        |  20 +
 net/ipv6/tcp_ipv6.c      |  14 +-
 11 files changed, 965 insertions(+), 3 deletions(-)
 create mode 100644 net/ipv4/tcp_ao.c
 create mode 100644 net/ipv6/tcp_ao.c

diff --git a/include/linux/sockptr.h b/include/linux/sockptr.h
index d45902fb4cad..f42575ffda53 100644
--- a/include/linux/sockptr.h
+++ b/include/linux/sockptr.h
@@ -55,6 +55,29 @@ static inline int copy_from_sockptr(void *dst, sockptr_t src, size_t size)
 	return copy_from_sockptr_offset(dst, src, 0, size);
 }
 
+static inline int copy_struct_from_sockptr(void *dst, size_t ksize,
+		sockptr_t src, size_t usize)
+{
+	size_t size = min(ksize, usize);
+	size_t rest = max(ksize, usize) - size;
+
+	if (!sockptr_is_kernel(src))
+		return copy_struct_from_user(dst, ksize, src.user, size);
+
+	if (usize < ksize) {
+		memset(dst + size, 0, rest);
+	} else if (usize > ksize) {
+		char *p = src.kernel;
+
+		while (rest--) {
+			if (*p++)
+				return -E2BIG;
+		}
+	}
+	memcpy(dst, src.kernel, size);
+	return 0;
+}
+
 static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset,
 		const void *src, size_t size)
 {
diff --git a/include/net/tcp.h b/include/net/tcp.h
index b4b009094bf6..278d0ab81796 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -2054,6 +2054,12 @@ struct tcp_sock_af_ops {
 				     sockptr_t optval,
 				     int optlen);
 #endif
+#ifdef CONFIG_TCP_AO
+	int			(*ao_parse)(struct sock *sk,
+					    int optname,
+					    sockptr_t optval,
+					    int optlen);
+#endif
 };
 
 struct tcp_request_sock_ops {
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index 39b3fc31e5a1..6d0d30e5542b 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -87,4 +87,19 @@ struct tcp_ao_info {
 	u32			rcv_sne_seq;
 };
 
+#ifdef CONFIG_TCP_AO
+int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
+		 sockptr_t optval, int optlen);
+void tcp_ao_destroy_sock(struct sock *sk);
+/* ipv4 specific functions */
+int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen);
+/* ipv6 specific functions */
+int tcp_v6_parse_ao(struct sock *sk, int cmd,
+		    sockptr_t optval, int optlen);
+#else
+static inline void tcp_ao_destroy_sock(struct sock *sk)
+{
+}
+#endif
+
 #endif /* _TCP_AO_H */
diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
index 849bbf2d3c38..5369458ae89f 100644
--- a/include/uapi/linux/tcp.h
+++ b/include/uapi/linux/tcp.h
@@ -129,6 +129,9 @@ enum {
 
 #define TCP_TX_DELAY		37	/* delay outgoing packets by XX usec */
 
+#define TCP_AO			38	/* (Add/Set MKT) */
+#define TCP_AO_DEL		39	/* (Delete MKT) */
+#define TCP_AO_MOD		40	/* (Modify MKT) */
 
 #define TCP_REPAIR_ON		1
 #define TCP_REPAIR_OFF		0
@@ -344,6 +347,38 @@ struct tcp_diag_md5sig {
 
 #define TCP_AO_MAXKEYLEN	80
 
+#define TCP_AO_CMDF_CURR	(1 << 0)	/* Only checks field sndid */
+#define TCP_AO_CMDF_NEXT	(1 << 1)	/* Only checks field rcvid */
+
+struct tcp_ao { /* setsockopt(TCP_AO) */
+	struct __kernel_sockaddr_storage tcpa_addr;
+	char	tcpa_alg_name[64];
+	__u16	tcpa_flags;
+	__u8	tcpa_prefix;
+	__u8	tcpa_sndid;
+	__u8	tcpa_rcvid;
+	__u8	tcpa_maclen;
+	__u8	tcpa_keyflags;
+	__u8	tcpa_keylen;
+	__u8	tcpa_key[TCP_AO_MAXKEYLEN];
+} __attribute__((aligned(8)));
+
+struct tcp_ao_del { /* setsockopt(TCP_AO_DEL) */
+	struct __kernel_sockaddr_storage tcpa_addr;
+	__u16	tcpa_flags;
+	__u8	tcpa_prefix;
+	__u8	tcpa_sndid;
+	__u8	tcpa_rcvid;
+	__u8	tcpa_current;
+	__u8	tcpa_rnext;
+} __attribute__((aligned(8)));
+
+struct tcp_ao_mod { /* setsockopt(TCP_AO_MOD) */
+	__u16	tcpa_flags;
+	__u8	tcpa_current;
+	__u8	tcpa_rnext;
+} __attribute__((aligned(8)));
+
 /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
 
 #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index bbdd9c44f14e..6d0b3e228b8a 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
 
 obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
 		      xfrm4_output.o xfrm4_protocol.o
+obj-$(CONFIG_TCP_AO) += tcp_ao.o
 
 ifeq ($(CONFIG_BPF_JIT),y)
 obj-$(CONFIG_BPF_SYSCALL) += bpf_tcp_ca.o
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 7c54b47e848f..85854b8afc47 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -3704,6 +3704,23 @@ static int do_tcp_setsockopt(struct sock *sk, int level, int optname,
 		__tcp_sock_set_quickack(sk, val);
 		break;
 
+#ifdef CONFIG_TCP_AO
+	case TCP_AO:
+	case TCP_AO_DEL:
+	case TCP_AO_MOD: {
+		u32 state = (1 << sk->sk_state) &
+			    (TCPF_CLOSE | TCPF_ESTABLISHED | TCPF_LISTEN);
+
+		if (!state || (state == TCPF_ESTABLISHED &&
+			       !rcu_dereference_protected(tcp_sk(sk)->ao_info,
+						lockdep_sock_is_held(sk))))
+			err = -EINVAL;
+		else
+			err = tp->af_specific->ao_parse(sk, optname, optval,
+							optlen);
+		break;
+	}
+#endif
 #ifdef CONFIG_TCP_MD5SIG
 	case TCP_MD5SIG:
 	case TCP_MD5SIG_EXT:
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
new file mode 100644
index 000000000000..7f53417ebdf7
--- /dev/null
+++ b/net/ipv4/tcp_ao.c
@@ -0,0 +1,828 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * INET		An implementation of the TCP Authentication Option (TCP-AO).
+ *		See RFC5925.
+ *
+ * Authors:	Dmitry Safonov <dima@arista.com>
+ *		Francesco Ruggeri <fruggeri@arista.com>
+ *		Salam Noureddine <noureddine@arista.com>
+ */
+#define pr_fmt(fmt) "TCP: " fmt
+
+#include <linux/inetdevice.h>
+#include <linux/tcp.h>
+#include <crypto/pool.h>
+
+#include <net/tcp.h>
+#include <net/ipv6.h>
+
+struct tcp_ao_key *tcp_ao_do_lookup_rcvid(struct sock *sk, u8 keyid)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct tcp_ao_key *key;
+	struct tcp_ao_info *ao;
+
+	ao = rcu_dereference_check(tp->ao_info, lockdep_sock_is_held(sk));
+
+	if (!ao)
+		return NULL;
+
+	hlist_for_each_entry_rcu(key, &ao->head, node) {
+		if (key->rcvid == keyid)
+			return key;
+	}
+	return NULL;
+}
+
+struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid)
+{
+	struct tcp_ao_key *key;
+	struct tcp_ao_info *ao;
+
+	ao = rcu_dereference_check(tcp_sk(sk)->ao_info,
+				   lockdep_sock_is_held(sk));
+	if (!ao)
+		return NULL;
+
+	hlist_for_each_entry_rcu(key, &ao->head, node) {
+		if (key->sndid == keyid)
+			return key;
+	}
+	return NULL;
+}
+
+static inline int ipv4_prefix_cmp(const struct in_addr *addr1,
+				  const struct in_addr *addr2,
+				  unsigned int prefixlen)
+{
+	__be32 mask = inet_make_mask(prefixlen);
+
+	if ((addr1->s_addr & mask) == (addr2->s_addr & mask))
+		return 0;
+	return ((addr1->s_addr & mask) > (addr2->s_addr & mask)) ? 1 : -1;
+}
+
+static int __tcp_ao_key_cmp(const struct tcp_ao_key *key,
+			    const union tcp_ao_addr *addr, u8 prefixlen,
+			    int family, int sndid, int rcvid, u16 port)
+{
+	if (sndid >= 0 && key->sndid != sndid)
+		return (key->sndid > sndid) ? 1 : -1;
+	if (rcvid >= 0 && key->rcvid != rcvid)
+		return (key->rcvid > rcvid) ? 1 : -1;
+	if (port != 0 && key->port != 0 && port != key->port)
+		return (key->port > port) ? 1 : -1;
+
+	if (family == AF_UNSPEC)
+		return 0;
+	if (key->family != family)
+		return (key->family > family) ? 1 : -1;
+
+	if (family == AF_INET) {
+		if (key->addr.a4.s_addr == INADDR_ANY)
+			return 0;
+		if (addr->a4.s_addr == INADDR_ANY)
+			return 0;
+		return ipv4_prefix_cmp(&key->addr.a4, &addr->a4, prefixlen);
+	} else {
+		if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6))
+			return 0;
+		if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen))
+			return 0;
+		return memcmp(&key->addr.a6, &addr->a6, prefixlen);
+	}
+}
+
+int tcp_ao_key_cmp(const struct tcp_ao_key *key,
+		   const union tcp_ao_addr *addr, u8 prefixlen,
+		   int family, int sndid, int rcvid, u16 port)
+{
+	if (family == AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) {
+		__be32 addr4 = addr->a6.s6_addr32[3];
+
+		return __tcp_ao_key_cmp(key, (union tcp_ao_addr *)&addr4,
+					prefixlen, AF_INET, sndid, rcvid, port);
+	}
+	return __tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid, port);
+}
+
+struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
+				    const union tcp_ao_addr *addr,
+				    int family, int sndid, int rcvid, u16 port)
+{
+	struct tcp_ao_key *key;
+	struct tcp_ao_info *ao;
+
+	ao = rcu_dereference_check(tcp_sk(sk)->ao_info,
+				   lockdep_sock_is_held(sk));
+	if (!ao)
+		return NULL;
+
+	hlist_for_each_entry_rcu(key, &ao->head, node) {
+		if (!tcp_ao_key_cmp(key, addr, key->prefixlen,
+				    family, sndid, rcvid, port))
+			return key;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL(tcp_ao_do_lookup);
+
+static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags,
+		struct tcp_ao_info *cloned_from)
+{
+	struct tcp_ao_info *ao;
+
+	ao = kzalloc(sizeof(*ao), flags);
+	if (!ao)
+		return NULL;
+	INIT_HLIST_HEAD(&ao->head);
+
+	if (cloned_from)
+		ao->ao_flags = cloned_from->ao_flags;
+	return ao;
+}
+
+void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt)
+{
+	hlist_add_head_rcu(&mkt->node, &ao->head);
+}
+
+static void tcp_ao_key_free_rcu(struct rcu_head *head)
+{
+	struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu);
+
+	crypto_pool_release(key->crypto_pool_id);
+	kfree(key);
+}
+
+void tcp_ao_destroy_sock(struct sock *sk)
+{
+	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 (!ao)
+		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);
+		call_rcu(&key->rcu, tcp_ao_key_free_rcu);
+	}
+
+	kfree_rcu(ao, rcu);
+}
+
+static int tcp_ao_current_rnext(struct sock *sk, u16 tcpa_flags,
+				u8 tcpa_sndid, u8 tcpa_rcvid)
+{
+	struct tcp_ao_info *ao_info;
+	struct tcp_ao_key *key;
+
+	ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info,
+					    lockdep_sock_is_held(sk));
+	if ((tcpa_flags & (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT)) && !ao_info)
+		return -EINVAL;
+
+	if (tcpa_flags & TCP_AO_CMDF_CURR) {
+		key = tcp_ao_do_lookup_sndid(sk, tcpa_sndid);
+		if (!key)
+			return -ENOENT;
+		if (ao_info->current_key != key)
+			WRITE_ONCE(ao_info->current_key, key);
+	}
+
+	if (tcpa_flags & TCP_AO_CMDF_NEXT) {
+		key = tcp_ao_do_lookup_rcvid(sk, tcpa_rcvid);
+		if (!key)
+			return -ENOENT;
+		if (ao_info->rnext_key != key)
+			WRITE_ONCE(ao_info->rnext_key, key);
+	}
+
+	return 0;
+}
+
+static int tcp_ao_verify_port(struct sock *sk, u16 port)
+{
+	struct inet_sock *inet = inet_sk(sk);
+
+	if (port != 0) /* FIXME */
+		return -EINVAL;
+
+	/* Check that MKT port is consistent with socket */
+	if (port != 0 && inet->inet_dport != 0 && port != inet->inet_dport)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao *cmd,
+			      union tcp_md5_addr **addr, u16 *port)
+{
+	struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->tcpa_addr;
+	struct inet_sock *inet = inet_sk(sk);
+
+	if (sin->sin_family != AF_INET)
+		return -EINVAL;
+
+	if (tcp_ao_verify_port(sk, ntohs(sin->sin_port)))
+		return -EINVAL;
+
+	/* Check prefix and trailing 0's in addr */
+	if (cmd->tcpa_prefix != 0) {
+		__be32 mask;
+
+		if (sin->sin_addr.s_addr == INADDR_ANY)
+			return -EINVAL;
+		if (cmd->tcpa_prefix > 32)
+			return -EINVAL;
+
+		mask = inet_make_mask(cmd->tcpa_prefix);
+		if (sin->sin_addr.s_addr & ~mask)
+			return -EINVAL;
+
+		/* Check that MKT address is consistent with socket */
+		if (inet->inet_daddr != INADDR_ANY &&
+		    (inet->inet_daddr & mask) != sin->sin_addr.s_addr)
+			return -EINVAL;
+	} else {
+		if (sin->sin_addr.s_addr != INADDR_ANY)
+			return -EINVAL;
+	}
+
+	*addr = (union tcp_md5_addr *)&sin->sin_addr;
+	*port = ntohs(sin->sin_port);
+	return 0;
+}
+
+static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao *cmd,
+			      union tcp_md5_addr **paddr, u16 *port,
+			      unsigned short int *family)
+{
+	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->tcpa_addr;
+	struct in6_addr *addr = &sin6->sin6_addr;
+	u8 prefix = cmd->tcpa_prefix;
+
+	if (sin6->sin6_family != AF_INET6)
+		return -EINVAL;
+	/* Not supposed to happen: here from af-specific callback */
+	if (WARN_ON_ONCE(!sk_fullsock(sk)))
+		return -EINVAL;
+
+	if (tcp_ao_verify_port(sk, ntohs(sin6->sin6_port)))
+		return -EINVAL;
+
+	/* Check prefix and trailing 0's in addr */
+	if (cmd->tcpa_prefix != 0 && ipv6_addr_v4mapped(addr)) {
+		__be32 addr4 = addr->s6_addr32[3];
+		__be32 mask;
+
+		if (prefix > 32 || addr4 == INADDR_ANY)
+			return -EINVAL;
+
+		mask = inet_make_mask(prefix);
+		if (addr4 & ~mask)
+			return -EINVAL;
+
+		/* Check that MKT address is consistent with socket */
+		if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
+			__be32 daddr4 = sk->sk_v6_daddr.s6_addr32[3];
+
+			if (!ipv6_addr_v4mapped(&sk->sk_v6_daddr))
+				return -EINVAL;
+			if ((daddr4 & mask) != addr4)
+				return -EINVAL;
+		}
+
+		*paddr = (union tcp_md5_addr *)&addr->s6_addr32[3];
+		*family = AF_INET;
+		*port = ntohs(sin6->sin6_port);
+		return 0;
+	} else if (cmd->tcpa_prefix != 0) {
+		struct in6_addr pfx;
+
+		if (ipv6_addr_any(addr) || prefix > 128)
+			return -EINVAL;
+
+		ipv6_addr_prefix(&pfx, addr, prefix);
+		if (ipv6_addr_cmp(&pfx, addr))
+			return -EINVAL;
+
+		/* Check that MKT address is consistent with socket */
+		if (!ipv6_addr_any(&sk->sk_v6_daddr) &&
+		    !ipv6_prefix_equal(&sk->sk_v6_daddr, addr, prefix))
+
+			return -EINVAL;
+	} else {
+		if (!ipv6_addr_any(addr))
+			return -EINVAL;
+	}
+
+	*paddr = (union tcp_md5_addr *)addr;
+	*port = ntohs(sin6->sin6_port);
+	return 0;
+}
+
+static int tcp_ao_parse_crypto(struct tcp_ao *cmd, struct tcp_ao_key *key)
+{
+	unsigned int syn_tcp_option_space;
+	struct crypto_pool_ahash hp;
+	bool is_kdf_aes_128_cmac = false;
+	struct crypto_ahash *tfm;
+	int err, pool_id;
+
+	/* Force null-termination of tcpa_alg_name */
+	cmd->tcpa_alg_name[ARRAY_SIZE(cmd->tcpa_alg_name) - 1] = '\0';
+
+	/* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */
+	if (!strcmp("cmac(aes128)", cmd->tcpa_alg_name)) {
+		strcpy(cmd->tcpa_alg_name, "cmac(aes)");
+		is_kdf_aes_128_cmac = (cmd->tcpa_keylen != 16);
+	}
+
+	key->maclen = cmd->tcpa_maclen ?: 12; /* 12 is the default in RFC5925 */
+
+	/* Check: maclen + tcp-ao header <= (MAX_TCP_OPTION_SPACE - mss
+	 *					- tstamp - wscale - sackperm),
+	 * see tcp_syn_options(), tcp_synack_options(), commit 33ad798c924b.
+	 *
+	 * In order to allow D-SACK with TCP-AO, the header size should be:
+	 * (MAX_TCP_OPTION_SPACE - TCPOLEN_TSTAMP_ALIGNED
+	 *			- TCPOLEN_SACK_BASE_ALIGNED
+	 *			- 2 * TCPOLEN_SACK_PERBLOCK) = 8 (maclen = 4),
+	 * see tcp_established_options().
+	 *
+	 * RFC5925, 2.2:
+	 * Typical MACs are 96-128 bits (12-16 bytes), but any length
+	 * that fits in the header of the segment being authenticated
+	 * is allowed.
+	 *
+	 * RFC5925, 7.6:
+	 * TCP-AO continues to consume 16 bytes in non-SYN segments,
+	 * leaving a total of 24 bytes for other options, of which
+	 * the timestamp consumes 10.  This leaves 14 bytes, of which 10
+	 * are used for a single SACK block. When two SACK blocks are used,
+	 * such as to handle D-SACK, a smaller TCP-AO MAC would be required
+	 * to make room for the additional SACK block (i.e., to leave 18
+	 * bytes for the D-SACK variant of the SACK option) [RFC2883].
+	 * Note that D-SACK is not supportable in TCP MD5 in the presence
+	 * of timestamps, because TCP MD5’s MAC length is fixed and too
+	 * large to leave sufficient option space.
+	 */
+	syn_tcp_option_space = MAX_TCP_OPTION_SPACE;
+	syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED;
+	syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED;
+	syn_tcp_option_space -= TCPOLEN_SACKPERM_ALIGNED;
+	if (tcp_ao_len(key) > syn_tcp_option_space)
+		return -EMSGSIZE;
+
+	key->keylen = cmd->tcpa_keylen;
+	memcpy(key->key, cmd->tcpa_key, cmd->tcpa_keylen);
+
+	pool_id = crypto_pool_alloc_ahash(cmd->tcpa_alg_name);
+	if (pool_id < 0)
+		return pool_id;
+
+	if (is_kdf_aes_128_cmac) {
+		err = crypto_pool_reserve_scratch(16);
+		if (err)
+			goto err_free_pool;
+	}
+
+	err = crypto_pool_get(pool_id, (struct crypto_pool *)&hp);
+	if (err)
+		goto err_free_pool;
+
+	tfm = crypto_ahash_reqtfm(hp.req);
+	if (crypto_ahash_alignmask(tfm) > TCP_AO_KEY_ALIGN) {
+		err = -EOPNOTSUPP;
+		goto err_put_pool;
+	}
+
+	if (is_kdf_aes_128_cmac) {
+		void *scratch = hp.base.scratch;
+		struct scatterlist sg;
+
+		/* Using zero-key of 16 bytes as described in RFC5926 */
+		memset(scratch, 0, 16);
+		sg_init_one(&sg, cmd->tcpa_key, cmd->tcpa_keylen);
+
+		err = crypto_ahash_setkey(tfm, scratch, 16);
+		if (err)
+			goto err_put_pool;
+
+		err = crypto_ahash_init(hp.req);
+		if (err)
+			goto err_put_pool;
+
+		ahash_request_set_crypt(hp.req, &sg, key->key, cmd->tcpa_keylen);
+		err = crypto_ahash_update(hp.req);
+		if (err)
+			goto err_put_pool;
+
+		err |= crypto_ahash_final(hp.req);
+		if (err)
+			goto err_put_pool;
+		key->keylen = 16;
+	}
+
+	err = crypto_ahash_setkey(tfm, key->key, key->keylen);
+	if (err)
+		goto err_put_pool;
+
+	key->digest_size = crypto_ahash_digestsize(tfm);
+	crypto_pool_put();
+
+	err = crypto_pool_reserve_scratch(sizeof(struct tcphdr) +
+					  sizeof(struct tcp_ao_hdr) +
+					  key->digest_size);
+	if (err)
+		goto err_free_pool;
+
+	if (key->digest_size > TCP_AO_MAX_HASH_SIZE) {
+		err = -ENOBUFS;
+		goto err_free_pool;
+	}
+	if (key->maclen > key->digest_size) {
+		err = -EINVAL;
+		goto err_free_pool;
+	}
+
+	key->crypto_pool_id = pool_id;
+	return 0;
+
+err_put_pool:
+	crypto_pool_put();
+err_free_pool:
+	crypto_pool_release(pool_id);
+	return err;
+}
+
+/* tcp_ao_mkt_overlap_v4() assumes cmd already went through tcp_ao_verify_ipv4.
+ * RFC5925 3.1 The IDs of MKTs MUST NOT overlap where their TCP connection
+ * identifiers overlap.
+ */
+static bool tcp_ao_mkt_overlap_v4(struct tcp_ao *cmd,
+				  struct tcp_ao_info *ao_info)
+{
+	struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->tcpa_addr;
+	__be32 addr = sin->sin_addr.s_addr;
+	__u8 prefix = cmd->tcpa_prefix;
+	__u16 port = ntohs(sin->sin_port);
+	__u8 sndid = cmd->tcpa_sndid;
+	__u8 rcvid = cmd->tcpa_rcvid;
+	struct tcp_ao_key *key;
+
+	/* Check for TCP connection identifiers overlap */
+
+	hlist_for_each_entry_rcu(key, &ao_info->head, node) {
+		__be32 key_addr;
+		__be32 mask;
+
+		/* Check for overlapping ids */
+		if (key->sndid != sndid && key->rcvid != rcvid)
+			continue;
+
+		key_addr = key->addr.a4.s_addr;
+		mask = inet_make_mask(min(prefix, key->prefixlen));
+
+		/* Check for overlapping addresses */
+		if (addr == INADDR_ANY || key_addr == INADDR_ANY ||
+		    (addr & mask) == (key_addr & mask)) {
+			/* Check for overlapping ports */
+			if (port == 0 || key->port == 0 || port == key->port)
+				return true;
+		}
+	}
+
+	return false;
+}
+
+/* tcp_ao_mkt_overlap_v6() assumes cmd already went through tcp_ao_verify_ipv6.
+ * RFC5925 3.1 The IDs of MKTs MUST NOT overlap where their TCP connection
+ * identifiers overlap.
+ */
+static bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd,
+				  struct tcp_ao_info *ao_info)
+{
+	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->tcpa_addr;
+	struct in6_addr *addr = &sin6->sin6_addr;
+	bool v4_mapped = ipv6_addr_v4mapped(addr);
+	__u8 prefix = cmd->tcpa_prefix;
+	__u16 port = ntohs(sin6->sin6_port);
+	__u8 sndid = cmd->tcpa_sndid;
+	__u8 rcvid = cmd->tcpa_rcvid;
+	struct tcp_ao_key *key;
+	__be32 addr4 = v4_mapped ? addr->s6_addr32[3] : 0;
+
+	hlist_for_each_entry_rcu(key, &ao_info->head, node) {
+		struct in6_addr pfx, key_pfx;
+		struct in6_addr *key_addr;
+		int min_prefixlen;
+
+		/* Check for overlapping ids */
+		if (key->sndid != sndid && key->rcvid != rcvid)
+			continue;
+
+		key_addr = &key->addr.a6;
+
+		if (v4_mapped) {
+			__be32 key_addr4;
+			__be32 mask;
+
+			if (!ipv6_addr_v4mapped(key_addr))
+				continue;
+
+			key_addr4 = key_addr->s6_addr32[3];
+			mask = inet_make_mask(min(prefix, key->prefixlen));
+
+			/* Check for overlapping addresses */
+			if (addr4 == INADDR_ANY || key_addr4 == INADDR_ANY ||
+			    (addr4 & mask) == (key_addr4 & mask)) {
+				/* Check for overlapping ports */
+				if (port == 0 || key->port == 0 ||
+				    port == key->port)
+					return true;
+			}
+		} else {
+			min_prefixlen = min(prefix, key->prefixlen);
+			ipv6_addr_prefix(&pfx, addr, min_prefixlen);
+			ipv6_addr_prefix(&key_pfx, key_addr, min_prefixlen);
+
+			/* Check for overlapping addresses */
+			if (ipv6_addr_any(addr) || ipv6_addr_any(key_addr) ||
+			    !ipv6_addr_cmp(&pfx, &key_pfx)) {
+				/* Check for overlapping ports */
+				if (port == 0 || key->port == 0 ||
+				    port == key->port)
+					return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+#define TCP_AO_KEYF_ALL		(0)
+#define TCP_AO_CMDF_ADDMOD_VALID					\
+	(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT)
+#define TCP_AO_CMDF_DEL_VALID						\
+	(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT)
+
+static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
+			  sockptr_t optval, int optlen)
+{
+	struct tcp_ao_info *ao_info;
+	union tcp_md5_addr *addr;
+	struct tcp_ao_key *key;
+	bool first = false;
+	struct tcp_ao cmd;
+	int ret, size;
+	u16 port;
+
+	if (optlen < sizeof(cmd))
+		return -EINVAL;
+
+	ret = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen);
+	if (ret)
+		return ret;
+
+	if (cmd.tcpa_keylen > TCP_AO_MAXKEYLEN)
+		return -EINVAL;
+
+	if (cmd.tcpa_flags & ~TCP_AO_CMDF_ADDMOD_VALID)
+		return -EINVAL;
+
+	if (family == AF_INET)
+		ret = tcp_ao_verify_ipv4(sk, &cmd, &addr, &port);
+	else
+		ret = tcp_ao_verify_ipv6(sk, &cmd, &addr, &port, &family);
+	if (ret)
+		return ret;
+
+	if (cmd.tcpa_keyflags & ~TCP_AO_KEYF_ALL)
+		return -EINVAL;
+
+	ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info,
+					    lockdep_sock_is_held(sk));
+
+	if (!ao_info) {
+		ao_info = tcp_ao_alloc_info(GFP_KERNEL, NULL);
+		if (!ao_info)
+			return -ENOMEM;
+		first = true;
+	} else {
+		if (family == AF_INET) {
+			if (tcp_ao_mkt_overlap_v4(&cmd, ao_info))
+				return -EEXIST;
+		} else {
+			if (tcp_ao_mkt_overlap_v6(&cmd, ao_info))
+				return -EEXIST;
+		}
+	}
+
+	/* TODO: We should add twice the key->diget_size instead of the max
+	 * so rework this in a way to know the digest_size before allocating
+	 * the tcp_ao_key struct.
+	 */
+	size = sizeof(struct tcp_ao_key) + (TCP_AO_MAX_HASH_SIZE << 1);
+	key = sock_kmalloc(sk, size, GFP_KERNEL);
+	if (!key) {
+		ret = -ENOMEM;
+		goto err_free_ao;
+	}
+
+	INIT_HLIST_NODE(&key->node);
+	memcpy(&key->addr, addr, (family == AF_INET) ? sizeof(struct in_addr) :
+						       sizeof(struct in6_addr));
+	key->port	= port;
+	key->prefixlen	= cmd.tcpa_prefix;
+	key->family	= family;
+	key->keyflags	= cmd.tcpa_keyflags;
+	key->sndid	= cmd.tcpa_sndid;
+	key->rcvid	= cmd.tcpa_rcvid;
+
+	ret = tcp_ao_parse_crypto(&cmd, key);
+	if (ret < 0)
+		goto err_free_sock;
+
+	tcp_ao_link_mkt(ao_info, key);
+	if (first) {
+		sk_gso_disable(sk);
+		rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info);
+	}
+
+	/* Can't fail: the key with sndid/rcvid was just added */
+	WARN_ON_ONCE(tcp_ao_current_rnext(sk, cmd.tcpa_flags,
+					  cmd.tcpa_sndid, cmd.tcpa_rcvid));
+	return 0;
+
+err_free_sock:
+	atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
+	kfree(key);
+err_free_ao:
+	if (first)
+		kfree(ao_info);
+	return ret;
+}
+
+static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_key *key,
+		struct tcp_ao_info *ao_info, struct tcp_ao_del *cmd)
+{
+	int err;
+
+	hlist_del_rcu(&key->node);
+
+	/* At this moment another CPU could have looked this key up
+	 * while it was unlinked from the list. Wait for RCU grace period,
+	 * after which the key is off-list and can't be looked up again;
+	 * the rx path [just before RCU came] might have used it and set it
+	 * as current_key (very unlikely).
+	 */
+	synchronize_rcu();
+	err = tcp_ao_current_rnext(sk, cmd->tcpa_flags,
+				   cmd->tcpa_current, cmd->tcpa_rnext);
+	if (err)
+		goto add_key;
+
+	if (unlikely(READ_ONCE(ao_info->current_key) == key ||
+			       READ_ONCE(ao_info->rnext_key) == key)) {
+		err = -EBUSY;
+		goto add_key;
+	}
+
+	atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
+	call_rcu(&key->rcu, tcp_ao_key_free_rcu);
+
+	return 0;
+add_key:
+	hlist_add_head_rcu(&key->node, &ao_info->head);
+	return err;
+}
+
+static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family,
+			  sockptr_t optval, int optlen)
+{
+	struct tcp_ao_info *ao_info;
+	struct tcp_ao_key *key;
+	struct tcp_ao_del cmd;
+	int err;
+	union tcp_md5_addr *addr;
+	__u8 prefix;
+	__be16 port;
+	int addr_len;
+
+	if (optlen < sizeof(cmd))
+		return -EINVAL;
+
+	err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen);
+	if (err)
+		return err;
+
+	if (cmd.tcpa_flags & ~TCP_AO_CMDF_DEL_VALID)
+		return -EINVAL;
+
+	ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info,
+					    lockdep_sock_is_held(sk));
+	if (!ao_info)
+		return -ENOENT;
+
+	if (family == AF_INET) {
+		struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpa_addr;
+
+		addr = (union tcp_md5_addr *)&sin->sin_addr;
+		addr_len = sizeof(struct in_addr);
+		port = ntohs(sin->sin_port);
+	} else {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpa_addr;
+		struct in6_addr *addr6 = &sin6->sin6_addr;
+
+		if (ipv6_addr_v4mapped(addr6)) {
+			addr = (union tcp_md5_addr *)&addr6->s6_addr32[3];
+			addr_len = sizeof(struct in_addr);
+			family = AF_INET;
+		} else {
+			addr = (union tcp_md5_addr *)addr6;
+			addr_len = sizeof(struct in6_addr);
+		}
+		port = ntohs(sin6->sin6_port);
+	}
+	prefix = cmd.tcpa_prefix;
+
+	/* We could choose random present key here for current/rnext
+	 * but that's less predictable. Let's be strict and don't
+	 * allow removing a key that's in use. RFC5925 doesn't
+	 * specify how-to coordinate key removal, but says:
+	 * "It is presumed that an MKT affecting a particular
+	 * connection cannot be destroyed during an active connection"
+	 */
+	hlist_for_each_entry_rcu(key, &ao_info->head, node) {
+		if (cmd.tcpa_sndid != key->sndid ||
+		    cmd.tcpa_rcvid != key->rcvid)
+			continue;
+
+		if (family != key->family ||
+		    prefix != key->prefixlen ||
+		    port != key->port ||
+		    memcmp(addr, &key->addr, addr_len))
+			continue;
+
+		return tcp_ao_delete_key(sk, key, ao_info, &cmd);
+	}
+	return -ENOENT;
+}
+
+static int tcp_ao_mod_cmd(struct sock *sk, unsigned short int family,
+			  sockptr_t optval, int optlen)
+{
+	struct tcp_ao_info *ao_info;
+	struct tcp_ao_mod cmd;
+	int err;
+
+	if (optlen < sizeof(cmd))
+		return -EINVAL;
+
+	err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen);
+	if (err)
+		return err;
+
+	if (cmd.tcpa_flags & ~TCP_AO_CMDF_ADDMOD_VALID)
+		return -EINVAL;
+
+	ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info,
+					    lockdep_sock_is_held(sk));
+	if (!ao_info)
+		return -ENOENT;
+	/* TODO: make tcp_ao_current_rnext() and flags set atomic */
+	return tcp_ao_current_rnext(sk, cmd.tcpa_flags,
+			cmd.tcpa_current, cmd.tcpa_rnext);
+}
+
+int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
+		 sockptr_t optval, int optlen)
+{
+	if (WARN_ON_ONCE(family != AF_INET && family != AF_INET6))
+		return -EOPNOTSUPP;
+
+	switch (cmd) {
+	case TCP_AO:
+		return tcp_ao_add_cmd(sk, family, optval, optlen);
+	case TCP_AO_DEL:
+		return tcp_ao_del_cmd(sk, family, optval, optlen);
+	case TCP_AO_MOD:
+		return tcp_ao_mod_cmd(sk, family, optval, optlen);
+	default:
+		WARN_ON_ONCE(1);
+		return -EINVAL;
+	}
+}
+
+int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen)
+{
+	return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen);
+}
+
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 6bafe7429902..755154523ffc 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2231,11 +2231,16 @@ const struct inet_connection_sock_af_ops ipv4_specific = {
 };
 EXPORT_SYMBOL(ipv4_specific);
 
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
 static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
+#ifdef CONFIG_TCP_MD5SIG
 	.md5_lookup		= tcp_v4_md5_lookup,
 	.calc_md5_hash		= tcp_v4_md5_hash_skb,
 	.md5_parse		= tcp_v4_parse_md5_keys,
+#endif
+#ifdef CONFIG_TCP_AO
+	.ao_parse		= tcp_v4_parse_ao,
+#endif
 };
 #endif
 
@@ -2301,6 +2306,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
 		rcu_assign_pointer(tp->md5sig_info, NULL);
 	}
 #endif
+	tcp_ao_destroy_sock(sk);
 
 	/* Clean up a referenced TCP bind bucket. */
 	if (inet_csk(sk)->icsk_bind_hash)
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 3036a45e8a1e..68a991499e63 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -53,3 +53,4 @@ ifneq ($(CONFIG_IPV6),)
 obj-$(CONFIG_NET_UDP_TUNNEL) += ip6_udp_tunnel.o
 obj-y += mcast_snoop.o
 endif
+obj-$(CONFIG_TCP_AO) += tcp_ao.o
diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c
new file mode 100644
index 000000000000..f9f242a7e0f2
--- /dev/null
+++ b/net/ipv6/tcp_ao.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * INET		An implementation of the TCP Authentication Option (TCP-AO).
+ *		See RFC5925.
+ *
+ * Authors:	Dmitry Safonov <dima@arista.com>
+ *		Francesco Ruggeri <fruggeri@arista.com>
+ *		Salam Noureddine <noureddine@arista.com>
+ */
+#include <linux/tcp.h>
+#include <crypto/pool.h>
+
+#include <net/tcp.h>
+#include <net/ipv6.h>
+
+int tcp_v6_parse_ao(struct sock *sk, int cmd,
+		    sockptr_t optval, int optlen)
+{
+	return tcp_parse_ao(sk, cmd, AF_INET6, optval, optlen);
+}
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index f75569f889e7..741cbeb52117 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1883,11 +1883,16 @@ const struct inet_connection_sock_af_ops ipv6_specific = {
 	.mtu_reduced	   = tcp_v6_mtu_reduced,
 };
 
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
 static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
+#ifdef CONFIG_TCP_MD5SIG
 	.md5_lookup	=	tcp_v6_md5_lookup,
 	.calc_md5_hash	=	tcp_v6_md5_hash_skb,
 	.md5_parse	=	tcp_v6_parse_md5_keys,
+#endif
+#ifdef CONFIG_TCP_AO
+	.ao_parse	=	tcp_v6_parse_ao,
+#endif
 };
 #endif
 
@@ -1909,11 +1914,16 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = {
 	.mtu_reduced	   = tcp_v4_mtu_reduced,
 };
 
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
 static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
+#ifdef CONFIG_TCP_MD5SIG
 	.md5_lookup	=	tcp_v4_md5_lookup,
 	.calc_md5_hash	=	tcp_v4_md5_hash_skb,
 	.md5_parse	=	tcp_v6_parse_md5_keys,
+#endif
+#ifdef CONFIG_TCP_AO
+	.ao_parse	=	tcp_v6_parse_ao,
+#endif
 };
 #endif
 
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 09/31] net/tcp: Prevent TCP-MD5 with TCP-AO being set
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (7 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 10/31] net/tcp: Calculate TCP-AO traffic keys Dmitry Safonov
                   ` (22 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Be as conservative as possible: if there is TCP-MD5 key for a given peer
regardless of L3 interface - don't allow setting TCP-AO key for the same
peer. According to RFC5925, TCP-AO is supposed to replace TCP-MD5 and
there can't be any switch between both on any connected tuple.
Later it can be relaxed, if there's a use, but in the beginning restrict
any intersection.

Note: it's still should be possible to set both TCP-MD5 and TCP-AO keys
on a listening socket for *different* peers.

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>
---
 include/net/tcp.h     | 23 +++++++++++++++++++++--
 include/net/tcp_ao.h  | 15 +++++++++++++++
 net/ipv4/tcp_ao.c     | 12 ++++++++++++
 net/ipv4/tcp_ipv4.c   | 10 ++++++++--
 net/ipv4/tcp_output.c | 22 ++++++++++++++++++++++
 net/ipv6/tcp_ao.c     | 17 +++++++++++++++++
 net/ipv6/tcp_ipv6.c   | 22 ++++++++++++++++++----
 7 files changed, 113 insertions(+), 8 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index 278d0ab81796..9c71f48cc99c 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1670,14 +1670,23 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
 extern struct static_key_false_deferred tcp_md5_needed;
 struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,
 					   const union tcp_md5_addr *addr,
-					   int family);
+					   int family, bool any_l3index);
 static inline struct tcp_md5sig_key *
 tcp_md5_do_lookup(const struct sock *sk, int l3index,
 		  const union tcp_md5_addr *addr, int family)
 {
 	if (!static_branch_unlikely(&tcp_md5_needed.key))
 		return NULL;
-	return __tcp_md5_do_lookup(sk, l3index, addr, family);
+	return __tcp_md5_do_lookup(sk, l3index, addr, family, false);
+}
+
+static inline struct tcp_md5sig_key *
+tcp_md5_do_lookup_any_l3index(const struct sock *sk,
+			      const union tcp_md5_addr *addr, int family)
+{
+	if (!static_branch_unlikely(&tcp_md5_needed.key))
+		return NULL;
+	return __tcp_md5_do_lookup(sk, 0, addr, family, true);
 }
 
 enum skb_drop_reason
@@ -1695,6 +1704,13 @@ tcp_md5_do_lookup(const struct sock *sk, int l3index,
 	return NULL;
 }
 
+static inline struct tcp_md5sig_key *
+tcp_md5_do_lookup_any_l3index(const struct sock *sk,
+			      const union tcp_md5_addr *addr, int family)
+{
+	return NULL;
+}
+
 static inline enum skb_drop_reason
 tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 		     const void *saddr, const void *daddr,
@@ -2059,6 +2075,9 @@ struct tcp_sock_af_ops {
 					    int optname,
 					    sockptr_t optval,
 					    int optlen);
+	struct tcp_ao_key	*(*ao_lookup)(const struct sock *sk,
+					      struct sock  *addr_sk,
+					      int sndid, int rcvid);
 #endif
 };
 
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index 6d0d30e5542b..c550f1a6f5fd 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -91,12 +91,27 @@ struct tcp_ao_info {
 int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
 		 sockptr_t optval, int optlen);
 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, u16 port);
 /* ipv4 specific functions */
 int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen);
+struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
+				    int sndid, int rcvid);
 /* ipv6 specific functions */
+struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
+				    struct sock *addr_sk,
+				    int sndid, int rcvid);
 int tcp_v6_parse_ao(struct sock *sk, int cmd,
 		    sockptr_t optval, int optlen);
 #else
+static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
+		const union tcp_ao_addr *addr,
+		int family, int sndid, int rcvid, u16 port)
+{
+	return NULL;
+}
+
 static inline void tcp_ao_destroy_sock(struct sock *sk)
 {
 }
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 7f53417ebdf7..be68eb6b4a92 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -176,6 +176,14 @@ void tcp_ao_destroy_sock(struct sock *sk)
 	kfree_rcu(ao, rcu);
 }
 
+struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
+				    int sndid, int rcvid)
+{
+	union tcp_ao_addr *addr = (union tcp_ao_addr *)&addr_sk->sk_daddr;
+
+	return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid, 0);
+}
+
 static int tcp_ao_current_rnext(struct sock *sk, u16 tcpa_flags,
 				u8 tcpa_sndid, u8 tcpa_rcvid)
 {
@@ -607,6 +615,10 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
 	if (cmd.tcpa_keyflags & ~TCP_AO_KEYF_ALL)
 		return -EINVAL;
 
+	/* Don't allow keys for peers that have a matching TCP-MD5 key */
+	if (tcp_md5_do_lookup_any_l3index(sk, addr, family))
+		return -EKEYREJECTED;
+
 	ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info,
 					    lockdep_sock_is_held(sk));
 
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 755154523ffc..fc65d64b9570 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1065,7 +1065,7 @@ static bool better_md5_match(struct tcp_md5sig_key *old, struct tcp_md5sig_key *
 /* Find the Key structure for an address.  */
 struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,
 					   const union tcp_md5_addr *addr,
-					   int family)
+					   int family, bool any_l3index)
 {
 	const struct tcp_sock *tp = tcp_sk(sk);
 	struct tcp_md5sig_key *key;
@@ -1084,7 +1084,8 @@ struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,
 				 lockdep_sock_is_held(sk)) {
 		if (key->family != family)
 			continue;
-		if (key->flags & TCP_MD5SIG_FLAG_IFINDEX && key->l3index != l3index)
+		if (!any_l3index && key->flags & TCP_MD5SIG_FLAG_IFINDEX &&
+		    key->l3index != l3index)
 			continue;
 		if (family == AF_INET) {
 			mask = inet_make_mask(key->prefixlen);
@@ -1344,6 +1345,10 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,
 	if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
 		return -EINVAL;
 
+	/* Don't allow keys for peers that have a matching TCP-AO key */
+	if (tcp_ao_do_lookup(sk, addr, AF_INET, -1, -1, 0))
+		return -EKEYREJECTED;
+
 	return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags,
 			      cmd.tcpm_key, cmd.tcpm_keylen);
 }
@@ -2239,6 +2244,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
 	.md5_parse		= tcp_v4_parse_md5_keys,
 #endif
 #ifdef CONFIG_TCP_AO
+	.ao_lookup		= tcp_v4_ao_lookup,
 	.ao_parse		= tcp_v4_parse_ao,
 #endif
 };
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 9e12845a8758..243ff6b99d17 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -3837,6 +3837,28 @@ int tcp_connect(struct sock *sk)
 
 	tcp_call_bpf(sk, BPF_SOCK_OPS_TCP_CONNECT_CB, 0, NULL);
 
+#if defined(CONFIG_TCP_MD5SIG) && defined(CONFIG_TCP_AO)
+	/* Has to be checked late, after setting daddr/saddr/ops */
+	if (unlikely(rcu_dereference_protected(tp->md5sig_info,
+					       lockdep_sock_is_held(sk)))) {
+		bool needs_md5 = !!tp->af_specific->md5_lookup(sk, sk);
+		bool needs_ao = !!tp->af_specific->ao_lookup(sk, sk, -1, -1);
+
+		if (needs_md5 && needs_ao)
+			return -EKEYREJECTED;
+	}
+#endif
+#ifdef CONFIG_TCP_AO
+	if (unlikely(rcu_dereference_protected(tp->ao_info,
+					       lockdep_sock_is_held(sk)))) {
+		/* Don't allow connecting if ao is configured but no
+		 * matching key is found.
+		 */
+		if (tp->af_specific->ao_lookup(sk, sk, -1, -1) == NULL)
+			return -EKEYREJECTED;
+	}
+#endif
+
 	if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk))
 		return -EHOSTUNREACH; /* Routing failure or similar. */
 
diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c
index f9f242a7e0f2..221b8adb4f73 100644
--- a/net/ipv6/tcp_ao.c
+++ b/net/ipv6/tcp_ao.c
@@ -13,6 +13,23 @@
 #include <net/tcp.h>
 #include <net/ipv6.h>
 
+struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk,
+				       const struct in6_addr *addr,
+				       int sndid, int rcvid)
+{
+	return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)addr, AF_INET6,
+				sndid, rcvid, 0);
+}
+
+struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
+				    struct sock *addr_sk,
+				    int sndid, int rcvid)
+{
+	struct in6_addr *addr = &addr_sk->sk_v6_daddr;
+
+	return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid);
+}
+
 int tcp_v6_parse_ao(struct sock *sk, int cmd,
 		    sockptr_t optval, int optlen)
 {
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 741cbeb52117..f5780a3fbd1b 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -602,6 +602,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,
 {
 	struct tcp_md5sig cmd;
 	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr;
+	union tcp_ao_addr *addr;
 	int l3index = 0;
 	u8 prefixlen;
 	u8 flags;
@@ -656,13 +657,24 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,
 	if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN)
 		return -EINVAL;
 
-	if (ipv6_addr_v4mapped(&sin6->sin6_addr))
-		return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
+	if (ipv6_addr_v4mapped(&sin6->sin6_addr)) {
+		addr = (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3];
+
+		/* Don't allow keys for peers that have a matching TCP-AO key */
+		if (tcp_ao_do_lookup(sk, addr, AF_INET, -1, -1, 0))
+			return -EKEYREJECTED;
+		return tcp_md5_do_add(sk, addr,
 				      AF_INET, prefixlen, l3index, flags,
 				      cmd.tcpm_key, cmd.tcpm_keylen);
+	}
+
+	addr = (union tcp_md5_addr *)&sin6->sin6_addr;
+
+	/* Don't allow keys for peers that have a matching TCP-AO key */
+	if (tcp_ao_do_lookup(sk, addr, AF_INET6, -1, -1, 0))
+		return -EKEYREJECTED;
 
-	return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr,
-			      AF_INET6, prefixlen, l3index, flags,
+	return tcp_md5_do_add(sk, addr, AF_INET6, prefixlen, l3index, flags,
 			      cmd.tcpm_key, cmd.tcpm_keylen);
 }
 
@@ -1891,6 +1903,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
 	.md5_parse	=	tcp_v6_parse_md5_keys,
 #endif
 #ifdef CONFIG_TCP_AO
+	.ao_lookup	=	tcp_v6_ao_lookup,
 	.ao_parse	=	tcp_v6_parse_ao,
 #endif
 };
@@ -1922,6 +1935,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
 	.md5_parse	=	tcp_v6_parse_md5_keys,
 #endif
 #ifdef CONFIG_TCP_AO
+	.ao_lookup	=	tcp_v6_ao_lookup,
 	.ao_parse	=	tcp_v6_parse_ao,
 #endif
 };
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 10/31] net/tcp: Calculate TCP-AO traffic keys
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (8 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 09/31] net/tcp: Prevent TCP-MD5 with TCP-AO being set Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 11/31] net/tcp: Add TCP-AO sign to outgoing packets Dmitry Safonov
                   ` (21 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Add traffic key calculation the way it's described in RFC5926.
Wire it up to tcp_finish_connect() and cache the new keys straight away
on already established TCP connections.

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>
---
 include/net/tcp.h     |   5 ++
 include/net/tcp_ao.h  |  44 ++++++++++-
 net/ipv4/tcp_ao.c     | 176 ++++++++++++++++++++++++++++++++++++++++++
 net/ipv4/tcp_input.c  |   1 +
 net/ipv4/tcp_ipv4.c   |   1 +
 net/ipv4/tcp_output.c |   1 +
 net/ipv6/tcp_ao.c     |  40 ++++++++++
 net/ipv6/tcp_ipv6.c   |   1 +
 8 files changed, 268 insertions(+), 1 deletion(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index 9c71f48cc99c..e140ae4fe653 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -2078,6 +2078,11 @@ struct tcp_sock_af_ops {
 	struct tcp_ao_key	*(*ao_lookup)(const struct sock *sk,
 					      struct sock  *addr_sk,
 					      int sndid, int rcvid);
+	int			(*ao_calc_key_sk)(struct tcp_ao_key *mkt,
+						  u8 *key,
+						  const struct sock *sk,
+						  __be32 sisn, __be32 disn,
+						  bool send);
 #endif
 };
 
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index c550f1a6f5fd..f83a4d09a4ce 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -88,9 +88,33 @@ struct tcp_ao_info {
 };
 
 #ifdef CONFIG_TCP_AO
+/* TCP-AO structures and functions */
+
+struct tcp4_ao_context {
+	__be32		saddr;
+	__be32		daddr;
+	__be16		sport;
+	__be16		dport;
+	__be32		sisn;
+	__be32		disn;
+};
+
+struct tcp6_ao_context {
+	struct in6_addr	saddr;
+	struct in6_addr	daddr;
+	__be16		sport;
+	__be16		dport;
+	__be32		sisn;
+	__be32		disn;
+};
+
 int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
 		 sockptr_t optval, int optlen);
+int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
+			    unsigned int len);
 void tcp_ao_destroy_sock(struct sock *sk);
+int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao,
+			      struct tcp_ao_key *ao_key);
 struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 				    const union tcp_ao_addr *addr,
 				    int family, int sndid, int rcvid, u16 port);
@@ -98,13 +122,23 @@ struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen);
 struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
 				    int sndid, int rcvid);
+int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
+			  const struct sock *sk,
+			  __be32 sisn, __be32 disn, bool send);
 /* ipv6 specific functions */
+int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
+				 const struct sock *sk, __be32 sisn,
+				 __be32 disn, bool send);
 struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
 				    struct sock *addr_sk,
 				    int sndid, int rcvid);
 int tcp_v6_parse_ao(struct sock *sk, int cmd,
 		    sockptr_t optval, int optlen);
-#else
+void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb);
+void tcp_ao_connect_init(struct sock *sk);
+
+#else /* CONFIG_TCP_AO */
+
 static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 		const union tcp_ao_addr *addr,
 		int family, int sndid, int rcvid, u16 port)
@@ -115,6 +149,14 @@ static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 static inline void tcp_ao_destroy_sock(struct sock *sk)
 {
 }
+
+static inline void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
+{
+}
+
+static inline void tcp_ao_connect_init(struct sock *sk)
+{
+}
 #endif
 
 #endif /* _TCP_AO_H */
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index be68eb6b4a92..60fe385fda1c 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -16,6 +16,42 @@
 #include <net/tcp.h>
 #include <net/ipv6.h>
 
+int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
+			    unsigned int len)
+{
+	struct crypto_pool_ahash hp;
+	struct scatterlist sg;
+	int ret;
+
+	if (crypto_pool_get(mkt->crypto_pool_id, (struct crypto_pool *)&hp))
+		goto clear_hash_noput;
+
+	if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req),
+				mkt->key, mkt->keylen))
+		goto clear_hash;
+
+	ret = crypto_ahash_init(hp.req);
+	if (ret)
+		goto clear_hash;
+
+	sg_init_one(&sg, ctx, len);
+	ahash_request_set_crypt(hp.req, &sg, key, len);
+	crypto_ahash_update(hp.req);
+
+	/* TODO: Revisit on how to get different output length */
+	ret = crypto_ahash_final(hp.req);
+	if (ret)
+		goto clear_hash;
+
+	crypto_pool_put();
+	return 0;
+clear_hash:
+	crypto_pool_put();
+clear_hash_noput:
+	memset(key, 0, tcp_ao_digest_size(mkt));
+	return 1;
+}
+
 struct tcp_ao_key *tcp_ao_do_lookup_rcvid(struct sock *sk, u8 keyid)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
@@ -176,6 +212,47 @@ void tcp_ao_destroy_sock(struct sock *sk)
 	kfree_rcu(ao, rcu);
 }
 
+/* 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,
+			      __be16 sport, __be16 dport,
+			      __be32 sisn,  __be32 disn)
+{
+	/* See RFC5926 3.1.1 */
+	struct kdf_input_block {
+		u8                      counter;
+		u8                      label[6];
+		struct tcp4_ao_context	ctx;
+		__be16                  outlen;
+	} __packed tmp;
+
+	tmp.counter	= 1;
+	memcpy(tmp.label, "TCP-AO", 6);
+	tmp.ctx.saddr	= saddr;
+	tmp.ctx.daddr	= daddr;
+	tmp.ctx.sport	= sport;
+	tmp.ctx.dport	= dport;
+	tmp.ctx.sisn	= sisn;
+	tmp.ctx.disn	= disn;
+	tmp.outlen	= htons(tcp_ao_digest_size(mkt) * 8); /* in bits */
+
+	return tcp_ao_calc_traffic_key(mkt, key, &tmp, sizeof(tmp));
+}
+
+int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
+			  const struct sock *sk,
+			  __be32 sisn, __be32 disn, bool send)
+{
+	if (send)
+		return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr,
+					  sk->sk_daddr, htons(sk->sk_num),
+					  sk->sk_dport, sisn, disn);
+	else
+		return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr,
+					  sk->sk_rcv_saddr, sk->sk_dport,
+					  htons(sk->sk_num), disn, sisn);
+}
+
 struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
 				    int sndid, int rcvid)
 {
@@ -184,6 +261,99 @@ 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, 0);
 }
 
+int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao,
+			      struct tcp_ao_key *ao_key)
+{
+	u8 *traffic_key = snd_other_key(ao_key);
+	int ret;
+
+	ret = tcp_sk(sk)->af_specific->ao_calc_key_sk(ao_key, traffic_key, sk,
+						      ao->lisn, ao->risn, true);
+	if (ret)
+		return ret;
+
+	traffic_key = rcv_other_key(ao_key);
+	return tcp_sk(sk)->af_specific->ao_calc_key_sk(ao_key, traffic_key, sk,
+						       ao->lisn, ao->risn,
+						       false);
+}
+
+void tcp_ao_connect_init(struct sock *sk)
+{
+	struct tcp_ao_info *ao_info;
+	struct tcp_ao_key *key;
+	struct tcp_sock *tp = tcp_sk(sk);
+	union tcp_ao_addr *addr;
+	int family;
+
+	ao_info = rcu_dereference_protected(tp->ao_info,
+					    lockdep_sock_is_held(sk));
+	if (!ao_info)
+		return;
+
+	/* Remove all keys that don't match the peer */
+	family = sk->sk_family;
+	if (family == AF_INET)
+		addr = (union tcp_ao_addr *)&sk->sk_daddr;
+	else
+		addr = (union tcp_ao_addr *)&sk->sk_v6_daddr;
+
+	hlist_for_each_entry_rcu(key, &ao_info->head, node) {
+		if (tcp_ao_key_cmp(key, addr, key->prefixlen, family,
+		    -1, -1, sk->sk_dport) == 0)
+			continue;
+
+		if (key == ao_info->current_key)
+			ao_info->current_key = NULL;
+		if (key == ao_info->rnext_key)
+			ao_info->rnext_key = NULL;
+		hlist_del_rcu(&key->node);
+		crypto_pool_release(key->crypto_pool_id);
+		atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
+		kfree_rcu(key, rcu);
+	}
+
+	key = tp->af_specific->ao_lookup(sk, sk, -1, -1);
+	if (key) {
+		/* if current_key or rnext_key were not provided,
+		 * use the first key matching the peer
+		 */
+		if (!ao_info->current_key)
+			ao_info->current_key = key;
+		if (!ao_info->rnext_key)
+			ao_info->rnext_key = key;
+		tp->tcp_header_len += tcp_ao_len(key);
+
+		ao_info->lisn = htonl(tp->write_seq);
+		ao_info->snd_sne = 0;
+		ao_info->snd_sne_seq = tp->write_seq;
+	} else {
+		WARN_ON_ONCE(1);
+		kfree(tp->ao_info);
+		rcu_assign_pointer(tp->ao_info, NULL);
+	}
+}
+
+void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
+{
+	struct tcp_ao_info *ao;
+	struct tcp_ao_key *key;
+
+	ao = rcu_dereference_protected(tcp_sk(sk)->ao_info,
+				       lockdep_sock_is_held(sk));
+	if (!ao)
+		return;
+
+	ao->risn = tcp_hdr(skb)->seq;
+
+	ao->rcv_sne = 0;
+	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);
+	}
+}
+
 static int tcp_ao_current_rnext(struct sock *sk, u16 tcpa_flags,
 				u8 tcpa_sndid, u8 tcpa_rcvid)
 {
@@ -662,6 +832,12 @@ 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)
+		tcp_ao_cache_traffic_keys(sk, ao_info, key);
+
 	tcp_ao_link_mkt(ao_info, key);
 	if (first) {
 		sk_gso_disable(sk);
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index ab5f0ea166f1..a5d6c7f1ead3 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -6039,6 +6039,7 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb)
 	struct tcp_sock *tp = tcp_sk(sk);
 	struct inet_connection_sock *icsk = inet_csk(sk);
 
+	tcp_ao_finish_connect(sk, skb);
 	tcp_set_state(sk, TCP_ESTABLISHED);
 	icsk->icsk_ack.lrcvtime = tcp_jiffies32;
 
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index fc65d64b9570..64fea0945901 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2246,6 +2246,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
 #ifdef CONFIG_TCP_AO
 	.ao_lookup		= tcp_v4_ao_lookup,
 	.ao_parse		= tcp_v4_parse_ao,
+	.ao_calc_key_sk		= tcp_v4_ao_calc_key_sk,
 #endif
 };
 #endif
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 243ff6b99d17..ee0c642a87e5 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -3661,6 +3661,7 @@ static void tcp_connect_init(struct sock *sk)
 	if (tp->af_specific->md5_lookup(sk, sk))
 		tp->tcp_header_len += TCPOLEN_MD5SIG_ALIGNED;
 #endif
+	tcp_ao_connect_init(sk);
 
 	/* If user gave his TCP_MAXSEG, record it to clamp */
 	if (tp->rx_opt.user_mss)
diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c
index 221b8adb4f73..888ee6242334 100644
--- a/net/ipv6/tcp_ao.c
+++ b/net/ipv6/tcp_ao.c
@@ -13,6 +13,46 @@
 #include <net/tcp.h>
 #include <net/ipv6.h>
 
+int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
+		       const struct in6_addr *saddr,
+		       const struct in6_addr *daddr,
+		       __be16 sport, __be16 dport,
+		       __be32 sisn, __be32 disn)
+{
+	struct kdf_input_block {
+		u8			counter;
+		u8			label[6];
+		struct tcp6_ao_context	ctx;
+		__be16			outlen;
+	} __packed tmp;
+
+	tmp.counter	= 1;
+	memcpy(tmp.label, "TCP-AO", 6);
+	tmp.ctx.saddr	= *saddr;
+	tmp.ctx.daddr	= *daddr;
+	tmp.ctx.sport	= sport;
+	tmp.ctx.dport	= dport;
+	tmp.ctx.sisn	= sisn;
+	tmp.ctx.disn	= disn;
+	tmp.outlen	= htons(tcp_ao_digest_size(mkt) * 8); /* in bits */
+
+	return tcp_ao_calc_traffic_key(mkt, key, &tmp, sizeof(tmp));
+}
+
+int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
+			  const struct sock *sk, __be32 sisn,
+			  __be32 disn, bool send)
+{
+	if (send)
+		return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_rcv_saddr,
+					  &sk->sk_v6_daddr, htons(sk->sk_num),
+					  sk->sk_dport, sisn, disn);
+	else
+		return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_daddr,
+					  &sk->sk_v6_rcv_saddr, sk->sk_dport,
+					  htons(sk->sk_num), disn, sisn);
+}
+
 struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk,
 				       const struct in6_addr *addr,
 				       int sndid, int rcvid)
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index f5780a3fbd1b..c6d2389030f2 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1905,6 +1905,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
 #ifdef CONFIG_TCP_AO
 	.ao_lookup	=	tcp_v6_ao_lookup,
 	.ao_parse	=	tcp_v6_parse_ao,
+	.ao_calc_key_sk	=	tcp_v6_ao_calc_key_sk,
 #endif
 };
 #endif
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 11/31] net/tcp: Add TCP-AO sign to outgoing packets
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (9 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 10/31] net/tcp: Calculate TCP-AO traffic keys Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 12/31] net/tcp: Add tcp_parse_auth_options() Dmitry Safonov
                   ` (20 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Using precalculated traffic keys, sign TCP segments as prescribed by
RFC5925. Per RFC, TCP header options are included in sign calculation:
"The TCP header, by default including options, and where the TCP
checksum and TCP-AO MAC fields are set to zero, all in network-
byte order." (5.1.3)

tcp_ao_hash_header() has exclude_options parameter to optionally exclude
TCP header from hash calculation, as described in RFC5925 (9.1), this is
needed for interaction with middleboxes that may change "some TCP
options". This is wired up to AO key flags and setsockopt() later.

Similarly to TCP-MD5 hash TCP segment fragments.

From this moment a user can start sending TCP-AO signed segments with
one of crypto ahash algorithms from supported by Linux kernel. It can
have a user-specified MAC length, to either save TCP option header space
or provide higher protection using a longer signature.
The inbound segments are not yet verified, TCP-AO option is ignored and
they are accepted.

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>
---
 include/net/tcp.h     |   8 ++
 include/net/tcp_ao.h  |  14 +++
 net/ipv4/tcp_ao.c     | 209 ++++++++++++++++++++++++++++++++++++++++++
 net/ipv4/tcp_ipv4.c   |   1 +
 net/ipv4/tcp_output.c | 121 +++++++++++++++++++++---
 net/ipv6/tcp_ao.c     |  27 ++++++
 net/ipv6/tcp_ipv6.c   |   2 +
 7 files changed, 371 insertions(+), 11 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index e140ae4fe653..d91a963f430d 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -26,6 +26,7 @@
 #include <linux/kref.h>
 #include <linux/ktime.h>
 #include <linux/indirect_call_wrapper.h>
+#include <crypto/pool.h>
 
 #include <net/inet_connection_sock.h>
 #include <net/inet_timewait_sock.h>
@@ -187,6 +188,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
 #define TCPOPT_SACK             5       /* SACK Block */
 #define TCPOPT_TIMESTAMP	8	/* Better RTT estimations/PAWS */
 #define TCPOPT_MD5SIG		19	/* MD5 Signature (RFC2385) */
+#define TCPOPT_AO		29	/* Authentication Option (RFC5925) */
 #define TCPOPT_MPTCP		30	/* Multipath TCP (RFC6824) */
 #define TCPOPT_FASTOPEN		34	/* Fast open (RFC7413) */
 #define TCPOPT_EXP		254	/* Experimental */
@@ -2078,6 +2080,12 @@ struct tcp_sock_af_ops {
 	struct tcp_ao_key	*(*ao_lookup)(const struct sock *sk,
 					      struct sock  *addr_sk,
 					      int sndid, int rcvid);
+	int			(*calc_ao_hash)(char *location,
+						struct tcp_ao_key *ao,
+						const struct sock *sk,
+						const struct sk_buff *skb,
+						const u8 *tkey,
+						int hash_offset, u32 sne);
 	int			(*ao_calc_key_sk)(struct tcp_ao_key *mkt,
 						  u8 *key,
 						  const struct sock *sk,
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index f83a4d09a4ce..f840b693d038 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -108,11 +108,16 @@ struct tcp6_ao_context {
 	__be32		disn;
 };
 
+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,
+		    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);
 int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
 			    unsigned int len);
 void tcp_ao_destroy_sock(struct sock *sk);
+u32 tcp_ao_compute_sne(u32 sne, u32 seq, u32 new_seq);
 int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao,
 			      struct tcp_ao_key *ao_key);
 struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
@@ -125,13 +130,22 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
 int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 			  const struct sock *sk,
 			  __be32 sisn, __be32 disn, bool send);
+int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
+		       const struct sock *sk, const struct sk_buff *skb,
+		       const u8 *tkey, int hash_offset, u32 sne);
 /* ipv6 specific functions */
+int tcp_v6_ao_hash_pseudoheader(struct crypto_pool_ahash *hp,
+				const struct in6_addr *daddr,
+				const struct in6_addr *saddr, int nbytes);
 int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 				 const struct sock *sk, __be32 sisn,
 				 __be32 disn, bool send);
 struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
 				    struct sock *addr_sk,
 				    int sndid, int rcvid);
+int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
+		       const struct sock *sk, const struct sk_buff *skb,
+		       const u8 *tkey, int hash_offset, u32 sne);
 int tcp_v6_parse_ao(struct sock *sk, int cmd,
 		    sockptr_t optval, int optlen);
 void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb);
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 60fe385fda1c..ec3ab0c3dc77 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -253,6 +253,215 @@ int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 					  htons(sk->sk_num), disn, sisn);
 }
 
+static int tcp_v4_ao_hash_pseudoheader(struct crypto_pool_ahash *hp,
+				       __be32 daddr, __be32 saddr,
+				       int nbytes)
+{
+	struct tcp4_pseudohdr *bp;
+	struct scatterlist sg;
+
+	bp = hp->base.scratch;
+	bp->saddr = saddr;
+	bp->daddr = daddr;
+	bp->pad = 0;
+	bp->protocol = IPPROTO_TCP;
+	bp->len = cpu_to_be16(nbytes);
+
+	sg_init_one(&sg, bp, sizeof(*bp));
+	ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp));
+	return crypto_ahash_update(hp->req);
+}
+
+static int tcp_ao_hash_pseudoheader(unsigned short int family,
+				    const struct sock *sk,
+				    const struct sk_buff *skb,
+				    struct crypto_pool_ahash *hp, int nbytes)
+{
+	const struct tcphdr *th = tcp_hdr(skb);
+
+	/* TODO: Can we rely on checksum being zero to mean outbound pkt? */
+	if (!th->check) {
+		if (family == AF_INET)
+			return tcp_v4_ao_hash_pseudoheader(hp, sk->sk_daddr,
+					sk->sk_rcv_saddr, skb->len);
+		else
+			return tcp_v6_ao_hash_pseudoheader(hp, &sk->sk_v6_daddr,
+					&sk->sk_v6_rcv_saddr, skb->len);
+	}
+
+	if (family == AF_INET) {
+		const struct iphdr *iph = ip_hdr(skb);
+
+		return tcp_v4_ao_hash_pseudoheader(hp, iph->daddr,
+				iph->saddr, skb->len);
+	} else {
+		const struct ipv6hdr *iph = ipv6_hdr(skb);
+
+		return tcp_v6_ao_hash_pseudoheader(hp, &iph->daddr,
+				&iph->saddr, skb->len);
+	}
+}
+
+u32 tcp_ao_compute_sne(u32 prev_sne, u32 prev_seq, u32 seq)
+{
+	u32 sne = prev_sne;
+
+	if (before(seq, prev_seq)) {
+		if (seq > prev_seq)
+			sne--;
+	} else {
+		if (seq < prev_seq)
+			sne++;
+	}
+
+	return sne;
+}
+
+/* tcp_ao_hash_sne(struct crypto_pool_ahash *hp)
+ * @hp	- used for hashing
+ * @sne - sne value
+ */
+static int tcp_ao_hash_sne(struct crypto_pool_ahash *hp, u32 sne)
+{
+	struct scatterlist sg;
+	__be32 *bp;
+
+	bp = (__be32 *)hp->base.scratch;
+	*bp = htonl(sne);
+
+	sg_init_one(&sg, bp, sizeof(*bp));
+	ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp));
+	return crypto_ahash_update(hp->req);
+}
+
+static int tcp_ao_hash_header(struct crypto_pool_ahash *hp,
+			      const struct tcphdr *th,
+			      bool exclude_options, u8 *hash,
+			      int hash_offset, int hash_len)
+{
+	struct scatterlist sg;
+	u8 *hdr = hp->base.scratch;
+	int err, len = th->doff << 2;
+
+	/* We are not allowed to change tcphdr, make a local copy */
+	if (exclude_options) {
+		len = sizeof(*th) + sizeof(struct tcp_ao_hdr) + hash_len;
+		memcpy(hdr, th, sizeof(*th));
+		memcpy(hdr + sizeof(*th),
+		       (u8 *)th + hash_offset - sizeof(struct tcp_ao_hdr),
+		       sizeof(struct tcp_ao_hdr));
+		memset(hdr + sizeof(*th) + sizeof(struct tcp_ao_hdr),
+		       0, hash_len);
+		((struct tcphdr *)hdr)->check = 0;
+	} else {
+		len = th->doff << 2;
+		memcpy(hdr, th, len);
+		/* zero out tcp-ao hash */
+		((struct tcphdr *)hdr)->check = 0;
+		memset(hdr + hash_offset, 0, hash_len);
+	}
+
+	sg_init_one(&sg, hdr, len);
+	ahash_request_set_crypt(hp->req, &sg, NULL, len);
+	err = crypto_ahash_update(hp->req);
+	WARN_ON_ONCE(err != 0);
+	return err;
+}
+
+static int tcp_ao_hash_skb_data(struct crypto_pool_ahash *hp,
+				const struct sk_buff *skb,
+				unsigned int header_len)
+{
+	struct scatterlist sg;
+	const struct tcphdr *tp = tcp_hdr(skb);
+	struct ahash_request *req = hp->req;
+	unsigned int i;
+	const unsigned int head_data_len = skb_headlen(skb) > header_len ?
+					   skb_headlen(skb) - header_len : 0;
+	const struct skb_shared_info *shi = skb_shinfo(skb);
+	struct sk_buff *frag_iter;
+
+	WARN_ON(skb_headlen(skb) < header_len);
+
+	sg_init_table(&sg, 1);
+
+	sg_set_buf(&sg, ((u8 *)tp) + header_len, head_data_len);
+	ahash_request_set_crypt(req, &sg, NULL, head_data_len);
+	if (crypto_ahash_update(req))
+		return 1;
+
+	for (i = 0; i < shi->nr_frags; ++i) {
+		const skb_frag_t *f = &shi->frags[i];
+		unsigned int offset = skb_frag_off(f);
+		struct page *page = skb_frag_page(f) + (offset >> PAGE_SHIFT);
+
+		sg_set_page(&sg, page, skb_frag_size(f),
+			    offset_in_page(offset));
+		ahash_request_set_crypt(req, &sg, NULL, skb_frag_size(f));
+		if (crypto_ahash_update(req))
+			return 1;
+	}
+
+	skb_walk_frags(skb, frag_iter)
+		if (tcp_ao_hash_skb_data(hp, frag_iter, 0))
+			return 1;
+
+	return 0;
+}
+
+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,
+		    const u8 *tkey,  int hash_offset, u32 sne)
+{
+	const struct tcphdr *th = tcp_hdr(skb);
+	int tkey_len = tcp_ao_digest_size(key);
+	__u8 tmp_hash[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align;
+	struct crypto_pool_ahash hp;
+
+	if (crypto_pool_get(key->crypto_pool_id, (struct crypto_pool *)&hp))
+		goto clear_hash_noput;
+
+	if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len))
+		goto clear_hash;
+
+	/* For now use sha1 by default. Depends on alg in tcp_ao_key */
+	if (crypto_ahash_init(hp.req))
+		goto clear_hash;
+
+	if (tcp_ao_hash_sne(&hp, sne))
+		goto clear_hash;
+	if (tcp_ao_hash_pseudoheader(family, sk, skb, &hp, skb->len))
+		goto clear_hash;
+	if (tcp_ao_hash_header(&hp, th, false,
+			       ao_hash, hash_offset, tcp_ao_maclen(key)))
+		goto clear_hash;
+	if (tcp_ao_hash_skb_data(&hp, skb, th->doff << 2))
+		goto clear_hash;
+	ahash_request_set_crypt(hp.req, NULL, tmp_hash, 0);
+	if (crypto_ahash_final(hp.req))
+		goto clear_hash;
+
+	memcpy(ao_hash, tmp_hash, tcp_ao_maclen(key));
+	crypto_pool_put();
+	return 0;
+
+clear_hash:
+	crypto_pool_put();
+clear_hash_noput:
+	memset(ao_hash, 0, tcp_ao_maclen(key));
+	return 1;
+}
+EXPORT_SYMBOL(tcp_ao_hash_skb);
+
+int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
+		       const struct sock *sk, const struct sk_buff *skb,
+		       const u8 *tkey, int hash_offset, u32 sne)
+{
+	return tcp_ao_hash_skb(AF_INET, ao_hash, key, sk, skb,
+			       tkey, hash_offset, sne);
+}
+
 struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
 				    int sndid, int rcvid)
 {
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 64fea0945901..f6fe9ec1c99d 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2245,6 +2245,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
 #endif
 #ifdef CONFIG_TCP_AO
 	.ao_lookup		= tcp_v4_ao_lookup,
+	.calc_ao_hash		= tcp_v4_ao_hash_skb,
 	.ao_parse		= tcp_v4_parse_ao,
 	.ao_calc_key_sk		= tcp_v4_ao_calc_key_sk,
 #endif
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index ee0c642a87e5..26315cac09dd 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -413,6 +413,7 @@ static inline bool tcp_urg_mode(const struct tcp_sock *tp)
 #define OPTION_FAST_OPEN_COOKIE	BIT(8)
 #define OPTION_SMC		BIT(9)
 #define OPTION_MPTCP		BIT(10)
+#define OPTION_AO		BIT(11)
 
 static void smc_options_write(__be32 *ptr, u16 *options)
 {
@@ -605,7 +606,8 @@ static void bpf_skops_write_hdr_opt(struct sock *sk, struct sk_buff *skb,
  * (but it may well be that other scenarios fail similarly).
  */
 static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp,
-			      struct tcp_out_options *opts)
+			      struct tcp_out_options *opts,
+			      struct tcp_ao_key *ao_key)
 {
 	__be32 *ptr = (__be32 *)(th + 1);
 	u16 options = opts->options;	/* mungable copy */
@@ -617,7 +619,32 @@ static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp,
 		opts->hash_location = (__u8 *)ptr;
 		ptr += 4;
 	}
-
+#ifdef CONFIG_TCP_AO
+	if (unlikely(OPTION_AO & options)) {
+		u8 maclen;
+
+		if (tp) {
+			struct tcp_ao_info *ao_info;
+
+			ao_info = rcu_dereference_check(tp->ao_info,
+				lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk));
+			if (WARN_ON_ONCE(!ao_key || !ao_info || !ao_info->rnext_key))
+				goto out_ao;
+			maclen = tcp_ao_maclen(ao_key);
+			*ptr++ = htonl((TCPOPT_AO << 24) |
+				       (tcp_ao_len(ao_key) << 16) |
+				       (ao_key->sndid << 8) |
+				       (ao_info->rnext_key->rcvid));
+		}
+		opts->hash_location = (__u8 *)ptr;
+		ptr += maclen / sizeof(*ptr);
+		if (unlikely(maclen % sizeof(*ptr))) {
+			memset(ptr, TCPOPT_NOP, sizeof(*ptr));
+			ptr++;
+		}
+	}
+out_ao:
+#endif
 	if (unlikely(opts->mss)) {
 		*ptr++ = htonl((TCPOPT_MSS << 24) |
 			       (TCPOLEN_MSS << 16) |
@@ -758,7 +785,8 @@ static void mptcp_set_option_cond(const struct request_sock *req,
  */
 static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
 				struct tcp_out_options *opts,
-				struct tcp_md5sig_key **md5)
+				struct tcp_md5sig_key **md5,
+				struct tcp_ao_key *ao_key)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
 	unsigned int remaining = MAX_TCP_OPTION_SPACE;
@@ -775,6 +803,12 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
 		}
 	}
 #endif
+#ifdef CONFIG_TCP_AO
+	if (ao_key) {
+		opts->options |= OPTION_AO;
+		remaining -= tcp_ao_len(ao_key);
+	}
+#endif
 
 	/* We always get an MSS option.  The option bytes which will be seen in
 	 * normal data packets should timestamps be used, must be in the MSS
@@ -842,6 +876,7 @@ static unsigned int tcp_synack_options(const struct sock *sk,
 				       unsigned int mss, struct sk_buff *skb,
 				       struct tcp_out_options *opts,
 				       const struct tcp_md5sig_key *md5,
+				       const struct tcp_ao_key *ao,
 				       struct tcp_fastopen_cookie *foc,
 				       enum tcp_synack_type synack_type,
 				       struct sk_buff *syn_skb)
@@ -863,6 +898,14 @@ static unsigned int tcp_synack_options(const struct sock *sk,
 			ireq->tstamp_ok &= !ireq->sack_ok;
 	}
 #endif
+#ifdef CONFIG_TCP_AO
+	if (ao) {
+		opts->options |= OPTION_AO;
+		remaining -= tcp_ao_len(ao);
+		ireq->tstamp_ok &= !ireq->sack_ok;
+	}
+#endif
+	WARN_ON_ONCE(md5 && ao);
 
 	/* We always send an MSS option. */
 	opts->mss = mss;
@@ -912,7 +955,8 @@ static unsigned int tcp_synack_options(const struct sock *sk,
  */
 static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb,
 					struct tcp_out_options *opts,
-					struct tcp_md5sig_key **md5)
+					struct tcp_md5sig_key **md5,
+					struct tcp_ao_key *ao_key)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
 	unsigned int size = 0;
@@ -931,6 +975,12 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
 		}
 	}
 #endif
+#ifdef CONFIG_TCP_AO
+	if (ao_key) {
+		opts->options |= OPTION_AO;
+		size += tcp_ao_len(ao_key);
+	}
+#endif
 
 	if (likely(tp->rx_opt.tstamp_ok)) {
 		opts->options |= OPTION_TS;
@@ -1245,6 +1295,10 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
 	unsigned int tcp_options_size, tcp_header_size;
 	struct sk_buff *oskb = NULL;
 	struct tcp_md5sig_key *md5;
+#ifdef CONFIG_TCP_AO
+	struct tcp_ao_info *ao;
+#endif
+	struct tcp_ao_key *ao_key = NULL;
 	struct tcphdr *th;
 	u64 prior_wstamp;
 	int err;
@@ -1276,11 +1330,17 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
 	tcb = TCP_SKB_CB(skb);
 	memset(&opts, 0, sizeof(opts));
 
+#ifdef CONFIG_TCP_AO
+	ao = rcu_dereference_protected(tcp_sk(sk)->ao_info,
+				       lockdep_sock_is_held(sk));
+	if (ao)
+		ao_key = ao->current_key;
+#endif
 	if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) {
-		tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5);
+		tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5, ao_key);
 	} else {
 		tcp_options_size = tcp_established_options(sk, skb, &opts,
-							   &md5);
+							   &md5, ao_key);
 		/* Force a PSH flag on all (GSO) packets to expedite GRO flush
 		 * at receiver : This slightly improve GRO performance.
 		 * Note that we do not force the PSH flag for non GSO packets,
@@ -1354,7 +1414,7 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
 		th->window	= htons(min(tp->rcv_wnd, 65535U));
 	}
 
-	tcp_options_write(th, tp, &opts);
+	tcp_options_write(th, tp, &opts, ao_key);
 
 #ifdef CONFIG_TCP_MD5SIG
 	/* Calculate the MD5 hash, as we have all we need now */
@@ -1364,6 +1424,32 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
 					       md5, sk, skb);
 	}
 #endif
+#ifdef CONFIG_TCP_AO
+	if (ao) {
+		u8 *traffic_key;
+		u8 key_buf[TCP_AO_MAX_HASH_SIZE];
+		u32 sne;
+		__u32 disn;
+
+		sk_gso_disable(sk);
+		if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) {
+			if (tcb->tcp_flags & TCPHDR_ACK)
+				disn = ao->risn;
+			else
+				disn = 0;
+			traffic_key = key_buf;
+			tp->af_specific->ao_calc_key_sk(ao_key, traffic_key,
+							sk, ao->lisn, disn, true);
+		} else {
+			traffic_key = snd_other_key(ao_key);
+		}
+		sne = tcp_ao_compute_sne(ao->snd_sne, ao->snd_sne_seq,
+					 ntohl(th->seq));
+		tp->af_specific->calc_ao_hash(opts.hash_location, ao_key, sk, skb,
+					      traffic_key,
+					      opts.hash_location - (u8 *)th, sne);
+	}
+#endif
 
 	/* BPF prog is the last one writing header option */
 	bpf_skops_write_hdr_opt(sk, skb, NULL, NULL, 0, &opts);
@@ -1825,6 +1911,10 @@ unsigned int tcp_current_mss(struct sock *sk)
 	unsigned int header_len;
 	struct tcp_out_options opts;
 	struct tcp_md5sig_key *md5;
+	struct tcp_ao_key *ao_key = NULL;
+#ifdef CONFIG_TCP_AO
+	struct tcp_ao_info *ao_info;
+#endif
 
 	mss_now = tp->mss_cache;
 
@@ -1833,8 +1923,17 @@ unsigned int tcp_current_mss(struct sock *sk)
 		if (mtu != inet_csk(sk)->icsk_pmtu_cookie)
 			mss_now = tcp_sync_mss(sk, mtu);
 	}
-
-	header_len = tcp_established_options(sk, NULL, &opts, &md5) +
+#ifdef CONFIG_TCP_AO
+	ao_info = rcu_dereference_check(tp->ao_info, lockdep_sock_is_held(sk));
+	if (ao_info)
+		/* TODO: verify if we can access current_key or we need to pass
+		 * it from every caller of tcp_current_mss instead. The reason
+		 * is that the current_key pointer can change asynchronously
+		 * from the rx path.
+		 */
+		ao_key = ao_info->current_key;
+#endif
+	header_len = tcp_established_options(sk, NULL, &opts, &md5, ao_key) +
 		     sizeof(struct tcphdr);
 	/* The mss_cache is sized based on tp->tcp_header_len, which assumes
 	 * some common options. If this is an odd packet (because we have SACK
@@ -3580,7 +3679,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
 	/* bpf program will be interested in the tcp_flags */
 	TCP_SKB_CB(skb)->tcp_flags = TCPHDR_SYN | TCPHDR_ACK;
 	tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, md5,
-					     foc, synack_type,
+					     NULL, foc, synack_type,
 					     syn_skb) + sizeof(*th);
 
 	skb_push(skb, tcp_header_size);
@@ -3601,7 +3700,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
 
 	/* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
 	th->window = htons(min(req->rsk_rcv_wnd, 65535U));
-	tcp_options_write(th, NULL, &opts);
+	tcp_options_write(th, NULL, &opts, NULL);
 	th->doff = (tcp_header_size >> 2);
 	__TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS);
 
diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c
index 888ee6242334..7fd31c60488a 100644
--- a/net/ipv6/tcp_ao.c
+++ b/net/ipv6/tcp_ao.c
@@ -70,6 +70,33 @@ struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
 	return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid);
 }
 
+int tcp_v6_ao_hash_pseudoheader(struct crypto_pool_ahash *hp,
+				const struct in6_addr *daddr,
+				const struct in6_addr *saddr, int nbytes)
+{
+	struct tcp6_pseudohdr *bp;
+	struct scatterlist sg;
+
+	bp = hp->base.scratch;
+	/* 1. TCP pseudo-header (RFC2460) */
+	bp->saddr = *saddr;
+	bp->daddr = *daddr;
+	bp->len = cpu_to_be32(nbytes);
+	bp->protocol = cpu_to_be32(IPPROTO_TCP);
+
+	sg_init_one(&sg, bp, sizeof(*bp));
+	ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp));
+	return crypto_ahash_update(hp->req);
+}
+
+int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
+		       const struct sock *sk, const struct sk_buff *skb,
+		       const u8 *tkey, int hash_offset, u32 sne)
+{
+	return tcp_ao_hash_skb(AF_INET6, ao_hash, key, sk, skb, tkey,
+			hash_offset, sne);
+}
+
 int tcp_v6_parse_ao(struct sock *sk, int cmd,
 		    sockptr_t optval, int optlen)
 {
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index c6d2389030f2..b5fa5ae53a47 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1904,6 +1904,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
 #endif
 #ifdef CONFIG_TCP_AO
 	.ao_lookup	=	tcp_v6_ao_lookup,
+	.calc_ao_hash	=	tcp_v6_ao_hash_skb,
 	.ao_parse	=	tcp_v6_parse_ao,
 	.ao_calc_key_sk	=	tcp_v6_ao_calc_key_sk,
 #endif
@@ -1937,6 +1938,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
 #endif
 #ifdef CONFIG_TCP_AO
 	.ao_lookup	=	tcp_v6_ao_lookup,
+	.calc_ao_hash	=	tcp_v6_ao_hash_skb,
 	.ao_parse	=	tcp_v6_parse_ao,
 #endif
 };
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 12/31] net/tcp: Add tcp_parse_auth_options()
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (10 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 11/31] net/tcp: Add TCP-AO sign to outgoing packets Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 19:00   ` kernel test robot
  2022-08-18 16:59 ` [PATCH 13/31] net/tcp: Add AO sign to RST packets Dmitry Safonov
                   ` (19 subsequent siblings)
  31 siblings, 1 reply; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Introduce a helper that:
(1) shares the common code with TCP-MD5 header options parsing
(2) looks for hash signature only once for both TCP-MD5 and TCP-AO
(3) fails with -EEXIST if any TCP sign option is present twice, see
    RFC5925 (2.2):
    ">> A single TCP segment MUST NOT have more than one TCP-AO in its
    options sequence. When multiple TCP-AOs appear, TCP MUST discard
    the segment."

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>
---
 include/net/tcp.h    | 24 +++++++++++++++++++++++-
 include/net/tcp_ao.h |  3 +++
 net/ipv4/tcp.c       |  3 ++-
 net/ipv4/tcp_input.c | 39 +++++++++++++++++++++++++++++----------
 net/ipv4/tcp_ipv4.c  | 21 ++++++++++++++-------
 net/ipv6/tcp_ipv6.c  | 17 +++++++++++------
 6 files changed, 82 insertions(+), 25 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index d91a963f430d..061fe8471bfc 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -428,7 +428,6 @@ int tcp_mmap(struct file *file, struct socket *sock,
 void tcp_parse_options(const struct net *net, const struct sk_buff *skb,
 		       struct tcp_options_received *opt_rx,
 		       int estab, struct tcp_fastopen_cookie *foc);
-const u8 *tcp_parse_md5sig_option(const struct tcphdr *th);
 
 /*
  *	BPF SKB-less helpers
@@ -2467,4 +2466,27 @@ static inline u64 tcp_transmit_time(const struct sock *sk)
 	return 0;
 }
 
+static inline int tcp_parse_auth_options(const struct tcphdr *th,
+		const u8 **md5_hash, const struct tcp_ao_hdr **aoh)
+{
+	const u8 *md5_tmp, *ao_tmp;
+	int ret;
+
+	ret = tcp_do_parse_auth_options(th, &md5_tmp, &ao_tmp);
+	if (ret)
+		return ret;
+
+	if (md5_hash)
+		*md5_hash = md5_tmp;
+
+	if (aoh) {
+		if (!ao_tmp)
+			*aoh = NULL;
+		else
+			*aoh = (struct tcp_ao_hdr *)(ao_tmp - 2);
+	}
+
+	return 0;
+}
+
 #endif	/* _TCP_H */
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index f840b693d038..b5516c83e489 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -87,6 +87,9 @@ struct tcp_ao_info {
 	u32			rcv_sne_seq;
 };
 
+int tcp_do_parse_auth_options(const struct tcphdr *th,
+			      const u8 **md5_hash, const u8 **ao_hash);
+
 #ifdef CONFIG_TCP_AO
 /* TCP-AO structures and functions */
 
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 85854b8afc47..a5e94d8e8450 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -4526,7 +4526,8 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 	l3index = sdif ? dif : 0;
 
 	hash_expected = tcp_md5_do_lookup(sk, l3index, saddr, family);
-	hash_location = tcp_parse_md5sig_option(th);
+	if (tcp_parse_auth_options(th, &hash_location, NULL))
+		return true;
 
 	/* We've parsed the options - do we have a hash? */
 	if (!hash_expected && !hash_location)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index a5d6c7f1ead3..27e95876cba1 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -4188,39 +4188,58 @@ static bool tcp_fast_parse_options(const struct net *net,
 	return true;
 }
 
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
 /*
- * Parse MD5 Signature option
+ * Parse Signature options
  */
-const u8 *tcp_parse_md5sig_option(const struct tcphdr *th)
+int tcp_do_parse_auth_options(const struct tcphdr *th,
+			      const u8 **md5_hash, const u8 **ao_hash)
 {
 	int length = (th->doff << 2) - sizeof(*th);
 	const u8 *ptr = (const u8 *)(th + 1);
+	unsigned int minlen = TCPOLEN_MD5SIG;
+
+	if (IS_ENABLED(CONFIG_TCP_AO))
+		minlen = sizeof(struct tcp_ao_hdr) + 1;
+
+	*md5_hash = NULL;
+	*ao_hash = NULL;
 
 	/* If not enough data remaining, we can short cut */
-	while (length >= TCPOLEN_MD5SIG) {
+	while (length >= minlen) {
 		int opcode = *ptr++;
 		int opsize;
 
 		switch (opcode) {
 		case TCPOPT_EOL:
-			return NULL;
+			return 0;
 		case TCPOPT_NOP:
 			length--;
 			continue;
 		default:
 			opsize = *ptr++;
 			if (opsize < 2 || opsize > length)
-				return NULL;
-			if (opcode == TCPOPT_MD5SIG)
-				return opsize == TCPOLEN_MD5SIG ? ptr : NULL;
+				return -EINVAL;
+			if (opcode == TCPOPT_MD5SIG) {
+				if (opsize != TCPOLEN_MD5SIG)
+					return -EINVAL;
+				if (unlikely(*md5_hash || *ao_hash))
+					return -EEXIST;
+				*md5_hash = ptr;
+			} else if (opcode == TCPOPT_AO) {
+				if (opsize <= sizeof(struct tcp_ao_hdr))
+					return -EINVAL;
+				if (unlikely(*md5_hash || *ao_hash))
+					return -EEXIST;
+				*ao_hash = ptr;
+			}
 		}
 		ptr += opsize - 2;
 		length -= opsize;
 	}
-	return NULL;
+	return 0;
 }
-EXPORT_SYMBOL(tcp_parse_md5sig_option);
+EXPORT_SYMBOL(tcp_do_parse_auth_options);
 #endif
 
 /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index f6fe9ec1c99d..034549c73beb 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -660,7 +660,9 @@ EXPORT_SYMBOL(tcp_v4_send_check);
  *	Exception: precedence violation. We do not implement it in any case.
  */
 
-#ifdef CONFIG_TCP_MD5SIG
+#ifdef CONFIG_TCP_AO
+#define OPTION_BYTES MAX_TCP_OPTION_SPACE
+#elif CONFIG_TCP_MD5SIG
 #define OPTION_BYTES TCPOLEN_MD5SIG_ALIGNED
 #else
 #define OPTION_BYTES sizeof(__be32)
@@ -676,7 +678,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
 	struct ip_reply_arg arg;
 #ifdef CONFIG_TCP_MD5SIG
 	struct tcp_md5sig_key *key = NULL;
-	const __u8 *hash_location = NULL;
+	const __u8 *md5_hash_location = NULL;
 	unsigned char newhash[16];
 	int genhash;
 	struct sock *sk1 = NULL;
@@ -715,9 +717,14 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
 	arg.iov[0].iov_len  = sizeof(rep.th);
 
 	net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
+	/* Invalid TCP option size or twice included auth */
+	if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, NULL))
+		return;
+
 	rcu_read_lock();
-	hash_location = tcp_parse_md5sig_option(th);
+#endif
+#ifdef CONFIG_TCP_MD5SIG
 	if (sk && sk_fullsock(sk)) {
 		const union tcp_md5_addr *addr;
 		int l3index;
@@ -728,7 +735,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
 		l3index = tcp_v4_sdif(skb) ? inet_iif(skb) : 0;
 		addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr;
 		key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET);
-	} else if (hash_location) {
+	} else if (md5_hash_location) {
 		const union tcp_md5_addr *addr;
 		int sdif = tcp_v4_sdif(skb);
 		int dif = inet_iif(skb);
@@ -760,7 +767,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
 
 
 		genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
-		if (genhash || memcmp(hash_location, newhash, 16) != 0)
+		if (genhash || memcmp(md5_hash_location, newhash, 16) != 0)
 			goto out;
 
 	}
@@ -835,7 +842,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
 	__TCP_INC_STATS(net, TCP_MIB_OUTRSTS);
 	local_bh_enable();
 
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
 out:
 	rcu_read_unlock();
 #endif
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index b5fa5ae53a47..ba968e856ca9 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -982,7 +982,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 	u32 seq = 0, ack_seq = 0;
 	struct tcp_md5sig_key *key = NULL;
 #ifdef CONFIG_TCP_MD5SIG
-	const __u8 *hash_location = NULL;
+	const __u8 *md5_hash_location = NULL;
 	unsigned char newhash[16];
 	int genhash;
 	struct sock *sk1 = NULL;
@@ -1002,9 +1002,14 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 		return;
 
 	net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
+	/* Invalid TCP option size or twice included auth */
+	if (tcp_parse_auth_options(th, &md5_hash_location, NULL))
+		return;
+
 	rcu_read_lock();
-	hash_location = tcp_parse_md5sig_option(th);
+#endif
+#ifdef CONFIG_TCP_MD5SIG
 	if (sk && sk_fullsock(sk)) {
 		int l3index;
 
@@ -1013,7 +1018,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 		 */
 		l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0;
 		key = tcp_v6_md5_do_lookup(sk, &ipv6h->saddr, l3index);
-	} else if (hash_location) {
+	} else if (md5_hash_location) {
 		int dif = tcp_v6_iif_l3_slave(skb);
 		int sdif = tcp_v6_sdif(skb);
 		int l3index;
@@ -1043,7 +1048,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 			goto out;
 
 		genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb);
-		if (genhash || memcmp(hash_location, newhash, 16) != 0)
+		if (genhash || memcmp(md5_hash_location, newhash, 16) != 0)
 			goto out;
 	}
 #endif
@@ -1076,7 +1081,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 	tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, oif, key, 1,
 			     ipv6_get_dsfield(ipv6h), label, priority);
 
-#ifdef CONFIG_TCP_MD5SIG
+#if  defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
 out:
 	rcu_read_unlock();
 #endif
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 13/31] net/tcp: Add AO sign to RST packets
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (11 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 12/31] net/tcp: Add tcp_parse_auth_options() Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 14/31] net/tcp: Add TCP-AO sign to twsk Dmitry Safonov
                   ` (18 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Wire up sending resets to TCP-AO hashing.

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>
---
 include/net/tcp_ao.h |  7 ++++
 net/ipv4/tcp_ao.c    | 48 +++++++++++++++++++++++++++
 net/ipv4/tcp_ipv4.c  | 48 ++++++++++++++++++++++++++-
 net/ipv6/tcp_ipv6.c  | 77 ++++++++++++++++++++++++++++++++++++++++++--
 4 files changed, 176 insertions(+), 4 deletions(-)

diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index b5516c83e489..35c33f7e9c27 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -117,6 +117,7 @@ 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_do_lookup_sndid(const struct sock *sk, u8 keyid);
 int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
 			    unsigned int len);
 void tcp_ao_destroy_sock(struct sock *sk);
@@ -126,6 +127,12 @@ int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao,
 struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 				    const union tcp_ao_addr *addr,
 				    int family, int sndid, int rcvid, u16 port);
+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);
+
 /* ipv4 specific functions */
 int tcp_v4_parse_ao(struct sock *sk, int optname, 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 ec3ab0c3dc77..4bdd97536610 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -409,6 +409,54 @@ static int tcp_ao_hash_skb_data(struct crypto_pool_ahash *hp,
 	return 0;
 }
 
+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)
+{
+	struct crypto_pool_ahash hp;
+	int tkey_len = tcp_ao_digest_size(key);
+	int hash_offset = ao_hash - (char *)th;
+
+	if (crypto_pool_get(key->crypto_pool_id, (struct crypto_pool *)&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;
+	} else if (family == AF_INET6) {
+		if (tcp_v6_ao_hash_pseudoheader(&hp, &daddr->a6,
+						&saddr->a6, th->doff * 4))
+			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, ao_hash, 0);
+	if (crypto_ahash_final(hp.req))
+		goto clear_hash;
+
+	crypto_pool_put();
+	return 0;
+
+clear_hash:
+	crypto_pool_put();
+clear_hash_noput:
+	memset(ao_hash, 0, tcp_ao_maclen(key));
+	return 1;
+}
+EXPORT_SYMBOL(tcp_ao_hash_hdr);
+
 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,
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 034549c73beb..c0add782d7af 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -679,11 +679,17 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
 #ifdef CONFIG_TCP_MD5SIG
 	struct tcp_md5sig_key *key = NULL;
 	const __u8 *md5_hash_location = NULL;
+	const struct tcp_ao_hdr *aoh;
 	unsigned char newhash[16];
 	int genhash;
 	struct sock *sk1 = NULL;
 #endif
 	u64 transmit_time = 0;
+#ifdef CONFIG_TCP_AO
+	struct tcp_ao_key *ao_key = NULL;
+	char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align;
+	u32 ao_sne;
+#endif
 	struct sock *ctl_sk;
 	struct net *net;
 
@@ -719,7 +725,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
 	net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
 #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
 	/* Invalid TCP option size or twice included auth */
-	if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, NULL))
+	if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, &aoh))
 		return;
 
 	rcu_read_lock();
@@ -785,6 +791,46 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
 				     key, ip_hdr(skb)->saddr,
 				     ip_hdr(skb)->daddr, &rep.th);
 	}
+#endif
+#ifdef CONFIG_TCP_AO
+	if (sk && aoh && sk->sk_state != TCP_LISTEN) {
+		/* lookup key based on peer address and rcv_next*/
+		ao_key = tcp_ao_do_lookup_sndid(sk, aoh->rnext_keyid);
+
+		if (ao_key) {
+			struct tcp_ao_info *ao_info;
+			u8 keyid;
+
+			ao_info = rcu_dereference(tcp_sk(sk)->ao_info);
+
+			/* XXX: optimize by using cached traffic key depending
+			 * on socket state
+			 */
+			tcp_v4_ao_calc_key_sk(ao_key, traffic_key, sk,
+					      ao_info->lisn, ao_info->risn,
+					      true);
+
+			/* rcv_next holds the rcv_next of the peer, make keyid
+			 * hold our rcv_next
+			 */
+			keyid = ao_info->rnext_key->rcvid;
+			ao_sne = tcp_ao_compute_sne(ao_info->snd_sne,
+						    ao_info->snd_sne_seq,
+						    ntohl(rep.th.seq));
+
+			rep.opt[0] = htonl((TCPOPT_AO << 24) |
+					   (tcp_ao_len(ao_key) << 16) |
+					   (aoh->rnext_keyid << 8) | keyid);
+			arg.iov[0].iov_len += round_up(tcp_ao_len(ao_key), 4);
+			rep.th.doff = arg.iov[0].iov_len / 4;
+
+			tcp_ao_hash_hdr(AF_INET, (char *)&rep.opt[1],
+					ao_key, traffic_key,
+					(union tcp_ao_addr *)&ip_hdr(skb)->saddr,
+					(union tcp_ao_addr *)&ip_hdr(skb)->daddr,
+					&rep.th, ao_sne);
+		}
+	}
 #endif
 	/* Can't co-exist with TCPMD5, hence check rep.opt[0] */
 	if (rep.opt[0] == 0) {
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index ba968e856ca9..f5d339d5291a 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -847,7 +847,9 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
 static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 seq,
 				 u32 ack, u32 win, u32 tsval, u32 tsecr,
 				 int oif, struct tcp_md5sig_key *key, int rst,
-				 u8 tclass, __be32 label, u32 priority)
+				 u8 tclass, __be32 label, u32 priority,
+				 struct tcp_ao_key *ao_key, char *tkey,
+				 u8 rcv_next, u32 ao_sne)
 {
 	const struct tcphdr *th = tcp_hdr(skb);
 	struct tcphdr *t1;
@@ -866,6 +868,13 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32
 	if (key)
 		tot_len += TCPOLEN_MD5SIG_ALIGNED;
 #endif
+#ifdef CONFIG_TCP_AO
+	if (ao_key)
+		tot_len += tcp_ao_len(ao_key);
+#endif
+#if defined(CONFIG_TCP_MD5SIG) && defined(CONFIG_TCP_AO)
+	WARN_ON_ONCE(key && ao_key);
+#endif
 
 #ifdef CONFIG_MPTCP
 	if (rst && !key) {
@@ -917,6 +926,21 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32
 				    &ipv6_hdr(skb)->daddr, t1);
 	}
 #endif
+#ifdef CONFIG_TCP_AO
+	if (ao_key) {
+		*topt++ = htonl((TCPOPT_AO << 24) | (tcp_ao_len(ao_key) << 16) |
+				(ao_key->sndid << 8) | (rcv_next));
+
+		/* TODO: this is right now not going to work for listening
+		 * sockets since the socket won't have the needed ipv6
+		 * addresses
+		 */
+		tcp_ao_hash_hdr(AF_INET6, (char *)topt, ao_key, tkey,
+				(union tcp_ao_addr *)&ipv6_hdr(skb)->saddr,
+				(union tcp_ao_addr *)&ipv6_hdr(skb)->daddr,
+				t1, ao_sne);
+	}
+#endif
 
 	memset(&fl6, 0, sizeof(fl6));
 	fl6.daddr = ipv6_hdr(skb)->saddr;
@@ -990,6 +1014,15 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 	__be32 label = 0;
 	u32 priority = 0;
 	struct net *net;
+	struct tcp_ao_key *ao_key = NULL;
+	u8 rcv_next = 0;
+	u32 ao_sne = 0;
+#ifdef CONFIG_TCP_AO
+	char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align;
+	const struct tcp_ao_hdr *aoh;
+#else
+	u8 *traffic_key = NULL;
+#endif
 	int oif = 0;
 
 	if (th->rst)
@@ -1004,8 +1037,13 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 	net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
 #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
 	/* Invalid TCP option size or twice included auth */
+#if defined(CONFIG_TCP_AO)
+	if (tcp_parse_auth_options(th, &md5_hash_location, &aoh))
+		return;
+#else
 	if (tcp_parse_auth_options(th, &md5_hash_location, NULL))
 		return;
+#endif
 
 	rcu_read_lock();
 #endif
@@ -1059,6 +1097,38 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 		ack_seq = ntohl(th->seq) + th->syn + th->fin + skb->len -
 			  (th->doff << 2);
 
+#ifdef CONFIG_TCP_AO
+	/* XXX: Not implemented for listening sockets yet. How do we
+	 * get the initial sequence numbers? Might need to pass in
+	 * the request socket.
+	 */
+	if (sk && aoh && sk->sk_state != TCP_LISTEN) {
+		struct tcp_ao_info *ao_info;
+
+		if (WARN_ON_ONCE(sk->sk_state == TCP_NEW_SYN_RECV))
+			goto out;
+
+		/* rcv_next is the peer's here */
+		ao_key = tcp_ao_do_lookup_sndid(sk, aoh->rnext_keyid);
+
+		if (ao_key) {
+			ao_info = rcu_dereference(tcp_sk(sk)->ao_info);
+
+			/* XXX: optimize by using cached traffic key depending
+			 * on socket state
+			 */
+			tcp_v6_ao_calc_key_sk(ao_key, traffic_key, sk,
+					      ao_info->lisn, ao_info->risn,
+					      true);
+
+			/* rcv_next switches to our rcv_next */
+			rcv_next = ao_info->rnext_key->rcvid;
+			ao_sne = tcp_ao_compute_sne(ao_info->snd_sne,
+						    ao_info->snd_sne_seq, seq);
+		}
+	}
+#endif
+
 	if (sk) {
 		oif = sk->sk_bound_dev_if;
 		if (sk_fullsock(sk)) {
@@ -1079,7 +1149,8 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 	}
 
 	tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, oif, key, 1,
-			     ipv6_get_dsfield(ipv6h), label, priority);
+			     ipv6_get_dsfield(ipv6h), label, priority,
+			     ao_key, traffic_key, rcv_next, ao_sne);
 
 #if  defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
 out:
@@ -1093,7 +1164,7 @@ static void tcp_v6_send_ack(const struct sock *sk, struct sk_buff *skb, u32 seq,
 			    __be32 label, u32 priority)
 {
 	tcp_v6_send_response(sk, skb, seq, ack, win, tsval, tsecr, oif, key, 0,
-			     tclass, label, priority);
+			     tclass, label, priority, NULL, NULL, 0, 0);
 }
 
 static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 14/31] net/tcp: Add TCP-AO sign to twsk
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (12 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 13/31] net/tcp: Add AO sign to RST packets Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 15/31] net/tcp: Wire TCP-AO to request sockets Dmitry Safonov
                   ` (17 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

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>
---
 include/linux/tcp.h      |  3 ++
 include/net/tcp_ao.h     | 11 +++++--
 net/ipv4/tcp_ao.c        | 47 ++++++++++++++++++++++----
 net/ipv4/tcp_ipv4.c      | 71 ++++++++++++++++++++++++++++++++++++----
 net/ipv4/tcp_minisocks.c |  4 ++-
 net/ipv6/tcp_ipv6.c      | 47 +++++++++++++++++++++++---
 6 files changed, 161 insertions(+), 22 deletions(-)

diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index c8a8aaaf725b..8031995b58a2 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -489,6 +489,9 @@ struct tcp_timewait_sock {
 #ifdef CONFIG_TCP_MD5SIG
 	struct tcp_md5sig_key	  *tw_md5_key;
 #endif
+#ifdef CONFIG_TCP_AO
+	struct tcp_ao_info	  *ao_info;
+#endif
 };
 
 static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk)
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index 35c33f7e9c27..af82b4aeef11 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -85,6 +85,7 @@ struct tcp_ao_info {
 	u32			snd_sne_seq;
 	u32			rcv_sne;
 	u32			rcv_sne_seq;
+	atomic_t		refcnt;		/* Protects twsk destruction */
 };
 
 int tcp_do_parse_auth_options(const struct tcphdr *th,
@@ -120,8 +121,9 @@ int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
 struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid);
 int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
 			    unsigned int len);
-void tcp_ao_destroy_sock(struct sock *sk);
+void tcp_ao_destroy_sock(struct sock *sk, bool twsk);
 u32 tcp_ao_compute_sne(u32 sne, u32 seq, u32 new_seq);
+void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
 int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao,
 			      struct tcp_ao_key *ao_key);
 struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
@@ -170,7 +172,7 @@ static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 	return NULL;
 }
 
-static inline void tcp_ao_destroy_sock(struct sock *sk)
+static inline void tcp_ao_destroy_sock(struct sock *sk, bool twsk)
 {
 }
 
@@ -178,6 +180,11 @@ static inline void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
 {
 }
 
+static inline void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw,
+				    struct tcp_sock *tp)
+{
+}
+
 static inline void tcp_ao_connect_init(struct sock *sk)
 {
 }
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 4bdd97536610..528b6650c72c 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -75,8 +75,13 @@ struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid)
 	struct tcp_ao_key *key;
 	struct tcp_ao_info *ao;
 
-	ao = rcu_dereference_check(tcp_sk(sk)->ao_info,
-				   lockdep_sock_is_held(sk));
+	if (sk->sk_state == TCP_TIME_WAIT)
+		ao = rcu_dereference_check(tcp_twsk(sk)->ao_info,
+					   lockdep_sock_is_held(sk));
+	else
+		ao = rcu_dereference_check(tcp_sk(sk)->ao_info,
+					   lockdep_sock_is_held(sk));
+
 	if (!ao)
 		return NULL;
 
@@ -172,6 +177,7 @@ static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags,
 	if (!ao)
 		return NULL;
 	INIT_HLIST_HEAD(&ao->head);
+	atomic_set(&ao->refcnt, 1);
 
 	if (cloned_from)
 		ao->ao_flags = cloned_from->ao_flags;
@@ -191,27 +197,54 @@ static void tcp_ao_key_free_rcu(struct rcu_head *head)
 	kfree(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 || !atomic_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);
+		}
+
+		atomic_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,
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index c0add782d7af..003c5f320bfc 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -801,7 +801,10 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
 			struct tcp_ao_info *ao_info;
 			u8 keyid;
 
-			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);
 
 			/* XXX: optimize by using cached traffic key depending
 			 * on socket state
@@ -902,16 +905,16 @@ static void tcp_v4_send_ack(const struct sock *sk,
 			    struct sk_buff *skb, u32 seq, u32 ack,
 			    u32 win, u32 tsval, u32 tsecr, int oif,
 			    struct tcp_md5sig_key *key,
+			    struct tcp_ao_key *ao_key,
+			    u8 *traffic_key,
+			    u8 rcv_next,
+			    u32 ao_sne,
 			    int reply_flags, u8 tos)
 {
 	const struct tcphdr *th = tcp_hdr(skb);
 	struct {
 		struct tcphdr th;
-		__be32 opt[(TCPOLEN_TSTAMP_ALIGNED >> 2)
-#ifdef CONFIG_TCP_MD5SIG
-			   + (TCPOLEN_MD5SIG_ALIGNED >> 2)
-#endif
-			];
+		__be32 opt[(MAX_TCP_OPTION_SPACE  >> 2)];
 	} rep;
 	struct net *net = sock_net(sk);
 	struct ip_reply_arg arg;
@@ -957,6 +960,25 @@ static void tcp_v4_send_ack(const struct sock *sk,
 				    ip_hdr(skb)->daddr, &rep.th);
 	}
 #endif
+#ifdef CONFIG_TCP_AO
+	if (ao_key) {
+		int offset = (tsecr) ? 3 : 0;
+
+		rep.opt[offset++] = htonl((TCPOPT_AO << 24) |
+					  (tcp_ao_len(ao_key) << 16) |
+					  (ao_key->sndid << 8) | rcv_next);
+		arg.iov[0].iov_len += round_up(tcp_ao_len(ao_key), 4);
+		rep.th.doff = arg.iov[0].iov_len / 4;
+
+		tcp_ao_hash_hdr(AF_INET, (char *)&rep.opt[offset],
+				ao_key, traffic_key,
+				(union tcp_ao_addr *)&ip_hdr(skb)->saddr,
+				(union tcp_ao_addr *)&ip_hdr(skb)->daddr,
+				&rep.th, ao_sne);
+	}
+	WARN_ON_ONCE(key && ao_key);
+#endif
+	/* XXX: TCP-AO: hash ACK header */
 	arg.flags = reply_flags;
 	arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr,
 				      ip_hdr(skb)->saddr, /* XXX */
@@ -990,6 +1012,36 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
 {
 	struct inet_timewait_sock *tw = inet_twsk(sk);
 	struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
+	struct tcp_ao_key *ao_key = NULL;
+	u8 *traffic_key = NULL;
+	u8 rcv_next = 0;
+	u32 ao_sne = 0;
+#ifdef CONFIG_TCP_AO
+	struct tcp_ao_info *ao_info = NULL;
+	const struct tcp_ao_hdr *aoh;
+#endif
+
+#ifdef CONFIG_TCP_AO
+	if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh))
+		goto out; /* something is wrong with the sign */
+
+	/* FIXME: we haven't verified the segment to-be-acked */
+	if (aoh)
+		ao_key = tcp_ao_do_lookup_sndid(sk, aoh->rnext_keyid);
+
+	if (ao_key) {
+		traffic_key = snd_other_key(ao_key);
+		ao_info = rcu_dereference(tcptw->ao_info);
+		/* It's possible we can get rid of computing the sne
+		 * 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,
+					    tcptw->tw_snd_nxt);
+		rcv_next = ao_info->rnext_key->rcvid;
+	}
+#endif
 
 	tcp_v4_send_ack(sk, skb,
 			tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
@@ -998,10 +1050,14 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
 			tcptw->tw_ts_recent,
 			tw->tw_bound_dev_if,
 			tcp_twsk_md5_key(tcptw),
+			ao_key, traffic_key, rcv_next, ao_sne,
 			tw->tw_transparent ? IP_REPLY_ARG_NOSRCCHECK : 0,
 			tw->tw_tos
 			);
 
+#ifdef CONFIG_TCP_AO
+out:
+#endif
 	inet_twsk_put(tw);
 }
 
@@ -1031,6 +1087,7 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
 			req->ts_recent,
 			0,
 			tcp_md5_do_lookup(sk, l3index, addr, AF_INET),
+			NULL, NULL, 0, 0,
 			inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0,
 			ip_hdr(skb)->tos);
 }
@@ -2367,7 +2424,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
 		rcu_assign_pointer(tp->md5sig_info, NULL);
 	}
 #endif
-	tcp_ao_destroy_sock(sk);
+	tcp_ao_destroy_sock(sk, false);
 
 	/* Clean up a referenced TCP bind bucket. */
 	if (inet_csk(sk)->icsk_bind_hash)
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index d1d30337ffec..94012a015bd0 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -246,7 +246,7 @@ EXPORT_SYMBOL(tcp_timewait_state_process);
 void tcp_time_wait(struct sock *sk, int state, int timeo)
 {
 	const struct inet_connection_sock *icsk = inet_csk(sk);
-	const struct tcp_sock *tp = tcp_sk(sk);
+	struct tcp_sock *tp = tcp_sk(sk);
 	struct inet_timewait_sock *tw;
 	struct inet_timewait_death_row *tcp_death_row = sock_net(sk)->ipv4.tcp_death_row;
 
@@ -305,6 +305,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
 			}
 		} while (0);
 #endif
+		tcp_ao_time_wait(tcptw, tp);
 
 		/* Get the TIME_WAIT timeout firing. */
 		if (timeo < rto)
@@ -359,6 +360,7 @@ void tcp_twsk_destructor(struct sock *sk)
 			call_rcu(&twsk->tw_md5_key->rcu, tcp_md5_twsk_free_rcu);
 	}
 #endif
+	tcp_ao_destroy_sock(sk, true);
 }
 EXPORT_SYMBOL_GPL(tcp_twsk_destructor);
 
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index f5d339d5291a..bab4a1883b3c 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1112,7 +1112,10 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 		ao_key = tcp_ao_do_lookup_sndid(sk, aoh->rnext_keyid);
 
 		if (ao_key) {
-			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);
 
 			/* XXX: optimize by using cached traffic key depending
 			 * on socket state
@@ -1161,23 +1164,56 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 static void tcp_v6_send_ack(const struct sock *sk, struct sk_buff *skb, u32 seq,
 			    u32 ack, u32 win, u32 tsval, u32 tsecr, int oif,
 			    struct tcp_md5sig_key *key, u8 tclass,
-			    __be32 label, u32 priority)
+			    __be32 label, u32 priority,
+			    struct tcp_ao_key *ao_key, char *tkey,
+			    u8 rcv_next, u32 ao_sne)
 {
 	tcp_v6_send_response(sk, skb, seq, ack, win, tsval, tsecr, oif, key, 0,
-			     tclass, label, priority, NULL, NULL, 0, 0);
+			     tclass, label, priority,
+			     ao_key, tkey, rcv_next, ao_sne);
 }
 
 static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
 {
 	struct inet_timewait_sock *tw = inet_twsk(sk);
 	struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
+	struct tcp_ao_key *ao_key = NULL;
+	u8 *traffic_key = NULL;
+	u8 rcv_next = 0;
+	u32 ao_sne = 0;
+#ifdef CONFIG_TCP_AO
+	struct tcp_ao_info *ao_info;
+	const struct tcp_ao_hdr *aoh;
+
+	ao_info = rcu_dereference(tcptw->ao_info);
+	if (ao_info) {
+		/* Invalid TCP option size or twice included auth */
+		if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh))
+			goto out;
+		/* FIXME: we haven't verified the segment to-be-acked */
+		if (aoh)
+			ao_key = tcp_ao_do_lookup_sndid(sk, aoh->rnext_keyid);
+		if (ao_key) {
+			traffic_key = snd_other_key(ao_key);
+			/* rcv_next switches to our rcv_next */
+			rcv_next = ao_info->rnext_key->rcvid;
+			ao_sne = tcp_ao_compute_sne(ao_info->snd_sne,
+						    ao_info->snd_sne_seq,
+						    tcptw->tw_snd_nxt);
+		}
+	}
+#endif
 
 	tcp_v6_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
 			tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
 			tcp_time_stamp_raw() + tcptw->tw_ts_offset,
 			tcptw->tw_ts_recent, tw->tw_bound_dev_if, tcp_twsk_md5_key(tcptw),
-			tw->tw_tclass, cpu_to_be32(tw->tw_flowlabel), tw->tw_priority);
+			tw->tw_tclass, cpu_to_be32(tw->tw_flowlabel), tw->tw_priority,
+			ao_key, traffic_key, rcv_next, ao_sne);
 
+#ifdef CONFIG_TCP_AO
+out:
+#endif
 	inet_twsk_put(tw);
 }
 
@@ -1203,7 +1239,8 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
 			tcp_time_stamp_raw() + tcp_rsk(req)->ts_off,
 			req->ts_recent, sk->sk_bound_dev_if,
 			tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr, l3index),
-			ipv6_get_dsfield(ipv6_hdr(skb)), 0, sk->sk_priority);
+			ipv6_get_dsfield(ipv6_hdr(skb)), 0, sk->sk_priority,
+			NULL, NULL, 0, 0);
 }
 
 
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 15/31] net/tcp: Wire TCP-AO to request sockets
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (13 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 14/31] net/tcp: Add TCP-AO sign to twsk Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 16/31] net/tcp: Sign SYN-ACK segments with TCP-AO Dmitry Safonov
                   ` (16 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Now when the new request socket is created from the listening socket,
it's recorded what MKT was used by the peer. tcp_rsk_used_ao() is
a new helper for checking if TCP-AO option was used to create the
request socket.
tcp_ao_copy_all_matching() will copy all keys that match the peer on the
request socket, as well as preparing them for the usage (creating
traffic keys).

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>
---
 include/linux/tcp.h      |  18 +++++
 include/net/tcp.h        |   7 ++
 include/net/tcp_ao.h     |  16 +++++
 net/ipv4/tcp_ao.c        | 137 +++++++++++++++++++++++++++++++++++++++
 net/ipv4/tcp_input.c     |  19 +++++-
 net/ipv4/tcp_ipv4.c      |  63 ++++++++++++++++--
 net/ipv4/tcp_minisocks.c |  10 +++
 net/ipv4/tcp_output.c    |  13 +++-
 net/ipv6/tcp_ao.c        |  21 ++++++
 net/ipv6/tcp_ipv6.c      |  78 ++++++++++++++++++----
 10 files changed, 361 insertions(+), 21 deletions(-)

diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 8031995b58a2..0e0f07d12f7b 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -165,6 +165,11 @@ struct tcp_request_sock {
 						  * after data-in-SYN.
 						  */
 	u8				syn_tos;
+#ifdef CONFIG_TCP_AO
+	u8				ao_keyid;
+	u8				ao_rcv_next;
+	u8				maclen;
+#endif
 };
 
 static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req)
@@ -172,6 +177,19 @@ static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req)
 	return (struct tcp_request_sock *)req;
 }
 
+static inline bool tcp_rsk_used_ao(const struct request_sock *req)
+{
+	/* The real length of MAC is saved in the request socket,
+	 * signing anything with zero-length makes no sense, so here is
+	 * a little hack..
+	 */
+#ifndef CONFIG_TCP_AO
+	return false;
+#else
+	return tcp_rsk(req)->maclen != 0;
+#endif
+}
+
 struct tcp_sock {
 	/* inet_connection_sock has to be the first member of tcp_sock */
 	struct inet_connection_sock	inet_conn;
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 061fe8471bfc..f21898bb31bd 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -2103,6 +2103,13 @@ struct tcp_request_sock_ops {
 					  const struct sock *sk,
 					  const struct sk_buff *skb);
 #endif
+#ifdef CONFIG_TCP_AO
+	struct tcp_ao_key	*(*ao_lookup)(const struct sock *sk,
+					       struct request_sock *req,
+					       int sndid, int rcvid);
+	int			(*ao_calc_key)(struct tcp_ao_key *mkt, u8 *key,
+						struct request_sock *sk);
+#endif
 #ifdef CONFIG_SYN_COOKIES
 	__u32 (*cookie_init_seq)(const struct sk_buff *skb,
 				 __u16 *mss);
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index af82b4aeef11..a1d26e1a0b82 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -119,6 +119,9 @@ int tcp_ao_hash_skb(unsigned short int family,
 int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family,
 		 sockptr_t optval, int optlen);
 struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid);
+int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk,
+			     struct request_sock *req, struct sk_buff *skb,
+			     int family);
 int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
 			    unsigned int len);
 void tcp_ao_destroy_sock(struct sock *sk, bool twsk);
@@ -142,6 +145,11 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
 int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 			  const struct sock *sk,
 			  __be32 sisn, __be32 disn, bool send);
+int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
+			   struct request_sock *req);
+struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk,
+					struct request_sock *req,
+					int sndid, int rcvid);
 int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
 		       const struct sock *sk, const struct sk_buff *skb,
 		       const u8 *tkey, int hash_offset, u32 sne);
@@ -152,9 +160,17 @@ int tcp_v6_ao_hash_pseudoheader(struct crypto_pool_ahash *hp,
 int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 				 const struct sock *sk, __be32 sisn,
 				 __be32 disn, bool send);
+int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
+			   struct request_sock *req);
+struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk,
+				       const struct in6_addr *addr,
+				       int sndid, int rcvid);
 struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
 				    struct sock *addr_sk,
 				    int sndid, int rcvid);
+struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk,
+					struct request_sock *req,
+					int sndid, int rcvid);
 int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
 		       const struct sock *sk, const struct sk_buff *skb,
 		       const u8 *tkey, int hash_offset, u32 sne);
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 528b6650c72c..a797eedfbcdf 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -52,6 +52,21 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
 	return 1;
 }
 
+struct tcp_ao_key *tcp_ao_do_lookup_keyid(struct tcp_ao_info *ao,
+					  int sndid, int rcvid)
+{
+	struct tcp_ao_key *key;
+
+	hlist_for_each_entry_rcu(key, &ao->head, node) {
+		if ((sndid >= 0 && key->sndid != sndid) ||
+		    (rcvid >= 0 && key->rcvid != rcvid))
+			continue;
+		return key;
+	}
+
+	return NULL;
+}
+
 struct tcp_ao_key *tcp_ao_do_lookup_rcvid(struct sock *sk, u8 keyid)
 {
 	struct tcp_sock *tp = tcp_sk(sk);
@@ -189,6 +204,22 @@ void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt)
 	hlist_add_head_rcu(&mkt->node, &ao->head);
 }
 
+struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, struct tcp_ao_key *key)
+{
+	struct tcp_ao_key *new_key;
+
+	new_key = sock_kmalloc(sk, tcp_ao_sizeof_key(key),
+			       GFP_ATOMIC);
+	if (!new_key)
+		return NULL;
+
+	*new_key = *key;
+	INIT_HLIST_NODE(&new_key->node);
+	crypto_pool_add(new_key->crypto_pool_id);
+
+	return new_key;
+}
+
 static void tcp_ao_key_free_rcu(struct rcu_head *head)
 {
 	struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu);
@@ -286,6 +317,18 @@ int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 					  htons(sk->sk_num), disn, sisn);
 }
 
+int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
+			   struct request_sock *req)
+{
+	struct inet_request_sock *ireq = inet_rsk(req);
+
+	return tcp_v4_ao_calc_key(mkt, key,
+				  ireq->ir_loc_addr, ireq->ir_rmt_addr,
+				  htons(ireq->ir_num), ireq->ir_rmt_port,
+				  htonl(tcp_rsk(req)->snt_isn),
+				  htonl(tcp_rsk(req)->rcv_isn));
+}
+
 static int tcp_v4_ao_hash_pseudoheader(struct crypto_pool_ahash *hp,
 				       __be32 daddr, __be32 saddr,
 				       int nbytes)
@@ -543,6 +586,16 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
 			       tkey, hash_offset, sne);
 }
 
+struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk,
+					struct request_sock *req,
+					int sndid, int rcvid)
+{
+	union tcp_ao_addr *addr =
+			(union tcp_ao_addr *)&inet_rsk(req)->ir_rmt_addr;
+
+	return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid, 0);
+}
+
 struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
 				    int sndid, int rcvid)
 {
@@ -644,6 +697,90 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb)
 	}
 }
 
+int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk,
+			     struct request_sock *req, struct sk_buff *skb,
+			     int family)
+{
+	struct tcp_ao_info *ao;
+	struct tcp_ao_info *new_ao;
+	struct tcp_ao_key *key, *new_key, *first_key;
+	struct hlist_node *n;
+	union tcp_ao_addr *addr;
+	bool match = false;
+
+	ao = rcu_dereference(tcp_sk(sk)->ao_info);
+	if (!ao)
+		return 0;
+
+	/* New socket without TCP-AO on it */
+	if (!tcp_rsk_used_ao(req))
+		return 0;
+
+	new_ao = tcp_ao_alloc_info(GFP_ATOMIC, ao);
+	if (!new_ao)
+		return -ENOMEM;
+	new_ao->lisn = htonl(tcp_rsk(req)->snt_isn);
+	new_ao->risn = htonl(tcp_rsk(req)->rcv_isn);
+
+	if (family == AF_INET)
+		addr = (union tcp_ao_addr *)&newsk->sk_daddr;
+	else
+		addr = (union tcp_ao_addr *)&newsk->sk_v6_daddr;
+
+	hlist_for_each_entry_rcu(key, &ao->head, node) {
+		if (tcp_ao_key_cmp(key, addr, key->prefixlen, family,
+				   -1, -1, 0))
+			continue;
+
+		new_key = tcp_ao_copy_key(newsk, key);
+		if (!new_key)
+			goto free_and_exit;
+
+		tcp_ao_cache_traffic_keys(newsk, new_ao, new_key);
+		tcp_ao_link_mkt(new_ao, new_key);
+		match = true;
+	}
+
+	if (match) {
+		struct hlist_node *key_head;
+
+		key_head = rcu_dereference(hlist_first_rcu(&new_ao->head));
+		first_key = hlist_entry_safe(key_head, struct tcp_ao_key, node);
+
+		/* set current_key */
+		key = tcp_ao_do_lookup_keyid(new_ao, tcp_rsk(req)->ao_keyid, -1);
+		if (key)
+			new_ao->current_key = key;
+		else
+			new_ao->current_key = first_key;
+
+		/* set rnext_key */
+		key = tcp_ao_do_lookup_keyid(new_ao, -1, tcp_rsk(req)->ao_rcv_next);
+		if (key)
+			new_ao->rnext_key = key;
+		else
+			new_ao->rnext_key = first_key;
+
+		new_ao->snd_sne_seq = tcp_rsk(req)->snt_isn;
+		new_ao->rcv_sne_seq = tcp_rsk(req)->rcv_isn;
+
+		sk_gso_disable(newsk);
+		rcu_assign_pointer(tcp_sk(newsk)->ao_info, new_ao);
+	}
+
+	return 0;
+
+free_and_exit:
+	hlist_for_each_entry_safe(key, n, &new_ao->head, node) {
+		hlist_del(&key->node);
+		crypto_pool_release(key->crypto_pool_id);
+		atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc);
+		kfree(key);
+	}
+	kfree(new_ao);
+	return -ENOMEM;
+}
+
 static int tcp_ao_current_rnext(struct sock *sk, u16 tcpa_flags,
 				u8 tcpa_sndid, u8 tcpa_rcvid)
 {
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 27e95876cba1..b8175ded8a70 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -6921,6 +6921,14 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
 	struct dst_entry *dst;
 	struct flowi fl;
 	u8 syncookies;
+	const struct tcp_ao_hdr *aoh = NULL;
+
+#ifdef CONFIG_TCP_AO
+	/* TODO: Add an option to require TCP-AO signature */
+	/* Packet was already validated in tcp_v[46]_rcv */
+	if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh))
+		goto drop; /* Invalid TCP options */
+#endif
 
 	syncookies = READ_ONCE(net->ipv4.sysctl_tcp_syncookies);
 
@@ -6928,7 +6936,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
 	 * limitations, they conserve resources and peer is
 	 * evidently real one.
 	 */
-	if ((syncookies == 2 || inet_csk_reqsk_queue_is_full(sk)) && !isn) {
+	if (!aoh && (syncookies == 2 || inet_csk_reqsk_queue_is_full(sk)) && !isn) {
 		want_cookie = tcp_syn_flood_action(sk, rsk_ops->slab_name);
 		if (!want_cookie)
 			goto drop;
@@ -7007,6 +7015,15 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
 			inet_rsk(req)->ecn_ok = 0;
 	}
 
+#ifdef CONFIG_TCP_AO
+	if (aoh) {
+		tcp_rsk(req)->maclen = aoh->length - sizeof(struct tcp_ao_hdr);
+		tcp_rsk(req)->ao_rcv_next = aoh->keyid;
+		tcp_rsk(req)->ao_keyid = aoh->rnext_keyid;
+	} else {
+		tcp_rsk(req)->maclen = 0;
+	}
+#endif
 	tcp_rsk(req)->snt_isn = isn;
 	tcp_rsk(req)->txhash = net_tx_rndhash();
 	tcp_rsk(req)->syn_tos = TCP_SKB_CB(skb)->ip_dsfield;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 003c5f320bfc..55c763b8bf24 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1064,30 +1064,73 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
 static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
 				  struct request_sock *req)
 {
+	struct tcp_md5sig_key *md5_key = NULL;
+	struct tcp_ao_key *ao_key = NULL;
 	const union tcp_md5_addr *addr;
-	int l3index;
+	u8 keyid = 0;
+#ifdef CONFIG_TCP_AO
+	u8 traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align;
+	const struct tcp_ao_hdr *aoh;
+#else
+	u8 *traffic_key = NULL;
+#endif
 
 	/* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
 	 * sk->sk_state == TCP_SYN_RECV -> for Fast Open.
 	 */
 	u32 seq = (sk->sk_state == TCP_LISTEN) ? tcp_rsk(req)->snt_isn + 1 :
 					     tcp_sk(sk)->snd_nxt;
+	addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr;
+
+	if (tcp_rsk_used_ao(req)) {
+#ifdef CONFIG_TCP_AO
+		/* Invalid TCP option size or twice included auth */
+		if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh))
+			return;
+
+		if (!aoh)
+			return;
+
+		ao_key = tcp_ao_do_lookup(sk, addr, AF_INET,
+					  aoh->rnext_keyid, -1, 0);
+		if (unlikely(!ao_key)) {
+			/* Send ACK with any matching MKT for the peer */
+			ao_key = tcp_ao_do_lookup(sk, addr,
+						  AF_INET, -1, -1, 0);
+			/* Matching key disappeared (user removed the key?)
+			 * let the handshake timeout.
+			 */
+			if (!ao_key) {
+				net_info_ratelimited("TCP-AO key for (%pI4, %d)->(%pI4, %d) suddenly disappeared, won't ACK new connection\n",
+						addr,
+						ntohs(tcp_hdr(skb)->source),
+						&ip_hdr(skb)->daddr,
+						ntohs(tcp_hdr(skb)->dest));
+				return;
+			}
+		}
 
+		keyid = aoh->keyid;
+		tcp_v4_ao_calc_key_rsk(ao_key, traffic_key, req);
+#endif
+	} else {
+		int l3index;
+
+		l3index = tcp_v4_sdif(skb) ? inet_iif(skb) : 0;
+		md5_key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET);
+	}
 	/* RFC 7323 2.3
 	 * The window field (SEG.WND) of every outgoing segment, with the
 	 * exception of <SYN> segments, MUST be right-shifted by
 	 * Rcv.Wind.Shift bits:
 	 */
-	addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr;
-	l3index = tcp_v4_sdif(skb) ? inet_iif(skb) : 0;
 	tcp_v4_send_ack(sk, skb, seq,
 			tcp_rsk(req)->rcv_nxt,
 			req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale,
 			tcp_time_stamp_raw() + tcp_rsk(req)->ts_off,
 			req->ts_recent,
 			0,
-			tcp_md5_do_lookup(sk, l3index, addr, AF_INET),
-			NULL, NULL, 0, 0,
+			md5_key, ao_key, traffic_key, keyid, 0,
 			inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0,
 			ip_hdr(skb)->tos);
 }
@@ -1603,6 +1646,10 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
 	.req_md5_lookup	=	tcp_v4_md5_lookup,
 	.calc_md5_hash	=	tcp_v4_md5_hash_skb,
 #endif
+#ifdef CONFIG_TCP_AO
+	.ao_lookup	=	tcp_v4_ao_lookup_rsk,
+	.ao_calc_key	=	tcp_v4_ao_calc_key_rsk,
+#endif
 #ifdef CONFIG_SYN_COOKIES
 	.cookie_init_seq =	cookie_v4_init_sequence,
 #endif
@@ -1704,7 +1751,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
 	/* Copy over the MD5 key from the original socket */
 	addr = (union tcp_md5_addr *)&newinet->inet_daddr;
 	key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET);
-	if (key) {
+	if (key && !tcp_rsk_used_ao(req)) {
 		/*
 		 * We're using one, so create a matching key
 		 * on the newsk structure. If we fail to get
@@ -1715,6 +1762,10 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
 		sk_gso_disable(newsk);
 	}
 #endif
+#ifdef CONFIG_TCP_AO
+	if (tcp_ao_copy_all_matching(sk, newsk, req, skb, AF_INET))
+		goto put_and_exit; /* OOM, release back memory */
+#endif
 
 	if (__inet_inherit_port(sk, newsk) < 0)
 		goto put_and_exit;
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 94012a015bd0..03f1b809866b 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -473,6 +473,9 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
 	struct inet_connection_sock *newicsk;
 	struct tcp_sock *oldtp, *newtp;
 	u32 seq;
+#ifdef CONFIG_TCP_AO
+	struct tcp_ao_key *ao_key;
+#endif
 
 	if (!newsk)
 		return NULL;
@@ -551,6 +554,13 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
 	if (treq->af_specific->req_md5_lookup(sk, req_to_sk(req)))
 		newtp->tcp_header_len += TCPOLEN_MD5SIG_ALIGNED;
 #endif
+#ifdef CONFIG_TCP_AO
+	newtp->ao_info = NULL;
+	ao_key = treq->af_specific->ao_lookup(sk, req,
+				tcp_rsk(req)->ao_keyid, -1);
+	if (ao_key)
+		newtp->tcp_header_len += tcp_ao_len(ao_key);
+ #endif
 	if (skb->len >= TCP_MSS_DEFAULT + newtp->tcp_header_len)
 		newicsk->icsk_ack.last_seg_size = skb->len - newtp->tcp_header_len;
 	newtp->rx_opt.mss_clamp = req->mss;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 26315cac09dd..81f897f95147 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -606,6 +606,7 @@ static void bpf_skops_write_hdr_opt(struct sock *sk, struct sk_buff *skb,
  * (but it may well be that other scenarios fail similarly).
  */
 static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp,
+			      const struct tcp_request_sock *tcprsk,
 			      struct tcp_out_options *opts,
 			      struct tcp_ao_key *ao_key)
 {
@@ -636,6 +637,14 @@ static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp,
 				       (ao_key->sndid << 8) |
 				       (ao_info->rnext_key->rcvid));
 		}
+		if (tcprsk) {
+			u8 aolen = tcprsk->maclen + sizeof(struct tcp_ao_hdr);
+
+			maclen = tcprsk->maclen;
+			*ptr++ = htonl((TCPOPT_AO << 24) | (aolen << 16) |
+				       (tcprsk->ao_keyid << 8) |
+				       (tcprsk->ao_rcv_next));
+		}
 		opts->hash_location = (__u8 *)ptr;
 		ptr += maclen / sizeof(*ptr);
 		if (unlikely(maclen % sizeof(*ptr))) {
@@ -1414,7 +1423,7 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
 		th->window	= htons(min(tp->rcv_wnd, 65535U));
 	}
 
-	tcp_options_write(th, tp, &opts, ao_key);
+	tcp_options_write(th, tp, NULL, &opts, ao_key);
 
 #ifdef CONFIG_TCP_MD5SIG
 	/* Calculate the MD5 hash, as we have all we need now */
@@ -3700,7 +3709,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
 
 	/* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
 	th->window = htons(min(req->rsk_rcv_wnd, 65535U));
-	tcp_options_write(th, NULL, &opts, NULL);
+	tcp_options_write(th, NULL, NULL, &opts, NULL);
 	th->doff = (tcp_header_size >> 2);
 	__TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS);
 
diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c
index 7fd31c60488a..31ae504af8e6 100644
--- a/net/ipv6/tcp_ao.c
+++ b/net/ipv6/tcp_ao.c
@@ -53,6 +53,18 @@ int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 					  htons(sk->sk_num), disn, sisn);
 }
 
+int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
+			   struct request_sock *req)
+{
+	struct inet_request_sock *ireq = inet_rsk(req);
+
+	return tcp_v6_ao_calc_key(mkt, key,
+			&ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr,
+			htons(ireq->ir_num), ireq->ir_rmt_port,
+			htonl(tcp_rsk(req)->snt_isn),
+			htonl(tcp_rsk(req)->rcv_isn));
+}
+
 struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk,
 				       const struct in6_addr *addr,
 				       int sndid, int rcvid)
@@ -70,6 +82,15 @@ struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
 	return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid);
 }
 
+struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk,
+					struct request_sock *req,
+					int sndid, int rcvid)
+{
+	struct in6_addr *addr = &inet_rsk(req)->ir_v6_rmt_addr;
+
+	return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid);
+}
+
 int tcp_v6_ao_hash_pseudoheader(struct crypto_pool_ahash *hp,
 				const struct in6_addr *daddr,
 				const struct in6_addr *saddr, int nbytes)
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index bab4a1883b3c..7610fdf5d519 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -835,6 +835,10 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
 	.req_md5_lookup	=	tcp_v6_md5_lookup,
 	.calc_md5_hash	=	tcp_v6_md5_hash_skb,
 #endif
+#ifdef CONFIG_TCP_AO
+	.ao_lookup	=	tcp_v6_ao_lookup_rsk,
+	.ao_calc_key	=	tcp_v6_ao_calc_key_rsk,
+#endif
 #ifdef CONFIG_SYN_COOKIES
 	.cookie_init_seq =	cookie_v6_init_sequence,
 #endif
@@ -1220,9 +1224,51 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
 static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
 				  struct request_sock *req)
 {
+	struct tcp_md5sig_key *md5_key = NULL;
+	struct tcp_ao_key *ao_key = NULL;
+	const struct in6_addr *addr;
+	u8 keyid = 0;
+#ifdef CONFIG_TCP_AO
+	char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align;
+	const struct tcp_ao_hdr *aoh;
+#else
+	u8 *traffic_key = NULL;
+#endif
 	int l3index;
 
 	l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0;
+	addr = &ipv6_hdr(skb)->saddr;
+
+	if (tcp_rsk_used_ao(req)) {
+#ifdef CONFIG_TCP_AO
+		/* Invalid TCP option size or twice included auth */
+		if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh))
+			return;
+		if (!aoh)
+			return;
+		ao_key = tcp_v6_ao_do_lookup(sk, addr, aoh->rnext_keyid, -1);
+		if (unlikely(!ao_key)) {
+			/* Send ACK with any matching MKT for the peer */
+			ao_key = tcp_v6_ao_do_lookup(sk, addr, -1, -1);
+			/* Matching key disappeared (user removed the key?)
+			 * let the handshake timeout.
+			 */
+			if (!ao_key) {
+				net_info_ratelimited("TCP-AO key for (%pI6, %d)->(%pI6, %d) suddenly disappeared, won't ACK new connection\n",
+						addr,
+						ntohs(tcp_hdr(skb)->source),
+						&ipv6_hdr(skb)->daddr,
+						ntohs(tcp_hdr(skb)->dest));
+				return;
+			}
+		}
+
+		keyid = aoh->keyid;
+		tcp_v6_ao_calc_key_rsk(ao_key, traffic_key, req);
+#endif
+	} else {
+		md5_key = tcp_v6_md5_do_lookup(sk, addr, l3index);
+	}
 
 	/* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
 	 * sk->sk_state == TCP_SYN_RECV -> for Fast Open.
@@ -1238,9 +1284,9 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,
 			req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale,
 			tcp_time_stamp_raw() + tcp_rsk(req)->ts_off,
 			req->ts_recent, sk->sk_bound_dev_if,
-			tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr, l3index),
+			md5_key,
 			ipv6_get_dsfield(ipv6_hdr(skb)), 0, sk->sk_priority,
-			NULL, NULL, 0, 0);
+			ao_key, traffic_key, keyid, 0);
 }
 
 
@@ -1470,18 +1516,26 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
 #ifdef CONFIG_TCP_MD5SIG
 	l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif);
 
-	/* Copy over the MD5 key from the original socket */
-	key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr, l3index);
-	if (key) {
-		/* We're using one, so create a matching key
-		 * on the newsk structure. If we fail to get
-		 * memory, then we end up not copying the key
-		 * across. Shucks.
-		 */
-		tcp_md5_key_copy(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr,
-			       AF_INET6, 128, l3index, key);
+	if (!tcp_rsk_used_ao(req)) {
+		const struct in6_addr *daddr = &newsk->sk_v6_daddr;
+		/* Copy over the MD5 key from the original socket */
+		key = tcp_v6_md5_do_lookup(sk, daddr, l3index);
+		if (key) {
+			/* We're using one, so create a matching key
+			 * on the newsk structure. If we fail to get
+			 * memory, then we end up not copying the key
+			 * across. Shucks.
+			 */
+			tcp_md5_key_copy(newsk, (union tcp_md5_addr *)daddr,
+					 AF_INET6, 128, l3index, key);
+		}
 	}
 #endif
+#ifdef CONFIG_TCP_AO
+	/* Copy over tcp_ao_info if any */
+	if (tcp_ao_copy_all_matching(sk, newsk, req, skb, AF_INET6))
+		goto out; /* OOM */
+#endif
 
 	if (__inet_inherit_port(sk, newsk) < 0) {
 		inet_csk_prepare_forced_close(newsk);
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 16/31] net/tcp: Sign SYN-ACK segments with TCP-AO
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (14 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 15/31] net/tcp: Wire TCP-AO to request sockets Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 17/31] net/tcp: Verify inbound TCP-AO signed segments Dmitry Safonov
                   ` (15 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Similarly to RST segments, wire SYN-ACKs to TCP-AO.
tcp_rsk_used_ao() is handy here to check if the request socket used AO
and needs a signature on the outgoing segments.

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>
---
 include/net/tcp.h     |  4 ++++
 include/net/tcp_ao.h  |  6 ++++++
 net/ipv4/tcp_ao.c     | 14 ++++++++++++++
 net/ipv4/tcp_ipv4.c   |  1 +
 net/ipv4/tcp_output.c | 37 +++++++++++++++++++++++++++++++------
 net/ipv6/tcp_ao.c     | 14 ++++++++++++++
 net/ipv6/tcp_ipv6.c   |  1 +
 7 files changed, 71 insertions(+), 6 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index f21898bb31bd..19549be29265 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -2109,6 +2109,10 @@ struct tcp_request_sock_ops {
 					       int sndid, int rcvid);
 	int			(*ao_calc_key)(struct tcp_ao_key *mkt, u8 *key,
 						struct request_sock *sk);
+	int		(*ao_synack_hash)(char *ao_hash, struct tcp_ao_key *mkt,
+					  struct request_sock *req,
+					  const struct sk_buff *skb,
+					  int hash_offset, u32 sne);
 #endif
 #ifdef CONFIG_SYN_COOKIES
 	__u32 (*cookie_init_seq)(const struct sk_buff *skb,
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index a1d26e1a0b82..cc3f6686d5c9 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -142,6 +142,9 @@ int tcp_ao_hash_hdr(unsigned short family, char *ao_hash,
 int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen);
 struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
 				    int sndid, int rcvid);
+int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *mkt,
+			  struct request_sock *req, const struct sk_buff *skb,
+			  int hash_offset, u32 sne);
 int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 			  const struct sock *sk,
 			  __be32 sisn, __be32 disn, bool send);
@@ -176,6 +179,9 @@ int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
 		       const u8 *tkey, int hash_offset, u32 sne);
 int tcp_v6_parse_ao(struct sock *sk, int cmd,
 		    sockptr_t optval, int optlen);
+int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
+			  struct request_sock *req, const struct sk_buff *skb,
+			  int hash_offset, u32 sne);
 void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb);
 void tcp_ao_connect_init(struct sock *sk);
 
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index a797eedfbcdf..78c24ade9a03 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -586,6 +586,20 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
 			       tkey, hash_offset, sne);
 }
 
+int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
+			  struct request_sock *req, const struct sk_buff *skb,
+			  int hash_offset, u32 sne)
+{
+	char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align;
+
+	tcp_v4_ao_calc_key_rsk(ao_key, traffic_key, req);
+
+	tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb,
+			traffic_key, hash_offset, sne);
+
+	return 0;
+}
+
 struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk,
 					struct request_sock *req,
 					int sndid, int rcvid)
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 55c763b8bf24..02ab94461e86 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1649,6 +1649,7 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
 #ifdef CONFIG_TCP_AO
 	.ao_lookup	=	tcp_v4_ao_lookup_rsk,
 	.ao_calc_key	=	tcp_v4_ao_calc_key_rsk,
+	.ao_synack_hash	=	tcp_v4_ao_synack_hash,
 #endif
 #ifdef CONFIG_SYN_COOKIES
 	.cookie_init_seq =	cookie_v4_init_sequence,
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 81f897f95147..43111e39b6e1 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -3630,6 +3630,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
 	struct inet_request_sock *ireq = inet_rsk(req);
 	const struct tcp_sock *tp = tcp_sk(sk);
 	struct tcp_md5sig_key *md5 = NULL;
+	struct tcp_ao_key *ao_key = NULL;
 	struct tcp_out_options opts;
 	struct sk_buff *skb;
 	int tcp_header_size;
@@ -3680,16 +3681,32 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
 			tcp_rsk(req)->snt_synack = tcp_skb_timestamp_us(skb);
 	}
 
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
 	rcu_read_lock();
-	md5 = tcp_rsk(req)->af_specific->req_md5_lookup(sk, req_to_sk(req));
 #endif
+	if (tcp_rsk_used_ao(req)) {
+#ifdef CONFIG_TCP_AO
+		/* TODO: what should we do if the key is no longer available on
+		 * the listening socket? Maybe we can try a different matching
+		 * key (without sndid match). If that also fails what should
+		 * we do? We currently send an unsigned synack. It's probably
+		 * better to not send anything.
+		 */
+		ao_key = tcp_sk(sk)->af_specific->ao_lookup(sk, req_to_sk(req),
+						    tcp_rsk(req)->ao_keyid, -1);
+#endif
+	} else {
+#ifdef CONFIG_TCP_MD5SIG
+		md5 = tcp_rsk(req)->af_specific->req_md5_lookup(sk,
+								req_to_sk(req));
+#endif
+	}
 	skb_set_hash(skb, tcp_rsk(req)->txhash, PKT_HASH_TYPE_L4);
 	/* bpf program will be interested in the tcp_flags */
 	TCP_SKB_CB(skb)->tcp_flags = TCPHDR_SYN | TCPHDR_ACK;
 	tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, md5,
-					     NULL, foc, synack_type,
-					     syn_skb) + sizeof(*th);
+					     ao_key, foc, synack_type, syn_skb)
+					+ sizeof(*th);
 
 	skb_push(skb, tcp_header_size);
 	skb_reset_transport_header(skb);
@@ -3709,7 +3726,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
 
 	/* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
 	th->window = htons(min(req->rsk_rcv_wnd, 65535U));
-	tcp_options_write(th, NULL, NULL, &opts, NULL);
+	tcp_options_write(th, NULL, tcp_rsk(req), &opts, NULL);
 	th->doff = (tcp_header_size >> 2);
 	__TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS);
 
@@ -3717,7 +3734,15 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
 	/* Okay, we have all we need - do the md5 hash if needed */
 	if (md5)
 		tcp_rsk(req)->af_specific->calc_md5_hash(opts.hash_location,
-					       md5, req_to_sk(req), skb);
+					md5, req_to_sk(req), skb);
+#endif
+#ifdef CONFIG_TCP_AO
+	if (ao_key)
+		tcp_rsk(req)->af_specific->ao_synack_hash(opts.hash_location,
+					ao_key, req, skb,
+					opts.hash_location - (u8 *)th, 0);
+#endif
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
 	rcu_read_unlock();
 #endif
 
diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c
index 31ae504af8e6..526bbe232a64 100644
--- a/net/ipv6/tcp_ao.c
+++ b/net/ipv6/tcp_ao.c
@@ -123,3 +123,17 @@ int tcp_v6_parse_ao(struct sock *sk, int cmd,
 {
 	return tcp_parse_ao(sk, cmd, AF_INET6, optval, optlen);
 }
+
+int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
+			  struct request_sock *req, const struct sk_buff *skb,
+			  int hash_offset, u32 sne)
+{
+	char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align;
+
+	tcp_v6_ao_calc_key_rsk(ao_key, traffic_key, req);
+
+	tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb,
+			traffic_key, hash_offset, sne);
+
+	return 0;
+}
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 7610fdf5d519..16cea7de0851 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -838,6 +838,7 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
 #ifdef CONFIG_TCP_AO
 	.ao_lookup	=	tcp_v6_ao_lookup_rsk,
 	.ao_calc_key	=	tcp_v6_ao_calc_key_rsk,
+	.ao_synack_hash =	tcp_v6_ao_synack_hash,
 #endif
 #ifdef CONFIG_SYN_COOKIES
 	.cookie_init_seq =	cookie_v6_init_sequence,
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 17/31] net/tcp: Verify inbound TCP-AO signed segments
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (15 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 16/31] net/tcp: Sign SYN-ACK segments with TCP-AO Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 18/31] net/tcp: Add TCP-AO segments counters Dmitry Safonov
                   ` (14 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Now there is a common function to verify signature on TCP segments:
tcp_inbound_hash(). It has checks for all possible cross-interactions
with MD5 signs as well as with unsigned segments.

The rules from RFC5925 are:
(1) Any TCP segment can have at max only one signature.
(2) TCP connections can't switch between using TCP-MD5 and TCP-AO.
(3) TCP-AO connections can't stop using AO, as well as unsigned
    connections can't suddenly start using AO.

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>
---
 include/net/dropreason.h |  18 +++++
 include/net/tcp.h        |  59 +++++++++++++++-
 include/net/tcp_ao.h     |  21 ++++++
 net/ipv4/tcp.c           |  39 +++--------
 net/ipv4/tcp_ao.c        | 148 +++++++++++++++++++++++++++++++++++++++
 net/ipv4/tcp_ipv4.c      |  11 +--
 net/ipv6/tcp_ao.c        |  12 ++++
 net/ipv6/tcp_ipv6.c      |  11 +--
 8 files changed, 276 insertions(+), 43 deletions(-)

diff --git a/include/net/dropreason.h b/include/net/dropreason.h
index fae9b40e54fa..b4906323b9a7 100644
--- a/include/net/dropreason.h
+++ b/include/net/dropreason.h
@@ -60,6 +60,11 @@ enum skb_drop_reason {
 	 * drop out of udp_memory_allocated.
 	 */
 	SKB_DROP_REASON_PROTO_MEM,
+	/**
+	 * @SKB_DROP_REASON_TCP_AUTH_HDR: TCP-MD5 or TCP-AO hashes are met
+	 * twice or set incorrectly.
+	 */
+	SKB_DROP_REASON_TCP_AUTH_HDR,
 	/**
 	 * @SKB_DROP_REASON_TCP_MD5NOTFOUND: no MD5 hash and one expected,
 	 * corresponding to LINUX_MIB_TCPMD5NOTFOUND
@@ -75,6 +80,19 @@ enum skb_drop_reason {
 	 * to LINUX_MIB_TCPMD5FAILURE
 	 */
 	SKB_DROP_REASON_TCP_MD5FAILURE,
+	/**
+	 * @SKB_DROP_REASON_TCP_AONOTFOUND: no TCP-AO hash and one was expected
+	 */
+	SKB_DROP_REASON_TCP_AONOTFOUND,
+	/**
+	 * @SKB_DROP_REASON_TCP_AOUNEXPECTED: TCP-AO hash is present and it
+	 * was not expected.
+	 */
+	SKB_DROP_REASON_TCP_AOUNEXPECTED,
+	/** @SKB_DROP_REASON_TCP_AOKEYNOTFOUND: TCP-AO key is unknown */
+	SKB_DROP_REASON_TCP_AOKEYNOTFOUND,
+	/** @SKB_DROP_REASON_TCP_AOFAILURE: TCP-AO hash is wrong */
+	SKB_DROP_REASON_TCP_AOFAILURE,
 	/**
 	 * @SKB_DROP_REASON_SOCKET_BACKLOG: failed to add skb to socket backlog (
 	 * see LINUX_MIB_TCPBACKLOGDROP)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 19549be29265..2e75c542e7ed 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1693,7 +1693,7 @@ tcp_md5_do_lookup_any_l3index(const struct sock *sk,
 enum skb_drop_reason
 tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 		     const void *saddr, const void *daddr,
-		     int family, int dif, int sdif);
+		     int family, int l3index, const __u8 *hash_location);
 
 
 #define tcp_twsk_md5_key(twsk)	((twsk)->tw_md5_key)
@@ -1715,7 +1715,7 @@ tcp_md5_do_lookup_any_l3index(const struct sock *sk,
 static inline enum skb_drop_reason
 tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 		     const void *saddr, const void *daddr,
-		     int family, int dif, int sdif)
+		     int family, int l3index, const __u8 *hash_location)
 {
 	return SKB_NOT_DROPPED_YET;
 }
@@ -2090,6 +2090,10 @@ struct tcp_sock_af_ops {
 						  const struct sock *sk,
 						  __be32 sisn, __be32 disn,
 						  bool send);
+	int			(*ao_calc_key_skb)(struct tcp_ao_key *mkt,
+						   u8 *key,
+						   const struct sk_buff *skb,
+						   __be32 sisn, __be32 disn);
 #endif
 };
 
@@ -2500,4 +2504,55 @@ static inline int tcp_parse_auth_options(const struct tcphdr *th,
 	return 0;
 }
 
+/* Called with rcu_read_lock() */
+static inline enum skb_drop_reason
+tcp_inbound_hash(struct sock *sk, const struct request_sock *req,
+		 const struct sk_buff *skb,
+		 const void *saddr, const void *daddr,
+		 int family, int dif, int sdif)
+{
+	const struct tcphdr *th = tcp_hdr(skb);
+	const struct tcp_ao_hdr *aoh;
+	const __u8 *md5_location;
+	int l3index;
+
+	/* Invalid option or two times meet any of auth options */
+	if (tcp_parse_auth_options(th, &md5_location, &aoh))
+		return SKB_DROP_REASON_TCP_AUTH_HDR;
+
+	if (req) {
+		if (tcp_rsk_used_ao(req) != !!aoh)
+			return SKB_DROP_REASON_TCP_AOFAILURE;
+	}
+
+	/* sdif set, means packet ingressed via a device
+	 * in an L3 domain and dif is set to the l3mdev
+	 */
+	l3index = sdif ? dif : 0;
+
+	/* Fast path: unsigned segments */
+	if (likely(!md5_location && !aoh)) {
+		/* Drop if there's TCP-MD5 or TCP-AO key with any rcvid/sndid
+		 * for the remote peer. On TCP-AO established connection
+		 * the last key is impossible to remove, so there's
+		 * always at least one current_key.
+		 */
+#ifdef CONFIG_TCP_AO
+		if (unlikely(tcp_ao_do_lookup(sk, saddr, family, -1, -1, 0)))
+			return SKB_DROP_REASON_TCP_AONOTFOUND;
+#endif
+		if (unlikely(tcp_md5_do_lookup(sk, l3index, saddr, family))) {
+			NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
+			return SKB_DROP_REASON_TCP_MD5NOTFOUND;
+		}
+		return SKB_NOT_DROPPED_YET;
+	}
+
+	if (aoh)
+		return tcp_inbound_ao_hash(sk, skb, family, req, aoh);
+
+	return tcp_inbound_md5_hash(sk, skb, saddr, daddr, family,
+				    l3index, md5_location);
+}
+
 #endif	/* _TCP_H */
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index cc3f6686d5c9..7cb802de49ba 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -112,6 +112,9 @@ struct tcp6_ao_context {
 	__be32		disn;
 };
 
+#define TCP_AO_ESTABLISHED (TCPF_ESTABLISHED|TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|\
+		TCPF_CLOSE|TCPF_CLOSE_WAIT|TCPF_LAST_ACK|TCPF_CLOSING)
+
 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,
@@ -129,6 +132,10 @@ u32 tcp_ao_compute_sne(u32 sne, u32 seq, u32 new_seq);
 void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
 int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao,
 			      struct tcp_ao_key *ao_key);
+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);
 struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 				    const union tcp_ao_addr *addr,
 				    int family, int sndid, int rcvid, u16 port);
@@ -150,9 +157,14 @@ int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 			  __be32 sisn, __be32 disn, bool send);
 int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
 			   struct request_sock *req);
+int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
+			   const struct sk_buff *skb, __be32 sisn, __be32 disn);
 struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk,
 					struct request_sock *req,
 					int sndid, int rcvid);
+bool tcp_v4_inbound_ao_hash(struct sock *sk,
+			    struct request_sock *req,
+			    const struct sk_buff *skb);
 int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
 		       const struct sock *sk, const struct sk_buff *skb,
 		       const u8 *tkey, int hash_offset, u32 sne);
@@ -160,6 +172,9 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
 int tcp_v6_ao_hash_pseudoheader(struct crypto_pool_ahash *hp,
 				const struct in6_addr *daddr,
 				const struct in6_addr *saddr, int nbytes);
+int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
+			   const struct sk_buff *skb, __be32 sisn,
+			    __be32 disn);
 int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 				 const struct sock *sk, __be32 sisn,
 				 __be32 disn, bool send);
@@ -187,6 +202,12 @@ void tcp_ao_connect_init(struct sock *sk);
 
 #else /* CONFIG_TCP_AO */
 
+static inline 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)
+{
+	return SKB_NOT_DROPPED_YET;
+}
 static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
 		const union tcp_ao_addr *addr,
 		int family, int sndid, int rcvid, u16 port)
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index a5e94d8e8450..8df03d456ebb 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -4503,42 +4503,23 @@ EXPORT_SYMBOL(tcp_md5_hash_key);
 enum skb_drop_reason
 tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 		     const void *saddr, const void *daddr,
-		     int family, int dif, int sdif)
+		     int family, int l3index, const __u8 *hash_location)
 {
-	/*
-	 * This gets called for each TCP segment that arrives
-	 * so we want to be efficient.
+	/* This gets called for each TCP segment that has TCP-MD5 option.
 	 * We have 3 drop cases:
 	 * o No MD5 hash and one expected.
 	 * o MD5 hash and we're not expecting one.
 	 * o MD5 hash and its wrong.
 	 */
-	const __u8 *hash_location = NULL;
-	struct tcp_md5sig_key *hash_expected;
 	const struct tcphdr *th = tcp_hdr(skb);
 	struct tcp_sock *tp = tcp_sk(sk);
-	int genhash, l3index;
+	struct tcp_md5sig_key *key;
+	int genhash;
 	u8 newhash[16];
 
-	/* sdif set, means packet ingressed via a device
-	 * in an L3 domain and dif is set to the l3mdev
-	 */
-	l3index = sdif ? dif : 0;
-
-	hash_expected = tcp_md5_do_lookup(sk, l3index, saddr, family);
-	if (tcp_parse_auth_options(th, &hash_location, NULL))
-		return true;
-
-	/* We've parsed the options - do we have a hash? */
-	if (!hash_expected && !hash_location)
-		return SKB_NOT_DROPPED_YET;
-
-	if (hash_expected && !hash_location) {
-		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
-		return SKB_DROP_REASON_TCP_MD5NOTFOUND;
-	}
+	key = tcp_md5_do_lookup(sk, l3index, saddr, family);
 
-	if (!hash_expected && hash_location) {
+	if (!key && hash_location) {
 		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED);
 		return SKB_DROP_REASON_TCP_MD5UNEXPECTED;
 	}
@@ -4548,14 +4529,10 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 	 * IPv4-mapped case.
 	 */
 	if (family == AF_INET)
-		genhash = tcp_v4_md5_hash_skb(newhash,
-					      hash_expected,
-					      NULL, skb);
+		genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb);
 	else
-		genhash = tp->af_specific->calc_md5_hash(newhash,
-							 hash_expected,
+		genhash = tp->af_specific->calc_md5_hash(newhash, key,
 							 NULL, skb);
-
 	if (genhash || memcmp(hash_location, newhash, 16) != 0) {
 		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE);
 		if (family == AF_INET) {
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 78c24ade9a03..10cd6af3c45f 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -329,6 +329,17 @@ int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
 				  htonl(tcp_rsk(req)->rcv_isn));
 }
 
+int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
+			   const struct sk_buff *skb, __be32 sisn,
+			   __be32 disn)
+{
+	const struct iphdr *iph = ip_hdr(skb);
+	const struct tcphdr *th = tcp_hdr(skb);
+
+	return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr,
+				     th->source, th->dest, sisn, disn);
+}
+
 static int tcp_v4_ao_hash_pseudoheader(struct crypto_pool_ahash *hp,
 				       __be32 daddr, __be32 saddr,
 				       int nbytes)
@@ -618,6 +629,143 @@ 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, 0);
 }
 
+static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family,
+		const struct sock *sk, const struct sk_buff *skb,
+		int sndid, int rcvid)
+{
+	if (family == AF_INET) {
+		const struct iphdr *iph = ip_hdr(skb);
+
+		return tcp_ao_do_lookup(sk, (union tcp_md5_addr *)&iph->saddr,
+				AF_INET, sndid, rcvid, 0);
+	} else {
+		const struct ipv6hdr *iph = ipv6_hdr(skb);
+
+		return tcp_ao_do_lookup(sk, (union tcp_md5_addr *)&iph->saddr,
+				AF_INET6, sndid, rcvid, 0);
+	}
+}
+
+static enum skb_drop_reason
+tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb,
+		   unsigned short int family, struct tcp_ao_info *info,
+		   const struct tcp_ao_hdr *aoh, struct tcp_ao_key *key,
+		   u8 *traffic_key, u8 *phash, u32 sne)
+{
+	unsigned char newhash[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align;
+	u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr);
+	const struct tcphdr *th = tcp_hdr(skb);
+
+	if (maclen != tcp_ao_maclen(key))
+		return SKB_DROP_REASON_TCP_AOFAILURE;
+
+	/* XXX: make it per-AF callback? */
+	tcp_ao_hash_skb(family, newhash, key, sk, skb, traffic_key,
+			(phash - (u8 *)th), sne);
+	if (memcmp(phash, newhash, maclen))
+		return SKB_DROP_REASON_TCP_AOFAILURE;
+	return SKB_NOT_DROPPED_YET;
+}
+
+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)
+{
+	const struct tcp_sock_af_ops *ops = tcp_sk(sk)->af_specific;
+	u8 key_buf[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align;
+	const struct tcphdr *th = tcp_hdr(skb);
+	u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */
+	struct tcp_ao_info *info;
+	struct tcp_ao_key *key;
+	__be32 sisn, disn;
+	u8 *traffic_key;
+	u32 sne;
+
+	info = rcu_dereference(tcp_sk(sk)->ao_info);
+	if (!info)
+		return SKB_DROP_REASON_TCP_AOUNEXPECTED;
+
+	/* Fast-path */
+	/* TODO: fix fastopen and simultaneous open (TCPF_SYN_RECV) */
+	if (likely((1 << sk->sk_state) & (TCP_AO_ESTABLISHED | TCPF_SYN_RECV))) {
+		enum skb_drop_reason err;
+
+		/* Check if this socket's rnext_key matches the keyid in the
+		 * packet. If not we lookup the key based on the keyid
+		 * matching the rcvid in the mkt.
+		 */
+		key = info->rnext_key;
+		if (key->rcvid != aoh->keyid) {
+			key = tcp_ao_do_lookup_rcvid(sk, aoh->keyid);
+			if (!key)
+				goto key_not_found;
+		}
+		sne = tcp_ao_compute_sne(info->rcv_sne, info->rcv_sne_seq,
+					 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,
+					 traffic_key, phash, sne);
+		if (err)
+			return err;
+		/* Key rotation: the peer asks us to use new key (RNext) */
+		if (unlikely(aoh->rnext_keyid != info->current_key->sndid)) {
+			/* If the key is not found we do nothing. */
+			key = tcp_ao_do_lookup_sndid(sk, aoh->rnext_keyid);
+			if (key)
+				/* pairs with tcp_ao_del_cmd */
+				WRITE_ONCE(info->current_key, key);
+		}
+		return SKB_NOT_DROPPED_YET;
+	}
+
+	sne = 0;
+	/* Lookup key based on peer address and keyid.
+	 * current_key and rnext_key must not be used on tcp listen
+	 * sockets as otherwise:
+	 * - request sockets would race on those key pointers
+	 * - tcp_ao_del_cmd() allows async key removal
+	 */
+	key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid);
+	if (!key)
+		goto key_not_found;
+
+	if (sk->sk_state == TCP_LISTEN) {
+		/* Make the initial syn the likely case here */
+		if (unlikely(req)) {
+			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 {
+			sisn = th->seq;
+			disn = 0;
+		}
+	} else if (sk->sk_state == TCP_SYN_SENT) {
+		disn = info->lisn;
+		if (th->syn) {
+			sisn = th->seq;
+			if (!th->ack) {
+				/* Simultaneous connect */
+				disn = 0;
+			}
+		} else {
+			sisn = info->risn;
+		}
+	} else {
+		WARN_ONCE(1, "TCP-AO: Unknown sk_state %d", sk->sk_state);
+		return SKB_DROP_REASON_TCP_AOFAILURE;
+	}
+	traffic_key = key_buf;
+	ops->ao_calc_key_skb(key, traffic_key, skb, sisn, disn);
+	return tcp_ao_verify_hash(sk, skb, family, info, aoh, key,
+				  traffic_key, phash, sne);
+
+key_not_found:
+	return SKB_DROP_REASON_TCP_AOKEYNOTFOUND;
+}
+
 int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao,
 			      struct tcp_ao_key *ao_key)
 {
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 02ab94461e86..a1e1a23abfea 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2172,9 +2172,9 @@ int tcp_v4_rcv(struct sk_buff *skb)
 		if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
 			drop_reason = SKB_DROP_REASON_XFRM_POLICY;
 		else
-			drop_reason = tcp_inbound_md5_hash(sk, skb,
-						   &iph->saddr, &iph->daddr,
-						   AF_INET, dif, sdif);
+			drop_reason = tcp_inbound_hash(sk, req, skb,
+						       &iph->saddr, &iph->daddr,
+						       AF_INET, dif, sdif);
 		if (unlikely(drop_reason)) {
 			sk_drops_add(sk, skb);
 			reqsk_put(req);
@@ -2250,8 +2250,8 @@ int tcp_v4_rcv(struct sk_buff *skb)
 		goto discard_and_relse;
 	}
 
-	drop_reason = tcp_inbound_md5_hash(sk, skb, &iph->saddr,
-					   &iph->daddr, AF_INET, dif, sdif);
+	drop_reason = tcp_inbound_hash(sk, NULL, skb, &iph->saddr, &iph->daddr,
+				       AF_INET, dif, sdif);
 	if (drop_reason)
 		goto discard_and_relse;
 
@@ -2410,6 +2410,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
 	.calc_ao_hash		= tcp_v4_ao_hash_skb,
 	.ao_parse		= tcp_v4_parse_ao,
 	.ao_calc_key_sk		= tcp_v4_ao_calc_key_sk,
+	.ao_calc_key_skb	= tcp_v4_ao_calc_key_skb,
 #endif
 };
 #endif
diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c
index 526bbe232a64..f23c817166bb 100644
--- a/net/ipv6/tcp_ao.c
+++ b/net/ipv6/tcp_ao.c
@@ -39,6 +39,18 @@ int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
 	return tcp_ao_calc_traffic_key(mkt, key, &tmp, sizeof(tmp));
 }
 
+int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
+			   const struct sk_buff *skb,
+			   __be32 sisn, __be32 disn)
+{
+	const struct ipv6hdr *iph = ipv6_hdr(skb);
+	const struct tcphdr *th = tcp_hdr(skb);
+
+	return tcp_v6_ao_calc_key(mkt, key, &iph->saddr,
+				  &iph->daddr, th->source,
+				  th->dest, sisn, disn);
+}
+
 int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 			  const struct sock *sk, __be32 sisn,
 			  __be32 disn, bool send)
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 16cea7de0851..8a27408549cd 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1808,9 +1808,9 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb)
 		struct sock *nsk;
 
 		sk = req->rsk_listener;
-		drop_reason = tcp_inbound_md5_hash(sk, skb,
-						   &hdr->saddr, &hdr->daddr,
-						   AF_INET6, dif, sdif);
+		drop_reason = tcp_inbound_hash(sk, req, skb,
+					       &hdr->saddr, &hdr->daddr,
+					       AF_INET6, dif, sdif);
 		if (drop_reason) {
 			sk_drops_add(sk, skb);
 			reqsk_put(req);
@@ -1882,8 +1882,8 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb)
 		goto discard_and_relse;
 	}
 
-	drop_reason = tcp_inbound_md5_hash(sk, skb, &hdr->saddr, &hdr->daddr,
-					   AF_INET6, dif, sdif);
+	drop_reason = tcp_inbound_hash(sk, NULL, skb, &hdr->saddr, &hdr->daddr,
+				       AF_INET6, dif, sdif);
 	if (drop_reason)
 		goto discard_and_relse;
 
@@ -2075,6 +2075,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
 	.calc_ao_hash	=	tcp_v6_ao_hash_skb,
 	.ao_parse	=	tcp_v6_parse_ao,
 	.ao_calc_key_sk	=	tcp_v6_ao_calc_key_sk,
+	.ao_calc_key_skb =	tcp_v6_ao_calc_key_skb,
 #endif
 };
 #endif
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 18/31] net/tcp: Add TCP-AO segments counters
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (16 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 17/31] net/tcp: Verify inbound TCP-AO signed segments Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 19/31] net/tcp: Add TCP-AO SNE support Dmitry Safonov
                   ` (13 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Introduce segment counters that are useful for troubleshooting/debugging
as well as for writing tests.
Now there are global snmp counters as well as per-socket and per-key.

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>
---
 include/net/dropreason.h  | 15 +++++++++++----
 include/net/tcp.h         |  9 ++++++++-
 include/net/tcp_ao.h      | 10 ++++++++++
 include/uapi/linux/snmp.h |  4 ++++
 net/ipv4/proc.c           |  4 ++++
 net/ipv4/tcp_ao.c         | 25 ++++++++++++++++++++++---
 6 files changed, 59 insertions(+), 8 deletions(-)

diff --git a/include/net/dropreason.h b/include/net/dropreason.h
index b4906323b9a7..dd90cca282eb 100644
--- a/include/net/dropreason.h
+++ b/include/net/dropreason.h
@@ -81,17 +81,24 @@ enum skb_drop_reason {
 	 */
 	SKB_DROP_REASON_TCP_MD5FAILURE,
 	/**
-	 * @SKB_DROP_REASON_TCP_AONOTFOUND: no TCP-AO hash and one was expected
+	 * @SKB_DROP_REASON_TCP_AONOTFOUND: no TCP-AO hash and one was expected,
+	 * corresponding to LINUX_MIB_TCPAOREQUIRED
 	 */
 	SKB_DROP_REASON_TCP_AONOTFOUND,
 	/**
 	 * @SKB_DROP_REASON_TCP_AOUNEXPECTED: TCP-AO hash is present and it
-	 * was not expected.
+	 * was not expected, corresponding to LINUX_MIB_TCPAOKEYNOTFOUND
 	 */
 	SKB_DROP_REASON_TCP_AOUNEXPECTED,
-	/** @SKB_DROP_REASON_TCP_AOKEYNOTFOUND: TCP-AO key is unknown */
+	/**
+	 * @SKB_DROP_REASON_TCP_AOKEYNOTFOUND: TCP-AO key is unknown,
+	 * corresponding to LINUX_MIB_TCPAOKEYNOTFOUND
+	 */
 	SKB_DROP_REASON_TCP_AOKEYNOTFOUND,
-	/** @SKB_DROP_REASON_TCP_AOFAILURE: TCP-AO hash is wrong */
+	/**
+	 * @SKB_DROP_REASON_TCP_AOFAILURE: TCP-AO hash is wrong,
+	 * corresponding to LINUX_MIB_TCPAOBAD
+	 */
 	SKB_DROP_REASON_TCP_AOFAILURE,
 	/**
 	 * @SKB_DROP_REASON_SOCKET_BACKLOG: failed to add skb to socket backlog (
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 2e75c542e7ed..94573219f58d 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -2538,8 +2538,15 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req,
 		 * always at least one current_key.
 		 */
 #ifdef CONFIG_TCP_AO
-		if (unlikely(tcp_ao_do_lookup(sk, saddr, family, -1, -1, 0)))
+		if (unlikely(tcp_ao_do_lookup(sk, saddr, family, -1, -1, 0))) {
+			struct tcp_ao_info *ao_info;
+
+			ao_info = rcu_dereference_check(tcp_sk(sk)->ao_info,
+					lockdep_sock_is_held(sk));
+			NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOREQUIRED);
+			atomic64_inc(&ao_info->counters.ao_required);
 			return SKB_DROP_REASON_TCP_AONOTFOUND;
+		}
 #endif
 		if (unlikely(tcp_md5_do_lookup(sk, l3index, saddr, family))) {
 			NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index 7cb802de49ba..dbeaa7d4e212 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -20,6 +20,13 @@ struct tcp_ao_hdr {
 	u8	rnext_keyid;
 };
 
+struct tcp_ao_counters {
+	atomic64_t	pkt_good;
+	atomic64_t	pkt_bad;
+	atomic64_t	key_not_found;
+	atomic64_t	ao_required;
+};
+
 struct tcp_ao_key {
 	struct hlist_node	node;
 	union tcp_ao_addr	addr;
@@ -35,6 +42,8 @@ struct tcp_ao_key {
 	u8			maclen;
 	u8			digest_size;
 	struct rcu_head		rcu;
+	atomic64_t		pkt_good;
+	atomic64_t		pkt_bad;
 	u8			traffic_keys[];
 };
 
@@ -78,6 +87,7 @@ struct tcp_ao_info {
 	 */
 	struct tcp_ao_key	*volatile current_key;
 	struct tcp_ao_key	*rnext_key;
+	struct tcp_ao_counters	counters;
 	u8			ao_flags;
 	__be32			lisn;
 	__be32			risn;
diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h
index 4d7470036a8b..f09119db8b40 100644
--- a/include/uapi/linux/snmp.h
+++ b/include/uapi/linux/snmp.h
@@ -292,6 +292,10 @@ enum
 	LINUX_MIB_TCPDSACKIGNOREDDUBIOUS,	/* TCPDSACKIgnoredDubious */
 	LINUX_MIB_TCPMIGRATEREQSUCCESS,		/* TCPMigrateReqSuccess */
 	LINUX_MIB_TCPMIGRATEREQFAILURE,		/* TCPMigrateReqFailure */
+	LINUX_MIB_TCPAOREQUIRED,		/* TCPAORequired */
+	LINUX_MIB_TCPAOBAD,			/* TCPAOBad */
+	LINUX_MIB_TCPAOKEYNOTFOUND,		/* TCPAOKeyNotFound */
+	LINUX_MIB_TCPAOGOOD,			/* TCPAOGood */
 	__LINUX_MIB_MAX
 };
 
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index 0088a4c64d77..1b5a078adcf1 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -297,6 +297,10 @@ static const struct snmp_mib snmp4_net_list[] = {
 	SNMP_MIB_ITEM("TCPDSACKIgnoredDubious", LINUX_MIB_TCPDSACKIGNOREDDUBIOUS),
 	SNMP_MIB_ITEM("TCPMigrateReqSuccess", LINUX_MIB_TCPMIGRATEREQSUCCESS),
 	SNMP_MIB_ITEM("TCPMigrateReqFailure", LINUX_MIB_TCPMIGRATEREQFAILURE),
+	SNMP_MIB_ITEM("TCPAORequired", LINUX_MIB_TCPAOREQUIRED),
+	SNMP_MIB_ITEM("TCPAOBad", LINUX_MIB_TCPAOBAD),
+	SNMP_MIB_ITEM("TCPAOKeyNotFound", LINUX_MIB_TCPAOKEYNOTFOUND),
+	SNMP_MIB_ITEM("TCPAOGood", LINUX_MIB_TCPAOGOOD),
 	SNMP_MIB_SENTINEL
 };
 
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 10cd6af3c45f..3a33733a714d 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -216,6 +216,8 @@ struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, struct tcp_ao_key *key)
 	*new_key = *key;
 	INIT_HLIST_NODE(&new_key->node);
 	crypto_pool_add(new_key->crypto_pool_id);
+	atomic64_set(&new_key->pkt_good, 0);
+	atomic64_set(&new_key->pkt_bad, 0);
 
 	return new_key;
 }
@@ -656,14 +658,25 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb,
 	u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr);
 	const struct tcphdr *th = tcp_hdr(skb);
 
-	if (maclen != tcp_ao_maclen(key))
+	if (maclen != tcp_ao_maclen(key)) {
+		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
+		atomic64_inc(&info->counters.pkt_bad);
+		atomic64_inc(&key->pkt_bad);
 		return SKB_DROP_REASON_TCP_AOFAILURE;
+	}
 
 	/* XXX: make it per-AF callback? */
 	tcp_ao_hash_skb(family, newhash, key, sk, skb, traffic_key,
 			(phash - (u8 *)th), sne);
-	if (memcmp(phash, newhash, maclen))
+	if (memcmp(phash, newhash, maclen)) {
+		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
+		atomic64_inc(&info->counters.pkt_bad);
+		atomic64_inc(&key->pkt_bad);
 		return SKB_DROP_REASON_TCP_AOFAILURE;
+	}
+	NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD);
+	atomic64_inc(&info->counters.pkt_good);
+	atomic64_inc(&key->pkt_good);
 	return SKB_NOT_DROPPED_YET;
 }
 
@@ -683,8 +696,10 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
 	u32 sne;
 
 	info = rcu_dereference(tcp_sk(sk)->ao_info);
-	if (!info)
+	if (!info) {
+		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND);
 		return SKB_DROP_REASON_TCP_AOUNEXPECTED;
+	}
 
 	/* Fast-path */
 	/* TODO: fix fastopen and simultaneous open (TCPF_SYN_RECV) */
@@ -763,6 +778,8 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
 				  traffic_key, phash, sne);
 
 key_not_found:
+	NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND);
+	atomic64_inc(&info->counters.key_not_found);
 	return SKB_DROP_REASON_TCP_AOKEYNOTFOUND;
 }
 
@@ -1416,6 +1433,8 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
 	key->keyflags	= cmd.tcpa_keyflags;
 	key->sndid	= cmd.tcpa_sndid;
 	key->rcvid	= cmd.tcpa_rcvid;
+	atomic64_set(&key->pkt_good, 0);
+	atomic64_set(&key->pkt_bad, 0);
 
 	ret = tcp_ao_parse_crypto(&cmd, key);
 	if (ret < 0)
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 19/31] net/tcp: Add TCP-AO SNE support
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (17 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 18/31] net/tcp: Add TCP-AO segments counters Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-23 14:50   ` Leonard Crestez
  2022-08-18 16:59 ` [PATCH 20/31] net/tcp: Add tcp_hash_fail() ratelimited logs Dmitry Safonov
                   ` (12 subsequent siblings)
  31 siblings, 1 reply; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Add Sequence Number Extension (SNE) extension 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>
---
 net/ipv4/tcp_input.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index b8175ded8a70..8d326994d1a1 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3516,9 +3516,21 @@ 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) {
+		if (ack < ao->snd_sne_seq)
+			ao->snd_sne++;
+		ao->snd_sne_seq = ack;
+	}
+#endif
 	tp->snd_una = ack;
 }
 
@@ -3526,9 +3538,21 @@ 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) {
+		if (seq < ao->rcv_sne_seq)
+			ao->rcv_sne++;
+		ao->rcv_sne_seq = seq;
+	}
+#endif
 	WRITE_ONCE(tp->rcv_nxt, seq);
 }
 
@@ -6344,6 +6368,17 @@ 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) {
+			ao->risn = th->seq;
+			ao->rcv_sne = 0;
+			ao->rcv_sne_seq = ntohl(th->seq);
+		}
+#endif
 		tcp_set_state(sk, TCP_SYN_RECV);
 
 		if (tp->rx_opt.saw_tstamp) {
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 20/31] net/tcp: Add tcp_hash_fail() ratelimited logs
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (18 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 19/31] net/tcp: Add TCP-AO SNE support Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 21/31] net/tcp: Ignore specific ICMPs for TCP-AO connections Dmitry Safonov
                   ` (11 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Add a helper for logging connection-detailed messages for failed TCP
hash verification (both MD5 and AO).

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>
---
 include/net/tcp.h    | 14 ++++++++++++--
 include/net/tcp_ao.h | 27 +++++++++++++++++++++++++++
 net/ipv4/tcp.c       | 23 +++++++++++++----------
 net/ipv4/tcp_ao.c    |  7 +++++++
 4 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/include/net/tcp.h b/include/net/tcp.h
index 94573219f58d..896db7ba0670 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -2517,12 +2517,19 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req,
 	int l3index;
 
 	/* Invalid option or two times meet any of auth options */
-	if (tcp_parse_auth_options(th, &md5_location, &aoh))
+	if (tcp_parse_auth_options(th, &md5_location, &aoh)) {
+		tcp_hash_fail("TCP segment has incorrect auth options set",
+				family, skb, "");
 		return SKB_DROP_REASON_TCP_AUTH_HDR;
+	}
 
 	if (req) {
-		if (tcp_rsk_used_ao(req) != !!aoh)
+		if (tcp_rsk_used_ao(req) != !!aoh) {
+			tcp_hash_fail("TCP connection can't start/end using TCP-AO",
+					family, skb, " %s",
+					!aoh ? "missing AO" : "AO signed");
 			return SKB_DROP_REASON_TCP_AOFAILURE;
+		}
 	}
 
 	/* sdif set, means packet ingressed via a device
@@ -2545,11 +2552,14 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req,
 					lockdep_sock_is_held(sk));
 			NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOREQUIRED);
 			atomic64_inc(&ao_info->counters.ao_required);
+			tcp_hash_fail("AO hash is required, but not found",
+					family, skb, "");
 			return SKB_DROP_REASON_TCP_AONOTFOUND;
 		}
 #endif
 		if (unlikely(tcp_md5_do_lookup(sk, l3index, saddr, family))) {
 			NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND);
+			tcp_hash_fail("MD5 Hash not found", family, skb, "");
 			return SKB_DROP_REASON_TCP_MD5NOTFOUND;
 		}
 		return SKB_NOT_DROPPED_YET;
diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index dbeaa7d4e212..e99c8f300a5a 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -101,6 +101,33 @@ struct tcp_ao_info {
 int tcp_do_parse_auth_options(const struct tcphdr *th,
 			      const u8 **md5_hash, const u8 **ao_hash);
 
+#define tcp_hash_fail(msg, family, skb, fmt, ...)			\
+do {									\
+	const struct tcphdr *th = tcp_hdr(skb);				\
+	char hdr_flags[5] = {};						\
+	char *f = hdr_flags;						\
+									\
+	if (th->fin)							\
+		*f++ = 'F';						\
+	if (th->syn)							\
+		*f++ = 'S';						\
+	if (th->rst)							\
+		*f++ = 'R';						\
+	if (th->ack)							\
+		*f = 'A';						\
+	if (family == AF_INET) {					\
+		net_info_ratelimited("%s for (%pI4, %d)->(%pI4, %d) %s" fmt "\n", \
+				msg, &ip_hdr(skb)->saddr, ntohs(th->source), \
+				&ip_hdr(skb)->daddr, ntohs(th->dest),	\
+				hdr_flags, ##__VA_ARGS__);		\
+	} else {							\
+		net_info_ratelimited("%s for [%pI6c]:%u->[%pI6c]:%u %s" fmt "\n", \
+				msg, &ipv6_hdr(skb)->saddr, ntohs(th->source), \
+				&ipv6_hdr(skb)->daddr, ntohs(th->dest),	\
+				hdr_flags, ##__VA_ARGS__);		\
+	}								\
+} while (0)
+
 #ifdef CONFIG_TCP_AO
 /* TCP-AO structures and functions */
 
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 8df03d456ebb..3ef9d69fa561 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -4511,7 +4511,6 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 	 * o MD5 hash and we're not expecting one.
 	 * o MD5 hash and its wrong.
 	 */
-	const struct tcphdr *th = tcp_hdr(skb);
 	struct tcp_sock *tp = tcp_sk(sk);
 	struct tcp_md5sig_key *key;
 	int genhash;
@@ -4521,6 +4520,7 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 
 	if (!key && hash_location) {
 		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED);
+		tcp_hash_fail("Unexpected MD5 Hash found", family, skb, "");
 		return SKB_DROP_REASON_TCP_MD5UNEXPECTED;
 	}
 
@@ -4536,16 +4536,19 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb,
 	if (genhash || memcmp(hash_location, newhash, 16) != 0) {
 		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE);
 		if (family == AF_INET) {
-			net_info_ratelimited("MD5 Hash failed for (%pI4, %d)->(%pI4, %d)%s L3 index %d\n",
-					saddr, ntohs(th->source),
-					daddr, ntohs(th->dest),
-					genhash ? " tcp_v4_calc_md5_hash failed"
-					: "", l3index);
+			tcp_hash_fail("MD5 Hash failed", AF_INET, skb, "%s L3 index %d",
+				      genhash ? " tcp_v4_calc_md5_hash failed"
+				      : "", l3index);
 		} else {
-			net_info_ratelimited("MD5 Hash %s for [%pI6c]:%u->[%pI6c]:%u L3 index %d\n",
-					genhash ? "failed" : "mismatch",
-					saddr, ntohs(th->source),
-					daddr, ntohs(th->dest), l3index);
+			if (genhash) {
+				tcp_hash_fail("MD5 Hash failed",
+					      AF_INET6, skb, " L3 index %d",
+					      l3index);
+			} else {
+				tcp_hash_fail("MD5 Hash mismatch",
+					      AF_INET6, skb, " L3 index %d",
+					      l3index);
+			}
 		}
 		return SKB_DROP_REASON_TCP_MD5FAILURE;
 	}
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 3a33733a714d..4283e0193e2a 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -662,6 +662,8 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb,
 		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
 		atomic64_inc(&info->counters.pkt_bad);
 		atomic64_inc(&key->pkt_bad);
+		tcp_hash_fail("AO hash wrong length", family, skb,
+			      " %u != %d", maclen, tcp_ao_maclen(key));
 		return SKB_DROP_REASON_TCP_AOFAILURE;
 	}
 
@@ -672,6 +674,7 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb,
 		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
 		atomic64_inc(&info->counters.pkt_bad);
 		atomic64_inc(&key->pkt_bad);
+		tcp_hash_fail("AO hash mismatch", family, skb, "");
 		return SKB_DROP_REASON_TCP_AOFAILURE;
 	}
 	NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD);
@@ -698,6 +701,8 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
 	info = rcu_dereference(tcp_sk(sk)->ao_info);
 	if (!info) {
 		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND);
+		tcp_hash_fail("AO key not found", family, skb,
+			      " keyid: %u", aoh->keyid);
 		return SKB_DROP_REASON_TCP_AOUNEXPECTED;
 	}
 
@@ -780,6 +785,8 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
 key_not_found:
 	NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND);
 	atomic64_inc(&info->counters.key_not_found);
+	tcp_hash_fail("Requested by the peer AO key id not found",
+		      family, skb, "");
 	return SKB_DROP_REASON_TCP_AOKEYNOTFOUND;
 }
 
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 21/31] net/tcp: Ignore specific ICMPs for TCP-AO connections
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (19 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 20/31] net/tcp: Add tcp_hash_fail() ratelimited logs Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 22/31] net/tcp: Add option for TCP-AO to (not) hash header Dmitry Safonov
                   ` (10 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Similarly to IPsec, RFC5925 prescribes:
  ">> A TCP-AO implementation MUST default to ignore incoming ICMPv4
  messages of Type 3 (destination unreachable), Codes 2-4 (protocol
  unreachable, port unreachable, and fragmentation needed -- ’hard
  errors’), and ICMPv6 Type 1 (destination unreachable), Code 1
  (administratively prohibited) and Code 4 (port unreachable) intended
  for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN-
  WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs."

A selftest (later in patch series) verifies that this attack is not
possible in this TCP-AO implementation.

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>
---
 include/net/tcp_ao.h      |  9 +++++
 include/uapi/linux/snmp.h |  1 +
 include/uapi/linux/tcp.h  |  1 +
 net/ipv4/proc.c           |  1 +
 net/ipv4/tcp_ao.c         | 70 ++++++++++++++++++++++++++++++++++++++-
 net/ipv4/tcp_ipv4.c       |  5 +++
 net/ipv6/tcp_ipv6.c       |  4 +++
 7 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index e99c8f300a5a..743a910ba508 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -25,6 +25,7 @@ struct tcp_ao_counters {
 	atomic64_t	pkt_bad;
 	atomic64_t	key_not_found;
 	atomic64_t	ao_required;
+	atomic64_t	dropped_icmp;
 };
 
 struct tcp_ao_key {
@@ -77,6 +78,9 @@ static inline unsigned int tcp_ao_digest_size(struct tcp_ao_key *key)
 	return key->digest_size;
 }
 
+/* bits in 'ao_flags' */
+#define AO_ACCEPT_ICMPS		BIT(0)
+
 struct tcp_ao_info {
 	struct hlist_head	head;
 	struct rcu_head		rcu;
@@ -169,6 +173,7 @@ u32 tcp_ao_compute_sne(u32 sne, u32 seq, u32 new_seq);
 void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
 int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao,
 			      struct tcp_ao_key *ao_key);
+bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code);
 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,
@@ -239,6 +244,10 @@ void tcp_ao_connect_init(struct sock *sk);
 
 #else /* CONFIG_TCP_AO */
 
+static inline bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code)
+{
+	return false;
+}
 static inline 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)
diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h
index f09119db8b40..bc7655394e9a 100644
--- a/include/uapi/linux/snmp.h
+++ b/include/uapi/linux/snmp.h
@@ -296,6 +296,7 @@ enum
 	LINUX_MIB_TCPAOBAD,			/* TCPAOBad */
 	LINUX_MIB_TCPAOKEYNOTFOUND,		/* TCPAOKeyNotFound */
 	LINUX_MIB_TCPAOGOOD,			/* TCPAOGood */
+	LINUX_MIB_TCPAODROPPEDICMPS,		/* TCPAODroppedIcmps */
 	__LINUX_MIB_MAX
 };
 
diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
index 5369458ae89f..508bedbc6ad8 100644
--- a/include/uapi/linux/tcp.h
+++ b/include/uapi/linux/tcp.h
@@ -349,6 +349,7 @@ struct tcp_diag_md5sig {
 
 #define TCP_AO_CMDF_CURR	(1 << 0)	/* Only checks field sndid */
 #define TCP_AO_CMDF_NEXT	(1 << 1)	/* Only checks field rcvid */
+#define TCP_AO_CMDF_ACCEPT_ICMP	(1 << 2)	/* Accept incoming ICMPs */
 
 struct tcp_ao { /* setsockopt(TCP_AO) */
 	struct __kernel_sockaddr_storage tcpa_addr;
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index 1b5a078adcf1..ccfb7f51e82f 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -301,6 +301,7 @@ static const struct snmp_mib snmp4_net_list[] = {
 	SNMP_MIB_ITEM("TCPAOBad", LINUX_MIB_TCPAOBAD),
 	SNMP_MIB_ITEM("TCPAOKeyNotFound", LINUX_MIB_TCPAOKEYNOTFOUND),
 	SNMP_MIB_ITEM("TCPAOGood", LINUX_MIB_TCPAOGOOD),
+	SNMP_MIB_ITEM("TCPAODroppedIcmps", LINUX_MIB_TCPAODROPPEDICMPS),
 	SNMP_MIB_SENTINEL
 };
 
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 4283e0193e2a..858295393643 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -15,6 +15,7 @@
 
 #include <net/tcp.h>
 #include <net/ipv6.h>
+#include <net/icmp.h>
 
 int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
 			    unsigned int len)
@@ -52,6 +53,63 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
 	return 1;
 }
 
+bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code)
+{
+	struct tcp_ao_info *ao;
+	bool ignore_icmp = false;
+
+	/* RFC5925, 7.8:
+	 * >> A TCP-AO implementation MUST default to ignore incoming ICMPv4
+	 * messages of Type 3 (destination unreachable), Codes 2-4 (protocol
+	 * unreachable, port unreachable, and fragmentation needed -- ’hard
+	 * errors’), and ICMPv6 Type 1 (destination unreachable), Code 1
+	 * (administratively prohibited) and Code 4 (port unreachable) intended
+	 * for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN-
+	 * WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs.
+	 */
+	if (sk->sk_family == AF_INET) {
+		if (type != ICMP_DEST_UNREACH)
+			return false;
+		if (code < ICMP_PROT_UNREACH || code > ICMP_FRAG_NEEDED)
+			return false;
+	} else if (sk->sk_family == AF_INET6) {
+		if (type != ICMPV6_DEST_UNREACH)
+			return false;
+		if (code != ICMPV6_ADM_PROHIBITED && code != ICMPV6_PORT_UNREACH)
+			return false;
+	} else {
+		WARN_ON_ONCE(1);
+		return false;
+	}
+
+	rcu_read_lock();
+	switch (sk->sk_state) {
+	case TCP_TIME_WAIT:
+		ao = rcu_dereference(tcp_twsk(sk)->ao_info);
+		break;
+	case TCP_SYN_SENT:
+	case TCP_SYN_RECV:
+	case TCP_LISTEN:
+	case TCP_NEW_SYN_RECV:
+		/* RFC5925 specifies to ignore ICMPs *only* on connections
+		 * in synchronized states.
+		 */
+		rcu_read_unlock();
+		return false;
+	default:
+		ao = rcu_dereference(tcp_sk(sk)->ao_info);
+	}
+
+	if (ao && !(ao->ao_flags & AO_ACCEPT_ICMPS)) {
+		ignore_icmp = true;
+		__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAODROPPEDICMPS);
+		atomic64_inc(&ao->counters.dropped_icmp);
+	}
+	rcu_read_unlock();
+
+	return ignore_icmp;
+}
+
 struct tcp_ao_key *tcp_ao_do_lookup_keyid(struct tcp_ao_info *ao,
 					  int sndid, int rcvid)
 {
@@ -1360,7 +1418,7 @@ static bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd,
 
 #define TCP_AO_KEYF_ALL		(0)
 #define TCP_AO_CMDF_ADDMOD_VALID					\
-	(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT)
+	(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP)
 #define TCP_AO_CMDF_DEL_VALID						\
 	(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT)
 
@@ -1443,6 +1501,11 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
 	atomic64_set(&key->pkt_good, 0);
 	atomic64_set(&key->pkt_bad, 0);
 
+	if (cmd.tcpa_flags & TCP_AO_CMDF_ACCEPT_ICMP)
+		ao_info->ao_flags |= AO_ACCEPT_ICMPS;
+	else
+		ao_info->ao_flags &= ~AO_ACCEPT_ICMPS;
+
 	ret = tcp_ao_parse_crypto(&cmd, key);
 	if (ret < 0)
 		goto err_free_sock;
@@ -1601,6 +1664,11 @@ static int tcp_ao_mod_cmd(struct sock *sk, unsigned short int family,
 	if (!ao_info)
 		return -ENOENT;
 	/* TODO: make tcp_ao_current_rnext() and flags set atomic */
+	if (cmd.tcpa_flags & TCP_AO_CMDF_ACCEPT_ICMP)
+		ao_info->ao_flags |= AO_ACCEPT_ICMPS;
+	else
+		ao_info->ao_flags &= ~AO_ACCEPT_ICMPS;
+
 	return tcp_ao_current_rnext(sk, cmd.tcpa_flags,
 			cmd.tcpa_current, cmd.tcpa_rnext);
 }
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index a1e1a23abfea..bb4d208da8a8 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -484,6 +484,8 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)
 		return -ENOENT;
 	}
 	if (sk->sk_state == TCP_TIME_WAIT) {
+		/* To increase the counter of ignored icmps for TCP-AO */
+		tcp_ao_ignore_icmp(sk, type, code);
 		inet_twsk_put(inet_twsk(sk));
 		return 0;
 	}
@@ -498,6 +500,9 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)
 	}
 
 	bh_lock_sock(sk);
+	if (tcp_ao_ignore_icmp(sk, type, code))
+		goto out;
+
 	/* If too many ICMPs get dropped on busy
 	 * servers this needs to be solved differently.
 	 * We do take care of PMTU discovery (RFC1191) special case :
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 8a27408549cd..78994d1cbc45 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -399,6 +399,8 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 	}
 
 	if (sk->sk_state == TCP_TIME_WAIT) {
+		/* To increase the counter of ignored icmps for TCP-AO */
+		tcp_ao_ignore_icmp(sk, type, code);
 		inet_twsk_put(inet_twsk(sk));
 		return 0;
 	}
@@ -410,6 +412,8 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 	}
 
 	bh_lock_sock(sk);
+	if (tcp_ao_ignore_icmp(sk, type, code))
+		goto out;
 	if (sock_owned_by_user(sk) && type != ICMPV6_PKT_TOOBIG)
 		__NET_INC_STATS(net, LINUX_MIB_LOCKDROPPEDICMPS);
 
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 22/31] net/tcp: Add option for TCP-AO to (not) hash header
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (20 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 21/31] net/tcp: Ignore specific ICMPs for TCP-AO connections Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 23/31] net/tcp: Add getsockopt(TCP_AO_GET) Dmitry Safonov
                   ` (9 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Provide setsockopt() key flag that makes TCP-AO exclude hashing TCP
header for peers that match the key. This is needed for interraction
with middleboxes that may change TCP options, see RFC5925 (9.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>
---
 include/uapi/linux/tcp.h | 2 ++
 net/ipv4/tcp_ao.c        | 8 +++++---
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
index 508bedbc6ad8..b60933ee2a27 100644
--- a/include/uapi/linux/tcp.h
+++ b/include/uapi/linux/tcp.h
@@ -347,6 +347,8 @@ struct tcp_diag_md5sig {
 
 #define TCP_AO_MAXKEYLEN	80
 
+#define TCP_AO_KEYF_EXCLUDE_OPT	(1 << 0)
+
 #define TCP_AO_CMDF_CURR	(1 << 0)	/* Only checks field sndid */
 #define TCP_AO_CMDF_NEXT	(1 << 1)	/* Only checks field rcvid */
 #define TCP_AO_CMDF_ACCEPT_ICMP	(1 << 2)	/* Accept incoming ICMPs */
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 858295393643..6e18a8cdee90 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -586,7 +586,8 @@ int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash,
 						&saddr->a6, th->doff * 4))
 			goto clear_hash;
 	}
-	if (tcp_ao_hash_header(&hp, th, false,
+	if (tcp_ao_hash_header(&hp, th,
+			       !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT),
 			       ao_hash, hash_offset, tcp_ao_maclen(key)))
 		goto clear_hash;
 	ahash_request_set_crypt(hp.req, NULL, ao_hash, 0);
@@ -628,7 +629,8 @@ int tcp_ao_hash_skb(unsigned short int family,
 		goto clear_hash;
 	if (tcp_ao_hash_pseudoheader(family, sk, skb, &hp, skb->len))
 		goto clear_hash;
-	if (tcp_ao_hash_header(&hp, th, false,
+	if (tcp_ao_hash_header(&hp, th,
+			       !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT),
 			       ao_hash, hash_offset, tcp_ao_maclen(key)))
 		goto clear_hash;
 	if (tcp_ao_hash_skb_data(&hp, skb, th->doff << 2))
@@ -1416,7 +1418,7 @@ static bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd,
 	return false;
 }
 
-#define TCP_AO_KEYF_ALL		(0)
+#define TCP_AO_KEYF_ALL		(TCP_AO_KEYF_EXCLUDE_OPT)
 #define TCP_AO_CMDF_ADDMOD_VALID					\
 	(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP)
 #define TCP_AO_CMDF_DEL_VALID						\
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 23/31] net/tcp: Add getsockopt(TCP_AO_GET)
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (21 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 22/31] net/tcp: Add option for TCP-AO to (not) hash header Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-23 14:45   ` Leonard Crestez
  2022-08-18 16:59 ` [PATCH 24/31] net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs) Dmitry Safonov
                   ` (8 subsequent siblings)
  31 siblings, 1 reply; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Introduce getsockopt() that let user get TCP-AO keys and their
properties from a socket. A user can provide a filter to match
a specific key to be dumped or TCP_AO_GET_ALL flag may be used to dump
all keys in one syscall.

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>
---
 include/net/tcp_ao.h     |   1 +
 include/uapi/linux/tcp.h |  19 ++++
 net/ipv4/tcp.c           |  11 ++
 net/ipv4/tcp_ao.c        | 223 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 254 insertions(+)

diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index 743a910ba508..b5088d4c5587 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -174,6 +174,7 @@ void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
 int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao,
 			      struct tcp_ao_key *ao_key);
 bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code);
+int tcp_ao_get_mkts(struct sock *sk, char __user *optval, int __user *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,
diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
index b60933ee2a27..453187d21da8 100644
--- a/include/uapi/linux/tcp.h
+++ b/include/uapi/linux/tcp.h
@@ -132,6 +132,7 @@ enum {
 #define TCP_AO			38	/* (Add/Set MKT) */
 #define TCP_AO_DEL		39	/* (Delete MKT) */
 #define TCP_AO_MOD		40	/* (Modify MKT) */
+#define TCP_AO_GET		41	/* (Get MKTs) */
 
 #define TCP_REPAIR_ON		1
 #define TCP_REPAIR_OFF		0
@@ -353,6 +354,10 @@ struct tcp_diag_md5sig {
 #define TCP_AO_CMDF_NEXT	(1 << 1)	/* Only checks field rcvid */
 #define TCP_AO_CMDF_ACCEPT_ICMP	(1 << 2)	/* Accept incoming ICMPs */
 
+#define TCP_AO_GET_CURR		TCP_AO_CMDF_CURR
+#define TCP_AO_GET_NEXT		TCP_AO_CMDF_NEXT
+#define TCP_AO_GET_ALL		(1 << 2)
+
 struct tcp_ao { /* setsockopt(TCP_AO) */
 	struct __kernel_sockaddr_storage tcpa_addr;
 	char	tcpa_alg_name[64];
@@ -382,6 +387,20 @@ struct tcp_ao_mod { /* setsockopt(TCP_AO_MOD) */
 	__u8	tcpa_rnext;
 } __attribute__((aligned(8)));
 
+struct tcp_ao_getsockopt { /* getsockopt(TCP_AO_GET) */
+	struct __kernel_sockaddr_storage addr;
+	__u8	sndid;
+	__u8	rcvid;
+	__u32	nkeys;
+	char	alg_name[64];
+	__u16	flags;
+	__u8	prefix;
+	__u8	maclen;
+	__u8	keyflags;
+	__u8	keylen;
+	__u8	key[TCP_AO_MAXKEYLEN];
+} __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 3ef9d69fa561..aa5ce2c738f0 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -4376,6 +4376,17 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
 			err = -EFAULT;
 		return err;
 	}
+#endif
+#ifdef CONFIG_TCP_AO
+	case TCP_AO_GET: {
+		int err;
+
+		lock_sock(sk);
+		err = tcp_ao_get_mkts(sk, optval, optlen);
+		release_sock(sk);
+
+		return err;
+	}
 #endif
 	default:
 		return -ENOPROTOOPT;
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 6e18a8cdee90..5ab16b857c29 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -1423,6 +1423,8 @@ static bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd,
 	(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP)
 #define TCP_AO_CMDF_DEL_VALID						\
 	(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT)
+#define TCP_AO_GETF_VALID						\
+	(TCP_AO_GET_ALL | TCP_AO_GET_CURR | TCP_AO_GET_NEXT)
 
 static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
 			  sockptr_t optval, int optlen)
@@ -1699,3 +1701,224 @@ int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen)
 	return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen);
 }
 
+/* tcp_ao_copy_mkts_to_user(ao_info, optval, optlen)
+ *
+ * @ao_info:	struct tcp_ao_info on the socket that
+ *		socket getsockopt(TCP_AO_GET) is executed on
+ * @optval:	pointer to array of tcp_ao_getsockopt structures in user space.
+ *		Must be != NULL.
+ * @optlen:	pointer to size of tcp_ao_getsockopt structure.
+ *		Must be != NULL.
+ *
+ * Return value: 0 on success, a negative error number otherwise.
+ *
+ * optval points to an array of tcp_ao_getsockopt structures in user space.
+ * optval[0] is used as both input and output to getsockopt. It determines
+ * which keys are returned by the kernel.
+ * optval[0].nkeys is the size of the array in user space. On return it contains
+ * the number of keys matching the search criteria.
+ * If TCP_AO_GET_ALL is set in "flags", then all keys in the socket are
+ * returned, otherwise only keys matching <addr, prefix, sndid, rcvid>
+ * in optval[0] are returned.
+ * optlen is also used as both input and output. The user provides the size
+ * of struct tcp_ao_getsockopt in user space, and the kernel returns the size
+ * of the structure in kernel space.
+ * The size of struct tcp_ao_getsockopt may differ between user and kernel.
+ * There are three cases to consider:
+ *  * If usize == ksize, then keys are copied verbatim.
+ *  * If usize < ksize, then the userspace has passed an old struct to a
+ *    newer kernel. The rest of the trailing bytes in optval[0]
+ *    (ksize - usize) are interpreted as 0 by the kernel.
+ *  * If usize > ksize, then the userspace has passed a new struct to an
+ *    older kernel. The trailing bytes unknown to the kernel (usize - ksize)
+ *    are checked to ensure they are zeroed, otherwise -E2BIG is returned.
+ * On return the kernel fills in min(usize, ksize) in each entry of the array.
+ * The layout of the fields in the user and kernel structures is expected to
+ * be the same (including in the 32bit vs 64bit case).
+ */
+int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info,
+			     char __user *optval, int __user *optlen)
+{
+	struct tcp_ao_getsockopt opt_in;
+	struct tcp_ao_getsockopt opt_out;
+	struct tcp_ao_getsockopt __user *optval_in;
+	int user_len;
+	unsigned int max_keys;	/* maximum number of keys to copy to user */
+	u32 copied_keys;	/* keys copied to user so far */
+	int matched_keys;	/* keys from ao_info matched so far */
+	int bytes_to_write;	/* number of bytes to write to user level */
+	struct tcp_ao_key *key;
+	struct sockaddr_in *sin;   /* (struct sockaddr_in *)&opt_in.addr */
+	struct sockaddr_in6 *sin6; /* (struct sockaddr_in6 *)&opt_in.addr */
+	struct in6_addr *addr6;    /* &sin6->sin6_addr */
+	__kernel_sa_family_t ss_family;
+	union tcp_ao_addr *addr;
+	int optlen_out;
+	u8 prefix_in;
+	u16 port = 0;
+	bool copy_all, copy_current, copy_next;
+	int err;
+
+	if (get_user(user_len, optlen))
+		return -EFAULT;
+
+	if (user_len <= 0)
+		return -EINVAL;
+
+	memset(&opt_in, 0, sizeof(struct tcp_ao_getsockopt));
+	err = copy_struct_from_user(&opt_in, sizeof(struct tcp_ao_getsockopt),
+				    optval, user_len);
+	if (err < 0)
+		return err;
+
+	optval_in = (struct tcp_ao_getsockopt __user *)optval;
+	ss_family = opt_in.addr.ss_family;
+
+	BUILD_BUG_ON(TCP_AO_GET_ALL & (TCP_AO_GET_CURR | TCP_AO_GET_NEXT));
+	if (opt_in.flags & ~TCP_AO_GETF_VALID)
+		return -EINVAL;
+
+	max_keys = opt_in.nkeys;
+	copy_all = !!(opt_in.flags & TCP_AO_GET_ALL);
+	copy_current = !!(opt_in.flags & TCP_AO_GET_CURR);
+	copy_next = !!(opt_in.flags & TCP_AO_GET_NEXT);
+
+	if (!(copy_all || copy_current || copy_next)) {
+		prefix_in = opt_in.prefix;
+
+		switch (ss_family) {
+		case AF_INET: {
+			sin = (struct sockaddr_in *)&opt_in.addr;
+			port = sin->sin_port;
+			addr = (union tcp_ao_addr *)&sin->sin_addr;
+
+			if (prefix_in > 32)
+				return -EINVAL;
+
+			if (sin->sin_addr.s_addr == INADDR_ANY &&
+			    prefix_in != 0)
+				return -EINVAL;
+
+			break;
+		}
+		case AF_INET6: {
+			sin6 = (struct sockaddr_in6 *)&opt_in.addr;
+			addr = (union tcp_ao_addr *)&sin6->sin6_addr;
+			addr6 = &sin6->sin6_addr;
+			port = sin6->sin6_port;
+
+			if (prefix_in != 0) {
+				if (ipv6_addr_v4mapped(addr6)) {
+					__be32 addr4 = addr6->s6_addr32[3];
+
+					if (prefix_in > 32 ||
+					    addr4 == INADDR_ANY)
+						return -EINVAL;
+				} else {
+					if (ipv6_addr_any(addr6) ||
+					    prefix_in > 128)
+						return -EINVAL;
+				}
+			} else if (!ipv6_addr_any(addr6)) {
+				return -EINVAL;
+			}
+
+			break;
+		}
+		default:
+			return -EINVAL;
+		}
+	}
+
+	bytes_to_write = min(user_len, (int)sizeof(struct tcp_ao_getsockopt));
+	copied_keys = 0;
+	matched_keys = 0;
+
+	hlist_for_each_entry_rcu(key, &ao_info->head, node) {
+		if (copy_all)
+			goto match;
+
+		if (copy_current || copy_next) {
+			if (copy_current && key == ao_info->current_key)
+				goto match;
+			if (copy_next && key == ao_info->rnext_key)
+				goto match;
+			continue;
+		}
+
+		if (tcp_ao_key_cmp(key, addr, opt_in.prefix,
+				   opt_in.addr.ss_family,
+				   opt_in.sndid, opt_in.rcvid, port) != 0)
+			continue;
+match:
+		matched_keys++;
+		if (copied_keys >= max_keys)
+			continue;
+
+		memset(&opt_out, 0, sizeof(struct tcp_ao_getsockopt));
+
+		if (key->family == AF_INET) {
+			struct sockaddr_in *sin_out = (struct sockaddr_in *)&opt_out.addr;
+
+			sin_out->sin_family = key->family;
+			sin_out->sin_port = ntohs(key->port);
+			memcpy(&sin_out->sin_addr, &key->addr, sizeof(struct in_addr));
+		} else {
+			struct sockaddr_in6 *sin6_out = (struct sockaddr_in6 *)&opt_out.addr;
+
+			sin6_out->sin6_family = key->family;
+			sin6_out->sin6_port = ntohs(key->port);
+			memcpy(&sin6_out->sin6_addr, &key->addr, sizeof(struct in6_addr));
+		}
+		opt_out.sndid = key->sndid;
+		opt_out.rcvid = key->rcvid;
+		opt_out.prefix = key->prefixlen;
+		opt_out.keyflags = key->keyflags;
+		opt_out.flags = 0;
+		if (key == ao_info->current_key)
+			opt_out.flags |= TCP_AO_GET_CURR;
+		if (key == ao_info->rnext_key)
+			opt_out.flags |= TCP_AO_GET_NEXT;
+		opt_out.nkeys = 0;
+		opt_out.maclen = key->maclen;
+		opt_out.keylen = key->keylen;
+		memcpy(&opt_out.key, key->key, key->keylen);
+		crypto_pool_algo(key->crypto_pool_id, opt_out.alg_name, 64);
+
+		/* Copy key to user */
+		if (copy_to_user(optval, &opt_out, bytes_to_write))
+			return -EFAULT;
+		optval += user_len;
+		copied_keys++;
+	}
+
+	optlen_out = (int)sizeof(struct tcp_ao_getsockopt);
+	if (copy_to_user(optlen, &optlen_out, sizeof(int)))
+		return -EFAULT;
+
+	if (copy_to_user(&optval_in->nkeys, &matched_keys, sizeof(u32)))
+		return -EFAULT;
+
+	return 0;
+}
+
+int tcp_ao_get_mkts(struct sock *sk, char __user *optval, int __user *optlen)
+{
+	struct tcp_ao_info *ao_info;
+	u32 state;
+
+	/* Check socket state */
+	state = (1 << sk->sk_state) &
+		(TCPF_CLOSE | TCPF_ESTABLISHED | TCPF_LISTEN);
+	if (!state)
+		return -ESOCKTNOSUPPORT;
+
+	/* Check ao_info */
+	ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info,
+					    lockdep_sock_is_held(sk));
+	if (!ao_info)
+		return -ENOENT;
+
+	return tcp_ao_copy_mkts_to_user(ao_info, optval, optlen);
+}
+
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 24/31] net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs)
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (22 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 23/31] net/tcp: Add getsockopt(TCP_AO_GET) Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-18 16:59 ` [PATCH 25/31] selftests/net: Add TCP-AO library Dmitry Safonov
                   ` (7 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Delete becomes very, very fast - almost free, but after setsockopt()
syscall returns, the key is still alive until next RCU grace period.
Which is fine for listen sockets as userspace needs to be aware of
setsockopt(TCP_AO) and accept() race and resolve it with verification
by getsockopt() after TCP connection was accepted.

The benchmark results (on non-loaded box, worse with more RCU work pending):
> ok 33    Worst case delete    16384 keys: min=5ms max=10ms mean=6.93904ms stddev=0.263421
> ok 34        Add a new key    16384 keys: min=1ms max=4ms mean=2.17751ms stddev=0.147564
> ok 35 Remove random-search    16384 keys: min=5ms max=10ms mean=6.50243ms stddev=0.254999
> ok 36         Remove async    16384 keys: min=0ms max=0ms mean=0.0296107ms stddev=0.0172078

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>
---
 include/uapi/linux/tcp.h |  3 +++
 net/ipv4/tcp_ao.c        | 17 ++++++++++++++++-
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
index 453187d21da8..42850ae6e99d 100644
--- a/include/uapi/linux/tcp.h
+++ b/include/uapi/linux/tcp.h
@@ -353,6 +353,9 @@ struct tcp_diag_md5sig {
 #define TCP_AO_CMDF_CURR	(1 << 0)	/* Only checks field sndid */
 #define TCP_AO_CMDF_NEXT	(1 << 1)	/* Only checks field rcvid */
 #define TCP_AO_CMDF_ACCEPT_ICMP	(1 << 2)	/* Accept incoming ICMPs */
+#define TCP_AO_CMDF_DEL_ASYNC	(1 << 3)	/* Asynchronious delete, valid
+						 * only for listen sockets
+						 */
 
 #define TCP_AO_GET_CURR		TCP_AO_CMDF_CURR
 #define TCP_AO_GET_NEXT		TCP_AO_CMDF_NEXT
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index 5ab16b857c29..8e75432c0cc8 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -1422,7 +1422,7 @@ static bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd,
 #define TCP_AO_CMDF_ADDMOD_VALID					\
 	(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP)
 #define TCP_AO_CMDF_DEL_VALID						\
-	(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT)
+	(TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_DEL_ASYNC)
 #define TCP_AO_GETF_VALID						\
 	(TCP_AO_GET_ALL | TCP_AO_GET_CURR | TCP_AO_GET_NEXT)
 
@@ -1547,11 +1547,26 @@ static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_key *key,
 
 	hlist_del_rcu(&key->node);
 
+	/* Support for async delete on listening sockets: as they don't
+	 * need current_key/rnext_key maintaining, we don't need to check
+	 * them and we can just free all resources in RCU fashion.
+	 */
+	if (cmd->tcpa_flags & TCP_AO_CMDF_DEL_ASYNC) {
+		if (sk->sk_state != TCP_LISTEN)
+			return -EINVAL;
+		atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
+		call_rcu(&key->rcu, tcp_ao_key_free_rcu);
+		return 0;
+	}
+
 	/* At this moment another CPU could have looked this key up
 	 * while it was unlinked from the list. Wait for RCU grace period,
 	 * after which the key is off-list and can't be looked up again;
 	 * the rx path [just before RCU came] might have used it and set it
 	 * as current_key (very unlikely).
+	 * Free the key with next RCU grace period (in case it was
+	 * current_key before tcp_ao_current_rnext() might have
+	 * changed it in forced-delete).
 	 */
 	synchronize_rcu();
 	err = tcp_ao_current_rnext(sk, cmd->tcpa_flags,
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 25/31] selftests/net: Add TCP-AO library
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (23 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 24/31] net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs) Dmitry Safonov
@ 2022-08-18 16:59 ` Dmitry Safonov
  2022-08-23 15:47   ` Shuah Khan
  2022-08-18 17:00 ` [PATCH 26/31] selftests/net: Verify that TCP-AO complies with ignoring ICMPs Dmitry Safonov
                   ` (6 subsequent siblings)
  31 siblings, 1 reply; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 16:59 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Provide functions to create selftests dedicated to TCP-AO.
They can run in parallel, as they use temporary net namespaces.
They can be very specific to the feature being tested.
This will allow to create a lot of TCP-AO tests, without complicating
one binary with many --options and to create scenarios, that are
hard to put in bash script that uses one binary.

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 tools/testing/selftests/Makefile              |   1 +
 tools/testing/selftests/net/tcp_ao/.gitignore |   2 +
 tools/testing/selftests/net/tcp_ao/Makefile   |  45 +++
 tools/testing/selftests/net/tcp_ao/connect.c  |  81 +++++
 .../testing/selftests/net/tcp_ao/lib/aolib.h  | 333 +++++++++++++++++
 .../selftests/net/tcp_ao/lib/netlink.c        | 341 ++++++++++++++++++
 tools/testing/selftests/net/tcp_ao/lib/proc.c | 267 ++++++++++++++
 .../testing/selftests/net/tcp_ao/lib/setup.c  | 297 +++++++++++++++
 tools/testing/selftests/net/tcp_ao/lib/sock.c | 294 +++++++++++++++
 .../testing/selftests/net/tcp_ao/lib/utils.c  |  30 ++
 10 files changed, 1691 insertions(+)
 create mode 100644 tools/testing/selftests/net/tcp_ao/.gitignore
 create mode 100644 tools/testing/selftests/net/tcp_ao/Makefile
 create mode 100644 tools/testing/selftests/net/tcp_ao/connect.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/aolib.h
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/netlink.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/proc.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/setup.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/sock.c
 create mode 100644 tools/testing/selftests/net/tcp_ao/lib/utils.c

diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 10b34bb03bc1..2a3b15a13ccb 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -46,6 +46,7 @@ TARGETS += net
 TARGETS += net/af_unix
 TARGETS += net/forwarding
 TARGETS += net/mptcp
+TARGETS += net/tcp_ao
 TARGETS += netfilter
 TARGETS += nsfs
 TARGETS += pidfd
diff --git a/tools/testing/selftests/net/tcp_ao/.gitignore b/tools/testing/selftests/net/tcp_ao/.gitignore
new file mode 100644
index 000000000000..e8bb81b715b7
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/.gitignore
@@ -0,0 +1,2 @@
+*_ipv4
+*_ipv6
diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile
new file mode 100644
index 000000000000..cb23d67944d7
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/Makefile
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0
+TEST_BOTH_AF := connect
+
+TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4)
+TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6)
+
+TEST_GEN_PROGS := $(TEST_IPV4_PROGS) $(TEST_IPV6_PROGS)
+
+top_srcdir	  := ../../../../..
+KSFT_KHDR_INSTALL := 1
+include ../../lib.mk
+
+HOSTAR ?= ar
+
+# Drop it on port to linux/master with commit 8ce72dc32578
+.DEFAULT_GOAL := all
+
+LIBDIR	:= $(OUTPUT)/lib
+LIB	:= $(LIBDIR)/libaotst.a
+LDLIBS	+= $(LIB) -pthread
+LIBDEPS	:= lib/aolib.h Makefile
+
+CFLAGS	:= -Wall -O2 -g -D_GNU_SOURCE -fno-strict-aliasing
+CFLAGS	+= -I ../../../../../usr/include/ -iquote $(LIBDIR)
+CFLAGS	+= -I ../../../../include/
+
+# Library
+LIBSRC	:= setup.c netlink.c utils.c sock.c proc.c
+LIBOBJ	:= $(LIBSRC:%.c=$(LIBDIR)/%.o)
+EXTRA_CLEAN += $(LIBOBJ) $(LIB)
+
+$(LIB): $(LIBOBJ)
+	$(HOSTAR) rcs $@ $^
+
+$(LIBDIR)/%.o: ./lib/%.c $(LIBDEPS)
+	$(CC) $< $(CFLAGS) $(CPPFLAGS) -o $@ -c
+
+$(TEST_GEN_PROGS): $(LIB)
+
+$(OUTPUT)/%_ipv4: %.c
+	$(LINK.c) $^ $(LDLIBS) -o $@
+
+$(OUTPUT)/%_ipv6: %.c
+	$(LINK.c) -DIPV6_TEST $^ $(LDLIBS) -o $@
+
diff --git a/tools/testing/selftests/net/tcp_ao/connect.c b/tools/testing/selftests/net/tcp_ao/connect.c
new file mode 100644
index 000000000000..02aa50f0266c
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/connect.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dmitry Safonov <dima@arista.com> */
+#include <inttypes.h>
+#include "aolib.h"
+
+static void *server_fn(void *arg)
+{
+	int err, sk, lsk;
+	ssize_t bytes;
+
+	lsk = test_listen_socket(this_ip_addr, test_server_port, 1);
+
+	if (test_set_ao(lsk, "password", 0, this_ip_dest, -1, 100, 100))
+		test_error("setsockopt(TCP_AO)");
+	synchronize_threads();
+
+	err = test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0);
+	if (!err)
+		test_error("timeouted for accept()");
+	else if (err < 0)
+		test_error("test_wait_fd()");
+
+	sk = accept(lsk, NULL, NULL);
+	if (sk < 0)
+		test_error("accept()");
+
+	synchronize_threads();
+
+	bytes = test_server_run(sk, 0, 0);
+
+	test_fail("server served: %zd", bytes);
+	return NULL;
+}
+
+static void *client_fn(void *arg)
+{
+	int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
+	uint64_t before_aogood, after_aogood;
+	const size_t nr_packets = 20;
+	struct netstat *ns_before, *ns_after;
+
+	if (sk < 0)
+		test_error("socket()");
+
+	if (test_set_ao(sk, "password", 0, this_ip_dest, -1, 100, 100))
+		test_error("setsockopt(TCP_AO)");
+
+	synchronize_threads();
+	if (test_connect_socket(sk, this_ip_dest, test_server_port) <= 0)
+		test_error("failed to connect()");
+	synchronize_threads();
+
+	ns_before = netstat_read();
+	before_aogood = netstat_get(ns_before, "TCPAOGood", NULL);
+	if (test_client_verify(sk, 100, nr_packets, TEST_TIMEOUT_SEC)) {
+		test_fail("verify failed");
+		return NULL;
+	}
+
+	ns_after = netstat_read();
+	after_aogood = netstat_get(ns_after, "TCPAOGood", NULL);
+	netstat_print_diff(ns_before, ns_after);
+	netstat_free(ns_before);
+	netstat_free(ns_after);
+
+	if (nr_packets > (after_aogood - before_aogood)) {
+		test_fail("TCPAOGood counter mismatch: %zu > (%zu - %zu)",
+				nr_packets, after_aogood, before_aogood);
+		return NULL;
+	}
+
+	test_ok("connect TCPAOGood %" PRIu64 " => %" PRIu64 ", sent %" PRIu64,
+			before_aogood, after_aogood, nr_packets);
+	return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+	test_init(1, server_fn, client_fn);
+	return 0;
+}
diff --git a/tools/testing/selftests/net/tcp_ao/lib/aolib.h b/tools/testing/selftests/net/tcp_ao/lib/aolib.h
new file mode 100644
index 000000000000..d5810fd04816
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/lib/aolib.h
@@ -0,0 +1,333 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * TCP-AO selftest library. Provides helpers to unshare network
+ * namespaces, create veth, assign ip addresses, set routes,
+ * manipulate socket options, read network counter and etc.
+ * Author: Dmitry Safonov <dima@arista.com>
+ */
+#ifndef _AOLIB_H_
+#define _AOLIB_H_
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <linux/snmp.h>
+#include <linux/tcp.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "../../../../../include/linux/stringify.h"
+
+/* Working around ksft, see the comment in lib/setup.c */
+extern void __test_msg(const char *buf);
+extern void __test_ok(const char *buf);
+extern void __test_fail(const char *buf);
+extern void __test_error(const char *buf);
+
+__attribute__((__format__(__printf__, 2, 3)))
+static inline void __test_print(void (*fn)(const char *), const char *fmt, ...)
+{
+#define TEST_MSG_BUFFER_SIZE 4096
+	char buf[TEST_MSG_BUFFER_SIZE];
+	va_list arg;
+
+	va_start(arg, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, arg);
+	va_end(arg);
+	fn(buf);
+}
+
+#define test_print(fmt, ...)						\
+	__test_print(__test_msg, "%ld[%s:%u] " fmt "\n",		\
+		     syscall(SYS_gettid),				\
+		     __FILE__, __LINE__, ##__VA_ARGS__)
+
+#define test_ok(fmt, ...)						\
+	__test_print(__test_ok, fmt "\n", ##__VA_ARGS__)
+
+#define test_fail(fmt, ...)						\
+do {									\
+	if (errno)							\
+		__test_print(__test_fail, fmt ": %m\n", ##__VA_ARGS__);	\
+	else								\
+		__test_print(__test_fail, fmt "\n", ##__VA_ARGS__);	\
+	test_failed();							\
+} while(0)
+
+#define KSFT_FAIL  1
+#define test_error(fmt, ...)						\
+do {									\
+	if (errno)							\
+		__test_print(__test_error, "%ld[%s:%u] " fmt ": %m\n",	\
+			     syscall(SYS_gettid), __FILE__, __LINE__,	\
+			     ##__VA_ARGS__);				\
+	else								\
+		__test_print(__test_error, "%ld[%s:%u] " fmt "\n",	\
+			     syscall(SYS_gettid), __FILE__, __LINE__,	\
+			     ##__VA_ARGS__);				\
+	exit(KSFT_FAIL);						\
+} while(0)
+
+union tcp_addr {
+	struct in_addr a4;
+	struct in6_addr a6;
+};
+
+typedef void *(*thread_fn)(void*);
+extern void test_failed(void);
+extern void __test_init(unsigned int ntests, int family, unsigned prefix,
+			union tcp_addr addr1, union tcp_addr addr2,
+			thread_fn peer1, thread_fn peer2);
+
+static inline void test_init2(unsigned int ntests,
+			      thread_fn peer1, thread_fn peer2,
+			      int family, unsigned prefix,
+			      const char *addr1, const char *addr2)
+{
+	union tcp_addr taddr1, taddr2;
+
+	if (inet_pton(family, addr1, &taddr1) != 1)
+		test_error("Can't convert ip address %s", addr1);
+	if (inet_pton(family, addr2, &taddr2) != 1)
+		test_error("Can't convert ip address %s", addr2);
+
+	__test_init(ntests, family, prefix, taddr1, taddr2, peer1, peer2);
+}
+extern void test_add_destructor(void (*d)(void));
+extern void test_set_optmem(size_t value);
+
+extern const struct sockaddr_in6 addr_any6;
+extern const struct sockaddr_in addr_any4;
+
+#ifdef IPV6_TEST
+# define __TEST_CLIENT_IP(n)	("2001:db8:" __stringify(n) "::1")
+# define TEST_CLIENT_IP	__TEST_CLIENT_IP(1)
+# define TEST_WRONG_IP	"2001:db8:253::1"
+# define TEST_SERVER_IP	"2001:db8:254::1"
+# define TEST_NETWORK	"2001::"
+# define TEST_PREFIX	128
+# define TEST_FAMILY	AF_INET6
+# define SOCKADDR_ANY	addr_any6
+#else
+# define __TEST_CLIENT_IP(n)	("10.0." __stringify(n) ".1")
+# define TEST_CLIENT_IP	__TEST_CLIENT_IP(1)
+# define TEST_WRONG_IP	"10.0.253.1"
+# define TEST_SERVER_IP	"10.0.254.1"
+# define TEST_NETWORK	"10.0.0.0"
+# define TEST_PREFIX	32
+# define TEST_FAMILY	AF_INET
+# define SOCKADDR_ANY	addr_any4
+#endif
+
+static inline void test_init(unsigned int ntests,
+			     thread_fn peer1, thread_fn peer2)
+{
+	test_init2(ntests, peer1, peer2, TEST_FAMILY, TEST_PREFIX,
+			TEST_SERVER_IP, TEST_CLIENT_IP);
+}
+extern void synchronize_threads(void);
+extern void switch_ns(int fd);
+
+extern __thread union tcp_addr this_ip_addr;
+extern __thread union tcp_addr this_ip_dest;
+extern int test_family;
+
+extern void randomize_buffer(void *buf, size_t buflen);
+extern const char veth_name[];
+extern int add_veth(const char *name, int nsfda, int nsfdb);
+extern int ip_addr_add(const char *intf, int family,
+		       union tcp_addr addr, uint8_t prefix);
+extern int ip_route_add(const char *intf, int family,
+			union tcp_addr src, union tcp_addr dst);
+extern int link_set_up(const char *intf);
+
+extern const unsigned test_server_port;
+extern int test_wait_fd(int sk, time_t sec, bool write);
+extern int __test_connect_socket(int sk, void *addr, size_t addr_sz,
+				 time_t timeout);
+extern int __test_listen_socket(int backlog, void *addr, size_t addr_sz);
+
+static inline int test_listen_socket(const union tcp_addr taddr, unsigned port,
+				     int backlog)
+{
+#ifdef IPV6_TEST
+	struct sockaddr_in6 addr = {
+		.sin6_family	= AF_INET6,
+		.sin6_port	= htons(port),
+		.sin6_addr	= taddr.a6,
+	};
+#else
+	struct sockaddr_in addr = {
+		.sin_family	= AF_INET,
+		.sin_port	= htons(port),
+		.sin_addr	= taddr.a4,
+	};
+#endif
+	return __test_listen_socket(backlog, (void *)&addr, sizeof(addr));
+}
+
+#ifndef DEFAULT_TEST_ALGO
+#define DEFAULT_TEST_ALGO	"cmac(aes128)"
+#endif
+
+#ifdef IPV6_TEST
+#define DEFAULT_TEST_PREFIX	128
+#else
+#define DEFAULT_TEST_PREFIX	32
+#endif
+
+/*
+ * Timeout on syscalls where failure is not expected.
+ * You may want to rise it if the test machine is very busy.
+ */
+#ifndef TEST_TIMEOUT_SEC
+#define TEST_TIMEOUT_SEC	5
+#endif
+
+/*
+ * Timeout on connect() where a failure is expected.
+ * If set to 0 - kernel will try to retransmit SYN number of times, set in
+ * /proc/sys/net/ipv4/tcp_syn_retries
+ * By default set to 1 to make tests pass faster on non-busy machine.
+ */
+#ifndef TEST_RETRANSMIT_SEC
+#define TEST_RETRANSMIT_SEC	1
+#endif
+
+
+static inline int _test_connect_socket(int sk, const union tcp_addr taddr,
+					unsigned port, time_t timeout)
+{
+#ifdef IPV6_TEST
+	struct sockaddr_in6 addr = {
+		.sin6_family	= AF_INET6,
+		.sin6_port	= htons(port),
+		.sin6_addr	= taddr.a6,
+	};
+#else
+	struct sockaddr_in addr = {
+		.sin_family	= AF_INET,
+		.sin_port	= htons(port),
+		.sin_addr	= taddr.a4,
+	};
+#endif
+	return __test_connect_socket(sk, (void *)&addr, sizeof(addr), timeout);
+}
+
+static inline int test_connect_socket(int sk,
+		const union tcp_addr taddr, unsigned port)
+{
+	return _test_connect_socket(sk, taddr, port, TEST_TIMEOUT_SEC);
+}
+
+extern int test_prepare_ao_sockaddr(struct tcp_ao *ao,
+		const char *alg, uint16_t flags,
+		void *addr, size_t addr_sz, uint8_t prefix,
+		uint8_t sndid, uint8_t rcvid, uint8_t maclen,
+		uint8_t keyflags, uint8_t keylen, const char *key);
+
+static inline int test_prepare_ao(struct tcp_ao *ao,
+		const char *alg, uint16_t flags,
+		union tcp_addr in_addr, uint8_t prefix,
+		uint8_t sndid, uint8_t rcvid, uint8_t maclen,
+		uint8_t keyflags, uint8_t keylen, const char *key)
+{
+#ifdef IPV6_TEST
+	struct sockaddr_in6 addr = {
+		.sin6_family	= AF_INET6,
+		.sin6_port	= 0,
+		.sin6_addr	= in_addr.a6,
+	};
+#else
+	struct sockaddr_in addr = {
+		.sin_family	= AF_INET,
+		.sin_port	= 0,
+		.sin_addr	= in_addr.a4,
+	};
+#endif
+
+	return test_prepare_ao_sockaddr(ao, alg, flags,
+			(void *)&addr, sizeof(addr), prefix, sndid, rcvid,
+			maclen, keyflags, keylen, key);
+}
+
+static inline int test_prepare_def_ao(struct tcp_ao *ao,
+		const char *key, uint16_t flags,
+		union tcp_addr in_addr, uint8_t prefix,
+		uint8_t sndid, uint8_t rcvid)
+{
+	if (prefix > DEFAULT_TEST_PREFIX)
+		prefix = DEFAULT_TEST_PREFIX;
+
+	return test_prepare_ao(ao, DEFAULT_TEST_ALGO, flags, in_addr,
+			prefix, sndid, rcvid, 0, 0, strlen(key), key);
+}
+
+extern int test_get_one_ao(int sk, struct tcp_ao_getsockopt *out,
+			   uint16_t flags, void *addr, size_t addr_sz,
+			   uint8_t prefix, uint8_t sndid, uint8_t rcvid);
+extern int test_cmp_getsockopt_setsockopt(const struct tcp_ao *a,
+					  const struct tcp_ao_getsockopt *b);
+
+static inline int test_verify_socket_ao(int sk, struct tcp_ao *ao)
+{
+	struct tcp_ao_getsockopt tmp;
+	int err;
+
+	err = test_get_one_ao(sk, &tmp, 0, &ao->tcpa_addr,
+			sizeof(ao->tcpa_addr), ao->tcpa_prefix,
+			ao->tcpa_sndid, ao->tcpa_rcvid);
+	if (err)
+		return err;
+
+	return test_cmp_getsockopt_setsockopt(ao, &tmp);
+}
+
+static inline int test_set_ao(int sk, const char *key, uint16_t flags,
+			      union tcp_addr in_addr, uint8_t prefix,
+			      uint8_t sndid, uint8_t rcvid)
+{
+	struct tcp_ao tmp;
+	int err;
+
+	err = test_prepare_def_ao(&tmp, key, flags, in_addr,
+			prefix, sndid, rcvid);
+	if (err)
+		return err;
+
+	if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &tmp, sizeof(tmp)) < 0)
+		return -errno;
+
+	return test_verify_socket_ao(sk, &tmp);
+}
+
+extern ssize_t test_server_run(int sk, ssize_t quota, time_t timeout_sec);
+extern ssize_t test_client_loop(int sk, char *buf, size_t buf_sz,
+				const size_t msg_len, time_t timeout_sec);
+extern int test_client_verify(int sk, const size_t msg_len, const size_t nr,
+			      time_t timeout_sec);
+
+struct netstat;
+extern struct netstat *netstat_read(void);
+extern void netstat_free(struct netstat *ns);
+extern void netstat_print_diff(struct netstat *nsa, struct netstat *nsb);
+extern uint64_t netstat_get(struct netstat *ns,
+			    const char *name, bool *not_found);
+
+static inline uint64_t netstat_get_one(const char *name, bool *not_found)
+{
+	struct netstat *ns = netstat_read();
+	uint64_t ret;
+
+	ret = netstat_get(ns, name, not_found);
+
+	netstat_free(ns);
+	return ret;
+}
+
+#endif /* _AOLIB_H_ */
diff --git a/tools/testing/selftests/net/tcp_ao/lib/netlink.c b/tools/testing/selftests/net/tcp_ao/lib/netlink.c
new file mode 100644
index 000000000000..f04757c921d0
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/lib/netlink.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Original from tools/testing/selftests/net/ipsec.c */
+#include <linux/netlink.h>
+#include <linux/random.h>
+#include <linux/rtnetlink.h>
+#include <linux/veth.h>
+#include <net/if.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "aolib.h"
+
+#define MAX_PAYLOAD		2048
+
+static int netlink_sock(int *sock, uint32_t *seq_nr, int proto)
+{
+	if (*sock > 0) {
+		seq_nr++;
+		return 0;
+	}
+
+	*sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, proto);
+	if (*sock <= 0) {
+		test_print("socket(AF_NETLINK)");
+		return -1;
+	}
+
+	randomize_buffer(seq_nr, sizeof(*seq_nr));
+
+	return 0;
+}
+
+static int netlink_check_answer(int sock, bool quite)
+{
+	struct nlmsgerror {
+		struct nlmsghdr hdr;
+		int error;
+		struct nlmsghdr orig_msg;
+	} answer;
+
+	if (recv(sock, &answer, sizeof(answer), 0) < 0) {
+		test_print("recv()");
+		return -1;
+	} else if (answer.hdr.nlmsg_type != NLMSG_ERROR) {
+		test_print("expected NLMSG_ERROR, got %d",
+			   (int)answer.hdr.nlmsg_type);
+		return -1;
+	} else if (answer.error) {
+		if (!quite) {
+			test_print("NLMSG_ERROR: %d: %s",
+				answer.error, strerror(-answer.error));
+		}
+		return answer.error;
+	}
+
+	return 0;
+}
+
+static inline struct rtattr *rtattr_hdr(struct nlmsghdr *nh)
+{
+	return (struct rtattr *)((char *)(nh) + RTA_ALIGN((nh)->nlmsg_len));
+}
+
+static int rtattr_pack(struct nlmsghdr *nh, size_t req_sz,
+		unsigned short rta_type, const void *payload, size_t size)
+{
+	/* NLMSG_ALIGNTO == RTA_ALIGNTO, nlmsg_len already aligned */
+	struct rtattr *attr = rtattr_hdr(nh);
+	size_t nl_size = RTA_ALIGN(nh->nlmsg_len) + RTA_LENGTH(size);
+
+	if (req_sz < nl_size) {
+		test_print("req buf is too small: %zu < %zu", req_sz, nl_size);
+		return -1;
+	}
+	nh->nlmsg_len = nl_size;
+
+	attr->rta_len = RTA_LENGTH(size);
+	attr->rta_type = rta_type;
+	memcpy(RTA_DATA(attr), payload, size);
+
+	return 0;
+}
+
+static struct rtattr *_rtattr_begin(struct nlmsghdr *nh, size_t req_sz,
+		unsigned short rta_type, const void *payload, size_t size)
+{
+	struct rtattr *ret = rtattr_hdr(nh);
+
+	if (rtattr_pack(nh, req_sz, rta_type, payload, size))
+		return 0;
+
+	return ret;
+}
+
+static inline struct rtattr *rtattr_begin(struct nlmsghdr *nh, size_t req_sz,
+		unsigned short rta_type)
+{
+	return _rtattr_begin(nh, req_sz, rta_type, 0, 0);
+}
+
+static inline void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr)
+{
+	char *nlmsg_end = (char *)nh + nh->nlmsg_len;
+
+	attr->rta_len = nlmsg_end - (char *)attr;
+}
+
+static int veth_pack_peerb(struct nlmsghdr *nh, size_t req_sz,
+		const char *peer, int ns)
+{
+	struct ifinfomsg pi;
+	struct rtattr *peer_attr;
+
+	memset(&pi, 0, sizeof(pi));
+	pi.ifi_family	= AF_UNSPEC;
+	pi.ifi_change	= 0xFFFFFFFF;
+
+	peer_attr = _rtattr_begin(nh, req_sz, VETH_INFO_PEER, &pi, sizeof(pi));
+	if (!peer_attr)
+		return -1;
+
+	if (rtattr_pack(nh, req_sz, IFLA_IFNAME, peer, strlen(peer)))
+		return -1;
+
+	if (rtattr_pack(nh, req_sz, IFLA_NET_NS_FD, &ns, sizeof(ns)))
+		return -1;
+
+	rtattr_end(nh, peer_attr);
+
+	return 0;
+}
+
+static int __add_veth(int sock, uint32_t seq, const char *name,
+		      int ns_a, int ns_b)
+{
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE;
+	struct {
+		struct nlmsghdr		nh;
+		struct ifinfomsg	info;
+		char			attrbuf[MAX_PAYLOAD];
+	} req;
+	const char veth_type[] = "veth";
+	struct rtattr *link_info, *info_data;
+
+	memset(&req, 0, sizeof(req));
+	req.nh.nlmsg_len	= NLMSG_LENGTH(sizeof(req.info));
+	req.nh.nlmsg_type	= RTM_NEWLINK;
+	req.nh.nlmsg_flags	= flags;
+	req.nh.nlmsg_seq	= seq;
+	req.info.ifi_family	= AF_UNSPEC;
+	req.info.ifi_change	= 0xFFFFFFFF;
+
+	if (rtattr_pack(&req.nh, sizeof(req), IFLA_IFNAME, name, strlen(name)))
+		return -1;
+
+	if (rtattr_pack(&req.nh, sizeof(req), IFLA_NET_NS_FD, &ns_a, sizeof(ns_a)))
+		return -1;
+
+	link_info = rtattr_begin(&req.nh, sizeof(req), IFLA_LINKINFO);
+	if (!link_info)
+		return -1;
+
+	if (rtattr_pack(&req.nh, sizeof(req), IFLA_INFO_KIND, veth_type, sizeof(veth_type)))
+		return -1;
+
+	info_data = rtattr_begin(&req.nh, sizeof(req), IFLA_INFO_DATA);
+	if (!info_data)
+		return -1;
+
+	if (veth_pack_peerb(&req.nh, sizeof(req), name, ns_b))
+		return -1;
+
+	rtattr_end(&req.nh, info_data);
+	rtattr_end(&req.nh, link_info);
+
+	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
+		test_print("send()");
+		return -1;
+	}
+	return netlink_check_answer(sock, false);
+}
+
+int add_veth(const char *name, int nsfda, int nsfdb)
+{
+	int route_sock = -1, ret;
+	uint32_t route_seq;
+
+	if (netlink_sock(&route_sock, &route_seq, NETLINK_ROUTE))
+		test_error("Failed to open netlink route socket\n");
+
+	ret = __add_veth(route_sock, route_seq++, name, nsfda, nsfdb);
+	close(route_sock);
+	return ret;
+}
+
+static int __ip_addr_add(int sock, uint32_t seq, const char *intf,
+			 int family, union tcp_addr addr, uint8_t prefix)
+{
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE;
+	struct {
+		struct nlmsghdr		nh;
+		struct ifaddrmsg	info;
+		char			attrbuf[MAX_PAYLOAD];
+	} req;
+	size_t addr_len = (family == AF_INET) ? sizeof(struct in_addr) :
+						sizeof(struct in6_addr);
+
+	memset(&req, 0, sizeof(req));
+	req.nh.nlmsg_len	= NLMSG_LENGTH(sizeof(req.info));
+	req.nh.nlmsg_type	= RTM_NEWADDR;
+	req.nh.nlmsg_flags	= flags;
+	req.nh.nlmsg_seq	= seq;
+	req.info.ifa_family	= family;
+	req.info.ifa_prefixlen	= prefix;
+	req.info.ifa_index	= if_nametoindex(intf);
+	req.info.ifa_flags	= IFA_F_NODAD;
+
+	if (rtattr_pack(&req.nh, sizeof(req), IFA_LOCAL, &addr, addr_len))
+		return -1;
+
+	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
+		test_print("send()");
+		return -1;
+	}
+	return netlink_check_answer(sock, true);
+}
+
+int ip_addr_add(const char *intf, int family,
+		union tcp_addr addr, uint8_t prefix)
+{
+	int route_sock = -1, ret;
+	uint32_t route_seq;
+
+	if (netlink_sock(&route_sock, &route_seq, NETLINK_ROUTE))
+		test_error("Failed to open netlink route socket\n");
+
+	ret = __ip_addr_add(route_sock, route_seq++, intf,
+			    family, addr, prefix);
+
+	close(route_sock);
+	return ret;
+}
+
+static int __ip_route_add(int sock, uint32_t seq, const char *intf, int family,
+			  union tcp_addr src, union tcp_addr dst)
+{
+	struct {
+		struct nlmsghdr	nh;
+		struct rtmsg	rt;
+		char		attrbuf[MAX_PAYLOAD];
+	} req;
+	unsigned int index = if_nametoindex(intf);
+	size_t addr_len = (family == AF_INET) ? sizeof(struct in_addr) :
+						sizeof(struct in6_addr);
+
+	memset(&req, 0, sizeof(req));
+	req.nh.nlmsg_len	= NLMSG_LENGTH(sizeof(req.rt));
+	req.nh.nlmsg_type	= RTM_NEWROUTE;
+	req.nh.nlmsg_flags	= NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE;
+	req.nh.nlmsg_seq	= seq;
+	req.rt.rtm_family	= family;
+	req.rt.rtm_dst_len	= (family == AF_INET) ? 32 : 128;
+	req.rt.rtm_table	= RT_TABLE_MAIN;
+	req.rt.rtm_protocol	= RTPROT_BOOT;
+	req.rt.rtm_scope	= RT_SCOPE_UNIVERSE;
+	req.rt.rtm_type		= RTN_UNICAST;
+
+	if (rtattr_pack(&req.nh, sizeof(req), RTA_DST, &dst, addr_len))
+		return -1;
+
+	if (rtattr_pack(&req.nh, sizeof(req), RTA_PREFSRC, &src, addr_len))
+		return -1;
+
+	if (rtattr_pack(&req.nh, sizeof(req), RTA_OIF, &index, sizeof(index)))
+		return -1;
+
+	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
+		test_print("send()");
+		return -1;
+	}
+
+	return netlink_check_answer(sock, true);
+}
+
+int ip_route_add(const char *intf, int family,
+		 union tcp_addr src, union tcp_addr dst)
+{
+	int route_sock = -1, ret;
+	uint32_t route_seq;
+
+	if (netlink_sock(&route_sock, &route_seq, NETLINK_ROUTE))
+		test_error("Failed to open netlink route socket\n");
+
+	ret = __ip_route_add(route_sock, route_seq++, intf, family, src, dst);
+	if (ret == -EEXIST) /* ignoring */
+		ret = 0;
+
+	close(route_sock);
+	return ret;
+}
+
+static int __link_set_up(int sock, uint32_t seq, const char *intf)
+{
+	struct {
+		struct nlmsghdr		nh;
+		struct ifinfomsg	info;
+		char			attrbuf[MAX_PAYLOAD];
+	} req;
+
+	memset(&req, 0, sizeof(req));
+	req.nh.nlmsg_len	= NLMSG_LENGTH(sizeof(req.info));
+	req.nh.nlmsg_type	= RTM_NEWLINK;
+	req.nh.nlmsg_flags	= NLM_F_REQUEST | NLM_F_ACK;
+	req.nh.nlmsg_seq	= seq;
+	req.info.ifi_family	= AF_UNSPEC;
+	req.info.ifi_change	= 0xFFFFFFFF;
+	req.info.ifi_index	= if_nametoindex(intf);
+	req.info.ifi_flags	= IFF_UP;
+	req.info.ifi_change	= IFF_UP;
+
+	if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
+		test_print("send()");
+		return -1;
+	}
+	return netlink_check_answer(sock, false);
+}
+
+int link_set_up(const char *intf)
+{
+	int route_sock = -1, ret;
+	uint32_t route_seq;
+
+	if (netlink_sock(&route_sock, &route_seq, NETLINK_ROUTE))
+		test_error("Failed to open netlink route socket\n");
+
+	ret = __link_set_up(route_sock, route_seq++, intf);
+
+	close(route_sock);
+	return ret;
+}
diff --git a/tools/testing/selftests/net/tcp_ao/lib/proc.c b/tools/testing/selftests/net/tcp_ao/lib/proc.c
new file mode 100644
index 000000000000..815bb7b5975a
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/lib/proc.c
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include "../../../../../include/linux/compiler.h"
+#include "../../../../../include/linux/kernel.h"
+#include "aolib.h"
+
+struct netstat_counter {
+	uint64_t val;
+	char *name;
+};
+
+struct netstat {
+	char *header_name;
+	struct netstat *next;
+	size_t counters_nr;
+	struct netstat_counter *counters;
+};
+
+static struct netstat *lookup_type(struct netstat *ns,
+		const char *type, size_t len)
+{
+	while (ns != NULL) {
+		size_t cmp = max(len, strlen(ns->header_name));
+
+		if (!strncmp(ns->header_name, type, cmp))
+			return ns;
+		ns = ns->next;
+	}
+	return NULL;
+}
+
+static struct netstat *lookup_get(struct netstat *ns,
+				  const char *type, const size_t len)
+{
+	struct netstat *ret;
+
+	ret = lookup_type(ns, type, len);
+	if (ret != NULL)
+		return ret;
+
+	ret = malloc(sizeof(struct netstat));
+	if (!ret)
+		test_error("malloc()");
+
+	ret->header_name = strndup(type, len);
+	if (ret->header_name == NULL)
+		test_error("strndup()");
+	ret->next = ns;
+	ret->counters_nr = 0;
+	ret->counters = NULL;
+
+	return ret;
+}
+
+static struct netstat *lookup_get_column(struct netstat *ns, const char *line)
+{
+	char *column;
+
+	column = strchr(line, ':');
+	if (!column)
+		test_error("can't parse netstat file");
+
+	return lookup_get(ns, line, column - line);
+}
+
+static void netstat_read_type(FILE *fnetstat, struct netstat **dest, char *line)
+{
+	struct netstat *type = lookup_get_column(*dest, line);
+	const char *pos = line;
+	size_t i, nr_elems = 0;
+	char tmp;
+
+	while ((pos = strchr(pos, ' '))) {
+		nr_elems++;
+		pos++;
+	}
+
+	*dest = type;
+	type->counters = reallocarray(type->counters,
+				type->counters_nr + nr_elems,
+				sizeof(struct netstat_counter));
+	if (!type->counters)
+		test_error("reallocarray()");
+
+	pos = strchr(line, ' ') + 1;
+
+	if (fscanf(fnetstat, type->header_name) == EOF)
+		test_error("fscanf(%s)", type->header_name);
+	if (fread(&tmp, 1, 1, fnetstat) != 1 || tmp != ':')
+		test_error("Unexpected netstat format (%c)", tmp);
+
+	for (i = type->counters_nr; i < type->counters_nr + nr_elems; i++) {
+		struct netstat_counter *nc = &type->counters[i];
+		const char *new_pos = strchr(pos, ' ');
+		const char *fmt = " %" PRIu64;
+
+		if (new_pos == NULL)
+			new_pos = strchr(pos, '\n');
+
+		nc->name = strndup(pos, new_pos - pos);
+		if (nc->name == NULL)
+			test_error("strndup()");
+
+		if (unlikely(!strcmp(nc->name, "MaxConn")))
+			fmt = " %" PRId64; /* MaxConn is signed, RFC 2012 */
+		if (fscanf(fnetstat, fmt, &nc->val) != 1)
+			test_error("fscanf(%s)", nc->name);
+		pos = new_pos + 1;
+	}
+	type->counters_nr += nr_elems;
+
+	if (fread(&tmp, 1, 1, fnetstat) != 1 || tmp != '\n')
+		test_error("Unexpected netstat format");
+}
+
+static const char *snmp6_name = "Snmp6";
+static void snmp6_read(FILE *fnetstat, struct netstat **dest)
+{
+	struct netstat *type = lookup_get(*dest, snmp6_name, strlen(snmp6_name));
+	char *counter_name;
+	size_t i;
+
+	for (i = type->counters_nr;; i++) {
+		struct netstat_counter *nc;
+		uint64_t counter;
+
+		if (fscanf(fnetstat, "%ms", &counter_name) == EOF)
+			break;
+		if (fscanf(fnetstat, "%" PRIu64, &counter) == EOF)
+			test_error("Unexpected snmp6 format");
+		type->counters = reallocarray(type->counters, i + 1,
+					sizeof(struct netstat_counter));
+		if (!type->counters)
+			test_error("reallocarray()");
+		nc = &type->counters[i];
+		nc->name = counter_name;
+		nc->val = counter;
+	}
+	type->counters_nr = i;
+	*dest = type;
+}
+
+struct netstat *netstat_read(void)
+{
+	struct netstat *ret = 0;
+	size_t line_sz = 0;
+	char *line = NULL;
+	FILE *fnetstat;
+
+	errno = 0;
+	fnetstat = fopen("/proc/net/netstat", "r");
+	if (fnetstat == NULL)
+		test_error("failed to open /proc/net/netstat");
+
+	while (getline(&line, &line_sz, fnetstat) != -1)
+		netstat_read_type(fnetstat, &ret, line);
+	fclose(fnetstat);
+
+	errno = 0;
+	fnetstat = fopen("/proc/net/snmp", "r");
+	if (fnetstat == NULL)
+		test_error("failed to open /proc/net/snmp");
+
+	while (getline(&line, &line_sz, fnetstat) != -1)
+		netstat_read_type(fnetstat, &ret, line);
+	fclose(fnetstat);
+
+	errno = 0;
+	fnetstat = fopen("/proc/net/snmp6", "r");
+	if (fnetstat == NULL)
+		test_error("failed to open /proc/net/snmp6");
+
+	snmp6_read(fnetstat, &ret);
+	fclose(fnetstat);
+
+	free(line);
+	return ret;
+}
+
+void netstat_free(struct netstat *ns)
+{
+	while (ns != NULL) {
+		struct netstat *prev = ns;
+		size_t i;
+
+		free(ns->header_name);
+		for (i = 0; i < ns->counters_nr; i++)
+			free(ns->counters[i].name);
+		free(ns->counters);
+		ns = ns->next;
+		free(prev);
+	}
+}
+
+static void inline
+__netstat_print_diff(uint64_t a, struct netstat *nsb, size_t i)
+{
+	if (unlikely(!strcmp(nsb->header_name, "MaxConn"))) {
+		test_print("%8s %25s: %" PRId64 " => %" PRId64,
+				nsb->header_name, nsb->counters[i].name,
+				a, nsb->counters[i].val);
+		return;
+	}
+
+	test_print("%8s %25s: %" PRIu64 " => %" PRIu64, nsb->header_name,
+			nsb->counters[i].name, a, nsb->counters[i].val);
+}
+
+void netstat_print_diff(struct netstat *nsa, struct netstat *nsb)
+{
+	size_t i, j;
+
+	while (nsb != NULL) {
+		if (unlikely(strcmp(nsb->header_name, nsa->header_name))) {
+			for (i = 0; i < nsb->counters_nr; i++)
+				__netstat_print_diff(0, nsb, i);
+			nsb = nsb->next;
+			continue;
+		}
+
+		if (nsb->counters_nr < nsa->counters_nr)
+			test_error("Unexpected: some counters dissapeared!");
+
+		for (j = 0, i = 0; i < nsb->counters_nr; i++) {
+			if (strcmp(nsb->counters[i].name, nsa->counters[j].name)) {
+				__netstat_print_diff(0, nsb, i);
+				continue;
+			}
+
+			if (nsa->counters[j].val == nsb->counters[i].val) {
+				j++;
+				continue;
+			}
+
+			__netstat_print_diff(nsa->counters[j].val, nsb, i);
+			j++;
+		}
+		if (j != nsa->counters_nr)
+			test_error("Unexpected: some counters dissapeared!");
+
+		nsb = nsb->next;
+		nsa = nsa->next;
+	}
+}
+
+uint64_t netstat_get(struct netstat *ns, const char *name, bool *not_found)
+{
+	if (not_found)
+		*not_found = false;
+
+	while (ns != NULL) {
+		size_t i;
+
+		for (i = 0; i < ns->counters_nr; i++) {
+			if (!strcmp(name, ns->counters[i].name))
+				return ns->counters[i].val;
+		}
+
+		ns = ns->next;
+	}
+
+	if (not_found)
+		*not_found = true;
+	return 0;
+}
diff --git a/tools/testing/selftests/net/tcp_ao/lib/setup.c b/tools/testing/selftests/net/tcp_ao/lib/setup.c
new file mode 100644
index 000000000000..b47672a2a5c0
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/lib/setup.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include "aolib.h"
+
+/*
+ * Can't be included in the header: it defines static variables which
+ * will be unique to every object. Let's include it only once here.
+ */
+#include "../../../kselftest.h"
+
+/* Prevent overriding of one thread's output by another */
+static pthread_mutex_t ksft_print_lock = PTHREAD_MUTEX_INITIALIZER;
+
+void __test_msg(const char *buf)
+{
+	pthread_mutex_lock(&ksft_print_lock);
+	ksft_print_msg(buf);
+	pthread_mutex_unlock(&ksft_print_lock);
+}
+void __test_ok(const char *buf)
+{
+	pthread_mutex_lock(&ksft_print_lock);
+	ksft_test_result_pass(buf);
+	pthread_mutex_unlock(&ksft_print_lock);
+}
+void __test_fail(const char *buf)
+{
+	pthread_mutex_lock(&ksft_print_lock);
+	ksft_test_result_fail(buf);
+	pthread_mutex_unlock(&ksft_print_lock);
+}
+
+void __test_error(const char *buf)
+{
+	pthread_mutex_lock(&ksft_print_lock);
+	ksft_test_result_error(buf);
+	pthread_mutex_unlock(&ksft_print_lock);
+}
+
+static volatile int failed;
+
+void test_failed(void)
+{
+	failed = 1;
+}
+
+static void test_exit(void)
+{
+	if (failed)
+		ksft_exit_fail();
+	else
+		ksft_exit_pass();
+}
+
+struct dlist_t {
+	void (*destruct)(void);
+	struct dlist_t *next;
+};
+static struct dlist_t *destructors_list;
+
+void test_add_destructor(void (*d)(void))
+{
+	struct dlist_t *p;
+
+	p = malloc(sizeof(struct dlist_t));
+	if (p == NULL)
+		test_error("malloc() failed");
+
+	p->next = destructors_list;
+	p->destruct = d;
+	destructors_list = p;
+}
+
+static void test_destructor(void) __attribute__((destructor));
+static void test_destructor(void)
+{
+	while (destructors_list) {
+		struct dlist_t *p = destructors_list->next;
+
+		destructors_list->destruct();
+		free(destructors_list);
+		destructors_list = p;
+	}
+	test_exit();
+}
+
+static void sig_int(int signo)
+{
+	test_error("Caught SIGINT - exiting");
+}
+
+static int open_netns(void)
+{
+	const char *netns_path = "/proc/self/ns/net";
+	int fd;
+
+	fd = open(netns_path, O_RDONLY);
+	if (fd <= 0)
+		test_error("open(%s)", netns_path);
+	return fd;
+}
+
+static int unshare_open(void)
+{
+	if (unshare(CLONE_NEWNET) != 0)
+		test_error("unshare()");
+
+	return open_netns();
+}
+
+void switch_ns(int fd)
+{
+	if (setns(fd, CLONE_NEWNET))
+		test_error("setns()");
+}
+
+int switch_save_ns(int new_ns)
+{
+	int ret = open_netns();
+
+	switch_ns(new_ns);
+	return ret;
+}
+
+static int nsfd_outside	= -1;
+static int nsfd_parent	= -1;
+static int nsfd_child	= -1;
+const char veth_name[]	= "ktst-veth";
+
+static void init_namespaces(void)
+{
+	nsfd_outside = open_netns();
+	nsfd_parent = unshare_open();
+	nsfd_child = unshare_open();
+}
+
+static void link_init(const char *veth, int family, uint8_t prefix,
+		      union tcp_addr addr, union tcp_addr dest)
+{
+	if (link_set_up(veth))
+		test_error("Failed to set link up");
+	if (ip_addr_add(veth, family, addr, prefix))
+		test_error("Failed to add ip address");
+	if (ip_route_add(veth, family, addr, dest))
+		test_error("Failed to add route");
+}
+
+static unsigned nr_threads = 1;
+
+static pthread_mutex_t sync_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t sync_cond = PTHREAD_COND_INITIALIZER;
+static volatile unsigned stage_threads[2];
+static volatile unsigned stage_nr;
+
+/* synchronize all threads in the same stage */
+void synchronize_threads(void)
+{
+	unsigned q = stage_nr;
+
+	pthread_mutex_lock(&sync_lock);
+	stage_threads[q]++;
+	if (stage_threads[q] == nr_threads) {
+		stage_nr ^= 1;
+		stage_threads[stage_nr] = 0;
+		pthread_cond_signal(&sync_cond);
+	}
+	while (stage_threads[q] < nr_threads)
+		pthread_cond_wait(&sync_cond, &sync_lock);
+	pthread_mutex_unlock(&sync_lock);
+}
+
+__thread union tcp_addr this_ip_addr;
+__thread union tcp_addr this_ip_dest;
+int test_family;
+
+struct new_pthread_arg {
+	thread_fn	func;
+	union tcp_addr	my_ip;
+	union tcp_addr	dest_ip;
+};
+static void *new_pthread_entry(void *arg)
+{
+	struct new_pthread_arg *p = arg;
+
+	this_ip_addr = p->my_ip;
+	this_ip_dest = p->dest_ip;
+	p->func(NULL); /* shouldn't return */
+	exit(KSFT_FAIL);
+}
+
+void __test_init(unsigned int ntests, int family, unsigned prefix,
+			union tcp_addr addr1, union tcp_addr addr2,
+			thread_fn peer1, thread_fn peer2)
+{
+	struct sigaction sa = {
+		.sa_handler = sig_int,
+		.sa_flags = SA_RESTART,
+	};
+	time_t seed = time(NULL);
+
+	test_family = family;
+	ksft_set_plan(ntests);
+
+	test_print("rand seed %u", (unsigned int)seed);
+	srand(seed);
+
+	sigemptyset(&sa.sa_mask);
+	if (sigaction(SIGINT, &sa, NULL))
+		test_error("Can't set SIGINT handler");
+
+	ksft_print_header();
+	init_namespaces();
+
+	if (add_veth(veth_name, nsfd_parent, nsfd_child))
+		test_error("Failed to add veth");
+
+	switch_ns(nsfd_child);
+	link_init(veth_name, family, prefix, addr2, addr1);
+	if (peer2) {
+		struct new_pthread_arg targ;
+		pthread_t t;
+
+		targ.my_ip = addr2;
+		targ.dest_ip = addr1;
+		targ.func = peer2;
+		nr_threads++;
+		if (pthread_create(&t, NULL, new_pthread_entry, &targ))
+			test_error("Failed to create pthread");
+	}
+	switch_ns(nsfd_parent);
+	link_init(veth_name, family, prefix, addr1, addr2);
+
+	this_ip_addr = addr1;
+	this_ip_dest = addr2;
+	peer1(NULL);
+	if (failed)
+		exit(KSFT_FAIL);
+	else
+		exit(KSFT_PASS);
+}
+
+/* /proc/sys/net/core/optmem_max artifically limits the amount of memory
+ * that can be allocated with sock_kmalloc() on each socket in the system.
+ * It is not virtualized, so it has to written outside test namespaces.
+ * To be nice a test will revert optmem back to the old value.
+ * Keeping it simple without any file lock, which means the tests that
+ * need to set/increase optmem value shouldn't run in parallel.
+ * Also, not re-entrant.
+ */
+static const char *optmem_file = "/proc/sys/net/core/optmem_max";
+static size_t saved_optmem;
+
+static void __test_set_optmem(size_t new, size_t *old)
+{
+	FILE *foptmem;
+	int old_ns;
+
+	old_ns = switch_save_ns(nsfd_outside);
+	foptmem = fopen(optmem_file, "r+");
+	if (!foptmem)
+		test_error("failed to open %s", optmem_file);
+
+	if (old != NULL) {
+		if (fscanf(foptmem, "%zu", old) != 1)
+			test_error("can't read from %s", optmem_file);
+		fclose(foptmem);
+		foptmem = fopen(optmem_file, "w");
+		if (!foptmem)
+			test_error("failed to open %s", optmem_file);
+	}
+
+	if (fprintf(foptmem, "%zu", new) <= 0)
+		test_error("can't write %zu to %s", new, optmem_file);
+	fclose(foptmem);
+	switch_ns(old_ns);
+}
+
+static void test_revert_optmem(void)
+{
+	if (saved_optmem == 0)
+		return;
+
+	__test_set_optmem(saved_optmem, NULL);
+}
+
+void test_set_optmem(size_t value)
+{
+	if (saved_optmem == 0) {
+		__test_set_optmem(value, &saved_optmem);
+		test_add_destructor(test_revert_optmem);
+	} else {
+		__test_set_optmem(value, NULL);
+	}
+}
diff --git a/tools/testing/selftests/net/tcp_ao/lib/sock.c b/tools/testing/selftests/net/tcp_ao/lib/sock.c
new file mode 100644
index 000000000000..c0b0ac77b644
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/lib/sock.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <alloca.h>
+#include <fcntl.h>
+#include <string.h>
+#include "../../../../../include/linux/kernel.h"
+#include "../../../../../include/linux/stringify.h"
+#include "aolib.h"
+
+const unsigned test_server_port = 7010;
+int __test_listen_socket(int backlog, void *addr, size_t addr_sz)
+{
+	int err, sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
+	long flags;
+
+	if (sk < 0)
+		test_error("socket()");
+
+	err = setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, veth_name,
+			 strlen(veth_name) + 1);
+	if (err < 0)
+		test_error("setsockopt(SO_BINDTODEVICE)");
+
+	if (bind(sk, (struct sockaddr *)addr, addr_sz) < 0)
+		test_error("bind()");
+
+	flags = fcntl(sk, F_GETFL);
+	if ((flags < 0) || (fcntl(sk, F_SETFL, flags | O_NONBLOCK) < 0))
+		test_error("fcntl()");
+
+	if (listen(sk, backlog))
+		test_error("listen()");
+
+	return sk;
+}
+
+int test_wait_fd(int sk, time_t sec, bool write)
+{
+	struct timeval tv = { .tv_sec = sec };
+	struct timeval *ptv = NULL;
+	fd_set fds, efds;
+	int ret;
+	socklen_t slen = sizeof(ret);
+
+	FD_ZERO(&fds);
+	FD_SET(sk, &fds);
+	FD_ZERO(&efds);
+	FD_SET(sk, &efds);
+
+	if (sec)
+		ptv = &tv;
+
+	errno = 0;
+	if (write)
+		ret = select(sk + 1, NULL, &fds, &efds, ptv);
+	else
+		ret = select(sk + 1, &fds, NULL, &efds, ptv);
+	if (ret <= 0)
+		return -errno;
+
+	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &slen) || ret)
+		return -ret;
+	return sk;
+}
+
+int __test_connect_socket(int sk, void *addr, size_t addr_sz, time_t timeout)
+{
+	long flags;
+	int err;
+
+	err = setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, veth_name,
+			 strlen(veth_name) + 1);
+	if (err < 0)
+		test_error("setsockopt(SO_BINDTODEVICE)");
+
+	if (!timeout) {
+		err = connect(sk, addr, addr_sz);
+		if (err) {
+			err = -errno;
+			goto out;
+		}
+		return 0;
+	}
+
+	flags = fcntl(sk, F_GETFL);
+	if ((flags < 0) || (fcntl(sk, F_SETFL, flags | O_NONBLOCK) < 0))
+		test_error("fcntl()");
+
+	if (connect(sk, addr, addr_sz) < 0) {
+		if (errno != EINPROGRESS) {
+			err = -errno;
+			goto out;
+		}
+		err = test_wait_fd(sk, timeout, 1);
+		if (err <= 0)
+			goto out;
+	}
+	return sk;
+
+out:
+	close(sk);
+	return err;
+}
+
+int test_prepare_ao_sockaddr(struct tcp_ao *ao, const char *alg, uint16_t flags,
+		void *addr, size_t addr_sz, uint8_t prefix,
+		uint8_t sndid, uint8_t rcvid, uint8_t maclen,
+		uint8_t keyflags, uint8_t keylen, const char *key)
+{
+	memset(ao, 0, sizeof(struct tcp_ao));
+
+	ao->tcpa_flags		= flags;
+	ao->tcpa_prefix		= prefix;
+	ao->tcpa_sndid		= sndid;
+	ao->tcpa_rcvid		= rcvid;
+	ao->tcpa_maclen		= maclen;
+	ao->tcpa_keyflags	= keyflags;
+	ao->tcpa_keylen		= keylen;
+
+	memcpy(&ao->tcpa_addr, addr, addr_sz);
+
+	if (strlen(alg) > 64)
+		return -ENOBUFS;
+	strncpy(ao->tcpa_alg_name, alg, 64);
+
+	memcpy(ao->tcpa_key, key,
+		(TCP_AO_MAXKEYLEN < keylen) ? TCP_AO_MAXKEYLEN : keylen);
+	return 0;
+}
+
+int test_get_one_ao(int sk, struct tcp_ao_getsockopt *out, uint16_t flags,
+		void *addr, size_t addr_sz, uint8_t prefix,
+		uint8_t sndid, uint8_t rcvid)
+{
+	struct tcp_ao_getsockopt tmp = {};
+	socklen_t tmp_sz = sizeof(tmp);
+	int ret;
+
+	memcpy(&tmp.addr, addr, addr_sz);
+	tmp.prefix = prefix;
+	tmp.sndid  = sndid;
+	tmp.rcvid  = rcvid;
+	tmp.flags  = flags;
+	tmp.nkeys  = 1;
+
+	ret = getsockopt(sk, IPPROTO_TCP, TCP_AO_GET, &tmp, &tmp_sz);
+	if (ret)
+		return ret;
+	if (tmp.nkeys != 1)
+		return -ENOENT;
+	*out = tmp;
+	return 0;
+}
+
+int test_cmp_getsockopt_setsockopt(const struct tcp_ao *a,
+				   const struct tcp_ao_getsockopt *b)
+{
+	bool is_kdf_aes_128_cmac = false;
+
+	if (!strcmp("cmac(aes128)", a->tcpa_alg_name))
+		is_kdf_aes_128_cmac = (a->tcpa_keylen != 16);
+
+#define __cmp_ao(member)						\
+	if (b->member != a->tcpa_##member) {				\
+		test_fail("getsockopt(): " __stringify(member) " %u != %u",	\
+				b->member, a->tcpa_##member);		\
+		return -1;						\
+	}
+	__cmp_ao(sndid);
+	__cmp_ao(rcvid);
+	__cmp_ao(prefix);
+	__cmp_ao(keyflags);
+	if (a->tcpa_maclen) {
+		__cmp_ao(maclen);
+	} else if (b->maclen != 12) {
+		test_fail("getsockopt(): expected default maclen 12, but it's %u",
+				b->maclen);
+		return -1;
+	}
+	if (!is_kdf_aes_128_cmac) {
+		__cmp_ao(keylen);
+	} else if (b->keylen != 16) {
+		test_fail("getsockopt(): expected keylen 16 for cmac(aes128), but it's %u",
+				b->keylen);
+		return -1;
+	}
+#undef __cmp_ao
+	if (!is_kdf_aes_128_cmac && memcmp(b->key, a->tcpa_key, a->tcpa_keylen)) {
+		test_fail("getsockopt(): returned key is different `%s' != `%s'",
+				b->key, a->tcpa_key);
+		return -1;
+	}
+	if (memcmp(&b->addr, &a->tcpa_addr, sizeof(b->addr))) {
+		test_fail("getsockopt(): returned address is different");
+		return -1;
+	}
+	if (!is_kdf_aes_128_cmac && strcmp(b->alg_name, a->tcpa_alg_name)) {
+		test_fail("getsockopt(): returned algorithm is different");
+		return -1;
+	}
+	if (is_kdf_aes_128_cmac && strcmp(b->alg_name, "cmac(aes)")) {
+		test_fail("getsockopt(): returned algorithm is different");
+		return -1;
+	}
+	return 0;
+}
+
+#define TEST_BUF_SIZE 4096
+ssize_t test_server_run(int sk, ssize_t quota, time_t timeout_sec)
+{
+	ssize_t total = 0;
+
+	do {
+		char buf[TEST_BUF_SIZE];
+		ssize_t bytes, sent;
+		int ret;
+
+		ret = test_wait_fd(sk, timeout_sec, 0);
+		if (ret <= 0)
+			return ret;
+
+		bytes = recv(sk, buf, sizeof(buf), 0);
+
+		if (bytes < 0)
+			test_error("recv(): %zd", bytes);
+		if (bytes == 0)
+			break;
+
+		ret = test_wait_fd(sk, timeout_sec, 1);
+		if (ret <= 0)
+			return ret;
+
+		sent = send(sk, buf, bytes, 0);
+		if (sent == 0)
+			break;
+		if (sent != bytes)
+			test_error("send()");
+		total += bytes;
+	} while (!quota || total < quota);
+
+	return total;
+}
+
+ssize_t test_client_loop(int sk, char *buf, size_t buf_sz,
+			 const size_t msg_len, time_t timeout_sec)
+{
+	char msg[msg_len];
+	int nodelay = 1;
+	size_t i;
+
+	if (setsockopt(sk, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)))
+		test_error("setsockopt(TCP_NODELAY)");
+
+	for (i = 0; i < buf_sz; i += min(msg_len, buf_sz - i)) {
+		size_t sent, bytes = min(msg_len, buf_sz - i);
+		int ret;
+
+		ret = test_wait_fd(sk, timeout_sec, 1);
+		if (ret <= 0)
+			return ret;
+
+		sent = send(sk, buf + i, bytes, 0);
+		if (sent == 0)
+			break;
+		if (sent != bytes)
+			test_error("send()");
+
+		ret = test_wait_fd(sk, timeout_sec, 0);
+		if (ret <= 0)
+			return ret;
+
+		bytes = recv(sk, msg, sizeof(msg), 0);
+		if (bytes < 0)
+			test_error("recv(): %zd", bytes);
+		if (bytes != sent)
+			test_error("recv(): %zd != %zd", bytes, sent);
+		if (memcmp(buf + i, msg, bytes) != 0) {
+			test_fail("received message differs");
+			return -1;
+		}
+	}
+	return i;
+}
+
+int test_client_verify(int sk, const size_t msg_len, const size_t nr,
+		       time_t timeout_sec)
+{
+	size_t buf_sz = msg_len * nr;
+	char *buf = alloca(buf_sz);
+
+	randomize_buffer(buf, buf_sz);
+	if (test_client_loop(sk, buf, buf_sz, msg_len, timeout_sec) != buf_sz)
+		return -1;
+	return 0;
+}
diff --git a/tools/testing/selftests/net/tcp_ao/lib/utils.c b/tools/testing/selftests/net/tcp_ao/lib/utils.c
new file mode 100644
index 000000000000..372daca525f5
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/lib/utils.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "aolib.h"
+#include <string.h>
+
+void randomize_buffer(void *buf, size_t buflen)
+{
+	int *p = (int *)buf;
+	size_t words = buflen / sizeof(int);
+	size_t leftover = buflen % sizeof(int);
+
+	if (!buflen)
+		return;
+
+	while (words--)
+		*p++ = rand();
+
+	if (leftover) {
+		int tmp = rand();
+
+		memcpy(buf + buflen - leftover, &tmp, leftover);
+	}
+}
+
+const struct sockaddr_in6 addr_any6 = {
+	.sin6_family	= AF_INET6,
+};
+
+const struct sockaddr_in addr_any4 = {
+	.sin_family	= AF_INET,
+};
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 26/31] selftests/net: Verify that TCP-AO complies with ignoring ICMPs
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (24 preceding siblings ...)
  2022-08-18 16:59 ` [PATCH 25/31] selftests/net: Add TCP-AO library Dmitry Safonov
@ 2022-08-18 17:00 ` Dmitry Safonov
  2022-08-18 17:00 ` [PATCH 27/31] selftest/net: Add TCP-AO ICMPs accept test Dmitry Safonov
                   ` (5 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 17:00 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Hand-crafted ICMP packets are sent to the server, the server checks for
hard/soft errors and fails if any.

Expected output for ipv4 version:
> # ./icmps-discard_ipv4
> 1..3
> # 3164[lib/setup.c:166] rand seed 1642623745
> TAP version 13
> # 3164[lib/proc.c:207]    Snmp6             Ip6InReceives: 0 => 1
> # 3164[lib/proc.c:207]    Snmp6             Ip6InNoRoutes: 0 => 1
> # 3164[lib/proc.c:207]    Snmp6               Ip6InOctets: 0 => 76
> # 3164[lib/proc.c:207]    Snmp6            Ip6InNoECTPkts: 0 => 1
> # 3164[lib/proc.c:207]      Tcp                    InSegs: 2 => 203
> # 3164[lib/proc.c:207]      Tcp                   OutSegs: 1 => 202
> # 3164[lib/proc.c:207]  IcmpMsg                   InType3: 0 => 543
> # 3164[lib/proc.c:207]     Icmp                    InMsgs: 0 => 543
> # 3164[lib/proc.c:207]     Icmp            InDestUnreachs: 0 => 543
> # 3164[lib/proc.c:207]       Ip                InReceives: 2 => 746
> # 3164[lib/proc.c:207]       Ip                InDelivers: 2 => 746
> # 3164[lib/proc.c:207]       Ip               OutRequests: 1 => 202
> # 3164[lib/proc.c:207]    IpExt                  InOctets: 132 => 61684
> # 3164[lib/proc.c:207]    IpExt                 OutOctets: 68 => 31324
> # 3164[lib/proc.c:207]    IpExt               InNoECTPkts: 2 => 744
> # 3164[lib/proc.c:207]   TcpExt               TCPPureAcks: 1 => 2
> # 3164[lib/proc.c:207]   TcpExt           TCPOrigDataSent: 0 => 200
> # 3164[lib/proc.c:207]   TcpExt              TCPDelivered: 0 => 199
> # 3164[lib/proc.c:207]   TcpExt                 TCPAOGood: 2 => 203
> # 3164[lib/proc.c:207]   TcpExt         TCPAODroppedIcmps: 0 => 541
> ok 1 InDestUnreachs delivered 543
> ok 2 Server survived 20000 bytes of traffic
> ok 3 ICMPs ignored 541
> # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0

Expected output for ipv6 version:
> # ./icmps-discard_ipv6
> 1..3
> # 3186[lib/setup.c:166] rand seed 1642623803
> TAP version 13
> # 3186[lib/proc.c:207]    Snmp6             Ip6InReceives: 4 => 568
> # 3186[lib/proc.c:207]    Snmp6             Ip6InDelivers: 3 => 564
> # 3186[lib/proc.c:207]    Snmp6            Ip6OutRequests: 2 => 204
> # 3186[lib/proc.c:207]    Snmp6            Ip6InMcastPkts: 1 => 4
> # 3186[lib/proc.c:207]    Snmp6           Ip6OutMcastPkts: 0 => 1
> # 3186[lib/proc.c:207]    Snmp6               Ip6InOctets: 320 => 70420
> # 3186[lib/proc.c:207]    Snmp6              Ip6OutOctets: 160 => 35512
> # 3186[lib/proc.c:207]    Snmp6          Ip6InMcastOctets: 72 => 336
> # 3186[lib/proc.c:207]    Snmp6         Ip6OutMcastOctets: 0 => 76
> # 3186[lib/proc.c:207]    Snmp6            Ip6InNoECTPkts: 4 => 568
> # 3186[lib/proc.c:207]    Snmp6               Icmp6InMsgs: 1 => 361
> # 3186[lib/proc.c:207]    Snmp6              Icmp6OutMsgs: 1 => 2
> # 3186[lib/proc.c:207]    Snmp6       Icmp6InDestUnreachs: 0 => 360
> # 3186[lib/proc.c:207]    Snmp6      Icmp6OutMLDv2Reports: 0 => 1
> # 3186[lib/proc.c:207]    Snmp6              Icmp6InType1: 0 => 360
> # 3186[lib/proc.c:207]    Snmp6           Icmp6OutType143: 0 => 1
> # 3186[lib/proc.c:207]      Tcp                    InSegs: 2 => 203
> # 3186[lib/proc.c:207]      Tcp                   OutSegs: 1 => 202
> # 3186[lib/proc.c:207]   TcpExt               TCPPureAcks: 1 => 2
> # 3186[lib/proc.c:207]   TcpExt           TCPOrigDataSent: 0 => 200
> # 3186[lib/proc.c:207]   TcpExt              TCPDelivered: 0 => 199
> # 3186[lib/proc.c:207]   TcpExt                 TCPAOGood: 2 => 203
> # 3186[lib/proc.c:207]   TcpExt         TCPAODroppedIcmps: 0 => 360
> ok 1 Icmp6InDestUnreachs delivered 360
> ok 2 Server survived 20000 bytes of traffic
> ok 3 ICMPs ignored 360
> # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 tools/testing/selftests/net/tcp_ao/Makefile   |   2 +-
 .../selftests/net/tcp_ao/icmps-discard.c      | 434 ++++++++++++++++++
 2 files changed, 435 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/net/tcp_ao/icmps-discard.c

diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile
index cb23d67944d7..9acfd782c20f 100644
--- a/tools/testing/selftests/net/tcp_ao/Makefile
+++ b/tools/testing/selftests/net/tcp_ao/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-TEST_BOTH_AF := connect
+TEST_BOTH_AF := connect icmps-discard
 
 TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4)
 TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6)
diff --git a/tools/testing/selftests/net/tcp_ao/icmps-discard.c b/tools/testing/selftests/net/tcp_ao/icmps-discard.c
new file mode 100644
index 000000000000..16d691809567
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/icmps-discard.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Selftest that verifies that incomping ICMPs are ignored,
+ * the TCP connection stays alive, no hard or soft errors get reported
+ * to the usespace and the counter for ignored ICMPs is updated.
+ *
+ * RFC5925, 7.8:
+ * >> A TCP-AO implementation MUST default to ignore incoming ICMPv4
+ * messages of Type 3 (destination unreachable), Codes 2-4 (protocol
+ * unreachable, port unreachable, and fragmentation needed -- ’hard
+ * errors’), and ICMPv6 Type 1 (destination unreachable), Code 1
+ * (administratively prohibited) and Code 4 (port unreachable) intended
+ * for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN-
+ * WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs.
+ *
+ * Author: Dmitry Safonov <dima@arista.com>
+ */
+#include <inttypes.h>
+#include <linux/icmp.h>
+#include <linux/icmpv6.h>
+#include <linux/ipv6.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include "aolib.h"
+
+#ifndef SOL_TCP
+/* can't include <netinet/tcp.h> as including <linux/tcp.h> */
+# define SOL_TCP		6	/* TCP level */
+#endif
+
+const size_t packets_nr = 20;
+const size_t packet_size = 100;
+const char *tcpao_icmps	= "TCPAODroppedIcmps";
+
+#ifdef IPV6_TEST
+const char *dst_unreach	= "Icmp6InDestUnreachs";
+const int sk_ip_level	= SOL_IPV6;
+const int sk_recverr	= IPV6_RECVERR;
+#else
+const char *dst_unreach	= "InDestUnreachs";
+const int sk_ip_level	= SOL_IP;
+const int sk_recverr	= IP_RECVERR;
+#endif
+
+#define test_icmps_fail test_fail
+#define test_icmps_ok test_ok
+
+static void serve_interfered(int sk)
+{
+	ssize_t test_quota = packet_size * packets_nr * 10;
+	uint64_t dest_unreach_a, dest_unreach_b;
+	uint64_t icmp_ignored_a, icmp_ignored_b;
+	bool counter_not_found;
+	struct netstat *ns_after, *ns_before;
+	ssize_t bytes;
+
+	ns_before = netstat_read();
+	dest_unreach_a = netstat_get(ns_before, dst_unreach, NULL);
+	icmp_ignored_a = netstat_get(ns_before, tcpao_icmps, NULL);
+	bytes = test_server_run(sk, test_quota, 0);
+	ns_after = netstat_read();
+	netstat_print_diff(ns_before, ns_after);
+	dest_unreach_b = netstat_get(ns_after, dst_unreach, NULL);
+	icmp_ignored_b = netstat_get(ns_after, tcpao_icmps,
+					&counter_not_found);
+
+	netstat_free(ns_before);
+	netstat_free(ns_after);
+
+	if (dest_unreach_a >= dest_unreach_b) {
+		test_fail("%s counter didn't change: %" PRIu64 " >= %" PRIu64,
+				dst_unreach, dest_unreach_a, dest_unreach_b);
+		return;
+	} else {
+		test_ok("%s delivered %" PRIu64,
+				dst_unreach, dest_unreach_b - dest_unreach_a);
+	}
+	if (bytes < 0) {
+		test_icmps_fail("server failed with %zd: %s", bytes, strerrordesc_np(-bytes));
+	} else {
+		test_icmps_ok("Server survived %zd bytes of traffic", test_quota);
+	}
+	if (counter_not_found) {
+		test_fail("Not found %s counter", tcpao_icmps);
+		return;
+	}
+	if (icmp_ignored_a >= icmp_ignored_b) {
+		test_icmps_fail("%s counter didn't change: %" PRIu64 " >= %" PRIu64,
+				tcpao_icmps, icmp_ignored_a, icmp_ignored_b);
+		return;
+	}
+	test_icmps_ok("ICMPs ignored %" PRIu64, icmp_ignored_b - icmp_ignored_a);
+}
+
+static void *server_fn(void *arg)
+{
+	int val, err, sk, lsk;
+	uint16_t flags = 0;
+
+	lsk = test_listen_socket(this_ip_addr, test_server_port, 1);
+
+	if (test_set_ao(lsk, "password", flags, this_ip_dest, -1, 100, 100))
+		test_error("setsockopt(TCP_AO)");
+	synchronize_threads();
+
+	err = test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0);
+	if (!err)
+		test_error("timeouted for accept()");
+	else if (err < 0)
+		test_error("test_wait_fd()");
+
+	sk = accept(lsk, NULL, NULL);
+	if (sk < 0)
+		test_error("accept()");
+
+	/* Fail on hard ip errors, such as dest unreachable (RFC1122) */
+	val = 1;
+	if (setsockopt(sk, sk_ip_level, sk_recverr, &val, sizeof(val)))
+		test_error("setsockopt()");
+
+	synchronize_threads();
+
+	serve_interfered(sk);
+	return NULL;
+}
+
+static size_t packets_sent;
+static size_t icmps_sent;
+
+static uint32_t checksum4_nofold(void *data, size_t len, uint32_t sum)
+{
+	uint16_t *words = data;
+	size_t i;
+
+	for (i = 0; i < len / sizeof(uint16_t); i++)
+		sum += words[i];
+	if (len & 1)
+		sum += ((char *)data)[len - 1];
+	return sum;
+}
+
+static uint16_t checksum4_fold(void *data, size_t len, uint32_t sum)
+{
+	sum = checksum4_nofold(data, len, sum);
+	while (sum > 0xFFFF)
+		sum = (sum & 0xFFFF) + (sum >> 16);
+	return ~sum;
+}
+
+static void set_ip4hdr(struct iphdr *iph, size_t packet_len, int proto,
+		struct sockaddr_in *src, struct sockaddr_in *dst)
+{
+	iph->version	= 4;
+	iph->ihl	= 5;
+	iph->tos	= 0;
+	iph->tot_len	= htons(packet_len);
+	iph->ttl	= 2;
+	iph->protocol	= proto;
+	iph->saddr	= src->sin_addr.s_addr;
+	iph->daddr	= dst->sin_addr.s_addr;
+	iph->check	= checksum4_fold((void *)iph, iph->ihl << 1, 0);
+}
+
+static void icmp_interfere4(uint8_t type, uint8_t code, uint32_t rcv_nxt,
+		struct sockaddr_in *src, struct sockaddr_in *dst)
+{
+	int sk = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+	struct {
+		struct iphdr iph;
+		struct icmphdr icmph;
+		struct iphdr iphe;
+		struct {
+			uint16_t sport;
+			uint16_t dport;
+			uint32_t seq;
+		} tcph;
+	} packet = {};
+	size_t packet_len;
+	ssize_t bytes;
+
+	if (sk < 0)
+		test_error("socket(AF_INET, SOCK_RAW, IPPROTO_RAW)");
+
+	packet_len = sizeof(packet);
+	set_ip4hdr(&packet.iph, packet_len, IPPROTO_ICMP, src, dst);
+
+	packet.icmph.type = type;
+	packet.icmph.code = code;
+	if (code == ICMP_FRAG_NEEDED) {
+		randomize_buffer(&packet.icmph.un.frag.mtu,
+				sizeof(packet.icmph.un.frag.mtu));
+	}
+
+	packet_len = sizeof(packet.iphe) + sizeof(packet.tcph);
+	set_ip4hdr(&packet.iphe, packet_len, IPPROTO_TCP, dst, src);
+
+	packet.tcph.sport = dst->sin_port;
+	packet.tcph.dport = src->sin_port;
+	packet.tcph.seq = htonl(rcv_nxt);
+
+	packet_len = sizeof(packet) - sizeof(packet.iph);
+	packet.icmph.checksum = checksum4_fold((void *)&packet.icmph,
+						packet_len, 0);
+
+	bytes = sendto(sk, &packet, sizeof(packet), 0,
+			(struct sockaddr*)dst, sizeof(*dst));
+	if (bytes != sizeof(packet))
+		test_error("send(): %zd", bytes);
+	icmps_sent++;
+
+	close(sk);
+}
+
+static void set_ip6hdr(struct ipv6hdr *iph, size_t packet_len, int proto,
+		struct sockaddr_in6 *src, struct sockaddr_in6 *dst)
+{
+	iph->version		= 6;
+	iph->payload_len	= htons(packet_len);
+	iph->nexthdr		= proto;
+	iph->hop_limit		= 2;
+	iph->saddr		= src->sin6_addr;
+	iph->daddr		= dst->sin6_addr;
+}
+
+static inline uint16_t csum_fold(uint32_t csum)
+{
+	uint32_t sum = csum;
+	sum = (sum & 0xffff) + (sum >> 16);
+	sum = (sum & 0xffff) + (sum >> 16);
+	return (uint16_t)~sum;
+}
+
+static inline uint32_t csum_add(uint32_t csum, uint32_t addend)
+{
+	uint32_t res = csum;
+
+	res += addend;
+	return res + (res < addend);
+}
+
+__attribute__ ((noinline)) uint32_t checksum6_nofold(void *data, size_t len, uint32_t sum)
+{
+	uint16_t *words = data;
+	size_t i;
+
+	for (i = 0; i < len / sizeof(uint16_t); i++)
+		sum = csum_add(sum, words[i]);
+	if (len & 1)
+		sum = csum_add(sum, ((char *)data)[len - 1]);
+	return sum;
+}
+
+__attribute__ ((noinline)) uint16_t icmp6_checksum(struct sockaddr_in6 *src,
+			struct sockaddr_in6 *dst,
+			void *ptr, size_t len, uint8_t proto)
+{
+	struct {
+		struct in6_addr saddr;
+		struct in6_addr daddr;
+		uint32_t payload_len;
+		uint8_t zero[3];
+		uint8_t nexthdr;
+	} pseudo_header = {};
+	uint32_t sum;
+
+	pseudo_header.saddr		= src->sin6_addr;
+	pseudo_header.daddr		= dst->sin6_addr;
+	pseudo_header.payload_len	= htonl(len);
+	pseudo_header.nexthdr		= proto;
+
+	sum = checksum6_nofold(&pseudo_header, sizeof(pseudo_header), 0);
+	sum = checksum6_nofold(ptr, len, sum);
+
+	return csum_fold(sum);
+}
+
+static void icmp6_interfere(int type, int code, uint32_t rcv_nxt,
+		struct sockaddr_in6 *src, struct sockaddr_in6 *dst)
+{
+	int sk = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
+	struct sockaddr_in6 dst_raw = *dst;
+	struct {
+		struct ipv6hdr iph;
+		struct icmp6hdr icmph;
+		struct ipv6hdr iphe;
+		struct {
+			uint16_t sport;
+			uint16_t dport;
+			uint32_t seq;
+		} tcph;
+	} packet = {};
+	size_t packet_len;
+	ssize_t bytes;
+
+
+	if (sk < 0)
+		test_error("socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)");
+
+	packet_len = sizeof(packet) - sizeof(packet.iph);
+	set_ip6hdr(&packet.iph, packet_len, IPPROTO_ICMPV6, src, dst);
+
+	packet.icmph.icmp6_type = type;
+	packet.icmph.icmp6_code = code;
+
+	packet_len = sizeof(packet.iphe) + sizeof(packet.tcph);
+	set_ip6hdr(&packet.iphe, packet_len, IPPROTO_TCP, dst, src);
+
+	packet.tcph.sport = dst->sin6_port;
+	packet.tcph.dport = src->sin6_port;
+	packet.tcph.seq = htonl(rcv_nxt);
+
+	packet_len = sizeof(packet) - sizeof(packet.iph);
+
+	packet.icmph.icmp6_cksum = icmp6_checksum(src, dst,
+			(void *)&packet.icmph, packet_len, IPPROTO_ICMPV6);
+
+	dst_raw.sin6_port = htons(IPPROTO_RAW);
+	bytes = sendto(sk, &packet, sizeof(packet), 0,
+			(struct sockaddr*)&dst_raw, sizeof(dst_raw));
+	if (bytes != sizeof(packet))
+		test_error("send(): %zd", bytes);
+	icmps_sent++;
+
+	close(sk);
+}
+
+static uint32_t get_rcv_nxt(int sk)
+{
+	int val = TCP_REPAIR_ON;
+	uint32_t ret;
+	socklen_t sz = sizeof(ret);
+
+	if (setsockopt(sk, SOL_TCP, TCP_REPAIR, &val, sizeof(val)))
+		test_error("setsockopt(TCP_REPAIR)");
+	val = TCP_RECV_QUEUE;
+	if (setsockopt(sk, SOL_TCP, TCP_REPAIR_QUEUE, &val, sizeof(val)))
+		test_error("setsockopt(TCP_REPAIR_QUEUE)");
+	if (getsockopt(sk, SOL_TCP, TCP_QUEUE_SEQ, &ret, &sz))
+		test_error("getsockopt(TCP_QUEUE_SEQ)");
+	val = TCP_REPAIR_OFF_NO_WP;
+	if (setsockopt(sk, SOL_TCP, TCP_REPAIR, &val, sizeof(val)))
+		test_error("setsockopt(TCP_REPAIR)");
+	return ret;
+}
+
+static void icmp_interfere(const size_t nr, uint32_t rcv_nxt, void *src, void *dst)
+{
+	struct sockaddr_in *saddr4 = src;
+	struct sockaddr_in *daddr4 = dst;
+	struct sockaddr_in6 *saddr6 = src;
+	struct sockaddr_in6 *daddr6 = dst;
+	size_t i;
+
+	if (saddr4->sin_family != daddr4->sin_family)
+		test_error("Different address families");
+
+	for (i = 0; i < nr; i++) {
+		if (saddr4->sin_family == AF_INET) {
+			icmp_interfere4(ICMP_DEST_UNREACH, ICMP_PROT_UNREACH,
+					rcv_nxt, saddr4, daddr4);
+			icmp_interfere4(ICMP_DEST_UNREACH, ICMP_PORT_UNREACH,
+					rcv_nxt, saddr4, daddr4);
+			icmp_interfere4(ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+					rcv_nxt, saddr4, daddr4);
+			icmps_sent += 3;
+		} else if (saddr4->sin_family == AF_INET6) {
+			icmp6_interfere(ICMPV6_DEST_UNREACH,
+					ICMPV6_ADM_PROHIBITED,
+					rcv_nxt, saddr6, daddr6);
+			icmp6_interfere(ICMPV6_DEST_UNREACH,
+					ICMPV6_PORT_UNREACH,
+					rcv_nxt, saddr6, daddr6);
+			icmps_sent += 2;
+		} else {
+			test_error("Not ip address family");
+		}
+	}
+}
+
+static void send_interfered(int sk)
+{
+	const unsigned timeout = TEST_TIMEOUT_SEC;
+	struct sockaddr_in6 src, dst;
+	socklen_t addr_sz;
+
+	addr_sz = sizeof(src);
+	if (getsockname(sk, &src, &addr_sz))
+		test_error("getsockname()");
+	addr_sz = sizeof(dst);
+	if (getpeername(sk, &dst, &addr_sz))
+		test_error("getpeername()");
+
+	while (1) {
+		uint32_t rcv_nxt;
+
+		if (test_client_verify(sk, packet_size, packets_nr, timeout)) {
+			test_fail("client: connection is broken");
+			return;
+		}
+		packets_sent += packets_nr;
+		rcv_nxt = get_rcv_nxt(sk);
+		icmp_interfere(packets_nr, rcv_nxt, (void *)&src, (void *)&dst);
+	}
+}
+
+static void *client_fn(void *arg)
+{
+	int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
+
+	if (sk < 0)
+		test_error("socket()");
+
+	if (test_set_ao(sk, "password", 0, this_ip_dest, -1, 100, 100))
+		test_error("setsockopt(TCP_AO)");
+
+	synchronize_threads();
+	if (test_connect_socket(sk, this_ip_dest, test_server_port) <= 0)
+		test_error("failed to connect()");
+	synchronize_threads();
+
+	send_interfered(sk);
+
+	/* Not expecting client to quit */
+	test_fail("client disconnected");
+
+	return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+	test_init(3, server_fn, client_fn);
+	return 0;
+}
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 27/31] selftest/net: Add TCP-AO ICMPs accept test
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (25 preceding siblings ...)
  2022-08-18 17:00 ` [PATCH 26/31] selftests/net: Verify that TCP-AO complies with ignoring ICMPs Dmitry Safonov
@ 2022-08-18 17:00 ` Dmitry Safonov
  2022-08-18 17:00 ` [PATCH 28/31] selftest/tcp-ao: Add a test for MKT matching Dmitry Safonov
                   ` (4 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 17:00 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Reverse to icmps-discard test: the server accepts ICMPs, using
TCP_AO_CMDF_ACCEPT_ICMP and it is expected to fail under ICMP
flood from client. Test that the default pre-TCP-AO behaviour functions
when TCP_AO_CMDF_ACCEPT_ICMP is set.

Expected output for ipv4 version (in case it receives ICMP_PROT_UNREACH):
> # ./icmps-accept_ipv4
> 1..3
> # 3209[lib/setup.c:166] rand seed 1642623870
> TAP version 13
> # 3209[lib/proc.c:207]    Snmp6             Ip6InReceives: 0 => 1
> # 3209[lib/proc.c:207]    Snmp6             Ip6InNoRoutes: 0 => 1
> # 3209[lib/proc.c:207]    Snmp6               Ip6InOctets: 0 => 76
> # 3209[lib/proc.c:207]    Snmp6            Ip6InNoECTPkts: 0 => 1
> # 3209[lib/proc.c:207]      Tcp                    InSegs: 3 => 23
> # 3209[lib/proc.c:207]      Tcp                   OutSegs: 2 => 22
> # 3209[lib/proc.c:207]  IcmpMsg                   InType3: 0 => 4
> # 3209[lib/proc.c:207]     Icmp                    InMsgs: 0 => 4
> # 3209[lib/proc.c:207]     Icmp            InDestUnreachs: 0 => 4
> # 3209[lib/proc.c:207]       Ip                InReceives: 3 => 27
> # 3209[lib/proc.c:207]       Ip                InDelivers: 3 => 27
> # 3209[lib/proc.c:207]       Ip               OutRequests: 2 => 22
> # 3209[lib/proc.c:207]    IpExt                  InOctets: 288 => 3420
> # 3209[lib/proc.c:207]    IpExt                 OutOctets: 124 => 3244
> # 3209[lib/proc.c:207]    IpExt               InNoECTPkts: 3 => 25
> # 3209[lib/proc.c:207]   TcpExt               TCPPureAcks: 1 => 2
> # 3209[lib/proc.c:207]   TcpExt           TCPOrigDataSent: 0 => 20
> # 3209[lib/proc.c:207]   TcpExt              TCPDelivered: 0 => 19
> # 3209[lib/proc.c:207]   TcpExt                 TCPAOGood: 3 => 23
> ok 1 InDestUnreachs delivered 4
> ok 2 server failed with -92: Protocol not available
> ok 3 TCPAODroppedIcmps counter didn't change: 0 >= 0
> # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0

Expected output for ipv6 version (in case it receives ADM_PROHIBITED):
> # ./icmps-accept_ipv6
> 1..3
> # 3277[lib/setup.c:166] rand seed 1642624035
> TAP version 13
> # 3277[lib/proc.c:207]    Snmp6             Ip6InReceives: 6 => 31
> # 3277[lib/proc.c:207]    Snmp6             Ip6InDelivers: 4 => 29
> # 3277[lib/proc.c:207]    Snmp6            Ip6OutRequests: 4 => 24
> # 3277[lib/proc.c:207]    Snmp6               Ip6InOctets: 592 => 4492
> # 3277[lib/proc.c:207]    Snmp6              Ip6OutOctets: 332 => 3852
> # 3277[lib/proc.c:207]    Snmp6            Ip6InNoECTPkts: 6 => 31
> # 3277[lib/proc.c:207]    Snmp6               Icmp6InMsgs: 1 => 6
> # 3277[lib/proc.c:207]    Snmp6       Icmp6InDestUnreachs: 0 => 5
> # 3277[lib/proc.c:207]    Snmp6              Icmp6InType1: 0 => 5
> # 3277[lib/proc.c:207]      Tcp                    InSegs: 3 => 23
> # 3277[lib/proc.c:207]      Tcp                   OutSegs: 2 => 22
> # 3277[lib/proc.c:207]   TcpExt               TCPPureAcks: 1 => 2
> # 3277[lib/proc.c:207]   TcpExt           TCPOrigDataSent: 0 => 20
> # 3277[lib/proc.c:207]   TcpExt              TCPDelivered: 0 => 19
> # 3277[lib/proc.c:207]   TcpExt                 TCPAOGood: 3 => 23
> ok 1 Icmp6InDestUnreachs delivered 5
> ok 2 server failed with -13: Permission denied
> ok 3 TCPAODroppedIcmps counter didn't change: 0 >= 0
> # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0

With some luck the server may fail with ECONNREFUSED (depending on what
icmp packet was delivered firstly).
For the kernel error handlers see: tab_unreach[] and icmp_err_convert[].

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 tools/testing/selftests/net/tcp_ao/Makefile     |  4 +++-
 .../testing/selftests/net/tcp_ao/icmps-accept.c |  1 +
 .../selftests/net/tcp_ao/icmps-discard.c        | 17 +++++++++++++++--
 3 files changed, 19 insertions(+), 3 deletions(-)
 create mode 120000 tools/testing/selftests/net/tcp_ao/icmps-accept.c

diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile
index 9acfd782c20f..a178bde0af08 100644
--- a/tools/testing/selftests/net/tcp_ao/Makefile
+++ b/tools/testing/selftests/net/tcp_ao/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-TEST_BOTH_AF := connect icmps-discard
+TEST_BOTH_AF := connect icmps-discard icmps-accept
 
 TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4)
 TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6)
@@ -43,3 +43,5 @@ $(OUTPUT)/%_ipv4: %.c
 $(OUTPUT)/%_ipv6: %.c
 	$(LINK.c) -DIPV6_TEST $^ $(LDLIBS) -o $@
 
+$(OUTPUT)/icmps-accept_ipv4: CFLAGS+= -DTEST_ICMPS_ACCEPT
+$(OUTPUT)/icmps-accept_ipv6: CFLAGS+= -DTEST_ICMPS_ACCEPT
diff --git a/tools/testing/selftests/net/tcp_ao/icmps-accept.c b/tools/testing/selftests/net/tcp_ao/icmps-accept.c
new file mode 120000
index 000000000000..0a5bb85eb260
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/icmps-accept.c
@@ -0,0 +1 @@
+icmps-discard.c
\ No newline at end of file
diff --git a/tools/testing/selftests/net/tcp_ao/icmps-discard.c b/tools/testing/selftests/net/tcp_ao/icmps-discard.c
index 16d691809567..143571277548 100644
--- a/tools/testing/selftests/net/tcp_ao/icmps-discard.c
+++ b/tools/testing/selftests/net/tcp_ao/icmps-discard.c
@@ -43,8 +43,17 @@ const int sk_ip_level	= SOL_IP;
 const int sk_recverr	= IP_RECVERR;
 #endif
 
-#define test_icmps_fail test_fail
-#define test_icmps_ok test_ok
+/*
+ * Server is expected to fail with hard error if
+ * TCP_AO_CMDF_ACCEPT_ICMP is set
+ */
+#ifdef TEST_ICMPS_ACCEPT
+# define test_icmps_fail test_ok
+# define test_icmps_ok test_fail
+#else
+# define test_icmps_fail test_fail
+# define test_icmps_ok test_ok
+#endif
 
 static void serve_interfered(int sk)
 {
@@ -100,6 +109,10 @@ static void *server_fn(void *arg)
 
 	lsk = test_listen_socket(this_ip_addr, test_server_port, 1);
 
+#ifdef TEST_ICMPS_ACCEPT
+	flags = TCP_AO_CMDF_ACCEPT_ICMP;
+#endif
+
 	if (test_set_ao(lsk, "password", flags, this_ip_dest, -1, 100, 100))
 		test_error("setsockopt(TCP_AO)");
 	synchronize_threads();
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 28/31] selftest/tcp-ao: Add a test for MKT matching
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (26 preceding siblings ...)
  2022-08-18 17:00 ` [PATCH 27/31] selftest/net: Add TCP-AO ICMPs accept test Dmitry Safonov
@ 2022-08-18 17:00 ` Dmitry Safonov
  2022-08-18 17:00 ` [PATCH 29/31] selftest/tcp-ao: Add test for TCP-AO add setsockopt() command Dmitry Safonov
                   ` (3 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 17:00 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Add TCP-AO tests on connect()/accept() pair.
SNMP counters exposed by kernel are very useful here to verify the
expected behavior of TCP-AO.

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 tools/testing/selftests/net/tcp_ao/Makefile   |   2 +-
 .../selftests/net/tcp_ao/connect-deny.c       | 217 ++++++++++++++++++
 2 files changed, 218 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/net/tcp_ao/connect-deny.c

diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile
index a178bde0af08..5064e34ebe38 100644
--- a/tools/testing/selftests/net/tcp_ao/Makefile
+++ b/tools/testing/selftests/net/tcp_ao/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-TEST_BOTH_AF := connect icmps-discard icmps-accept
+TEST_BOTH_AF := connect icmps-discard icmps-accept connect-deny
 
 TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4)
 TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6)
diff --git a/tools/testing/selftests/net/tcp_ao/connect-deny.c b/tools/testing/selftests/net/tcp_ao/connect-deny.c
new file mode 100644
index 000000000000..cf71dda52c49
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/connect-deny.c
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dmitry Safonov <dima@arista.com> */
+#include <inttypes.h>
+#include "aolib.h"
+
+typedef uint8_t fault_t;
+#define F_TIMEOUT	1
+#define F_KEYREJECT	2
+
+#define fault(type)	(inj == type)
+
+static void try_accept(const char *tst_name, unsigned port, const char *pwd,
+		       union tcp_addr addr, uint8_t prefix,
+		       uint8_t sndid, uint8_t rcvid, const char *cnt_name,
+		       fault_t inj)
+{
+	uint64_t before_cnt, after_cnt;
+	int lsk, err, sk = 0;
+	time_t timeout;
+
+	lsk = test_listen_socket(this_ip_addr, port, 1);
+
+	if (pwd && test_set_ao(lsk, pwd, 0, addr, prefix, sndid, rcvid))
+		test_error("setsockopt(TCP_AO)");
+
+	if (cnt_name)
+		before_cnt = netstat_get_one(cnt_name, NULL);
+
+	synchronize_threads(); /* preparations done */
+
+	timeout = fault(F_TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC;
+	err = test_wait_fd(lsk, timeout, 0);
+	if (err < 0)
+		test_error("test_wait_fd()");
+	else if (!err) {
+		if (!fault(F_TIMEOUT))
+			test_fail("timeouted for accept()");
+	} else {
+		if (fault(F_TIMEOUT))
+			test_fail("ready to accept");
+
+		sk = accept(lsk, NULL, NULL);
+		if (sk < 0) {
+			test_error("accept()");
+		} else {
+			if (fault(F_TIMEOUT))
+				test_fail("%s: accepted", tst_name);
+		}
+	}
+
+	close(lsk);
+
+	if (!cnt_name)
+		goto out;
+
+	after_cnt = netstat_get_one(cnt_name, NULL);
+
+	if (after_cnt <= before_cnt) {
+		test_fail("%s: %s counter did not increase: %zu <= %zu",
+				tst_name, cnt_name, after_cnt, before_cnt);
+	} else {
+		test_ok("%s: counter %s increased %zu => %zu",
+			tst_name, cnt_name, before_cnt, after_cnt);
+	}
+
+out:
+	synchronize_threads(); /* close() */
+	if (sk > 0)
+		close(sk);
+}
+
+static void *server_fn(void *arg)
+{
+	union tcp_addr wrong_addr, network_addr;
+	unsigned port = test_server_port;
+
+	if (inet_pton(TEST_FAMILY, TEST_WRONG_IP, &wrong_addr) != 1)
+		test_error("Can't convert ip address %s", TEST_WRONG_IP);
+
+	try_accept("Non-AO server + AO client", port++, NULL,
+		this_ip_dest, -1, 100, 100, "TCPAOKeyNotFound", F_TIMEOUT);
+
+	try_accept("AO server + Non-AO client", port++, "password",
+		this_ip_dest, -1, 100, 100, "TCPAORequired", F_TIMEOUT);
+
+	try_accept("Wrong password", port++, "password2",
+		this_ip_dest, -1, 100, 100, "TCPAOBad", F_TIMEOUT);
+
+	try_accept("Wrong rcv id", port++, "password",
+		this_ip_dest, -1, 100, 101, "TCPAOKeyNotFound", F_TIMEOUT);
+
+	try_accept("Wrong snd id", port++, "password",
+		this_ip_dest, -1, 101, 100, "TCPAOGood", F_TIMEOUT);
+
+	try_accept("Server: Wrong addr", port++, "password",
+		wrong_addr, -1, 100, 100, "TCPAOKeyNotFound", F_TIMEOUT);
+
+	try_accept("Client: Wrong addr", port++, NULL,
+		this_ip_dest, -1, 100, 100, NULL, F_TIMEOUT);
+
+	try_accept("rcv id != snd id", port++, "password",
+		this_ip_dest, -1, 200, 100, "TCPAOGood", 0);
+
+	if (inet_pton(TEST_FAMILY, TEST_NETWORK, &network_addr) != 1)
+		test_error("Can't convert ip address %s", TEST_NETWORK);
+
+	try_accept("Server: prefix match", port++, "password",
+		network_addr, 16, 100, 100, "TCPAOGood", 0);
+
+	try_accept("Client: prefix match", port++, "password",
+		this_ip_dest, -1, 100, 100, "TCPAOGood", 0);
+
+	/* client exits */
+	synchronize_threads();
+	return NULL;
+}
+
+static void try_connect(const char *tst_name, unsigned port,
+			const char *pwd, union tcp_addr addr, uint8_t prefix,
+			uint8_t sndid, uint8_t rcvid, fault_t inj)
+{
+	time_t timeout;
+	int sk, ret;
+
+	sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
+	if (sk < 0)
+		test_error("socket()");
+
+	if (pwd && test_set_ao(sk, pwd, 0, addr, prefix, sndid, rcvid))
+		test_error("setsockopt(TCP_AO)");
+
+	synchronize_threads(); /* preparations done */
+
+	timeout = fault(F_TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC;
+	ret = _test_connect_socket(sk, this_ip_dest, port, timeout);
+
+	if (ret < 0) {
+		if (fault(F_KEYREJECT) && ret == -EKEYREJECTED) {
+			test_ok("%s: connect() was prevented", tst_name);
+			goto out;
+		} else if (ret == -ECONNREFUSED &&
+				(fault(F_TIMEOUT) || fault(F_KEYREJECT))) {
+			test_ok("%s: refused to connect", tst_name);
+			goto out;
+		} else {
+			test_error("%s: connect() returned %d", tst_name, ret);
+		}
+	}
+
+	if (ret == 0) {
+		if (fault(F_TIMEOUT))
+			test_ok("%s", tst_name);
+		else
+			test_fail("%s: failed to connect()", tst_name);
+	} else {
+		if (fault(F_TIMEOUT) || fault(F_KEYREJECT))
+			test_fail("%s: connected", tst_name);
+		else
+			test_ok("%s: connected", tst_name);
+	}
+
+out:
+	synchronize_threads(); /* close() */
+
+	if (ret > 0)
+		close(sk);
+}
+
+static void *client_fn(void *arg)
+{
+	union tcp_addr wrong_addr, network_addr;
+	unsigned port = test_server_port;
+
+	if (inet_pton(TEST_FAMILY, TEST_WRONG_IP, &wrong_addr) != 1)
+		test_error("Can't convert ip address %s", TEST_WRONG_IP);
+
+	try_connect("Non-AO server + AO client", port++, "password",
+			this_ip_dest, -1, 100, 100, F_TIMEOUT);
+
+	try_connect("AO server + Non-AO client", port++, NULL,
+			this_ip_dest, -1, 100, 100, F_TIMEOUT);
+
+	try_connect("Wrong password", port++, "password",
+			this_ip_dest, -1, 100, 100, F_TIMEOUT);
+
+	try_connect("Wrong rcv id", port++, "password",
+			this_ip_dest, -1, 100, 100, F_TIMEOUT);
+
+	try_connect("Wrong snd id", port++, "password",
+			this_ip_dest, -1, 100, 100, F_TIMEOUT);
+
+	try_connect("Server: Wrong addr", port++, "password",
+			this_ip_dest, -1, 100, 100, F_TIMEOUT);
+
+	try_connect("Client: Wrong addr", port++, "password",
+			wrong_addr, -1, 100, 100, F_KEYREJECT);
+
+	try_connect("rcv id != snd id", port++, "password",
+			this_ip_dest, -1, 100, 200, 0);
+
+	if (inet_pton(TEST_FAMILY, TEST_NETWORK, &network_addr) != 1)
+		test_error("Can't convert ip address %s", TEST_NETWORK);
+
+	try_connect("Server: prefix match", port++, "password",
+			this_ip_dest, -1, 100, 100, 0);
+
+	try_connect("Client: prefix match", port++, "password",
+			network_addr, 16, 100, 100, 0);
+
+	return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+	test_init(20, server_fn, client_fn);
+	return 0;
+}
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 29/31] selftest/tcp-ao: Add test for TCP-AO add setsockopt() command
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (27 preceding siblings ...)
  2022-08-18 17:00 ` [PATCH 28/31] selftest/tcp-ao: Add a test for MKT matching Dmitry Safonov
@ 2022-08-18 17:00 ` Dmitry Safonov
  2022-08-18 17:00 ` [PATCH 30/31] selftests/tcp-ao: Add TCP-AO + TCP-MD5 + no sign listen socket tests Dmitry Safonov
                   ` (2 subsequent siblings)
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 17:00 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Verify corner-cases for UAPI.
Sample output:
> # ./setsockopt-closed_ipv6
> 1..16
> # 9508[lib/setup.c:173] rand seed 1643819055
> TAP version 13
> ok 1 minimum size
> ok 2 extended size
> ok 3 bad algo
> ok 4 bad ao flags
> ok 5 empty prefix
> ok 6 prefix, any addr
> ok 7 no prefix, any addr
> ok 8 too short prefix
> ok 9 too big prefix
> ok 10 too big maclen
> ok 11 bad key flags
> ok 12 too big keylen
> not ok 13 duplicate: full copy: setsockopt() was expected to fail with 17
> ok 14 duplicate: any addr key on the socket
> ok 15 duplicate: add any addr key
> not ok 16 duplicate: add any addr for the same subnet: setsockopt() was expected to fail with 17

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 tools/testing/selftests/net/tcp_ao/Makefile   |   3 +-
 .../selftests/net/tcp_ao/setsockopt-closed.c  | 191 ++++++++++++++++++
 2 files changed, 193 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/net/tcp_ao/setsockopt-closed.c

diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile
index 5064e34ebe38..a001dc2aed4e 100644
--- a/tools/testing/selftests/net/tcp_ao/Makefile
+++ b/tools/testing/selftests/net/tcp_ao/Makefile
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
-TEST_BOTH_AF := connect icmps-discard icmps-accept connect-deny
+TEST_BOTH_AF := connect icmps-discard icmps-accept connect-deny \
+		setsockopt-closed
 
 TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4)
 TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6)
diff --git a/tools/testing/selftests/net/tcp_ao/setsockopt-closed.c b/tools/testing/selftests/net/tcp_ao/setsockopt-closed.c
new file mode 100644
index 000000000000..be2cbc407f60
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/setsockopt-closed.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dmitry Safonov <dima@arista.com> */
+#include <inttypes.h>
+#include "../../../../include/linux/kernel.h"
+#include "aolib.h"
+
+static void clean_ao(int sk, struct tcp_ao *ao)
+{
+	struct tcp_ao_del ao_del = {};
+
+	ao_del.tcpa_sndid = ao->tcpa_sndid;
+	ao_del.tcpa_rcvid = ao->tcpa_rcvid;
+	ao_del.tcpa_prefix = ao->tcpa_prefix;
+	memcpy(&ao_del.tcpa_addr, &ao->tcpa_addr, sizeof(ao->tcpa_addr));
+
+	if (setsockopt(sk, IPPROTO_TCP, TCP_AO_DEL, &ao_del, sizeof(ao_del)))
+		test_error("setsockopt(TCP_AO_DEL) failed to clean");
+	close(sk);
+}
+
+static void setsockopt_checked(int sk, int optname, struct tcp_ao *ao,
+			       int err, const char *tst)
+{
+	int ret;
+
+	errno = 0;
+	ret = setsockopt(sk, IPPROTO_TCP, optname, ao, sizeof(*ao));
+	if (ret == -1) {
+		if (errno == err) {
+			test_ok("%s", tst);
+			return;
+		}
+		test_fail("%s: setsockopt() returned %d", tst, err);
+		return;
+	}
+
+	if (err) {
+		test_fail("%s: setsockopt() was expected to fail with %d", tst, err);
+	} else {
+		test_ok("%s", tst);
+		test_verify_socket_ao(sk, ao);
+	}
+	clean_ao(sk, ao);
+}
+
+static int prepare_defs(struct tcp_ao *ao)
+{
+	int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
+
+	if (sk < 0)
+		test_error("socket()");
+
+	if (test_prepare_def_ao(ao, "password", 0, this_ip_dest, -1, 100, 100))
+		test_error("prepare default tcp_ao");
+
+	return sk;
+}
+
+static void test_extend(void)
+{
+	struct tcp_ao ao;
+	struct {
+		struct tcp_ao ao;
+		char *extend[100];
+	} ao_big = {};
+	int ret, sk;
+
+	sk = prepare_defs(&ao);
+	errno = 0;
+	ret = setsockopt(sk, IPPROTO_TCP, TCP_AO,
+			&ao, offsetof(struct tcp_ao, tcpa_key));
+	if (!ret) {
+		test_fail("minminum size: accepted invalid size");
+		clean_ao(sk, &ao);
+	} else if (errno != EINVAL) {
+		test_fail("minminum size: failed with %d", errno);
+	} else {
+		test_ok("minimum size");
+	}
+
+	sk = prepare_defs(&ao_big.ao);
+	errno = 0;
+	ret = setsockopt(sk, IPPROTO_TCP, TCP_AO, &ao_big.ao, sizeof(ao_big));
+	if (ret) {
+		test_fail("extended size: returned %d", ret);
+	} else {
+		test_ok("extended size");
+		clean_ao(sk, &ao_big.ao);
+	}
+}
+
+static void einval_tests(void)
+{
+	struct tcp_ao ao;
+	int sk;
+
+	sk = prepare_defs(&ao);
+	strcpy(ao.tcpa_alg_name, "imaginary hash algo");
+	setsockopt_checked(sk, TCP_AO, &ao, ENOENT, "bad algo");
+
+	sk = prepare_defs(&ao);
+	ao.tcpa_flags = (uint16_t)(-1);
+	setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "bad ao flags");
+
+	sk = prepare_defs(&ao);
+	ao.tcpa_prefix = 0;
+	setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "empty prefix");
+
+	sk = prepare_defs(&ao);
+	ao.tcpa_prefix = 32;
+	memcpy(&ao.tcpa_addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY));
+	setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "prefix, any addr");
+
+	sk = prepare_defs(&ao);
+	ao.tcpa_prefix = 0;
+	memcpy(&ao.tcpa_addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY));
+	setsockopt_checked(sk, TCP_AO, &ao, 0, "no prefix, any addr");
+
+	sk = prepare_defs(&ao);
+	ao.tcpa_prefix = 2;
+	setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "too short prefix");
+
+	sk = prepare_defs(&ao);
+	ao.tcpa_prefix = 129;
+	setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "too big prefix");
+
+	sk = prepare_defs(&ao);
+	ao.tcpa_maclen = 100;
+	setsockopt_checked(sk, TCP_AO, &ao, EMSGSIZE, "too big maclen");
+
+	sk = prepare_defs(&ao);
+	ao.tcpa_keyflags = (uint8_t)(-1);
+	setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "bad key flags");
+
+	sk = prepare_defs(&ao);
+	ao.tcpa_keylen = TCP_AO_MAXKEYLEN + 1;
+	setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "too big keylen");
+}
+
+static void duplicate_tests(void)
+{
+	union tcp_addr network_dup;
+	struct tcp_ao ao, ao2;
+	int sk;
+
+	sk = prepare_defs(&ao);
+	if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &ao, sizeof(ao)))
+		test_error("setsockopt()");
+	setsockopt_checked(sk, TCP_AO, &ao, EEXIST, "duplicate: full copy");
+
+	sk = prepare_defs(&ao);
+	ao2 = ao;
+	memcpy(&ao2.tcpa_addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY));
+	ao2.tcpa_prefix = 0;
+	if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &ao2, sizeof(ao)))
+		test_error("setsockopt()");
+	setsockopt_checked(sk, TCP_AO, &ao, EEXIST, "duplicate: any addr key on the socket");
+
+	sk = prepare_defs(&ao);
+	if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &ao, sizeof(ao)))
+		test_error("setsockopt()");
+	memcpy(&ao.tcpa_addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY));
+	ao.tcpa_prefix = 0;
+	setsockopt_checked(sk, TCP_AO, &ao, EEXIST, "duplicate: add any addr key");
+
+
+	if (inet_pton(TEST_FAMILY, TEST_NETWORK, &network_dup) != 1)
+		test_error("Can't convert ip address %s", TEST_NETWORK);
+	sk = prepare_defs(&ao);
+	if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &ao, sizeof(ao)))
+		test_error("setsockopt()");
+	if (test_prepare_def_ao(&ao, "password", 0, network_dup, 16, 100, 100))
+		test_error("prepare default tcp_ao");
+	setsockopt_checked(sk, TCP_AO, &ao, EEXIST, "duplicate: add any addr for the same subnet");
+}
+
+
+static void *client_fn(void *arg)
+{
+	test_extend();
+	einval_tests();
+	duplicate_tests();
+
+	return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+	test_init(16, client_fn, NULL);
+	return 0;
+}
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 30/31] selftests/tcp-ao: Add TCP-AO + TCP-MD5 + no sign listen socket tests
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (28 preceding siblings ...)
  2022-08-18 17:00 ` [PATCH 29/31] selftest/tcp-ao: Add test for TCP-AO add setsockopt() command Dmitry Safonov
@ 2022-08-18 17:00 ` Dmitry Safonov
  2022-08-18 17:00 ` [PATCH 31/31] selftests/aolib: Add test/benchmark for removing MKTs Dmitry Safonov
  2022-08-21 20:34 ` [PATCH 00/31] net/tcp: Add TCP-AO support Leonard Crestez
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 17:00 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

The test plan was (most of tests have all 3 client types):
1. TCP-AO listen (INADDR_ANY)
2. TCP-MD5 listen (INADDR_ANY)
3. non-signed listen (INADDR_ANY)
4. TCP-AO + TCP-MD5 listen (prefix)
5. TCP-AO subprefix add failure [checked in setsockopt-closed.c]
6. TCP-AO out of prefix connect [checked in connect-deny.c]
7. TCP-AO + TCP-MD5 on connect()
8. TCP-AO intersect with TCP-MD5 failure
9. Established TCP-AO: add TCP-MD5 key
10. Established TCP-MD5: add TCP-AO key
11. Established non-signed: add TCP-AO key

Output produced:
1..42
TAP version 13
ok 1 AO server (INADDR_ANY): AO client: connected
ok 2 AO server (INADDR_ANY): AO client: counter TCPAOGood increased 0 => 2
ok 3 AO server (INADDR_ANY): MD5 client
ok 4 AO server (INADDR_ANY): MD5 client: counter TCPMD5Unexpected increased 0 => 1
ok 5 AO server (INADDR_ANY): no sign client: counter TCPAORequired increased 0 => 1
ok 6 AO server (INADDR_ANY): unsigned client
ok 7 MD5 server (INADDR_ANY): AO client: counter TCPAOKeyNotFound increased 0 => 1
ok 8 MD5 server (INADDR_ANY): AO client
ok 9 MD5 server (INADDR_ANY): MD5 client: connected
ok 10 MD5 server (INADDR_ANY): no sign client: counter TCPMD5NotFound increased 0 => 1
ok 11 MD5 server (INADDR_ANY): no sign client
ok 12 no sign server: AO client
ok 13 no sign server: AO client: counter TCPAOKeyNotFound increased 1 => 2
ok 14 no sign server: MD5 client
ok 15 no sign server: MD5 client: counter TCPMD5Unexpected increased 1 => 2
ok 16 no sign server: no sign client: connected
ok 17 no sign server: no sign client: counter CurrEstab increased 0 => 1
ok 18 AO+MD5 server: AO client (matching): connected
ok 19 AO+MD5 server: AO client (matching): counter TCPAOGood increased 4 => 6
ok 20 AO+MD5 server: AO client (misconfig, matching MD5)
ok 21 AO+MD5 server: AO client (misconfig, matching MD5): counter TCPAOKeyNotFound increased 2 => 3
ok 22 AO+MD5 server: AO client (misconfig, non-matching): counter TCPAOKeyNotFound increased 3 => 4
ok 23 AO+MD5 server: AO client (misconfig, non-matching)
ok 24 AO+MD5 server: MD5 client (matching): connected
ok 25 AO+MD5 server: MD5 client (misconfig, matching AO)
ok 26 AO+MD5 server: MD5 client (misconfig, matching AO): counter TCPMD5Unexpected increased 2 => 3
ok 27 AO+MD5 server: MD5 client (misconfig, non-matching): counter TCPMD5Unexpected increased 3 => 4
ok 28 AO+MD5 server: MD5 client (misconfig, non-matching)
ok 29 AO+MD5 server: no sign client (unmatched): connected
ok 30 AO+MD5 server: no sign client (unmatched): counter CurrEstab increased 0 => 1
ok 31 AO+MD5 server: no sign client (misconfig, matching AO)
ok 32 AO+MD5 server: no sign client (misconfig, matching AO): counter TCPAORequired increased 1 => 2
ok 33 AO+MD5 server: no sign client (misconfig, matching MD5)
ok 34 AO+MD5 server: no sign client (misconfig, matching MD5): counter TCPMD5NotFound increased 1 => 2
ok 35 AO+MD5 server: client with both [TCP-MD5] and TCP-AO keys: connect() was prevented
ok 36 AO+MD5 server: client with both TCP-MD5 and [TCP-AO] keys: connect() was prevented
ok 37 TCP-AO established: add TCP-MD5 key: postfailed as expected
ok 38 TCP-AO established: add TCP-MD5 key: counter TCPAOGood increased 7 => 9
ok 39 TCP-MD5 established: add TCP-AO key: postfailed as expected
ok 40 non-signed established: add TCP-AO key: postfailed as expected
ok 41 non-signed established: add TCP-AO key: counter CurrEstab increased 0 => 1
ok 42 TCP-AO key intersects with TCP-MD5 key: prefailed as expected

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 tools/testing/selftests/net/tcp_ao/Makefile   |   2 +-
 .../selftests/net/tcp_ao/unsigned-md5.c       | 483 ++++++++++++++++++
 2 files changed, 484 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/net/tcp_ao/unsigned-md5.c

diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile
index a001dc2aed4e..da44966f3687 100644
--- a/tools/testing/selftests/net/tcp_ao/Makefile
+++ b/tools/testing/selftests/net/tcp_ao/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 TEST_BOTH_AF := connect icmps-discard icmps-accept connect-deny \
-		setsockopt-closed
+		setsockopt-closed unsigned-md5
 
 TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4)
 TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6)
diff --git a/tools/testing/selftests/net/tcp_ao/unsigned-md5.c b/tools/testing/selftests/net/tcp_ao/unsigned-md5.c
new file mode 100644
index 000000000000..d62c47617dbf
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/unsigned-md5.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dmitry Safonov <dima@arista.com> */
+#include <inttypes.h>
+#include "aolib.h"
+#include "../../../../include/linux/bits.h"
+
+typedef uint8_t fault_t;
+#define F_TIMEOUT	1
+#define F_KEYREJECT	2
+#define F_PREINSTALL	3
+#define F_POSTINSTALL	4
+
+#define fault(type)	(inj == type)
+
+static const char *md5_password = "Some evil genius, enemy to mankind, must have been the first contriver.";
+static const char *ao_password = "In this hour, I do not believe that any darkness will endure.";
+
+static union tcp_addr client2;
+static union tcp_addr client3;
+
+static int test_set_md5(int sk, const union tcp_addr in_addr, uint8_t prefix)
+{
+	size_t pwd_len = strlen(md5_password);
+	struct tcp_md5sig md5sig = {};
+#ifdef IPV6_TEST
+	struct sockaddr_in6 addr = {
+		.sin6_family	= AF_INET6,
+		.sin6_port	= 0,
+		.sin6_addr	= in_addr.a6,
+	};
+#else
+	struct sockaddr_in addr = {
+		.sin_family	= AF_INET,
+		.sin_port	= 0,
+		.sin_addr	= in_addr.a4,
+	};
+#endif
+
+	if (prefix > DEFAULT_TEST_PREFIX)
+		prefix = DEFAULT_TEST_PREFIX;
+
+	md5sig.tcpm_keylen = pwd_len;
+	memcpy(md5sig.tcpm_key, md5_password, pwd_len);
+	md5sig.tcpm_flags = TCP_MD5SIG_FLAG_PREFIX;
+	md5sig.tcpm_prefixlen = prefix;
+	memcpy(&md5sig.tcpm_addr, &addr, sizeof(addr));
+
+	return setsockopt(sk, IPPROTO_TCP, TCP_MD5SIG_EXT,
+			  &md5sig, sizeof(md5sig));
+}
+
+static void try_accept(const char *tst_name, unsigned port,
+		       union tcp_addr *md5_addr, uint8_t md5_prefix,
+		       union tcp_addr *ao_addr, uint8_t ao_prefix,
+		       uint8_t sndid, uint8_t rcvid, const char *cnt_name,
+		       fault_t inj)
+{
+	uint64_t before_cnt, after_cnt;
+	int lsk, err, sk = 0;
+	time_t timeout;
+
+	lsk = test_listen_socket(this_ip_addr, port, 1);
+
+	if (md5_addr && test_set_md5(lsk, *md5_addr, md5_prefix))
+		test_error("setsockopt(TCP_MD5SIG_EXT)");
+
+	if (ao_addr && test_set_ao(lsk, ao_password, 0, *ao_addr,
+				   ao_prefix, sndid, rcvid))
+		test_error("setsockopt(TCP_AO)");
+
+	if (cnt_name)
+		before_cnt = netstat_get_one(cnt_name, NULL);
+
+	synchronize_threads(); /* preparations done */
+
+	timeout = fault(F_TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC;
+	err = test_wait_fd(lsk, timeout, 0);
+	if (err < 0)
+		test_error("test_wait_fd()");
+	else if (!err) {
+		if (!fault(F_TIMEOUT))
+			test_fail("timeouted for accept()");
+	} else {
+		if (fault(F_TIMEOUT))
+			test_fail("ready to accept");
+
+		sk = accept(lsk, NULL, NULL);
+		if (sk < 0) {
+			test_error("accept()");
+		} else {
+			if (fault(F_TIMEOUT))
+				test_fail("%s: accepted", tst_name);
+		}
+	}
+
+	close(lsk);
+
+	if (!cnt_name)
+		goto out;
+
+	after_cnt = netstat_get_one(cnt_name, NULL);
+
+	if (after_cnt <= before_cnt) {
+		test_fail("%s: %s counter did not increase: %zu <= %zu",
+				tst_name, cnt_name, after_cnt, before_cnt);
+	} else {
+		test_ok("%s: counter %s increased %zu => %zu",
+			tst_name, cnt_name, before_cnt, after_cnt);
+	}
+
+out:
+	synchronize_threads(); /* close() */
+	if (sk > 0)
+		close(sk);
+}
+
+static void server_add_routes(void)
+{
+	int family = TEST_FAMILY;
+
+	synchronize_threads(); /* client_add_ips() */
+
+	if (ip_route_add(veth_name, family, this_ip_addr, client2))
+		test_error("Failed to add route");
+	if (ip_route_add(veth_name, family, this_ip_addr, client3))
+		test_error("Failed to add route");
+}
+
+static void server_add_fail_tests(unsigned *port)
+{
+	union tcp_addr addr_any = {};
+
+	try_accept("TCP-AO established: add TCP-MD5 key", (*port)++, NULL, 0,
+		   &addr_any, 0, 100, 100, "TCPAOGood", 0);
+	try_accept("TCP-MD5 established: add TCP-AO key", (*port)++, &addr_any, 0,
+		   NULL, 0, 0, 0, NULL, 0);
+	try_accept("non-signed established: add TCP-AO key", (*port)++, NULL, 0,
+		   NULL, 0, 0, 0, "CurrEstab", 0);
+}
+
+static void *server_fn(void *arg)
+{
+	unsigned port = test_server_port;
+	union tcp_addr addr_any = {};
+
+	server_add_routes();
+
+	try_accept("AO server (INADDR_ANY): AO client", port++, NULL, 0,
+		   &addr_any, 0, 100, 100, "TCPAOGood", 0);
+	try_accept("AO server (INADDR_ANY): MD5 client", port++, NULL, 0,
+		   &addr_any, 0, 100, 100, "TCPMD5Unexpected", F_TIMEOUT);
+	try_accept("AO server (INADDR_ANY): no sign client", port++, NULL, 0,
+		   &addr_any, 0, 100, 100, "TCPAORequired", F_TIMEOUT);
+
+	try_accept("MD5 server (INADDR_ANY): AO client", port++, &addr_any, 0,
+		   NULL, 0, 0, 0, "TCPAOKeyNotFound", F_TIMEOUT);
+	try_accept("MD5 server (INADDR_ANY): MD5 client", port++, &addr_any, 0,
+		   NULL, 0, 0, 0, NULL, 0);
+	try_accept("MD5 server (INADDR_ANY): no sign client", port++, &addr_any, 0,
+		   NULL, 0, 0, 0, "TCPMD5NotFound", F_TIMEOUT);
+
+	try_accept("no sign server: AO client", port++, NULL, 0,
+		   NULL, 0, 0, 0, "TCPAOKeyNotFound", F_TIMEOUT);
+	try_accept("no sign server: MD5 client", port++, NULL, 0,
+		   NULL, 0, 0, 0, "TCPMD5Unexpected", F_TIMEOUT);
+	try_accept("no sign server: no sign client", port++, NULL, 0,
+		   NULL, 0, 0, 0, "CurrEstab", 0);
+
+	try_accept("AO+MD5 server: AO client (matching)", port++,
+		&this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX,
+		100, 100, "TCPAOGood", 0);
+	try_accept("AO+MD5 server: AO client (misconfig, matching MD5)", port++,
+		&this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX,
+		100, 100, "TCPAOKeyNotFound", F_TIMEOUT);
+	try_accept("AO+MD5 server: AO client (misconfig, non-matching)", port++,
+		&this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX,
+		100, 100, "TCPAOKeyNotFound", F_TIMEOUT);
+	try_accept("AO+MD5 server: MD5 client (matching)", port++,
+		&this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX,
+		100, 100, NULL, 0);
+	try_accept("AO+MD5 server: MD5 client (misconfig, matching AO)", port++,
+		&this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX,
+		100, 100, "TCPMD5Unexpected", F_TIMEOUT);
+	try_accept("AO+MD5 server: MD5 client (misconfig, non-matching)", port++,
+		&this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX,
+		100, 100, "TCPMD5Unexpected", F_TIMEOUT);
+	try_accept("AO+MD5 server: no sign client (unmatched)", port++,
+		&this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX,
+		100, 100, "CurrEstab", 0);
+	try_accept("AO+MD5 server: no sign client (misconfig, matching AO)",
+		port++, &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX,
+		100, 100, "TCPAORequired", F_TIMEOUT);
+	try_accept("AO+MD5 server: no sign client (misconfig, matching MD5)",
+		port++, &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX,
+		100, 100, "TCPMD5NotFound", F_TIMEOUT);
+
+	try_accept("AO+MD5 server: client with both [TCP-MD5] and TCP-AO keys",
+		port++, &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX,
+		100, 100, NULL, F_TIMEOUT);
+	try_accept("AO+MD5 server: client with both TCP-MD5 and [TCP-AO] keys",
+		port++, &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX,
+		100, 100, NULL, F_TIMEOUT);
+
+	server_add_fail_tests(&port);
+
+	/* client exits */
+	synchronize_threads();
+	return NULL;
+}
+
+static int client_bind(int sk, union tcp_addr bind_addr)
+{
+#ifdef IPV6_TEST
+	struct sockaddr_in6 addr = {
+		.sin6_family	= AF_INET6,
+		.sin6_port	= 0,
+		.sin6_addr	= bind_addr.a6,
+	};
+#else
+	struct sockaddr_in addr = {
+		.sin_family	= AF_INET,
+		.sin_port	= 0,
+		.sin_addr	= bind_addr.a4,
+	};
+#endif
+	return bind(sk, &addr, sizeof(addr));
+}
+
+static void try_connect(const char *tst_name, unsigned port,
+		       union tcp_addr *md5_addr, uint8_t md5_prefix,
+		       union tcp_addr *ao_addr, uint8_t ao_prefix,
+		       uint8_t sndid, uint8_t rcvid, fault_t inj,
+		       union tcp_addr *bind_addr)
+{
+	time_t timeout;
+	int sk, ret;
+
+	sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
+	if (sk < 0)
+		test_error("socket()");
+
+	if (bind_addr && client_bind(sk, *bind_addr))
+		test_error("bind()");
+
+	if (md5_addr && test_set_md5(sk, *md5_addr, md5_prefix))
+		test_error("setsockopt(TCP_MD5SIG_EXT)");
+
+	if (ao_addr && test_set_ao(sk, ao_password, 0, *ao_addr,
+				   ao_prefix, sndid, rcvid))
+		test_error("setsockopt(TCP_AO)");
+
+	synchronize_threads(); /* preparations done */
+
+	timeout = fault(F_TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC;
+	ret = _test_connect_socket(sk, this_ip_dest, port, timeout);
+
+	if (ret < 0) {
+		if (fault(F_KEYREJECT) && ret == -EKEYREJECTED) {
+			test_ok("%s: connect() was prevented", tst_name);
+			goto out;
+		} else if (ret == -ECONNREFUSED &&
+				(fault(F_TIMEOUT) || fault(F_KEYREJECT))) {
+			test_ok("%s: refused to connect", tst_name);
+			goto out;
+		} else {
+			test_error("%s: connect() returned %d", tst_name, ret);
+		}
+	}
+
+	if (ret == 0) {
+		if (fault(F_TIMEOUT))
+			test_ok("%s", tst_name);
+		else
+			test_fail("%s: failed to connect()", tst_name);
+	} else {
+		if (fault(F_TIMEOUT) || fault(F_KEYREJECT))
+			test_fail("%s: connected", tst_name);
+		else
+			test_ok("%s: connected", tst_name);
+	}
+
+out:
+	synchronize_threads(); /* close() */
+	/* _test_connect_socket() cleans up on failure */
+	if (ret > 0)
+		close(sk);
+}
+
+#define PREINSTALL_MD5	BIT(1)
+#define POSTINSTALL_MD5	BIT(2)
+#define PREINSTALL_AO	BIT(3)
+#define POSTINSTALL_AO	BIT(4)
+
+static void try_to_add(const char *tst_name, unsigned port,
+		       unsigned strategy,
+		       union tcp_addr md5_addr, uint8_t md5_prefix,
+		       union tcp_addr ao_addr, uint8_t ao_prefix,
+		       uint8_t sndid, uint8_t rcvid, fault_t inj)
+{
+	time_t timeout;
+	int sk, ret;
+
+	sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
+	if (sk < 0)
+		test_error("socket()");
+
+	if (client_bind(sk, this_ip_addr))
+		test_error("bind()");
+
+	if (strategy & PREINSTALL_MD5) {
+		if (test_set_md5(sk, md5_addr, md5_prefix))
+			test_error("setsockopt(TCP_MD5SIG_EXT)");
+	}
+
+	if (strategy & PREINSTALL_AO) {
+		if (test_set_ao(sk, ao_password, 0, ao_addr,
+				ao_prefix, sndid, rcvid)) {
+			if (fault(F_PREINSTALL)) {
+				test_ok("%s: prefailed as expected", tst_name);
+				goto out_no_sync;
+			} else {
+				test_error("setsockopt(TCP_AO)");
+			}
+		} else if (fault(F_PREINSTALL)) {
+			test_fail("%s: setsockopt()s were expected to fail", tst_name);
+			goto out_no_sync;
+		}
+	}
+
+	synchronize_threads(); /* preparations done */
+
+	timeout = fault(F_TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC;
+	ret = _test_connect_socket(sk, this_ip_dest, port, timeout);
+
+	if (ret <= 0) {
+		test_error("%s: connect() returned %d", tst_name, ret);
+		goto out;
+	}
+
+	if (strategy & POSTINSTALL_MD5) {
+		if (test_set_md5(sk, md5_addr, md5_prefix)) {
+			if (fault(F_POSTINSTALL)) {
+				test_ok("%s: postfailed as expected", tst_name);
+				goto out;
+			} else {
+				test_error("setsockopt(TCP_MD5SIG_EXT)");
+			}
+		} else if (fault(F_POSTINSTALL)) {
+			test_fail("%s: post setsockopt() was expected to fail", tst_name);
+			goto out;
+		}
+	}
+
+	if (strategy & POSTINSTALL_AO) {
+		if (test_set_ao(sk, ao_password, 0, ao_addr,
+				ao_prefix, sndid, rcvid)) {
+			if (fault(F_POSTINSTALL)) {
+				test_ok("%s: postfailed as expected", tst_name);
+				goto out;
+			} else {
+				test_error("setsockopt(TCP_AO)");
+			}
+		} else if (fault(F_POSTINSTALL)) {
+			test_fail("%s: post setsockopt() was expected to fail", tst_name);
+			goto out;
+		}
+	}
+
+out:
+	synchronize_threads(); /* close() */
+out_no_sync:
+	/* _test_connect_socket() cleans up on failure */
+	if (ret > 0)
+		close(sk);
+}
+
+static void client_add_ip(union tcp_addr *client, const char *ip)
+{
+	int family = TEST_FAMILY;
+
+	if (inet_pton(family, ip, client) != 1)
+		test_error("Can't convert ip address %s", ip);
+
+	if (ip_addr_add(veth_name, family, *client, TEST_PREFIX))
+		test_error("Failed to add ip address");
+	if (ip_route_add(veth_name, family, *client, this_ip_dest))
+		test_error("Failed to add route");
+}
+
+static void client_add_ips(void)
+{
+	client_add_ip(&client2, __TEST_CLIENT_IP(2));
+	client_add_ip(&client3, __TEST_CLIENT_IP(3));
+	synchronize_threads(); /* server_add_routes() */
+}
+
+static void client_add_fail_tests(unsigned *port)
+{
+	try_to_add("TCP-AO established: add TCP-MD5 key",
+		   (*port)++, POSTINSTALL_MD5 | PREINSTALL_AO,
+		   this_ip_dest, TEST_PREFIX, this_ip_dest, TEST_PREFIX,
+		   100, 100, F_POSTINSTALL);
+	try_to_add("TCP-MD5 established: add TCP-AO key",
+		   (*port)++, PREINSTALL_MD5 | POSTINSTALL_AO,
+		   this_ip_dest, TEST_PREFIX, this_ip_dest, TEST_PREFIX,
+		   100, 100, F_POSTINSTALL);
+	try_to_add("non-signed established: add TCP-AO key",
+		   (*port)++, POSTINSTALL_AO,
+		   this_ip_dest, TEST_PREFIX, this_ip_dest, TEST_PREFIX,
+		   100, 100, F_POSTINSTALL);
+
+	try_to_add("TCP-AO key intersects with TCP-MD5 key",
+		   (*port), PREINSTALL_MD5 | PREINSTALL_AO,
+		   this_ip_addr, TEST_PREFIX, this_ip_addr, TEST_PREFIX,
+		   100, 100, F_PREINSTALL);
+}
+
+static void *client_fn(void *arg)
+{
+	unsigned port = test_server_port;
+	union tcp_addr addr_any = {};
+
+	client_add_ips();
+
+	try_connect("AO server (INADDR_ANY): AO client", port++, NULL, 0,
+		    &addr_any, 0, 100, 100, 0, &this_ip_addr);
+	try_connect("AO server (INADDR_ANY): MD5 client", port++, &addr_any, 0,
+		    NULL, 0, 100, 100, F_TIMEOUT, &this_ip_addr);
+	try_connect("AO server (INADDR_ANY): unsigned client", port++, NULL, 0,
+		    NULL, 0, 100, 100, F_TIMEOUT, &this_ip_addr);
+
+	try_connect("MD5 server (INADDR_ANY): AO client", port++, NULL, 0,
+		   &addr_any, 0, 100, 100, F_TIMEOUT, &this_ip_addr);
+	try_connect("MD5 server (INADDR_ANY): MD5 client", port++, &addr_any, 0,
+		   NULL, 0, 100, 100, 0, &this_ip_addr);
+	try_connect("MD5 server (INADDR_ANY): no sign client", port++, NULL, 0,
+		   NULL, 0, 100, 100, F_TIMEOUT, &this_ip_addr);
+
+	try_connect("no sign server: AO client", port++, NULL, 0,
+		   &addr_any, 0, 100, 100, F_TIMEOUT, &this_ip_addr);
+	try_connect("no sign server: MD5 client", port++, &addr_any, 0,
+		   NULL, 0, 100, 100, F_TIMEOUT, &this_ip_addr);
+	try_connect("no sign server: no sign client", port++, NULL, 0,
+		   NULL, 0, 100, 100, 0, &this_ip_addr);
+
+	try_connect("AO+MD5 server: AO client (matching)", port++, NULL, 0,
+		   &addr_any, 0, 100, 100, 0, &client2);
+	try_connect("AO+MD5 server: AO client (misconfig, matching MD5)",
+		   port++, NULL, 0, &addr_any, 0, 100, 100,
+		   F_TIMEOUT, &this_ip_addr);
+	try_connect("AO+MD5 server: AO client (misconfig, non-matching)",
+		   port++, NULL, 0, &addr_any, 0, 100, 100,
+		   F_TIMEOUT, &client3);
+	try_connect("AO+MD5 server: MD5 client (matching)", port++, &addr_any, 0,
+		   NULL, 0, 100, 100, 0, &this_ip_addr);
+	try_connect("AO+MD5 server: MD5 client (misconfig, matching AO)",
+		   port++, &addr_any, 0, NULL, 0, 100, 100, F_TIMEOUT, &client2);
+	try_connect("AO+MD5 server: MD5 client (misconfig, non-matching)",
+		   port++, &addr_any, 0, NULL, 0, 100, 100, F_TIMEOUT, &client3);
+	try_connect("AO+MD5 server: no sign client (unmatched)",
+		   port++, NULL, 0, NULL, 0, 100, 100, 0, &client3);
+	try_connect("AO+MD5 server: no sign client (misconfig, matching AO)",
+		   port++, NULL, 0, NULL, 0, 100, 100, F_TIMEOUT, &client2);
+	try_connect("AO+MD5 server: no sign client (misconfig, matching MD5)",
+		   port++, NULL, 0, NULL, 0, 100, 100, F_TIMEOUT, &this_ip_addr);
+
+	try_connect("AO+MD5 server: client with both [TCP-MD5] and TCP-AO keys",
+		   port++, &this_ip_addr, TEST_PREFIX,
+		   &client2, TEST_PREFIX, 100, 100, F_KEYREJECT, &this_ip_addr);
+	try_connect("AO+MD5 server: client with both TCP-MD5 and [TCP-AO] keys",
+		   port++, &this_ip_addr, TEST_PREFIX,
+		   &client2, TEST_PREFIX, 100, 100, F_KEYREJECT, &client2);
+
+	client_add_fail_tests(&port);
+
+	return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+	test_init(42, server_fn, client_fn);
+	return 0;
+}
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 31/31] selftests/aolib: Add test/benchmark for removing MKTs
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (29 preceding siblings ...)
  2022-08-18 17:00 ` [PATCH 30/31] selftests/tcp-ao: Add TCP-AO + TCP-MD5 + no sign listen socket tests Dmitry Safonov
@ 2022-08-18 17:00 ` Dmitry Safonov
  2022-08-21 20:34 ` [PATCH 00/31] net/tcp: Add TCP-AO support Leonard Crestez
  31 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-18 17:00 UTC (permalink / raw)
  To: Eric Dumazet, David S. Miller, linux-kernel
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

Sample output:
> 1..36
> # 1106[lib/setup.c:207] rand seed 1660754406
> TAP version 13
> ok 1   Worst case connect       512 keys: min=0ms max=1ms mean=0.583329ms stddev=0.076376
> ok 2 Connect random-search      512 keys: min=0ms max=1ms mean=0.53412ms stddev=0.0516779
> ok 3    Worst case delete       512 keys: min=2ms max=11ms mean=6.04139ms stddev=0.245792
> ok 4        Add a new key       512 keys: min=0ms max=13ms mean=0.673415ms stddev=0.0820618
> ok 5 Remove random-search       512 keys: min=5ms max=9ms mean=6.65969ms stddev=0.258064
> ok 6         Remove async       512 keys: min=0ms max=0ms mean=0.041825ms stddev=0.0204512
> ok 7   Worst case connect       1024 keys: min=0ms max=2ms mean=0.520357ms stddev=0.0721358
> ok 8 Connect random-search      1024 keys: min=0ms max=2ms mean=0.535312ms stddev=0.0517355
> ok 9    Worst case delete       1024 keys: min=5ms max=9ms mean=8.27219ms stddev=0.287614
> ok 10        Add a new key      1024 keys: min=0ms max=1ms mean=0.688121ms stddev=0.0829531
> ok 11 Remove random-search      1024 keys: min=5ms max=9ms mean=8.37649ms stddev=0.289422
> ok 12         Remove async      1024 keys: min=0ms max=0ms mean=0.0457096ms stddev=0.0213798
> ok 13   Worst case connect      2048 keys: min=0ms max=2ms mean=0.748804ms stddev=0.0865335
> ok 14 Connect random-search     2048 keys: min=0ms max=2ms mean=0.782993ms stddev=0.0625697
> ok 15    Worst case delete      2048 keys: min=5ms max=10ms mean=8.23106ms stddev=0.286898
> ok 16        Add a new key      2048 keys: min=0ms max=1ms mean=0.812988ms stddev=0.0901658
> ok 17 Remove random-search      2048 keys: min=8ms max=9ms mean=8.84949ms stddev=0.297481
> ok 18         Remove async      2048 keys: min=0ms max=0ms mean=0.0297223ms stddev=0.0172402
> ok 19   Worst case connect      4096 keys: min=1ms max=5ms mean=1.53352ms stddev=0.123836
> ok 20 Connect random-search     4096 keys: min=1ms max=5ms mean=1.52226ms stddev=0.0872429
> ok 21    Worst case delete      4096 keys: min=5ms max=9ms mean=8.25874ms stddev=0.28738
> ok 22        Add a new key      4096 keys: min=0ms max=3ms mean=1.67382ms stddev=0.129376
> ok 23 Remove random-search      4096 keys: min=5ms max=10ms mean=8.26178ms stddev=0.287433
> ok 24         Remove async      4096 keys: min=0ms max=0ms mean=0.0340009ms stddev=0.0184393
> ok 25   Worst case connect      8192 keys: min=2ms max=4ms mean=2.86208ms stddev=0.169177
> ok 26 Connect random-search     8192 keys: min=2ms max=4ms mean=2.87592ms stddev=0.119915
> ok 27    Worst case delete      8192 keys: min=6ms max=11ms mean=7.55291ms stddev=0.274826
> ok 28        Add a new key      8192 keys: min=1ms max=5ms mean=2.56797ms stddev=0.160249
> ok 29 Remove random-search      8192 keys: min=5ms max=10ms mean=7.14002ms stddev=0.267208
> ok 30         Remove async      8192 keys: min=0ms max=0ms mean=0.0320066ms stddev=0.0178904
> ok 31   Worst case connect      16384 keys: min=5ms max=6ms mean=5.55334ms stddev=0.235655
> ok 32 Connect random-search     16384 keys: min=5ms max=6ms mean=5.52614ms stddev=0.166225
> ok 33    Worst case delete      16384 keys: min=5ms max=11ms mean=7.39109ms stddev=0.271866
> ok 34        Add a new key      16384 keys: min=2ms max=4ms mean=3.35799ms stddev=0.183248
> ok 35 Remove random-search      16384 keys: min=5ms max=8ms mean=6.86078ms stddev=0.261931
> ok 36         Remove async      16384 keys: min=0ms max=0ms mean=0.0302384ms stddev=0.0173892
> # Totals: pass:36 fail:0 xfail:0 xpass:0 skip:0 error:0

From it it's visible that the current simplified approach with
linked-list of MKTs scales quite fine even for thousands of keys.
And that also means that the majority of the time for delete is eaten by
synchronize_rcu() [which I can confirm separately by tracing].

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 tools/testing/selftests/net/tcp_ao/Makefile   |   4 +-
 .../selftests/net/tcp_ao/bench-lookups.c      | 403 ++++++++++++++++++
 2 files changed, 406 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/net/tcp_ao/bench-lookups.c

diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile
index da44966f3687..a4af7d4da169 100644
--- a/tools/testing/selftests/net/tcp_ao/Makefile
+++ b/tools/testing/selftests/net/tcp_ao/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 TEST_BOTH_AF := connect icmps-discard icmps-accept connect-deny \
-		setsockopt-closed unsigned-md5
+		setsockopt-closed unsigned-md5 bench-lookups
 
 TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4)
 TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6)
@@ -46,3 +46,5 @@ $(OUTPUT)/%_ipv6: %.c
 
 $(OUTPUT)/icmps-accept_ipv4: CFLAGS+= -DTEST_ICMPS_ACCEPT
 $(OUTPUT)/icmps-accept_ipv6: CFLAGS+= -DTEST_ICMPS_ACCEPT
+$(OUTPUT)/bench-lookups_ipv4: LDFLAGS+= -lm
+$(OUTPUT)/bench-lookups_ipv6: LDFLAGS+= -lm
diff --git a/tools/testing/selftests/net/tcp_ao/bench-lookups.c b/tools/testing/selftests/net/tcp_ao/bench-lookups.c
new file mode 100644
index 000000000000..41456d85e06a
--- /dev/null
+++ b/tools/testing/selftests/net/tcp_ao/bench-lookups.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Author: Dmitry Safonov <dima@arista.com> */
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "../../../../include/linux/bits.h"
+#include "../../../../include/linux/kernel.h"
+#include "aolib.h"
+
+#define AO_KEY_SZ	300 /* ~= sizeof(struct tcp_ao_key) */
+#define NR_ITERS	100 /* number of times to run gathering statistics */
+
+#ifdef IPV6_TEST
+typedef struct in6_addr ipaddr_t;
+static ipaddr_t get_ipaddr_t(ipaddr_t net, size_t n)
+{
+	ipaddr_t ret = net;
+
+	ret.s6_addr32[3] = htonl(n & (BIT(32) - 1));
+	ret.s6_addr32[2] = htonl((n >> 32) & (BIT(32) - 1));
+
+	return ret;
+}
+#else
+typedef struct in_addr ipaddr_t;
+static ipaddr_t get_ipaddr_t(ipaddr_t net, size_t n)
+{
+	ipaddr_t ret;
+
+	ret.s_addr = htonl(ntohl(net.s_addr) + n);
+	return ret;
+}
+#endif
+
+static void gen_test_ips(ipaddr_t *ips, size_t ips_nr, bool use_rand)
+{
+	ipaddr_t net;
+	size_t i, j;
+
+	if (inet_pton(TEST_FAMILY, TEST_NETWORK, &net) != 1)
+		test_error("Can't convert ip address %s", TEST_NETWORK);
+
+	if (!use_rand) {
+		for (i = 0; i < ips_nr; i++)
+			ips[i] = get_ipaddr_t(net, 2 * i + 1);
+		return;
+	}
+	for (i = 0; i < ips_nr; i++) {
+		size_t r = (size_t)random() | 0x1;
+
+		ips[i] = get_ipaddr_t(net, r);
+
+		for (j = i - 1; j > 0 && i > 0; j--) {
+			if (!memcmp(&ips[i], &ips[j], sizeof(ipaddr_t))) {
+				i--; /* collision */
+				break;
+			}
+		}
+	}
+}
+
+static void test_add_routes(ipaddr_t *ips, size_t ips_nr)
+{
+	size_t i;
+
+	for (i = 0; i < ips_nr; i++) {
+		union tcp_addr *p = (union tcp_addr *)&ips[i];
+
+		if (ip_route_add(veth_name, TEST_FAMILY, this_ip_addr, *p))
+			test_error("Failed to add route");
+	}
+}
+
+static void server_apply_keys(int lsk, ipaddr_t *ips, size_t ips_nr)
+{
+	size_t i;
+
+	for (i = 0; i < ips_nr; i++) {
+		union tcp_addr *p = (union tcp_addr *)&ips[i];
+
+		if (test_set_ao(lsk, "password", 0, *p, -1, 100, 100))
+			test_error("setsockopt(TCP_AO)");
+	}
+}
+
+static const size_t nr_keys[] = { 512, 1024, 2048, 4096, 8192, 16384 };
+static ipaddr_t *test_ips;
+
+struct bench_stats {
+	uint64_t min;
+	uint64_t max;
+	uint64_t nr;
+	double mean;
+	double s2;
+};
+
+static struct bench_tests {
+	struct bench_stats delete_last_key;
+	struct bench_stats add_key;
+	struct bench_stats delete_rand_key;
+	struct bench_stats connect_last_key;
+	struct bench_stats connect_rand_key;
+	struct bench_stats delete_async;
+} bench_results[ARRAY_SIZE(nr_keys)];
+
+#define NSEC_PER_SEC 1000000000ULL
+
+static void measure_call(struct bench_stats *st,
+			 void (*f)(int, void *), int sk, void *arg)
+{
+	struct timespec start = {}, end = {};
+	double delta;
+	uint64_t nsec;
+
+	if (clock_gettime(CLOCK_MONOTONIC, &start))
+		test_error("clock_gettime()");
+
+	f(sk, arg);
+
+	if (clock_gettime(CLOCK_MONOTONIC, &end))
+		test_error("clock_gettime()");
+
+	nsec = (end.tv_sec - start.tv_sec) * NSEC_PER_SEC;
+	if (end.tv_nsec >= start.tv_nsec)
+		nsec += end.tv_nsec - start.tv_nsec;
+	else
+		nsec -= start.tv_nsec - end.tv_nsec;
+
+	if (st->nr == 0) {
+		st->min = st->max = nsec;
+	} else {
+		if (st->min > nsec)
+			st->min = nsec;
+		if (st->max < nsec)
+			st->max = nsec;
+	}
+
+	/* Welford-Knuth algorithm */
+	st->nr++;
+	delta = (double)nsec - st->mean;
+	st->mean += delta / st->nr;
+	st->s2 += delta * ((double)nsec - st->mean);
+}
+
+static void delete_mkt(int sk, void *arg)
+{
+	struct tcp_ao_del *ao = arg;
+
+	if (setsockopt(sk, IPPROTO_TCP, TCP_AO_DEL, ao, sizeof(*ao)))
+		test_error("setsockopt(TCP_AO_DEL)");
+}
+
+static void add_back_mkt(int sk, void *arg)
+{
+	union tcp_addr *p = arg;
+
+	if (test_set_ao(sk, "password", 0, *p, -1, 100, 100))
+		test_error("setsockopt(TCP_AO)");
+}
+
+static void memcpy_sockaddr(void *dest, union tcp_addr *in_addr)
+{
+#ifdef IPV6_TEST
+	struct sockaddr_in6 addr = {
+		.sin6_family	= AF_INET6,
+		.sin6_port	= 0,
+		.sin6_addr	= in_addr->a6,
+	};
+#else
+	struct sockaddr_in addr = {
+		.sin_family	= AF_INET,
+		.sin_port	= 0,
+		.sin_addr	= in_addr->a4,
+	};
+#endif
+
+	memcpy(dest, &addr, sizeof(addr));
+}
+
+static void bench_delete(int lsk, struct bench_stats *add,
+			 struct bench_stats *del,
+			 ipaddr_t *ips, size_t ips_nr,
+			 bool rand_order, bool async)
+{
+	struct tcp_ao_del ao_del = {};
+	union tcp_addr *p;
+	size_t i;
+
+	ao_del.tcpa_sndid = 100;
+	ao_del.tcpa_rcvid = 100;
+	if (async)
+		ao_del.tcpa_flags = TCP_AO_CMDF_DEL_ASYNC;
+	ao_del.tcpa_prefix = DEFAULT_TEST_PREFIX;
+
+	/* Remove the first added */
+	p = (union tcp_addr *)&ips[0];
+	memcpy_sockaddr(&ao_del.tcpa_addr, p);
+
+	for (i = 0; i < NR_ITERS; i++) {
+		measure_call(del, delete_mkt, lsk, (void *)&ao_del);
+
+		/* Restore it back */
+		measure_call(add, add_back_mkt, lsk, (void *)p);
+
+		/*
+		 * Slowest for FILO-linked-list:
+		 * on (i) iteration removing ips[i] element. When it gets
+		 * added to the list back - it becomes first to fetch, so
+		 * on (i + 1) iteration go to ips[i + 1] element.
+		 */
+		if (rand_order)
+			p = (union tcp_addr *)&ips[rand() % ips_nr];
+		else
+			p = (union tcp_addr *)&ips[i % ips_nr];
+		memcpy_sockaddr(&ao_del.tcpa_addr, p);
+	}
+}
+
+static void bench_connect_srv(int lsk, ipaddr_t *ips, size_t ips_nr)
+{
+	size_t i;
+
+	for (i = 0; i < NR_ITERS; i++) {
+		int err, sk;
+
+		synchronize_threads();
+
+		err = test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0);
+		if (!err)
+			test_error("timeouted for accept()");
+		else if (err < 0)
+			test_error("test_wait_fd()");
+
+		sk = accept(lsk, NULL, NULL);
+		if (sk < 0)
+			test_error("accept()");
+
+		close(sk);
+	}
+}
+
+static void test_print_stats(const char *desc, size_t nr, struct bench_stats *bs)
+{
+	test_ok("%20s\t%zu keys: min=%" PRIu64 "ms max=%" PRIu64 "ms mean=%gms stddev=%g",
+		desc, nr, bs->min / 1000000, bs->max / 1000000,
+		bs->mean / 1000000, sqrt((bs->mean / 1000000) / bs->nr));
+}
+
+static void *server_fn(void *arg)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(nr_keys); i++) {
+		struct bench_tests *bt = &bench_results[i];
+		int lsk;
+
+		test_ips = malloc(nr_keys[i] * sizeof(ipaddr_t));
+		if (!test_ips)
+			test_error("malloc()");
+
+		lsk = test_listen_socket(this_ip_addr, test_server_port + i, 1);
+
+		gen_test_ips(test_ips, nr_keys[i], false);
+		test_add_routes(test_ips, nr_keys[i]);
+		test_set_optmem(AO_KEY_SZ * nr_keys[i]);
+		server_apply_keys(lsk, test_ips, nr_keys[i]);
+
+		synchronize_threads();
+		bench_connect_srv(lsk, test_ips, nr_keys[i]);
+		bench_connect_srv(lsk, test_ips, nr_keys[i]);
+
+		/* The worst case for FILO-list */
+		bench_delete(lsk, &bt->add_key, &bt->delete_last_key,
+			     test_ips, nr_keys[i], false, false);
+		test_print_stats("Worst case delete",
+				nr_keys[i], &bt->delete_last_key);
+		test_print_stats("Add a new key",
+				nr_keys[i], &bt->add_key);
+
+		bench_delete(lsk, &bt->add_key, &bt->delete_rand_key,
+			     test_ips, nr_keys[i], true, false);
+		test_print_stats("Remove random-search",
+				nr_keys[i], &bt->delete_rand_key);
+
+		bench_delete(lsk, &bt->add_key, &bt->delete_async,
+			     test_ips, nr_keys[i], false, true);
+		test_print_stats("Remove async", nr_keys[i], &bt->delete_async);
+
+		free(test_ips);
+		close(lsk);
+	}
+
+	return NULL;
+}
+
+static void connect_client(int sk, void *arg)
+{
+	size_t *p = arg;
+
+	if (test_connect_socket(sk, this_ip_dest, test_server_port + *p) <= 0)
+		test_error("failed to connect()");
+}
+
+static void client_addr_setup(int sk, union tcp_addr taddr)
+{
+#ifdef IPV6_TEST
+	struct sockaddr_in6 addr = {
+		.sin6_family	= AF_INET6,
+		.sin6_port	= 0,
+		.sin6_addr	= taddr.a6,
+	};
+#else
+	struct sockaddr_in addr = {
+		.sin_family	= AF_INET,
+		.sin_port	= 0,
+		.sin_addr	= taddr.a4,
+	};
+#endif
+	int ret;
+
+	ret = ip_addr_add(veth_name, TEST_FAMILY, taddr, TEST_PREFIX);
+	if (ret && ret != -EEXIST)
+		test_error("Failed to add ip address");
+	ret = ip_route_add(veth_name, TEST_FAMILY, taddr, this_ip_dest);
+	if (ret && ret != -EEXIST)
+		test_error("Failed to add route");
+
+	if (bind(sk, &addr, sizeof(addr)))
+		test_error("bind()");
+}
+
+static void bench_connect_client(size_t port_off, struct bench_tests *bt,
+		ipaddr_t *ips, size_t ips_nr, bool rand_order)
+{
+	struct bench_stats *con;
+	union tcp_addr *p;
+	size_t i;
+
+	if (rand_order)
+		con = &bt->connect_rand_key;
+	else
+		con = &bt->connect_last_key;
+
+	p = (union tcp_addr *)&ips[0];
+
+	for (i = 0; i < NR_ITERS; i++) {
+		int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP);
+
+		if (sk < 0)
+			test_error("socket()");
+
+		client_addr_setup(sk, *p);
+		if (test_set_ao(sk, "password", 0, this_ip_dest, -1, 100, 100))
+			test_error("setsockopt(TCP_AO)");
+
+		synchronize_threads();
+
+		measure_call(con, connect_client, sk, (void *)&port_off);
+
+		close(sk);
+
+		/*
+		 * Slowest for FILO-linked-list:
+		 * on (i) iteration removing ips[i] element. When it gets
+		 * added to the list back - it becomes first to fetch, so
+		 * on (i + 1) iteration go to ips[i + 1] element.
+		 */
+		if (rand_order)
+			p = (union tcp_addr *)&ips[rand() % ips_nr];
+		else
+			p = (union tcp_addr *)&ips[i % ips_nr];
+	}
+}
+
+static void *client_fn(void *arg)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(nr_keys); i++) {
+		struct bench_tests *bt = &bench_results[i];
+
+		synchronize_threads();
+		bench_connect_client(i, bt, test_ips, nr_keys[i], false);
+		test_print_stats("Worst case connect",
+				nr_keys[i], &bt->connect_last_key);
+
+		bench_connect_client(i, bt, test_ips, nr_keys[i], false);
+		test_print_stats("Connect random-search",
+				nr_keys[i], &bt->connect_last_key);
+	}
+	synchronize_threads();
+	return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+	test_init(36, server_fn, client_fn);
+	return 0;
+}
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* Re: [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s
  2022-08-18 16:59 ` [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s Dmitry Safonov
@ 2022-08-18 18:50   ` kernel test robot
  2022-08-18 18:50   ` kernel test robot
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 65+ messages in thread
From: kernel test robot @ 2022-08-18 18:50 UTC (permalink / raw)
  To: Dmitry Safonov, Eric Dumazet, David S. Miller, linux-kernel
  Cc: kbuild-all, netdev, Dmitry Safonov, Andy Lutomirski,
	Ard Biesheuvel, Bob Gilligan, David Ahern, Eric Biggers,
	Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande,
	Jakub Kicinski, Leonard Crestez, Paolo Abeni, Salam Noureddine,
	Shuah Khan, linux-crypto

Hi Dmitry,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on e34cfee65ec891a319ce79797dda18083af33a76]

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628
base:   e34cfee65ec891a319ce79797dda18083af33a76
config: x86_64-randconfig-a015 (https://download.01.org/0day-ci/archive/20220819/202208190223.JG4u6Nhm-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/469bd71e5ea011f6ae5a1554b75157471448341d
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628
        git checkout 469bd71e5ea011f6ae5a1554b75157471448341d
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash net/ipv4/ net/ipv6/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> net/ipv4/tcp_ao.c:19:20: warning: no previous prototype for 'tcp_ao_do_lookup_rcvid' [-Wmissing-prototypes]
      19 | struct tcp_ao_key *tcp_ao_do_lookup_rcvid(struct sock *sk, u8 keyid)
         |                    ^~~~~~~~~~~~~~~~~~~~~~
   net/ipv4/tcp_ao.c:37:20: warning: no previous prototype for 'tcp_ao_do_lookup_sndid' [-Wmissing-prototypes]
      37 | struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid)
         |                    ^~~~~~~~~~~~~~~~~~~~~~
   net/ipv4/tcp_ao.c: In function '__tcp_ao_key_cmp':
   net/ipv4/tcp_ao.c:88:46: error: 'const union tcp_ao_addr' has no member named 'a6'; did you mean 'a4'?
      88 |                 if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6))
         |                                              ^~
         |                                              a4
   net/ipv4/tcp_ao.c:88:74: error: 'const union tcp_ao_addr' has no member named 'a6'; did you mean 'a4'?
      88 |                 if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6))
         |                                                                          ^~
         |                                                                          a4
   net/ipv4/tcp_ao.c:90:50: error: 'const union tcp_ao_addr' has no member named 'a6'; did you mean 'a4'?
      90 |                 if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen))
         |                                                  ^~
         |                                                  a4
   net/ipv4/tcp_ao.c:90:61: error: 'const union tcp_ao_addr' has no member named 'a6'; did you mean 'a4'?
      90 |                 if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen))
         |                                                             ^~
         |                                                             a4
   net/ipv4/tcp_ao.c:92:42: error: 'const union tcp_ao_addr' has no member named 'a6'; did you mean 'a4'?
      92 |                 return memcmp(&key->addr.a6, &addr->a6, prefixlen);
         |                                          ^~
         |                                          a4
   net/ipv4/tcp_ao.c:92:53: error: 'const union tcp_ao_addr' has no member named 'a6'; did you mean 'a4'?
      92 |                 return memcmp(&key->addr.a6, &addr->a6, prefixlen);
         |                                                     ^~
         |                                                     a4
   net/ipv4/tcp_ao.c: At top level:
>> net/ipv4/tcp_ao.c:96:5: warning: no previous prototype for 'tcp_ao_key_cmp' [-Wmissing-prototypes]
      96 | int tcp_ao_key_cmp(const struct tcp_ao_key *key,
         |     ^~~~~~~~~~~~~~
   net/ipv4/tcp_ao.c: In function 'tcp_ao_key_cmp':
   net/ipv4/tcp_ao.c:100:61: error: 'const union tcp_ao_addr' has no member named 'a6'; did you mean 'a4'?
     100 |         if (family == AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) {
         |                                                             ^~
         |                                                             a4
   net/ipv4/tcp_ao.c:101:38: error: 'const union tcp_ao_addr' has no member named 'a6'; did you mean 'a4'?
     101 |                 __be32 addr4 = addr->a6.s6_addr32[3];
         |                                      ^~
         |                                      a4
   net/ipv4/tcp_ao.c: At top level:
   net/ipv4/tcp_ao.c:109:20: warning: no previous prototype for 'tcp_ao_do_lookup' [-Wmissing-prototypes]
     109 | struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
         |                    ^~~~~~~~~~~~~~~~
>> net/ipv4/tcp_ao.c:145:6: warning: no previous prototype for 'tcp_ao_link_mkt' [-Wmissing-prototypes]
     145 | void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt)
         |      ^~~~~~~~~~~~~~~
   In file included from include/linux/tcp.h:19,
                    from net/ipv4/tcp_ao.c:13:
   net/ipv4/tcp_ao.c: In function 'tcp_ao_verify_ipv6':
   include/net/sock.h:385:45: error: 'struct sock_common' has no member named 'skc_v6_daddr'; did you mean 'skc_daddr'?
     385 | #define sk_v6_daddr             __sk_common.skc_v6_daddr
         |                                             ^~~~~~~~~~~~
   net/ipv4/tcp_ao.c:292:41: note: in expansion of macro 'sk_v6_daddr'
     292 |                 if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
         |                                         ^~~~~~~~~~~
   include/net/sock.h:385:45: error: 'struct sock_common' has no member named 'skc_v6_daddr'; did you mean 'skc_daddr'?
     385 | #define sk_v6_daddr             __sk_common.skc_v6_daddr
         |                                             ^~~~~~~~~~~~
   net/ipv4/tcp_ao.c:293:45: note: in expansion of macro 'sk_v6_daddr'
     293 |                         __be32 daddr4 = sk->sk_v6_daddr.s6_addr32[3];
         |                                             ^~~~~~~~~~~
   include/net/sock.h:385:45: error: 'struct sock_common' has no member named 'skc_v6_daddr'; did you mean 'skc_daddr'?
     385 | #define sk_v6_daddr             __sk_common.skc_v6_daddr
         |                                             ^~~~~~~~~~~~
   net/ipv4/tcp_ao.c:295:54: note: in expansion of macro 'sk_v6_daddr'
     295 |                         if (!ipv6_addr_v4mapped(&sk->sk_v6_daddr))
         |                                                      ^~~~~~~~~~~
   include/net/sock.h:385:45: error: 'struct sock_common' has no member named 'skc_v6_daddr'; did you mean 'skc_daddr'?
     385 | #define sk_v6_daddr             __sk_common.skc_v6_daddr
         |                                             ^~~~~~~~~~~~
   net/ipv4/tcp_ao.c:316:41: note: in expansion of macro 'sk_v6_daddr'
     316 |                 if (!ipv6_addr_any(&sk->sk_v6_daddr) &&
         |                                         ^~~~~~~~~~~
   include/net/sock.h:385:45: error: 'struct sock_common' has no member named 'skc_v6_daddr'; did you mean 'skc_daddr'?
     385 | #define sk_v6_daddr             __sk_common.skc_v6_daddr
         |                                             ^~~~~~~~~~~~
   net/ipv4/tcp_ao.c:317:45: note: in expansion of macro 'sk_v6_daddr'
     317 |                     !ipv6_prefix_equal(&sk->sk_v6_daddr, addr, prefix))
         |                                             ^~~~~~~~~~~
   net/ipv4/tcp_ao.c: In function 'tcp_ao_mkt_overlap_v6':
   net/ipv4/tcp_ao.c:531:39: error: 'union tcp_ao_addr' has no member named 'a6'; did you mean 'a4'?
     531 |                 key_addr = &key->addr.a6;
         |                                       ^~
         |                                       a4
   net/ipv4/tcp_ao.c: In function '__tcp_ao_key_cmp':
   net/ipv4/tcp_ao.c:94:1: error: control reaches end of non-void function [-Werror=return-type]
      94 | }
         | ^
   cc1: some warnings being treated as errors


vim +/tcp_ao_do_lookup_rcvid +19 net/ipv4/tcp_ao.c

    18	
  > 19	struct tcp_ao_key *tcp_ao_do_lookup_rcvid(struct sock *sk, u8 keyid)
    20	{
    21		struct tcp_sock *tp = tcp_sk(sk);
    22		struct tcp_ao_key *key;
    23		struct tcp_ao_info *ao;
    24	
    25		ao = rcu_dereference_check(tp->ao_info, lockdep_sock_is_held(sk));
    26	
    27		if (!ao)
    28			return NULL;
    29	
    30		hlist_for_each_entry_rcu(key, &ao->head, node) {
    31			if (key->rcvid == keyid)
    32				return key;
    33		}
    34		return NULL;
    35	}
    36	
    37	struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid)
    38	{
    39		struct tcp_ao_key *key;
    40		struct tcp_ao_info *ao;
    41	
    42		ao = rcu_dereference_check(tcp_sk(sk)->ao_info,
    43					   lockdep_sock_is_held(sk));
    44		if (!ao)
    45			return NULL;
    46	
    47		hlist_for_each_entry_rcu(key, &ao->head, node) {
    48			if (key->sndid == keyid)
    49				return key;
    50		}
    51		return NULL;
    52	}
    53	
    54	static inline int ipv4_prefix_cmp(const struct in_addr *addr1,
    55					  const struct in_addr *addr2,
    56					  unsigned int prefixlen)
    57	{
    58		__be32 mask = inet_make_mask(prefixlen);
    59	
    60		if ((addr1->s_addr & mask) == (addr2->s_addr & mask))
    61			return 0;
    62		return ((addr1->s_addr & mask) > (addr2->s_addr & mask)) ? 1 : -1;
    63	}
    64	
    65	static int __tcp_ao_key_cmp(const struct tcp_ao_key *key,
    66				    const union tcp_ao_addr *addr, u8 prefixlen,
    67				    int family, int sndid, int rcvid, u16 port)
    68	{
    69		if (sndid >= 0 && key->sndid != sndid)
    70			return (key->sndid > sndid) ? 1 : -1;
    71		if (rcvid >= 0 && key->rcvid != rcvid)
    72			return (key->rcvid > rcvid) ? 1 : -1;
    73		if (port != 0 && key->port != 0 && port != key->port)
    74			return (key->port > port) ? 1 : -1;
    75	
    76		if (family == AF_UNSPEC)
    77			return 0;
    78		if (key->family != family)
    79			return (key->family > family) ? 1 : -1;
    80	
    81		if (family == AF_INET) {
    82			if (key->addr.a4.s_addr == INADDR_ANY)
    83				return 0;
    84			if (addr->a4.s_addr == INADDR_ANY)
    85				return 0;
    86			return ipv4_prefix_cmp(&key->addr.a4, &addr->a4, prefixlen);
    87		} else {
    88			if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6))
    89				return 0;
    90			if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen))
    91				return 0;
  > 92			return memcmp(&key->addr.a6, &addr->a6, prefixlen);
    93		}
    94	}
    95	
  > 96	int tcp_ao_key_cmp(const struct tcp_ao_key *key,
    97			   const union tcp_ao_addr *addr, u8 prefixlen,
    98			   int family, int sndid, int rcvid, u16 port)
    99	{
   100		if (family == AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) {
 > 101			__be32 addr4 = addr->a6.s6_addr32[3];
   102	
   103			return __tcp_ao_key_cmp(key, (union tcp_ao_addr *)&addr4,
   104						prefixlen, AF_INET, sndid, rcvid, port);
   105		}
   106		return __tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid, port);
   107	}
   108	
   109	struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
   110					    const union tcp_ao_addr *addr,
   111					    int family, int sndid, int rcvid, u16 port)
   112	{
   113		struct tcp_ao_key *key;
   114		struct tcp_ao_info *ao;
   115	
   116		ao = rcu_dereference_check(tcp_sk(sk)->ao_info,
   117					   lockdep_sock_is_held(sk));
   118		if (!ao)
   119			return NULL;
   120	
   121		hlist_for_each_entry_rcu(key, &ao->head, node) {
   122			if (!tcp_ao_key_cmp(key, addr, key->prefixlen,
   123					    family, sndid, rcvid, port))
   124				return key;
   125		}
   126		return NULL;
   127	}
   128	EXPORT_SYMBOL(tcp_ao_do_lookup);
   129	
   130	static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags,
   131			struct tcp_ao_info *cloned_from)
   132	{
   133		struct tcp_ao_info *ao;
   134	
   135		ao = kzalloc(sizeof(*ao), flags);
   136		if (!ao)
   137			return NULL;
   138		INIT_HLIST_HEAD(&ao->head);
   139	
   140		if (cloned_from)
   141			ao->ao_flags = cloned_from->ao_flags;
   142		return ao;
   143	}
   144	
 > 145	void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt)
   146	{
   147		hlist_add_head_rcu(&mkt->node, &ao->head);
   148	}
   149	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s
  2022-08-18 16:59 ` [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s Dmitry Safonov
  2022-08-18 18:50   ` kernel test robot
@ 2022-08-18 18:50   ` kernel test robot
  2022-08-23 14:45   ` Leonard Crestez
  2022-08-25 15:31   ` David Ahern
  3 siblings, 0 replies; 65+ messages in thread
From: kernel test robot @ 2022-08-18 18:50 UTC (permalink / raw)
  To: Dmitry Safonov, Eric Dumazet, David S. Miller, linux-kernel
  Cc: kbuild-all, netdev, Dmitry Safonov, Andy Lutomirski,
	Ard Biesheuvel, Bob Gilligan, David Ahern, Eric Biggers,
	Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande,
	Jakub Kicinski, Leonard Crestez, Paolo Abeni, Salam Noureddine,
	Shuah Khan, linux-crypto

Hi Dmitry,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on e34cfee65ec891a319ce79797dda18083af33a76]

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628
base:   e34cfee65ec891a319ce79797dda18083af33a76
config: x86_64-defconfig (https://download.01.org/0day-ci/archive/20220819/202208190249.8CCVAMql-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/469bd71e5ea011f6ae5a1554b75157471448341d
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628
        git checkout 469bd71e5ea011f6ae5a1554b75157471448341d
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash net/ipv4/ net/ipv6/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> net/ipv4/tcp_ao.c:19:20: warning: no previous prototype for 'tcp_ao_do_lookup_rcvid' [-Wmissing-prototypes]
      19 | struct tcp_ao_key *tcp_ao_do_lookup_rcvid(struct sock *sk, u8 keyid)
         |                    ^~~~~~~~~~~~~~~~~~~~~~
   net/ipv4/tcp_ao.c:37:20: warning: no previous prototype for 'tcp_ao_do_lookup_sndid' [-Wmissing-prototypes]
      37 | struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid)
         |                    ^~~~~~~~~~~~~~~~~~~~~~
>> net/ipv4/tcp_ao.c:96:5: warning: no previous prototype for 'tcp_ao_key_cmp' [-Wmissing-prototypes]
      96 | int tcp_ao_key_cmp(const struct tcp_ao_key *key,
         |     ^~~~~~~~~~~~~~
   net/ipv4/tcp_ao.c:109:20: warning: no previous prototype for 'tcp_ao_do_lookup' [-Wmissing-prototypes]
     109 | struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
         |                    ^~~~~~~~~~~~~~~~
>> net/ipv4/tcp_ao.c:145:6: warning: no previous prototype for 'tcp_ao_link_mkt' [-Wmissing-prototypes]
     145 | void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt)
         |      ^~~~~~~~~~~~~~~


vim +/tcp_ao_do_lookup_rcvid +19 net/ipv4/tcp_ao.c

    18	
  > 19	struct tcp_ao_key *tcp_ao_do_lookup_rcvid(struct sock *sk, u8 keyid)
    20	{
    21		struct tcp_sock *tp = tcp_sk(sk);
    22		struct tcp_ao_key *key;
    23		struct tcp_ao_info *ao;
    24	
    25		ao = rcu_dereference_check(tp->ao_info, lockdep_sock_is_held(sk));
    26	
    27		if (!ao)
    28			return NULL;
    29	
    30		hlist_for_each_entry_rcu(key, &ao->head, node) {
    31			if (key->rcvid == keyid)
    32				return key;
    33		}
    34		return NULL;
    35	}
    36	
    37	struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid)
    38	{
    39		struct tcp_ao_key *key;
    40		struct tcp_ao_info *ao;
    41	
    42		ao = rcu_dereference_check(tcp_sk(sk)->ao_info,
    43					   lockdep_sock_is_held(sk));
    44		if (!ao)
    45			return NULL;
    46	
    47		hlist_for_each_entry_rcu(key, &ao->head, node) {
    48			if (key->sndid == keyid)
    49				return key;
    50		}
    51		return NULL;
    52	}
    53	
    54	static inline int ipv4_prefix_cmp(const struct in_addr *addr1,
    55					  const struct in_addr *addr2,
    56					  unsigned int prefixlen)
    57	{
    58		__be32 mask = inet_make_mask(prefixlen);
    59	
    60		if ((addr1->s_addr & mask) == (addr2->s_addr & mask))
    61			return 0;
    62		return ((addr1->s_addr & mask) > (addr2->s_addr & mask)) ? 1 : -1;
    63	}
    64	
    65	static int __tcp_ao_key_cmp(const struct tcp_ao_key *key,
    66				    const union tcp_ao_addr *addr, u8 prefixlen,
    67				    int family, int sndid, int rcvid, u16 port)
    68	{
    69		if (sndid >= 0 && key->sndid != sndid)
    70			return (key->sndid > sndid) ? 1 : -1;
    71		if (rcvid >= 0 && key->rcvid != rcvid)
    72			return (key->rcvid > rcvid) ? 1 : -1;
    73		if (port != 0 && key->port != 0 && port != key->port)
    74			return (key->port > port) ? 1 : -1;
    75	
    76		if (family == AF_UNSPEC)
    77			return 0;
    78		if (key->family != family)
    79			return (key->family > family) ? 1 : -1;
    80	
    81		if (family == AF_INET) {
    82			if (key->addr.a4.s_addr == INADDR_ANY)
    83				return 0;
    84			if (addr->a4.s_addr == INADDR_ANY)
    85				return 0;
    86			return ipv4_prefix_cmp(&key->addr.a4, &addr->a4, prefixlen);
    87		} else {
    88			if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6))
    89				return 0;
    90			if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen))
    91				return 0;
    92			return memcmp(&key->addr.a6, &addr->a6, prefixlen);
    93		}
    94	}
    95	
  > 96	int tcp_ao_key_cmp(const struct tcp_ao_key *key,
    97			   const union tcp_ao_addr *addr, u8 prefixlen,
    98			   int family, int sndid, int rcvid, u16 port)
    99	{
   100		if (family == AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) {
   101			__be32 addr4 = addr->a6.s6_addr32[3];
   102	
   103			return __tcp_ao_key_cmp(key, (union tcp_ao_addr *)&addr4,
   104						prefixlen, AF_INET, sndid, rcvid, port);
   105		}
   106		return __tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid, port);
   107	}
   108	
   109	struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk,
   110					    const union tcp_ao_addr *addr,
   111					    int family, int sndid, int rcvid, u16 port)
   112	{
   113		struct tcp_ao_key *key;
   114		struct tcp_ao_info *ao;
   115	
   116		ao = rcu_dereference_check(tcp_sk(sk)->ao_info,
   117					   lockdep_sock_is_held(sk));
   118		if (!ao)
   119			return NULL;
   120	
   121		hlist_for_each_entry_rcu(key, &ao->head, node) {
   122			if (!tcp_ao_key_cmp(key, addr, key->prefixlen,
   123					    family, sndid, rcvid, port))
   124				return key;
   125		}
   126		return NULL;
   127	}
   128	EXPORT_SYMBOL(tcp_ao_do_lookup);
   129	
   130	static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags,
   131			struct tcp_ao_info *cloned_from)
   132	{
   133		struct tcp_ao_info *ao;
   134	
   135		ao = kzalloc(sizeof(*ao), flags);
   136		if (!ao)
   137			return NULL;
   138		INIT_HLIST_HEAD(&ao->head);
   139	
   140		if (cloned_from)
   141			ao->ao_flags = cloned_from->ao_flags;
   142		return ao;
   143	}
   144	
 > 145	void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt)
   146	{
   147		hlist_add_head_rcu(&mkt->node, &ao->head);
   148	}
   149	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 12/31] net/tcp: Add tcp_parse_auth_options()
  2022-08-18 16:59 ` [PATCH 12/31] net/tcp: Add tcp_parse_auth_options() Dmitry Safonov
@ 2022-08-18 19:00   ` kernel test robot
  0 siblings, 0 replies; 65+ messages in thread
From: kernel test robot @ 2022-08-18 19:00 UTC (permalink / raw)
  To: Dmitry Safonov, Eric Dumazet, David S. Miller, linux-kernel
  Cc: kbuild-all, netdev, Dmitry Safonov, Andy Lutomirski,
	Ard Biesheuvel, Bob Gilligan, David Ahern, Eric Biggers,
	Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande,
	Jakub Kicinski, Leonard Crestez, Paolo Abeni, Salam Noureddine,
	Shuah Khan, linux-crypto

Hi Dmitry,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on e34cfee65ec891a319ce79797dda18083af33a76]

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628
base:   e34cfee65ec891a319ce79797dda18083af33a76
config: um-i386_defconfig (https://download.01.org/0day-ci/archive/20220819/202208190256.Kj0PqjuN-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/c206ead11e78dec4ec0058427d494521f9d53c3f
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628
        git checkout c206ead11e78dec4ec0058427d494521f9d53c3f
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=um SUBARCH=i386 SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> net/ipv4/tcp_ipv4.c:665:7: warning: "CONFIG_TCP_MD5SIG" is not defined, evaluates to 0 [-Wundef]
     665 | #elif CONFIG_TCP_MD5SIG
         |       ^~~~~~~~~~~~~~~~~


vim +/CONFIG_TCP_MD5SIG +665 net/ipv4/tcp_ipv4.c

   649	
   650	/*
   651	 *	This routine will send an RST to the other tcp.
   652	 *
   653	 *	Someone asks: why I NEVER use socket parameters (TOS, TTL etc.)
   654	 *		      for reset.
   655	 *	Answer: if a packet caused RST, it is not for a socket
   656	 *		existing in our system, if it is matched to a socket,
   657	 *		it is just duplicate segment or bug in other side's TCP.
   658	 *		So that we build reply only basing on parameters
   659	 *		arrived with segment.
   660	 *	Exception: precedence violation. We do not implement it in any case.
   661	 */
   662	
   663	#ifdef CONFIG_TCP_AO
   664	#define OPTION_BYTES MAX_TCP_OPTION_SPACE
 > 665	#elif CONFIG_TCP_MD5SIG
   666	#define OPTION_BYTES TCPOLEN_MD5SIG_ALIGNED
   667	#else
   668	#define OPTION_BYTES sizeof(__be32)
   669	#endif
   670	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 00/31] net/tcp: Add TCP-AO support
  2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
                   ` (30 preceding siblings ...)
  2022-08-18 17:00 ` [PATCH 31/31] selftests/aolib: Add test/benchmark for removing MKTs Dmitry Safonov
@ 2022-08-21 20:34 ` Leonard Crestez
  2022-08-21 23:51   ` David Ahern
  2022-08-22 18:42   ` Salam Noureddine
  31 siblings, 2 replies; 65+ messages in thread
From: Leonard Crestez @ 2022-08-21 20:34 UTC (permalink / raw)
  To: Dmitry Safonov, Eric Dumazet, David S. Miller, David Ahern
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, Dmitry Safonov,
	Eric Biggers, Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI,
	Ivan Delalande, Jakub Kicinski, Paolo Abeni, Salam Noureddine,
	Shuah Khan, netdev, linux-crypto, linux-kernel

On 8/18/22 19:59, Dmitry Safonov wrote:
> This patchset implements the TCP-AO option as described in RFC5925. There
> is a request from industry to move away from TCP-MD5SIG and it seems the time
> is right to have a TCP-AO upstreamed. This TCP option is meant to replace
> the TCP MD5 option and address its shortcomings. Specifically, it provides
> more secure hashing, key rotation and support for long-lived connections
> (see the summary of TCP-AO advantages over TCP-MD5 in (1.3) of RFC5925).
> The patch series starts with six patches that are not specific to TCP-AO
> but implement a general crypto facility that we thought is useful
> to eliminate code duplication between TCP-MD5SIG and TCP-AO as well as other
> crypto users. These six patches are being submitted separately in
> a different patchset [1]. Including them here will show better the gain
> in code sharing. Next are 18 patches that implement the actual TCP-AO option,
> followed by patches implementing selftests.
> 
> The patch set was written as a collaboration of three authors (in alphabetical
> order): Dmitry Safonov, Francesco Ruggeri and Salam Noureddine. Additional
> credits should be given to Prasad Koya, who was involved in early prototyping
> a few years back. There is also a separate submission done by Leonard Crestez
> whom we thank for his efforts getting an implementation of RFC5925 submitted
> for review upstream [2]. This is an independent implementation that makes
> different design decisions.

Is this based on something that Arista has had running for a while now 
or is a recent new development?

> For example, we chose a similar design to the TCP-MD5SIG implementation and
> used setsockopt()s to program per-socket keys, avoiding the extra complexity
> of managing a centralized key database in the kernel. A centralized database
> in the kernel has dubious benefits since it doesn’t eliminate per-socket
> setsockopts needed to specify which sockets need TCP-AO and what are the
> currently preferred keys. It also complicates traffic key caching and
> preventing deletion of in-use keys.

My implementation started with per-socket lists but switched to a global 
list because this way is much easier to manage from userspace. In 
practice userspace apps will want to ensure that all sockets use the 
same set of keys anyway.

> In this implementation, a centralized database of keys can be thought of
> as living in user space and user applications would have to program those
> keys on matching sockets. On the server side, the user application programs
> keys (MKTS in TCP-AO nomenclature) on the listening socket for all peers that
> are expected to connect. Prefix matching on the peer address is supported.
> When a peer issues a successful connect, all the MKTs matching the IP address
> of the peer are copied to the newly created socket. On the active side,
> when a connect() is issued all MKTs that do not match the peer are deleted
> from the socket since they will never match the peer. This implementation
> uses three setsockopt()s for adding, deleting and modifying keys on a socket.
> All three setsockopt()s have extensive sanity checks that prevent
> inconsistencies in the keys on a given socket. A getsockopt() is provided
> to get key information from any given socket.

My series doesn't try to prevent inconsistencies inside the key lists 
because it's not clear that the kernel should prevent userspace from 
shooting itself in the foot. Worst case is connection failure on 
misconfiguration which seems fine.

The RFC doesn't specify in detail how key management is to be performed, 
for example if two valid keys are available it doesn't mention which one 
should be used. Some guidance is found in RFC8177 but again not very much.

I implemented an ABI that can be used by userspace for RFC8177-style key 
management and asked for feedback but received very little. If you had 
come with a clear ABI proposal I would have tried to implement it.

Here's a link to our older discussion:

https://lore.kernel.org/netdev/e7f0449a-2bad-99ad-4737-016a0e6b8b84@gmail.com/

Seeing an entirely distinct unrelated implementation is very unexpected. 
What made you do this?

--
Regards,
Leonard

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 00/31] net/tcp: Add TCP-AO support
  2022-08-21 20:34 ` [PATCH 00/31] net/tcp: Add TCP-AO support Leonard Crestez
@ 2022-08-21 23:51   ` David Ahern
  2022-08-22 20:35     ` Dmitry Safonov
  2022-08-22 18:42   ` Salam Noureddine
  1 sibling, 1 reply; 65+ messages in thread
From: David Ahern @ 2022-08-21 23:51 UTC (permalink / raw)
  To: Leonard Crestez, Dmitry Safonov, Eric Dumazet, David S. Miller
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, Dmitry Safonov,
	Eric Biggers, Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI,
	Ivan Delalande, Jakub Kicinski, Paolo Abeni, Salam Noureddine,
	Shuah Khan, netdev, linux-crypto, linux-kernel

On 8/21/22 2:34 PM, Leonard Crestez wrote:
> On 8/18/22 19:59, Dmitry Safonov wrote:
>> This patchset implements the TCP-AO option as described in RFC5925. There
>> is a request from industry to move away from TCP-MD5SIG and it seems
>> the time
>> is right to have a TCP-AO upstreamed. This TCP option is meant to replace
>> the TCP MD5 option and address its shortcomings. Specifically, it
>> provides
>> more secure hashing, key rotation and support for long-lived connections
>> (see the summary of TCP-AO advantages over TCP-MD5 in (1.3) of RFC5925).
>> The patch series starts with six patches that are not specific to TCP-AO
>> but implement a general crypto facility that we thought is useful
>> to eliminate code duplication between TCP-MD5SIG and TCP-AO as well as
>> other
>> crypto users. These six patches are being submitted separately in
>> a different patchset [1]. Including them here will show better the gain
>> in code sharing. Next are 18 patches that implement the actual TCP-AO
>> option,
>> followed by patches implementing selftests.
>>
>> The patch set was written as a collaboration of three authors (in
>> alphabetical
>> order): Dmitry Safonov, Francesco Ruggeri and Salam Noureddine.
>> Additional
>> credits should be given to Prasad Koya, who was involved in early
>> prototyping
>> a few years back. There is also a separate submission done by Leonard
>> Crestez
>> whom we thank for his efforts getting an implementation of RFC5925
>> submitted
>> for review upstream [2]. This is an independent implementation that makes
>> different design decisions.
> 
> Is this based on something that Arista has had running for a while now
> or is a recent new development?
> 

...

> Seeing an entirely distinct unrelated implementation is very unexpected.
> What made you do this?
> 

I am curious as well. You are well aware of Leonard's efforts which go
back a long time, why go off and do a separate implementation?


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 02/31] crypto_pool: Add crypto_pool_reserve_scratch()
  2022-08-18 16:59 ` [PATCH 02/31] crypto_pool: Add crypto_pool_reserve_scratch() Dmitry Safonov
  (?)
@ 2022-08-22 10:45 ` Dan Carpenter
  -1 siblings, 0 replies; 65+ messages in thread
From: kernel test robot @ 2022-08-22 10:21 UTC (permalink / raw)
  To: kbuild

[-- Attachment #1: Type: text/plain, Size: 4839 bytes --]

BCC: lkp(a)intel.com
CC: kbuild-all(a)lists.01.org
In-Reply-To: <20220818170005.747015-3-dima@arista.com>
References: <20220818170005.747015-3-dima@arista.com>
TO: Dmitry Safonov <dima@arista.com>
TO: Eric Dumazet <edumazet@google.com>
TO: "David S. Miller" <davem@davemloft.net>
CC: netdev(a)vger.kernel.org
TO: linux-kernel(a)vger.kernel.org
CC: Dmitry Safonov <dima@arista.com>
CC: Andy Lutomirski <luto@amacapital.net>
CC: Ard Biesheuvel <ardb@kernel.org>
CC: Bob Gilligan <gilligan@arista.com>
CC: David Ahern <dsahern@kernel.org>
CC: Eric Biggers <ebiggers@kernel.org>
CC: Francesco Ruggeri <fruggeri@arista.com>
CC: Herbert Xu <herbert@gondor.apana.org.au>
CC: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
CC: Ivan Delalande <colona@arista.com>
CC: Jakub Kicinski <kuba@kernel.org>
CC: Leonard Crestez <cdleonard@gmail.com>
CC: Paolo Abeni <pabeni@redhat.com>
CC: Salam Noureddine <noureddine@arista.com>
CC: Shuah Khan <skhan@linuxfoundation.org>
CC: linux-crypto(a)vger.kernel.org

Hi Dmitry,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on e34cfee65ec891a319ce79797dda18083af33a76]

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628
base:   e34cfee65ec891a319ce79797dda18083af33a76
:::::: branch date: 4 days ago
:::::: commit date: 4 days ago
config: x86_64-randconfig-m001 (https://download.01.org/0day-ci/archive/20220822/202208221817.t5uzfegL-lkp(a)intel.com/config)
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

New smatch warnings:
crypto/crypto_pool.c:203 crypto_pool_alloc_ahash() error: uninitialized symbol 'err'.

Old smatch warnings:
crypto/crypto_pool.c:193 crypto_pool_alloc_ahash() error: testing array offset 'i' after use.

vim +/err +203 crypto/crypto_pool.c

f4c3873630fc8c4 Dmitry Safonov 2022-08-18  166  
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  167  /**
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  168   * crypto_pool_alloc_ahash - allocates pool for ahash requests
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  169   * @alg: name of async hash algorithm
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  170   */
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  171  int crypto_pool_alloc_ahash(const char *alg)
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  172  {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  173  	unsigned int i;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  174  	int err;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  175  
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  176  	/* slow-path */
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  177  	mutex_lock(&cpool_mutex);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  178  	for (i = 0; i < last_allocated; i++) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  179  		if (cpool[i].alg && !strcmp(cpool[i].alg, alg)) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  180  			if (kref_read(&cpool[i].kref) > 0) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  181  				kref_get(&cpool[i].kref);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  182  				goto out;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  183  			} else {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  184  				break;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  185  			}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  186  		}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  187  	}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  188  
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  189  	for (i = 0; i < last_allocated; i++) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  190  		if (!cpool[i].alg)
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  191  			break;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  192  	}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  193  	if (i >= CPOOL_SIZE) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  194  		err = -ENOSPC;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  195  		goto out;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  196  	}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  197  
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  198  	err = __cpool_alloc_ahash(&cpool[i], alg);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  199  	if (!err && last_allocated <= i)
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  200  		last_allocated++;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  201  out:
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  202  	mutex_unlock(&cpool_mutex);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18 @203  	return err ?: (int)i;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  204  }
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  205  EXPORT_SYMBOL_GPL(crypto_pool_alloc_ahash);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  206  

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 02/31] crypto_pool: Add crypto_pool_reserve_scratch()
@ 2022-08-22 10:45 ` Dan Carpenter
  0 siblings, 0 replies; 65+ messages in thread
From: Dan Carpenter @ 2022-08-22 10:45 UTC (permalink / raw)
  To: kbuild, Dmitry Safonov, Eric Dumazet, David S. Miller, linux-kernel
  Cc: lkp, kbuild-all, netdev, Dmitry Safonov, Andy Lutomirski,
	Ard Biesheuvel, Bob Gilligan, David Ahern, Eric Biggers,
	Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande,
	Jakub Kicinski, Leonard Crestez, Paolo Abeni, Salam Noureddine,
	Shuah Khan, linux-crypto

Hi Dmitry,

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628
base:   e34cfee65ec891a319ce79797dda18083af33a76
config: x86_64-randconfig-m001 (https://download.01.org/0day-ci/archive/20220822/202208221817.t5uzfegL-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

New smatch warnings:
crypto/crypto_pool.c:203 crypto_pool_alloc_ahash() error: uninitialized symbol 'err'.

vim +/err +203 crypto/crypto_pool.c

f4c3873630fc8c4 Dmitry Safonov 2022-08-18  171  int crypto_pool_alloc_ahash(const char *alg)
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  172  {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  173  	unsigned int i;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  174  	int err;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  175  
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  176  	/* slow-path */
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  177  	mutex_lock(&cpool_mutex);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  178  	for (i = 0; i < last_allocated; i++) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  179  		if (cpool[i].alg && !strcmp(cpool[i].alg, alg)) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  180  			if (kref_read(&cpool[i].kref) > 0) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  181  				kref_get(&cpool[i].kref);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  182  				goto out;

"err" not set.  It was supposed to be set to zero at the start.  But
better to say "ret = i;" here maybe?

Why is i unsigned?  It leads to unsightly casts.  Presumably some static
checker insists on this... :/

f4c3873630fc8c4 Dmitry Safonov 2022-08-18  183  			} else {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  184  				break;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  185  			}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  186  		}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  187  	}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  188  
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  189  	for (i = 0; i < last_allocated; i++) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  190  		if (!cpool[i].alg)
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  191  			break;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  192  	}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  193  	if (i >= CPOOL_SIZE) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  194  		err = -ENOSPC;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  195  		goto out;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  196  	}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  197  
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  198  	err = __cpool_alloc_ahash(&cpool[i], alg);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  199  	if (!err && last_allocated <= i)
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  200  		last_allocated++;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  201  out:
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  202  	mutex_unlock(&cpool_mutex);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18 @203  	return err ?: (int)i;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  204  }

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 02/31] crypto_pool: Add crypto_pool_reserve_scratch()
@ 2022-08-22 10:45 ` Dan Carpenter
  0 siblings, 0 replies; 65+ messages in thread
From: Dan Carpenter @ 2022-08-22 10:45 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 3232 bytes --]

Hi Dmitry,

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628
base:   e34cfee65ec891a319ce79797dda18083af33a76
config: x86_64-randconfig-m001 (https://download.01.org/0day-ci/archive/20220822/202208221817.t5uzfegL-lkp(a)intel.com/config)
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

New smatch warnings:
crypto/crypto_pool.c:203 crypto_pool_alloc_ahash() error: uninitialized symbol 'err'.

vim +/err +203 crypto/crypto_pool.c

f4c3873630fc8c4 Dmitry Safonov 2022-08-18  171  int crypto_pool_alloc_ahash(const char *alg)
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  172  {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  173  	unsigned int i;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  174  	int err;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  175  
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  176  	/* slow-path */
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  177  	mutex_lock(&cpool_mutex);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  178  	for (i = 0; i < last_allocated; i++) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  179  		if (cpool[i].alg && !strcmp(cpool[i].alg, alg)) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  180  			if (kref_read(&cpool[i].kref) > 0) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  181  				kref_get(&cpool[i].kref);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  182  				goto out;

"err" not set.  It was supposed to be set to zero at the start.  But
better to say "ret = i;" here maybe?

Why is i unsigned?  It leads to unsightly casts.  Presumably some static
checker insists on this... :/

f4c3873630fc8c4 Dmitry Safonov 2022-08-18  183  			} else {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  184  				break;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  185  			}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  186  		}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  187  	}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  188  
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  189  	for (i = 0; i < last_allocated; i++) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  190  		if (!cpool[i].alg)
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  191  			break;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  192  	}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  193  	if (i >= CPOOL_SIZE) {
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  194  		err = -ENOSPC;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  195  		goto out;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  196  	}
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  197  
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  198  	err = __cpool_alloc_ahash(&cpool[i], alg);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  199  	if (!err && last_allocated <= i)
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  200  		last_allocated++;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  201  out:
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  202  	mutex_unlock(&cpool_mutex);
f4c3873630fc8c4 Dmitry Safonov 2022-08-18 @203  	return err ?: (int)i;
f4c3873630fc8c4 Dmitry Safonov 2022-08-18  204  }

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 11/31] net/tcp: Add TCP-AO sign to outgoing packets
  2022-08-18 16:59 ` [PATCH 11/31] net/tcp: Add TCP-AO sign to outgoing packets Dmitry Safonov
  (?)
@ 2022-08-22 12:03 ` Dan Carpenter
  -1 siblings, 0 replies; 65+ messages in thread
From: kernel test robot @ 2022-08-22 11:22 UTC (permalink / raw)
  To: kbuild

[-- Attachment #1: Type: text/plain, Size: 13982 bytes --]

BCC: lkp(a)intel.com
CC: kbuild-all(a)lists.01.org
In-Reply-To: <20220818170005.747015-12-dima@arista.com>
References: <20220818170005.747015-12-dima@arista.com>
TO: Dmitry Safonov <dima@arista.com>
TO: Eric Dumazet <edumazet@google.com>
TO: "David S. Miller" <davem@davemloft.net>
CC: netdev(a)vger.kernel.org
TO: linux-kernel(a)vger.kernel.org
CC: Dmitry Safonov <dima@arista.com>
CC: Andy Lutomirski <luto@amacapital.net>
CC: Ard Biesheuvel <ardb@kernel.org>
CC: Bob Gilligan <gilligan@arista.com>
CC: David Ahern <dsahern@kernel.org>
CC: Eric Biggers <ebiggers@kernel.org>
CC: Francesco Ruggeri <fruggeri@arista.com>
CC: Herbert Xu <herbert@gondor.apana.org.au>
CC: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
CC: Ivan Delalande <colona@arista.com>
CC: Jakub Kicinski <kuba@kernel.org>
CC: Leonard Crestez <cdleonard@gmail.com>
CC: Paolo Abeni <pabeni@redhat.com>
CC: Salam Noureddine <noureddine@arista.com>
CC: Shuah Khan <skhan@linuxfoundation.org>
CC: linux-crypto(a)vger.kernel.org

Hi Dmitry,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on e34cfee65ec891a319ce79797dda18083af33a76]

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628
base:   e34cfee65ec891a319ce79797dda18083af33a76
:::::: branch date: 4 days ago
:::::: commit date: 4 days ago
config: x86_64-randconfig-m001 (https://download.01.org/0day-ci/archive/20220822/202208221901.Fs6wW5Jd-lkp(a)intel.com/config)
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

smatch warnings:
net/ipv4/tcp_output.c:640 tcp_options_write() error: uninitialized symbol 'maclen'.
net/ipv4/tcp_output.c:686 tcp_options_write() error: we previously assumed 'tp' could be null (see line 626)

vim +/maclen +640 net/ipv4/tcp_output.c

331fca4315efa3b Martin KaFai Lau      2020-08-20  594  
67edfef78639573 Andi Kleen            2009-07-21  595  /* Write previously computed TCP options to the packet.
67edfef78639573 Andi Kleen            2009-07-21  596   *
67edfef78639573 Andi Kleen            2009-07-21  597   * Beware: Something in the Internet is very sensitive to the ordering of
fd6149d332973ba Ilpo Järvinen         2008-10-23  598   * TCP options, we learned this through the hard way, so be careful here.
fd6149d332973ba Ilpo Järvinen         2008-10-23  599   * Luckily we can at least blame others for their non-compliance but from
8e3bff96afa6736 stephen hemminger     2013-12-08  600   * inter-operability perspective it seems that we're somewhat stuck with
fd6149d332973ba Ilpo Järvinen         2008-10-23  601   * the ordering which we have been using if we want to keep working with
fd6149d332973ba Ilpo Järvinen         2008-10-23  602   * those broken things (not that it currently hurts anybody as there isn't
fd6149d332973ba Ilpo Järvinen         2008-10-23  603   * particular reason why the ordering would need to be changed).
fd6149d332973ba Ilpo Järvinen         2008-10-23  604   *
fd6149d332973ba Ilpo Järvinen         2008-10-23  605   * At least SACK_PERM as the first option is known to lead to a disaster
fd6149d332973ba Ilpo Järvinen         2008-10-23  606   * (but it may well be that other scenarios fail similarly).
fd6149d332973ba Ilpo Järvinen         2008-10-23  607   */
ea66758c1795cef Paolo Abeni           2022-05-04  608  static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp,
85df6b860d509a9 Dmitry Safonov        2022-08-18  609  			      struct tcp_out_options *opts,
85df6b860d509a9 Dmitry Safonov        2022-08-18  610  			      struct tcp_ao_key *ao_key)
bd0388ae7707502 William Allen Simpson 2009-12-02  611  {
ea66758c1795cef Paolo Abeni           2022-05-04  612  	__be32 *ptr = (__be32 *)(th + 1);
2100c8d2d9db23c Yuchung Cheng         2012-07-19  613  	u16 options = opts->options;	/* mungable copy */
bd0388ae7707502 William Allen Simpson 2009-12-02  614  
bd0388ae7707502 William Allen Simpson 2009-12-02  615  	if (unlikely(OPTION_MD5 & options)) {
1a2c6181c4a1922 Christoph Paasch      2013-03-17  616  		*ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
1a2c6181c4a1922 Christoph Paasch      2013-03-17  617  			       (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG);
bd0388ae7707502 William Allen Simpson 2009-12-02  618  		/* overload cookie hash location */
bd0388ae7707502 William Allen Simpson 2009-12-02  619  		opts->hash_location = (__u8 *)ptr;
33ad798c924b4a1 Adam Langley          2008-07-19  620  		ptr += 4;
33ad798c924b4a1 Adam Langley          2008-07-19  621  	}
85df6b860d509a9 Dmitry Safonov        2022-08-18  622  #ifdef CONFIG_TCP_AO
85df6b860d509a9 Dmitry Safonov        2022-08-18  623  	if (unlikely(OPTION_AO & options)) {
85df6b860d509a9 Dmitry Safonov        2022-08-18  624  		u8 maclen;
33ad798c924b4a1 Adam Langley          2008-07-19  625  
85df6b860d509a9 Dmitry Safonov        2022-08-18 @626  		if (tp) {
85df6b860d509a9 Dmitry Safonov        2022-08-18  627  			struct tcp_ao_info *ao_info;
85df6b860d509a9 Dmitry Safonov        2022-08-18  628  
85df6b860d509a9 Dmitry Safonov        2022-08-18  629  			ao_info = rcu_dereference_check(tp->ao_info,
85df6b860d509a9 Dmitry Safonov        2022-08-18  630  				lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk));
85df6b860d509a9 Dmitry Safonov        2022-08-18  631  			if (WARN_ON_ONCE(!ao_key || !ao_info || !ao_info->rnext_key))
85df6b860d509a9 Dmitry Safonov        2022-08-18  632  				goto out_ao;
85df6b860d509a9 Dmitry Safonov        2022-08-18  633  			maclen = tcp_ao_maclen(ao_key);
85df6b860d509a9 Dmitry Safonov        2022-08-18  634  			*ptr++ = htonl((TCPOPT_AO << 24) |
85df6b860d509a9 Dmitry Safonov        2022-08-18  635  				       (tcp_ao_len(ao_key) << 16) |
85df6b860d509a9 Dmitry Safonov        2022-08-18  636  				       (ao_key->sndid << 8) |
85df6b860d509a9 Dmitry Safonov        2022-08-18  637  				       (ao_info->rnext_key->rcvid));
85df6b860d509a9 Dmitry Safonov        2022-08-18  638  		}
85df6b860d509a9 Dmitry Safonov        2022-08-18  639  		opts->hash_location = (__u8 *)ptr;
85df6b860d509a9 Dmitry Safonov        2022-08-18 @640  		ptr += maclen / sizeof(*ptr);
85df6b860d509a9 Dmitry Safonov        2022-08-18  641  		if (unlikely(maclen % sizeof(*ptr))) {
85df6b860d509a9 Dmitry Safonov        2022-08-18  642  			memset(ptr, TCPOPT_NOP, sizeof(*ptr));
85df6b860d509a9 Dmitry Safonov        2022-08-18  643  			ptr++;
85df6b860d509a9 Dmitry Safonov        2022-08-18  644  		}
85df6b860d509a9 Dmitry Safonov        2022-08-18  645  	}
85df6b860d509a9 Dmitry Safonov        2022-08-18  646  out_ao:
85df6b860d509a9 Dmitry Safonov        2022-08-18  647  #endif
fd6149d332973ba Ilpo Järvinen         2008-10-23  648  	if (unlikely(opts->mss)) {
fd6149d332973ba Ilpo Järvinen         2008-10-23  649  		*ptr++ = htonl((TCPOPT_MSS << 24) |
fd6149d332973ba Ilpo Järvinen         2008-10-23  650  			       (TCPOLEN_MSS << 16) |
fd6149d332973ba Ilpo Järvinen         2008-10-23  651  			       opts->mss);
fd6149d332973ba Ilpo Järvinen         2008-10-23  652  	}
fd6149d332973ba Ilpo Järvinen         2008-10-23  653  
bd0388ae7707502 William Allen Simpson 2009-12-02  654  	if (likely(OPTION_TS & options)) {
bd0388ae7707502 William Allen Simpson 2009-12-02  655  		if (unlikely(OPTION_SACK_ADVERTISE & options)) {
33ad798c924b4a1 Adam Langley          2008-07-19  656  			*ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
33ad798c924b4a1 Adam Langley          2008-07-19  657  				       (TCPOLEN_SACK_PERM << 16) |
33ad798c924b4a1 Adam Langley          2008-07-19  658  				       (TCPOPT_TIMESTAMP << 8) |
33ad798c924b4a1 Adam Langley          2008-07-19  659  				       TCPOLEN_TIMESTAMP);
bd0388ae7707502 William Allen Simpson 2009-12-02  660  			options &= ~OPTION_SACK_ADVERTISE;
33ad798c924b4a1 Adam Langley          2008-07-19  661  		} else {
496c98dff8e3538 YOSHIFUJI Hideaki     2006-10-10  662  			*ptr++ = htonl((TCPOPT_NOP << 24) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  663  				       (TCPOPT_NOP << 16) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  664  				       (TCPOPT_TIMESTAMP << 8) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  665  				       TCPOLEN_TIMESTAMP);
40efc6fa179f440 Stephen Hemminger     2006-01-03  666  		}
33ad798c924b4a1 Adam Langley          2008-07-19  667  		*ptr++ = htonl(opts->tsval);
33ad798c924b4a1 Adam Langley          2008-07-19  668  		*ptr++ = htonl(opts->tsecr);
33ad798c924b4a1 Adam Langley          2008-07-19  669  	}
33ad798c924b4a1 Adam Langley          2008-07-19  670  
bd0388ae7707502 William Allen Simpson 2009-12-02  671  	if (unlikely(OPTION_SACK_ADVERTISE & options)) {
33ad798c924b4a1 Adam Langley          2008-07-19  672  		*ptr++ = htonl((TCPOPT_NOP << 24) |
33ad798c924b4a1 Adam Langley          2008-07-19  673  			       (TCPOPT_NOP << 16) |
33ad798c924b4a1 Adam Langley          2008-07-19  674  			       (TCPOPT_SACK_PERM << 8) |
33ad798c924b4a1 Adam Langley          2008-07-19  675  			       TCPOLEN_SACK_PERM);
33ad798c924b4a1 Adam Langley          2008-07-19  676  	}
33ad798c924b4a1 Adam Langley          2008-07-19  677  
bd0388ae7707502 William Allen Simpson 2009-12-02  678  	if (unlikely(OPTION_WSCALE & options)) {
33ad798c924b4a1 Adam Langley          2008-07-19  679  		*ptr++ = htonl((TCPOPT_NOP << 24) |
33ad798c924b4a1 Adam Langley          2008-07-19  680  			       (TCPOPT_WINDOW << 16) |
33ad798c924b4a1 Adam Langley          2008-07-19  681  			       (TCPOLEN_WINDOW << 8) |
33ad798c924b4a1 Adam Langley          2008-07-19  682  			       opts->ws);
33ad798c924b4a1 Adam Langley          2008-07-19  683  	}
33ad798c924b4a1 Adam Langley          2008-07-19  684  
33ad798c924b4a1 Adam Langley          2008-07-19  685  	if (unlikely(opts->num_sack_blocks)) {
33ad798c924b4a1 Adam Langley          2008-07-19 @686  		struct tcp_sack_block *sp = tp->rx_opt.dsack ?
33ad798c924b4a1 Adam Langley          2008-07-19  687  			tp->duplicate_sack : tp->selective_acks;
40efc6fa179f440 Stephen Hemminger     2006-01-03  688  		int this_sack;
40efc6fa179f440 Stephen Hemminger     2006-01-03  689  
40efc6fa179f440 Stephen Hemminger     2006-01-03  690  		*ptr++ = htonl((TCPOPT_NOP  << 24) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  691  			       (TCPOPT_NOP  << 16) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  692  			       (TCPOPT_SACK <<  8) |
33ad798c924b4a1 Adam Langley          2008-07-19  693  			       (TCPOLEN_SACK_BASE + (opts->num_sack_blocks *
40efc6fa179f440 Stephen Hemminger     2006-01-03  694  						     TCPOLEN_SACK_PERBLOCK)));
2de979bd7da9c8b Stephen Hemminger     2007-03-08  695  
33ad798c924b4a1 Adam Langley          2008-07-19  696  		for (this_sack = 0; this_sack < opts->num_sack_blocks;
33ad798c924b4a1 Adam Langley          2008-07-19  697  		     ++this_sack) {
40efc6fa179f440 Stephen Hemminger     2006-01-03  698  			*ptr++ = htonl(sp[this_sack].start_seq);
40efc6fa179f440 Stephen Hemminger     2006-01-03  699  			*ptr++ = htonl(sp[this_sack].end_seq);
40efc6fa179f440 Stephen Hemminger     2006-01-03  700  		}
2de979bd7da9c8b Stephen Hemminger     2007-03-08  701  
40efc6fa179f440 Stephen Hemminger     2006-01-03  702  		tp->rx_opt.dsack = 0;
40efc6fa179f440 Stephen Hemminger     2006-01-03  703  	}
2100c8d2d9db23c Yuchung Cheng         2012-07-19  704  
2100c8d2d9db23c Yuchung Cheng         2012-07-19  705  	if (unlikely(OPTION_FAST_OPEN_COOKIE & options)) {
2100c8d2d9db23c Yuchung Cheng         2012-07-19  706  		struct tcp_fastopen_cookie *foc = opts->fastopen_cookie;
7f9b838b71eb78a Daniel Lee            2015-04-06  707  		u8 *p = (u8 *)ptr;
7f9b838b71eb78a Daniel Lee            2015-04-06  708  		u32 len; /* Fast Open option length */
2100c8d2d9db23c Yuchung Cheng         2012-07-19  709  
7f9b838b71eb78a Daniel Lee            2015-04-06  710  		if (foc->exp) {
7f9b838b71eb78a Daniel Lee            2015-04-06  711  			len = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
7f9b838b71eb78a Daniel Lee            2015-04-06  712  			*ptr = htonl((TCPOPT_EXP << 24) | (len << 16) |
2100c8d2d9db23c Yuchung Cheng         2012-07-19  713  				     TCPOPT_FASTOPEN_MAGIC);
7f9b838b71eb78a Daniel Lee            2015-04-06  714  			p += TCPOLEN_EXP_FASTOPEN_BASE;
7f9b838b71eb78a Daniel Lee            2015-04-06  715  		} else {
7f9b838b71eb78a Daniel Lee            2015-04-06  716  			len = TCPOLEN_FASTOPEN_BASE + foc->len;
7f9b838b71eb78a Daniel Lee            2015-04-06  717  			*p++ = TCPOPT_FASTOPEN;
7f9b838b71eb78a Daniel Lee            2015-04-06  718  			*p++ = len;
7f9b838b71eb78a Daniel Lee            2015-04-06  719  		}
2100c8d2d9db23c Yuchung Cheng         2012-07-19  720  
7f9b838b71eb78a Daniel Lee            2015-04-06  721  		memcpy(p, foc->val, foc->len);
7f9b838b71eb78a Daniel Lee            2015-04-06  722  		if ((len & 3) == 2) {
7f9b838b71eb78a Daniel Lee            2015-04-06  723  			p[foc->len] = TCPOPT_NOP;
7f9b838b71eb78a Daniel Lee            2015-04-06  724  			p[foc->len + 1] = TCPOPT_NOP;
2100c8d2d9db23c Yuchung Cheng         2012-07-19  725  		}
7f9b838b71eb78a Daniel Lee            2015-04-06  726  		ptr += (len + 3) >> 2;
2100c8d2d9db23c Yuchung Cheng         2012-07-19  727  	}
60e2a7780793bae Ursula Braun          2017-10-25  728  
60e2a7780793bae Ursula Braun          2017-10-25  729  	smc_options_write(ptr, &options);
eda7acddf8080bb Peter Krystad         2020-01-21  730  
ea66758c1795cef Paolo Abeni           2022-05-04  731  	mptcp_options_write(th, ptr, tp, opts);
60e2a7780793bae Ursula Braun          2017-10-25  732  }
60e2a7780793bae Ursula Braun          2017-10-25  733  

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [kbuild] Re: [PATCH 11/31] net/tcp: Add TCP-AO sign to outgoing packets
@ 2022-08-22 12:03 ` Dan Carpenter
  0 siblings, 0 replies; 65+ messages in thread
From: Dan Carpenter @ 2022-08-22 12:03 UTC (permalink / raw)
  To: kbuild, Dmitry Safonov, Eric Dumazet, David S. Miller, linux-kernel
  Cc: lkp, kbuild-all, netdev, Dmitry Safonov, Andy Lutomirski,
	Ard Biesheuvel, Bob Gilligan, David Ahern, Eric Biggers,
	Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande,
	Jakub Kicinski, Leonard Crestez, Paolo Abeni, Salam Noureddine,
	Shuah Khan, linux-crypto

Hi Dmitry,

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628  
base:   e34cfee65ec891a319ce79797dda18083af33a76
config: x86_64-randconfig-m001 (https://download.01.org/0day-ci/archive/20220822/202208221901.Fs6wW5Jd-lkp@intel.com/config  )
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

smatch warnings:
net/ipv4/tcp_output.c:640 tcp_options_write() error: uninitialized symbol 'maclen'.
net/ipv4/tcp_output.c:686 tcp_options_write() error: we previously assumed 'tp' could be null (see line 626)

vim +/maclen +640 net/ipv4/tcp_output.c

ea66758c1795cef Paolo Abeni           2022-05-04  608  static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp,
85df6b860d509a9 Dmitry Safonov        2022-08-18  609  			      struct tcp_out_options *opts,
85df6b860d509a9 Dmitry Safonov        2022-08-18  610  			      struct tcp_ao_key *ao_key)
bd0388ae7707502 William Allen Simpson 2009-12-02  611  {
ea66758c1795cef Paolo Abeni           2022-05-04  612  	__be32 *ptr = (__be32 *)(th + 1);
2100c8d2d9db23c Yuchung Cheng         2012-07-19  613  	u16 options = opts->options;	/* mungable copy */
bd0388ae7707502 William Allen Simpson 2009-12-02  614  
bd0388ae7707502 William Allen Simpson 2009-12-02  615  	if (unlikely(OPTION_MD5 & options)) {
1a2c6181c4a1922 Christoph Paasch      2013-03-17  616  		*ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
1a2c6181c4a1922 Christoph Paasch      2013-03-17  617  			       (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG);
bd0388ae7707502 William Allen Simpson 2009-12-02  618  		/* overload cookie hash location */
bd0388ae7707502 William Allen Simpson 2009-12-02  619  		opts->hash_location = (__u8 *)ptr;
33ad798c924b4a1 Adam Langley          2008-07-19  620  		ptr += 4;
33ad798c924b4a1 Adam Langley          2008-07-19  621  	}
85df6b860d509a9 Dmitry Safonov        2022-08-18  622  #ifdef CONFIG_TCP_AO
85df6b860d509a9 Dmitry Safonov        2022-08-18  623  	if (unlikely(OPTION_AO & options)) {
85df6b860d509a9 Dmitry Safonov        2022-08-18  624  		u8 maclen;
33ad798c924b4a1 Adam Langley          2008-07-19  625  
85df6b860d509a9 Dmitry Safonov        2022-08-18 @626  		if (tp) {

Can "tp" really be NULL?  Everything else assumes it can't.

85df6b860d509a9 Dmitry Safonov        2022-08-18  627  			struct tcp_ao_info *ao_info;
85df6b860d509a9 Dmitry Safonov        2022-08-18  628  
85df6b860d509a9 Dmitry Safonov        2022-08-18  629  			ao_info = rcu_dereference_check(tp->ao_info,
85df6b860d509a9 Dmitry Safonov        2022-08-18  630  				lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk));
85df6b860d509a9 Dmitry Safonov        2022-08-18  631  			if (WARN_ON_ONCE(!ao_key || !ao_info || !ao_info->rnext_key))
85df6b860d509a9 Dmitry Safonov        2022-08-18  632  				goto out_ao;
85df6b860d509a9 Dmitry Safonov        2022-08-18  633  			maclen = tcp_ao_maclen(ao_key);
85df6b860d509a9 Dmitry Safonov        2022-08-18  634  			*ptr++ = htonl((TCPOPT_AO << 24) |
85df6b860d509a9 Dmitry Safonov        2022-08-18  635  				       (tcp_ao_len(ao_key) << 16) |
85df6b860d509a9 Dmitry Safonov        2022-08-18  636  				       (ao_key->sndid << 8) |
85df6b860d509a9 Dmitry Safonov        2022-08-18  637  				       (ao_info->rnext_key->rcvid));
85df6b860d509a9 Dmitry Safonov        2022-08-18  638  		}

"maclen" not initialized on else path.

85df6b860d509a9 Dmitry Safonov        2022-08-18  639  		opts->hash_location = (__u8 *)ptr;
85df6b860d509a9 Dmitry Safonov        2022-08-18 @640  		ptr += maclen / sizeof(*ptr);

Uninitialized.

85df6b860d509a9 Dmitry Safonov        2022-08-18  641  		if (unlikely(maclen % sizeof(*ptr))) {
85df6b860d509a9 Dmitry Safonov        2022-08-18  642  			memset(ptr, TCPOPT_NOP, sizeof(*ptr));
85df6b860d509a9 Dmitry Safonov        2022-08-18  643  			ptr++;
85df6b860d509a9 Dmitry Safonov        2022-08-18  644  		}
85df6b860d509a9 Dmitry Safonov        2022-08-18  645  	}
85df6b860d509a9 Dmitry Safonov        2022-08-18  646  out_ao:
85df6b860d509a9 Dmitry Safonov        2022-08-18  647  #endif
fd6149d332973ba Ilpo Järvinen         2008-10-23  648  	if (unlikely(opts->mss)) {
fd6149d332973ba Ilpo Järvinen         2008-10-23  649  		*ptr++ = htonl((TCPOPT_MSS << 24) |
fd6149d332973ba Ilpo Järvinen         2008-10-23  650  			       (TCPOLEN_MSS << 16) |
fd6149d332973ba Ilpo Järvinen         2008-10-23  651  			       opts->mss);
fd6149d332973ba Ilpo Järvinen         2008-10-23  652  	}
fd6149d332973ba Ilpo Järvinen         2008-10-23  653  
bd0388ae7707502 William Allen Simpson 2009-12-02  654  	if (likely(OPTION_TS & options)) {
bd0388ae7707502 William Allen Simpson 2009-12-02  655  		if (unlikely(OPTION_SACK_ADVERTISE & options)) {
33ad798c924b4a1 Adam Langley          2008-07-19  656  			*ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
33ad798c924b4a1 Adam Langley          2008-07-19  657  				       (TCPOLEN_SACK_PERM << 16) |
33ad798c924b4a1 Adam Langley          2008-07-19  658  				       (TCPOPT_TIMESTAMP << 8) |
33ad798c924b4a1 Adam Langley          2008-07-19  659  				       TCPOLEN_TIMESTAMP);
bd0388ae7707502 William Allen Simpson 2009-12-02  660  			options &= ~OPTION_SACK_ADVERTISE;
33ad798c924b4a1 Adam Langley          2008-07-19  661  		} else {
496c98dff8e3538 YOSHIFUJI Hideaki     2006-10-10  662  			*ptr++ = htonl((TCPOPT_NOP << 24) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  663  				       (TCPOPT_NOP << 16) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  664  				       (TCPOPT_TIMESTAMP << 8) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  665  				       TCPOLEN_TIMESTAMP);
40efc6fa179f440 Stephen Hemminger     2006-01-03  666  		}
33ad798c924b4a1 Adam Langley          2008-07-19  667  		*ptr++ = htonl(opts->tsval);
33ad798c924b4a1 Adam Langley          2008-07-19  668  		*ptr++ = htonl(opts->tsecr);
33ad798c924b4a1 Adam Langley          2008-07-19  669  	}
33ad798c924b4a1 Adam Langley          2008-07-19  670  
bd0388ae7707502 William Allen Simpson 2009-12-02  671  	if (unlikely(OPTION_SACK_ADVERTISE & options)) {
33ad798c924b4a1 Adam Langley          2008-07-19  672  		*ptr++ = htonl((TCPOPT_NOP << 24) |
33ad798c924b4a1 Adam Langley          2008-07-19  673  			       (TCPOPT_NOP << 16) |
33ad798c924b4a1 Adam Langley          2008-07-19  674  			       (TCPOPT_SACK_PERM << 8) |
33ad798c924b4a1 Adam Langley          2008-07-19  675  			       TCPOLEN_SACK_PERM);
33ad798c924b4a1 Adam Langley          2008-07-19  676  	}
33ad798c924b4a1 Adam Langley          2008-07-19  677  
bd0388ae7707502 William Allen Simpson 2009-12-02  678  	if (unlikely(OPTION_WSCALE & options)) {
33ad798c924b4a1 Adam Langley          2008-07-19  679  		*ptr++ = htonl((TCPOPT_NOP << 24) |
33ad798c924b4a1 Adam Langley          2008-07-19  680  			       (TCPOPT_WINDOW << 16) |
33ad798c924b4a1 Adam Langley          2008-07-19  681  			       (TCPOLEN_WINDOW << 8) |
33ad798c924b4a1 Adam Langley          2008-07-19  682  			       opts->ws);
33ad798c924b4a1 Adam Langley          2008-07-19  683  	}
33ad798c924b4a1 Adam Langley          2008-07-19  684  
33ad798c924b4a1 Adam Langley          2008-07-19  685  	if (unlikely(opts->num_sack_blocks)) {
33ad798c924b4a1 Adam Langley          2008-07-19 @686  		struct tcp_sack_block *sp = tp->rx_opt.dsack ?

Unchecked dereference.

33ad798c924b4a1 Adam Langley          2008-07-19  687  			tp->duplicate_sack : tp->selective_acks;
40efc6fa179f440 Stephen Hemminger     2006-01-03  688  		int this_sack;
40efc6fa179f440 Stephen Hemminger     2006-01-03  689  
40efc6fa179f440 Stephen Hemminger     2006-01-03  690  		*ptr++ = htonl((TCPOPT_NOP  << 24) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  691  			       (TCPOPT_NOP  << 16) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  692  			       (TCPOPT_SACK <<  8) |
33ad798c924b4a1 Adam Langley          2008-07-19  693  			       (TCPOLEN_SACK_BASE + (opts->num_sack_blocks *
40efc6fa179f440 Stephen Hemminger     2006-01-03  694  						     TCPOLEN_SACK_PERBLOCK)));
2de979bd7da9c8b Stephen Hemminger     2007-03-08  695  
33ad798c924b4a1 Adam Langley          2008-07-19  696  		for (this_sack = 0; this_sack < opts->num_sack_blocks;
33ad798c924b4a1 Adam Langley          2008-07-19  697  		     ++this_sack) {
40efc6fa179f440 Stephen Hemminger     2006-01-03  698  			*ptr++ = htonl(sp[this_sack].start_seq);
40efc6fa179f440 Stephen Hemminger     2006-01-03  699  			*ptr++ = htonl(sp[this_sack].end_seq);
40efc6fa179f440 Stephen Hemminger     2006-01-03  700  		}
2de979bd7da9c8b Stephen Hemminger     2007-03-08  701  
40efc6fa179f440 Stephen Hemminger     2006-01-03  702  		tp->rx_opt.dsack = 0;
40efc6fa179f440 Stephen Hemminger     2006-01-03  703  	}
2100c8d2d9db23c Yuchung Cheng         2012-07-19  704  
2100c8d2d9db23c Yuchung Cheng         2012-07-19  705  	if (unlikely(OPTION_FAST_OPEN_COOKIE & options)) {
2100c8d2d9db23c Yuchung Cheng         2012-07-19  706  		struct tcp_fastopen_cookie *foc = opts->fastopen_cookie;
7f9b838b71eb78a Daniel Lee            2015-04-06  707  		u8 *p = (u8 *)ptr;
7f9b838b71eb78a Daniel Lee            2015-04-06  708  		u32 len; /* Fast Open option length */
2100c8d2d9db23c Yuchung Cheng         2012-07-19  709  
7f9b838b71eb78a Daniel Lee            2015-04-06  710  		if (foc->exp) {
7f9b838b71eb78a Daniel Lee            2015-04-06  711  			len = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
7f9b838b71eb78a Daniel Lee            2015-04-06  712  			*ptr = htonl((TCPOPT_EXP << 24) | (len << 16) |
2100c8d2d9db23c Yuchung Cheng         2012-07-19  713  				     TCPOPT_FASTOPEN_MAGIC);
7f9b838b71eb78a Daniel Lee            2015-04-06  714  			p += TCPOLEN_EXP_FASTOPEN_BASE;
7f9b838b71eb78a Daniel Lee            2015-04-06  715  		} else {
7f9b838b71eb78a Daniel Lee            2015-04-06  716  			len = TCPOLEN_FASTOPEN_BASE + foc->len;
7f9b838b71eb78a Daniel Lee            2015-04-06  717  			*p++ = TCPOPT_FASTOPEN;
7f9b838b71eb78a Daniel Lee            2015-04-06  718  			*p++ = len;
7f9b838b71eb78a Daniel Lee            2015-04-06  719  		}
2100c8d2d9db23c Yuchung Cheng         2012-07-19  720  
7f9b838b71eb78a Daniel Lee            2015-04-06  721  		memcpy(p, foc->val, foc->len);
7f9b838b71eb78a Daniel Lee            2015-04-06  722  		if ((len & 3) == 2) {
7f9b838b71eb78a Daniel Lee            2015-04-06  723  			p[foc->len] = TCPOPT_NOP;
7f9b838b71eb78a Daniel Lee            2015-04-06  724  			p[foc->len + 1] = TCPOPT_NOP;
2100c8d2d9db23c Yuchung Cheng         2012-07-19  725  		}
7f9b838b71eb78a Daniel Lee            2015-04-06  726  		ptr += (len + 3) >> 2;
2100c8d2d9db23c Yuchung Cheng         2012-07-19  727  	}
60e2a7780793bae Ursula Braun          2017-10-25  728  
60e2a7780793bae Ursula Braun          2017-10-25  729  	smc_options_write(ptr, &options);
eda7acddf8080bb Peter Krystad         2020-01-21  730  
ea66758c1795cef Paolo Abeni           2022-05-04  731  	mptcp_options_write(th, ptr, tp, opts);
                                                                                     ^^
Not checked here either.

60e2a7780793bae Ursula Braun          2017-10-25  732  }

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp  
_______________________________________________
kbuild mailing list -- kbuild@lists.01.org
To unsubscribe send an email to kbuild-leave@lists.01.org


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [kbuild] Re: [PATCH 11/31] net/tcp: Add TCP-AO sign to outgoing packets
@ 2022-08-22 12:03 ` Dan Carpenter
  0 siblings, 0 replies; 65+ messages in thread
From: Dan Carpenter @ 2022-08-22 12:03 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 11648 bytes --]

Hi Dmitry,

url:    https://github.com/intel-lab-lkp/linux/commits/Dmitry-Safonov/net-tcp-Add-TCP-AO-support/20220819-010628  
base:   e34cfee65ec891a319ce79797dda18083af33a76
config: x86_64-randconfig-m001 (https://download.01.org/0day-ci/archive/20220822/202208221901.Fs6wW5Jd-lkp(a)intel.com/config  )
compiler: gcc-11 (Debian 11.3.0-5) 11.3.0

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

smatch warnings:
net/ipv4/tcp_output.c:640 tcp_options_write() error: uninitialized symbol 'maclen'.
net/ipv4/tcp_output.c:686 tcp_options_write() error: we previously assumed 'tp' could be null (see line 626)

vim +/maclen +640 net/ipv4/tcp_output.c

ea66758c1795cef Paolo Abeni           2022-05-04  608  static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp,
85df6b860d509a9 Dmitry Safonov        2022-08-18  609  			      struct tcp_out_options *opts,
85df6b860d509a9 Dmitry Safonov        2022-08-18  610  			      struct tcp_ao_key *ao_key)
bd0388ae7707502 William Allen Simpson 2009-12-02  611  {
ea66758c1795cef Paolo Abeni           2022-05-04  612  	__be32 *ptr = (__be32 *)(th + 1);
2100c8d2d9db23c Yuchung Cheng         2012-07-19  613  	u16 options = opts->options;	/* mungable copy */
bd0388ae7707502 William Allen Simpson 2009-12-02  614  
bd0388ae7707502 William Allen Simpson 2009-12-02  615  	if (unlikely(OPTION_MD5 & options)) {
1a2c6181c4a1922 Christoph Paasch      2013-03-17  616  		*ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
1a2c6181c4a1922 Christoph Paasch      2013-03-17  617  			       (TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG);
bd0388ae7707502 William Allen Simpson 2009-12-02  618  		/* overload cookie hash location */
bd0388ae7707502 William Allen Simpson 2009-12-02  619  		opts->hash_location = (__u8 *)ptr;
33ad798c924b4a1 Adam Langley          2008-07-19  620  		ptr += 4;
33ad798c924b4a1 Adam Langley          2008-07-19  621  	}
85df6b860d509a9 Dmitry Safonov        2022-08-18  622  #ifdef CONFIG_TCP_AO
85df6b860d509a9 Dmitry Safonov        2022-08-18  623  	if (unlikely(OPTION_AO & options)) {
85df6b860d509a9 Dmitry Safonov        2022-08-18  624  		u8 maclen;
33ad798c924b4a1 Adam Langley          2008-07-19  625  
85df6b860d509a9 Dmitry Safonov        2022-08-18 @626  		if (tp) {

Can "tp" really be NULL?  Everything else assumes it can't.

85df6b860d509a9 Dmitry Safonov        2022-08-18  627  			struct tcp_ao_info *ao_info;
85df6b860d509a9 Dmitry Safonov        2022-08-18  628  
85df6b860d509a9 Dmitry Safonov        2022-08-18  629  			ao_info = rcu_dereference_check(tp->ao_info,
85df6b860d509a9 Dmitry Safonov        2022-08-18  630  				lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk));
85df6b860d509a9 Dmitry Safonov        2022-08-18  631  			if (WARN_ON_ONCE(!ao_key || !ao_info || !ao_info->rnext_key))
85df6b860d509a9 Dmitry Safonov        2022-08-18  632  				goto out_ao;
85df6b860d509a9 Dmitry Safonov        2022-08-18  633  			maclen = tcp_ao_maclen(ao_key);
85df6b860d509a9 Dmitry Safonov        2022-08-18  634  			*ptr++ = htonl((TCPOPT_AO << 24) |
85df6b860d509a9 Dmitry Safonov        2022-08-18  635  				       (tcp_ao_len(ao_key) << 16) |
85df6b860d509a9 Dmitry Safonov        2022-08-18  636  				       (ao_key->sndid << 8) |
85df6b860d509a9 Dmitry Safonov        2022-08-18  637  				       (ao_info->rnext_key->rcvid));
85df6b860d509a9 Dmitry Safonov        2022-08-18  638  		}

"maclen" not initialized on else path.

85df6b860d509a9 Dmitry Safonov        2022-08-18  639  		opts->hash_location = (__u8 *)ptr;
85df6b860d509a9 Dmitry Safonov        2022-08-18 @640  		ptr += maclen / sizeof(*ptr);

Uninitialized.

85df6b860d509a9 Dmitry Safonov        2022-08-18  641  		if (unlikely(maclen % sizeof(*ptr))) {
85df6b860d509a9 Dmitry Safonov        2022-08-18  642  			memset(ptr, TCPOPT_NOP, sizeof(*ptr));
85df6b860d509a9 Dmitry Safonov        2022-08-18  643  			ptr++;
85df6b860d509a9 Dmitry Safonov        2022-08-18  644  		}
85df6b860d509a9 Dmitry Safonov        2022-08-18  645  	}
85df6b860d509a9 Dmitry Safonov        2022-08-18  646  out_ao:
85df6b860d509a9 Dmitry Safonov        2022-08-18  647  #endif
fd6149d332973ba Ilpo Järvinen         2008-10-23  648  	if (unlikely(opts->mss)) {
fd6149d332973ba Ilpo Järvinen         2008-10-23  649  		*ptr++ = htonl((TCPOPT_MSS << 24) |
fd6149d332973ba Ilpo Järvinen         2008-10-23  650  			       (TCPOLEN_MSS << 16) |
fd6149d332973ba Ilpo Järvinen         2008-10-23  651  			       opts->mss);
fd6149d332973ba Ilpo Järvinen         2008-10-23  652  	}
fd6149d332973ba Ilpo Järvinen         2008-10-23  653  
bd0388ae7707502 William Allen Simpson 2009-12-02  654  	if (likely(OPTION_TS & options)) {
bd0388ae7707502 William Allen Simpson 2009-12-02  655  		if (unlikely(OPTION_SACK_ADVERTISE & options)) {
33ad798c924b4a1 Adam Langley          2008-07-19  656  			*ptr++ = htonl((TCPOPT_SACK_PERM << 24) |
33ad798c924b4a1 Adam Langley          2008-07-19  657  				       (TCPOLEN_SACK_PERM << 16) |
33ad798c924b4a1 Adam Langley          2008-07-19  658  				       (TCPOPT_TIMESTAMP << 8) |
33ad798c924b4a1 Adam Langley          2008-07-19  659  				       TCPOLEN_TIMESTAMP);
bd0388ae7707502 William Allen Simpson 2009-12-02  660  			options &= ~OPTION_SACK_ADVERTISE;
33ad798c924b4a1 Adam Langley          2008-07-19  661  		} else {
496c98dff8e3538 YOSHIFUJI Hideaki     2006-10-10  662  			*ptr++ = htonl((TCPOPT_NOP << 24) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  663  				       (TCPOPT_NOP << 16) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  664  				       (TCPOPT_TIMESTAMP << 8) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  665  				       TCPOLEN_TIMESTAMP);
40efc6fa179f440 Stephen Hemminger     2006-01-03  666  		}
33ad798c924b4a1 Adam Langley          2008-07-19  667  		*ptr++ = htonl(opts->tsval);
33ad798c924b4a1 Adam Langley          2008-07-19  668  		*ptr++ = htonl(opts->tsecr);
33ad798c924b4a1 Adam Langley          2008-07-19  669  	}
33ad798c924b4a1 Adam Langley          2008-07-19  670  
bd0388ae7707502 William Allen Simpson 2009-12-02  671  	if (unlikely(OPTION_SACK_ADVERTISE & options)) {
33ad798c924b4a1 Adam Langley          2008-07-19  672  		*ptr++ = htonl((TCPOPT_NOP << 24) |
33ad798c924b4a1 Adam Langley          2008-07-19  673  			       (TCPOPT_NOP << 16) |
33ad798c924b4a1 Adam Langley          2008-07-19  674  			       (TCPOPT_SACK_PERM << 8) |
33ad798c924b4a1 Adam Langley          2008-07-19  675  			       TCPOLEN_SACK_PERM);
33ad798c924b4a1 Adam Langley          2008-07-19  676  	}
33ad798c924b4a1 Adam Langley          2008-07-19  677  
bd0388ae7707502 William Allen Simpson 2009-12-02  678  	if (unlikely(OPTION_WSCALE & options)) {
33ad798c924b4a1 Adam Langley          2008-07-19  679  		*ptr++ = htonl((TCPOPT_NOP << 24) |
33ad798c924b4a1 Adam Langley          2008-07-19  680  			       (TCPOPT_WINDOW << 16) |
33ad798c924b4a1 Adam Langley          2008-07-19  681  			       (TCPOLEN_WINDOW << 8) |
33ad798c924b4a1 Adam Langley          2008-07-19  682  			       opts->ws);
33ad798c924b4a1 Adam Langley          2008-07-19  683  	}
33ad798c924b4a1 Adam Langley          2008-07-19  684  
33ad798c924b4a1 Adam Langley          2008-07-19  685  	if (unlikely(opts->num_sack_blocks)) {
33ad798c924b4a1 Adam Langley          2008-07-19 @686  		struct tcp_sack_block *sp = tp->rx_opt.dsack ?

Unchecked dereference.

33ad798c924b4a1 Adam Langley          2008-07-19  687  			tp->duplicate_sack : tp->selective_acks;
40efc6fa179f440 Stephen Hemminger     2006-01-03  688  		int this_sack;
40efc6fa179f440 Stephen Hemminger     2006-01-03  689  
40efc6fa179f440 Stephen Hemminger     2006-01-03  690  		*ptr++ = htonl((TCPOPT_NOP  << 24) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  691  			       (TCPOPT_NOP  << 16) |
40efc6fa179f440 Stephen Hemminger     2006-01-03  692  			       (TCPOPT_SACK <<  8) |
33ad798c924b4a1 Adam Langley          2008-07-19  693  			       (TCPOLEN_SACK_BASE + (opts->num_sack_blocks *
40efc6fa179f440 Stephen Hemminger     2006-01-03  694  						     TCPOLEN_SACK_PERBLOCK)));
2de979bd7da9c8b Stephen Hemminger     2007-03-08  695  
33ad798c924b4a1 Adam Langley          2008-07-19  696  		for (this_sack = 0; this_sack < opts->num_sack_blocks;
33ad798c924b4a1 Adam Langley          2008-07-19  697  		     ++this_sack) {
40efc6fa179f440 Stephen Hemminger     2006-01-03  698  			*ptr++ = htonl(sp[this_sack].start_seq);
40efc6fa179f440 Stephen Hemminger     2006-01-03  699  			*ptr++ = htonl(sp[this_sack].end_seq);
40efc6fa179f440 Stephen Hemminger     2006-01-03  700  		}
2de979bd7da9c8b Stephen Hemminger     2007-03-08  701  
40efc6fa179f440 Stephen Hemminger     2006-01-03  702  		tp->rx_opt.dsack = 0;
40efc6fa179f440 Stephen Hemminger     2006-01-03  703  	}
2100c8d2d9db23c Yuchung Cheng         2012-07-19  704  
2100c8d2d9db23c Yuchung Cheng         2012-07-19  705  	if (unlikely(OPTION_FAST_OPEN_COOKIE & options)) {
2100c8d2d9db23c Yuchung Cheng         2012-07-19  706  		struct tcp_fastopen_cookie *foc = opts->fastopen_cookie;
7f9b838b71eb78a Daniel Lee            2015-04-06  707  		u8 *p = (u8 *)ptr;
7f9b838b71eb78a Daniel Lee            2015-04-06  708  		u32 len; /* Fast Open option length */
2100c8d2d9db23c Yuchung Cheng         2012-07-19  709  
7f9b838b71eb78a Daniel Lee            2015-04-06  710  		if (foc->exp) {
7f9b838b71eb78a Daniel Lee            2015-04-06  711  			len = TCPOLEN_EXP_FASTOPEN_BASE + foc->len;
7f9b838b71eb78a Daniel Lee            2015-04-06  712  			*ptr = htonl((TCPOPT_EXP << 24) | (len << 16) |
2100c8d2d9db23c Yuchung Cheng         2012-07-19  713  				     TCPOPT_FASTOPEN_MAGIC);
7f9b838b71eb78a Daniel Lee            2015-04-06  714  			p += TCPOLEN_EXP_FASTOPEN_BASE;
7f9b838b71eb78a Daniel Lee            2015-04-06  715  		} else {
7f9b838b71eb78a Daniel Lee            2015-04-06  716  			len = TCPOLEN_FASTOPEN_BASE + foc->len;
7f9b838b71eb78a Daniel Lee            2015-04-06  717  			*p++ = TCPOPT_FASTOPEN;
7f9b838b71eb78a Daniel Lee            2015-04-06  718  			*p++ = len;
7f9b838b71eb78a Daniel Lee            2015-04-06  719  		}
2100c8d2d9db23c Yuchung Cheng         2012-07-19  720  
7f9b838b71eb78a Daniel Lee            2015-04-06  721  		memcpy(p, foc->val, foc->len);
7f9b838b71eb78a Daniel Lee            2015-04-06  722  		if ((len & 3) == 2) {
7f9b838b71eb78a Daniel Lee            2015-04-06  723  			p[foc->len] = TCPOPT_NOP;
7f9b838b71eb78a Daniel Lee            2015-04-06  724  			p[foc->len + 1] = TCPOPT_NOP;
2100c8d2d9db23c Yuchung Cheng         2012-07-19  725  		}
7f9b838b71eb78a Daniel Lee            2015-04-06  726  		ptr += (len + 3) >> 2;
2100c8d2d9db23c Yuchung Cheng         2012-07-19  727  	}
60e2a7780793bae Ursula Braun          2017-10-25  728  
60e2a7780793bae Ursula Braun          2017-10-25  729  	smc_options_write(ptr, &options);
eda7acddf8080bb Peter Krystad         2020-01-21  730  
ea66758c1795cef Paolo Abeni           2022-05-04  731  	mptcp_options_write(th, ptr, tp, opts);
                                                                                     ^^
Not checked here either.

60e2a7780793bae Ursula Braun          2017-10-25  732  }

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp  
_______________________________________________
kbuild mailing list -- kbuild(a)lists.01.org
To unsubscribe send an email to kbuild-leave(a)lists.01.org

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 00/31] net/tcp: Add TCP-AO support
  2022-08-21 20:34 ` [PATCH 00/31] net/tcp: Add TCP-AO support Leonard Crestez
  2022-08-21 23:51   ` David Ahern
@ 2022-08-22 18:42   ` Salam Noureddine
  1 sibling, 0 replies; 65+ messages in thread
From: Salam Noureddine @ 2022-08-22 18:42 UTC (permalink / raw)
  To: Leonard Crestez
  Cc: Dmitry Safonov, Eric Dumazet, David S. Miller, David Ahern,
	Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, Dmitry Safonov,
	Eric Biggers, Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI,
	Ivan Delalande, Jakub Kicinski, Paolo Abeni, Shuah Khan, netdev,
	linux-crypto, linux-kernel

On Sun, Aug 21, 2022 at 1:34 PM Leonard Crestez <cdleonard@gmail.com> wrote:
>
> On 8/18/22 19:59, Dmitry Safonov wrote:
> > This patchset implements the TCP-AO option as described in RFC5925. There
> > is a request from industry to move away from TCP-MD5SIG and it seems the time
> > is right to have a TCP-AO upstreamed. This TCP option is meant to replace
> > the TCP MD5 option and address its shortcomings.
...
> >
> > The patch set was written as a collaboration of three authors (in alphabetical
> > order): Dmitry Safonov, Francesco Ruggeri and Salam Noureddine. Additional
> > credits should be given to Prasad Koya, who was involved in early prototyping
> > a few years back. There is also a separate submission done by Leonard Crestez
> > whom we thank for his efforts getting an implementation of RFC5925 submitted
> > for review upstream [2]. This is an independent implementation that makes
> > different design decisions.
>
> Is this based on something that Arista has had running for a while now
> or is a recent new development?
>

This is based on prototype code we had worked on internally three years
ago. The implementation effort was restarted to get it over the finish
line. For business reasons we had to have our own implementation ready
and not tied to unmerged upstream code. Please also note that our
implementation is based on linux-4.19 and was only ported forward to
mainline recently. So it wasn’t ready to be submitted upstream.

> > For example, we chose a similar design to the TCP-MD5SIG implementation and
> > used setsockopt()s to program per-socket keys, avoiding the extra complexity
> > of managing a centralized key database in the kernel. A centralized database
> > in the kernel has dubious benefits since it doesn’t eliminate per-socket
> > setsockopts needed to specify which sockets need TCP-AO and what are the
> > currently preferred keys. It also complicates traffic key caching and
> > preventing deletion of in-use keys.
>
> My implementation started with per-socket lists but switched to a global
> list because this way is much easier to manage from userspace. In
> practice userspace apps will want to ensure that all sockets use the
> same set of keys anyway.
>

We did consider a global list early on but we didn’t find it
beneficial. We still believe that per-socket lists reduce complexity
of the implementation, are more scalable and ensure predictable
behavior. Our expectation is that TCP-AO will be only useful for a
limited set of routing applications, rather than used transparently
like IPSEC for non-routing apps. We would be happy to discuss this in
more detail.

> > In this implementation, a centralized database of keys can be thought of
> > as living in user space and user applications would have to program those
> > keys on matching sockets. On the server side, the user application programs
> > keys (MKTS in TCP-AO nomenclature) on the listening socket for all peers that
> > are expected to connect. Prefix matching on the peer address is supported.
...
>
> My series doesn't try to prevent inconsistencies inside the key lists
> because it's not clear that the kernel should prevent userspace from
> shooting itself in the foot. Worst case is connection failure on
> misconfiguration which seems fine.
>
> The RFC doesn't specify in detail how key management is to be performed,
> for example if two valid keys are available it doesn't mention which one
> should be used. Some guidance is found in RFC8177 but again not very much.
>
> I implemented an ABI that can be used by userspace for RFC8177-style key
> management and asked for feedback but received very little. If you had
> come with a clear ABI proposal I would have tried to implement it.
>
> Here's a link to our older discussion:
>
> https://lore.kernel.org/netdev/e7f0449a-2bad-99ad-4737-016a0e6b8b84@gmail.com/
>
> Seeing an entirely distinct unrelated implementation is very unexpected.
> What made you do this?
>
> --
> Regards,
> Leonard

Our goal was not to have a competing TCP-AO upstream submission but
to implement the RFC for our customers to use. Had there been an
already upstreamed implementation we would have used it and
implemented customer requirements on top of it. Just like we do with
all other kernel features. This is not a bad situation, we believe it
is good for the upstream community to have two fully functioning
implementations to consider. Possibly a third collaborative
submission might emerge that takes the best of both. A year ago, there
wasn’t much available online about TCP-AO besides the RFC. We are
excited with the current interest in TCP-AO and hope to see it
upstreamed soon.

Best,

Salam

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 00/31] net/tcp: Add TCP-AO support
  2022-08-21 23:51   ` David Ahern
@ 2022-08-22 20:35     ` Dmitry Safonov
  2022-08-23 15:30       ` Leonard Crestez
  0 siblings, 1 reply; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-22 20:35 UTC (permalink / raw)
  To: David Ahern, Leonard Crestez, Eric Dumazet, David S. Miller
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, Dmitry Safonov,
	Eric Biggers, Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI,
	Ivan Delalande, Jakub Kicinski, Paolo Abeni, Salam Noureddine,
	Shuah Khan, netdev, linux-crypto, linux-kernel

Hi Leonard, David,

On 8/22/22 00:51, David Ahern wrote:
> On 8/21/22 2:34 PM, Leonard Crestez wrote:
>> On 8/18/22 19:59, Dmitry Safonov wrote:
>>> This patchset implements the TCP-AO option as described in RFC5925. There
>>> is a request from industry to move away from TCP-MD5SIG and it seems
>>> the time
>>> is right to have a TCP-AO upstreamed. This TCP option is meant to replace
>>> the TCP MD5 option and address its shortcomings. Specifically, it
>>> provides
>>> more secure hashing, key rotation and support for long-lived connections
>>> (see the summary of TCP-AO advantages over TCP-MD5 in (1.3) of RFC5925).
>>> The patch series starts with six patches that are not specific to TCP-AO
>>> but implement a general crypto facility that we thought is useful
>>> to eliminate code duplication between TCP-MD5SIG and TCP-AO as well as
>>> other
>>> crypto users. These six patches are being submitted separately in
>>> a different patchset [1]. Including them here will show better the gain
>>> in code sharing. Next are 18 patches that implement the actual TCP-AO
>>> option,
>>> followed by patches implementing selftests.
>>>
>>> The patch set was written as a collaboration of three authors (in
>>> alphabetical
>>> order): Dmitry Safonov, Francesco Ruggeri and Salam Noureddine.
>>> Additional
>>> credits should be given to Prasad Koya, who was involved in early
>>> prototyping
>>> a few years back. There is also a separate submission done by Leonard
>>> Crestez
>>> whom we thank for his efforts getting an implementation of RFC5925
>>> submitted
>>> for review upstream [2]. This is an independent implementation that makes
>>> different design decisions.
>>
>> Is this based on something that Arista has had running for a while now
>> or is a recent new development?
>>
> 
> ...
> 
>> Seeing an entirely distinct unrelated implementation is very unexpected.
>> What made you do this?
>>
> 
> I am curious as well. You are well aware of Leonard's efforts which go
> back a long time, why go off and do a separate implementation?

When I started working on this, there was a prototype that was neither
good for upstream, nor for customers. At the moment Leonard submitted
his RFC, I was already giving feedback/reviews to local code and
patches. So, as I was aware of the details of TCP-AO, I started giving
Leonard feedback and reviews, based on what I've learned from RFC/code.
I thought whatever code will make it upstream, it can benefit from my
reviews. Some of my comments were based on a better code I saw locally,
or a way of improving it that I've suggested to both sides.

I'm quite happy that Leonard addressed some of my comments (i.e.
extendable syscalls), I see that it improved his patches.
On the other hand, some of the comments I've left moved to "known
limitations" with no foreseeable plan to fix them, while they were
addressed in local/Arista code.

And now a little bit more than a year later, it seems that the quality
of local patches has reached a point where they can be submitted for
an upstream review. So, please don't misunderstand me, it's not that
"drop your implementation, take ours" and it's not that we've
intentionally hidden that we're working on TCP-AO. It's that it is the
first moment we can make upstream aware of an alternative implementation.

Personally, I think it's best for opensource community:
- Arista's implementation is now public
- there are now at least 4 people that understand RFC5925 and the
  code/details
- in a discussion, it will be possible to find what will be the best
  from both implementations for Linux and come up with better code

At this particular moment, it seems neither of patch sets is ready to be
merged "as-is". But it seems that there's enough interest from both
sides and likely it guarantees that there will be enough effort to make
something merge-able, that will work for all interested parties.

As for my part, I'm interested in the best code upstream, regardless who
is the author. This includes:
- reusing the existing TCP-MD5 code, rather than copying'n'pasting for
  TCP-AO with intent to refactor it some day later
- making setsockopt()s and other syscalls extendable
- cover functionality with selftests
- following RFC5925 in implementation, especially "required" and "must"
  parts

I hope that clarifies how and why now there are two patch sets that
implement the same RFC/functionality.

Thanks,
          Dmitry

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 23/31] net/tcp: Add getsockopt(TCP_AO_GET)
  2022-08-18 16:59 ` [PATCH 23/31] net/tcp: Add getsockopt(TCP_AO_GET) Dmitry Safonov
@ 2022-08-23 14:45   ` Leonard Crestez
  0 siblings, 0 replies; 65+ messages in thread
From: Leonard Crestez @ 2022-08-23 14:45 UTC (permalink / raw)
  To: Dmitry Safonov
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, David Ahern,
	Dmitry Safonov, Eric Biggers, Francesco Ruggeri, Herbert Xu,
	Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski, Paolo Abeni,
	Salam Noureddine, Shuah Khan, netdev, linux-crypto, linux-kernel,
	Eric Dumazet, David S. Miller

On 8/18/22 19:59, Dmitry Safonov wrote:
> Introduce getsockopt() that let user get TCP-AO keys and their
> properties from a socket. A user can provide a filter to match
> a specific key to be dumped or TCP_AO_GET_ALL flag may be used to dump
> all keys in one syscall.

No equivalent for this exists for TCP_MD5SIG or my TCP_AUTHOPT series. I 
do however have a proc file to dump all keys in the system.

The list of keys is normally fully controlled by a single application so 
it shouldn't need to read back the keys that it wrote itself. The real 
reason this exists is because on the server side keys are copied on 
"synack" rather than "accept" and userspace can't know if a newly 
accepted socket has all the latest keychain updates.

This effectively dumps responsibility for a kernel implementation race 
onto userspace. At least you should mention how it's meant to be used in 
the commit message, and that it's not really optional.

I think making keys global is easier for userspace to use, despite the 
difference versus TCP_MD5.

--
Regards,
Leonard

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s
  2022-08-18 16:59 ` [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s Dmitry Safonov
  2022-08-18 18:50   ` kernel test robot
  2022-08-18 18:50   ` kernel test robot
@ 2022-08-23 14:45   ` Leonard Crestez
  2022-08-31 18:48     ` Dmitry Safonov
  2022-08-25 15:31   ` David Ahern
  3 siblings, 1 reply; 65+ messages in thread
From: Leonard Crestez @ 2022-08-23 14:45 UTC (permalink / raw)
  To: Dmitry Safonov
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, David Ahern,
	Dmitry Safonov, Eric Biggers, Francesco Ruggeri, Herbert Xu,
	Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski, Paolo Abeni,
	Salam Noureddine, Shuah Khan, netdev, linux-crypto, Eric Dumazet,
	linux-kernel, David S. Miller

On 8/18/22 19:59, Dmitry Safonov wrote:
> Add 3 setsockopt()s:
> 1. to add a new Master Key Tuple (MKT) on a socket
> 2. to delete present MKT from a socket
> 3. to change flags of an MKT
> 
> Userspace has to introduce keys on every socket it wants to use TCP-AO
> option on, similarly to TCP_MD5SIG/TCP_MD5SIG_EXT.
> RFC5925 prohibits definition of MKTs that would match the same peer,
> so do sanity checks on the data provided by userspace. Be as
> conservative as possible, including refusal of defining MKT on
> an established connection with no AO, removing the key in-use and etc.
> 
> (1) and (2) are to be used by userspace key manager to add/remove keys.
> (3) main purpose is to set rnext_key, which (as prescribed by RFC5925)
> is the key id that will be requested in TCP-AO header from the peer to
> sign their segments with.
> 
> At this moment the life of ao_info ends in tcp_v4_destroy_sock().


> +#define TCP_AO			38	/* (Add/Set MKT) */
> +#define TCP_AO_DEL		39	/* (Delete MKT) */
> +#define TCP_AO_MOD		40	/* (Modify MKT) */

The TCP_AO_MOD sockopt doesn't actually modify and MKT, it only controls 
per-socket properties. It is equivalent to my TCP_AUTHOPT sockopt while 
TCP_AO is equivalent to TCP_AUTHOPT_KEY. My equivalent of TCP_AO_DEL 
sockopt is a flag inside tcp_authopt_key.

> +struct tcp_ao { /* setsockopt(TCP_AO) */
> +	struct __kernel_sockaddr_storage tcpa_addr;
> +	char	tcpa_alg_name[64];
> +	__u16	tcpa_flags;

This field accept TCP_AO_CMDF_CURR and TCP_AO_CMDF_NEXT which means that 
you are combining key addition with key selection. Not clear it 
shouldn't just always be a separate sockopt?

> +	__u8	tcpa_prefix;
> +	__u8	tcpa_sndid;
> +	__u8	tcpa_rcvid;
> +	__u8	tcpa_maclen;
> +	__u8	tcpa_keyflags;
> +	__u8	tcpa_keylen;
> +	__u8	tcpa_key[TCP_AO_MAXKEYLEN];
> +} __attribute__((aligned(8)));
> +
> +struct tcp_ao_del { /* setsockopt(TCP_AO_DEL) */
> +	struct __kernel_sockaddr_storage tcpa_addr;
> +	__u16	tcpa_flags;
> +	__u8	tcpa_prefix;
> +	__u8	tcpa_sndid;
> +	__u8	tcpa_rcvid;
> +	__u8	tcpa_current;
> +	__u8	tcpa_rnext;
> +} __attribute__((aligned(8)));
> +
> +struct tcp_ao_mod { /* setsockopt(TCP_AO_MOD) */
> +	__u16	tcpa_flags;
> +	__u8	tcpa_current;
> +	__u8	tcpa_rnext;
> +} __attribute__((aligned(8)));

This is quite similar to my "struct tcp_authopt" in the fact that it is 
intented to support controlling the "current keys".

* tcpa_current is equivalent to send_keyid
* tcpa_rnext is equivalent to send_rnextkeyid

I also have two fields called "recv_keyid" and "recv_rnextkeyid" which 
inform userspace about what the remote is sending, I'm not seeing an 
equivalent on your side.

The specification around send_keyid in the RFC is conflicting:
* User must be able to control it
* Implementation must respect rnextkeyid in incoming packet

I solved this apparent conflict by adding a 
"TCP_AUTHOPT_FLAG_LOCK_KEYID" flag so that user can choose if it wants 
to control the sending key or let it be controlled from the other side.

The "send_rnextkeyid" is also optional in my patch, if 
TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID is not passed then the recv_id of the 
sending key is sent.

Here's a link to my implementation of key selection controls:

https://lore.kernel.org/netdev/2956d99e7fbf9ff2a8cc720c67baaef35bc32343.1660852705.git.cdleonard@gmail.com/

> +static int tcp_ao_parse_crypto(struct tcp_ao *cmd, struct tcp_ao_key *key)
> +{
> +	unsigned int syn_tcp_option_space;
> +	struct crypto_pool_ahash hp;
> +	bool is_kdf_aes_128_cmac = false;
> +	struct crypto_ahash *tfm;
> +	int err, pool_id;
> +
> +	/* Force null-termination of tcpa_alg_name */
> +	cmd->tcpa_alg_name[ARRAY_SIZE(cmd->tcpa_alg_name) - 1] = '\0';
> +
> +	/* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */
> +	if (!strcmp("cmac(aes128)", cmd->tcpa_alg_name)) {
> +		strcpy(cmd->tcpa_alg_name, "cmac(aes)");
> +		is_kdf_aes_128_cmac = (cmd->tcpa_keylen != 16);
> +	}

Only two algorithms are defined in RFC5926 and you have to treat one of 
them as a special case. I remain convinced that generic support for 
arbitrary algorithms is undesirable; it's better for the algorithm to be 
specified as an enum.

--
Regards,
Leonard

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 19/31] net/tcp: Add TCP-AO SNE support
  2022-08-18 16:59 ` [PATCH 19/31] net/tcp: Add TCP-AO SNE support Dmitry Safonov
@ 2022-08-23 14:50   ` Leonard Crestez
  2022-08-23 22:40     ` Francesco Ruggeri
  0 siblings, 1 reply; 65+ messages in thread
From: Leonard Crestez @ 2022-08-23 14:50 UTC (permalink / raw)
  To: Dmitry Safonov
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, David Ahern,
	Dmitry Safonov, Eric Biggers, Francesco Ruggeri, Herbert Xu,
	Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski, Paolo Abeni,
	Salam Noureddine, Shuah Khan, netdev, linux-crypto, Eric Dumazet,
	David S. Miller, linux-kernel

On 8/18/22 19:59, Dmitry Safonov wrote:
> Add Sequence Number Extension (SNE) extension 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).

> +#ifdef CONFIG_TCP_AO
> +	ao = rcu_dereference_protected(tp->ao_info,
> +				       lockdep_sock_is_held((struct sock *)tp));
> +	if (ao) {
> +		if (ack < ao->snd_sne_seq)
> +			ao->snd_sne++;
> +		ao->snd_sne_seq = ack;
> +	}
> +#endif
>   	tp->snd_una = ack;
>   }

... snip ...

> +#ifdef CONFIG_TCP_AO
> +	ao = rcu_dereference_protected(tp->ao_info,
> +				       lockdep_sock_is_held((struct sock *)tp));
> +	if (ao) {
> +		if (seq < ao->rcv_sne_seq)
> +			ao->rcv_sne++;
> +		ao->rcv_sne_seq = seq;
> +	}
> +#endif
>   	WRITE_ONCE(tp->rcv_nxt, seq);

It should always be the case that (rcv_nxt == rcv_sne_seq) and (snd_una 
== snd_sne_seq) so the _sne_seq fields are redundant. It's possible to 
avoid those extra fields.

However 8 bytes per TCP-AO socket is inconsequential.

--
Regards,
Leonard

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 00/31] net/tcp: Add TCP-AO support
  2022-08-22 20:35     ` Dmitry Safonov
@ 2022-08-23 15:30       ` Leonard Crestez
  2022-08-23 16:31         ` Dmitry Safonov
  2022-08-24 12:46         ` Andrew Lunn
  0 siblings, 2 replies; 65+ messages in thread
From: Leonard Crestez @ 2022-08-23 15:30 UTC (permalink / raw)
  To: Dmitry Safonov, David Ahern
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, Dmitry Safonov,
	Eric Biggers, Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI,
	Ivan Delalande, Jakub Kicinski, Paolo Abeni, Salam Noureddine,
	Shuah Khan, netdev, linux-crypto, linux-kernel, David S. Miller,
	Eric Dumazet

On 8/22/22 23:35, Dmitry Safonov wrote:
> Hi Leonard, David,
> 
> On 8/22/22 00:51, David Ahern wrote:
>> On 8/21/22 2:34 PM, Leonard Crestez wrote:
>>> On 8/18/22 19:59, Dmitry Safonov wrote:
>>>> This patchset implements the TCP-AO option as described in RFC5925. There
>>>> is a request from industry to move away from TCP-MD5SIG and it seems
>>>> the time
>>>> is right to have a TCP-AO upstreamed. This TCP option is meant to replace
>>>> the TCP MD5 option and address its shortcomings. Specifically, it
>>>> provides
>>>> more secure hashing, key rotation and support for long-lived connections
>>>> (see the summary of TCP-AO advantages over TCP-MD5 in (1.3) of RFC5925).
>>>> The patch series starts with six patches that are not specific to TCP-AO
>>>> but implement a general crypto facility that we thought is useful
>>>> to eliminate code duplication between TCP-MD5SIG and TCP-AO as well as
>>>> other
>>>> crypto users. These six patches are being submitted separately in
>>>> a different patchset [1]. Including them here will show better the gain
>>>> in code sharing. Next are 18 patches that implement the actual TCP-AO
>>>> option,
>>>> followed by patches implementing selftests.
>>>>
>>>> The patch set was written as a collaboration of three authors (in
>>>> alphabetical
>>>> order): Dmitry Safonov, Francesco Ruggeri and Salam Noureddine.
>>>> Additional
>>>> credits should be given to Prasad Koya, who was involved in early
>>>> prototyping
>>>> a few years back. There is also a separate submission done by Leonard
>>>> Crestez
>>>> whom we thank for his efforts getting an implementation of RFC5925
>>>> submitted
>>>> for review upstream [2]. This is an independent implementation that makes
>>>> different design decisions.
>>>
>>> Is this based on something that Arista has had running for a while now
>>> or is a recent new development?
>>>
>>
>> ...
>>
>>> Seeing an entirely distinct unrelated implementation is very unexpected.
>>> What made you do this?
>>>
>>
>> I am curious as well. You are well aware of Leonard's efforts which go
>> back a long time, why go off and do a separate implementation?
> 
> When I started working on this, there was a prototype that was neither
> good for upstream, nor for customers. At the moment Leonard submitted
> his RFC, I was already giving feedback/reviews to local code and
> patches. So, as I was aware of the details of TCP-AO, I started giving
> Leonard feedback and reviews, based on what I've learned from RFC/code.
> I thought whatever code will make it upstream, it can benefit from my
> reviews. Some of my comments were based on a better code I saw locally,
> or a way of improving it that I've suggested to both sides.
> 
> I'm quite happy that Leonard addressed some of my comments (i.e.
> extendable syscalls), I see that it improved his patches.
> On the other hand, some of the comments I've left moved to "known
> limitations" with no foreseeable plan to fix them, while they were
> addressed in local/Arista code.
> 
> And now a little bit more than a year later, it seems that the quality
> of local patches has reached a point where they can be submitted for
> an upstream review. So, please don't misunderstand me, it's not that
> "drop your implementation, take ours" and it's not that we've
> intentionally hidden that we're working on TCP-AO. It's that it is the
> first moment we can make upstream aware of an alternative implementation.
> 
> Personally, I think it's best for opensource community:
> - Arista's implementation is now public
> - there are now at least 4 people that understand RFC5925 and the
>    code/details
> - in a discussion, it will be possible to find what will be the best
>    from both implementations for Linux and come up with better code
> 
> At this particular moment, it seems neither of patch sets is ready to be
> merged "as-is". But it seems that there's enough interest from both
> sides and likely it guarantees that there will be enough effort to make
> something merge-able, that will work for all interested parties.
> 
> As for my part, I'm interested in the best code upstream, regardless who
> is the author. This includes:
> - reusing the existing TCP-MD5 code, rather than copying'n'pasting for
>    TCP-AO with intent to refactor it some day later

I had a requirement to deploy on linux 5.4 so I very deliberately 
avoided touching MD5. I'm not sure there very much duplication anyway.

> - making setsockopt()s and other syscalls extendable
> - cover functionality with selftests

My implementation is tested with a standalone python package, this is a 
design choice which doesn't particularly matter.

> - following RFC5925 in implementation, especially "required" and "must"
>    parts

I'm not convinced that "don't delete current key" needs to be literally 
interpreted as a hard requirement for the linux ABI. Most TCP RFCs don't 
specify any sort of API at all and it would be entirely valid to 
implement BGP-TCP-AO as a single executable with no internally 
documented boundaries.

> I hope that clarifies how and why now there are two patch sets that
> implement the same RFC/functionality.

As far as I can tell the biggest problem is that is quite difficult to 
implement the userspace side of TCP-AO complete with key rollover. Our 
two implementation both claim to support this but through different ABI 
and both require active management from userspace.

I think it would make sense to push key validity times and the key 
selection policy entirely in the kernel so that it can handle key 
rotation/expiration by itself. This way userspace only has to configure 
the keys and doesn't have to touch established connections at all.

My series has a "flags" field on the key struct where it can filter by 
IP, prefix, ifindex and so on. It would be possible to add additional 
flags for making the key only valid between certain times (by wall time).

The kernel could then make key selections itself:
  - send rnextkeyid based on the key with the longest recv lifetime
  - send keyid based on remote rnextkeyid.
    - If not applicable (rnextkeyid not found locally, or for SYN) pick 
based on longest send lifetime.
  - If all keys expire then return an error on write()
  - Solve other ambiguities in a predictable way: if valid times are 
equal then pick the lowest numeric send_id or recv_id.

Explicit key selection from userspace could still be supported but it 
would be optional and most apps wouldn't bother implementing their own 
policy. The biggest advantage is that it would be much easier for 
applications to adopt TCP-AO.

--
Regards,
Leonard

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 25/31] selftests/net: Add TCP-AO library
  2022-08-18 16:59 ` [PATCH 25/31] selftests/net: Add TCP-AO library Dmitry Safonov
@ 2022-08-23 15:47   ` Shuah Khan
  2022-09-05 20:24     ` Dmitry Safonov
  2022-09-06 16:34     ` Dmitry Safonov
  0 siblings, 2 replies; 65+ messages in thread
From: Shuah Khan @ 2022-08-23 15:47 UTC (permalink / raw)
  To: Dmitry Safonov, Eric Dumazet, David S. Miller, linux-kernel
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, David Ahern,
	Dmitry Safonov, Eric Biggers, Francesco Ruggeri, Herbert Xu,
	Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto, Shuah Khan

On 8/18/22 10:59 AM, Dmitry Safonov wrote:
> Provide functions to create selftests dedicated to TCP-AO.
> They can run in parallel, as they use temporary net namespaces.
> They can be very specific to the feature being tested.
> This will allow to create a lot of TCP-AO tests, without complicating
> one binary with many --options and to create scenarios, that are
> hard to put in bash script that uses one binary.
> 
> Signed-off-by: Dmitry Safonov <dima@arista.com>
> ---
>   tools/testing/selftests/Makefile              |   1 +
>   tools/testing/selftests/net/tcp_ao/.gitignore |   2 +
>   tools/testing/selftests/net/tcp_ao/Makefile   |  45 +++
>   tools/testing/selftests/net/tcp_ao/connect.c  |  81 +++++
>   .../testing/selftests/net/tcp_ao/lib/aolib.h  | 333 +++++++++++++++++
>   .../selftests/net/tcp_ao/lib/netlink.c        | 341 ++++++++++++++++++
>   tools/testing/selftests/net/tcp_ao/lib/proc.c | 267 ++++++++++++++
>   .../testing/selftests/net/tcp_ao/lib/setup.c  | 297 +++++++++++++++
>   tools/testing/selftests/net/tcp_ao/lib/sock.c | 294 +++++++++++++++
>   .../testing/selftests/net/tcp_ao/lib/utils.c  |  30 ++
>   10 files changed, 1691 insertions(+)
>   create mode 100644 tools/testing/selftests/net/tcp_ao/.gitignore
>   create mode 100644 tools/testing/selftests/net/tcp_ao/Makefile
>   create mode 100644 tools/testing/selftests/net/tcp_ao/connect.c
>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/aolib.h
>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/netlink.c
>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/proc.c
>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/setup.c
>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/sock.c
>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/utils.c
> 
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index 10b34bb03bc1..2a3b15a13ccb 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -46,6 +46,7 @@ TARGETS += net
>   TARGETS += net/af_unix
>   TARGETS += net/forwarding
>   TARGETS += net/mptcp
> +TARGETS += net/tcp_ao

Please look into a wayto invoke all of them instead of adding individual
net/* to the main Makefile. This list seems to be growing. :)

>   TARGETS += netfilter
>   TARGETS += nsfs
>   TARGETS += pidfd

[snip]

> +
> +__attribute__((__format__(__printf__, 2, 3)))
> +static inline void __test_print(void (*fn)(const char *), const char *fmt, ...)
> +{
> +#define TEST_MSG_BUFFER_SIZE 4096
> +	char buf[TEST_MSG_BUFFER_SIZE];
> +	va_list arg;
> +
> +	va_start(arg, fmt);
> +	vsnprintf(buf, sizeof(buf), fmt, arg);
> +	va_end(arg);
> +	fn(buf);
> +}
> +

Is there a reason add these instead of using kselftest_* print
functions?

> +#define test_print(fmt, ...)						\
> +	__test_print(__test_msg, "%ld[%s:%u] " fmt "\n",		\
> +		     syscall(SYS_gettid),				\
> +		     __FILE__, __LINE__, ##__VA_ARGS__)
> +
> +#define test_ok(fmt, ...)						\
> +	__test_print(__test_ok, fmt "\n", ##__VA_ARGS__)
> +
> +#define test_fail(fmt, ...)						\
> +do {									\
> +	if (errno)							\
> +		__test_print(__test_fail, fmt ": %m\n", ##__VA_ARGS__);	\
> +	else								\
> +		__test_print(__test_fail, fmt "\n", ##__VA_ARGS__);	\
> +	test_failed();							\
> +} while(0)
> +
> +#define KSFT_FAIL  1
> +#define test_error(fmt, ...)						\
> +do {									\
> +	if (errno)							\
> +		__test_print(__test_error, "%ld[%s:%u] " fmt ": %m\n",	\
> +			     syscall(SYS_gettid), __FILE__, __LINE__,	\
> +			     ##__VA_ARGS__);				\
> +	else								\
> +		__test_print(__test_error, "%ld[%s:%u] " fmt "\n",	\
> +			     syscall(SYS_gettid), __FILE__, __LINE__,	\
> +			     ##__VA_ARGS__);				\
> +	exit(KSFT_FAIL);						\
> +} while(0)
> +

Is there a reason add these instead of using kselftest_* print
functions?

> + * Timeout on syscalls where failure is not expected.
> + * You may want to rise it if the test machine is very busy.
> + */
> +#ifndef TEST_TIMEOUT_SEC
> +#define TEST_TIMEOUT_SEC	5
> +#endif
> +

Where is the TEST_TIMEOUT_SEC usually defined? Does this come
from shell wrapper that runs this test? Can we add a message before
starting the test print the timeout used?

> +/*
> + * Timeout on connect() where a failure is expected.
> + * If set to 0 - kernel will try to retransmit SYN number of times, set in
> + * /proc/sys/net/ipv4/tcp_syn_retries
> + * By default set to 1 to make tests pass faster on non-busy machine.
> + */
> +#ifndef TEST_RETRANSMIT_SEC
> +#define TEST_RETRANSMIT_SEC	1
> +#endif
> +

Where would this TEST_RETRANSMIT_SEC defined usually?

> +
> +static inline int _test_connect_socket(int sk, const union tcp_addr taddr,
> +					unsigned port, time_t timeout)
> +{
> +#ifdef IPV6_TEST
> +	struct sockaddr_in6 addr = {
> +		.sin6_family	= AF_INET6,
> +		.sin6_port	= htons(port),
> +		.sin6_addr	= taddr.a6,
> +	};
> +#else
> +	struct sockaddr_in addr = {
> +		.sin_family	= AF_INET,
> +		.sin_port	= htons(port),
> +		.sin_addr	= taddr.a4,
> +	};
> +#endif

Why do we defined these here - are they also defined in a kernel
header?

> +	return __test_connect_socket(sk, (void *)&addr, sizeof(addr), timeout);
> +}
> +
> +static inline int test_connect_socket(int sk,
> +		const union tcp_addr taddr, unsigned port)
> +{
> +	return _test_connect_socket(sk, taddr, port, TEST_TIMEOUT_SEC);
> +}
> +
> +extern int test_prepare_ao_sockaddr(struct tcp_ao *ao,
> +		const char *alg, uint16_t flags,
> +		void *addr, size_t addr_sz, uint8_t prefix,
> +		uint8_t sndid, uint8_t rcvid, uint8_t maclen,
> +		uint8_t keyflags, uint8_t keylen, const char *key);
> +
> +static inline int test_prepare_ao(struct tcp_ao *ao,
> +		const char *alg, uint16_t flags,
> +		union tcp_addr in_addr, uint8_t prefix,
> +		uint8_t sndid, uint8_t rcvid, uint8_t maclen,
> +		uint8_t keyflags, uint8_t keylen, const char *key)
> +{
> +#ifdef IPV6_TEST
> +	struct sockaddr_in6 addr = {
> +		.sin6_family	= AF_INET6,
> +		.sin6_port	= 0,
> +		.sin6_addr	= in_addr.a6,
> +	};
> +#else
> +	struct sockaddr_in addr = {
> +		.sin_family	= AF_INET,
> +		.sin_port	= 0,
> +		.sin_addr	= in_addr.a4,
> +	};
> +#endif
> +

Same question here. In general having these ifdefs isn't ideal without
a good reason.

> +	return test_prepare_ao_sockaddr(ao, alg, flags,
> +			(void *)&addr, sizeof(addr), prefix, sndid, rcvid,
> +			maclen, keyflags, keylen, key);
> +}
> +
> +static inline int test_prepare_def_ao(struct tcp_ao *ao,
> +		const char *key, uint16_t flags,
> +		union tcp_addr in_addr, uint8_t prefix,
> +		uint8_t sndid, uint8_t rcvid)
> +{
> +	if (prefix > DEFAULT_TEST_PREFIX)
> +		prefix = DEFAULT_TEST_PREFIX;
> +
> +	return test_prepare_ao(ao, DEFAULT_TEST_ALGO, flags, in_addr,
> +			prefix, sndid, rcvid, 0, 0, strlen(key), key);
> +}
> +
> +extern int test_get_one_ao(int sk, struct tcp_ao_getsockopt *out,
> +			   uint16_t flags, void *addr, size_t addr_sz,
> +			   uint8_t prefix, uint8_t sndid, uint8_t rcvid);
> +extern int test_cmp_getsockopt_setsockopt(const struct tcp_ao *a,
> +					  const struct tcp_ao_getsockopt *b);
> +
> +static inline int test_verify_socket_ao(int sk, struct tcp_ao *ao)
> +{
> +	struct tcp_ao_getsockopt tmp;
> +	int err;
> +
> +	err = test_get_one_ao(sk, &tmp, 0, &ao->tcpa_addr,
> +			sizeof(ao->tcpa_addr), ao->tcpa_prefix,
> +			ao->tcpa_sndid, ao->tcpa_rcvid);
> +	if (err)
> +		return err;

Is this always an error or could this a skip if dependencies aren't
met to run the test? This is a global comment for all error cases.

> +
> +	return test_cmp_getsockopt_setsockopt(ao, &tmp);
> +}
> +
> +static inline int test_set_ao(int sk, const char *key, uint16_t flags,
> +			      union tcp_addr in_addr, uint8_t prefix,
> +			      uint8_t sndid, uint8_t rcvid)
> +{
> +	struct tcp_ao tmp;
> +	int err;
> +
> +	err = test_prepare_def_ao(&tmp, key, flags, in_addr,
> +			prefix, sndid, rcvid);
> +	if (err)
> +		return err;

Same comment as above here.

> +
> +	if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &tmp, sizeof(tmp)) < 0)
> +		return -errno;
> +
> +	return test_verify_socket_ao(sk, &tmp);
> +}
> +
> +extern ssize_t test_server_run(int sk, ssize_t quota, time_t timeout_sec);
> +extern ssize_t test_client_loop(int sk, char *buf, size_t buf_sz,
> +				const size_t msg_len, time_t timeout_sec);
> +extern int test_client_verify(int sk, const size_t msg_len, const size_t nr,
> +			      time_t timeout_sec);
> +
> +struct netstat;
> +extern struct netstat *netstat_read(void);
> +extern void netstat_free(struct netstat *ns);
> +extern void netstat_print_diff(struct netstat *nsa, struct netstat *nsb);
> +extern uint64_t netstat_get(struct netstat *ns,
> +			    const char *name, bool *not_found);
> +
> +static inline uint64_t netstat_get_one(const char *name, bool *not_found)
> +{
> +	struct netstat *ns = netstat_read();
> +	uint64_t ret;
> +
> +	ret = netstat_get(ns, name, not_found);
> +
> +	netstat_free(ns);
> +	return ret;
> +}
> +
> +#endif /* _AOLIB_H_ */
> diff --git a/tools/testing/selftests/net/tcp_ao/lib/netlink.c b/tools/testing/selftests/net/tcp_ao/lib/netlink.c
> new file mode 100644
> index 000000000000..f04757c921d0
> --- /dev/null
> +++ b/tools/testing/selftests/net/tcp_ao/lib/netlink.c
> @@ -0,0 +1,341 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Original from tools/testing/selftests/net/ipsec.c */
> +#include <linux/netlink.h>
> +#include <linux/random.h>
> +#include <linux/rtnetlink.h>
> +#include <linux/veth.h>
> +#include <net/if.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <sys/socket.h>
> +
> +#include "aolib.h"
> +
> +#define MAX_PAYLOAD		2048

tools/testing/selftests/net/gro.c seem to define this as:

#define MAX_PAYLOAD (IP_MAXPACKET - sizeof(struct tcphdr) - sizeof(struct ipv6hdr))

Can you do the same instead of hard-coding?


> +
> +const struct sockaddr_in6 addr_any6 = {
> +	.sin6_family	= AF_INET6,
> +};
> +
> +const struct sockaddr_in addr_any4 = {
> +	.sin_family	= AF_INET,
> +};
> 

A couple of things to look at closely. For some failures such as
memory allocation for the test or not being able to open a file

fnetstat = fopen("/proc/net/netstat", "r");

Is this a failure or missing config or not having the right permissions
to open the fail. All of these cases would be a SKIP and not a test fail.

thanks,
-- Shuah


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 00/31] net/tcp: Add TCP-AO support
  2022-08-23 15:30       ` Leonard Crestez
@ 2022-08-23 16:31         ` Dmitry Safonov
  2022-08-24 12:46         ` Andrew Lunn
  1 sibling, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-23 16:31 UTC (permalink / raw)
  To: Leonard Crestez, David Ahern
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, Dmitry Safonov,
	Eric Biggers, Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI,
	Ivan Delalande, Jakub Kicinski, Paolo Abeni, Salam Noureddine,
	Shuah Khan, netdev, linux-crypto, linux-kernel, David S. Miller,
	Eric Dumazet

On 8/23/22 16:30, Leonard Crestez wrote:
> On 8/22/22 23:35, Dmitry Safonov wrote:
>> Hi Leonard, David,
[..]
>> At this particular moment, it seems neither of patch sets is ready to be
>> merged "as-is". But it seems that there's enough interest from both
>> sides and likely it guarantees that there will be enough effort to make
>> something merge-able, that will work for all interested parties.
>>
>> As for my part, I'm interested in the best code upstream, regardless who
>> is the author. This includes:
>> - reusing the existing TCP-MD5 code, rather than copying'n'pasting for
>>    TCP-AO with intent to refactor it some day later
> 
> I had a requirement to deploy on linux 5.4 so I very deliberately
> avoided touching MD5. I'm not sure there very much duplication anyway.

Yeah, I know what you mean: we deployed it on v4.19. But for the code
upstream I personally prefer to see "reusing" rather than copying.
Lesser code is easier to maintain in future.
Upstream submissions in my view should be based on "what would be easier
to maintain in future", rather than on "what would be easier to backport
to my maintenance release".

>> - making setsockopt()s and other syscalls extendable
>> - cover functionality with selftests
> 
> My implementation is tested with a standalone python package, this is a
> design choice which doesn't particularly matter.
> 
>> - following RFC5925 in implementation, especially "required" and "must"
>>    parts
> 
> I'm not convinced that "don't delete current key" needs to be literally
> interpreted as a hard requirement for the linux ABI. Most TCP RFCs don't
> specify any sort of API at all and it would be entirely valid to
> implement BGP-TCP-AO as a single executable with no internally
> documented boundaries.

I agree that RFC requirements and "musts" can be implemented in
userspace, rather than in kernel. On the other hand, my opinion is that
if you have "must"/"must not"/"required" in RFC and it's not hard to
limit those in kernel, than you _should_ do it.
In this point of view, debugging "hey, setsockopt() for key removal
returned -EBUSY, what's going on?" is better than "hey, tcp connection
died on my side and I didn't have tcp dump running, what was that?".

>> I hope that clarifies how and why now there are two patch sets that
>> implement the same RFC/functionality.
> 
> As far as I can tell the biggest problem is that is quite difficult to
> implement the userspace side of TCP-AO complete with key rollover. Our
> two implementation both claim to support this but through different ABI
> and both require active management from userspace.
> 
> I think it would make sense to push key validity times and the key
> selection policy entirely in the kernel so that it can handle key
> rotation/expiration by itself. This way userspace only has to configure
> the keys and doesn't have to touch established connections at all.

Respectfully I disagree here. I think all such policies should be
implemented in userspace. The kernel has to have as lesser as possible,
but enough to provide a way to sign, verify, log messages on TCP segments.
All the logic that may change, all business decisions and key management
should be implemented in userspace, keeping the kernel part as easier in
"KISS" sense as possible.

> My series has a "flags" field on the key struct where it can filter by
> IP, prefix, ifindex and so on. It would be possible to add additional
> flags for making the key only valid between certain times (by wall time).
> 
> The kernel could then make key selections itself:
>  - send rnextkeyid based on the key with the longest recv lifetime
>  - send keyid based on remote rnextkeyid.
>    - If not applicable (rnextkeyid not found locally, or for SYN) pick
> based on longest send lifetime.
>  - If all keys expire then return an error on write()
>  - Solve other ambiguities in a predictable way: if valid times are
> equal then pick the lowest numeric send_id or recv_id.
> 
> Explicit key selection from userspace could still be supported but it
> would be optional and most apps wouldn't bother implementing their own
> policy. The biggest advantage is that it would be much easier for
> applications to adopt TCP-AO.

Personally, I would think that all you mentioned better stay in
userspace app. The kernel should do as minimal and as much predictable
as possible job here, without 10 possible outcomes. If you want to share
the logic of key rotation/expiration, all timers and synchronization
between different BGP applications, that would be a proper job for a
shared library.

Thanks,
          Dmitry

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 19/31] net/tcp: Add TCP-AO SNE support
  2022-08-23 14:50   ` Leonard Crestez
@ 2022-08-23 22:40     ` Francesco Ruggeri
  0 siblings, 0 replies; 65+ messages in thread
From: Francesco Ruggeri @ 2022-08-23 22:40 UTC (permalink / raw)
  To: Leonard Crestez
  Cc: Dmitry Safonov, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	David Ahern, Dmitry Safonov, Eric Biggers, Herbert Xu,
	Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski, Paolo Abeni,
	Salam Noureddine, Shuah Khan, netdev, linux-crypto, Eric Dumazet,
	David S. Miller, linux-kernel

On Tue, Aug 23, 2022 at 7:50 AM Leonard Crestez <cdleonard@gmail.com> wrote:
>
> On 8/18/22 19:59, Dmitry Safonov wrote:
> > Add Sequence Number Extension (SNE) extension 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).
>
> > +#ifdef CONFIG_TCP_AO
> > +     ao = rcu_dereference_protected(tp->ao_info,
> > +                                    lockdep_sock_is_held((struct sock *)tp));
> > +     if (ao) {
> > +             if (ack < ao->snd_sne_seq)
> > +                     ao->snd_sne++;
> > +             ao->snd_sne_seq = ack;
> > +     }
> > +#endif
> >       tp->snd_una = ack;
> >   }
>
> ... snip ...
>
> > +#ifdef CONFIG_TCP_AO
> > +     ao = rcu_dereference_protected(tp->ao_info,
> > +                                    lockdep_sock_is_held((struct sock *)tp));
> > +     if (ao) {
> > +             if (seq < ao->rcv_sne_seq)
> > +                     ao->rcv_sne++;
> > +             ao->rcv_sne_seq = seq;
> > +     }
> > +#endif
> >       WRITE_ONCE(tp->rcv_nxt, seq);
>
> It should always be the case that (rcv_nxt == rcv_sne_seq) and (snd_una
> == snd_sne_seq) so the _sne_seq fields are redundant. It's possible to
> avoid those extra fields.

There are cases where rcv_nxt and snd_una are set outside of
tcp_rcv_nxt_update() and tcp_snd_una_update(), mostly during the
initial handshake, so those cases would have to be taken care of
explicitly, especially wrt rollovers.
I see your point though, there may be a potential for some cleaning
up here.

Francesco

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 00/31] net/tcp: Add TCP-AO support
  2022-08-23 15:30       ` Leonard Crestez
  2022-08-23 16:31         ` Dmitry Safonov
@ 2022-08-24 12:46         ` Andrew Lunn
  2022-08-24 17:55           ` Jakub Kicinski
  2022-08-27  8:55           ` Leonard Crestez
  1 sibling, 2 replies; 65+ messages in thread
From: Andrew Lunn @ 2022-08-24 12:46 UTC (permalink / raw)
  To: Leonard Crestez
  Cc: Dmitry Safonov, David Ahern, Andy Lutomirski, Ard Biesheuvel,
	Bob Gilligan, Dmitry Safonov, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Paolo Abeni, Salam Noureddine, Shuah Khan, netdev, linux-crypto,
	linux-kernel, David S. Miller, Eric Dumazet

> I think it would make sense to push key validity times and the key selection
> policy entirely in the kernel so that it can handle key rotation/expiration
> by itself. This way userspace only has to configure the keys and doesn't
> have to touch established connections at all.

I know nothing aobut TCP-AO, nor much about kTLS. But doesn't kTLS
have the same issue? Is there anything which can be learnt from kTLS?
Maybe the same mechanisms can be used? No point inventing something
new if you can copy/refactor working code?

> My series has a "flags" field on the key struct where it can filter by IP,
> prefix, ifindex and so on. It would be possible to add additional flags for
> making the key only valid between certain times (by wall time).

What out for wall clock time, it jumps around in funny ways. Plus the
kernel has no idea what time zone the wall the wall clock is mounted
on is in.

    Andrew

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 00/31] net/tcp: Add TCP-AO support
  2022-08-24 12:46         ` Andrew Lunn
@ 2022-08-24 17:55           ` Jakub Kicinski
  2022-08-27  8:55           ` Leonard Crestez
  1 sibling, 0 replies; 65+ messages in thread
From: Jakub Kicinski @ 2022-08-24 17:55 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Leonard Crestez, Dmitry Safonov, David Ahern, Andy Lutomirski,
	Ard Biesheuvel, Bob Gilligan, Dmitry Safonov, Eric Biggers,
	Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande,
	Paolo Abeni, Salam Noureddine, Shuah Khan, netdev, linux-crypto,
	linux-kernel, David S. Miller, Eric Dumazet

On Wed, 24 Aug 2022 14:46:32 +0200 Andrew Lunn wrote:
> > I think it would make sense to push key validity times and the key selection
> > policy entirely in the kernel so that it can handle key rotation/expiration
> > by itself. This way userspace only has to configure the keys and doesn't
> > have to touch established connections at all.  
> 
> I know nothing aobut TCP-AO, nor much about kTLS. But doesn't kTLS
> have the same issue? Is there anything which can be learnt from kTLS?
> Maybe the same mechanisms can be used? No point inventing something
> new if you can copy/refactor working code?

kTLS does not support key rotation FWIW. It's extremely rare.

> > My series has a "flags" field on the key struct where it can filter by IP,
> > prefix, ifindex and so on. It would be possible to add additional flags for
> > making the key only valid between certain times (by wall time).  
> 
> What out for wall clock time, it jumps around in funny ways. Plus the
> kernel has no idea what time zone the wall the wall clock is mounted
> on is in.

I'd do all of this over netlink, next-gen crypto on sockets is *the*
reason I started the YAML work. Sadly my crypto config via netlink
does not exist yet and is probably 2mo out ;(

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s
  2022-08-18 16:59 ` [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s Dmitry Safonov
                     ` (2 preceding siblings ...)
  2022-08-23 14:45   ` Leonard Crestez
@ 2022-08-25 15:31   ` David Ahern
  2022-08-25 18:21     ` David Laight
  3 siblings, 1 reply; 65+ messages in thread
From: David Ahern @ 2022-08-25 15:31 UTC (permalink / raw)
  To: Dmitry Safonov, Eric Dumazet, David S. Miller, linux-kernel
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, Dmitry Safonov,
	Eric Biggers, Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI,
	Ivan Delalande, Jakub Kicinski, Leonard Crestez, Paolo Abeni,
	Salam Noureddine, Shuah Khan, netdev, linux-crypto

On 8/18/22 9:59 AM, Dmitry Safonov wrote:
> diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
> index 849bbf2d3c38..5369458ae89f 100644
> --- a/include/uapi/linux/tcp.h
> +++ b/include/uapi/linux/tcp.h
> @@ -129,6 +129,9 @@ enum {
>  
>  #define TCP_TX_DELAY		37	/* delay outgoing packets by XX usec */
>  
> +#define TCP_AO			38	/* (Add/Set MKT) */
> +#define TCP_AO_DEL		39	/* (Delete MKT) */
> +#define TCP_AO_MOD		40	/* (Modify MKT) */
>  
>  #define TCP_REPAIR_ON		1
>  #define TCP_REPAIR_OFF		0
> @@ -344,6 +347,38 @@ struct tcp_diag_md5sig {
>  
>  #define TCP_AO_MAXKEYLEN	80
>  
> +#define TCP_AO_CMDF_CURR	(1 << 0)	/* Only checks field sndid */
> +#define TCP_AO_CMDF_NEXT	(1 << 1)	/* Only checks field rcvid */
> +
> +struct tcp_ao { /* setsockopt(TCP_AO) */
> +	struct __kernel_sockaddr_storage tcpa_addr;
> +	char	tcpa_alg_name[64];
> +	__u16	tcpa_flags;
> +	__u8	tcpa_prefix;
> +	__u8	tcpa_sndid;
> +	__u8	tcpa_rcvid;
> +	__u8	tcpa_maclen;
> +	__u8	tcpa_keyflags;
> +	__u8	tcpa_keylen;
> +	__u8	tcpa_key[TCP_AO_MAXKEYLEN];
> +} __attribute__((aligned(8)));
> +
> +struct tcp_ao_del { /* setsockopt(TCP_AO_DEL) */
> +	struct __kernel_sockaddr_storage tcpa_addr;
> +	__u16	tcpa_flags;
> +	__u8	tcpa_prefix;
> +	__u8	tcpa_sndid;
> +	__u8	tcpa_rcvid;
> +	__u8	tcpa_current;
> +	__u8	tcpa_rnext;
> +} __attribute__((aligned(8)));
> +
> +struct tcp_ao_mod { /* setsockopt(TCP_AO_MOD) */
> +	__u16	tcpa_flags;
> +	__u8	tcpa_current;
> +	__u8	tcpa_rnext;
> +} __attribute__((aligned(8)));
> +
>  /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
>  
>  #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1


I do not see anything in the uapi that would specify the VRF for the
address.


^ permalink raw reply	[flat|nested] 65+ messages in thread

* RE: [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s
  2022-08-25 15:31   ` David Ahern
@ 2022-08-25 18:21     ` David Laight
  0 siblings, 0 replies; 65+ messages in thread
From: David Laight @ 2022-08-25 18:21 UTC (permalink / raw)
  To: 'David Ahern',
	Dmitry Safonov, Eric Dumazet, David S. Miller, linux-kernel
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, Dmitry Safonov,
	Eric Biggers, Francesco Ruggeri, Herbert Xu, Hideaki YOSHIFUJI,
	Ivan Delalande, Jakub Kicinski, Leonard Crestez, Paolo Abeni,
	Salam Noureddine, Shuah Khan, netdev, linux-crypto

From: David Ahern
> Sent: 25 August 2022 16:32
> 
> On 8/18/22 9:59 AM, Dmitry Safonov wrote:
> > diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h
> > index 849bbf2d3c38..5369458ae89f 100644
> > --- a/include/uapi/linux/tcp.h
> > +++ b/include/uapi/linux/tcp.h
> > @@ -129,6 +129,9 @@ enum {
> >
> >  #define TCP_TX_DELAY		37	/* delay outgoing packets by XX usec */
> >
> > +#define TCP_AO			38	/* (Add/Set MKT) */
> > +#define TCP_AO_DEL		39	/* (Delete MKT) */
> > +#define TCP_AO_MOD		40	/* (Modify MKT) */
> >
> >  #define TCP_REPAIR_ON		1
> >  #define TCP_REPAIR_OFF		0
> > @@ -344,6 +347,38 @@ struct tcp_diag_md5sig {
> >
> >  #define TCP_AO_MAXKEYLEN	80
> >
> > +#define TCP_AO_CMDF_CURR	(1 << 0)	/* Only checks field sndid */
> > +#define TCP_AO_CMDF_NEXT	(1 << 1)	/* Only checks field rcvid */
> > +
> > +struct tcp_ao { /* setsockopt(TCP_AO) */
> > +	struct __kernel_sockaddr_storage tcpa_addr;
> > +	char	tcpa_alg_name[64];
> > +	__u16	tcpa_flags;
> > +	__u8	tcpa_prefix;
> > +	__u8	tcpa_sndid;
> > +	__u8	tcpa_rcvid;
> > +	__u8	tcpa_maclen;
> > +	__u8	tcpa_keyflags;
> > +	__u8	tcpa_keylen;
> > +	__u8	tcpa_key[TCP_AO_MAXKEYLEN];
> > +} __attribute__((aligned(8)));
> > +
> > +struct tcp_ao_del { /* setsockopt(TCP_AO_DEL) */
> > +	struct __kernel_sockaddr_storage tcpa_addr;
> > +	__u16	tcpa_flags;
> > +	__u8	tcpa_prefix;
> > +	__u8	tcpa_sndid;
> > +	__u8	tcpa_rcvid;
> > +	__u8	tcpa_current;
> > +	__u8	tcpa_rnext;
> > +} __attribute__((aligned(8)));
> > +
> > +struct tcp_ao_mod { /* setsockopt(TCP_AO_MOD) */
> > +	__u16	tcpa_flags;
> > +	__u8	tcpa_current;
> > +	__u8	tcpa_rnext;
> > +} __attribute__((aligned(8)));
> > +
> >  /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */
> >
> >  #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1
> 
> 
> I do not see anything in the uapi that would specify the VRF for the
> address.

(Having not spotted the original...)

You've also got implicit padding in the API structures.
That is generally a recipe for disaster.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 02/31] crypto_pool: Add crypto_pool_reserve_scratch()
  2022-08-22 10:45 ` Dan Carpenter
@ 2022-08-26 14:42   ` Dmitry Safonov
  -1 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-26 14:42 UTC (permalink / raw)
  To: Dan Carpenter, kbuild, Eric Dumazet, David S. Miller, linux-kernel
  Cc: lkp, kbuild-all, netdev, Andy Lutomirski, Ard Biesheuvel,
	Bob Gilligan, David Ahern, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	linux-crypto

Hi Dan,

On 8/22/22 11:45, Dan Carpenter wrote:
> Hi Dmitry,
[..]
> "err" not set.  It was supposed to be set to zero at the start.  But
> better to say "ret = i;" here maybe?
> 
> Why is i unsigned?  It leads to unsightly casts.  Presumably some static
> checker insists on this... :/

Thanks! Will be addressed/reworked in v2.

Thank you,
          Dmitry

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 02/31] crypto_pool: Add crypto_pool_reserve_scratch()
@ 2022-08-26 14:42   ` Dmitry Safonov
  0 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-26 14:42 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 371 bytes --]

Hi Dan,

On 8/22/22 11:45, Dan Carpenter wrote:
> Hi Dmitry,
[..]
> "err" not set.  It was supposed to be set to zero at the start.  But
> better to say "ret = i;" here maybe?
> 
> Why is i unsigned?  It leads to unsightly casts.  Presumably some static
> checker insists on this... :/

Thanks! Will be addressed/reworked in v2.

Thank you,
          Dmitry

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 00/31] net/tcp: Add TCP-AO support
  2022-08-24 12:46         ` Andrew Lunn
  2022-08-24 17:55           ` Jakub Kicinski
@ 2022-08-27  8:55           ` Leonard Crestez
  1 sibling, 0 replies; 65+ messages in thread
From: Leonard Crestez @ 2022-08-27  8:55 UTC (permalink / raw)
  To: Andrew Lunn, Dmitry Safonov
  Cc: David Ahern, Andy Lutomirski, Ard Biesheuvel, Bob Gilligan,
	Dmitry Safonov, Eric Biggers, Francesco Ruggeri, Herbert Xu,
	Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski, Paolo Abeni,
	Salam Noureddine, Shuah Khan, netdev, linux-crypto, linux-kernel,
	David S. Miller, Eric Dumazet



On 8/24/22 15:46, Andrew Lunn wrote:
>> I think it would make sense to push key validity times and the key selection
>> policy entirely in the kernel so that it can handle key rotation/expiration
>> by itself. This way userspace only has to configure the keys and doesn't
>> have to touch established connections at all.
> 
> I know nothing aobut TCP-AO, nor much about kTLS. But doesn't kTLS
> have the same issue? Is there anything which can be learnt from kTLS?
> Maybe the same mechanisms can be used? No point inventing something
> new if you can copy/refactor working code?
> 
>> My series has a "flags" field on the key struct where it can filter by IP,
>> prefix, ifindex and so on. It would be possible to add additional flags for
>> making the key only valid between certain times (by wall time).
> 
> What out for wall clock time, it jumps around in funny ways. Plus the
> kernel has no idea what time zone the wall the wall clock is mounted
> on is in.

A close equivalent seems to exist in ipsec in the "xfrm_lifetime_cfg" 
struct, specifically the soft/hard expires timers. These are optional 
validity times for each xfrm_state which is equivalent to a "key".

I'm not familiar with how those are used but ipsec usually relies on 
complex userspace daemons for managing xfrm states and policies and 
those daemons should be capable of adding and removing keys based on 
internal timers. Still, the linux kernel supports checking for key 
validity on it's own.

--
Regards,
Leonard

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [kbuild] Re: [PATCH 11/31] net/tcp: Add TCP-AO sign to outgoing packets
  2022-08-22 12:03 ` Dan Carpenter
@ 2022-08-29 17:55   ` Dmitry Safonov
  -1 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-29 17:55 UTC (permalink / raw)
  To: Dan Carpenter, kbuild, Eric Dumazet, David S. Miller, linux-kernel
  Cc: lkp, kbuild-all, netdev, Andy Lutomirski, Ard Biesheuvel,
	Bob Gilligan, David Ahern, Eric Biggers, Francesco Ruggeri,
	Herbert Xu, Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	linux-crypto

Hi Dan,

On 8/22/22 13:03, Dan Carpenter wrote:
> Hi Dmitry,
[..]
> ea66758c1795cef Paolo Abeni           2022-05-04  608  static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp,
[..]
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  622  #ifdef CONFIG_TCP_AO
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  623  	if (unlikely(OPTION_AO & options)) {
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  624  		u8 maclen;
> 33ad798c924b4a1 Adam Langley          2008-07-19  625  
> 85df6b860d509a9 Dmitry Safonov        2022-08-18 @626  		if (tp) {
> 
> Can "tp" really be NULL?  Everything else assumes it can't.

It actually can be NULL, see tcp_make_synack().
At this moment code assumes that *either* (tp == NULL) or (tcprsk == NULL).

> 85df6b860d509a9 Dmitry Safonov        2022-08-18  627  			struct tcp_ao_info *ao_info;
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  628  
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  629  			ao_info = rcu_dereference_check(tp->ao_info,
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  630  				lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk));
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  631  			if (WARN_ON_ONCE(!ao_key || !ao_info || !ao_info->rnext_key))
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  632  				goto out_ao;
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  633  			maclen = tcp_ao_maclen(ao_key);
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  634  			*ptr++ = htonl((TCPOPT_AO << 24) |
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  635  				       (tcp_ao_len(ao_key) << 16) |
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  636  				       (ao_key->sndid << 8) |
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  637  				       (ao_info->rnext_key->rcvid));
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  638  		}
> 
> "maclen" not initialized on else path.

Patch 15 ("net/tcp: Wire TCP-AO to request sockets") adds
+		if (tcprsk) {
+			u8 aolen = tcprsk->maclen + sizeof(struct tcp_ao_hdr);
+
+			maclen = tcprsk->maclen;
+			*ptr++ = htonl((TCPOPT_AO << 24) | (aolen << 16) |
+				       (tcprsk->ao_keyid << 8) |
+				       (tcprsk->ao_rcv_next));
+		}

Assuming that tp != NULL OR tcprsk != NULL, maclen is always initialized
_after both patches_.
For version 2, I'll make it (break patches) in more bisect-able way for
static analyzers and do WARN_ON(tp == NULL && tcprsk == NULL) with break
off TCP-AO signing to make sure the assumption will be valid with
the code changing later on.

[..]
> ea66758c1795cef Paolo Abeni           2022-05-04  731  	mptcp_options_write(th, ptr, tp, opts);
>                                                                                      ^^
> Not checked here either.

The function has checks inside.

Thanks,
          Dmitry

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [kbuild] Re: [PATCH 11/31] net/tcp: Add TCP-AO sign to outgoing packets
@ 2022-08-29 17:55   ` Dmitry Safonov
  0 siblings, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-29 17:55 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 2870 bytes --]

Hi Dan,

On 8/22/22 13:03, Dan Carpenter wrote:
> Hi Dmitry,
[..]
> ea66758c1795cef Paolo Abeni           2022-05-04  608  static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp,
[..]
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  622  #ifdef CONFIG_TCP_AO
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  623  	if (unlikely(OPTION_AO & options)) {
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  624  		u8 maclen;
> 33ad798c924b4a1 Adam Langley          2008-07-19  625  
> 85df6b860d509a9 Dmitry Safonov        2022-08-18 @626  		if (tp) {
> 
> Can "tp" really be NULL?  Everything else assumes it can't.

It actually can be NULL, see tcp_make_synack().
At this moment code assumes that *either* (tp == NULL) or (tcprsk == NULL).

> 85df6b860d509a9 Dmitry Safonov        2022-08-18  627  			struct tcp_ao_info *ao_info;
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  628  
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  629  			ao_info = rcu_dereference_check(tp->ao_info,
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  630  				lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk));
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  631  			if (WARN_ON_ONCE(!ao_key || !ao_info || !ao_info->rnext_key))
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  632  				goto out_ao;
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  633  			maclen = tcp_ao_maclen(ao_key);
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  634  			*ptr++ = htonl((TCPOPT_AO << 24) |
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  635  				       (tcp_ao_len(ao_key) << 16) |
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  636  				       (ao_key->sndid << 8) |
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  637  				       (ao_info->rnext_key->rcvid));
> 85df6b860d509a9 Dmitry Safonov        2022-08-18  638  		}
> 
> "maclen" not initialized on else path.

Patch 15 ("net/tcp: Wire TCP-AO to request sockets") adds
+		if (tcprsk) {
+			u8 aolen = tcprsk->maclen + sizeof(struct tcp_ao_hdr);
+
+			maclen = tcprsk->maclen;
+			*ptr++ = htonl((TCPOPT_AO << 24) | (aolen << 16) |
+				       (tcprsk->ao_keyid << 8) |
+				       (tcprsk->ao_rcv_next));
+		}

Assuming that tp != NULL OR tcprsk != NULL, maclen is always initialized
_after both patches_.
For version 2, I'll make it (break patches) in more bisect-able way for
static analyzers and do WARN_ON(tp == NULL && tcprsk == NULL) with break
off TCP-AO signing to make sure the assumption will be valid with
the code changing later on.

[..]
> ea66758c1795cef Paolo Abeni           2022-05-04  731  	mptcp_options_write(th, ptr, tp, opts);
>                                                                                      ^^
> Not checked here either.

The function has checks inside.

Thanks,
          Dmitry

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s
  2022-08-23 14:45   ` Leonard Crestez
@ 2022-08-31 18:48     ` Dmitry Safonov
  2022-09-03  9:35       ` Leonard Crestez
  0 siblings, 1 reply; 65+ messages in thread
From: Dmitry Safonov @ 2022-08-31 18:48 UTC (permalink / raw)
  To: Leonard Crestez
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, David Ahern,
	Dmitry Safonov, Eric Biggers, Francesco Ruggeri, Herbert Xu,
	Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski, Paolo Abeni,
	Salam Noureddine, Shuah Khan, netdev, linux-crypto, Eric Dumazet,
	linux-kernel, David S. Miller

On 8/23/22 15:45, Leonard Crestez wrote:
> On 8/18/22 19:59, Dmitry Safonov wrote:
[..]
>> +#define TCP_AO            38    /* (Add/Set MKT) */
>> +#define TCP_AO_DEL        39    /* (Delete MKT) */
>> +#define TCP_AO_MOD        40    /* (Modify MKT) */
>
> The TCP_AO_MOD sockopt doesn't actually modify and MKT, it only controls
> per-socket properties. It is equivalent to my TCP_AUTHOPT sockopt while
> TCP_AO is equivalent to TCP_AUTHOPT_KEY. My equivalent of TCP_AO_DEL
> sockopt is a flag inside tcp_authopt_key.

Fair point, the comment could be "Modify AO", rather than "Modify MKT".
On the other side, this can later support more per-key changes than in
the initial proposal: i.e., zero per-key counters. Password and rcv/snd
ids can't change to follow RFC text, but non-essentials may.
So, the comment to the command here is not really incorrect.

>> +struct tcp_ao { /* setsockopt(TCP_AO) */
>> +    struct __kernel_sockaddr_storage tcpa_addr;
>> +    char    tcpa_alg_name[64];
>> +    __u16    tcpa_flags;
>
> This field accept TCP_AO_CMDF_CURR and TCP_AO_CMDF_NEXT which means that
> you are combining key addition with key selection. Not clear it
> shouldn't just always be a separate sockopt?

I don't see any downside. A user can add a key and start using it immediately
with one syscall instead of two. It's not necessary, one can do it in
2 setsockopt()s if they want.

[..]
> I also have two fields called "recv_keyid" and "recv_rnextkeyid" which
> inform userspace about what the remote is sending, I'm not seeing an
> equivalent on your side.

Sounds like a good candidate for getsockopt() for logs/debugging.

> The specification around send_keyid in the RFC is conflicting:
> * User must be able to control it

I don't see where you read it, care to point it out?
I see choosing the current_key by marking the preferred key during
an establishment of a connection, but I don't see any "MUST control
current_key". We allow changing current_key, but that's actually
not something required by RFC, the only thing required is to respect
rnext_key that's asked by peer.

> * Implementation must respect rnextkeyid in incoming packet
>
> I solved this apparent conflict by adding a
> "TCP_AUTHOPT_FLAG_LOCK_KEYID" flag so that user can choose if it wants
> to control the sending key or let it be controlled from the other side.

That's exactly violating the above "Implementation must respect
rnextkeyid in incoming packet". See RFC5925 (7.5.2.e).

[..]
> Only two algorithms are defined in RFC5926 and you have to treat one of
> them as a special case. I remain convinced that generic support for
> arbitrary algorithms is undesirable; it's better for the algorithm to be
> specified as an enum.

On contrary, I see that as a really big feature. RFC5926 was published in 2010,
when sha1 was yet hard to break. These days sha1 is considered insecure.
I.e., the first link from Google:

> Starting with version 56, released this month, Google Chrome will mark all
> SHA-1-signed HTTPS certificates as unsafe. Other major browser vendors
> plan to do the same.
> "Hopefully these new efforts of Google of making a real-world attack possible
> will lead to vendors and infrastructure managers quickly removing SHA-1 from
> their products and configurations as, despite it being a deprecated algorithm,
> some vendors still sell products that do not support more modern hashing
> algorithms or charge an extra cost to do so," [..]

So, why limit a new TCP sign feature to already insecure algorithms?
One can already use any crypto algorithms for example, in tunnels.
And I don't see any benefit in defining new magic macros, only downside.

I prefer UAPI that takes crypto algo name as a string, rather than new
defined magic number from one of kernel headers.
IOW,
: strcpy(ao.tcpa_alg_name, "cmac(aes128)");
: setsockopt(sk, IPPROTO_TCP, opt, &ao, sizeof(ao));
is better than
: ao.tcp_alg = TCP_AO_CMAC_MAGIC_DEFINE;
: setsockopt(sk, IPPROTO_TCP, opt, &ao, sizeof(ao));

Neither I see a point in more patches adding new
#define TCP_AO_NEW_ALGO

BTW, I had some patches to add testing in fcnal-test.sh and covered
the following algorithms, that worked just fine (test changes not
included in v1):
hmac(sha1) cmac(aes128) hmac(rmd128) hmac(rmd160) hmac(sha512)
hmac(sha384) hmac(sha256) hmac(md5) hmac(sha224) hmac(sha3-512)

No point in artificially disabling them or introducing new magic #defines.

Thanks,
          Dmitry

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s
  2022-08-31 18:48     ` Dmitry Safonov
@ 2022-09-03  9:35       ` Leonard Crestez
  0 siblings, 0 replies; 65+ messages in thread
From: Leonard Crestez @ 2022-09-03  9:35 UTC (permalink / raw)
  To: Dmitry Safonov
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, David Ahern,
	Dmitry Safonov, Eric Biggers, Francesco Ruggeri, Herbert Xu,
	Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski, Paolo Abeni,
	Salam Noureddine, Shuah Khan, netdev, linux-crypto, Eric Dumazet,
	linux-kernel, David S. Miller

On 8/31/22 21:48, Dmitry Safonov wrote:
> On 8/23/22 15:45, Leonard Crestez wrote:
>> On 8/18/22 19:59, Dmitry Safonov wrote:
> [..]
>>> +#define TCP_AO            38    /* (Add/Set MKT) */
>>> +#define TCP_AO_DEL        39    /* (Delete MKT) */
>>> +#define TCP_AO_MOD        40    /* (Modify MKT) */
>>
>> The TCP_AO_MOD sockopt doesn't actually modify and MKT, it only controls
>> per-socket properties. It is equivalent to my TCP_AUTHOPT sockopt while
>> TCP_AO is equivalent to TCP_AUTHOPT_KEY. My equivalent of TCP_AO_DEL
>> sockopt is a flag inside tcp_authopt_key.
> 
> Fair point, the comment could be "Modify AO", rather than "Modify MKT".
> On the other side, this can later support more per-key changes than in
> the initial proposal: i.e., zero per-key counters. Password and rcv/snd
> ids can't change to follow RFC text, but non-essentials may.
> So, the comment to the command here is not really incorrect.

I think it makes sense to at least separate per-key and per-socket 
options. This way a sockopt for per-socket info doesn't contain fields 
used to identify keys which is much clearer.

>> I also have two fields called "recv_keyid" and "recv_rnextkeyid" which
>> inform userspace about what the remote is sending, I'm not seeing an
>> equivalent on your side.
> 
> Sounds like a good candidate for getsockopt() for logs/debugging.
> 
>> The specification around send_keyid in the RFC is conflicting:
>> * User must be able to control it
> 
> I don't see where you read it, care to point it out?
> I see choosing the current_key by marking the preferred key during
> an establishment of a connection, but I don't see any "MUST control
> current_key". We allow changing current_key, but that's actually
> not something required by RFC, the only thing required is to respect
> rnext_key that's asked by peer.
> 
>> * Implementation must respect rnextkeyid in incoming packet
>>
>> I solved this apparent conflict by adding a
>> "TCP_AUTHOPT_FLAG_LOCK_KEYID" flag so that user can choose if it wants
>> to control the sending key or let it be controlled from the other side.
> 
> That's exactly violating the above "Implementation must respect
> rnextkeyid in incoming packet". See RFC5925 (7.5.2.e).

This is based on paragraphs towards the end of Section 7.1:

 >> TCP SEND, or a sequence of commands resulting in a SEND, MUST be
augmented so that the preferred outgoing MKT (current_key) and/or the
preferred incoming MKT (rnext_key) of a connection can be indicated.

This is for TCP SEND, not just open/connect. I'm reading this as a
requirement that userspace *MUST* be able to control the current key. 
Yes, it does seem contradict 7.5.2.e which is why I implemented this as 
a "key lock flag".

 >> TCP RECEIVE, or the sequence of commands resulting in a RECEIVE,
MUST be augmented so that the KeyID and RNextKeyID of a recently
received segment is available to the user out of band (e.g., as an
additional parameter to RECEIVE or via a STATUS call).

It seems to me that it *MUST* be possible for userspace to read the 
incoming rnextkeyid and handle it by itself. It could choose to follow 
7.5.2.e or it could do something entirely different. When it can't 
respect rnextkeyid because the key is not yet valid then userspace has 
more information to make an alternative current_key decision.

> 
> [..]
>> Only two algorithms are defined in RFC5926 and you have to treat one of
>> them as a special case. I remain convinced that generic support for
>> arbitrary algorithms is undesirable; it's better for the algorithm to be
>> specified as an enum.
> 
> So, why limit a new TCP sign feature to already insecure algorithms?
> One can already use any crypto algorithms for example, in tunnels.
> And I don't see any benefit in defining new magic macros, only downside.

Adding support for arbitrary algorithms increases complexity for no 
real-world gain. There are also lots of corner cases that must be 
treated correctly like odd traffic_keylen and maclen, having an enum
means that userspace can't attempt to trick us. The ABI is also smaller.

There's also a special case in one of the two concrete KDFs defined by 
RFC5925. What if there are more, will the ABI be expanded to support all 
the cases?

Disagreements over whether a particular form of extensibility is 
"useful" are unlikely to result in any sort of useful conclusion. I'm 
lazy so I only care about interop with existing implementations from 
Juniper and Cisco.

--
Regards,
Leonard

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 25/31] selftests/net: Add TCP-AO library
  2022-08-23 15:47   ` Shuah Khan
@ 2022-09-05 20:24     ` Dmitry Safonov
  2022-09-06 16:34     ` Dmitry Safonov
  1 sibling, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-09-05 20:24 UTC (permalink / raw)
  To: Shuah Khan, Eric Dumazet, David S. Miller, linux-kernel
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, David Ahern,
	Dmitry Safonov, Eric Biggers, Francesco Ruggeri, Herbert Xu,
	Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

On 8/23/22 16:47, Shuah Khan wrote:
> On 8/18/22 10:59 AM, Dmitry Safonov wrote:
>> Provide functions to create selftests dedicated to TCP-AO.
>> They can run in parallel, as they use temporary net namespaces.
>> They can be very specific to the feature being tested.
>> This will allow to create a lot of TCP-AO tests, without complicating
>> one binary with many --options and to create scenarios, that are
>> hard to put in bash script that uses one binary.
>>
>> Signed-off-by: Dmitry Safonov <dima@arista.com>
>> ---
>>   tools/testing/selftests/Makefile              |   1 +
>>   tools/testing/selftests/net/tcp_ao/.gitignore |   2 +
>>   tools/testing/selftests/net/tcp_ao/Makefile   |  45 +++
>>   tools/testing/selftests/net/tcp_ao/connect.c  |  81 +++++
>>   .../testing/selftests/net/tcp_ao/lib/aolib.h  | 333 +++++++++++++++++
>>   .../selftests/net/tcp_ao/lib/netlink.c        | 341 ++++++++++++++++++
>>   tools/testing/selftests/net/tcp_ao/lib/proc.c | 267 ++++++++++++++
>>   .../testing/selftests/net/tcp_ao/lib/setup.c  | 297 +++++++++++++++
>>   tools/testing/selftests/net/tcp_ao/lib/sock.c | 294 +++++++++++++++
>>   .../testing/selftests/net/tcp_ao/lib/utils.c  |  30 ++
>>   10 files changed, 1691 insertions(+)
>>   create mode 100644 tools/testing/selftests/net/tcp_ao/.gitignore
>>   create mode 100644 tools/testing/selftests/net/tcp_ao/Makefile
>>   create mode 100644 tools/testing/selftests/net/tcp_ao/connect.c
>>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/aolib.h
>>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/netlink.c
>>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/proc.c
>>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/setup.c
>>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/sock.c
>>   create mode 100644 tools/testing/selftests/net/tcp_ao/lib/utils.c
>>
>> diff --git a/tools/testing/selftests/Makefile
>> b/tools/testing/selftests/Makefile
>> index 10b34bb03bc1..2a3b15a13ccb 100644
>> --- a/tools/testing/selftests/Makefile
>> +++ b/tools/testing/selftests/Makefile
>> @@ -46,6 +46,7 @@ TARGETS += net
>>   TARGETS += net/af_unix
>>   TARGETS += net/forwarding
>>   TARGETS += net/mptcp
>> +TARGETS += net/tcp_ao
> 
> Please look into a wayto invoke all of them instead of adding individual
> net/* to the main Makefile. This list seems to be growing. :)

Sent a patch separately to allow sub-dir defining their $(TARGETS):
https://lore.kernel.org/all/20220905202108.89338-1-dima@arista.com/T/#u

Will rebase this patch set if the other gets in :)

> 
>>   TARGETS += netfilter
>>   TARGETS += nsfs
>>   TARGETS += pidfd
> 
> [snip]

[..]
Thanks,
          Dmitry

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 25/31] selftests/net: Add TCP-AO library
  2022-08-23 15:47   ` Shuah Khan
  2022-09-05 20:24     ` Dmitry Safonov
@ 2022-09-06 16:34     ` Dmitry Safonov
  1 sibling, 0 replies; 65+ messages in thread
From: Dmitry Safonov @ 2022-09-06 16:34 UTC (permalink / raw)
  To: Shuah Khan, Eric Dumazet, David S. Miller, linux-kernel
  Cc: Andy Lutomirski, Ard Biesheuvel, Bob Gilligan, David Ahern,
	Dmitry Safonov, Eric Biggers, Francesco Ruggeri, Herbert Xu,
	Hideaki YOSHIFUJI, Ivan Delalande, Jakub Kicinski,
	Leonard Crestez, Paolo Abeni, Salam Noureddine, Shuah Khan,
	netdev, linux-crypto

On 8/23/22 16:47, Shuah Khan wrote:
> On 8/18/22 10:59 AM, Dmitry Safonov wrote:
[..]
>> +
>> +__attribute__((__format__(__printf__, 2, 3)))
>> +static inline void __test_print(void (*fn)(const char *), const char
>> *fmt, ...)
>> +{
>> +#define TEST_MSG_BUFFER_SIZE 4096
>> +    char buf[TEST_MSG_BUFFER_SIZE];
>> +    va_list arg;
>> +
>> +    va_start(arg, fmt);
>> +    vsnprintf(buf, sizeof(buf), fmt, arg);
>> +    va_end(arg);
>> +    fn(buf);
>> +}
>> +
> 
> Is there a reason add these instead of using kselftest_* print
> functions?

Inside __test_ok(), __test_msg(), __test_fail() and __test_error() are
calling ksft_*() functions. kselftest_*() by themselves are not
thread-safe and I was not sure if you would want them to be.

> 
>> +#define test_print(fmt, ...)                        \
>> +    __test_print(__test_msg, "%ld[%s:%u] " fmt "\n",        \
>> +             syscall(SYS_gettid),                \
>> +             __FILE__, __LINE__, ##__VA_ARGS__)
>> +
>> +#define test_ok(fmt, ...)                        \
>> +    __test_print(__test_ok, fmt "\n", ##__VA_ARGS__)
>> +
>> +#define test_fail(fmt, ...)                        \
>> +do {                                    \
>> +    if (errno)                            \
>> +        __test_print(__test_fail, fmt ": %m\n", ##__VA_ARGS__);    \
>> +    else                                \
>> +        __test_print(__test_fail, fmt "\n", ##__VA_ARGS__);    \
>> +    test_failed();                            \
>> +} while(0)
>> +
>> +#define KSFT_FAIL  1
>> +#define test_error(fmt, ...)                        \
>> +do {                                    \
>> +    if (errno)                            \
>> +        __test_print(__test_error, "%ld[%s:%u] " fmt ": %m\n",    \
>> +                 syscall(SYS_gettid), __FILE__, __LINE__,    \
>> +                 ##__VA_ARGS__);                \
>> +    else                                \
>> +        __test_print(__test_error, "%ld[%s:%u] " fmt "\n",    \
>> +                 syscall(SYS_gettid), __FILE__, __LINE__,    \
>> +                 ##__VA_ARGS__);                \
>> +    exit(KSFT_FAIL);                        \
>> +} while(0)
>> +
> 
> Is there a reason add these instead of using kselftest_* print
> functions?

The same reason: two or more threads my fail the test at the same
moment, I needed some way of protecting the output.

>> + * Timeout on syscalls where failure is not expected.
>> + * You may want to rise it if the test machine is very busy.
>> + */
>> +#ifndef TEST_TIMEOUT_SEC
>> +#define TEST_TIMEOUT_SEC    5
>> +#endif
>> +
> 
> Where is the TEST_TIMEOUT_SEC usually defined? Does this come
> from shell wrapper that runs this test? Can we add a message before
> starting the test print the timeout used?

Usually it's not re-defined and used as-is. Ifndef here is only to make
it easier to recompile with another timeout const: one can just add
CFLAGS+=-DTEST_TIMEOUT_SEC=10 and check if that helps on the busy hardware.


>> +/*
>> + * Timeout on connect() where a failure is expected.
>> + * If set to 0 - kernel will try to retransmit SYN number of times,
>> set in
>> + * /proc/sys/net/ipv4/tcp_syn_retries
>> + * By default set to 1 to make tests pass faster on non-busy machine.
>> + */
>> +#ifndef TEST_RETRANSMIT_SEC
>> +#define TEST_RETRANSMIT_SEC    1
>> +#endif
>> +
> 
> Where would this TEST_RETRANSMIT_SEC defined usually?

The same: I always used the default value, but protected by ifndef if
one wants to increase the value.

>> +
>> +static inline int _test_connect_socket(int sk, const union tcp_addr
>> taddr,
>> +                    unsigned port, time_t timeout)
>> +{
>> +#ifdef IPV6_TEST
>> +    struct sockaddr_in6 addr = {
>> +        .sin6_family    = AF_INET6,
>> +        .sin6_port    = htons(port),
>> +        .sin6_addr    = taddr.a6,
>> +    };
>> +#else
>> +    struct sockaddr_in addr = {
>> +        .sin_family    = AF_INET,
>> +        .sin_port    = htons(port),
>> +        .sin_addr    = taddr.a4,
>> +    };
>> +#endif
> 
> Why do we defined these here - are they also defined in a kernel
> header?

No, those functions are helpers that process family-specific members.
IPV6_TEST is coming from Makefile:
$(OUTPUT)/%_ipv6: %.c
	$(LINK.c) -DIPV6_TEST $^ $(LDLIBS) -o $@

This way all tests can be compiled from the same code for both address
families, resulting in *_ipv4 and *_ipv6 binaries that reuse all the
code, but just have those #ifdef IPV6_TEST in places where test converts
or produces IP addresses.

[..]
>> +static inline int test_prepare_ao(struct tcp_ao *ao,
>> +        const char *alg, uint16_t flags,
>> +        union tcp_addr in_addr, uint8_t prefix,
>> +        uint8_t sndid, uint8_t rcvid, uint8_t maclen,
>> +        uint8_t keyflags, uint8_t keylen, const char *key)
>> +{
>> +#ifdef IPV6_TEST
>> +    struct sockaddr_in6 addr = {
>> +        .sin6_family    = AF_INET6,
>> +        .sin6_port    = 0,
>> +        .sin6_addr    = in_addr.a6,
>> +    };
>> +#else
>> +    struct sockaddr_in addr = {
>> +        .sin_family    = AF_INET,
>> +        .sin_port    = 0,
>> +        .sin_addr    = in_addr.a4,
>> +    };
>> +#endif
>> +
> 
> Same question here. In general having these ifdefs isn't ideal without
> a good reason.

Same as above.

> 
>> +    return test_prepare_ao_sockaddr(ao, alg, flags,
>> +            (void *)&addr, sizeof(addr), prefix, sndid, rcvid,
>> +            maclen, keyflags, keylen, key);
>> +}
>> +
>> +static inline int test_prepare_def_ao(struct tcp_ao *ao,
>> +        const char *key, uint16_t flags,
>> +        union tcp_addr in_addr, uint8_t prefix,
>> +        uint8_t sndid, uint8_t rcvid)
>> +{
>> +    if (prefix > DEFAULT_TEST_PREFIX)
>> +        prefix = DEFAULT_TEST_PREFIX;
>> +
>> +    return test_prepare_ao(ao, DEFAULT_TEST_ALGO, flags, in_addr,
>> +            prefix, sndid, rcvid, 0, 0, strlen(key), key);
>> +}
>> +
>> +extern int test_get_one_ao(int sk, struct tcp_ao_getsockopt *out,
>> +               uint16_t flags, void *addr, size_t addr_sz,
>> +               uint8_t prefix, uint8_t sndid, uint8_t rcvid);
>> +extern int test_cmp_getsockopt_setsockopt(const struct tcp_ao *a,
>> +                      const struct tcp_ao_getsockopt *b);
>> +
>> +static inline int test_verify_socket_ao(int sk, struct tcp_ao *ao)
>> +{
>> +    struct tcp_ao_getsockopt tmp;
>> +    int err;
>> +
>> +    err = test_get_one_ao(sk, &tmp, 0, &ao->tcpa_addr,
>> +            sizeof(ao->tcpa_addr), ao->tcpa_prefix,
>> +            ao->tcpa_sndid, ao->tcpa_rcvid);
>> +    if (err)
>> +        return err;
> 
> Is this always an error or could this a skip if dependencies aren't
> met to run the test? This is a global comment for all error cases.

Yeah, at this moment all tests will FAIL if CONFIG_TCP_AO is disabled.
I'll look into making them SKIP when getsockopt()/setsockopt() returns
ENOPROTOOPT in next versions of patches.

> 
>> +
>> +    return test_cmp_getsockopt_setsockopt(ao, &tmp);
>> +}
>> +
>> +static inline int test_set_ao(int sk, const char *key, uint16_t flags,
>> +                  union tcp_addr in_addr, uint8_t prefix,
>> +                  uint8_t sndid, uint8_t rcvid)
>> +{
>> +    struct tcp_ao tmp;
>> +    int err;
>> +
>> +    err = test_prepare_def_ao(&tmp, key, flags, in_addr,
>> +            prefix, sndid, rcvid);
>> +    if (err)
>> +        return err;
> 
> Same comment as above here.
> 
>> +
>> +    if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &tmp, sizeof(tmp)) < 0)
>> +        return -errno;
>> +
>> +    return test_verify_socket_ao(sk, &tmp);
>> +}
>> +
>> +extern ssize_t test_server_run(int sk, ssize_t quota, time_t
>> timeout_sec);
>> +extern ssize_t test_client_loop(int sk, char *buf, size_t buf_sz,
>> +                const size_t msg_len, time_t timeout_sec);
>> +extern int test_client_verify(int sk, const size_t msg_len, const
>> size_t nr,
>> +                  time_t timeout_sec);
>> +
>> +struct netstat;
>> +extern struct netstat *netstat_read(void);
>> +extern void netstat_free(struct netstat *ns);
>> +extern void netstat_print_diff(struct netstat *nsa, struct netstat
>> *nsb);
>> +extern uint64_t netstat_get(struct netstat *ns,
>> +                const char *name, bool *not_found);
>> +
>> +static inline uint64_t netstat_get_one(const char *name, bool
>> *not_found)
>> +{
>> +    struct netstat *ns = netstat_read();
>> +    uint64_t ret;
>> +
>> +    ret = netstat_get(ns, name, not_found);
>> +
>> +    netstat_free(ns);
>> +    return ret;
>> +}
>> +
>> +#endif /* _AOLIB_H_ */
>> diff --git a/tools/testing/selftests/net/tcp_ao/lib/netlink.c
>> b/tools/testing/selftests/net/tcp_ao/lib/netlink.c
>> new file mode 100644
>> index 000000000000..f04757c921d0
>> --- /dev/null
>> +++ b/tools/testing/selftests/net/tcp_ao/lib/netlink.c
>> @@ -0,0 +1,341 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/* Original from tools/testing/selftests/net/ipsec.c */
>> +#include <linux/netlink.h>
>> +#include <linux/random.h>
>> +#include <linux/rtnetlink.h>
>> +#include <linux/veth.h>
>> +#include <net/if.h>
>> +#include <stdint.h>
>> +#include <string.h>
>> +#include <sys/socket.h>
>> +
>> +#include "aolib.h"
>> +
>> +#define MAX_PAYLOAD        2048
> 
> tools/testing/selftests/net/gro.c seem to define this as:
> 
> #define MAX_PAYLOAD (IP_MAXPACKET - sizeof(struct tcphdr) -
> sizeof(struct ipv6hdr))
> 
> Can you do the same instead of hard-coding?

I think I could look into way of dynamically allocate netlink buffer for
requests, but I would say it's not as bad as it looks: the functions
always use constant size of messages for the netlink messages, so the
buffer size is always constant. And if anything doesn't fit, the helper
rtattr_pack() will just fail the test and it'll be visible straight away.
So, in my point of view, it could have been nicer, but this is the
easiest and simplest way by allocating const buffer on stack, rather
than dynamically.

>> +
>> +const struct sockaddr_in6 addr_any6 = {
>> +    .sin6_family    = AF_INET6,
>> +};
>> +
>> +const struct sockaddr_in addr_any4 = {
>> +    .sin_family    = AF_INET,
>> +};
>>
> 
> A couple of things to look at closely. For some failures such as
> memory allocation for the test or not being able to open a file
> 
> fnetstat = fopen("/proc/net/netstat", "r");
> 
> Is this a failure or missing config or not having the right permissions
> to open the fail. All of these cases would be a SKIP and not a test fail.

That makes sense, I'll look into making tests SKIP on failed memory
allocations or failed fopen()s.

Thank you for the review,
          Dmitry

^ permalink raw reply	[flat|nested] 65+ messages in thread

end of thread, other threads:[~2022-09-06 16:49 UTC | newest]

Thread overview: 65+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-18 16:59 [PATCH 00/31] net/tcp: Add TCP-AO support Dmitry Safonov
2022-08-18 16:59 ` [PATCH 01/31] crypto: Introduce crypto_pool Dmitry Safonov
2022-08-18 16:59 ` [PATCH 02/31] crypto_pool: Add crypto_pool_reserve_scratch() Dmitry Safonov
2022-08-18 16:59 ` [PATCH 03/31] net/tcp: Separate tcp_md5sig_info allocation into tcp_md5sig_info_add() Dmitry Safonov
2022-08-18 16:59 ` [PATCH 04/31] net/tcp: Disable TCP-MD5 static key on tcp_md5sig_info destruction Dmitry Safonov
2022-08-18 16:59 ` [PATCH 05/31] net/tcp: Use crypto_pool for TCP-MD5 Dmitry Safonov
2022-08-18 16:59 ` [PATCH 06/31] net/ipv6: sr: Switch to using crypto_pool Dmitry Safonov
2022-08-18 16:59 ` [PATCH 07/31] tcp: Add TCP-AO config and structures Dmitry Safonov
2022-08-18 16:59 ` [PATCH 08/31] net/tcp: Introduce TCP_AO setsockopt()s Dmitry Safonov
2022-08-18 18:50   ` kernel test robot
2022-08-18 18:50   ` kernel test robot
2022-08-23 14:45   ` Leonard Crestez
2022-08-31 18:48     ` Dmitry Safonov
2022-09-03  9:35       ` Leonard Crestez
2022-08-25 15:31   ` David Ahern
2022-08-25 18:21     ` David Laight
2022-08-18 16:59 ` [PATCH 09/31] net/tcp: Prevent TCP-MD5 with TCP-AO being set Dmitry Safonov
2022-08-18 16:59 ` [PATCH 10/31] net/tcp: Calculate TCP-AO traffic keys Dmitry Safonov
2022-08-18 16:59 ` [PATCH 11/31] net/tcp: Add TCP-AO sign to outgoing packets Dmitry Safonov
2022-08-18 16:59 ` [PATCH 12/31] net/tcp: Add tcp_parse_auth_options() Dmitry Safonov
2022-08-18 19:00   ` kernel test robot
2022-08-18 16:59 ` [PATCH 13/31] net/tcp: Add AO sign to RST packets Dmitry Safonov
2022-08-18 16:59 ` [PATCH 14/31] net/tcp: Add TCP-AO sign to twsk Dmitry Safonov
2022-08-18 16:59 ` [PATCH 15/31] net/tcp: Wire TCP-AO to request sockets Dmitry Safonov
2022-08-18 16:59 ` [PATCH 16/31] net/tcp: Sign SYN-ACK segments with TCP-AO Dmitry Safonov
2022-08-18 16:59 ` [PATCH 17/31] net/tcp: Verify inbound TCP-AO signed segments Dmitry Safonov
2022-08-18 16:59 ` [PATCH 18/31] net/tcp: Add TCP-AO segments counters Dmitry Safonov
2022-08-18 16:59 ` [PATCH 19/31] net/tcp: Add TCP-AO SNE support Dmitry Safonov
2022-08-23 14:50   ` Leonard Crestez
2022-08-23 22:40     ` Francesco Ruggeri
2022-08-18 16:59 ` [PATCH 20/31] net/tcp: Add tcp_hash_fail() ratelimited logs Dmitry Safonov
2022-08-18 16:59 ` [PATCH 21/31] net/tcp: Ignore specific ICMPs for TCP-AO connections Dmitry Safonov
2022-08-18 16:59 ` [PATCH 22/31] net/tcp: Add option for TCP-AO to (not) hash header Dmitry Safonov
2022-08-18 16:59 ` [PATCH 23/31] net/tcp: Add getsockopt(TCP_AO_GET) Dmitry Safonov
2022-08-23 14:45   ` Leonard Crestez
2022-08-18 16:59 ` [PATCH 24/31] net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs) Dmitry Safonov
2022-08-18 16:59 ` [PATCH 25/31] selftests/net: Add TCP-AO library Dmitry Safonov
2022-08-23 15:47   ` Shuah Khan
2022-09-05 20:24     ` Dmitry Safonov
2022-09-06 16:34     ` Dmitry Safonov
2022-08-18 17:00 ` [PATCH 26/31] selftests/net: Verify that TCP-AO complies with ignoring ICMPs Dmitry Safonov
2022-08-18 17:00 ` [PATCH 27/31] selftest/net: Add TCP-AO ICMPs accept test Dmitry Safonov
2022-08-18 17:00 ` [PATCH 28/31] selftest/tcp-ao: Add a test for MKT matching Dmitry Safonov
2022-08-18 17:00 ` [PATCH 29/31] selftest/tcp-ao: Add test for TCP-AO add setsockopt() command Dmitry Safonov
2022-08-18 17:00 ` [PATCH 30/31] selftests/tcp-ao: Add TCP-AO + TCP-MD5 + no sign listen socket tests Dmitry Safonov
2022-08-18 17:00 ` [PATCH 31/31] selftests/aolib: Add test/benchmark for removing MKTs Dmitry Safonov
2022-08-21 20:34 ` [PATCH 00/31] net/tcp: Add TCP-AO support Leonard Crestez
2022-08-21 23:51   ` David Ahern
2022-08-22 20:35     ` Dmitry Safonov
2022-08-23 15:30       ` Leonard Crestez
2022-08-23 16:31         ` Dmitry Safonov
2022-08-24 12:46         ` Andrew Lunn
2022-08-24 17:55           ` Jakub Kicinski
2022-08-27  8:55           ` Leonard Crestez
2022-08-22 18:42   ` Salam Noureddine
2022-08-22 10:21 [PATCH 02/31] crypto_pool: Add crypto_pool_reserve_scratch() kernel test robot
2022-08-22 10:45 ` Dan Carpenter
2022-08-22 10:45 ` Dan Carpenter
2022-08-26 14:42 ` Dmitry Safonov
2022-08-26 14:42   ` Dmitry Safonov
2022-08-22 11:22 [PATCH 11/31] net/tcp: Add TCP-AO sign to outgoing packets kernel test robot
2022-08-22 12:03 ` [kbuild] " Dan Carpenter
2022-08-22 12:03 ` Dan Carpenter
2022-08-29 17:55 ` Dmitry Safonov
2022-08-29 17:55   ` Dmitry Safonov

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.