All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eric Biggers <ebiggers@kernel.org>
To: netdev@vger.kernel.org
Cc: linux-crypto@vger.kernel.org, linux-kernel@vger.kernel.org,
	Eric Dumazet <edumazet@google.com>,
	Neal Cardwell <ncardwell@google.com>,
	Kuniyuki Iwashima <kuniyu@google.com>,
	"David S . Miller" <davem@davemloft.net>,
	David Ahern <dsahern@kernel.org>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>, Ard Biesheuvel <ardb@kernel.org>,
	"Jason A . Donenfeld" <Jason@zx2c4.com>,
	Herbert Xu <herbert@gondor.apana.org.au>,
	Dmitry Safonov <0x7f454c46@gmail.com>,
	Eric Biggers <ebiggers@kernel.org>
Subject: [RFC PATCH 2/8] net/tcp-ao: Use crypto library API instead of crypto_ahash
Date: Sat,  7 Mar 2026 14:43:35 -0800	[thread overview]
Message-ID: <20260307224341.5644-3-ebiggers@kernel.org> (raw)
In-Reply-To: <20260307224341.5644-1-ebiggers@kernel.org>

Currently the kernel's TCP-AO implementation does the MAC and KDF
computations using the crypto_ahash API.  This API is inefficient and
difficult to use, and it has required extensive workarounds in the form
of per-CPU preallocated objects (tcp_sigpool) to work at all.

Let's use lib/crypto/ instead.  This means switching to straightforward
stack-allocated structures, virtually addressed buffers, and direct
function calls.  It also means removing quite a bit of error handling.
This makes TCP-AO quite a bit faster.

This also enables many additional cleanups, which later commits will
handle: removing tcp-sigpool, removing support for crypto_tfm cloning,
removing more error handling, and replacing more dynamically-allocated
buffers with stack buffers based on the now-statically-known limits.

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
---
 include/net/tcp_ao.h                      |  27 +-
 net/ipv4/Kconfig                          |   5 +-
 net/ipv4/tcp_ao.c                         | 523 +++++++++++-----------
 net/ipv6/tcp_ao.c                         |  63 ++-
 tools/testing/selftests/net/tcp_ao/config |   2 -
 5 files changed, 304 insertions(+), 316 deletions(-)

diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h
index 1e9e27d6e06ba..f845bc631bc1e 100644
--- a/include/net/tcp_ao.h
+++ b/include/net/tcp_ao.h
@@ -1,11 +1,10 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
 #ifndef _TCP_AO_H
 #define _TCP_AO_H
 
-#define TCP_AO_KEY_ALIGN	1
-#define __tcp_ao_key_align __aligned(TCP_AO_KEY_ALIGN)
+#include <crypto/sha2.h> /* for SHA256_DIGEST_SIZE */
 
 union tcp_ao_addr {
 	struct in_addr  a4;
 #if IS_ENABLED(CONFIG_IPV6)
 	struct in6_addr	a6;
@@ -30,15 +29,26 @@ struct tcp_ao_counters {
 	atomic64_t	key_not_found;
 	atomic64_t	ao_required;
 	atomic64_t	dropped_icmp;
 };
 
+enum tcp_ao_algo_id {
+	TCP_AO_ALGO_HMAC_SHA1 = 1, /* specified by RFC 5926 */
+	TCP_AO_ALGO_HMAC_SHA256 = 2, /* Linux extension */
+	TCP_AO_ALGO_AES_128_CMAC = 3, /* specified by RFC 5926 */
+};
+
+#define TCP_AO_MAX_MAC_LEN		SHA256_DIGEST_SIZE
+#define TCP_AO_MAX_TRAFFIC_KEY_LEN	SHA256_DIGEST_SIZE
+
+struct tcp_ao_mac_ctx;
+
 struct tcp_ao_key {
 	struct hlist_node	node;
 	union tcp_ao_addr	addr;
-	u8			key[TCP_AO_MAXKEYLEN] __tcp_ao_key_align;
-	unsigned int		tcp_sigpool_id;
+	u8			key[TCP_AO_MAXKEYLEN];
+	enum tcp_ao_algo_id	algo;
 	unsigned int		digest_size;
 	int			l3index;
 	u8			prefixlen;
 	u8			family;
 	u8			keylen;
@@ -166,18 +176,19 @@ struct tcp6_ao_context {
 	__be16		dport;
 	__be32		sisn;
 	__be32		disn;
 };
 
-struct tcp_sigpool;
 /* Established states are fast-path and there always is current_key/rnext_key */
 #define TCP_AO_ESTABLISHED (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2 | \
 			    TCPF_CLOSE_WAIT | TCPF_LAST_ACK | TCPF_CLOSING)
 
 int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
 			struct tcp_ao_key *key, struct tcphdr *th,
 			__u8 *hash_location);
+void tcp_ao_mac_update(struct tcp_ao_mac_ctx *mac_ctx, const void *data,
+		       size_t data_len);
 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,
@@ -186,12 +197,12 @@ struct tcp_ao_key *tcp_ao_established_key(const struct sock *sk,
 					  struct tcp_ao_info *ao,
 					  int sndid, int rcvid);
 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, struct tcp_sigpool *hp);
+void tcp_ao_calc_traffic_key(const struct tcp_ao_key *mkt, u8 *traffic_key,
+			     const void *input, unsigned int input_len);
 void tcp_ao_destroy_sock(struct sock *sk, bool twsk);
 void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
 bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code);
 int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen);
 int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen);
@@ -232,11 +243,11 @@ struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk,
 					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);
 /* ipv6 specific functions */
-int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp,
+int tcp_v6_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
 				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,
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index df922f9f52891..0fa293527cee9 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -745,13 +745,14 @@ config DEFAULT_TCP_CONG
 config TCP_SIGPOOL
 	tristate
 
 config TCP_AO
 	bool "TCP: Authentication Option (RFC5925)"
-	select CRYPTO
+	select CRYPTO_LIB_AES_CBC_MACS
+	select CRYPTO_LIB_SHA1
+	select CRYPTO_LIB_SHA256
 	select CRYPTO_LIB_UTILS
-	select TCP_SIGPOOL
 	depends on 64BIT && IPV6 != m # seq-number extension needs WRITE_ONCE(u64)
 	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
diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c
index b21bd69b4e829..0d24cbd66c9a1 100644
--- a/net/ipv4/tcp_ao.c
+++ b/net/ipv4/tcp_ao.c
@@ -7,11 +7,13 @@
  *		Francesco Ruggeri <fruggeri@arista.com>
  *		Salam Noureddine <noureddine@arista.com>
  */
 #define pr_fmt(fmt) "TCP: " fmt
 
-#include <crypto/hash.h>
+#include <crypto/aes-cbc-macs.h>
+#include <crypto/sha1.h>
+#include <crypto/sha2.h>
 #include <crypto/utils.h>
 #include <linux/inetdevice.h>
 #include <linux/tcp.h>
 
 #include <net/tcp.h>
@@ -19,36 +21,137 @@
 #include <net/icmp.h>
 #include <trace/events/tcp.h>
 
 DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_ao_needed, HZ);
 
-int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
-			    unsigned int len, struct tcp_sigpool *hp)
-{
-	struct scatterlist sg;
-	int ret;
+static const struct tcp_ao_algo {
+	const char *name;
+	unsigned int digest_size;
+} tcp_ao_algos[] = {
+	[TCP_AO_ALGO_HMAC_SHA1] = {
+		.name = "hmac(sha1)",
+		.digest_size = SHA1_DIGEST_SIZE,
+	},
+	[TCP_AO_ALGO_HMAC_SHA256] = {
+		.name = "hmac(sha256)",
+		.digest_size = SHA256_DIGEST_SIZE,
+	},
+	[TCP_AO_ALGO_AES_128_CMAC] = {
+		.name = "cmac(aes128)",
+		.digest_size = AES_BLOCK_SIZE, /* same as AES_KEYSIZE_128 */
+	},
+};
+
+struct tcp_ao_mac_ctx {
+	enum tcp_ao_algo_id algo;
+	union {
+		struct hmac_sha1_ctx hmac_sha1;
+		struct hmac_sha256_ctx hmac_sha256;
+		struct {
+			struct aes_cmac_key key;
+			struct aes_cmac_ctx ctx;
+		} aes_cmac;
+	};
+};
+
+static const struct tcp_ao_algo *tcp_ao_find_algo(const char *name)
+{
+	for (size_t i = 0; i < ARRAY_SIZE(tcp_ao_algos); i++) {
+		const struct tcp_ao_algo *algo = &tcp_ao_algos[i];
+
+		if (!algo->name)
+			continue;
+		if (WARN_ON_ONCE(algo->digest_size > TCP_AO_MAX_MAC_LEN ||
+				 algo->digest_size >
+					 TCP_AO_MAX_TRAFFIC_KEY_LEN))
+			continue;
+		if (strcmp(name, algo->name) == 0)
+			return algo;
+	}
+	return NULL;
+}
 
-	if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp->req),
-				mkt->key, mkt->keylen))
-		goto clear_hash;
+static void tcp_ao_mac_init(struct tcp_ao_mac_ctx *mac_ctx,
+			    enum tcp_ao_algo_id algo, const u8 *traffic_key)
+{
+	mac_ctx->algo = algo;
+	switch (mac_ctx->algo) {
+	case TCP_AO_ALGO_HMAC_SHA1:
+		hmac_sha1_init_usingrawkey(&mac_ctx->hmac_sha1, traffic_key,
+					   SHA1_DIGEST_SIZE);
+		return;
+	case TCP_AO_ALGO_HMAC_SHA256:
+		hmac_sha256_init_usingrawkey(&mac_ctx->hmac_sha256, traffic_key,
+					     SHA256_DIGEST_SIZE);
+		return;
+	case TCP_AO_ALGO_AES_128_CMAC:
+		aes_cmac_preparekey(&mac_ctx->aes_cmac.key, traffic_key,
+				    AES_KEYSIZE_128);
+		aes_cmac_init(&mac_ctx->aes_cmac.ctx, &mac_ctx->aes_cmac.key);
+		return;
+	default:
+		WARN_ON_ONCE(1); /* algo was validated earlier. */
+	}
+}
 
-	ret = crypto_ahash_init(hp->req);
-	if (ret)
-		goto clear_hash;
+void tcp_ao_mac_update(struct tcp_ao_mac_ctx *mac_ctx, const void *data,
+		       size_t data_len)
+{
+	switch (mac_ctx->algo) {
+	case TCP_AO_ALGO_HMAC_SHA1:
+		hmac_sha1_update(&mac_ctx->hmac_sha1, data, data_len);
+		return;
+	case TCP_AO_ALGO_HMAC_SHA256:
+		hmac_sha256_update(&mac_ctx->hmac_sha256, data, data_len);
+		return;
+	case TCP_AO_ALGO_AES_128_CMAC:
+		aes_cmac_update(&mac_ctx->aes_cmac.ctx, data, data_len);
+		return;
+	default:
+		WARN_ON_ONCE(1); /* algo was validated earlier. */
+	}
+}
 
-	sg_init_one(&sg, ctx, len);
-	ahash_request_set_crypt(hp->req, &sg, key, len);
-	crypto_ahash_update(hp->req);
+static void tcp_ao_mac_final(struct tcp_ao_mac_ctx *mac_ctx, u8 *out)
+{
+	switch (mac_ctx->algo) {
+	case TCP_AO_ALGO_HMAC_SHA1:
+		hmac_sha1_final(&mac_ctx->hmac_sha1, out);
+		return;
+	case TCP_AO_ALGO_HMAC_SHA256:
+		hmac_sha256_final(&mac_ctx->hmac_sha256, out);
+		return;
+	case TCP_AO_ALGO_AES_128_CMAC:
+		aes_cmac_final(&mac_ctx->aes_cmac.ctx, out);
+		return;
+	default:
+		WARN_ON_ONCE(1); /* algo was validated earlier. */
+	}
+}
 
-	ret = crypto_ahash_final(hp->req);
-	if (ret)
-		goto clear_hash;
+void tcp_ao_calc_traffic_key(const struct tcp_ao_key *mkt, u8 *traffic_key,
+			     const void *input, unsigned int input_len)
+{
+	switch (mkt->algo) {
+	case TCP_AO_ALGO_HMAC_SHA1:
+		hmac_sha1_usingrawkey(mkt->key, mkt->keylen, input, input_len,
+				      traffic_key);
+		return;
+	case TCP_AO_ALGO_HMAC_SHA256:
+		hmac_sha256_usingrawkey(mkt->key, mkt->keylen, input, input_len,
+					traffic_key);
+		return;
+	case TCP_AO_ALGO_AES_128_CMAC: {
+		struct aes_cmac_key k;
 
-	return 0;
-clear_hash:
-	memset(key, 0, tcp_ao_digest_size(mkt));
-	return 1;
+		aes_cmac_preparekey(&k, mkt->key, AES_KEYSIZE_128);
+		aes_cmac(&k, input, input_len, traffic_key);
+		return;
+	}
+	default:
+		WARN_ON_ONCE(1); /* algo was validated earlier. */
+	}
 }
 
 bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code)
 {
 	bool ignore_icmp = false;
@@ -252,33 +355,30 @@ static struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk,
 	if (!new_key)
 		return NULL;
 
 	*new_key = *key;
 	INIT_HLIST_NODE(&new_key->node);
-	tcp_sigpool_get(new_key->tcp_sigpool_id);
 	atomic64_set(&new_key->pkt_good, 0);
 	atomic64_set(&new_key->pkt_bad, 0);
 
 	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);
 
-	tcp_sigpool_release(key->tcp_sigpool_id);
 	kfree_sensitive(key);
 }
 
 static void tcp_ao_info_free(struct tcp_ao_info *ao)
 {
 	struct tcp_ao_key *key;
 	struct hlist_node *n;
 
 	hlist_for_each_entry_safe(key, n, &ao->head, node) {
 		hlist_del(&key->node);
-		tcp_sigpool_release(key->tcp_sigpool_id);
 		kfree_sensitive(key);
 	}
 	kfree(ao);
 	static_branch_slow_dec_deferred(&tcp_ao_needed);
 }
@@ -344,33 +444,26 @@ static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
 	struct kdf_input_block {
 		u8                      counter;
 		u8                      label[6];
 		struct tcp4_ao_context	ctx;
 		__be16                  outlen;
-	} __packed * tmp;
-	struct tcp_sigpool hp;
-	int err;
-
-	err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp);
-	if (err)
-		return err;
-
-	tmp = hp.scratch;
-	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 */
-
-	err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp);
-	tcp_sigpool_end(&hp);
-
-	return err;
+	} __packed input = {
+		.counter = 1,
+		.label = "TCP-AO",
+		.ctx = {
+			.saddr = saddr,
+			.daddr = daddr,
+			.sport = sport,
+			.dport = dport,
+			.sisn = sisn,
+			.disn = disn,
+		},
+		.outlen = htons(tcp_ao_digest_size(mkt) * 8), /* in bits */
+	};
+
+	tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input));
+	return 0;
 }
 
 int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
 			  const struct sock *sk,
 			  __be32 sisn, __be32 disn, bool send)
@@ -433,60 +526,57 @@ static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
 		return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn);
 #endif
 	return -EAFNOSUPPORT;
 }
 
-static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp,
+static int tcp_v4_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
 				       __be32 daddr, __be32 saddr,
 				       int nbytes)
 {
-	struct tcp4_pseudohdr *bp;
-	struct scatterlist sg;
+	struct tcp4_pseudohdr phdr = {
+		.saddr = saddr,
+		.daddr = daddr,
+		.pad = 0,
+		.protocol = IPPROTO_TCP,
+		.len = cpu_to_be16(nbytes),
+	};
 
-	bp = hp->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);
+	tcp_ao_mac_update(mac_ctx, &phdr, sizeof(phdr));
+	return 0;
 }
 
 static int tcp_ao_hash_pseudoheader(unsigned short int family,
 				    const struct sock *sk,
 				    const struct sk_buff *skb,
-				    struct tcp_sigpool *hp, int nbytes)
+				    struct tcp_ao_mac_ctx *mac_ctx, 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,
+			return tcp_v4_ao_hash_pseudoheader(mac_ctx, sk->sk_daddr,
 					sk->sk_rcv_saddr, skb->len);
 #if IS_ENABLED(CONFIG_IPV6)
 		else if (family == AF_INET6)
-			return tcp_v6_ao_hash_pseudoheader(hp, &sk->sk_v6_daddr,
+			return tcp_v6_ao_hash_pseudoheader(mac_ctx, &sk->sk_v6_daddr,
 					&sk->sk_v6_rcv_saddr, skb->len);
 #endif
 		else
 			return -EAFNOSUPPORT;
 	}
 
 	if (family == AF_INET) {
 		const struct iphdr *iph = ip_hdr(skb);
 
-		return tcp_v4_ao_hash_pseudoheader(hp, iph->daddr,
+		return tcp_v4_ao_hash_pseudoheader(mac_ctx, iph->daddr,
 				iph->saddr, skb->len);
 #if IS_ENABLED(CONFIG_IPV6)
 	} else if (family == AF_INET6) {
 		const struct ipv6hdr *iph = ipv6_hdr(skb);
 
-		return tcp_v6_ao_hash_pseudoheader(hp, &iph->daddr,
+		return tcp_v6_ao_hash_pseudoheader(mac_ctx, &iph->daddr,
 				&iph->saddr, skb->len);
 #endif
 	}
 	return -EAFNOSUPPORT;
 }
@@ -504,35 +594,24 @@ u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq)
 	}
 
 	return sne;
 }
 
-/* tcp_ao_hash_sne(struct tcp_sigpool *hp)
- * @hp	- used for hashing
- * @sne - sne value
- */
-static int tcp_ao_hash_sne(struct tcp_sigpool *hp, u32 sne)
+static void tcp_ao_hash_sne(struct tcp_ao_mac_ctx *mac_ctx, u32 sne)
 {
-	struct scatterlist sg;
-	__be32 *bp;
-
-	bp = (__be32 *)hp->scratch;
-	*bp = htonl(sne);
+	__be32 sne_be32 = 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);
+	tcp_ao_mac_update(mac_ctx, &sne_be32, sizeof(sne_be32));
 }
 
-static int tcp_ao_hash_header(struct tcp_sigpool *hp,
-			      const struct tcphdr *th,
-			      bool exclude_options, u8 *hash,
-			      int hash_offset, int hash_len)
+static void tcp_ao_hash_header(struct tcp_ao_mac_ctx *mac_ctx,
+			       const struct tcphdr *th, bool exclude_options,
+			       u8 *hash, int hash_offset, int hash_len)
 {
-	struct scatterlist sg;
-	u8 *hdr = hp->scratch;
-	int err, len;
+	/* Full TCP header (th->doff << 2) should fit into scratch area. */
+	u8 hdr[60];
+	int len;
 
 	/* 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));
@@ -548,126 +627,105 @@ static int tcp_ao_hash_header(struct tcp_sigpool *hp,
 		/* 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;
+	tcp_ao_mac_update(mac_ctx, hdr, len);
 }
 
 int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash,
 		    struct tcp_ao_key *key, const u8 *tkey,
 		    const union tcp_ao_addr *daddr,
 		    const union tcp_ao_addr *saddr,
 		    const struct tcphdr *th, u32 sne)
 {
-	int tkey_len = tcp_ao_digest_size(key);
 	int hash_offset = ao_hash - (char *)th;
-	struct tcp_sigpool hp;
-	void *hash_buf = NULL;
-
-	hash_buf = kmalloc(tkey_len, GFP_ATOMIC);
-	if (!hash_buf)
-		goto clear_hash_noput;
-
-	if (tcp_sigpool_start(key->tcp_sigpool_id, &hp))
-		goto clear_hash_noput;
-
-	if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len))
-		goto clear_hash;
-
-	if (crypto_ahash_init(hp.req))
-		goto clear_hash;
+	struct tcp_ao_mac_ctx mac_ctx;
+	u8 hash_buf[TCP_AO_MAX_MAC_LEN];
 
-	if (tcp_ao_hash_sne(&hp, sne))
-		goto clear_hash;
+	tcp_ao_mac_init(&mac_ctx, key->algo, tkey);
+	tcp_ao_hash_sne(&mac_ctx, sne);
 	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;
+		tcp_v4_ao_hash_pseudoheader(&mac_ctx, daddr->a4.s_addr,
+					    saddr->a4.s_addr, th->doff * 4);
 #if IS_ENABLED(CONFIG_IPV6)
 	} else if (family == AF_INET6) {
-		if (tcp_v6_ao_hash_pseudoheader(&hp, &daddr->a6,
-						&saddr->a6, th->doff * 4))
-			goto clear_hash;
+		tcp_v6_ao_hash_pseudoheader(&mac_ctx, &daddr->a6,
+					    &saddr->a6, th->doff * 4);
 #endif
 	} else {
 		WARN_ON_ONCE(1);
 		goto clear_hash;
 	}
-	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, hash_buf, 0);
-	if (crypto_ahash_final(hp.req))
-		goto clear_hash;
+	tcp_ao_hash_header(&mac_ctx, th,
+			   !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT),
+			   ao_hash, hash_offset, tcp_ao_maclen(key));
+	tcp_ao_mac_final(&mac_ctx, hash_buf);
 
 	memcpy(ao_hash, hash_buf, tcp_ao_maclen(key));
-	tcp_sigpool_end(&hp);
-	kfree(hash_buf);
 	return 0;
 
 clear_hash:
-	tcp_sigpool_end(&hp);
-clear_hash_noput:
 	memset(ao_hash, 0, tcp_ao_maclen(key));
-	kfree(hash_buf);
 	return 1;
 }
 
+static void tcp_ao_hash_skb_data(struct tcp_ao_mac_ctx *mac_ctx,
+				 const struct sk_buff *skb,
+				 unsigned int header_len)
+{
+	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;
+	unsigned int i;
+
+	tcp_ao_mac_update(mac_ctx, (const u8 *)tcp_hdr(skb) + header_len,
+			  head_data_len);
+
+	for (i = 0; i < shi->nr_frags; ++i) {
+		const skb_frag_t *f = &shi->frags[i];
+		u32 p_off, p_len, copied;
+		const void *vaddr;
+		struct page *p;
+
+		skb_frag_foreach_page(f, skb_frag_off(f), skb_frag_size(f),
+				      p, p_off, p_len, copied) {
+			vaddr = kmap_local_page(p);
+			tcp_ao_mac_update(mac_ctx, vaddr + p_off, p_len);
+			kunmap_local(vaddr);
+		}
+	}
+
+	skb_walk_frags(skb, frag_iter)
+		tcp_ao_hash_skb_data(mac_ctx, frag_iter, 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);
-	struct tcp_sigpool hp;
-	void *hash_buf = NULL;
-
-	hash_buf = kmalloc(tkey_len, GFP_ATOMIC);
-	if (!hash_buf)
-		goto clear_hash_noput;
-
-	if (tcp_sigpool_start(key->tcp_sigpool_id, &hp))
-		goto clear_hash_noput;
-
-	if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len))
-		goto clear_hash;
-
-	/* For now use sha1 by default. Depends on alg in tcp_ao_key */
-	if (crypto_ahash_init(hp.req))
-		goto clear_hash;
+	struct tcp_ao_mac_ctx mac_ctx;
+	u8 hash_buf[TCP_AO_MAX_MAC_LEN];
 
-	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,
-			       !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT),
-			       ao_hash, hash_offset, tcp_ao_maclen(key)))
-		goto clear_hash;
-	if (tcp_sigpool_hash_skb_data(&hp, skb, th->doff << 2))
-		goto clear_hash;
-	ahash_request_set_crypt(hp.req, NULL, hash_buf, 0);
-	if (crypto_ahash_final(hp.req))
+	tcp_ao_mac_init(&mac_ctx, key->algo, tkey);
+	tcp_ao_hash_sne(&mac_ctx, sne);
+	if (tcp_ao_hash_pseudoheader(family, sk, skb, &mac_ctx, skb->len))
 		goto clear_hash;
+	tcp_ao_hash_header(&mac_ctx, th,
+			   !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT),
+			   ao_hash, hash_offset, tcp_ao_maclen(key));
+	tcp_ao_hash_skb_data(&mac_ctx, skb, th->doff << 2);
+	tcp_ao_mac_final(&mac_ctx, hash_buf);
 
 	memcpy(ao_hash, hash_buf, tcp_ao_maclen(key));
-	tcp_sigpool_end(&hp);
-	kfree(hash_buf);
 	return 0;
 
 clear_hash:
-	tcp_sigpool_end(&hp);
-clear_hash_noput:
 	memset(ao_hash, 0, tcp_ao_maclen(key));
-	kfree(hash_buf);
 	return 1;
 }
 
 int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
 		       const struct sock *sk, const struct sk_buff *skb,
@@ -1278,11 +1336,10 @@ int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk,
 	return 0;
 
 free_and_exit:
 	hlist_for_each_entry_safe(key, key_head, &new_ao->head, node) {
 		hlist_del(&key->node);
-		tcp_sigpool_release(key->tcp_sigpool_id);
 		atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc);
 		kfree_sensitive(key);
 	}
 free_ao:
 	kfree(new_ao);
@@ -1334,27 +1391,14 @@ static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao_add *cmd,
 
 	*addr = (union tcp_ao_addr *)&sin->sin_addr;
 	return 0;
 }
 
-static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key)
+static int tcp_ao_parse_crypto(const struct tcp_ao_add *cmd,
+			       struct tcp_ao_key *key)
 {
 	unsigned int syn_tcp_option_space;
-	bool is_kdf_aes_128_cmac = false;
-	struct crypto_ahash *tfm;
-	struct tcp_sigpool hp;
-	void *tmp_key = NULL;
-	int err;
-
-	/* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */
-	if (!strcmp("cmac(aes128)", cmd->alg_name)) {
-		strscpy(cmd->alg_name, "cmac(aes)", sizeof(cmd->alg_name));
-		is_kdf_aes_128_cmac = (cmd->keylen != 16);
-		tmp_key = kmalloc(cmd->keylen, GFP_KERNEL);
-		if (!tmp_key)
-			return -ENOMEM;
-	}
 
 	key->maclen = cmd->maclen ?: 12; /* 12 is the default in RFC5925 */
 
 	/* Check: maclen + tcp-ao header <= (MAX_TCP_OPTION_SPACE - mss
 	 *					- tstamp (including sackperm)
@@ -1386,68 +1430,31 @@ static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key)
 	 */
 	syn_tcp_option_space = MAX_TCP_OPTION_SPACE;
 	syn_tcp_option_space -= TCPOLEN_MSS_ALIGNED;
 	syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED;
 	syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED;
-	if (tcp_ao_len_aligned(key) > syn_tcp_option_space) {
-		err = -EMSGSIZE;
-		goto err_kfree;
-	}
-
-	key->keylen = cmd->keylen;
-	memcpy(key->key, cmd->key, cmd->keylen);
-
-	err = tcp_sigpool_start(key->tcp_sigpool_id, &hp);
-	if (err)
-		goto err_kfree;
-
-	tfm = crypto_ahash_reqtfm(hp.req);
-	if (is_kdf_aes_128_cmac) {
-		void *scratch = hp.scratch;
-		struct scatterlist sg;
-
-		memcpy(tmp_key, cmd->key, cmd->keylen);
-		sg_init_one(&sg, tmp_key, cmd->keylen);
-
-		/* Using zero-key of 16 bytes as described in RFC5926 */
-		memset(scratch, 0, 16);
-		err = crypto_ahash_setkey(tfm, scratch, 16);
-		if (err)
-			goto err_pool_end;
-
-		err = crypto_ahash_init(hp.req);
-		if (err)
-			goto err_pool_end;
-
-		ahash_request_set_crypt(hp.req, &sg, key->key, cmd->keylen);
-		err = crypto_ahash_update(hp.req);
-		if (err)
-			goto err_pool_end;
-
-		err |= crypto_ahash_final(hp.req);
-		if (err)
-			goto err_pool_end;
-		key->keylen = 16;
+	if (tcp_ao_len_aligned(key) > syn_tcp_option_space)
+		return -EMSGSIZE;
+
+	if (key->algo == TCP_AO_ALGO_AES_128_CMAC &&
+	    cmd->keylen != AES_KEYSIZE_128) {
+		/* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */
+		static const u8 zeroes[AES_KEYSIZE_128];
+		struct aes_cmac_key extractor;
+
+		aes_cmac_preparekey(&extractor, zeroes, AES_KEYSIZE_128);
+		aes_cmac(&extractor, cmd->key, cmd->keylen, key->key);
+		key->keylen = AES_KEYSIZE_128;
+	} else {
+		memcpy(key->key, cmd->key, cmd->keylen);
+		key->keylen = cmd->keylen;
 	}
 
-	err = crypto_ahash_setkey(tfm, key->key, key->keylen);
-	if (err)
-		goto err_pool_end;
-
-	tcp_sigpool_end(&hp);
-	kfree_sensitive(tmp_key);
-
 	if (tcp_ao_maclen(key) > key->digest_size)
 		return -EINVAL;
 
 	return 0;
-
-err_pool_end:
-	tcp_sigpool_end(&hp);
-err_kfree:
-	kfree_sensitive(tmp_key);
-	return err;
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
 static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd,
 			      union tcp_ao_addr **paddr,
@@ -1547,58 +1554,37 @@ static struct tcp_ao_info *getsockopt_ao_info(struct sock *sk)
 #define TCP_AO_GET_KEYF_VALID	(TCP_AO_KEYF_IFINDEX)
 
 static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk,
 					   struct tcp_ao_add *cmd)
 {
-	const char *algo = cmd->alg_name;
-	unsigned int digest_size;
-	struct crypto_ahash *tfm;
+	const struct tcp_ao_algo *algo;
 	struct tcp_ao_key *key;
-	struct tcp_sigpool hp;
-	int err, pool_id;
 	size_t size;
 
 	/* Force null-termination of alg_name */
 	cmd->alg_name[ARRAY_SIZE(cmd->alg_name) - 1] = '\0';
 
-	/* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */
-	if (!strcmp("cmac(aes128)", algo))
-		algo = "cmac(aes)";
-	else if (strcmp("hmac(sha1)", algo) &&
-		 strcmp("hmac(sha256)", algo) &&
-		 (strcmp("cmac(aes)", algo) || cmd->keylen != 16))
-		return ERR_PTR(-ENOENT);
-
-	/* Full TCP header (th->doff << 2) should fit into scratch area,
-	 * see tcp_ao_hash_header().
+	/*
+	 * For backwards compatibility, accept "cmac(aes)" as an alias for
+	 * "cmac(aes128)", provided that the key length is exactly 128 bits.
 	 */
-	pool_id = tcp_sigpool_alloc_ahash(algo, 60);
-	if (pool_id < 0)
-		return ERR_PTR(pool_id);
-
-	err = tcp_sigpool_start(pool_id, &hp);
-	if (err)
-		goto err_free_pool;
+	if (strcmp(cmd->alg_name, "cmac(aes)") == 0 &&
+	    cmd->keylen == AES_KEYSIZE_128)
+		strscpy(cmd->alg_name, "cmac(aes128)");
 
-	tfm = crypto_ahash_reqtfm(hp.req);
-	digest_size = crypto_ahash_digestsize(tfm);
-	tcp_sigpool_end(&hp);
+	algo = tcp_ao_find_algo(cmd->alg_name);
+	if (!algo)
+		return ERR_PTR(-ENOENT);
 
-	size = sizeof(struct tcp_ao_key) + (digest_size << 1);
+	size = sizeof(struct tcp_ao_key) + (algo->digest_size << 1);
 	key = sock_kmalloc(sk, size, GFP_KERNEL);
-	if (!key) {
-		err = -ENOMEM;
-		goto err_free_pool;
-	}
+	if (!key)
+		return ERR_PTR(-ENOMEM);
 
-	key->tcp_sigpool_id = pool_id;
-	key->digest_size = digest_size;
+	key->algo = algo - tcp_ao_algos;
+	key->digest_size = algo->digest_size;
 	return key;
-
-err_free_pool:
-	tcp_sigpool_release(pool_id);
-	return ERR_PTR(err);
 }
 
 static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
 			  sockptr_t optval, int optlen)
 {
@@ -1755,11 +1741,10 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
 		WRITE_ONCE(ao_info->rnext_key, key);
 	return 0;
 
 err_free_sock:
 	atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
-	tcp_sigpool_release(key->tcp_sigpool_id);
 	kfree_sensitive(key);
 err_free_ao:
 	if (first)
 		kfree(ao_info);
 	return ret;
@@ -2286,11 +2271,15 @@ static int tcp_ao_copy_mkts_to_user(const struct sock *sk,
 		opt_out.keylen = key->keylen;
 		opt_out.ifindex = key->l3index;
 		opt_out.pkt_good = atomic64_read(&key->pkt_good);
 		opt_out.pkt_bad = atomic64_read(&key->pkt_bad);
 		memcpy(&opt_out.key, key->key, key->keylen);
-		tcp_sigpool_algo(key->tcp_sigpool_id, opt_out.alg_name, 64);
+		if (key->algo == TCP_AO_ALGO_AES_128_CMAC)
+			/* This is needed for backwards compatibility. */
+			strscpy(opt_out.alg_name, "cmac(aes)");
+		else
+			strscpy(opt_out.alg_name, tcp_ao_algos[key->algo].name);
 
 		/* Copy key to user */
 		if (copy_to_sockptr_offset(optval, out_offset,
 					   &opt_out, bytes_to_write))
 			return -EFAULT;
diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c
index 3c09ac26206e3..2dcfe9dda7f4a 100644
--- a/net/ipv6/tcp_ao.c
+++ b/net/ipv6/tcp_ao.c
@@ -5,11 +5,10 @@
  *
  * Authors:	Dmitry Safonov <dima@arista.com>
  *		Francesco Ruggeri <fruggeri@arista.com>
  *		Salam Noureddine <noureddine@arista.com>
  */
-#include <crypto/hash.h>
 #include <linux/tcp.h>
 
 #include <net/tcp.h>
 #include <net/ipv6.h>
 
@@ -22,33 +21,26 @@ static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
 	struct kdf_input_block {
 		u8			counter;
 		u8			label[6];
 		struct tcp6_ao_context	ctx;
 		__be16			outlen;
-	} __packed * tmp;
-	struct tcp_sigpool hp;
-	int err;
-
-	err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp);
-	if (err)
-		return err;
-
-	tmp = hp.scratch;
-	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 */
-
-	err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp);
-	tcp_sigpool_end(&hp);
-
-	return err;
+	} __packed input = {
+		.counter = 1,
+		.label = "TCP-AO",
+		.ctx = {
+			.saddr = *saddr,
+			.daddr = *daddr,
+			.sport = sport,
+			.dport = dport,
+			.sisn = sisn,
+			.disn = disn,
+		},
+		.outlen = htons(tcp_ao_digest_size(mkt) * 8), /* in bits */
+	};
+
+	tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input));
+	return 0;
 }
 
 int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
 			   const struct sk_buff *skb,
 			   __be32 sisn, __be32 disn)
@@ -110,27 +102,24 @@ struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk,
 	l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif);
 	return tcp_ao_do_lookup(sk, l3index, (union tcp_ao_addr *)addr,
 				AF_INET6, sndid, rcvid);
 }
 
-int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp,
+int tcp_v6_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
 				const struct in6_addr *daddr,
 				const struct in6_addr *saddr, int nbytes)
 {
-	struct tcp6_pseudohdr *bp;
-	struct scatterlist sg;
-
-	bp = hp->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);
+	struct tcp6_pseudohdr phdr = {
+		.saddr = *saddr,
+		.daddr = *daddr,
+		.len = cpu_to_be32(nbytes),
+		.protocol = cpu_to_be32(IPPROTO_TCP),
+	};
+
+	tcp_ao_mac_update(mac_ctx, &phdr, sizeof(phdr));
+	return 0;
 }
 
 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/tools/testing/selftests/net/tcp_ao/config b/tools/testing/selftests/net/tcp_ao/config
index 0ec38c167e6df..1b120bfd89c40 100644
--- a/tools/testing/selftests/net/tcp_ao/config
+++ b/tools/testing/selftests/net/tcp_ao/config
@@ -1,7 +1,5 @@
-CONFIG_CRYPTO_HMAC=y
-CONFIG_CRYPTO_SHA1=y
 CONFIG_IPV6=y
 CONFIG_IPV6_MULTIPLE_TABLES=y
 CONFIG_NET_L3_MASTER_DEV=y
 CONFIG_NET_VRF=y
 CONFIG_TCP_AO=y
-- 
2.53.0


  parent reply	other threads:[~2026-03-07 22:46 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-07 22:43 [RFC PATCH 0/8] Reimplement TCP-AO using crypto library Eric Biggers
2026-03-07 22:43 ` [RFC PATCH 1/8] net/tcp-ao: Drop support for most non-RFC-specified algorithms Eric Biggers
2026-03-07 22:43 ` Eric Biggers [this message]
2026-03-07 22:43 ` [RFC PATCH 3/8] net/tcp-ao: Use stack-allocated MAC and traffic_key buffers Eric Biggers
2026-03-07 22:43 ` [RFC PATCH 4/8] net/tcp-ao: Return void from functions that can no longer fail Eric Biggers
2026-03-07 22:43 ` [RFC PATCH 5/8] net/tcp: Remove tcp_sigpool Eric Biggers
2026-03-07 22:43 ` [RFC PATCH 6/8] crypto: hash - Remove support for cloning hash tfms Eric Biggers
2026-03-07 22:43 ` [RFC PATCH 7/8] crypto: cipher - Remove support for cloning cipher tfms Eric Biggers
2026-03-07 22:43 ` [RFC PATCH 8/8] crypto: api - Remove core support for cloning tfms Eric Biggers
2026-03-09  8:17 ` [RFC PATCH 0/8] Reimplement TCP-AO using crypto library Ard Biesheuvel
2026-03-09 22:33 ` Dmitry Safonov
2026-03-09 23:30   ` Eric Biggers
2026-03-10  7:42     ` Ard Biesheuvel

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=20260307224341.5644-3-ebiggers@kernel.org \
    --to=ebiggers@kernel.org \
    --cc=0x7f454c46@gmail.com \
    --cc=Jason@zx2c4.com \
    --cc=ardb@kernel.org \
    --cc=davem@davemloft.net \
    --cc=dsahern@kernel.org \
    --cc=edumazet@google.com \
    --cc=herbert@gondor.apana.org.au \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=kuniyu@google.com \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ncardwell@google.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

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

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