From patchwork Fri Jul 21 16:18:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123955 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp319334vqg; Fri, 21 Jul 2023 09:22:41 -0700 (PDT) X-Google-Smtp-Source: APBJJlFcAkZ8fzVBfktz2ZCOX+HtQU3OXZUQkBpz+T0C/KqaR7hDCsxGgQAsnjGXlAO+68SX7/DS X-Received: by 2002:a05:6a00:2daa:b0:641:d9b:a444 with SMTP id fb42-20020a056a002daa00b006410d9ba444mr643409pfb.31.1689956560974; Fri, 21 Jul 2023 09:22:40 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689956560; cv=none; d=google.com; s=arc-20160816; b=LMPVQ1hdX0BbPeKv1uD4w26m4NhXn1UWuloPC6RSLP6yIficHouXXHGV7m8UwzUofS LNqQEwb91uyKy2nIJnzzlLQNJAUHVZSUd0HxFURnjzWo0AaBXpN/CJ383bQY1mgvZrYy G/n3ye2qvOzL+ZRYeGH5gScDcj2cYwZ3Jlw66aAQI5jYXUspebt7BAqGr6smMARDUgZQ L9X/5rg4QwbEittLSOMRdPwX08dBfkF+d5FoLWZ+I+0pmXKibJwwfdt0zN5I9Hr2pUnF SR6devOTLakJ7qsmC6+bzn3kinYpe4utGHt/2TNSWNmy8N5qrQisYhx8FK1NaXADg4FR lw/A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=aI1yk2AbqHJzB/Cfd+R1UWR1aPkbc72fpTXKaEKCt2g=; fh=TbwYhDLmiCc3TYjKPbhBaJJHdEKgDeV+A6jQojShBCM=; b=lzsXQqv9sWE5L9cznzfBJILqbJx/nZLuRfVXMrmv+tZ5N7cn1F3LBTMuM7qZIe85Q1 pnRVDAsZJi3T6GTmbZGU2n/3X6LJKVrIMgl8c9Z8+oE1HvENmZh6OyM7+HMYIKj31Eea LRzkLzEqJF5cLrbfCPwovBuKFR6FQi5GzQT+bd1tDCnmuFGmAeLKEbzAkItB8PCNnVO4 MO26fKTwG666g/0R7e8QTohV72rYpgRXISK3BOPI7aRwN2NYZkdyNq9f3Qvk2ZH3qX0B Xp8KvbUzYX8KSbYTaYHNpqAconjX66Kua9E2mREB3MwQFhBWjEmtZ3JTyeH9rU2dYOvz 4s2Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b="Nne5h1/D"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id c15-20020a056a00248f00b006834175c797si3497814pfv.330.2023.07.21.09.22.26; Fri, 21 Jul 2023 09:22:40 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b="Nne5h1/D"; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232807AbjGUQVx (ORCPT + 99 others); Fri, 21 Jul 2023 12:21:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51360 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232791AbjGUQVQ (ORCPT ); Fri, 21 Jul 2023 12:21:16 -0400 Received: from mail-lj1-x22a.google.com (mail-lj1-x22a.google.com [IPv6:2a00:1450:4864:20::22a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 65F004693 for ; Fri, 21 Jul 2023 09:19:28 -0700 (PDT) Received: by mail-lj1-x22a.google.com with SMTP id 38308e7fff4ca-2b701e1ca63so32048831fa.1 for ; Fri, 21 Jul 2023 09:19:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956366; x=1690561166; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=aI1yk2AbqHJzB/Cfd+R1UWR1aPkbc72fpTXKaEKCt2g=; b=Nne5h1/D3XeCp8Wm7jT8oe+GpDQGlf7zsZ9V2RwiQKOXmIXpvvyF4U6G4MFyFrubyL EGNXbLbApFfZ6M0DWc7znhHET+1KepqbhgtjnrriF68CSWicQ3ogHWufERGpGm4itShf StJizt5V3m/7ZQ9cVZHuJb0LcbKT4en5LrN/KDrN4YzoucEyVNhDH12WXnv2+pkK1IbZ 2Ds5+dB1YxFjMMWvlQzpO7M60MOYRkwORgzpTbsH2LQ5LXAmmFtOLuK58b3bECGYYP+N x85yp6WKIwBg9TWqqReZkiNYFHdivIed404FBcrXplBwqaiR7Q0eNVdsv2ONW0ZBDi7n QCvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956366; x=1690561166; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=aI1yk2AbqHJzB/Cfd+R1UWR1aPkbc72fpTXKaEKCt2g=; b=BvQIhA9odEVnlskx4sGJhs9qKH87p0xmkoPJslPGqRysB8KDmhvYffmOpzU1mGURke KOcNSg2I/TcoNeV/nG8No56OmfxVGyvZr/UPkjKQlgNtj0N4iJ2l84mVMG40T9F4uvB/ GpH3KViiLu6UvKDgxLSLUOMapIDBRAkg+esbcG2+RFAenK6KwqGvekRhpeMaz1MNy9mj iSNABm/MMs6/xqyZstrLuotybi9Y45A1Pz8ElAioZ63th2dDZ4Ryea+Yp4CyB61wRKAS ry+xLKKq/VVvbKgfVYTWWmNWUbTbypRzSzcDpealG2UZusRu16e4HP7qXNcYG7xnmhtC xtfg== X-Gm-Message-State: ABy/qLau/yjceE/HaFlpnu6ZF04J7sUTZjvH4FBen6c0QVOYP1gI9bqB NJw/aQXExiZ3fvm/hp2LNbLfcg== X-Received: by 2002:a2e:954a:0:b0:2b5:8eae:7846 with SMTP id t10-20020a2e954a000000b002b58eae7846mr2128422ljh.18.1689956365954; Fri, 21 Jul 2023 09:19:25 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:25 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org, Steen Hegelund Subject: [PATCH v8.1 net-next 01/23] net/tcp: Prepare tcp_md5sig_pool for TCP-AO Date: Fri, 21 Jul 2023 17:18:52 +0100 Message-ID: <20230721161916.542667-2-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772047891287458906 X-GMAIL-MSGID: 1772047891287458906 TCP-AO, similarly to TCP-MD5, needs to allocate tfms on a slow-path, which is setsockopt() and use crypto ahash requests on fast paths, which are RX/TX softirqs. Also, it needs a temporary/scratch buffer for preparing the hash. Rework tcp_md5sig_pool in order to support other hashing algorithms than MD5. It will make it possible to share pre-allocated crypto_ahash descriptors and scratch area between all TCP hash users. Internally tcp_sigpool calls crypto_clone_ahash() API over pre-allocated crypto ahash tfm. Kudos to Herbert, who provided this new crypto API. I was a little concerned over GFP_ATOMIC allocations of ahash and crypto_request in RX/TX (see tcp_sigpool_start()), so I benchmarked both "backends" with different algorithms, using patched version of iperf3[2]. On my laptop with i7-7600U @ 2.80GHz: clone-tfm per-CPU-requests TCP-MD5 2.25 Gbits/sec 2.30 Gbits/sec TCP-AO(hmac(sha1)) 2.53 Gbits/sec 2.54 Gbits/sec TCP-AO(hmac(sha512)) 1.67 Gbits/sec 1.64 Gbits/sec TCP-AO(hmac(sha384)) 1.77 Gbits/sec 1.80 Gbits/sec TCP-AO(hmac(sha224)) 1.29 Gbits/sec 1.30 Gbits/sec TCP-AO(hmac(sha3-512)) 481 Mbits/sec 480 Mbits/sec TCP-AO(hmac(md5)) 2.07 Gbits/sec 2.12 Gbits/sec TCP-AO(hmac(rmd160)) 1.01 Gbits/sec 995 Mbits/sec TCP-AO(cmac(aes128)) [not supporetd yet] 2.11 Gbits/sec So, it seems that my concerns don't have strong grounds and per-CPU crypto_request allocation can be dropped/removed from tcp_sigpool once ciphers get crypto_clone_ahash() support. [1]: https://lore.kernel.org/all/ZDefxOq6Ax0JeTRH@gondor.apana.org.au/T/#u [2]: https://github.com/0x7f454c46/iperf/tree/tcp-md5-ao Signed-off-by: Dmitry Safonov Reviewed-by: Steen Hegelund --- include/net/tcp.h | 46 +++-- net/ipv4/Kconfig | 4 + net/ipv4/Makefile | 1 + net/ipv4/tcp.c | 135 +++------------ net/ipv4/tcp_ipv4.c | 97 ++++++----- net/ipv4/tcp_minisocks.c | 21 ++- net/ipv4/tcp_sigpool.c | 357 +++++++++++++++++++++++++++++++++++++++ net/ipv6/tcp_ipv6.c | 58 +++---- 8 files changed, 510 insertions(+), 209 deletions(-) create mode 100644 net/ipv4/tcp_sigpool.c diff --git a/include/net/tcp.h b/include/net/tcp.h index d17cb8ab4c48..95651f9c9242 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1692,12 +1692,35 @@ union tcp_md5sum_block { #endif }; -/* - pool: digest algorithm, hash description and scratch buffer */ -struct tcp_md5sig_pool { - struct ahash_request *md5_req; - void *scratch; +/* + * struct tcp_sigpool - per-CPU pool of ahash_requests + * @scratch: per-CPU temporary area, that can be used between + * tcp_sigpool_start() and tcp_sigpool_end() to perform + * crypto request + * @req: pre-allocated ahash request + */ +struct tcp_sigpool { + void *scratch; + struct ahash_request *req; }; +int tcp_sigpool_alloc_ahash(const char *alg, size_t scratch_size); +void tcp_sigpool_get(unsigned int id); +void tcp_sigpool_release(unsigned int id); +int tcp_sigpool_hash_skb_data(struct tcp_sigpool *hp, + const struct sk_buff *skb, + unsigned int header_len); +/** + * tcp_sigpool_start - disable bh and start using tcp_sigpool_ahash + * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash() + * @c: returned tcp_sigpool for usage (uninitialized on failure) + */ +int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c); +/** + * tcp_sigpool_end - enable bh and stop using tcp_sigpool + */ +void tcp_sigpool_end(struct tcp_sigpool *c); +size_t tcp_sigpool_algo(unsigned int id, char *buf, size_t buf_len); /* - 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); @@ -1753,17 +1776,12 @@ 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); +int tcp_md5_alloc_sigpool(void); +void tcp_md5_release_sigpool(void); +void tcp_md5_add_sigpool(void); +extern int tcp_md5_sigpool_id; -struct tcp_md5sig_pool *tcp_get_md5sig_pool(void); -static inline void tcp_put_md5sig_pool(void) -{ - local_bh_enable(); -} - -int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *, - unsigned int header_len); -int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, +int tcp_md5_hash_key(struct tcp_sigpool *hp, const struct tcp_md5sig_key *key); /* From tcp_fastopen.c */ diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 2dfb12230f08..89e2ab023272 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -741,10 +741,14 @@ config DEFAULT_TCP_CONG default "bbr" if DEFAULT_BBR default "cubic" +config TCP_SIGPOOL + tristate + config TCP_MD5SIG bool "TCP: MD5 Signature Option support (RFC2385)" select CRYPTO select CRYPTO_MD5 + select TCP_SIGPOOL help RFC2385 specifies a method of giving MD5 protection to TCP sessions. Its main (only?) use is to protect BGP sessions between core routers diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index b18ba8ef93ad..cd760793cfcb 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o +obj-$(CONFIG_TCP_SIGPOOL) += tcp_sigpool.o obj-$(CONFIG_NET_SOCK_MSG) += tcp_bpf.o obj-$(CONFIG_BPF_SYSCALL) += udp_bpf.o obj-$(CONFIG_NETLABEL) += cipso_ipv4.o diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index aca5620cf3ba..ca2c74c282ad 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4310,141 +4310,44 @@ 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_sigpool_id = -1; +EXPORT_SYMBOL_GPL(tcp_md5_sigpool_id); -static void __tcp_alloc_md5sig_pool(void) +int tcp_md5_alloc_sigpool(void) { - struct crypto_ahash *hash; - int cpu; + size_t scratch_size; + int ret; - 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; - - ahash_request_set_callback(req, 0, NULL, NULL); - - per_cpu(tcp_md5sig_pool, cpu).md5_req = req; + scratch_size = sizeof(union tcp_md5sum_block) + sizeof(struct tcphdr); + ret = tcp_sigpool_alloc_ahash("md5", scratch_size); + if (ret >= 0) { + tcp_md5_sigpool_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(); - /* Paired with READ_ONCE() from tcp_alloc_md5sig_pool() - * and tcp_get_md5sig_pool(). - */ - WRITE_ONCE(tcp_md5sig_pool_populated, true); + return ret; } -bool tcp_alloc_md5sig_pool(void) +void tcp_md5_release_sigpool(void) { - /* Paired with WRITE_ONCE() from __tcp_alloc_md5sig_pool() */ - if (unlikely(!READ_ONCE(tcp_md5sig_pool_populated))) { - mutex_lock(&tcp_md5sig_mutex); - - if (!tcp_md5sig_pool_populated) - __tcp_alloc_md5sig_pool(); - - mutex_unlock(&tcp_md5sig_mutex); - } - /* Paired with WRITE_ONCE() from __tcp_alloc_md5sig_pool() */ - return READ_ONCE(tcp_md5sig_pool_populated); + tcp_sigpool_release(tcp_md5_sigpool_id); } -EXPORT_SYMBOL(tcp_alloc_md5sig_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_sigpool(void) { - local_bh_disable(); - - /* Paired with WRITE_ONCE() from __tcp_alloc_md5sig_pool() */ - if (READ_ONCE(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; + tcp_sigpool_get(tcp_md5_sigpool_id); } -EXPORT_SYMBOL(tcp_get_md5sig_pool); -int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *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; - 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; - - 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_md5_hash_skb_data(hp, frag_iter, 0)) - return 1; - - return 0; -} -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 tcp_sigpool *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 09cffbc82d32..b3b8a11daa71 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1220,10 +1220,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; @@ -1245,15 +1241,21 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, struct tcp_sock *tp = tcp_sk(sk); if (!rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) { - if (tcp_md5sig_info_add(sk, GFP_KERNEL)) + if (tcp_md5_alloc_sigpool()) return -ENOMEM; + if (tcp_md5sig_info_add(sk, GFP_KERNEL)) { + tcp_md5_release_sigpool(); + return -ENOMEM; + } + if (!static_branch_inc(&tcp_md5_needed.key)) { struct tcp_md5sig_info *md5sig; md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk)); rcu_assign_pointer(tp->md5sig_info, NULL); kfree_rcu(md5sig, rcu); + tcp_md5_release_sigpool(); return -EUSERS; } } @@ -1270,8 +1272,12 @@ int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr, struct tcp_sock *tp = tcp_sk(sk); if (!rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) { - if (tcp_md5sig_info_add(sk, sk_gfp_mask(sk, GFP_ATOMIC))) + tcp_md5_add_sigpool(); + + if (tcp_md5sig_info_add(sk, sk_gfp_mask(sk, GFP_ATOMIC))) { + tcp_md5_release_sigpool(); return -ENOMEM; + } if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key)) { struct tcp_md5sig_info *md5sig; @@ -1280,6 +1286,7 @@ int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr, net_warn_ratelimited("Too many TCP-MD5 keys in the system\n"); rcu_assign_pointer(tp->md5sig_info, NULL); kfree_rcu(md5sig, rcu); + tcp_md5_release_sigpool(); return -EUSERS; } } @@ -1379,7 +1386,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 tcp_sigpool *hp, __be32 daddr, __be32 saddr, const struct tcphdr *th, int nbytes) { @@ -1399,38 +1406,35 @@ 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 tcp_sigpool hp; - hp = tcp_get_md5sig_pool(); - if (!hp) - goto clear_hash_noput; - req = hp->md5_req; + if (tcp_sigpool_start(tcp_md5_sigpool_id, &hp)) + goto clear_hash_nostart; - 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(); + tcp_sigpool_end(&hp); return 0; clear_hash: - tcp_put_md5sig_pool(); -clear_hash_noput: + tcp_sigpool_end(&hp); +clear_hash_nostart: memset(md5_hash, 0, 16); return 1; } @@ -1439,8 +1443,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 tcp_sigpool hp; const struct tcphdr *th = tcp_hdr(skb); __be32 saddr, daddr; @@ -1453,30 +1456,28 @@ 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) - goto clear_hash_noput; - req = hp->md5_req; + if (tcp_sigpool_start(tcp_md5_sigpool_id, &hp)) + goto clear_hash_nostart; - 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_sigpool_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(); + tcp_sigpool_end(&hp); return 0; clear_hash: - tcp_put_md5sig_pool(); -clear_hash_noput: + tcp_sigpool_end(&hp); +clear_hash_nostart: memset(md5_hash, 0, 16); return 1; } @@ -2294,6 +2295,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_sigpool(); +} +#endif + void tcp_v4_destroy_sock(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); @@ -2318,10 +2331,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 c8f2aa003387..5616b6a34bee 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -261,10 +261,9 @@ static void tcp_time_wait_init(struct sock *sk, struct tcp_timewait_sock *tcptw) tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC); if (!tcptw->tw_md5_key) return; - if (!tcp_alloc_md5sig_pool()) - goto out_free; if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key)) goto out_free; + tcp_md5_add_sigpool(); } return; out_free: @@ -349,16 +348,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_sigpool(); +} +#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/ipv4/tcp_sigpool.c b/net/ipv4/tcp_sigpool.c new file mode 100644 index 000000000000..93940761c4ec --- /dev/null +++ b/net/ipv4/tcp_sigpool.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include + +static size_t __scratch_size; +static DEFINE_PER_CPU(void __rcu *, sigpool_scratch); + +struct sigpool_entry { + struct crypto_ahash *hash; + const char *alg; + struct kref kref; + uint16_t needs_key:1, + reserved:15; +}; + +#define CPOOL_SIZE (PAGE_SIZE / sizeof(struct sigpool_entry)) +static struct sigpool_entry cpool[CPOOL_SIZE]; +static unsigned int cpool_populated; +static DEFINE_MUTEX(cpool_mutex); + +/* Slow-path */ +struct scratches_to_free { + struct rcu_head rcu; + unsigned int cnt; + void *scratches[]; +}; + +static void free_old_scratches(struct rcu_head *head) +{ + struct scratches_to_free *stf; + + stf = container_of(head, struct scratches_to_free, rcu); + while (stf->cnt--) + kfree(stf->scratches[stf->cnt]); + kfree(stf); +} + +/** + * sigpool_reserve_scratch - re-allocates scratch buffer, slow-path + * @size: request size for the scratch/temp buffer + */ +static int sigpool_reserve_scratch(size_t size) +{ + struct scratches_to_free *stf; + size_t stf_sz = struct_size(stf, scratches, num_possible_cpus()); + int cpu, err = 0; + + lockdep_assert_held(&cpool_mutex); + if (__scratch_size >= size) + return 0; + + stf = kmalloc(stf_sz, GFP_KERNEL); + if (!stf) + return -ENOMEM; + stf->cnt = 0; + + size = max(size, __scratch_size); + cpus_read_lock(); + for_each_possible_cpu(cpu) { + void *scratch, *old_scratch; + + scratch = kmalloc_node(size, GFP_KERNEL, cpu_to_node(cpu)); + if (!scratch) { + err = -ENOMEM; + break; + } + + old_scratch = rcu_replace_pointer(per_cpu(sigpool_scratch, cpu), scratch, lockdep_is_held(&cpool_mutex)); + if (!cpu_online(cpu) || !old_scratch) { + kfree(old_scratch); + continue; + } + stf->scratches[stf->cnt++] = old_scratch; + } + cpus_read_unlock(); + if (!err) + __scratch_size = size; + + call_rcu(&stf->rcu, free_old_scratches); + return err; +} + +static void sigpool_scratch_free(void) +{ + int cpu; + + for_each_possible_cpu(cpu) + kfree(rcu_replace_pointer(per_cpu(sigpool_scratch, cpu), + NULL, lockdep_is_held(&cpool_mutex))); + __scratch_size = 0; +} + +static int __cpool_try_clone(struct crypto_ahash *hash) +{ + struct crypto_ahash *tmp; + + tmp = crypto_clone_ahash(hash); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + crypto_free_ahash(tmp); + return 0; +} + +static int __cpool_alloc_ahash(struct sigpool_entry *e, const char *alg) +{ + struct crypto_ahash *cpu0_hash; + int ret; + + e->alg = kstrdup(alg, GFP_KERNEL); + if (!e->alg) + return -ENOMEM; + + cpu0_hash = crypto_alloc_ahash(alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(cpu0_hash)) { + ret = PTR_ERR(cpu0_hash); + goto out_free_alg; + } + + e->needs_key = crypto_ahash_get_flags(cpu0_hash) & CRYPTO_TFM_NEED_KEY; + + ret = __cpool_try_clone(cpu0_hash); + if (ret) + goto out_free_cpu0_hash; + e->hash = cpu0_hash; + kref_init(&e->kref); + return 0; + +out_free_cpu0_hash: + crypto_free_ahash(cpu0_hash); +out_free_alg: + kfree(e->alg); + e->alg = NULL; + return ret; +} + +/** + * tcp_sigpool_alloc_ahash - allocates pool for ahash requests + * @alg: name of async hash algorithm + * @scratch_size: reserve a tcp_sigpool::scratch buffer of this size + */ +int tcp_sigpool_alloc_ahash(const char *alg, size_t scratch_size) +{ + int i, ret; + + /* slow-path */ + mutex_lock(&cpool_mutex); + ret = sigpool_reserve_scratch(scratch_size); + if (ret) + goto out; + for (i = 0; i < cpool_populated; i++) { + if (!cpool[i].alg) + continue; + if (strcmp(cpool[i].alg, alg)) + continue; + + if (kref_read(&cpool[i].kref) > 0) + kref_get(&cpool[i].kref); + else + kref_init(&cpool[i].kref); + ret = i; + goto out; + } + + for (i = 0; i < cpool_populated; i++) { + if (!cpool[i].alg) + break; + } + if (i >= CPOOL_SIZE) { + ret = -ENOSPC; + goto out; + } + + ret = __cpool_alloc_ahash(&cpool[i], alg); + if (!ret) { + ret = i; + if (i == cpool_populated) + cpool_populated++; + } +out: + mutex_unlock(&cpool_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(tcp_sigpool_alloc_ahash); + +static void __cpool_free_entry(struct sigpool_entry *e) +{ + crypto_free_ahash(e->hash); + 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 < cpool_populated; 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) + sigpool_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); +} + +/** + * tcp_sigpool_release - decreases number of users for a pool. If it was + * the last user of the pool, releases any memory that was consumed. + * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash() + */ +void tcp_sigpool_release(unsigned int id) +{ + if (WARN_ON_ONCE(id > cpool_populated || !cpool[id].alg)) + return; + + /* slow-path */ + kref_put(&cpool[id].kref, cpool_schedule_cleanup); +} +EXPORT_SYMBOL_GPL(tcp_sigpool_release); + +/** + * tcp_sigpool_get - increases number of users (refcounter) for a pool + * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash() + */ +void tcp_sigpool_get(unsigned int id) +{ + if (WARN_ON_ONCE(id > cpool_populated || !cpool[id].alg)) + return; + kref_get(&cpool[id].kref); +} +EXPORT_SYMBOL_GPL(tcp_sigpool_get); + +int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c) +{ + struct crypto_ahash *hash; + + rcu_read_lock_bh(); + if (WARN_ON_ONCE(id > cpool_populated || !cpool[id].alg)) { + rcu_read_unlock_bh(); + return -EINVAL; + } + + hash = crypto_clone_ahash(cpool[id].hash); + if (IS_ERR(hash)) { + rcu_read_unlock_bh(); + return PTR_ERR(hash); + } + + c->req = ahash_request_alloc(hash, GFP_ATOMIC); + if (!c->req) { + crypto_free_ahash(hash); + rcu_read_unlock_bh(); + return -ENOMEM; + } + ahash_request_set_callback(c->req, 0, NULL, NULL); + + /* Pairs with tcp_sigpool_reserve_scratch(), scratch area is + * valid (allocated) until tcp_sigpool_end(). + */ + c->scratch = rcu_dereference_bh(*this_cpu_ptr(&sigpool_scratch)); + return 0; +} +EXPORT_SYMBOL_GPL(tcp_sigpool_start); + +void tcp_sigpool_end(struct tcp_sigpool *c) +{ + struct crypto_ahash *hash = crypto_ahash_reqtfm(c->req); + + rcu_read_unlock_bh(); + ahash_request_free(c->req); + crypto_free_ahash(hash); +} +EXPORT_SYMBOL_GPL(tcp_sigpool_end); + +/** + * tcp_sigpool_algo - return algorithm of tcp_sigpool + * @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash() + * @buf: buffer to return name of algorithm + * @buf_len: size of @buf + */ +size_t tcp_sigpool_algo(unsigned int id, char *buf, size_t buf_len) +{ + if (WARN_ON_ONCE(id > cpool_populated || !cpool[id].alg)) + return -EINVAL; + + return strscpy(buf, cpool[id].alg, buf_len); +} +EXPORT_SYMBOL_GPL(tcp_sigpool_algo); + +/** + * tcp_sigpool_hash_skb_data - hash data in skb with initialized tcp_sigpool + * @hp: tcp_sigpool pointer + * @skb: buffer to add sign for + * @header_len: TCP header length for this segment + */ +int tcp_sigpool_hash_skb_data(struct tcp_sigpool *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; + + 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_sigpool_hash_skb_data(hp, frag_iter, 0)) + return 1; + + return 0; +} +EXPORT_SYMBOL(tcp_sigpool_hash_skb_data); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Per-CPU pool of crypto requests"); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 4714eb695913..afa8db712dfb 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -670,7 +670,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 tcp_sigpool *hp, const struct in6_addr *daddr, const struct in6_addr *saddr, const struct tcphdr *th, int nbytes) @@ -691,39 +691,36 @@ 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 tcp_sigpool hp; - hp = tcp_get_md5sig_pool(); - if (!hp) - goto clear_hash_noput; - req = hp->md5_req; + if (tcp_sigpool_start(tcp_md5_sigpool_id, &hp)) + goto clear_hash_nostart; - 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(); + tcp_sigpool_end(&hp); return 0; clear_hash: - tcp_put_md5sig_pool(); -clear_hash_noput: + tcp_sigpool_end(&hp); +clear_hash_nostart: memset(md5_hash, 0, 16); return 1; } @@ -734,8 +731,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 tcp_sigpool hp; const struct tcphdr *th = tcp_hdr(skb); if (sk) { /* valid for establish/request sockets */ @@ -747,30 +743,28 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, daddr = &ip6h->daddr; } - hp = tcp_get_md5sig_pool(); - if (!hp) - goto clear_hash_noput; - req = hp->md5_req; + if (tcp_sigpool_start(tcp_md5_sigpool_id, &hp)) + goto clear_hash_nostart; - 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_sigpool_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(); + tcp_sigpool_end(&hp); return 0; clear_hash: - tcp_put_md5sig_pool(); -clear_hash_noput: + tcp_sigpool_end(&hp); +clear_hash_nostart: memset(md5_hash, 0, 16); return 1; } From patchwork Fri Jul 21 16:18:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123995 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp343454vqg; Fri, 21 Jul 2023 10:04:10 -0700 (PDT) X-Google-Smtp-Source: APBJJlFw1yMKelH9xcMjbxhJySNXH+/QV1ptnBYA0gPjr5GyfctoGlvqlZZivZaiotp7ZPKKG4QR X-Received: by 2002:a05:6a00:1484:b0:675:70d7:1eb4 with SMTP id v4-20020a056a00148400b0067570d71eb4mr635989pfu.14.1689959050410; Fri, 21 Jul 2023 10:04:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689959050; cv=none; d=google.com; s=arc-20160816; b=mGtIuQ4ctt6WXZ4CA/B37pg2skqXNDUvDX49iMvA8NHR4xmLFMVYyMp1TAktki6zPm wCzthfY0QQPsP6mnNw70fWmvwHpiHRnNMeXRXtE6znaZThCd/wonSAnspJw4ZQvLGgEP eQ/XfVvv6Mv14+NEFr+n8UM7n3MUdyf0pnjkm5k8HxYUbqADv9sT6F18nWn7impQJrOZ Zd3eA0D731uhjt0A74UHks43eP6+QIo+jcIfAnF++nu52ACayFllR+T15FqiOggVcYBl dQTOgcZ0e3Vs2S/683CPuEhvogH9Pk70ggQ8LIwcAt7ZASd5Oe9yqnBQ3xkAySrNx0iv mnqg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=SXko8P6r7e8uerkdDw+CjI2zgl0TQ49mqHOxxtoLcMU=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=UA+/O+2Z+jL+7lLvENS2PFEpX5Ocw5DYkETVmGPV62R5KNZmVUhl5PiCLwsYs4Ocif rQoYAKLwm+2m30tVJMT1qp8NKGNOSL4OBm8icfUDaSQWvbZERh7D2x/VG6AsbZAcx+26 cXn4X+KBKhOSrmxCyXgM4otDgrB/U1WI9x+GDtbCc/qdhLXzbYqcx4lloiGV+AJvW7f0 XpiqfWMajGlpQHXB+APFMrdTDkmvOjlelsQxnLI1aruDQb8sEqhFWFmPnV2/Bzf1/J3R eoWSkg+6DLtr03hGTi+sCiS+frxyoQpallm5Zzaiqx+SOCYVP6BWGtUYYlhDeKXTjHTH 9zhA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=TCjvhh5Y; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id u11-20020a62790b000000b0067a4b6093a8si3356821pfc.29.2023.07.21.10.03.51; Fri, 21 Jul 2023 10:04:10 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=TCjvhh5Y; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232673AbjGUQVt (ORCPT + 99 others); Fri, 21 Jul 2023 12:21:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51358 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232790AbjGUQVQ (ORCPT ); Fri, 21 Jul 2023 12:21:16 -0400 Received: from mail-lj1-x236.google.com (mail-lj1-x236.google.com [IPv6:2a00:1450:4864:20::236]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B188949C0 for ; Fri, 21 Jul 2023 09:19:29 -0700 (PDT) Received: by mail-lj1-x236.google.com with SMTP id 38308e7fff4ca-2b6f943383eso31218501fa.2 for ; Fri, 21 Jul 2023 09:19:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956368; x=1690561168; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=SXko8P6r7e8uerkdDw+CjI2zgl0TQ49mqHOxxtoLcMU=; b=TCjvhh5YZl5Fs71DrHekt0EQrMBxfzWHIzsBcYRCkov27vrAzqcAJM6JH+9PYy4dcq 7JqaOS79lh8CWgzGkZJBYLhYJeLW0JE/R3ydBVYAxrfnEN7ghlq4bgiO0MbJVVZqlyOA 3znu6TquT8ZDLwfNqFfWN+5sd+6KIL1QvmUlhS3wMAsdl3cyPkkRoqrrzgUzqL9C4P32 vSa774TofALarjjbFVpDesw/PejAQlwvv8X7LvJC1DLRsfNUXjeoaYJjFS14GZ5eir93 +wr/4x+jBDAyBK2AAf8ZU0eRpadO1v46lzAPhxNvi0RL7ial+PJa3lRg4Wnf9x4mi7ME 0b+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956368; x=1690561168; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=SXko8P6r7e8uerkdDw+CjI2zgl0TQ49mqHOxxtoLcMU=; b=gG2LPCgQIOSu1Dt5W61EPoC4fn910h/LuTGELvf786r1TJyKrsKUQUO5S/tNZwtO90 nstz/n92sxBWUD/GLm8AEisfiOJhGQh46tnszibOQuKIt6btgWhTbI6mUYnUMJ2slqi3 +e3dtF9oTgORC7TOYVoz6LXQPPy8aSXP88xQq57Uzj00oh15Ru2EJ6yqxPDOg7jTuRiN IAypldu49qEkzf4Zd6iTn1QHe/2SPrTUAgBCK9Vdo9CYl8SgfgWw1sTSWpTeGUw44FIe 4+T2MvkfaG60ZYBnb9JwF/OSDi0o+sdiFYCWmH0+OIRwm2gJLwEQ4RWBDrQjUkj+lkP4 q02Q== X-Gm-Message-State: ABy/qLb0xSp3w1eQl1Sgxm5+Q+qw7xP6RIBdfmYscR9yAqte4x66B136 l0cHelogo25SyVSdDTBuhV39od2jtFl3Q9UeWV4= X-Received: by 2002:a2e:98d3:0:b0:2b6:e3d5:76a7 with SMTP id s19-20020a2e98d3000000b002b6e3d576a7mr2009258ljj.24.1689956367695; Fri, 21 Jul 2023 09:19:27 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:27 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 02/23] net/tcp: Add TCP-AO config and structures Date: Fri, 21 Jul 2023 17:18:53 +0100 Message-ID: <20230721161916.542667-3-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050501591132000 X-GMAIL-MSGID: 1772050501591132000 Introduce new kernel config option and common structures as well as helpers to be used by TCP-AO code. Co-developed-by: Francesco Ruggeri Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/linux/tcp.h | 9 +++- include/net/tcp.h | 8 +--- include/net/tcp_ao.h | 91 ++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/tcp.h | 2 + net/ipv4/Kconfig | 13 ++++++ 5 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 include/net/tcp_ao.h diff --git a/include/linux/tcp.h b/include/linux/tcp.h index d16abdb3541a..9eb15e798c32 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -437,13 +437,18 @@ struct tcp_sock { bool syn_smc; /* SYN includes SMC */ #endif -#ifdef CONFIG_TCP_MD5SIG -/* TCP AF-Specific parts; only used by MD5 Signature support so far */ +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) +/* TCP AF-Specific parts; only used by TCP-AO/MD5 Signature support so far */ const struct tcp_sock_af_ops *af_specific; +#ifdef CONFIG_TCP_MD5SIG /* 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 +#endif /* TCP fastopen related information */ struct tcp_fastopen_request *fastopen_req; diff --git a/include/net/tcp.h b/include/net/tcp.h index 95651f9c9242..d1230ee888fd 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -1643,12 +1644,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..3870cc29af10 --- /dev/null +++ b/include/net/tcp_ao.h @@ -0,0 +1,91 @@ +/* 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) + +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 tcp_sigpool_id; + unsigned int digest_size; + u16 port; + u8 prefixlen; + u8 family; + u8 keylen; + u8 keyflags; + u8 sndid; + u8 rcvid; + u8 maclen; + 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_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; +} + +static inline int tcp_ao_sizeof_key(const struct tcp_ao_key *key) +{ + return sizeof(struct tcp_ao_key) + (key->digest_size << 1); +} + +struct tcp_ao_info { + /* List of tcp_ao_key's */ + struct hlist_head head; + /* 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. + * ::current_key may change in RX to the key that was requested by + * the peer, please use READ_ONCE()/WRITE_ONCE() in order to avoid + * load/store tearing. + * Do the same for ::rnext_key, if you don't hold socket lock + * (it's changed only by userspace request in setsockopt()). + */ + struct tcp_ao_key *current_key; + struct tcp_ao_key *rnext_key; + u32 flags; + __be32 lisn; + __be32 risn; + struct rcu_head rcu; +}; + +#endif /* _TCP_AO_H */ diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 879eeb0a084b..5655bfe28b8d 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -348,6 +348,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 89e2ab023272..8e94ed7c56a0 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -744,6 +744,19 @@ config DEFAULT_TCP_CONG config TCP_SIGPOOL tristate +config TCP_AO + bool "TCP: Authentication Option (RFC5925)" + select CRYPTO + 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 + connections than TCP MD5 (See RFC5925) + + If unsure, say N. + config TCP_MD5SIG bool "TCP: MD5 Signature Option support (RFC2385)" select CRYPTO From patchwork Fri Jul 21 16:18:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123971 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp338637vqg; Fri, 21 Jul 2023 09:57:24 -0700 (PDT) X-Google-Smtp-Source: APBJJlHgrxRyZwvO0LSQrvp71UqsCQJQWK3TFMmomALZFzGJ4K089RgLgM9e5C+I/2LUkSMHt7Ok X-Received: by 2002:ac2:5045:0:b0:4f9:b032:5361 with SMTP id a5-20020ac25045000000b004f9b0325361mr1676078lfm.10.1689958644599; Fri, 21 Jul 2023 09:57:24 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689958644; cv=none; d=google.com; s=arc-20160816; b=U4pDOQG+SIxIpxe7LN87MtmAUFXLGuiefYTXQO5iAjwf1sQAAQ7PJXClYqzYeLgbSV 35+K5+lddRkUDHpbqvWKhvhwGuFUOvtmsPB0HhoCXdm/yKbiVbynm1147BBhM37U86hw s+HffRtzkHlY43zJ3MiPeyouNzmVRbrNZeA2ydKSHFVnQjuuLfVqQ5eRkUdBOga1vg+N pr6KZxpW6DTi7aGcu4pAUhMIU/cioFHoyZrcrA1VP3GxBT5ZC1aUIyBcKxrdFxeG0ULl +jb8RkvZ++zajMTpD/IuSPvG/0N/YhLcIqMePH1MMoIfDSwoOqrpuK1tyv9Xj/CLeTg5 8mAQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=F8M4TpWVYWJQwaqg1VujgRp21aweIDfaeUhNiUIiAFE=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=ce4KZsRKEwbe252zXNcbcVZEy6PV8HgXCzlr10auDhDQ/biq0Zqi0kzz5waRhBV2AE Gi8pd9CfuByQQtvHBTVhwrevGLxMY0W15mQFLBYeADWXlF6EqawqWzWzJdYcaMc6gV+h tMfzLamii/eFBfsISRRcgeGyshNaWG3DpNVup9dKbto4j/rh6xhZs4S02v/BSYfjtK1o 9NoAzzDlw0jii5xpwlxI6m0SvsbDPajRbvCUi7L1oodDQ5+MlWMQwdlbWTZbx9b/sKH/ f5P7stRn8y+Y9L59rwBXROkHRyK/elJWVUj7ddPD1lL+REttjfB9nnentjHm5pwuPdtT 9jJQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=R1TAVnM2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id s3-20020aa7d783000000b00521acb44874si2410166edq.509.2023.07.21.09.57.00; Fri, 21 Jul 2023 09:57:24 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=R1TAVnM2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232814AbjGUQWA (ORCPT + 99 others); Fri, 21 Jul 2023 12:22:00 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51464 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232663AbjGUQVV (ORCPT ); Fri, 21 Jul 2023 12:21:21 -0400 Received: from mail-wm1-x32e.google.com (mail-wm1-x32e.google.com [IPv6:2a00:1450:4864:20::32e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7CB2E4208 for ; Fri, 21 Jul 2023 09:19:31 -0700 (PDT) Received: by mail-wm1-x32e.google.com with SMTP id 5b1f17b1804b1-3fc04692e20so18792325e9.0 for ; Fri, 21 Jul 2023 09:19:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956370; x=1690561170; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=F8M4TpWVYWJQwaqg1VujgRp21aweIDfaeUhNiUIiAFE=; b=R1TAVnM2FqGho0LallIOfRnNRFjGXclLvnv5ihB91Ch/nr2AGhL1KBN9S+52vhd0cy FwsxFos2+bTOUxcsh4V82QS2YmimLHPGfve+QbQWvt6HfPTj/0WgidAJqz08KE8wl8hG PPy0xW8CQ+OLiW7dWHQiSwtK67ZF9jq3cAuWKGvgbvt2O1PTAoLWFcNhS+CyczigCf6Z u1pgC7tVuCnjP+838uYoCyy9qPq+1NeRS8IUmSOctK1O4f7jpIBmqN8WpDQe2j+kjNBg DW3hI6KPqMiRSv4BkLkEcrf5IhSPaFBo55d5PbXizYdrcy0fRI88SZB9loR5qM8/mmSH Dk6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956370; x=1690561170; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=F8M4TpWVYWJQwaqg1VujgRp21aweIDfaeUhNiUIiAFE=; b=Ou2rqpa4YFZT4GoB6faKtrDsOvxVcA6E2EeYtyZK3GyUBKuSEmQbW1xEXTw49mQYXA JAkYzLdqLc0J4X55xgMEOIRNQDxxbWqKl0SkRHvwgRDIl9XRvsgQqh62C7BNjp6LaNTC HLW+MkBSc7RngfDossqM1c698PVyPjaJ+YD6z0gPE2w8IylinnJkCiQZ5ZxPuiR8Yjxm XYB3MjAZtPVtcTaGmr17GEYg0jM72kdTzYRm97ZcAymNFvNdMcWvk/RYDpz6hsY5fU/C OzwYg1OnOFyQ4OXIXQSV4S4f3irX9jP0Ge9zufX767HgSbZbOPSwTgHqafId73eZWsAg 420Q== X-Gm-Message-State: ABy/qLYWsR8V6hJqph3LhR6nhS1D/Dt9F73yEQCGoH1TnjXXtpBQ3jqe KshisuWw+Bd3lEqW5NrxxTeFHw== X-Received: by 2002:a1c:f715:0:b0:3fa:9538:963e with SMTP id v21-20020a1cf715000000b003fa9538963emr1855897wmh.36.1689956369602; Fri, 21 Jul 2023 09:19:29 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:29 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 03/23] net/tcp: Introduce TCP_AO setsockopt()s Date: Fri, 21 Jul 2023 17:18:54 +0100 Message-ID: <20230721161916.542667-4-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050075650703064 X-GMAIL-MSGID: 1772050075650703064 Add 3 setsockopt()s: 1. TCP_AO_ADD_KEY to add a new Master Key Tuple (MKT) on a socket 2. TCP_AO_DEL_KEY to delete present MKT from a socket 3. TCP_AO_INFO to change flags, Current_key/RNext_key on a TCP-AO sk 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 KeyID 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/linux/sockptr.h | 23 ++ include/net/tcp.h | 6 + include/net/tcp_ao.h | 18 +- include/uapi/linux/tcp.h | 46 +++ net/ipv4/Makefile | 1 + net/ipv4/tcp.c | 17 + net/ipv4/tcp_ao.c | 816 +++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 10 +- net/ipv6/Makefile | 1 + net/ipv6/tcp_ao.c | 19 + net/ipv6/tcp_ipv6.c | 39 +- 11 files changed, 978 insertions(+), 18 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 bae5e2369b4f..307961b41541 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 d1230ee888fd..98b970c078d1 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2122,6 +2122,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 3870cc29af10..1739116c6902 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -82,10 +82,26 @@ struct tcp_ao_info { */ struct tcp_ao_key *current_key; struct tcp_ao_key *rnext_key; - u32 flags; + u32 ao_required :1, + __unused :31; __be32 lisn; __be32 risn; struct rcu_head rcu; }; +#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 5655bfe28b8d..250e0ce2cc38 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_ADD_KEY 38 /* Add/Set MKT */ +#define TCP_AO_DEL_KEY 39 /* Delete MKT */ +#define TCP_AO_INFO 40 /* Modify TCP-AO per-socket options */ #define TCP_REPAIR_ON 1 #define TCP_REPAIR_OFF 0 @@ -350,6 +353,49 @@ struct tcp_diag_md5sig { #define TCP_AO_MAXKEYLEN 80 +#define TCP_AO_KEYF_IFINDEX (1 << 0) /* L3 ifindex for VRF */ + +struct tcp_ao_add { /* setsockopt(TCP_AO_ADD_KEY) */ + struct __kernel_sockaddr_storage addr; /* peer's address for the key */ + char alg_name[64]; /* crypto hash algorithm to use */ + __s32 ifindex; /* L3 dev index for VRF */ + __u32 set_current :1, /* set key as Current_key at once */ + set_rnext :1, /* request it from peer with RNext_key */ + reserved :30; /* must be 0 */ + __u16 reserved2; /* padding, must be 0 */ + __u8 prefix; /* peer's address prefix */ + __u8 sndid; /* SendID for outgoing segments */ + __u8 rcvid; /* RecvID to match for incoming seg */ + __u8 maclen; /* length of authentication code (hash) */ + __u8 keyflags; /* see TCP_AO_KEYF_ */ + __u8 keylen; /* length of ::key */ + __u8 key[TCP_AO_MAXKEYLEN]; +} __attribute__((aligned(8))); + +struct tcp_ao_del { /* setsockopt(TCP_AO_DEL_KEY) */ + struct __kernel_sockaddr_storage addr; /* peer's address for the key */ + __s32 ifindex; /* L3 dev index for VRF */ + __u32 set_current :1, /* corresponding ::current_key */ + set_rnext :1, /* corresponding ::rnext */ + reserved :30; /* must be 0 */ + __u16 reserved2; /* padding, must be 0 */ + __u8 prefix; /* peer's address prefix */ + __u8 sndid; /* SendID for outgoing segments */ + __u8 rcvid; /* RecvID to match for incoming seg */ + __u8 current_key; /* KeyID to set as Current_key */ + __u8 rnext; /* KeyID to set as Rnext_key */ + __u8 keyflags; /* see TCP_AO_KEYF_ */ +} __attribute__((aligned(8))); + +struct tcp_ao_info_opt { /* setsockopt(TCP_AO_INFO) */ + __u32 set_current :1, /* corresponding ::current_key */ + set_rnext :1, /* corresponding ::rnext */ + ao_required :1, /* don't accept non-AO connects */ + reserved :29; /* must be 0 */ + __u8 current_key; /* KeyID to set as Current_key */ + __u8 rnext; /* KeyID to set as Rnext_key */ +} __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 cd760793cfcb..e144a02a6a61 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -69,6 +69,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 ca2c74c282ad..d0b0565fa11e 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3608,6 +3608,23 @@ 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_ADD_KEY: + case TCP_AO_DEL_KEY: + case TCP_AO_INFO: { + /* If this is the first TCP-AO setsockopt() on the socket, + * sk_state has to be LISTEN or CLOSE + */ + if (((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) || + rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk))) + err = tp->af_specific->ao_parse(sk, optname, optval, + optlen); + else + err = -EISCONN; + 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..2b2696e24dcc --- /dev/null +++ b/net/ipv4/tcp_ao.c @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * INET An implementation of the TCP Authentication Option (TCP-AO). + * See RFC5925. + * + * Authors: Dmitry Safonov + * Francesco Ruggeri + * Salam Noureddine + */ +#define pr_fmt(fmt) "TCP: " fmt + +#include +#include +#include + +#include +#include + +/* Optimized version of tcp_ao_do_lookup(): only for sockets for which + * it's known that the keys in ao_info are matching peer's + * family/address/port/VRF/etc. + */ +static struct tcp_ao_key *tcp_ao_established_key(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; +} + +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); +#if IS_ENABLED(CONFIG_IPV6) + } 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); +#endif + } + return -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 IS_ENABLED(CONFIG_IPV6) + 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); + } +#endif + return __tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid, port); +} + +static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk, + const union tcp_ao_addr *addr, int family, u8 prefix, + 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) { + u8 prefixlen = min(prefix, key->prefixlen); + + if (!tcp_ao_key_cmp(key, addr, prefixlen, + family, sndid, rcvid, port)) + return key; + } + return NULL; +} + +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 __tcp_ao_do_lookup(sk, addr, family, U8_MAX, + sndid, rcvid, port); +} + +static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags) +{ + struct tcp_ao_info *ao; + + ao = kzalloc(sizeof(*ao), flags); + if (!ao) + return NULL; + INIT_HLIST_HEAD(&ao->head); + + return ao; +} + +static 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); + + tcp_sigpool_release(key->tcp_sigpool_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 bool tcp_ao_can_set_current_rnext(struct sock *sk) +{ + /* There aren't current/rnext keys on TCP_LISTEN sockets */ + if (sk->sk_state == TCP_LISTEN) + return false; + return true; +} + +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_add *cmd, + union tcp_ao_addr **addr, u16 *port) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->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->prefix != 0) { + __be32 mask; + + if (sin->sin_addr.s_addr == INADDR_ANY) + return -EINVAL; + if (cmd->prefix > 32) + return -EINVAL; + + mask = inet_make_mask(cmd->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_ao_addr *)&sin->sin_addr; + *port = ntohs(sin->sin_port); + return 0; +} + +static int tcp_ao_parse_crypto(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 - 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) { + 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; + } + + err = crypto_ahash_setkey(tfm, key->key, key->keylen); + if (err) + goto err_pool_end; + + tcp_sigpool_end(&hp); + + if (tcp_ao_maclen(key) > key->digest_size) + return -EINVAL; + + return 0; + +err_pool_end: + tcp_sigpool_end(&hp); +err_kfree: + kfree(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, u16 *port, + unsigned short int *family) +{ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->addr; + struct in6_addr *addr = &sin6->sin6_addr; + u8 prefix = cmd->prefix; + + if (sin6->sin6_family != AF_INET6) + return -EINVAL; + + if (tcp_ao_verify_port(sk, ntohs(sin6->sin6_port))) + return -EINVAL; + + /* Check prefix and trailing 0's in addr */ + if (cmd->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_ao_addr *)&addr->s6_addr32[3]; + *family = AF_INET; + *port = ntohs(sin6->sin6_port); + return 0; + } else if (cmd->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_ao_addr *)addr; + *port = ntohs(sin6->sin6_port); + return 0; +} +#else +static inline int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, + union tcp_ao_addr **paddr, u16 *port, + unsigned short int *family) +{ + return -EOPNOTSUPP; +} +#endif + +static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) +{ + if (sk_fullsock(sk)) { + return rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + } + return ERR_PTR(-ESOCKTNOSUPPORT); +} + +#define TCP_AO_KEYF_ALL (0) + +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; + 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)"; + + /* Full TCP header (th->doff << 2) should fit into scratch area, + * see tcp_ao_hash_header(). + */ + 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; + + tfm = crypto_ahash_reqtfm(hp.req); + if (crypto_ahash_alignmask(tfm) > TCP_AO_KEY_ALIGN) { + err = -EOPNOTSUPP; + goto err_pool_end; + } + digest_size = crypto_ahash_digestsize(tfm); + tcp_sigpool_end(&hp); + + size = sizeof(struct tcp_ao_key) + (digest_size << 1); + key = sock_kmalloc(sk, size, GFP_KERNEL); + if (!key) { + err = -ENOMEM; + goto err_free_pool; + } + + key->tcp_sigpool_id = pool_id; + key->digest_size = digest_size; + return key; + +err_pool_end: + tcp_sigpool_end(&hp); +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) +{ + struct tcp_ao_info *ao_info; + union tcp_ao_addr *addr; + struct tcp_ao_key *key; + struct tcp_ao_add cmd; + int ret; + bool first = false; + u16 port; + + if (optlen < sizeof(cmd)) + return -EINVAL; + + ret = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); + if (ret) + return ret; + + if (cmd.keylen > TCP_AO_MAXKEYLEN) + return -EINVAL; + + if (cmd.reserved != 0 || cmd.reserved2 != 0) + 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.keyflags & ~TCP_AO_KEYF_ALL) + return -EINVAL; + + if (cmd.set_current || cmd.set_rnext) { + if (!tcp_ao_can_set_current_rnext(sk)) + return -EINVAL; + } + + ao_info = setsockopt_ao_info(sk); + if (IS_ERR(ao_info)) + return PTR_ERR(ao_info); + + if (!ao_info) { + ao_info = tcp_ao_alloc_info(GFP_KERNEL); + if (!ao_info) + return -ENOMEM; + first = true; + } else { + /* Check that neither RecvID nor SendID match any + * existing key for the peer, RFC5925 3.1: + * > The IDs of MKTs MUST NOT overlap where their + * > TCP connection identifiers overlap. + */ + if (__tcp_ao_do_lookup(sk, addr, family, + cmd.prefix, -1, cmd.rcvid, port)) + return -EEXIST; + if (__tcp_ao_do_lookup(sk, addr, family, + cmd.prefix, cmd.sndid, -1, port)) + return -EEXIST; + } + + key = tcp_ao_key_alloc(sk, &cmd); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + 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.prefix; + key->family = family; + key->keyflags = cmd.keyflags; + key->sndid = cmd.sndid; + key->rcvid = cmd.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); + } + + if (cmd.set_current) + WRITE_ONCE(ao_info->current_key, key); + if (cmd.set_rnext) + 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(key); +err_free_ao: + if (first) + kfree(ao_info); + return ret; +} + +static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, + struct tcp_ao_key *key, + struct tcp_ao_key *new_current, + struct tcp_ao_key *new_rnext) +{ + 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(); + if (new_current) + WRITE_ONCE(ao_info->current_key, new_current); + if (new_rnext) + WRITE_ONCE(ao_info->rnext_key, new_rnext); + + 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_key *key, *new_current = NULL, *new_rnext = NULL; + struct tcp_ao_info *ao_info; + union tcp_ao_addr *addr; + struct tcp_ao_del cmd; + int err; + __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.reserved != 0 || cmd.reserved2 != 0) + return -EINVAL; + + if (cmd.set_current || cmd.set_rnext) { + if (!tcp_ao_can_set_current_rnext(sk)) + return -EINVAL; + } + + ao_info = setsockopt_ao_info(sk); + if (IS_ERR(ao_info)) + return PTR_ERR(ao_info); + if (!ao_info) + return -ENOENT; + + /* For sockets in TCP_CLOSED it's possible set keys that aren't + * matching the future peer (address/port/VRF/etc), + * tcp_ao_connect_init() will choose a correct matching MKT + * if there's any. + */ + if (cmd.set_current) { + new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); + if (!new_current) + return -ENOENT; + } + if (cmd.set_rnext) { + new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); + if (!new_rnext) + return -ENOENT; + } + + if (family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.addr; + + addr = (union tcp_ao_addr *)&sin->sin_addr; + addr_len = sizeof(struct in_addr); + port = ntohs(sin->sin_port); + } else { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.addr; + struct in6_addr *addr6 = &sin6->sin6_addr; + + if (ipv6_addr_v4mapped(addr6)) { + addr = (union tcp_ao_addr *)&addr6->s6_addr32[3]; + addr_len = sizeof(struct in_addr); + family = AF_INET; + } else { + addr = (union tcp_ao_addr *)addr6; + addr_len = sizeof(struct in6_addr); + } + port = ntohs(sin6->sin6_port); + } + prefix = cmd.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.sndid != key->sndid || + cmd.rcvid != key->rcvid) + continue; + + if (family != key->family || + prefix != key->prefixlen || + port != key->port || + memcmp(addr, &key->addr, addr_len)) + continue; + + if (key == new_current || key == new_rnext) + continue; + + return tcp_ao_delete_key(sk, ao_info, key, + new_current, new_rnext); + } + return -ENOENT; +} + +static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, + sockptr_t optval, int optlen) +{ + struct tcp_ao_info *ao_info; + struct tcp_ao_key *new_current = NULL, *new_rnext = NULL; + struct tcp_ao_info_opt cmd; + bool first = false; + int err; + + if (optlen < sizeof(cmd)) + return -EINVAL; + + err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); + if (err) + return err; + + if (cmd.set_current || cmd.set_rnext) { + if (!tcp_ao_can_set_current_rnext(sk)) + return -EINVAL; + } + + if (cmd.reserved != 0) + return -EINVAL; + + ao_info = setsockopt_ao_info(sk); + if (IS_ERR(ao_info)) + return PTR_ERR(ao_info); + if (!ao_info) { + ao_info = tcp_ao_alloc_info(GFP_KERNEL); + if (!ao_info) + return -ENOMEM; + first = true; + } + + /* For sockets in TCP_CLOSED it's possible set keys that aren't + * matching the future peer (address/port/VRF/etc), + * tcp_ao_connect_init() will choose a correct matching MKT + * if there's any. + */ + if (cmd.set_current) { + new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); + if (!new_current) { + err = -ENOENT; + goto out; + } + } + if (cmd.set_rnext) { + new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); + if (!new_rnext) { + err = -ENOENT; + goto out; + } + } + + ao_info->ao_required = cmd.ao_required; + if (new_current) + WRITE_ONCE(ao_info->current_key, new_current); + if (new_rnext) + WRITE_ONCE(ao_info->rnext_key, new_rnext); + if (first) { + sk_gso_disable(sk); + rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); + } + return 0; +out: + if (first) + kfree(ao_info); + return err; +} + +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 -EAFNOSUPPORT; + + switch (cmd) { + case TCP_AO_ADD_KEY: + return tcp_ao_add_cmd(sk, family, optval, optlen); + case TCP_AO_DEL_KEY: + return tcp_ao_del_cmd(sk, family, optval, optlen); + case TCP_AO_INFO: + return tcp_ao_info_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 b3b8a11daa71..8804eb801caf 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2269,11 +2269,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 @@ -2288,7 +2293,7 @@ static int tcp_v4_init_sock(struct sock *sk) icsk->icsk_af_ops = &ipv4_specific; -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) tcp_sk(sk)->af_specific = &tcp_sock_ipv4_specific; #endif @@ -2339,6 +2344,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..d283c59df4c1 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -52,4 +52,5 @@ obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o ifneq ($(CONFIG_IPV6),) obj-$(CONFIG_NET_UDP_TUNNEL) += ip6_udp_tunnel.o obj-y += mcast_snoop.o +obj-$(CONFIG_TCP_AO) += tcp_ao.o endif diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c new file mode 100644 index 000000000000..049ddbabe049 --- /dev/null +++ b/net/ipv6/tcp_ao.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * INET An implementation of the TCP Authentication Option (TCP-AO). + * See RFC5925. + * + * Authors: Dmitry Safonov + * Francesco Ruggeri + * Salam Noureddine + */ +#include + +#include +#include + +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 afa8db712dfb..96ec9c03d9eb 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -76,16 +76,9 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); static const struct inet_connection_sock_af_ops ipv6_mapped; const struct inet_connection_sock_af_ops ipv6_specific; -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) static const struct tcp_sock_af_ops tcp_sock_ipv6_specific; static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific; -#else -static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(const struct sock *sk, - const struct in6_addr *addr, - int l3index) -{ - return NULL; -} #endif /* Helper returning the inet6 address from a given tcp socket. @@ -239,7 +232,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, if (sk_is_mptcp(sk)) mptcpv6_handle_mapped(sk, true); sk->sk_backlog_rcv = tcp_v4_do_rcv; -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) tp->af_specific = &tcp_sock_ipv6_mapped_specific; #endif @@ -252,7 +245,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, if (sk_is_mptcp(sk)) mptcpv6_handle_mapped(sk, false); sk->sk_backlog_rcv = tcp_v6_do_rcv; -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) tp->af_specific = &tcp_sock_ipv6_specific; #endif goto failure; @@ -768,7 +761,13 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, memset(md5_hash, 0, 16); return 1; } - +#else /* CONFIG_TCP_MD5SIG */ +static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(const struct sock *sk, + const struct in6_addr *addr, + int l3index) +{ + return NULL; +} #endif static void tcp_v6_init_req(struct request_sock *req, @@ -1228,7 +1227,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * if (sk_is_mptcp(newsk)) mptcpv6_handle_mapped(newsk, true); newsk->sk_backlog_rcv = tcp_v4_do_rcv; -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) newtp->af_specific = &tcp_sock_ipv6_mapped_specific; #endif @@ -1892,11 +1891,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 @@ -1918,11 +1922,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 @@ -1937,7 +1946,7 @@ static int tcp_v6_init_sock(struct sock *sk) icsk->icsk_af_ops = &ipv6_specific; -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) tcp_sk(sk)->af_specific = &tcp_sock_ipv6_specific; #endif From patchwork Fri Jul 21 16:18:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123969 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp338054vqg; Fri, 21 Jul 2023 09:56:05 -0700 (PDT) X-Google-Smtp-Source: APBJJlGPVz9uZrqyakol09cD6CbPNEbQKu/YKNRPOY7F5+wqDtjCkIGl++nB7fD3JiE3PawKEtsw X-Received: by 2002:a05:6358:930e:b0:139:5a46:ea7e with SMTP id x14-20020a056358930e00b001395a46ea7emr639164rwa.28.1689958565189; Fri, 21 Jul 2023 09:56:05 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689958565; cv=none; d=google.com; s=arc-20160816; b=XALLvs9E7qVHGgKQSRI5Z9vZ2r3VsJ2Nfp4Rb1QMuEMGgEXGphhNRRvOEBXH6hmx8M CKrIUsQI9JBkcoCplYizQt917igwHM9qUUfAOpzY3Cuq4Okg0dSPhCS91deuMcdo3nGw jdmgRw3Z8SUsmxhm6dhpsh85lIJxdN2SSYTGC+zDc4dlF70I3gf4JTaTRL7FM0ncEQsE Sr3eoFQfQzJVZnyKvW8KxVg6y9MWWzN8wfWtyOkQ+OU2LhZXTpS5YGo7jLKmA1ZYsdM+ ZdCqMLjvpnMfJBiQkdlgYJCfCCro+TOzCjDgc2j8HQq0l4174qyQi5yU8mdEacFgQqxO niVg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=s4YI32Z4dqBPHQQhZadGPSq6UvQm2o/KFZCwB2wqJmg=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=z+wItya82T4L9MIAisCnXX5AygvQe1w3tF3Mrw56DPuBJPL28NmcwCQVUp+0njOkhz XBocvmfYtJ3aTivzriLU1HYmamRiI6ymlwiZ2vtu0cNZxdQ9bWKzde5edtZfYwnHWoWZ KeOUaXLKqgJfVaR46//GEZa0eMBJPARy9Vop1hDWux0aApVEJParDecI4unKnG/7sCvW 8cXRoZh0aMQwsAkPq30tibnRc+f12mOseDFvmY/7G6k9fwtz/fa7HLEwI9FtR0vi6Nzr 1EG26ZOAQhTAvVnH53HPOMlvvetNLVblmZchKRyv2FFS/xNPdByX70NaSuwyvwQsZaLS kjVA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=jCDFrVvq; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id u186-20020a6379c3000000b0055aff745fe2si3081502pgc.406.2023.07.21.09.55.52; Fri, 21 Jul 2023 09:56:05 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=jCDFrVvq; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232819AbjGUQWK (ORCPT + 99 others); Fri, 21 Jul 2023 12:22:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51726 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232614AbjGUQVh (ORCPT ); Fri, 21 Jul 2023 12:21:37 -0400 Received: from mail-wm1-x32a.google.com (mail-wm1-x32a.google.com [IPv6:2a00:1450:4864:20::32a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9BE9749CB for ; Fri, 21 Jul 2023 09:19:33 -0700 (PDT) Received: by mail-wm1-x32a.google.com with SMTP id 5b1f17b1804b1-3fd190065a8so19066445e9.3 for ; Fri, 21 Jul 2023 09:19:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956371; x=1690561171; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=s4YI32Z4dqBPHQQhZadGPSq6UvQm2o/KFZCwB2wqJmg=; b=jCDFrVvqVHGezHjBGxEt9gdJ37u0FN6gV6gztAJwTv3kjvQycilU3Wt8Kd/eJ2EjVR ZHuoFI+T3Igq42xUxjC0iX/QGWwazZ7QaSvDpZIkp/HY1wb2HWynxB/ekG+nxseANHmL xRD2Acv31Vcx9N7oIc6Y1V2YHrpzfH3nd9Fc6FSIVvYpnc7XTXP7s9om5jEF+YC2oJne WVd7BaUPiCUc4DBiY6aaL+mZb5Z6I7OAUDjp7tR71qOnJPiNwelebR+fgtvHGsdjr5a+ a763SweZ7rX8g0PKGnehTegtkQl1fs7kxV71eZeCsFimiN8Ny5QsiIMQEoC+caI/Swi7 svgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956371; x=1690561171; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=s4YI32Z4dqBPHQQhZadGPSq6UvQm2o/KFZCwB2wqJmg=; b=kuTuh4zDj9/UGkL43Zn0Vq0N/pXF0SaBn4y70+mmlQUeqYmTgjxzlkonRT/agS2rN1 Gxbi/eo6lZQQlAvhHISWwxLhiqDAWNheZ5OA1JhGQrAEcjs2GSIUpSrwqrSNtmBoWHVR d73CERS8VpnuhUfAw2TtGc1Jer43LmHrhTAdde9zFdHvW1w7wqGBFBCc9HSz10j8eF6O NHipqI1IxaVmjDMOdrz7ynUI4TTKF1QX3IEvfHfSbgL5bzFfftj2KMC9B2BHK+plKX1h CWq71mGsprOJ1rEKOf8AjvQfq7Y9Wx9/HY/Bb0ZPgxIT/NszfQbdkmV0I6/ciq4+Kc+D 1I/Q== X-Gm-Message-State: ABy/qLaGCqfnGCgRv8n0ndQRLlNBq5820YBWHwX22MSl2rIZZwfYZ9Dm dyoNjXR8w5Yx9lMwGB3RNJjrow== X-Received: by 2002:a7b:c7ca:0:b0:3fd:1cfa:939e with SMTP id z10-20020a7bc7ca000000b003fd1cfa939emr1933459wmk.4.1689956371501; Fri, 21 Jul 2023 09:19:31 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:31 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 04/23] net/tcp: Prevent TCP-MD5 with TCP-AO being set Date: Fri, 21 Jul 2023 17:18:55 +0100 Message-ID: <20230721161916.542667-5-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772049992504038378 X-GMAIL-MSGID: 1772049992504038378 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/tcp.h | 43 +++++++++++++++++++++++++++++++++++++-- include/net/tcp_ao.h | 15 ++++++++++++++ net/ipv4/tcp_ao.c | 40 ++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 14 ++++++++++--- net/ipv4/tcp_output.c | 47 +++++++++++++++++++++++++++++++++++++++++++ net/ipv6/tcp_ao.c | 17 ++++++++++++++++ net/ipv6/tcp_ipv6.c | 26 ++++++++++++++++++++---- 7 files changed, 193 insertions(+), 9 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 98b970c078d1..7e414873ef07 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1729,6 +1729,7 @@ int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr, int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family, u8 prefixlen, int l3index, u8 flags); +void tcp_clear_md5_list(struct sock *sk); struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, const struct sock *addr_sk); @@ -1737,14 +1738,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 @@ -1762,6 +1772,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, @@ -2127,6 +2144,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 }; @@ -2539,4 +2559,23 @@ static inline u64 tcp_transmit_time(const struct sock *sk) return 0; } +static inline bool tcp_ao_required(struct sock *sk, const void *saddr, + int family) +{ +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao_info; + struct tcp_ao_key *ao_key; + + ao_info = rcu_dereference_check(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (!ao_info) + return false; + + ao_key = tcp_ao_do_lookup(sk, saddr, family, -1, -1, 0); + if (ao_info->ao_required || ao_key) + return true; +#endif + return false; +} + #endif /* _TCP_H */ diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 1739116c6902..1c24b586c876 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -93,12 +93,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 2b2696e24dcc..c2be72beab70 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -171,6 +171,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 bool tcp_ao_can_set_current_rnext(struct sock *sk) { /* There aren't current/rnext keys on TCP_LISTEN sockets */ @@ -521,6 +529,10 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, 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 = setsockopt_ao_info(sk); if (IS_ERR(ao_info)) return PTR_ERR(ao_info); @@ -720,6 +732,31 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, return -ENOENT; } +/* cmd.ao_required makes a socket TCP-AO only. + * Don't allow any md5 keys for any l3intf on the socket together with it. + * Restricting it early in setsockopt() removes a check for + * ao_info->ao_required on inbound tcp segment fast-path. + */ +static inline int tcp_ao_required_verify(struct sock *sk) +{ +#ifdef CONFIG_TCP_MD5SIG + const struct tcp_md5sig_info *md5sig; + + if (!static_branch_unlikely(&tcp_md5_needed.key)) + return 0; + + md5sig = rcu_dereference_check(tcp_sk(sk)->md5sig_info, + lockdep_sock_is_held(sk)); + if (!md5sig) + return 0; + + if (rcu_dereference_check(hlist_first_rcu(&md5sig->head), + lockdep_sock_is_held(sk))) + return 1; +#endif + return 0; +} + static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, sockptr_t optval, int optlen) { @@ -754,6 +791,9 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, first = true; } + if (cmd.ao_required && tcp_ao_required_verify(sk)) + return -EKEYREJECTED; + /* For sockets in TCP_CLOSED it's possible set keys that aren't * matching the future peer (address/port/VRF/etc), * tcp_ao_connect_init() will choose a correct matching MKT diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 8804eb801caf..65e9bcac45f9 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1081,7 +1081,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; @@ -1100,7 +1100,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); @@ -1312,7 +1313,7 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family, } EXPORT_SYMBOL(tcp_md5_do_del); -static void tcp_clear_md5_list(struct sock *sk) +void tcp_clear_md5_list(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); struct tcp_md5sig_key *key; @@ -1382,6 +1383,12 @@ 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. + * See the comment in tcp_ao_add_cmd() + */ + if (tcp_ao_required(sk, addr, AF_INET)) + return -EKEYREJECTED; + return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, cmd.tcpm_key, cmd.tcpm_keylen); } @@ -2277,6 +2284,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 51d8638d4b4c..e98083abd7fb 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3928,6 +3928,53 @@ 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. + * Return error if the peer has both a md5 and a tcp-ao key + * configured as this is ambiguous. + */ + 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); + struct tcp_ao_info *ao_info; + + ao_info = rcu_dereference_check(tp->ao_info, + lockdep_sock_is_held(sk)); + if (ao_info) { + /* This is an extra check: tcp_ao_required() in + * tcp_v{4,6}_parse_md5_keys() should prevent adding + * md5 keys on ao_required socket. + */ + needs_ao |= ao_info->ao_required; + WARN_ON_ONCE(ao_info->ao_required && needs_md5); + } + if (needs_md5 && needs_ao) + return -EKEYREJECTED; + + /* If we have a matching md5 key and no matching tcp-ao key + * then free up ao_info if allocated. + */ + if (needs_md5) { + tcp_ao_destroy_sock(sk); + } else if (needs_ao) { + tcp_clear_md5_list(sk); + kfree(rcu_replace_pointer(tp->md5sig_info, NULL, + lockdep_sock_is_held(sk))); + } + } +#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)) + 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 049ddbabe049..a56ab0a9cf55 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -12,6 +12,23 @@ #include #include +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 96ec9c03d9eb..5c7c9dbfdb73 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -599,6 +599,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; @@ -653,13 +654,28 @@ 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. + * See the comment in tcp_ao_add_cmd() + */ + if (tcp_ao_required(sk, addr, AF_INET)) + return -EKEYREJECTED; + return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, cmd.tcpm_key, cmd.tcpm_keylen); + } - return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr, - AF_INET6, prefixlen, l3index, flags, + addr = (union tcp_md5_addr *)&sin6->sin6_addr; + + /* Don't allow keys for peers that have a matching TCP-AO key. + * See the comment in tcp_ao_add_cmd() + */ + if (tcp_ao_required(sk, addr, AF_INET6)) + return -EKEYREJECTED; + + return tcp_md5_do_add(sk, addr, AF_INET6, prefixlen, l3index, flags, cmd.tcpm_key, cmd.tcpm_keylen); } @@ -1899,6 +1915,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 }; @@ -1930,6 +1947,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 }; From patchwork Fri Jul 21 16:18:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 124014 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp347476vqg; Fri, 21 Jul 2023 10:09:34 -0700 (PDT) X-Google-Smtp-Source: APBJJlHZdP4ZoWaZZeXm42kwUeGxR4gQh643r35gAnqvTPwuyHzJjNbskEWw/UedXM9gWdJj7a0g X-Received: by 2002:a17:902:82c1:b0:1bb:7b0a:374 with SMTP id u1-20020a17090282c100b001bb7b0a0374mr254762plz.4.1689959374593; Fri, 21 Jul 2023 10:09:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689959374; cv=none; d=google.com; s=arc-20160816; b=p228u73c00KHgPI4NPf48/HUWQPN7NmP6P0MyaA4ydBT1Aa/Vd8F1FvyZwCC/bZqcB KoIGnsBJWjc6pmQYL8TmzyHzSL3/GF6n9KPQXW6rOcukDHs5nisvO7q+1t2m1/33Z09F PqMwMKfnIQGELagt6QE4mx4Urn4jx/CDxfODUdosZ23HV8piD9MFw/5v/nye4mVP7vAS +5dbz1zSV59o2JFn0cUF8wuHriiRJ1K3mntD3inb27UJ8UI89dfMWREsneoshZKqxsip 49bVhHtnZ0Bg2efBCkNk/0leBhoR2SMuKk3UAkd5T5sdMZnz4mHouoyuPsPYSQGuXALL TXUw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=oX7/wo/9PItlUtXRJtdSz/eNYWFCSxnenRaHJnSbXm0=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=JKqCyIxh6MXLIyHxB/kXoVkuokSwvL0OLMZV2O9jfsfRgQHvt4EzbejCi2/tK3TqGD cI7kMzxuCRSX44imvD8ckgsVCD8w2HElLmP0pkMVgX5baWtf1TtEUz+Fd51FFmoOSAYr 2KVg/9+wlJrv6D8nuxyEbb3mvTRqswyTtlSWP3YmHF8kUdU4NBA9+qlq/CUZ+b+0Gqos RdCB1X27WzFPU7ep3Z9aEGJjdxpI4/ouFKIhukeKnp3FHGR1pKXgKV1PVJl+cqntCHUx HurlRI8Tx3Ba1jA9RfYtXNCznlI/lEkshPBmdIKhonBNJnKB+PMACBN+ruTGzJblClFt xOIQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=Hy7OtH1f; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id h17-20020a170902f2d100b001b8a69660c1si3128368plc.88.2023.07.21.10.09.20; Fri, 21 Jul 2023 10:09:34 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=Hy7OtH1f; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231713AbjGUQWU (ORCPT + 99 others); Fri, 21 Jul 2023 12:22:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51800 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232661AbjGUQVk (ORCPT ); Fri, 21 Jul 2023 12:21:40 -0400 Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com [IPv6:2a00:1450:4864:20::32c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0C24F49D4 for ; Fri, 21 Jul 2023 09:19:35 -0700 (PDT) Received: by mail-wm1-x32c.google.com with SMTP id 5b1f17b1804b1-3fbc59de0e2so18100505e9.3 for ; Fri, 21 Jul 2023 09:19:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956373; x=1690561173; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=oX7/wo/9PItlUtXRJtdSz/eNYWFCSxnenRaHJnSbXm0=; b=Hy7OtH1fZiOdO2cPtB3mzufTdwODmzLF5DPF1VSk0ufLVomRJ7kypjr4c724Yy5ipZ ebTqkZPxXM8sSxhg8jmaZ76ocTErkOd3bKRr5Aeyh1O/kGfGVAMH/iOY/BRhgCAnnuXC xm9hMVEWPHMGyeZg7EspQupNX/MsZhh4YE9+Mrw5tAdHtu/r8rMNXGBHmDvk/8DcmCQ5 mCpQsf+1+9KLmPGDtxld3tGn/XkTpoUwUVVHxYBIHr8cT4qaLSbEz8QMYoGdUoyyDUVv FVIwIfTwNlkP9IgdAWFUwpvMwayw54r0Hf9zLk79TiCgwg6o4YZGnvHHtOWMcjvMp2kg T1xg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956373; x=1690561173; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=oX7/wo/9PItlUtXRJtdSz/eNYWFCSxnenRaHJnSbXm0=; b=DPAlKDblX21jyt7E1RyblTXqlmO3CdbzSnqFUG0oT0RgLNAYc/GOWTv5ZYfDGjT0U2 9ulJy0zyB1ImBwVK5bsCp5+57N5ztJ42ZYTj9k7lwKKRGsYDWpjXWuLr1LFVodnT20Se 2Lxp9mkB9fB5vGigzhnN6VRfs9Y2Vt4KiV5A6scdZO5n4IBWyqvj4gmDibMaR+U6UaXQ t9FVEUpbSd3PBHVITOoWdnfLOX/cROsf1d++Ek6qznTxfB6u8wSaGKVBpkLG66EYfLiR mnBVWrwLU2+vrxzdxg+E5KL3LRNkZmSm3kWUAI2mcZCckD/m1wpDeg6CC84FV4Fvvf4f VevQ== X-Gm-Message-State: ABy/qLac88hYXdLzhcefAjGfkkw7GF9cWV5LlOmwxwKYQG4/FWqPDDtj 0G6vzP7dvYmeyk/ew8hokjRHbw== X-Received: by 2002:a7b:c01a:0:b0:3f9:c986:a2ca with SMTP id c26-20020a7bc01a000000b003f9c986a2camr1833705wmb.24.1689956373304; Fri, 21 Jul 2023 09:19:33 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:32 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 05/23] net/tcp: Calculate TCP-AO traffic keys Date: Fri, 21 Jul 2023 17:18:56 +0100 Message-ID: <20230721161916.542667-6-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050840962558885 X-GMAIL-MSGID: 1772050840962558885 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/tcp.h | 5 ++ include/net/tcp_ao.h | 43 +++++++++- net/ipv4/tcp_ao.c | 191 ++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_input.c | 1 + net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 1 + net/ipv6/tcp_ao.c | 50 +++++++++++ net/ipv6/tcp_ipv6.c | 1 + 8 files changed, 292 insertions(+), 1 deletion(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 7e414873ef07..6a2e15a94214 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2147,6 +2147,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 1c24b586c876..1aac82c1d22d 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -90,8 +90,31 @@ 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; +}; +struct tcp_sigpool; + 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, struct tcp_sigpool *hp); 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, @@ -100,13 +123,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) @@ -117,6 +150,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 c2be72beab70..a5c70cbaa954 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -16,6 +16,34 @@ #include #include +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; + + 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); + + ret = crypto_ahash_final(hp->req); + if (ret) + goto clear_hash; + + return 0; +clear_hash: + memset(key, 0, tcp_ao_digest_size(mkt)); + return 1; +} + /* Optimized version of tcp_ao_do_lookup(): only for sockets for which * it's known that the keys in ao_info are matching peer's * family/address/port/VRF/etc. @@ -171,6 +199,71 @@ 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; + 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; +} + +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); +} + +static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, + __be32 sisn, __be32 disn, bool send) +{ + if (mkt->family == AF_INET) + return tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); +#if IS_ENABLED(CONFIG_IPV6) + else if (mkt->family == AF_INET6) + return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); +#endif + else + return -EOPNOTSUPP; +} + struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid) { @@ -179,6 +272,98 @@ 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 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_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); + ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk, + ao->lisn, ao->risn, false); + return ret; +} + +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; +#if IS_ENABLED(CONFIG_IPV6) + else if (family == AF_INET6) + addr = (union tcp_ao_addr *)&sk->sk_v6_daddr; +#endif + else + return; + + 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); + tcp_sigpool_release(key->tcp_sigpool_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); + } else { + /* TODO: probably, it should fail to connect() here */ + rcu_assign_pointer(tp->ao_info, NULL); + kfree(ao_info); + } +} + +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; + + hlist_for_each_entry_rcu(key, &ao->head, node) + tcp_ao_cache_traffic_keys(sk, ao, key); +} + static bool tcp_ao_can_set_current_rnext(struct sock *sk) { /* There aren't current/rnext keys on TCP_LISTEN sockets */ @@ -576,6 +761,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 670c3dab24f2..7780ceef3415 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6082,6 +6082,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 65e9bcac45f9..447e0c6f132f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2286,6 +2286,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 e98083abd7fb..937516aac99a 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3745,6 +3745,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 a56ab0a9cf55..0b490738c366 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -12,6 +12,56 @@ #include #include +static 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; + 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; +} + +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 5c7c9dbfdb73..1e67aaf42460 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1917,6 +1917,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 From patchwork Fri Jul 21 16:18:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 124025 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp357239vqg; Fri, 21 Jul 2023 10:25:30 -0700 (PDT) X-Google-Smtp-Source: APBJJlHoWTsMxgBTGdsuYnNxO72wc8DlGxFc096ubaPGO7OQ9JjgWuVzY08Sz3u2bhwseYdtj9rI X-Received: by 2002:a05:6808:f91:b0:39b:a87f:91e8 with SMTP id o17-20020a0568080f9100b0039ba87f91e8mr3333035oiw.45.1689960329986; Fri, 21 Jul 2023 10:25:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689960329; cv=none; d=google.com; s=arc-20160816; b=wzieVfb9rFj1Yjs8b+vaiD/+B1uIWvJlGxXTLru/qjXu66tGDQJUYprXjyYg8arFcu gcFrIqJk0xcbt/0TuKszP2en0we3azl5gSa7nNKpq/GcO5HkP/B8/xFhF9ozXed5X8Sm xWNtzyIKFzB4XqgPIXsq9FKFDW2J50SPD36AQ98fg62Y63fXXOLJlUTOD79WDh1eaLYa FCtMDyAEmbo0Wni3Y1J09r36yfTZ+BatMlPO7UWniMoLeRDSyakTVzg9OlN2CYYm0wNX mCwQfxJCR5Z1hhvxjsNXJyYS7033YFYue84fdoQy1DQve49Bkns5/E31VOadoP72Pwp+ RYyQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=AW1LtevE0xFQOigJmDK7EGJlOVAbensZWk3W9gNoWaY=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=KaQFcRN3q9lwti/pixz9kZG4bwbpvHIdm+hwD9+jS3s+lcgO/FLxAyHwJ+xfVnwZc3 /rrYu/fhGn3dA1wmmcok1MjI7TmjFhYaJ9NKFbrqSIHccIE/NVmJptNiK/poGjiIgmxR 5IXYsKemBJxPNzMezYc5+iWltLODt88fdhlUxxUGw2ZhYJWh8wJlw9nUefpeKNNnoy8b VlQCIhIdnFuZmKC9uNNe+oLqL0jZBlV/QyphPa5UMf2rarfHdEks+lJVQqJw1T5xYEq/ lduHELzWpYm7YROt6XIa9uFT6eiypKAkXWlLYCu7gaxG+PIu1fHoIADtYG7H2XMZXlBv GDPQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=YbEc94E7; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id d6-20020a17090ac24600b002631bfbd8d0si3485868pjx.129.2023.07.21.10.25.16; Fri, 21 Jul 2023 10:25:29 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=YbEc94E7; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232735AbjGUQWX (ORCPT + 99 others); Fri, 21 Jul 2023 12:22:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51810 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232666AbjGUQVl (ORCPT ); Fri, 21 Jul 2023 12:21:41 -0400 Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com [IPv6:2a00:1450:4864:20::32c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B9B1349DA for ; Fri, 21 Jul 2023 09:19:36 -0700 (PDT) Received: by mail-wm1-x32c.google.com with SMTP id 5b1f17b1804b1-3fbd33a57b6so19177195e9.2 for ; Fri, 21 Jul 2023 09:19:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956375; x=1690561175; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=AW1LtevE0xFQOigJmDK7EGJlOVAbensZWk3W9gNoWaY=; b=YbEc94E7myn1GCYtTjVKkzZm51Jc2bEeZ1BUVf/BqfgweNxIRGASqxJBh13E77kMLx +UTooya/jJEcmduo57G7LVbGZ8LvxFfMzT752zspyyLlnGP2+fxxk4TfPhuqOsbJ04qV yyvEwXbQpYTjj1CK24t6O+uugZstZE5rn2R7z6P0bU2PwPkqez41XXLPdIcyM4D6tHli jNm00CCDjJJ3KMqQh1ViWcGiA7EBxMtw8GIbeCXHmI2RXR6f9HZytdPDfzXI/LMl5rc/ NvuN0FhPWtrCRwlWDUpvYl8m3NyfuGds+xB/P+d7fBPgcuEMF7BUSVdynpW5+B4Eoe88 FHMA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956375; x=1690561175; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=AW1LtevE0xFQOigJmDK7EGJlOVAbensZWk3W9gNoWaY=; b=loxTwTwGaLYs4Q0aXTj9W/YY2HX/WQZp3oUEb6J++nZFy9np5oJtU5aGsbwDzHytYe 2BliXtMpWKWAEm4Pdc0NCoSNTanDw1Y7ENtBFAySD2lC7sSPoy7+jn821xz9dIb9H/04 KIwDIEMF7FMV0JUpOeIVM+33neTbN6ECKKmsoFxnIFin/NnG6YWcB/Qn/tCRLSgobd4N iPZ2lBbH+HF6DFkF3uMswGlUUiLg3mIEbxFsz12HuYdMYQGyBpHFzW53+EcwgdiHWYHm ewNMLsQUE0Dls46Dy/MrdWT8Ck7xmXHB+28JdiuEIj6oAvMSGgFXm3cBmlNwFeIrGvSJ O4/w== X-Gm-Message-State: ABy/qLYKLGL2cA5TqVTD+YxqdBL4ZxIVwRX0175+9dmxqTPu6/krXdXC MMsJQM6STE6O+R68G5ArI4kpZw== X-Received: by 2002:a1c:ed11:0:b0:3fb:b9b7:fb0c with SMTP id l17-20020a1ced11000000b003fbb9b7fb0cmr1944851wmh.31.1689956374999; Fri, 21 Jul 2023 09:19:34 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:34 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 06/23] net/tcp: Add TCP-AO sign to outgoing packets Date: Fri, 21 Jul 2023 17:18:57 +0100 Message-ID: <20230721161916.542667-7-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772051843112696085 X-GMAIL-MSGID: 1772051843112696085 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/tcp.h | 7 ++ include/net/tcp_ao.h | 13 ++++ net/ipv4/tcp_ao.c | 165 ++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 122 ++++++++++++++++++++++++++++--- net/ipv6/tcp_ao.c | 28 +++++++ net/ipv6/tcp_ipv6.c | 2 + 7 files changed, 328 insertions(+), 10 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 6a2e15a94214..d4344cc39893 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -186,6 +186,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 */ @@ -2147,6 +2148,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 1aac82c1d22d..8d61f2690d52 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -111,6 +111,10 @@ struct tcp6_ao_context { }; struct tcp_sigpool; +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, @@ -126,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 tcp_sigpool *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 a5c70cbaa954..3ff7b1efacfa 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -264,6 +264,171 @@ static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, return -EOPNOTSUPP; } +static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp, + __be32 daddr, __be32 saddr, + int nbytes) +{ + struct tcp4_pseudohdr *bp; + struct scatterlist sg; + + 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); +} + +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) +{ + 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); +#if IS_ENABLED(CONFIG_IPV6) + else if (family == AF_INET6) + return tcp_v6_ao_hash_pseudoheader(hp, &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, + 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, + &iph->saddr, skb->len); +#endif + } + return -EAFNOSUPPORT; +} + +/* 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) +{ + struct scatterlist sg; + __be32 *bp; + + bp = (__be32 *)hp->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 tcp_sigpool *hp, + 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 = 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; +} + +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; + + 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_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)) + goto clear_hash; + + 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, + 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 447e0c6f132f..8222f14e4c32 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2285,6 +2285,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 937516aac99a..813d865095ac 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -415,6 +415,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) { @@ -607,7 +608,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 */ @@ -619,7 +621,33 @@ 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) && tp) { + struct tcp_ao_key *rnext_key; + struct tcp_ao_info *ao_info; + u8 maclen; + if (WARN_ON_ONCE(!ao_key)) + goto out_ao; + ao_info = rcu_dereference_check(tp->ao_info, + lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk)); + rnext_key = READ_ONCE(ao_info->rnext_key); + if (WARN_ON_ONCE(!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) | + (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) | @@ -760,7 +788,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; @@ -777,6 +806,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 @@ -844,6 +879,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) @@ -865,6 +901,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; @@ -914,7 +958,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; @@ -933,6 +978,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; @@ -1244,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; @@ -1275,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 = READ_ONCE(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, @@ -1353,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 */ @@ -1363,6 +1424,34 @@ 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; + void *tkey_buf = NULL; + 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; + + tkey_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); + if (!tkey_buf) + return -ENOMEM; + traffic_key = tkey_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); + } + tp->af_specific->calc_ao_hash(opts.hash_location, ao_key, sk, skb, + traffic_key, + opts.hash_location - (u8 *)th, 0); + kfree(tkey_buf); + } +#endif /* BPF prog is the last one writing header option */ bpf_skops_write_hdr_opt(sk, skb, NULL, NULL, 0, &opts); @@ -1814,6 +1903,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; @@ -1822,8 +1915,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 = READ_ONCE(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 @@ -3664,7 +3766,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); @@ -3685,7 +3787,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 0b490738c366..54c3714a05b4 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -7,6 +7,7 @@ * Francesco Ruggeri * Salam Noureddine */ +#include #include #include @@ -79,6 +80,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 tcp_sigpool *hp, + 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); +} + +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 1e67aaf42460..1dd7c8f3aff4 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1916,6 +1916,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 @@ -1949,6 +1950,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_v4_ao_hash_skb, .ao_parse = tcp_v6_parse_ao, #endif }; From patchwork Fri Jul 21 16:18:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 124003 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp345268vqg; Fri, 21 Jul 2023 10:06:25 -0700 (PDT) X-Google-Smtp-Source: APBJJlHrbn3HT+gxjk13VlSA5B4m74R0PQQsSXaJ71ODbLaoc5SDNEzcsxCxUbIhpSMT9tuWYioW X-Received: by 2002:a17:902:a3cb:b0:1a9:40d5:b0ae with SMTP id q11-20020a170902a3cb00b001a940d5b0aemr1718079plb.12.1689959184977; Fri, 21 Jul 2023 10:06:24 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689959184; cv=none; d=google.com; s=arc-20160816; b=sWdrvaIVIvZmU1hBiQL1+iJNBsVMv6p7WX2xyvztG/P2o5tM2QifN4Ge2JsDhjh3uj 1/DUX6+0LwVqeGy12v9F+pHgFchTeFiYJWOB9V5oHPCSz9xxUhH6TWCR0qgUUhBxOzJJ aEI0UVmalB1IXcaWpFEM1PwHPmxBdnn85pLmCYCLXwxdcbGmCwABVbHhJw0UwEP7yWcP tGDCpFJ9GNh3+8jfZ7zg2/NO8rYt7Q5jMvGqa9fyl/T3DHbDlSmcwg3pRtE1SUUchBTe 69H03YSEwmdQHJn7kSHiStWjTPk+l90YCR4p3Yw1OUeTdnRMqX64VHb2N8Qbs5bAuLkD 2KTw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=8pdvJndqIykx+0FJxNSmcRlWy12cOp5LRT2bi6J8u9I=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=JWh53IcrrMHE79tE4G//DXPrwUs7jU3r3CkQ1Aou0eTjFkgZgHTQB//8tFCkHaHxgd 6hwpsZxmhBSORIwgG5ahkFjJ0X5ipeG3xyRztmeWw1YOV5rFoZczOVVn/lMeDHAyT6rp zPDWMQtgZGPmWVmCbBTWVcZB/0SczGM8sGs+wABBDOeXlwb00t2g24G2rLyQ0ROY/7EO 0v5nCXcd6sWekGSzLrxqlmXQqohrEhotmGcRbKaMFsug9DPjOibMIDtNvtzpCpXq60Tv 2cuwWh+tmYXIO7VuKbfdOWwWfVN+s2vmDpnNCS2kXjOBjUfxiJ3ysRRvff8GaRvyLB54 oUMQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=BUyXERDP; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id q8-20020a170902a3c800b001b9d2e6ab54si3180035plb.501.2023.07.21.10.06.10; Fri, 21 Jul 2023 10:06:24 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=BUyXERDP; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232743AbjGUQW2 (ORCPT + 99 others); Fri, 21 Jul 2023 12:22:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51842 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230506AbjGUQVm (ORCPT ); Fri, 21 Jul 2023 12:21:42 -0400 Received: from mail-lj1-x231.google.com (mail-lj1-x231.google.com [IPv6:2a00:1450:4864:20::231]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AEA252D51 for ; Fri, 21 Jul 2023 09:19:38 -0700 (PDT) Received: by mail-lj1-x231.google.com with SMTP id 38308e7fff4ca-2b700e85950so30304671fa.3 for ; Fri, 21 Jul 2023 09:19:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956377; x=1690561177; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=8pdvJndqIykx+0FJxNSmcRlWy12cOp5LRT2bi6J8u9I=; b=BUyXERDPbZvXBZSPfVFgd8d2dYKYmneMIIzUOT8f2rR+5L56s5fgYRX79rHARrjR7X DrczbbZ2mC2U/kCBcntXeAH+CyA4AaH8fLCAl9ni6b9NmPyfP2fAzmV0N+I0IrxL1/04 zJtetgnry7CGDmOQmOIBiE4Wh/nUYNa9M4c3GU9ZG3HX0JPPoysUfP19SNxPOX7UqXsw vpmHO0lya6todpjRBctYuBf/vxqnvlvdk3kMdBAKFuhwlR5hk/Iderb1mg+KpV64wreN lurkwkKQ5u4o1eO+DNJo9T0yPCAsTBI9UvJ0yA0jLmvGzVWYL5SZf4JZ0GyV29Bp+4c4 GKnw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956377; x=1690561177; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8pdvJndqIykx+0FJxNSmcRlWy12cOp5LRT2bi6J8u9I=; b=TTftMSO//wI+rZL/wUL8B2J8pABtP8xM3/qax2uRp/2AhIaq1XcdZXVAKqgdgKpWRW LZz3YEmchYFxJH7zeUX5+oxyWEZA96MpH9igo2Xaabg5shWLEYzQGQ8lftfcg5B/gpCX 4wrnQKMUiBRgLEU44AYJciI830oWGGZFZLmnAYDpXucES6guhIIuoZY9QBV0hRIp7uTf Hio5TdB0sieggMdPLYVO8+AJWdsl+cr1hP1GYSiuQ8cFn1oZgCfXLzoY2SJtBF5wy/1L ROx+vWvZmlOK9fezg5Lx0Dnh4AUbQPWC0Ui9mmBNeg5dO3NluHp/zOKV0iRSWCjvrNbJ 8A2g== X-Gm-Message-State: ABy/qLZ3hkgTJjsHmLbLcKo75rcz7HiB25kXau1S/NdN0DlCsQZmL9za w09Aqdu73+EXibVoZj/V72KIWA== X-Received: by 2002:a2e:990d:0:b0:2b6:9dd5:7a5 with SMTP id v13-20020a2e990d000000b002b69dd507a5mr1862232lji.12.1689956376763; Fri, 21 Jul 2023 09:19:36 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:36 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 07/23] net/tcp: Add tcp_parse_auth_options() Date: Fri, 21 Jul 2023 17:18:58 +0100 Message-ID: <20230721161916.542667-8-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050642741201996 X-GMAIL-MSGID: 1772050642741201996 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/dropreason-core.h | 6 ++++++ include/net/tcp.h | 24 ++++++++++++++++++++- include/net/tcp_ao.h | 17 ++++++++++++++- net/ipv4/tcp.c | 3 ++- net/ipv4/tcp_input.c | 39 ++++++++++++++++++++++++++--------- net/ipv4/tcp_ipv4.c | 15 +++++++++----- net/ipv6/tcp_ipv6.c | 11 ++++++---- 7 files changed, 93 insertions(+), 22 deletions(-) diff --git a/include/net/dropreason-core.h b/include/net/dropreason-core.h index f291a3b0f9e5..05255bffa2c8 100644 --- a/include/net/dropreason-core.h +++ b/include/net/dropreason-core.h @@ -20,6 +20,7 @@ FN(IP_NOPROTO) \ FN(SOCKET_RCVBUFF) \ FN(PROTO_MEM) \ + FN(TCP_AUTH_HDR) \ FN(TCP_MD5NOTFOUND) \ FN(TCP_MD5UNEXPECTED) \ FN(TCP_MD5FAILURE) \ @@ -140,6 +141,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 diff --git a/include/net/tcp.h b/include/net/tcp.h index d4344cc39893..17991a6848ff 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 @@ -2571,6 +2570,29 @@ 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; +} + static inline bool tcp_ao_required(struct sock *sk, const void *saddr, int family) { diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 8d61f2690d52..e97dfdc11451 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -150,7 +150,9 @@ 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); void tcp_ao_connect_init(struct sock *sk); - +void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, + struct tcp_request_sock *treq, + unsigned short int family); #else /* CONFIG_TCP_AO */ static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, @@ -173,4 +175,17 @@ static inline void tcp_ao_connect_init(struct sock *sk) } #endif +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) +int tcp_do_parse_auth_options(const struct tcphdr *th, + const u8 **md5_hash, const u8 **ao_hash); +#else +static int tcp_do_parse_auth_options(const struct tcphdr *th, + const u8 **md5_hash, const u8 **ao_hash) +{ + *md5_hash = NULL; + *ao_hash = NULL; + return 0; +} +#endif + #endif /* _TCP_AO_H */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index d0b0565fa11e..c90b3c6df620 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4395,7 +4395,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 SKB_DROP_REASON_TCP_AUTH_HDR; /* 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 7780ceef3415..d9241385468a 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4212,39 +4212,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 8222f14e4c32..0e9c6cfd4ab2 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -670,7 +670,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 defined(CONFIG_TCP_MD5SIG) #define OPTION_BYTES TCPOLEN_MD5SIG_ALIGNED #else #define OPTION_BYTES sizeof(__be32) @@ -686,7 +688,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; @@ -727,8 +729,11 @@ 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); #ifdef CONFIG_TCP_MD5SIG + /* 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); if (sk && sk_fullsock(sk)) { const union tcp_md5_addr *addr; int l3index; @@ -739,7 +744,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); @@ -771,7 +776,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; } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 1dd7c8f3aff4..4c645160362e 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -989,7 +989,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; @@ -1011,8 +1011,11 @@ 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); #ifdef CONFIG_TCP_MD5SIG + /* 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); if (sk && sk_fullsock(sk)) { int l3index; @@ -1021,7 +1024,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; @@ -1050,7 +1053,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 From patchwork Fri Jul 21 16:18:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123998 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp343579vqg; Fri, 21 Jul 2023 10:04:20 -0700 (PDT) X-Google-Smtp-Source: APBJJlHXHFvyktArKeaduL//9EfE6zu9C+Jcuu0v7i2H/pGqpGgG/KOdjqAtoQqldz0wI3yUgEsZ X-Received: by 2002:a05:6358:93a2:b0:135:5af1:438a with SMTP id h34-20020a05635893a200b001355af1438amr667674rwb.2.1689959059884; Fri, 21 Jul 2023 10:04:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689959059; cv=none; d=google.com; s=arc-20160816; b=tz2T9PnQ5cdJQfAlZ75zTkRqbKq+fJ8yeZdgKeatb44lEWrsPWhxHBGAKIG2m+EUFj YFVbJBlOUWTWMGeLJ6LzHEa1xBFtkfRtzuxwh/b8FOHr7a3WCrzzg+BZxcHPK8+rluV2 nHk3PjMJXwDlr+sBcU5sXnFArh3bOmiDI4EOfRkoZi0lt6gdMcS8UiKW52uWP/kLtGqa XUtv/Zmj2M9pw4jY5MP0DAkwNSu0a01H5s9i4IO4BEAELWpWC+4VTFwJrHIpACma+Eq8 GVlUGJqDiidfxn2NwVGjpaacseM03bpYHaXH94Vy946+R3aLUAAxvpj4mfWHRUClc0O3 gl3A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=1bKyGWhVLoLKYGUitfJEAEly4sQyP8YjvyO8xyoNIxg=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=dgv7p8+wkkzwcnRW/vZJMjULmTTCqvnovQ78X7Ms4A+bBwQUZPGrlgHtFLnSZYa3uP ryCXWi6NX5shqujEgKoR1bphq52sLKdFOJXCMDMTp5tCyPZO1DHDaencnGmxpkWxn+Nv 4lGZKFrbcoa8ZnacAihvltKPb2c7YqJ4+V1DyFzkXZC+LrBjLPniH2qRmLLuVNLLNibm 8M9CAGlldktHbtXxAxZFgtPtJ46mR1Oehrt3vSTr+E8pZSDmcxiSbJC4i42cli7fNizO a2u3aoi9qJPnkU3VG+KzX0k9Hykpq0ATFNdQNyoLwnAEE/BUkJtu0lpWyro6474972Z9 Jf/g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=UpujDmHN; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id w186-20020a6382c3000000b0053f0cdab81esi3309680pgd.357.2023.07.21.10.03.50; Fri, 21 Jul 2023 10:04:19 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=UpujDmHN; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232842AbjGUQWb (ORCPT + 99 others); Fri, 21 Jul 2023 12:22:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51882 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232630AbjGUQVn (ORCPT ); Fri, 21 Jul 2023 12:21:43 -0400 Received: from mail-lf1-x12f.google.com (mail-lf1-x12f.google.com [IPv6:2a00:1450:4864:20::12f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4F59130CB for ; Fri, 21 Jul 2023 09:19:40 -0700 (PDT) Received: by mail-lf1-x12f.google.com with SMTP id 2adb3069b0e04-4fb960b7c9dso3456236e87.0 for ; Fri, 21 Jul 2023 09:19:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956378; x=1690561178; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=1bKyGWhVLoLKYGUitfJEAEly4sQyP8YjvyO8xyoNIxg=; b=UpujDmHNZdUVIn523uBP9V/rGRrs3IzO3h+GsrSL1t21NnDClrmZ4mzSYrFEyBxa1E Kj9cYbk2rBpWNtl3nNDBwI8FgFL0UIq+bq/FGBKb3tasBX3wa4qQpyWV3aYNxd/fv5Zz FinLIA7iWOZgeiQ37UHk0oloKZpc4sJgnOwVRnwHdKGEufjNYvLlajoA8fr83bGIjr28 8o76fNdCbQ/Ryb4kpZa31rILc+Las4F6Y4JDjrgmCi/NpxTQTd5fxhYA/rx2wiXa1/u5 30c2IR6bvtO+0yw6bLEhXNBChh35baSIQR/GhSF9AVTUBYCSuY8G7QOjttfWl6G3+2Ly CSOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956378; x=1690561178; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=1bKyGWhVLoLKYGUitfJEAEly4sQyP8YjvyO8xyoNIxg=; b=C/t1G1w83M1o1yHkJdZGHTsuIglc8rw8JfRfUsbMB8YIoADCYtAwFsdd5QM4Ajo2ID IhgqMvPdBoRzXSc8yHRWGh/uPUsloL3Uaa9PpHKZmDlwOnUJydRutz083iOq/R68kRS9 /nnUi2QS/u4Firhiol2X4pfr1tO5NXP2pFIu2ZC1fWFwlRhBcZDTHwBTH+br3nbMM3M1 iuaQW81zWuW8AtCyP6q+r1iEbtt9UaDdOrbO9RCKAoJDODjDRD49+qtC6bAkYcYAsGxa cHsylKBpIP0OBF23SEM/U0IMahBn0qSofIgJjLCfBj0JDCYpiwp0aqP/4GVpVx96PW8L 0Lug== X-Gm-Message-State: ABy/qLYecDMYWutzb5DTYjG+0MorOyHUEeiVxOcPc6HS8Q0qtZYCAo/i I2zF1qqTqxYiB8xbTiLSyjM9wA== X-Received: by 2002:a05:6512:114a:b0:4f8:67aa:4f03 with SMTP id m10-20020a056512114a00b004f867aa4f03mr1926760lfg.1.1689956378425; Fri, 21 Jul 2023 09:19:38 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:38 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 08/23] net/tcp: Add AO sign to RST packets Date: Fri, 21 Jul 2023 17:18:59 +0100 Message-ID: <20230721161916.542667-9-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050511041814160 X-GMAIL-MSGID: 1772050511041814160 Wire up sending resets to TCP-AO hashing. Co-developed-by: Francesco Ruggeri Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/tcp_ao.h | 12 +++++ net/ipv4/tcp_ao.c | 105 ++++++++++++++++++++++++++++++++++++++++++- net/ipv4/tcp_ipv4.c | 69 ++++++++++++++++++++++------ net/ipv6/tcp_ipv6.c | 66 +++++++++++++++++++++------ 4 files changed, 224 insertions(+), 28 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index e97dfdc11451..6e8567b5bbf8 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -117,12 +117,24 @@ 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_established_key(struct tcp_ao_info *ao, + int sndid, int rcvid); 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_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); +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); +int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, + const struct tcp_ao_hdr *aoh, int l3index, u32 seq, + struct tcp_ao_key **key, char **traffic_key, + bool *allocated_traffic_key, u8 *keyid, u32 *sne); + /* 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 3ff7b1efacfa..b8b2e9caa096 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -48,8 +48,8 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, * it's known that the keys in ao_info are matching peer's * family/address/port/VRF/etc. */ -static struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao, - int sndid, int rcvid) +struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao, + int sndid, int rcvid) { struct tcp_ao_key *key; @@ -371,6 +371,66 @@ static int tcp_ao_hash_header(struct tcp_sigpool *hp, return err; } +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; + + 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; +#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; +#endif + } else { + WARN_ON_ONCE(1); + 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, hash_buf, 0); + if (crypto_ahash_final(hp.req)) + goto clear_hash; + + 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_ao_hash_skb(unsigned short int family, char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, @@ -437,6 +497,47 @@ 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_prepare_reset(const struct sock *sk, struct sk_buff *skb, + const struct tcp_ao_hdr *aoh, int l3index, u32 seq, + struct tcp_ao_key **key, char **traffic_key, + bool *allocated_traffic_key, u8 *keyid, u32 *sne) +{ + const struct tcphdr *th = tcp_hdr(skb); + struct tcp_ao_info *ao_info; + + *allocated_traffic_key = false; + /* If there's no socket - than initial sisn/disn are unknown. + * Drop the segment. RFC5925 (7.7) advises to require graceful + * restart [RFC4724]. Alternatively, the RFC5925 advises to + * save/restore traffic keys before/after reboot. + * Linux TCP-AO support provides TCP_AO_ADD_KEY and TCP_AO_REPAIR + * options to restore a socket post-reboot. + */ + if (!sk) + return -ENOTCONN; + + if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { + return -1; + } else { + struct tcp_ao_key *rnext_key; + + if (sk->sk_state == TCP_TIME_WAIT) + return -1; + ao_info = rcu_dereference(tcp_sk(sk)->ao_info); + if (!ao_info) + return -ENOENT; + + *key = tcp_ao_established_key(ao_info, aoh->rnext_keyid, -1); + if (!*key) + return -ENOENT; + *traffic_key = snd_other_key(*key); + rnext_key = READ_ONCE(ao_info->rnext_key); + *keyid = rnext_key->rcvid; + *sne = 0; + } + return 0; +} + static 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 0e9c6cfd4ab2..ee14a2405801 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -657,6 +657,52 @@ void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb) } EXPORT_SYMBOL(tcp_v4_send_check); +#define REPLY_OPTIONS_LEN (MAX_TCP_OPTION_SPACE / sizeof(__be32)) + +static bool tcp_v4_ao_sign_reset(const struct sock *sk, struct sk_buff *skb, + const struct tcp_ao_hdr *aoh, + struct ip_reply_arg *arg, struct tcphdr *reply, + __be32 reply_options[REPLY_OPTIONS_LEN]) +{ +#ifdef CONFIG_TCP_AO + int sdif = tcp_v4_sdif(skb); + int dif = inet_iif(skb); + int l3index = sdif ? dif : 0; + bool allocated_traffic_key; + struct tcp_ao_key *key; + char *traffic_key; + bool drop = true; + u32 ao_sne = 0; + u8 keyid; + + rcu_read_lock(); + if (tcp_ao_prepare_reset(sk, skb, aoh, l3index, reply->seq, + &key, &traffic_key, &allocated_traffic_key, + &keyid, &ao_sne)) + goto out; + + reply_options[0] = htonl((TCPOPT_AO << 24) | (tcp_ao_len(key) << 16) | + (aoh->rnext_keyid << 8) | keyid); + arg->iov[0].iov_len += round_up(tcp_ao_len(key), 4); + reply->doff = arg->iov[0].iov_len / 4; + + if (tcp_ao_hash_hdr(AF_INET, (char *)&reply_options[1], + key, traffic_key, + (union tcp_ao_addr *)&ip_hdr(skb)->saddr, + (union tcp_ao_addr *)&ip_hdr(skb)->daddr, + reply, ao_sne)) + goto out; + drop = false; +out: + rcu_read_unlock(); + if (allocated_traffic_key) + kfree(traffic_key); + return drop; +#else + return true; +#endif +} + /* * This routine will send an RST to the other tcp. * @@ -670,28 +716,21 @@ EXPORT_SYMBOL(tcp_v4_send_check); * Exception: precedence violation. We do not implement it in any case. */ -#ifdef CONFIG_TCP_AO -#define OPTION_BYTES MAX_TCP_OPTION_SPACE -#elif defined(CONFIG_TCP_MD5SIG) -#define OPTION_BYTES TCPOLEN_MD5SIG_ALIGNED -#else -#define OPTION_BYTES sizeof(__be32) -#endif - static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) { const struct tcphdr *th = tcp_hdr(skb); struct { struct tcphdr th; - __be32 opt[OPTION_BYTES / sizeof(__be32)]; + __be32 opt[REPLY_OPTIONS_LEN]; } rep; + const __u8 *md5_hash_location = NULL; + const struct tcp_ao_hdr *aoh; struct ip_reply_arg arg; #ifdef CONFIG_TCP_MD5SIG struct tcp_md5sig_key *key = NULL; - const __u8 *md5_hash_location = NULL; unsigned char newhash[16]; - int genhash; struct sock *sk1 = NULL; + int genhash; #endif u64 transmit_time = 0; struct sock *ctl_sk; @@ -728,11 +767,15 @@ 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 + /* 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; + if (aoh && tcp_v4_ao_sign_reset(sk, skb, aoh, &arg, &rep.th, rep.opt)) + return; + +#ifdef CONFIG_TCP_MD5SIG rcu_read_lock(); if (sk && sk_fullsock(sk)) { const union tcp_md5_addr *addr; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 4c645160362e..bc4ef00621b4 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -854,7 +854,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, u32 txhash) + u8 tclass, __be32 label, u32 priority, u32 txhash, + struct tcp_ao_key *ao_key, char *tkey, + u8 rcv_next, u32 ao_sne) { const struct tcphdr *th = tcp_hdr(skb); struct tcphdr *t1; @@ -873,6 +875,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) { @@ -924,6 +933,17 @@ 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)); + + 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; @@ -988,17 +1008,23 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) struct ipv6hdr *ipv6h = ipv6_hdr(skb); u32 seq = 0, ack_seq = 0; struct tcp_md5sig_key *key = NULL; -#ifdef CONFIG_TCP_MD5SIG + __be32 label = 0; + u32 priority = 0; + struct net *net; + struct tcp_ao_key *ao_key = NULL; + u8 rcv_next = 0; + u32 ao_sne = 0; + u32 txhash = 0; + int oif = 0; const __u8 *md5_hash_location = NULL; + const struct tcp_ao_hdr *aoh; + char *traffic_key = NULL; + bool allocated_traffic_key = false; +#ifdef CONFIG_TCP_MD5SIG unsigned char newhash[16]; int genhash; struct sock *sk1 = NULL; #endif - __be32 label = 0; - u32 priority = 0; - struct net *net; - u32 txhash = 0; - int oif = 0; if (th->rst) return; @@ -1010,12 +1036,11 @@ 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 /* Invalid TCP option size or twice included auth */ - if (tcp_parse_auth_options(th, &md5_hash_location, NULL)) + if (tcp_parse_auth_options(th, &md5_hash_location, &aoh)) return; - rcu_read_lock(); +#ifdef CONFIG_TCP_MD5SIG if (sk && sk_fullsock(sk)) { int l3index; @@ -1064,6 +1089,18 @@ 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 + if (aoh) { + int l3index; + + l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0; + if (tcp_ao_prepare_reset(sk, skb, aoh, l3index, seq, &ao_key, + &traffic_key, &allocated_traffic_key, + &rcv_next, &ao_sne)) + goto out; + } +#endif + if (sk) { oif = sk->sk_bound_dev_if; if (sk_fullsock(sk)) { @@ -1086,10 +1123,13 @@ 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, txhash); + ipv6_get_dsfield(ipv6h), label, priority, txhash, + ao_key, traffic_key, rcv_next, ao_sne); -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) out: + if (allocated_traffic_key) + kfree(traffic_key); rcu_read_unlock(); #endif } @@ -1100,7 +1140,7 @@ static void tcp_v6_send_ack(const struct sock *sk, struct sk_buff *skb, u32 seq, __be32 label, u32 priority, u32 txhash) { tcp_v6_send_response(sk, skb, seq, ack, win, tsval, tsecr, oif, key, 0, - tclass, label, priority, txhash); + tclass, label, priority, txhash, NULL, NULL, 0, 0); } static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) From patchwork Fri Jul 21 16:19:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 124028 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp357515vqg; Fri, 21 Jul 2023 10:26:03 -0700 (PDT) X-Google-Smtp-Source: APBJJlEYXLeWuCxA7EI+JQGReV1W1CBTxpCRCLqPQ10FPAvXJDWOrtC7OrHac0ZaDNbWKNGr/zAw X-Received: by 2002:a05:6a21:32a5:b0:134:37bb:89be with SMTP id yt37-20020a056a2132a500b0013437bb89bemr2665082pzb.57.1689960363464; Fri, 21 Jul 2023 10:26:03 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689960363; cv=none; d=google.com; s=arc-20160816; b=z4MMixtSmL3tYszEAtm5tbETylXSPzreZ5QtwCP2GuTjOMGdThw0W0xeLYcshmV3bW 7u/AfMtwkQyhH3itRN2BcwzS/snNdei22OqgT4LrOXiXkn6sVtWFYTfcn0SV6Q5lagjg tUsFk3bxdJndoL3JRusCeFpsoaGhpNEvLN/KC1D47nblnnvQo4K3C34iCKPLOAMhGPSO vEavoep6oIvERI7afK1SNgMrpyphnv4QdeTlgS2NOjimdFzDNw3AiGcTDIp4llO1lq6u sDuhTgox357cHA062Qkkw/GxqKkMKwdjIGg5tYPDfZ/zOKz8ApQVDJSWOHvNGMoz65QU 3nSw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=G+TD6h88VWrjTUczwakO6RmTXNk7T18G3BRKLDf5TdI=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=dJVhn/+EM9G9+4yx9Ad+Fg/6aH4VJbI+/VVdbptaV9wzqEW05ZM6gItT3voVBNBE57 YfoQbU5sXIseSC3qPVw+gVg7JyIQ2D7NooTICVixJBDV0uvi7Zx1/ZWBUrZ3KZ+lgDUs fTA/alMCaMbvE7Lk4NBlX3CnyCXEGiX3Pzao93LGFpmfOI76e+xQ+G/lkUcTxdOe2V3V f5tyiZpENfpqCaQkleL/swI/vcea0c5jjRV+BorG5WgMfkZFg/VgPPdswAF7Vekz7TXP wCpYCQJi38CuIYfTFRVY0XXZZy5TKfYm2NNDpcDIdfQRQGzk+S0c5ElHhdyYDsG9E8L2 QlkQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=PnsF0Ecy; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id d11-20020a170903230b00b001b89b77ce99si3830106plh.458.2023.07.21.10.25.50; Fri, 21 Jul 2023 10:26:03 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=PnsF0Ecy; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232852AbjGUQWe (ORCPT + 99 others); Fri, 21 Jul 2023 12:22:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51240 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232730AbjGUQVo (ORCPT ); Fri, 21 Jul 2023 12:21:44 -0400 Received: from mail-lj1-x231.google.com (mail-lj1-x231.google.com [IPv6:2a00:1450:4864:20::231]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D639A30F0 for ; Fri, 21 Jul 2023 09:19:41 -0700 (PDT) Received: by mail-lj1-x231.google.com with SMTP id 38308e7fff4ca-2b701e41cd3so30818811fa.3 for ; Fri, 21 Jul 2023 09:19:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956380; x=1690561180; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=G+TD6h88VWrjTUczwakO6RmTXNk7T18G3BRKLDf5TdI=; b=PnsF0EcyvWcsgkICytoGh8mkymMOlAB3/kbauO+HOQpd8HWeH5g3y/lfpCd+OzrgDb vfJFoCO4zWJEWcyVGc6ywzVvTgCLFk37Oay2IpIGRzMe+zHWPtn7b6/IXNcuBjls85SU Vg4yV0eejEqPkDZGVjUXBHVfPekq9URvyHvbns05BlR9AXCBGP+MYo4m3WayqTlXfWBX 3l3GNSmBp/8axpu4WP2dvqdLAYLBYTwqvvkqhZbX6dm45TxghfIb/trlXYM9yM/aww7p PyspjZMM/iQmjmtZb64g8c9A1KU6VpDCvuP7ZBHFGzxTZqUdKLbXr/NP4okQv6Abnjnl JzSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956380; x=1690561180; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=G+TD6h88VWrjTUczwakO6RmTXNk7T18G3BRKLDf5TdI=; b=QBTI9dYvGdt8z9GU8m0bt9Am4P7kOntBXMhG8wKC/9EvpwQRKHnN8/kmNBtHhbc2Hi TutcNZm8urysEJqMOPAuA/vtNGoNzskneG9SPMx0gmUM92extlBsw6efYrtIeWmTZ7Qs ErgOFBADHVd5O2hRY22CUiF6qy6toQuRlgK20LKf/BTzt/E7Fxy3k3fhEol4n9YUMSzn 0i8qyI86Iv6072iwHnkFIBr2tbI3P+OKc6xznJD6LaxU6gwp+kGYiUG13FSP84KbPhcW bjk/P4vz/eyCci3KFBlO/EDXBuOFjlwYDIcaArmi/lN9I6zu2hu7HLGx1f56BgtCiObZ W/ng== X-Gm-Message-State: ABy/qLbLcebEdX/qXP5cpFm7ptYRTV88bcBtamTmBCW/EbNj1QL1jgu0 +Vui8BH89vNOqsW4Ydo2UtepbQ== X-Received: by 2002:a2e:808d:0:b0:2b6:eb8c:af06 with SMTP id i13-20020a2e808d000000b002b6eb8caf06mr1724280ljg.8.1689956380143; Fri, 21 Jul 2023 09:19:40 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:39 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 09/23] net/tcp: Add TCP-AO sign to twsk Date: Fri, 21 Jul 2023 17:19:00 +0100 Message-ID: <20230721161916.542667-10-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772051877709220067 X-GMAIL-MSGID: 1772051877709220067 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/linux/tcp.h | 3 ++ include/net/tcp_ao.h | 11 ++++++-- net/ipv4/tcp_ao.c | 46 +++++++++++++++++++++++++----- net/ipv4/tcp_ipv4.c | 61 ++++++++++++++++++++++++++++++++++++---- net/ipv4/tcp_minisocks.c | 4 ++- net/ipv4/tcp_output.c | 2 +- net/ipv6/tcp_ipv6.c | 41 ++++++++++++++++++++++++--- 7 files changed, 147 insertions(+), 21 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 9eb15e798c32..bbc7db92f37c 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -502,6 +502,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 6e8567b5bbf8..7f6849f16d7a 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -86,6 +86,7 @@ struct tcp_ao_info { __unused :31; __be32 lisn; __be32 risn; + atomic_t refcnt; /* Protects twsk destruction */ struct rcu_head rcu; }; @@ -121,7 +122,8 @@ struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao, int sndid, int rcvid); 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_destroy_sock(struct sock *sk); +void tcp_ao_destroy_sock(struct sock *sk, bool twsk); +void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); 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); @@ -174,7 +176,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) { } @@ -182,6 +184,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 b8b2e9caa096..dc1423239160 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -161,6 +161,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); return ao; } @@ -178,27 +179,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, @@ -522,8 +550,9 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, struct tcp_ao_key *rnext_key; if (sk->sk_state == TCP_TIME_WAIT) - return -1; - ao_info = rcu_dereference(tcp_sk(sk)->ao_info); + ao_info = rcu_dereference(tcp_twsk(sk)->ao_info); + else + ao_info = rcu_dereference(tcp_sk(sk)->ao_info); if (!ao_info) return -ENOENT; @@ -880,6 +909,9 @@ static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) if (sk_fullsock(sk)) { return rcu_dereference_protected(tcp_sk(sk)->ao_info, lockdep_sock_is_held(sk)); + } else if (sk->sk_state == TCP_TIME_WAIT) { + return rcu_dereference_protected(tcp_twsk(sk)->ao_info, + lockdep_sock_is_held(sk)); } return ERR_PTR(-ESOCKTNOSUPPORT); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ee14a2405801..3e5aa89ec3ae 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -912,16 +912,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, u32 txhash) { 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; @@ -966,6 +966,24 @@ static void tcp_v4_send_ack(const struct sock *sk, key, ip_hdr(skb)->saddr, 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 arg.flags = reply_flags; arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, @@ -999,6 +1017,32 @@ 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; + + /* FIXME: the segment to-be-acked is not verified yet */ + ao_info = rcu_dereference(tcptw->ao_info); + if (ao_info) { + const struct tcp_ao_hdr *aoh; + + if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh)) + goto out; /* something is wrong with the sign */ + + if (aoh) + ao_key = tcp_ao_established_key(ao_info, aoh->rnext_keyid, -1); + } + if (ao_key) { + struct tcp_ao_key *rnext_key; + + traffic_key = snd_other_key(ao_key); + rnext_key = READ_ONCE(ao_info->rnext_key); + rcv_next = rnext_key->rcvid; + } +#endif tcp_v4_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, @@ -1007,11 +1051,15 @@ 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, tw->tw_txhash ); +#ifdef CONFIG_TCP_AO +out: +#endif inet_twsk_put(tw); } @@ -1041,6 +1089,7 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, READ_ONCE(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, READ_ONCE(tcp_rsk(req)->txhash)); @@ -2402,7 +2451,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 5616b6a34bee..3033f3187079 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -279,7 +279,7 @@ static void tcp_time_wait_init(struct sock *sk, struct tcp_timewait_sock *tcptw) 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 net *net = sock_net(sk); struct inet_timewait_sock *tw; @@ -316,6 +316,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) #endif tcp_time_wait_init(sk, tcptw); + tcp_ao_time_wait(tcptw, tp); /* Get the TIME_WAIT timeout firing. */ if (timeo < rto) @@ -370,6 +371,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/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 813d865095ac..0d74bb4460a6 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -4059,7 +4059,7 @@ int tcp_connect(struct sock *sk) * then free up ao_info if allocated. */ if (needs_md5) { - tcp_ao_destroy_sock(sk); + tcp_ao_destroy_sock(sk, false); } else if (needs_ao) { tcp_clear_md5_list(sk); kfree(rcu_replace_pointer(tp->md5sig_info, NULL, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index bc4ef00621b4..30db3fcab1ff 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1137,24 +1137,57 @@ 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, u32 txhash) + __be32 label, u32 priority, u32 txhash, + 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, txhash, NULL, NULL, 0, 0); + tclass, label, priority, txhash, + 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; + + /* FIXME: the segment to-be-acked is not verified yet */ + ao_info = rcu_dereference(tcptw->ao_info); + if (ao_info) { + const struct tcp_ao_hdr *aoh; + + /* Invalid TCP option size or twice included auth */ + if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh)) + goto out; + if (aoh) + ao_key = tcp_ao_established_key(ao_info, aoh->rnext_keyid, -1); + } + if (ao_key) { + struct tcp_ao_key *rnext_key; + + traffic_key = snd_other_key(ao_key); + /* rcv_next switches to our rcv_next */ + rnext_key = READ_ONCE(ao_info->rnext_key); + rcv_next = rnext_key->rcvid; + } +#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_txhash); + tw->tw_txhash, ao_key, traffic_key, rcv_next, ao_sne); +#ifdef CONFIG_TCP_AO +out: +#endif inet_twsk_put(tw); } @@ -1181,7 +1214,7 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, READ_ONCE(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, - READ_ONCE(tcp_rsk(req)->txhash)); + READ_ONCE(tcp_rsk(req)->txhash), NULL, NULL, 0, 0); } From patchwork Fri Jul 21 16:19:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123990 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp342780vqg; Fri, 21 Jul 2023 10:03:23 -0700 (PDT) X-Google-Smtp-Source: APBJJlHhDOKRgRtSUY4FfkKNF7lNlFvHtIdPWybnDtV9lQbTLdow3LCRtCrdbaELrAmurXRyR8Xa X-Received: by 2002:a19:4312:0:b0:4f8:7960:f72 with SMTP id q18-20020a194312000000b004f879600f72mr1573751lfa.44.1689959003301; Fri, 21 Jul 2023 10:03:23 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689959003; cv=none; d=google.com; s=arc-20160816; b=OMPzhNC4gW9SCPOj7xK2njF172NabAmcWaXF0xZ+6kNLgokHkcOnz3643e8bN+KLRI 2zmixI8J4qBlP2FUZQ5u1i3mnFaNZYGlQWoVTpShfPecKHy23u/I3GAlqROY0ANRdaUZ Y1r2jtIV6C3RtpFjNsHVrhucQjbIIf0Mo6b2DClV9gOsrgEek2isYyM0IK0D1f1jmQCM ryDBZdd4NVD1nRP7cbN+O0TTEF6HcMzcBWX80apu8Vas/7HwM/wu84ig8J4oePLjuIWN cYDasSBBWGuemIplaclpHSrcxvmP92QVoWiD1uR/PryD9H426jJzS0wRwPxP6BO/VgOp AT6Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=NXSUWIp3q4xNs3RiKBF5o6rWQIuHfqtMf9lvclne1Gs=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=cU+/PGPSpU1An/o+6XahqMqkq9qQFgSpA6lITE89msqQIHNzfnR68km4Ob98H2nqoQ 4C8W6lCazwSQ36ya1qQZfVhEAiKx3wamen+41UEQ/d8enDjYyMF8CGsAsu6HqY+xxX9+ b+gjUSXpbiz9ot3UsP//dKAcdkRw/P/mbWVrcpr+/SgjSiw8JML1P44F1Ms1D1hAEjH7 wnpucB49jTIRAesFcYW1GQAq/wJvH7OnCdyQLu7ImrhJdoMA5ljwnEt1sUiSFOeAqhlw VC+masO7N8b1DEL/aNOq3hQyH5EDqo7BjfAm+5+dnOwtbEU/PbWfFvJ95n8PErRXBtka mZVw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=adUbsK96; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id e6-20020a056402148600b00518744a06a4si2536591edv.83.2023.07.21.10.02.50; Fri, 21 Jul 2023 10:03:23 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=adUbsK96; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232775AbjGUQWt (ORCPT + 99 others); Fri, 21 Jul 2023 12:22:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51346 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232454AbjGUQVr (ORCPT ); Fri, 21 Jul 2023 12:21:47 -0400 Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com [IPv6:2a00:1450:4864:20::329]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E94AD35AB for ; Fri, 21 Jul 2023 09:19:43 -0700 (PDT) Received: by mail-wm1-x329.google.com with SMTP id 5b1f17b1804b1-3fbc244d307so18738715e9.1 for ; Fri, 21 Jul 2023 09:19:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956382; x=1690561182; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=NXSUWIp3q4xNs3RiKBF5o6rWQIuHfqtMf9lvclne1Gs=; b=adUbsK96CVmmkLxhhMU75CNCokIkIbvxJTNeoL2kln5/sbtfyrW09ycRh+dklhUegB xAw45PhcxCSNKlbyE5WMsnmMDSU/LdnN1tcww99z1PAmGUtOYZE7vLa2CECab6Sjfu6l ezKCeT9045d13ZqFRoplJV+1xXc0yk3LHMPNZBf+Yxlk4lOv5drsgD9lra8hH0WgJ0K3 h9gzRnXH2iNy0Y4kzQ3ZCAqnvaoOMUFPjJOLHw4P0JHNf+Sb63n8679i3VUtzhLpm2jY fOPNkLUuGtKXsiDGbqo38Z16x1Moq6tC7F2M42amamw8f9PAL1vVTfRYAs9323BkPOa7 6T7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956382; x=1690561182; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=NXSUWIp3q4xNs3RiKBF5o6rWQIuHfqtMf9lvclne1Gs=; b=AcxRsOnCI7DUtno8nAtSq7wvxiaveaKAehaTX5rfE2Cf+VOosZCSDJWJBvIIQ7ksia o6LbavZG/mcm0CXT3BzvfUKCBRknBu9Z0mjW7dv7xYMuBQ1aOKfZDuBbp2J7y4fwBmIF tk1jHV3kmPXtdcfmoFmzf/nT0iOoS7P3fxnqFVXKPZBvvMDx2iuS3wq5zUJcTdlo1B4U rK27QyqkFeY7+Zdl/V7LvjTkpUyG/w/2tK6F7ccr3Nzb5TplBdLqipk2RoUA2yI1lak2 8b9/CprEUgJbq9ctgDMkthd2yTm9yjjTXlW2eR7sySCkzCsmCvaAT8uLGcZQm04o7z4L Sdlw== X-Gm-Message-State: ABy/qLbIMo+eH3KDmzSq2YWYE2D4Ngxu8P3ZVxuvEOEN3cwZaXr65J9m 0Bq4Gyf1nSiYJoy+Gf92/4/a3w== X-Received: by 2002:a05:600c:210b:b0:3fb:b56b:470f with SMTP id u11-20020a05600c210b00b003fbb56b470fmr1839938wml.14.1689956381954; Fri, 21 Jul 2023 09:19:41 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:41 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 10/23] net/tcp: Wire TCP-AO to request sockets Date: Fri, 21 Jul 2023 17:19:01 +0100 Message-ID: <20230721161916.542667-11-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050451754877028 X-GMAIL-MSGID: 1772050451754877028 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/linux/tcp.h | 18 ++++ include/net/tcp.h | 7 ++ include/net/tcp_ao.h | 22 ++++ net/ipv4/syncookies.c | 2 + net/ipv4/tcp_ao.c | 219 ++++++++++++++++++++++++++++++++++++++- net/ipv4/tcp_input.c | 15 +++ net/ipv4/tcp_ipv4.c | 65 ++++++++++-- net/ipv4/tcp_minisocks.c | 10 ++ net/ipv4/tcp_output.c | 42 +++++--- net/ipv6/syncookies.c | 2 + net/ipv6/tcp_ao.c | 21 ++++ net/ipv6/tcp_ipv6.c | 74 +++++++++++-- 12 files changed, 466 insertions(+), 31 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index bbc7db92f37c..f276e7222760 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 +} + #define TCP_RMEM_TO_WIN_SCALE 8 struct tcp_sock { diff --git a/include/net/tcp.h b/include/net/tcp.h index 17991a6848ff..b8be3aefb9c4 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2171,6 +2171,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 7f6849f16d7a..b63c4f2c36ea 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -120,6 +120,9 @@ int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, sockptr_t optval, int optlen); struct tcp_ao_key *tcp_ao_established_key(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_destroy_sock(struct sock *sk, bool twsk); @@ -144,6 +147,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); @@ -154,9 +162,17 @@ int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *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); @@ -169,6 +185,12 @@ void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, unsigned short int family); #else /* CONFIG_TCP_AO */ +static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, + struct tcp_request_sock *treq, + unsigned short int family) +{ +} + 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/syncookies.c b/net/ipv4/syncookies.c index dc478a0574cb..23fca22bc992 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -394,6 +394,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) treq->snt_synack = 0; treq->tfo_listener = false; + tcp_ao_syncookie(sk, skb, treq, AF_INET); + if (IS_ENABLED(CONFIG_SMC)) ireq->smc_ok = 0; diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index dc1423239160..be9349239139 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -171,6 +171,23 @@ static void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt) hlist_add_head_rcu(&mkt->node, &ao->head); } +static 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); + tcp_sigpool_get(new_key->tcp_sigpool_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); @@ -292,6 +309,18 @@ static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, return -EOPNOTSUPP; } +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 tcp_sigpool *hp, __be32 daddr, __be32 saddr, int nbytes) @@ -517,6 +546,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) { @@ -545,7 +584,46 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, return -ENOTCONN; if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { - return -1; + union tcp_ao_addr *addr; + unsigned int family; + __be32 disn, sisn; + + if (sk->sk_state == TCP_NEW_SYN_RECV) { + struct request_sock *req = inet_reqsk(sk); + + sisn = htonl(tcp_rsk(req)->rcv_isn); + disn = htonl(tcp_rsk(req)->snt_isn); + *sne = 0; + } else { + sisn = th->seq; + disn = 0; + } + if (IS_ENABLED(CONFIG_IPV6) && sk->sk_family == AF_INET6) + addr = (union tcp_md5_addr *)&ipv6_hdr(skb)->saddr; + else + addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr; + family = sk->sk_family; +#if IS_ENABLED(CONFIG_IPV6) + if (family == AF_INET6 && ipv6_addr_v4mapped(&sk->sk_v6_daddr)) + family = AF_INET; +#endif + + sk = sk_const_to_full_sk(sk); + ao_info = rcu_dereference(tcp_sk(sk)->ao_info); + if (!ao_info) + return -ENOENT; + *key = tcp_ao_do_lookup(sk, l3index, addr, family, + -1, aoh->rnext_keyid, ntohs(th->source)); + if (!*key) + return -ENOENT; + *traffic_key = kmalloc(tcp_ao_digest_size(*key), GFP_ATOMIC); + if (!*traffic_key) + return -ENOMEM; + *allocated_traffic_key = true; + if (tcp_ao_calc_key_skb(*key, *traffic_key, skb, + sisn, disn, family)) + return -1; + *keyid = (*key)->rcvid; } else { struct tcp_ao_key *rnext_key; @@ -567,6 +645,50 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, return 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_ao_addr *)&iph->saddr, + AF_INET, sndid, rcvid, 0); + } else { + const struct ipv6hdr *iph = ipv6_hdr(skb); + + return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr, + AF_INET6, sndid, rcvid, 0); + } +} + +void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, + struct tcp_request_sock *treq, + unsigned short int family) +{ + const struct tcphdr *th = tcp_hdr(skb); + const struct tcp_ao_hdr *aoh; + struct tcp_ao_key *key; + + treq->maclen = 0; + + /* Shouldn't fail as this has been called on this packet + * in tcp_inbound_hash() + */ + tcp_parse_auth_options(th, NULL, &aoh); + if (!aoh) + return; + + key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid); + if (!key) + /* Key not found, continue without TCP-AO */ + return; + + treq->ao_rcv_next = aoh->keyid; + treq->ao_keyid = aoh->rnext_keyid; + treq->maclen = tcp_ao_maclen(key); +} + static int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key) @@ -659,6 +781,101 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb) tcp_ao_cache_traffic_keys(sk, ao, key); } +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_key *key, *new_key, *first_key; + struct tcp_ao_info *new_ao, *ao; + struct hlist_node *key_head; + union tcp_ao_addr *addr; + bool match = false; + int ret = -ENOMEM; + + 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); + if (!new_ao) + return -ENOMEM; + new_ao->lisn = htonl(tcp_rsk(req)->snt_isn); + new_ao->risn = htonl(tcp_rsk(req)->rcv_isn); + new_ao->ao_required = ao->ao_required; + + if (family == AF_INET) { + addr = (union tcp_ao_addr *)&newsk->sk_daddr; +#if IS_ENABLED(CONFIG_IPV6) + } else if (family == AF_INET6) { + addr = (union tcp_ao_addr *)&newsk->sk_v6_daddr; +#endif + } else { + ret = -EAFNOSUPPORT; + goto free_ao; + } + + 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) { + /* RFC5925 (7.4.1) specifies that the TCP-AO status + * of a connection is determined on the initial SYN. + * At this point the connection was TCP-AO enabled, so + * it can't switch to being unsigned if peer's key + * disappears on the listening socket. + */ + ret = -EKEYREJECTED; + goto free_and_exit; + } + + key_head = rcu_dereference(hlist_first_rcu(&new_ao->head)); + first_key = hlist_entry_safe(key_head, struct tcp_ao_key, node); + + key = tcp_ao_established_key(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_established_key(new_ao, -1, tcp_rsk(req)->ao_rcv_next); + if (key) + new_ao->rnext_key = key; + else + new_ao->rnext_key = first_key; + + 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, 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(key); + } +free_ao: + kfree(new_ao); + return ret; +} + static bool tcp_ao_can_set_current_rnext(struct sock *sk) { /* There aren't current/rnext keys on TCP_LISTEN sockets */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index d9241385468a..2a7ed329f7cb 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6973,6 +6973,10 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, struct flowi fl; u8 syncookies; +#ifdef CONFIG_TCP_AO + const struct tcp_ao_hdr *aoh; +#endif + syncookies = READ_ONCE(net->ipv4.sysctl_tcp_syncookies); /* TW buckets are converted to open requests without @@ -7058,6 +7062,17 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, inet_rsk(req)->ecn_ok = 0; } +#ifdef CONFIG_TCP_AO + if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh)) + goto drop_and_release; /* Invalid TCP options */ + 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 3e5aa89ec3ae..12b952d0dcf4 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1066,33 +1066,78 @@ 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; + u8 *traffic_key = NULL; +#ifdef CONFIG_TCP_AO + const struct tcp_ao_hdr *aoh; +#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; + } + } + traffic_key = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); + if (!traffic_key) + 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 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, READ_ONCE(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, READ_ONCE(tcp_rsk(req)->txhash)); + kfree(traffic_key); } /* @@ -1630,6 +1675,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 @@ -1731,12 +1780,16 @@ 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)) { if (tcp_md5_key_copy(newsk, addr, AF_INET, 32, l3index, key)) goto put_and_exit; 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 3033f3187079..11b76fa601a7 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -506,6 +506,9 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, const struct tcp_sock *oldtp; struct tcp_sock *newtp; u32 seq; +#ifdef CONFIG_TCP_AO + struct tcp_ao_key *ao_key; +#endif if (!newsk) return NULL; @@ -584,6 +587,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 0d74bb4460a6..27f7aaad5490 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -608,6 +608,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) { @@ -622,23 +623,36 @@ static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp, ptr += 4; } #ifdef CONFIG_TCP_AO - if (unlikely(OPTION_AO & options) && tp) { - struct tcp_ao_key *rnext_key; - struct tcp_ao_info *ao_info; + if (unlikely(OPTION_AO & options)) { u8 maclen; if (WARN_ON_ONCE(!ao_key)) goto out_ao; - ao_info = rcu_dereference_check(tp->ao_info, - lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk)); - rnext_key = READ_ONCE(ao_info->rnext_key); - if (WARN_ON_ONCE(!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) | - (rnext_key->rcvid)); + + if (tp) { + struct tcp_ao_key *rnext_key; + struct tcp_ao_info *ao_info; + + ao_info = rcu_dereference_check(tp->ao_info, + lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk)); + rnext_key = READ_ONCE(ao_info->rnext_key); + if (WARN_ON_ONCE(!rnext_key)) + goto out_ao; + *ptr++ = htonl((TCPOPT_AO << 24) | + (tcp_ao_len(ao_key) << 16) | + (ao_key->sndid << 8) | + (rnext_key->rcvid)); + } else if (tcprsk) { + u8 aolen = maclen + sizeof(struct tcp_ao_hdr); + + *ptr++ = htonl((TCPOPT_AO << 24) | (aolen << 16) | + (tcprsk->ao_keyid << 8) | + (tcprsk->ao_rcv_next)); + } else { + WARN_ON_ONCE(1); + goto out_ao; + } opts->hash_location = (__u8 *)ptr; ptr += maclen / sizeof(*ptr); if (unlikely(maclen % sizeof(*ptr))) { @@ -1414,7 +1428,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 */ @@ -3787,7 +3801,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/syncookies.c b/net/ipv6/syncookies.c index 5014aa663452..ad7a8caa7b2a 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -214,6 +214,8 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) treq->snt_isn = cookie; treq->ts_off = 0; treq->txhash = net_tx_rndhash(); + tcp_ao_syncookie(sk, skb, treq, AF_INET6); + if (IS_ENABLED(CONFIG_SMC)) ireq->smc_ok = 0; diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index 54c3714a05b4..65fa52f8c2e3 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -63,6 +63,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) @@ -80,6 +92,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 tcp_sigpool *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 30db3fcab1ff..6905484077db 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -842,6 +842,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 @@ -1194,9 +1198,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 *traffic_key = NULL; + u8 keyid = 0; 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 + const struct tcp_ao_hdr *aoh; + + /* 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; + } + } + traffic_key = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); + if (!traffic_key) + 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. @@ -1212,9 +1258,10 @@ 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, READ_ONCE(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, READ_ONCE(tcp_rsk(req)->txhash), NULL, NULL, 0, 0); + kfree(traffic_key); } @@ -1444,19 +1491,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) { - const union tcp_md5_addr *addr; + if (!tcp_rsk_used_ao(req)) { + /* Copy over the MD5 key from the original socket */ + key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr, l3index); + if (key) { + const union tcp_md5_addr *addr; - addr = (union tcp_md5_addr *)&newsk->sk_v6_daddr; - if (tcp_md5_key_copy(newsk, addr, AF_INET6, 128, l3index, key)) { - inet_csk_prepare_forced_close(newsk); - tcp_done(newsk); - goto out; + addr = (union tcp_md5_addr *)&newsk->sk_v6_daddr; + if (tcp_md5_key_copy(newsk, addr, AF_INET6, 128, l3index, key)) { + inet_csk_prepare_forced_close(newsk); + tcp_done(newsk); + goto out; + } } } #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); From patchwork Fri Jul 21 16:19:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 124012 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp347184vqg; Fri, 21 Jul 2023 10:09:08 -0700 (PDT) X-Google-Smtp-Source: APBJJlEdq/biTDUENvpmRUDtqDbamfuo4uv4CTllRE64bAjg2En0Meb4dd+bvM9fKAQtWHOFMjkB X-Received: by 2002:a05:6a20:9384:b0:134:589a:c0fc with SMTP id x4-20020a056a20938400b00134589ac0fcmr2775742pzh.32.1689959348240; Fri, 21 Jul 2023 10:09:08 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689959348; cv=none; d=google.com; s=arc-20160816; b=nPGpetWg+fjbfdSC81rnmrEobsved6f21OKoZt9I45NbixbGwQAdXWEtf3AN6WiVNM NyfCpVtsUBoonNFDoPB/UnYVa9mn2WvjQMH8UxNWGdwVvUT/0inWOFLiS2LyUa/iDxCP jEZhQTf4wlYwLwJV9rrsSiRRQ7PxcFiZRY63WGqLyX1QJkY+OtoHsRBLx7NB50PnXygy AgTccMYsJuZ2SE/MS2pWbKoHO54lCEaEmVjDH238xqXP6xIonSvfhGHCsOCLDv+4Uuy4 Xl5XZ69xqdskZFOzoT2hpO56ZYFLM0C64myvtxQ44Pt2OAs4HY92ZnUlTkm8nMj7J538 0yrQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=O2tbdUpm3a3G4jNNOOCBcva1YifyBpuvR71Op0JjGJI=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=LmFMwwqXy90BvYbxpDAtvI2LYaNNnItnAWCDqrKqTA5UOpUwUWMXj7Q2yDQKrqOHQV 7LhbY4B/6Qu2P9BvWGPNpAb2uF8TqsXgDkOyh6s46zS+nb/OJqus82cUt67jNw+vaTw2 jl6Sg8Nce0d69AkaPP79V7TYy61S2+vkSJO765wazDVNN4QjML0SpWgoRzgmreT2rHu/ 12/s8eyU05bSJu9RNxIQZ2rLKBQXgbjKeUVXsJI0ClwZ2/X8FnJ/yk4OYe+nqJNCxFqx yCjrQrPcWY90uhZ273Y8WA1rIGy/n0/P7KW/g9fBOSkVmg40PBs0/b9elhgPGOchJFIO LRpA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=Wxob7LLz; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id l2-20020a170903244200b001b86deba2fbsi3432977pls.605.2023.07.21.10.08.55; Fri, 21 Jul 2023 10:09:08 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=Wxob7LLz; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232867AbjGUQWv (ORCPT + 99 others); Fri, 21 Jul 2023 12:22:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51360 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232772AbjGUQVs (ORCPT ); Fri, 21 Jul 2023 12:21:48 -0400 Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 602CA3A8E for ; Fri, 21 Jul 2023 09:19:45 -0700 (PDT) Received: by mail-wm1-x332.google.com with SMTP id 5b1f17b1804b1-3fd0f000f1cso13500875e9.1 for ; Fri, 21 Jul 2023 09:19:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956383; x=1690561183; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=O2tbdUpm3a3G4jNNOOCBcva1YifyBpuvR71Op0JjGJI=; b=Wxob7LLzIRSPUfl+/ODrp7+VaVAR8syzYUqHvU9Fqg2NrsAtT5ER/D3M2KDDiP6MPp 5IbpVjnXdWxzefUmULeLsJeYWREi5pbzbRimY03rsRXfUBudRdZA+6me0dT4Nau5rlDH CkXDQ8JOcOCC/BO4m0fnACvD8ctCFM6SuBMOx0Dak+RVk3FLEC2IUQWC/wBwDrYJgG9K 5qcBmZLRHgazPoCK8/q1Zjw5gAAWrZBMJ5A0/WQk4eICB7XHvNijEabs8vDkHHmv1RfW 9H+Ne3j0XaLeS2cKW1ufVfToJkAZsuHWIMAQFITQkR++8faVAV2zjbFXGEtw35lpGFpV 2KQg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956383; x=1690561183; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=O2tbdUpm3a3G4jNNOOCBcva1YifyBpuvR71Op0JjGJI=; b=ZIAz4VdyKTc+K2urjK+XfkGQdbF05MSrPrAVXTCAdUr+bxjueNLs2SeB3iOIt9ELHw TeMjyxo+lq/zB371ikOmhGCSwJRgLGXS92QKv4bwHBBYQbPd0QZhuFsovnqEQZ4NtL1q 6lyFSW+dNk9DPppcX8B+eRjT3LfvY1K/Nmsi5FCwqNX01Xzoj62DqPKcRzLroHS3KrIv wCIEH5WM1Njy6KS4NsVxQnvDw0D2kNxSQGFu/YX8GNQ6j2XC8AVedUHUtq97yVCidu41 kimEs7HhmkQ70sfaaNLB0BvB+dIxOvG4DeNFRYPHAgWNy6sCvQH8PaSoto+E8HMHWojX JEOQ== X-Gm-Message-State: ABy/qLYAao9fJ9WRZA3KQvr9hAMXX9bbzqaos2cD6Depa5a3tmJZ+BVW 69jPJIGyxRYl9IwT3E+KOkEeaQ== X-Received: by 2002:a7b:cc95:0:b0:3fb:c075:b308 with SMTP id p21-20020a7bcc95000000b003fbc075b308mr2320474wma.12.1689956383734; Fri, 21 Jul 2023 09:19:43 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:43 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 11/23] net/tcp: Sign SYN-ACK segments with TCP-AO Date: Fri, 21 Jul 2023 17:19:02 +0100 Message-ID: <20230721161916.542667-12-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050813776953542 X-GMAIL-MSGID: 1772050813776953542 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/tcp.h | 4 ++++ include/net/tcp_ao.h | 6 ++++++ net/ipv4/tcp_ao.c | 22 ++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 48 +++++++++++++++++++++++++++++++++++++------ net/ipv6/tcp_ao.c | 22 ++++++++++++++++++++ net/ipv6/tcp_ipv6.c | 1 + 7 files changed, 98 insertions(+), 6 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index b8be3aefb9c4..ad300e5b6eb8 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2177,6 +2177,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 b63c4f2c36ea..4979c993973f 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -144,6 +144,9 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, 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); @@ -178,6 +181,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); void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index be9349239139..0233cf363de9 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -546,6 +546,28 @@ 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) +{ + void *hash_buf = NULL; + int err; + + hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); + if (!hash_buf) + return -ENOMEM; + + err = tcp_v4_ao_calc_key_rsk(ao_key, hash_buf, req); + if (err) + goto out; + + err = tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb, + hash_buf, hash_offset, sne); +out: + kfree(hash_buf); + return err; +} + 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 12b952d0dcf4..884d9f854276 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1678,6 +1678,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 27f7aaad5490..ed1164f287b7 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3722,6 +3722,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; @@ -3772,16 +3773,43 @@ 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 + u8 maclen = tcp_rsk(req)->maclen; + u8 keyid = tcp_rsk(req)->ao_keyid; + + ao_key = tcp_sk(sk)->af_specific->ao_lookup(sk, req_to_sk(req), + keyid, -1); + /* If there is no matching key - avoid sending anything, + * especially usigned segments. It could try harder and lookup + * for another peer-matching key, but the peer has requested + * ao_keyid (RFC5925 RNextKeyID), so let's keep it simple here. + */ + if (unlikely(!ao_key || tcp_ao_maclen(ao_key) != maclen)) { + rcu_read_unlock(); + skb_dst_drop(skb); + kfree_skb(skb); + net_warn_ratelimited("TCP-AO: the keyid %u with maclen %u|%u from SYN packet is not present - not sending SYNACK\n", + keyid, maclen, + ao_key ? tcp_ao_maclen(ao_key) : 0); + return NULL; + } +#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, READ_ONCE(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); @@ -3801,7 +3829,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, ao_key); th->doff = (tcp_header_size >> 2); TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS); @@ -3809,7 +3837,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 65fa52f8c2e3..ec320781bc5c 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -133,3 +133,25 @@ 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) +{ + void *hash_buf = NULL; + int err; + + hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); + if (!hash_buf) + return -ENOMEM; + + err = tcp_v6_ao_calc_key_rsk(ao_key, hash_buf, req); + if (err) + goto out; + + err = tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb, + hash_buf, hash_offset, sne); +out: + kfree(hash_buf); + return err; +} diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 6905484077db..000e4e5309d3 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -845,6 +845,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, From patchwork Fri Jul 21 16:19:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123992 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp343051vqg; Fri, 21 Jul 2023 10:03:41 -0700 (PDT) X-Google-Smtp-Source: APBJJlGg3+KAT7oh3QUQ57fL2+//QpaNEjzNZZJ3D6AHoZWadrmHGz7oA96kjN0L+p0mkVbU8w9L X-Received: by 2002:a17:902:e805:b0:1b8:4e69:c8f7 with SMTP id u5-20020a170902e80500b001b84e69c8f7mr2532036plg.23.1689959021592; Fri, 21 Jul 2023 10:03:41 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689959021; cv=none; d=google.com; s=arc-20160816; b=GvGXzqAzeIdEocsEnWAhaUPkbcqrxRemcO1tBCv0h1G52L5fnWSGcRPX137kuh88F7 fVkLPwBLej8qHOFRj7U6AQWFLL59vMmRZ8cXDlvLK+RsK1HyO50gYaxlRWQKWgXn3r3x vF98ChvGKrBcbA3q3522TmAKrut9fmkchpxJSzP1EfURriNXYWiXXNwTZ1Ebu+30j7g/ +kTFbszKq0NsrBNKBUWWTGuCcvUjS0gBXR6OSBBgH2JfhumYf/YOn8nq7P3oLUvYgCSa lsuZrZ6NlX2sQi9zAus520fwK2K5Wp3v8azS8vjLtM7Op2mA5qz8A7FXCUhvV8KmFb8G jt+w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=iBPG3Rqr7c8WUVSCpneQp6LxlqOXtNsyxL6SAvUkahc=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=yi56Xybhc9LjvWfedPkfSzqBiRtGi8ljGv8wk5+P8eHwvQe1bSLt+2AKlMRVIgy7xY eJ/Se71Z0zBsIyucLIcn3mOwA+bW0tsKXklPjHPRYmmrrOWBnVw9rCYePv7XsJAOeLq5 PWSZBYeovJaFT9op6RtPqLHwWzQr2y5bdJPOCA/421m+hRwwzFoYWN7pXi9XSljmDBGb jOeJK1KLZths9A7uEDp410oJZjCGRyg6qm1hdJPxA528OAstAwuo7GKA3A5LhNA0hyUi xjFpxgiLdpnxaNe7D1FGEOOSmYr2h5myw+jeuigtrZYXrImB/1R247WjoXa0bm/YnVxF P/7g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=bQGE9R0F; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id km16-20020a17090327d000b001b8a770ee62si3210827plb.70.2023.07.21.10.03.19; Fri, 21 Jul 2023 10:03:41 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=bQGE9R0F; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230230AbjGUQXH (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51464 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232793AbjGUQVt (ORCPT ); Fri, 21 Jul 2023 12:21:49 -0400 Received: from mail-wm1-x331.google.com (mail-wm1-x331.google.com [IPv6:2a00:1450:4864:20::331]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 423C04692 for ; Fri, 21 Jul 2023 09:19:47 -0700 (PDT) Received: by mail-wm1-x331.google.com with SMTP id 5b1f17b1804b1-3fbc6ab5ff5so18123245e9.1 for ; Fri, 21 Jul 2023 09:19:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956385; x=1690561185; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=iBPG3Rqr7c8WUVSCpneQp6LxlqOXtNsyxL6SAvUkahc=; b=bQGE9R0Fw5ipGo8zeltFVKtjKRWOAz9uO1HHcmJ2cXYcPAqN3nwQewAhG8NIa5MW47 YgrweDq7oKGIVMXp1IGNnj1bLHIhxOmH3/opy+f5IEhtwHGIlFKSzmrW52Tphnyi4hqS uPcu+8TrpZtqB/VkN4G4wXLmmr5zSeE6gas3N5VhnFERHwB52PYRFIFoye8iWmCplgq1 IZsIFLX6xEwXw4qFbyLYMwC8lxOl8wjNZz9EyfgJ29/9YXpw/tHg5VCiRB0semkt+O8v XipC2ShLXU4d3AHiVA5R7//qT11Wef2Ko9SAnxXbpreJsN97qVWc8pf9DN1iXLlvKM6e 0o3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956385; x=1690561185; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=iBPG3Rqr7c8WUVSCpneQp6LxlqOXtNsyxL6SAvUkahc=; b=CHXzV2pgR+cwNrQsS3eOaa3pOMWlg5sK/GIJQscAjfvbrUwk39Lx8V8VDYFV4pgd5Q dX40qp74isDn4TPIOFj5lam04SD6Y3AqEeK1F2FaepK373nX8toyyxYLIO4tJx1I94H2 t3H+x98JyBI+KZCWEyT5QE7p4dTsSZaB7kUz+mK6OgvjhcYR/halcEECOeDHAyB21mcb +gR2HJyLFu5p/ZOOm7vnBZEkC3mZSG3X5QqQMflmJczEdsllUqd7jqrmCoeQLmD/IzZI h3ZLSWBJnsFeGSpZ9somykTOaI+4k/F+O6PcGxWn+VhJPzEv/SuV3VuO3dNHmXuvRBOg GmKQ== X-Gm-Message-State: ABy/qLaAnn57dKFeUmIDrG1fiiXje49Li3Sw21Xai++gdY68u2gBWFd9 pEyeahh3U4q3abn1qbmuyFuxmA== X-Received: by 2002:a05:600c:21c5:b0:3fc:62c:8275 with SMTP id x5-20020a05600c21c500b003fc062c8275mr1877533wmj.35.1689956385380; Fri, 21 Jul 2023 09:19:45 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:44 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 12/23] net/tcp: Verify inbound TCP-AO signed segments Date: Fri, 21 Jul 2023 17:19:03 +0100 Message-ID: <20230721161916.542667-13-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050471287787562 X-GMAIL-MSGID: 1772050471287787562 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/dropreason-core.h | 17 ++++ include/net/tcp.h | 53 ++++++++++- include/net/tcp_ao.h | 17 ++++ net/ipv4/tcp.c | 39 ++------ net/ipv4/tcp_ao.c | 167 ++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 10 +- net/ipv6/tcp_ao.c | 12 +++ net/ipv6/tcp_ipv6.c | 11 ++- 8 files changed, 283 insertions(+), 43 deletions(-) diff --git a/include/net/dropreason-core.h b/include/net/dropreason-core.h index 05255bffa2c8..1de089ab73fd 100644 --- a/include/net/dropreason-core.h +++ b/include/net/dropreason-core.h @@ -24,6 +24,10 @@ FN(TCP_MD5NOTFOUND) \ FN(TCP_MD5UNEXPECTED) \ FN(TCP_MD5FAILURE) \ + FN(TCP_AONOTFOUND) \ + FN(TCP_AOUNEXPECTED) \ + FN(TCP_AOKEYNOTFOUND) \ + FN(TCP_AOFAILURE) \ FN(SOCKET_BACKLOG) \ FN(TCP_FLAGS) \ FN(TCP_ZEROWINDOW) \ @@ -161,6 +165,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 ad300e5b6eb8..6289ee642931 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1760,7 +1760,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) @@ -1782,7 +1782,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; } @@ -2623,4 +2623,53 @@ static inline bool tcp_ao_required(struct sock *sk, const void *saddr, return false; } +/* 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. + */ + if (tcp_ao_required(sk, saddr, family)) + return SKB_DROP_REASON_TCP_AONOTFOUND; + 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 4979c993973f..1b7ca7bf93a3 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -112,6 +112,9 @@ struct tcp6_ao_context { }; struct tcp_sigpool; +#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, @@ -127,6 +130,10 @@ 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_destroy_sock(struct sock *sk, bool twsk); void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); +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); @@ -162,6 +169,9 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *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); @@ -197,6 +207,13 @@ static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, { } +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 c90b3c6df620..d7e18c528c7b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4372,42 +4372,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); const 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; + key = tcp_md5_do_lookup(sk, l3index, saddr, family); - hash_expected = tcp_md5_do_lookup(sk, l3index, saddr, family); - if (tcp_parse_auth_options(th, &hash_location, NULL)) - return SKB_DROP_REASON_TCP_AUTH_HDR; - - /* 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; - } - - if (!hash_expected && hash_location) { + if (!key && hash_location) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED); return SKB_DROP_REASON_TCP_MD5UNEXPECTED; } @@ -4417,14 +4398,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 0233cf363de9..073be1680818 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -321,6 +321,30 @@ int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, htonl(tcp_rsk(req)->rcv_isn)); } +static 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_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, __be32 sisn, + __be32 disn, int family) +{ + if (family == AF_INET) + return tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn); +#if IS_ENABLED(CONFIG_IPV6) + else if (family == AF_INET6) + 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, __be32 daddr, __be32 saddr, int nbytes) @@ -711,6 +735,149 @@ void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, treq->maclen = tcp_ao_maclen(key); } +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) +{ + u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr); + const struct tcphdr *th = tcp_hdr(skb); + void *hash_buf = NULL; + + if (maclen != tcp_ao_maclen(key)) + return SKB_DROP_REASON_TCP_AOFAILURE; + + hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); + if (!hash_buf) + return SKB_DROP_REASON_NOT_SPECIFIED; + + /* XXX: make it per-AF callback? */ + tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key, + (phash - (u8 *)th), sne); + if (memcmp(phash, hash_buf, maclen)) { + kfree(hash_buf); + return SKB_DROP_REASON_TCP_AOFAILURE; + } + kfree(hash_buf); + 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 tcphdr *th = tcp_hdr(skb); + u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */ + struct tcp_ao_info *info; + enum skb_drop_reason ret; + struct tcp_ao_key *key; + __be32 sisn, disn; + u8 *traffic_key; + u32 sne = 0; + + info = rcu_dereference(tcp_sk(sk)->ao_info); + if (!info) + return SKB_DROP_REASON_TCP_AOUNEXPECTED; + + if (unlikely(th->syn)) { + sisn = th->seq; + disn = 0; + } + + /* 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; + struct tcp_ao_key *current_key; + + /* 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 = READ_ONCE(info->rnext_key); + if (key->rcvid != aoh->keyid) { + key = tcp_ao_established_key(info, -1, aoh->keyid); + if (!key) + goto key_not_found; + } + + /* Delayed retransmitted SYN */ + if (unlikely(th->syn && !th->ack)) + goto verify_hash; + + sne = 0; + /* 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; + current_key = READ_ONCE(info->current_key); + /* Key rotation: the peer asks us to use new key (RNext) */ + if (unlikely(aoh->rnext_keyid != current_key->sndid)) { + /* If the key is not found we do nothing. */ + key = tcp_ao_established_key(info, aoh->rnext_keyid, -1); + if (key) + /* pairs with tcp_ao_del_cmd */ + WRITE_ONCE(info->current_key, key); + } + return SKB_NOT_DROPPED_YET; + } + + /* 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 (th->syn && !th->ack) + goto verify_hash; + + if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { + /* Make the initial syn the likely case here */ + if (unlikely(req)) { + sne = 0; + sisn = htonl(tcp_rsk(req)->rcv_isn); + disn = htonl(tcp_rsk(req)->snt_isn); + } else if (unlikely(th->ack && !th->syn)) { + /* Possible syncookie packet */ + sisn = htonl(ntohl(th->seq) - 1); + disn = htonl(ntohl(th->ack_seq) - 1); + sne = 0; + } else if (unlikely(!th->syn)) { + /* no way to figure out initial sisn/disn - drop */ + return SKB_DROP_REASON_TCP_FLAGS; + } + } else if (sk->sk_state == TCP_SYN_SENT) { + disn = info->lisn; + if (th->syn || th->rst) + sisn = th->seq; + else + sisn = info->risn; + } else { + WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", sk->sk_state); + return SKB_DROP_REASON_TCP_AOFAILURE; + } +verify_hash: + traffic_key = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); + if (!traffic_key) + return SKB_DROP_REASON_NOT_SPECIFIED; + tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family); + ret = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, + traffic_key, phash, sne); + kfree(traffic_key); + return ret; + +key_not_found: + return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; +} + static 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 884d9f854276..f09c0389ae11 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2200,9 +2200,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); @@ -2279,8 +2279,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; diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index ec320781bc5c..984a75f96e20 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -49,6 +49,18 @@ static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, return err; } +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 000e4e5309d3..40266a3a99a6 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1779,9 +1779,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); @@ -1854,8 +1854,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; @@ -2083,6 +2083,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = { .ao_lookup = tcp_v6_ao_lookup, .calc_ao_hash = tcp_v4_ao_hash_skb, .ao_parse = tcp_v6_parse_ao, + .ao_calc_key_sk = tcp_v4_ao_calc_key_sk, #endif }; #endif From patchwork Fri Jul 21 16:19:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123956 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp320094vqg; Fri, 21 Jul 2023 09:23:56 -0700 (PDT) X-Google-Smtp-Source: APBJJlFvOpAq+vtqihTLFXLYWdgrgelTj1Q38lvJNc07AS44ADgXYzt6nD+wyD+WSp284ohYhaxi X-Received: by 2002:a05:6a20:6a03:b0:137:bca2:d2a6 with SMTP id p3-20020a056a206a0300b00137bca2d2a6mr2917166pzk.34.1689956636240; Fri, 21 Jul 2023 09:23:56 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689956636; cv=none; d=google.com; s=arc-20160816; b=GhLQRY3mdCuXVvsNZj1+0Y1or1P2TX3eHHT4BQwb3ZJF+qhnwfpitx66LfIN7n/bYI z7aURRHTHGIsoeRmL0Y6MeBVFyuusifMrOg3LHEqfPIq2NoPGL+dNVs+y/jJRsYmWjcE a/h+ZuphT4fb5Ehg2gT3MzmnHuY4EXvaaUEPw8Fbf01ZiTwBQ4UtCB+sHYHre9cbAtp/ 9Roa+BljCfMmfZ4FGw9UZwYzwj2n4nNYhKk8uiwmu3wuhyHZIIc281Y62k2rZDXDpW7b Dkx4nUt3aMfmkmCbDvqelQ4fU+P1fywrkHcZ/VsqG2bXbxKzh8B3/QSFV5HP0du36xYj XcwA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=kiva03hSDRK1UC8P87Xsz1sPlIEN/ZjJKgFHA949uEA=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=UE1dKbf6sU5v5/taPUqsDC63UoGHi9s2RLIDIuNarnpkRIbNk85C8YfzuYTJAH4PY2 V/WUSyjU5QrK+pYESdQOIuItgFJ+DuTq5NQMrKwTvzexh9wM5QrtdNr68dFXJJ8kAqyI G+Y+gue6bWpkPTpREZgHExXcqNYTKUFlWX0i0vrHsM+w+TpIv5+S0zvNYI4d20mQBp+m MdFMntDK4jYD+Sl3iM78WcPIoZI/hl36bSRdPWUZ2Q80lasKU5THwlSTxbKCNSSceDKn MNH6IVRVWin9bLOqlDLqqRHtt4TY3TZEOcYFoddVPzQiQ/+B/FK4rwqWCyZfl8aIvqvj oHJA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=jLap6emh; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id fb33-20020a056a002da100b0068094fd5451si3435610pfb.188.2023.07.21.09.23.42; Fri, 21 Jul 2023 09:23:56 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=jLap6emh; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232363AbjGUQXK (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51642 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229618AbjGUQWI (ORCPT ); Fri, 21 Jul 2023 12:22:08 -0400 Received: from mail-lf1-x131.google.com (mail-lf1-x131.google.com [IPv6:2a00:1450:4864:20::131]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 636B946A4 for ; Fri, 21 Jul 2023 09:19:49 -0700 (PDT) Received: by mail-lf1-x131.google.com with SMTP id 2adb3069b0e04-4fb41682472so3428688e87.2 for ; Fri, 21 Jul 2023 09:19:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956387; x=1690561187; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kiva03hSDRK1UC8P87Xsz1sPlIEN/ZjJKgFHA949uEA=; b=jLap6emhZQmziuSRUuzHvq92n3S7UFTQs4dvhHcaXuxMpOSRMhXi55GqZsjJjv7gc2 O9coqCF1oA+iMO99dOVS1RfQgIInASzRXPdhlsKDlgVV8gwB2gKkpIZxV2e1NC6Vnumf ZBY3nLvmPSNCSN/Me05w8veIu7sqq1lQb082TvhTDWl40fjGkR3Q/qf/A3uS+yfZXwvz nAUdNR7xB9qb6uTdZsUZ4d/XnvMi+20AkdWRG/c+k4OJ0rNF7FKJ3GQhAKKemtC3zJAX aAA56IDPwFQx/7wiwBMYmh1wfVzf/sNkly/I8IEjhI/XX/Wrmo5XiXvwfhKOMYqHvfjX IjsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956387; x=1690561187; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kiva03hSDRK1UC8P87Xsz1sPlIEN/ZjJKgFHA949uEA=; b=Mu6DboiCKc6YTp0ucSTbnS60EUwLGKxDBa4tSLVa55pwdgozrvtzvJF8Zombmjc9DP yGh/iFq5H3llWFN1HqMAFfivUHwCb0f7Fib0hbjTR4ErEsgCZR1P8Z67ryigkBEAwu3O IqRPzzhDU3PG9L9mv1e7GJr+XQipz1R3X3jjobJvlas3lSXkUyXUrMV16Gg82jnuJ53p T1Kn2HgiI+RL3y+mO4JvbQtzosu+M+YHKYwu2M9y0DHqIBBkrGSftQf5tZ9DTBSR57WU 1VoekLXfcvhiq+OIX0Xxgk1vOymY0lpVhF+V5u0koA0w72vWrpu/ajs7hPRBl2V3P6At wwww== X-Gm-Message-State: ABy/qLZVWOsNc5su5DnS8fGD7lDbJ9RCZZPwjpC6g3YsJwKpfMnNBJQc F8JVX3xU2Vmozhi9u6CJGsZKo2sJtNw/pE83FMQ= X-Received: by 2002:ac2:5e3a:0:b0:4fa:b380:4c03 with SMTP id o26-20020ac25e3a000000b004fab3804c03mr1605480lfg.56.1689956387185; Fri, 21 Jul 2023 09:19:47 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:46 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 13/23] net/tcp: Add TCP-AO segments counters Date: Fri, 21 Jul 2023 17:19:04 +0100 Message-ID: <20230721161916.542667-14-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772047969930447695 X-GMAIL-MSGID: 1772047969930447695 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/dropreason-core.h | 15 +++++++++++---- include/net/tcp.h | 15 +++++++++++---- include/net/tcp_ao.h | 10 ++++++++++ include/uapi/linux/snmp.h | 4 ++++ include/uapi/linux/tcp.h | 8 +++++++- net/ipv4/proc.c | 4 ++++ net/ipv4/tcp_ao.c | 30 +++++++++++++++++++++++++++--- net/ipv4/tcp_ipv4.c | 2 +- net/ipv6/tcp_ipv6.c | 4 ++-- 9 files changed, 77 insertions(+), 15 deletions(-) diff --git a/include/net/dropreason-core.h b/include/net/dropreason-core.h index 1de089ab73fd..f93ab108df4e 100644 --- a/include/net/dropreason-core.h +++ b/include/net/dropreason-core.h @@ -166,17 +166,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 6289ee642931..a58f95237ea3 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2605,7 +2605,7 @@ static inline int tcp_parse_auth_options(const struct tcphdr *th, } static inline bool tcp_ao_required(struct sock *sk, const void *saddr, - int family) + int family, bool stat_inc) { #ifdef CONFIG_TCP_AO struct tcp_ao_info *ao_info; @@ -2617,8 +2617,13 @@ static inline bool tcp_ao_required(struct sock *sk, const void *saddr, return false; ao_key = tcp_ao_do_lookup(sk, saddr, family, -1, -1, 0); - if (ao_info->ao_required || ao_key) + if (ao_info->ao_required || ao_key) { + if (stat_inc) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOREQUIRED); + atomic64_inc(&ao_info->counters.ao_required); + } return true; + } #endif return false; } @@ -2640,8 +2645,10 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req, return SKB_DROP_REASON_TCP_AUTH_HDR; if (req) { - if (tcp_rsk_used_ao(req) != !!aoh) + if (tcp_rsk_used_ao(req) != !!aoh) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); return SKB_DROP_REASON_TCP_AOFAILURE; + } } /* sdif set, means packet ingressed via a device @@ -2656,7 +2663,7 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req, * the last key is impossible to remove, so there's * always at least one current_key. */ - if (tcp_ao_required(sk, saddr, family)) + if (tcp_ao_required(sk, saddr, family, true)) return SKB_DROP_REASON_TCP_AONOTFOUND; 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 1b7ca7bf93a3..0a02098bddeb 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -19,6 +19,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; @@ -34,6 +41,8 @@ struct tcp_ao_key { u8 rcvid; u8 maclen; struct rcu_head rcu; + atomic64_t pkt_good; + atomic64_t pkt_bad; u8 traffic_keys[]; }; @@ -82,6 +91,7 @@ struct tcp_ao_info { */ struct tcp_ao_key *current_key; struct tcp_ao_key *rnext_key; + struct tcp_ao_counters counters; u32 ao_required :1, __unused :31; __be32 lisn; diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index 26f33a4c253d..06ddf4cd295c 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -296,6 +296,10 @@ enum LINUX_MIB_TCPMIGRATEREQSUCCESS, /* TCPMigrateReqSuccess */ LINUX_MIB_TCPMIGRATEREQFAILURE, /* TCPMigrateReqFailure */ LINUX_MIB_TCPPLBREHASH, /* TCPPLBRehash */ + LINUX_MIB_TCPAOREQUIRED, /* TCPAORequired */ + LINUX_MIB_TCPAOBAD, /* TCPAOBad */ + LINUX_MIB_TCPAOKEYNOTFOUND, /* TCPAOKeyNotFound */ + LINUX_MIB_TCPAOGOOD, /* TCPAOGood */ __LINUX_MIB_MAX }; diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 250e0ce2cc38..3fe0612ec59a 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -391,9 +391,15 @@ struct tcp_ao_info_opt { /* setsockopt(TCP_AO_INFO) */ __u32 set_current :1, /* corresponding ::current_key */ set_rnext :1, /* corresponding ::rnext */ ao_required :1, /* don't accept non-AO connects */ - reserved :29; /* must be 0 */ + set_counters :1, /* set/clear ::pkt_* counters */ + reserved :28; /* must be 0 */ + __u16 reserved2; /* padding, must be 0 */ __u8 current_key; /* KeyID to set as Current_key */ __u8 rnext; /* KeyID to set as Rnext_key */ + __u64 pkt_good; /* verified segments */ + __u64 pkt_bad; /* failed verification */ + __u64 pkt_key_not_found; /* could not find a key to verify */ + __u64 pkt_ao_required; /* segments missing TCP-AO sign */ } __attribute__((aligned(8))); /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index eaf1d3113b62..3f643cd29cfe 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -298,6 +298,10 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPMigrateReqSuccess", LINUX_MIB_TCPMIGRATEREQSUCCESS), SNMP_MIB_ITEM("TCPMigrateReqFailure", LINUX_MIB_TCPMIGRATEREQFAILURE), SNMP_MIB_ITEM("TCPPLBRehash", LINUX_MIB_TCPPLBREHASH), + 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 073be1680818..5ab66928a952 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -184,6 +184,8 @@ static struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, *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; } @@ -745,8 +747,12 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, const struct tcphdr *th = tcp_hdr(skb); void *hash_buf = NULL; - 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; + } hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); if (!hash_buf) @@ -756,9 +762,15 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key, (phash - (u8 *)th), sne); if (memcmp(phash, hash_buf, maclen)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); + atomic64_inc(&info->counters.pkt_bad); + atomic64_inc(&key->pkt_bad); kfree(hash_buf); 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); kfree(hash_buf); return SKB_NOT_DROPPED_YET; } @@ -778,8 +790,10 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, u32 sne = 0; 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; + } if (unlikely(th->syn)) { sisn = th->seq; @@ -875,6 +889,8 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, return ret; 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; } @@ -1460,6 +1476,8 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, key->keyflags = cmd.keyflags; key->sndid = cmd.sndid; key->rcvid = cmd.rcvid; + atomic64_set(&key->pkt_good, 0); + atomic64_set(&key->pkt_bad, 0); ret = tcp_ao_parse_crypto(&cmd, key); if (ret < 0) @@ -1673,7 +1691,7 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, return -EINVAL; } - if (cmd.reserved != 0) + if (cmd.reserved != 0 || cmd.reserved2 != 0) return -EINVAL; ao_info = setsockopt_ao_info(sk); @@ -1708,6 +1726,12 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, goto out; } } + if (cmd.set_counters) { + atomic64_set(&ao_info->counters.pkt_good, cmd.pkt_good); + atomic64_set(&ao_info->counters.pkt_bad, cmd.pkt_bad); + atomic64_set(&ao_info->counters.key_not_found, cmd.pkt_key_not_found); + atomic64_set(&ao_info->counters.ao_required, cmd.pkt_ao_required); + } ao_info->ao_required = cmd.ao_required; if (new_current) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f09c0389ae11..d8d2499b5e26 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1528,7 +1528,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname, /* Don't allow keys for peers that have a matching TCP-AO key. * See the comment in tcp_ao_add_cmd() */ - if (tcp_ao_required(sk, addr, AF_INET)) + if (tcp_ao_required(sk, addr, AF_INET, false)) return -EKEYREJECTED; return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 40266a3a99a6..c825d7c80f5b 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -660,7 +660,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, /* Don't allow keys for peers that have a matching TCP-AO key. * See the comment in tcp_ao_add_cmd() */ - if (tcp_ao_required(sk, addr, AF_INET)) + if (tcp_ao_required(sk, addr, AF_INET, false)) return -EKEYREJECTED; return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, @@ -672,7 +672,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, /* Don't allow keys for peers that have a matching TCP-AO key. * See the comment in tcp_ao_add_cmd() */ - if (tcp_ao_required(sk, addr, AF_INET6)) + if (tcp_ao_required(sk, addr, AF_INET6, false)) return -EKEYREJECTED; return tcp_md5_do_add(sk, addr, AF_INET6, prefixlen, l3index, flags, From patchwork Fri Jul 21 16:19:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 124035 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp359476vqg; Fri, 21 Jul 2023 10:29:58 -0700 (PDT) X-Google-Smtp-Source: APBJJlGnlB895W4luQ7bXhrp/9KehzfEb+NMcST7IthD6pHcg8KMM6Qwn6wdlILgIw8Ujd8Vf/nz X-Received: by 2002:a17:906:104c:b0:991:e2b6:2189 with SMTP id j12-20020a170906104c00b00991e2b62189mr1790802ejj.43.1689960598334; Fri, 21 Jul 2023 10:29:58 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689960598; cv=none; d=google.com; s=arc-20160816; b=ulNd6aOqYUJuOWmFat9hZ8ZYeKL55p8DCcdNlM7FP4HKT21QIvcs8YkVFOAyx+0klJ 3uuq+v5Qainb1ZrNTTn8xfb7CoPSe/2zSqK+SpwbSn03QQiWNt9mbmRK37W0x1i9NRfs 7Z1B44dJA/GwHxORzpV2gqyeFMgtRXHK+2clRHlcqLaQGQTSgBHgDXLemkqIxDeYg/zc ap9yUjJourwgtipD0qxPlUHORtPp+UlTlitnL7eF+3gxb1AsiNA9Cz/5rHmEymMT1Dwm 0agnf5hAHVyVsD/c19Af3XpFIHAVihBUBdEE/eUmgxccC+RVxdpkpWnXQNjiUUBja+3N csnQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=M1Hko4AFcO0IMdyB7ocCH9V+zaUpkiTYdC3M+qmlMZ4=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=vl68xvWZCxlLVKjhjhiCxkt9AZ5vPlFuwdn+vYAwQZ38ViK/B0gNahVNKgW316193t 8xCraRIVRuDDRZw8VldBkIMHDkO1f9tXLwqk3H9JsVQ39irOoQLs+UA28f4EXKguPBPl 4bQW38fGifOUpFfcejO+LepKNdxY8jSHsKSyyGACndRuSkpLK2iC69/N7Mzhqv1/Zaot zzv5bEtrFcsYz+vMZn/6R0Mlz73s3MDTEeCSqLhyyd38otHJvNHj/qwTQF41KRXdjtO+ uJdMIKyemf+PTm1BHdbX4ZIorz4PoMkhiwUM9ZYUCNqO62yeU4eCRh1OHtn+ZH7gvUFZ SafQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=jT5bLDnv; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id y4-20020a1709064b0400b00992f309cfe7si2195192eju.602.2023.07.21.10.29.34; Fri, 21 Jul 2023 10:29:58 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=jT5bLDnv; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232474AbjGUQXO (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51834 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232665AbjGUQWT (ORCPT ); Fri, 21 Jul 2023 12:22:19 -0400 Received: from mail-wm1-x333.google.com (mail-wm1-x333.google.com [IPv6:2a00:1450:4864:20::333]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1352249CA for ; Fri, 21 Jul 2023 09:19:52 -0700 (PDT) Received: by mail-wm1-x333.google.com with SMTP id 5b1f17b1804b1-3fbc5d5742bso18817155e9.2 for ; Fri, 21 Jul 2023 09:19:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956389; x=1690561189; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=M1Hko4AFcO0IMdyB7ocCH9V+zaUpkiTYdC3M+qmlMZ4=; b=jT5bLDnv1uDQH3GysAe3SXicpz4gNYBDBJC/AgDX6POstquAgsETAAOpy1HoU7QlRK ufJzAnZmnC6R7QpbW1nCApfeHd/HCObDMNhgPZeToVg4hQqHLGcACqzevz3fbYPIjrRN hYONqmqpKUlSSer+cYOux1TNlhMHyZlO/rHcplF/7Q6RasMMgYD+wQWQClANfepreXAC BmFu9dCG0kWsynijxYDdZZys4VHlyPmGWhjXCBSc98Bj1A59yMRM3K0YQC/x+WLHanLy S//eIrMByFlBECnMLrNEtiKbPU5d1FqCIHUaVXe9al4tM4qgrucIrZrCZ/Rdyx7waLxz Wlqg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956389; x=1690561189; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=M1Hko4AFcO0IMdyB7ocCH9V+zaUpkiTYdC3M+qmlMZ4=; b=ZaD5JMsgCcRsinWn1PakNQQy/cAu5z0BWfW/BNWuzWzbIfVTkgtVWIbmaQkBujBhaP gH2WFgDvKtrFWdjBPUglnZBxFSfts3PWJ+29cctNPG4B91Qn/7PoUfjeNnAK3AxbA3Ju iByAj6qc63D/ool0PSNsMNjJbHD4uVV4+9aB4HXil4FJ1Y3ykgQHnJq4q0nvcHfaJeou r5DIeK23kG0Tb3aIEe64zPDBRo8+a350pkcjUCnHYJ+9hkp0n7jNOSOUR5Rjfb1BsXJQ el+2u7u0B0EnnSTQVTXWUoU/gpV0z+PUIYQN+nf7JYKa83Cz/+5853oUuRJxqrq7scW0 Mo5A== X-Gm-Message-State: ABy/qLYXgOe0WPgnp63iUBEtLK5S0vschTuv6Llu6rdvdTdGI1PW3+rF JG/+N3W1BoCbZ9l97jxXGv9etw== X-Received: by 2002:a05:600c:2047:b0:3fb:e643:1225 with SMTP id p7-20020a05600c204700b003fbe6431225mr1825804wmg.13.1689956388940; Fri, 21 Jul 2023 09:19:48 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:48 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 14/23] net/tcp: Add TCP-AO SNE support Date: Fri, 21 Jul 2023 17:19:05 +0100 Message-ID: <20230721161916.542667-15-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772052124456721528 X-GMAIL-MSGID: 1772052124456721528 Add Sequence Number Extension (SNE) for TCP-AO. This is needed to protect long-living TCP-AO connections from replaying attacks after sequence number roll-over, see RFC5925 (6.2). Co-developed-by: Francesco Ruggeri Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/tcp_ao.h | 20 ++++++++++++++++++++ net/ipv4/tcp_ao.c | 40 +++++++++++++++++++++++++++++++++------- net/ipv4/tcp_input.c | 28 ++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_minisocks.c | 15 ++++++++++++++- net/ipv4/tcp_output.c | 6 +++++- net/ipv6/tcp_ipv6.c | 1 + 7 files changed, 102 insertions(+), 9 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 0a02098bddeb..fc5674763730 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -96,6 +96,25 @@ struct tcp_ao_info { __unused :31; __be32 lisn; __be32 risn; + /* Sequence Number Extension (SNE) are upper 4 bytes for SEQ, + * that protect TCP-AO connection from replayed old TCP segments. + * See RFC5925 (6.2). + * In order to get correct SNE, there's a helper tcp_ao_compute_sne(). + * It needs SEQ basis to understand whereabouts are lower SEQ numbers. + * According to that basis vector, it can provide incremented SNE + * when SEQ rolls over or provide decremented SNE when there's + * a retransmitted segment from before-rolling over. + * - for request sockets such basis is rcv_isn/snt_isn, which seems + * good enough as it's unexpected to receive 4 Gbytes on reqsk. + * - for full sockets the basis is rcv_nxt/snd_una. snd_una is + * taken instead of snd_nxt as currently it's easier to track + * in tcp_snd_una_update(), rather than updating SNE in all + * WRITE_ONCE(tp->snd_nxt, ...) + * - for time-wait sockets the basis is tw_rcv_nxt/tw_snd_nxt. + * tw_snd_nxt is not expected to change, while tw_rcv_nxt may. + */ + u32 snd_sne; + u32 rcv_sne; atomic_t refcnt; /* Protects twsk destruction */ struct rcu_head rcu; }; @@ -144,6 +163,7 @@ enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, unsigned short int family, const struct request_sock *req, const struct tcp_ao_hdr *aoh); +u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq); struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port); diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 5ab66928a952..5ebbfadfbfd0 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -403,6 +403,21 @@ static int tcp_ao_hash_pseudoheader(unsigned short int family, return -EAFNOSUPPORT; } +u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq) +{ + u32 sne = next_sne; + + if (before(seq, next_seq)) { + if (seq > next_seq) + sne--; + } else { + if (seq < next_seq) + sne++; + } + + return sne; +} + /* tcp_ao_hash_sne(struct tcp_sigpool *hp) * @hp - used for hashing * @sne - sne value @@ -641,7 +656,8 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, sisn = htonl(tcp_rsk(req)->rcv_isn); disn = htonl(tcp_rsk(req)->snt_isn); - *sne = 0; + *sne = tcp_ao_compute_sne(0, tcp_rsk(req)->snt_isn, + ntohl(seq)); } else { sisn = th->seq; disn = 0; @@ -674,11 +690,15 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, *keyid = (*key)->rcvid; } else { struct tcp_ao_key *rnext_key; + u32 snd_basis; - if (sk->sk_state == TCP_TIME_WAIT) + if (sk->sk_state == TCP_TIME_WAIT) { ao_info = rcu_dereference(tcp_twsk(sk)->ao_info); - else + snd_basis = tcp_twsk(sk)->tw_snd_nxt; + } else { ao_info = rcu_dereference(tcp_sk(sk)->ao_info); + snd_basis = tcp_sk(sk)->snd_una; + } if (!ao_info) return -ENOENT; @@ -688,7 +708,8 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, *traffic_key = snd_other_key(*key); rnext_key = READ_ONCE(ao_info->rnext_key); *keyid = rnext_key->rcvid; - *sne = 0; + *sne = tcp_ao_compute_sne(READ_ONCE(ao_info->snd_sne), + snd_basis, ntohl(seq)); } return 0; } @@ -821,7 +842,8 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, if (unlikely(th->syn && !th->ack)) goto verify_hash; - sne = 0; + sne = tcp_ao_compute_sne(info->rcv_sne, tcp_sk(sk)->rcv_nxt, + ntohl(th->seq)); /* Established socket, traffic key are cached */ traffic_key = rcv_other_key(key); err = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, @@ -856,14 +878,16 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { /* Make the initial syn the likely case here */ if (unlikely(req)) { - sne = 0; + sne = tcp_ao_compute_sne(0, tcp_rsk(req)->rcv_isn, + ntohl(th->seq)); sisn = htonl(tcp_rsk(req)->rcv_isn); disn = htonl(tcp_rsk(req)->snt_isn); } else if (unlikely(th->ack && !th->syn)) { /* Possible syncookie packet */ sisn = htonl(ntohl(th->seq) - 1); disn = htonl(ntohl(th->ack_seq) - 1); - sne = 0; + sne = tcp_ao_compute_sne(0, ntohl(sisn), + ntohl(th->seq)); } else if (unlikely(!th->syn)) { /* no way to figure out initial sisn/disn - drop */ return SKB_DROP_REASON_TCP_FLAGS; @@ -963,6 +987,7 @@ void tcp_ao_connect_init(struct sock *sk) tp->tcp_header_len += tcp_ao_len(key); ao_info->lisn = htonl(tp->write_seq); + ao_info->snd_sne = 0; } else { /* TODO: probably, it should fail to connect() here */ rcu_assign_pointer(tp->ao_info, NULL); @@ -981,6 +1006,7 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb) return; ao->risn = tcp_hdr(skb)->seq; + ao->rcv_sne = 0; hlist_for_each_entry_rcu(key, &ao->head, node) tcp_ao_cache_traffic_keys(sk, ao, key); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2a7ed329f7cb..4b37e82ab612 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3532,9 +3532,18 @@ static inline bool tcp_may_update_window(const struct tcp_sock *tp, static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack) { u32 delta = ack - tp->snd_una; +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao; +#endif sock_owned_by_me((struct sock *)tp); tp->bytes_acked += delta; +#ifdef CONFIG_TCP_AO + ao = rcu_dereference_protected(tp->ao_info, + lockdep_sock_is_held((struct sock *)tp)); + if (ao && ack < tp->snd_una) + ao->snd_sne++; +#endif tp->snd_una = ack; } @@ -3542,9 +3551,18 @@ static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack) static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq) { u32 delta = seq - tp->rcv_nxt; +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao; +#endif sock_owned_by_me((struct sock *)tp); tp->bytes_received += delta; +#ifdef CONFIG_TCP_AO + ao = rcu_dereference_protected(tp->ao_info, + lockdep_sock_is_held((struct sock *)tp)); + if (ao && seq < tp->rcv_nxt) + ao->rcv_sne++; +#endif WRITE_ONCE(tp->rcv_nxt, seq); } @@ -6387,6 +6405,16 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, * simultaneous connect with crossed SYNs. * Particularly, it can be connect to self. */ +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao; + + ao = rcu_dereference_protected(tp->ao_info, + lockdep_sock_is_held(sk)); + if (ao) { + ao->risn = th->seq; + ao->rcv_sne = 0; + } +#endif tcp_set_state(sk, TCP_SYN_RECV); if (tp->rx_opt.saw_tstamp) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d8d2499b5e26..37497dec5eb9 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1039,6 +1039,7 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb) struct tcp_ao_key *rnext_key; traffic_key = snd_other_key(ao_key); + ao_sne = READ_ONCE(ao_info->snd_sne); rnext_key = READ_ONCE(ao_info->rnext_key); rcv_next = rnext_key->rcvid; } diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 11b76fa601a7..3b367365dc7d 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -51,6 +51,18 @@ tcp_timewait_check_oow_rate_limit(struct inet_timewait_sock *tw, return TCP_TW_SUCCESS; } +static void twsk_rcv_nxt_update(struct tcp_timewait_sock *tcptw, u32 seq) +{ +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao; + + ao = rcu_dereference(tcptw->ao_info); + if (unlikely(ao && seq < tcptw->tw_rcv_nxt)) + WRITE_ONCE(ao->rcv_sne, ao->rcv_sne + 1); +#endif + tcptw->tw_rcv_nxt = seq; +} + /* * * Main purpose of TIME-WAIT state is to close connection gracefully, * when one of ends sits in LAST-ACK or CLOSING retransmitting FIN @@ -136,7 +148,8 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, /* FIN arrived, enter true time-wait state. */ tw->tw_substate = TCP_TIME_WAIT; - tcptw->tw_rcv_nxt = TCP_SKB_CB(skb)->end_seq; + twsk_rcv_nxt_update(tcptw, TCP_SKB_CB(skb)->end_seq); + if (tmp_opt.saw_tstamp) { tcptw->tw_ts_recent_stamp = ktime_get_seconds(); tcptw->tw_ts_recent = tmp_opt.rcv_tsval; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index ed1164f287b7..d87a3823fec5 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1443,6 +1443,7 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, u8 *traffic_key; void *tkey_buf = NULL; u32 disn; + u32 sne; sk_gso_disable(sk); if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) { @@ -1460,9 +1461,12 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, } else { traffic_key = snd_other_key(ao_key); } + sne = tcp_ao_compute_sne(READ_ONCE(ao->snd_sne), + READ_ONCE(tp->snd_una), + ntohl(th->seq)); tp->af_specific->calc_ao_hash(opts.hash_location, ao_key, sk, skb, traffic_key, - opts.hash_location - (u8 *)th, 0); + opts.hash_location - (u8 *)th, sne); kfree(tkey_buf); } #endif diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index c825d7c80f5b..873ce0d72f34 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1180,6 +1180,7 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) /* rcv_next switches to our rcv_next */ rnext_key = READ_ONCE(ao_info->rnext_key); rcv_next = rnext_key->rcvid; + ao_sne = READ_ONCE(ao_info->snd_sne); } #endif From patchwork Fri Jul 21 16:19:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123983 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp341328vqg; Fri, 21 Jul 2023 10:01:41 -0700 (PDT) X-Google-Smtp-Source: APBJJlEPeQMiZyPM9RoB7VqoN7nkxsSxeoB1mxWHCgx+faTREDslUVOrY1D0F3kK22Yw7fM7MgP0 X-Received: by 2002:a17:90b:18f:b0:262:ec71:b88f with SMTP id t15-20020a17090b018f00b00262ec71b88fmr2011043pjs.25.1689958901477; Fri, 21 Jul 2023 10:01:41 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689958901; cv=none; d=google.com; s=arc-20160816; b=EsEmjF6bwZGMaKFVVVC9Mpsqb7ymTYE8DAQ9p9WCzdAu0rdG5H53FWtDhgG5hwxXmR sJs8JvVsFAE/yZZsB6SNFnLURmMtxy5enMi8xLyIHexDdAHF08Ubc4m8c6UA24ue2lQR KzntJOvvQ6UcMI4Lam3rzu4GGGwAHyQDCiKd/BsEZkoJ9iCLqPpYghJlb4JcFPKFpIkv ORx7PVM5t2BTRv9EKERU8Do3B3mJEWtGI0Zm4EG1nfUsxodLmOA/IzOHHBw68RP4eapV LQO93tbMSC/01mIqDlcveKSt1jObKgzr6sNd9Y/+RVfKR/3C5YXTn4QBdenu8nLJNqNN CYtQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=dk0lf8Y1cfq9WOtMgndJknIwEJGJjIggnn3HMiwjqWo=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=y7CLPuYoWd8AYWJpyZSRCpEwszhLA62DVa76vcjIR0td1ZnNJ+sKADbN3CrtkhpZ88 GhZ/x3Mc6YmmP2MtUOCQgEcAR2kr6FZVmGjKp3ZaWgmT9H+XlWDCUG6vg2NiGlNfuYCv oNK3m9TKQ4MRvP/d9WWjpSO4L9XrDZn2aCa1C5AbSV9HjWNNMLh/LWs7h2y+D39D3/L4 IgNj9Z60HBaqgQEhiqTbUbZXj58vnHEMahHy1dZ9XYA1B0xi/gPjQk7WoJkpjBrUMvJP CG2MPhepUZABshK570nxbe+uQfBe9ozNqXvHTY/40cUKU87+J0sm77ZdX20/iM9aOClk r2Tg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=hUxCYU1P; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id kk13-20020a17090b4a0d00b002597ed3cc4fsi3728210pjb.189.2023.07.21.10.01.15; Fri, 21 Jul 2023 10:01:41 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=hUxCYU1P; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232651AbjGUQXT (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51208 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232711AbjGUQWU (ORCPT ); Fri, 21 Jul 2023 12:22:20 -0400 Received: from mail-wm1-x32e.google.com (mail-wm1-x32e.google.com [IPv6:2a00:1450:4864:20::32e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DEC0449F1 for ; Fri, 21 Jul 2023 09:19:52 -0700 (PDT) Received: by mail-wm1-x32e.google.com with SMTP id 5b1f17b1804b1-3fbc244d307so18739675e9.1 for ; Fri, 21 Jul 2023 09:19:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956390; x=1690561190; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=dk0lf8Y1cfq9WOtMgndJknIwEJGJjIggnn3HMiwjqWo=; b=hUxCYU1P+WACT1DlsadgIZYDyrhoVEAihsIpDdlugcZ3fRHbi+ZpXE5vyV+F3qAtqI Zx3dgzQ94nFO5t7w94KcNsVVz5hallnrhOd+rBjtf6OUbSLZknP36hIXP+TuOxhQBqJW vqA5Vwz4II9WLjN63sRsYALsCjQQKDN9vxSa9ShjLcBlzA/2jUJHLjp/ix+8twENs7m+ c64ikwm6FOsbjAkMM/zR2WbkAXNRDwST/sRM5wbXRIOqcZ53AXkFpJcv5uki8NmwMiNb C93S33yculI3JqxAjUrzXbTRuRq8mr/4bNE+CjDdfW9H5JDq0bY+L1MmCV2VYS1HJW8W Mn3w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956390; x=1690561190; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=dk0lf8Y1cfq9WOtMgndJknIwEJGJjIggnn3HMiwjqWo=; b=Li/EeAur9kQr/0T+NQ6gfSp/QBUAKHZ+MfpTDSOn3uEe7d2aPS13e+XCR6dPfy77QB XsffV1mWys/9reYfYC2f+5DVYHj3ccXKR+xJgl2tmQ2kZJkA/mFT5Yl0dUWr6QJu3XGF qBw9Le9Ix8u7yqHJ9VAvExmAlRF+iwnmvGeIcQX5bRa/inN9Qw/x3dofLIsIyv2poW70 UQcK3jDQHC3XJ2i4H6gTw2cNWlUlPbgW95d2mtVOAsrNix8AsgoGOavgHBFSrx4/7A4M Y1L8o/0Qkkf+7vPSkOVJSmRyMwfQiEzZ/t5pP5fPNFowjqBsvf/CQ7R2nFUdu/KmdNaB PS6g== X-Gm-Message-State: ABy/qLaiALYJ/m2GdoMeO83Sjt+/rBt2VCyzFL2VIdhwpWcxlXOaUJ0p Ftxxwv2tZuXlasxzenXS0eMobQ== X-Received: by 2002:a7b:c7d2:0:b0:3f9:c04:e76c with SMTP id z18-20020a7bc7d2000000b003f90c04e76cmr1779245wmk.28.1689956390645; Fri, 21 Jul 2023 09:19:50 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:50 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 15/23] net/tcp: Add tcp_hash_fail() ratelimited logs Date: Fri, 21 Jul 2023 17:19:06 +0100 Message-ID: <20230721161916.542667-16-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050345266823771 X-GMAIL-MSGID: 1772050345266823771 Add a helper for logging connection-detailed messages for failed TCP hash verification (both MD5 and AO). Co-developed-by: Francesco Ruggeri Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/tcp.h | 14 ++++++++++++-- include/net/tcp_ao.h | 29 +++++++++++++++++++++++++++++ net/ipv4/tcp.c | 23 +++++++++++++---------- net/ipv4/tcp_ao.c | 7 +++++++ 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index a58f95237ea3..50636ee7ee2f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2641,12 +2641,18 @@ 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) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); + 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; } } @@ -2663,10 +2669,14 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req, * the last key is impossible to remove, so there's * always at least one current_key. */ - if (tcp_ao_required(sk, saddr, family, true)) + if (tcp_ao_required(sk, saddr, family, true)) { + tcp_hash_fail("AO hash is required, but not found", + family, skb, "L3 index %d", l3index); return SKB_DROP_REASON_TCP_AONOTFOUND; + } 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 fc5674763730..801dca405df7 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -119,6 +119,35 @@ struct tcp_ao_info { struct rcu_head rcu; }; +#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 (f != hdr_flags) \ + *f = ' '; \ + 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 d7e18c528c7b..37d3eac0561b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4380,7 +4380,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); const struct tcp_sock *tp = tcp_sk(sk); struct tcp_md5sig_key *key; int genhash; @@ -4390,6 +4389,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; } @@ -4405,16 +4405,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 5ebbfadfbfd0..9ee7fc9f6a76 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -772,6 +772,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; } @@ -786,6 +788,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, ""); kfree(hash_buf); return SKB_DROP_REASON_TCP_AOFAILURE; } @@ -813,6 +816,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; } @@ -915,6 +920,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; } From patchwork Fri Jul 21 16:19:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123961 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp332180vqg; Fri, 21 Jul 2023 09:44:36 -0700 (PDT) X-Google-Smtp-Source: APBJJlFljF7rT9cWdfoyg0ySboGRQsazzRLgymx9pcvESCEn0l99DtgVWWXNcW78HviWpiD6uqyG X-Received: by 2002:a05:6a20:8e1b:b0:138:3302:1471 with SMTP id y27-20020a056a208e1b00b0013833021471mr2279890pzj.6.1689957875764; Fri, 21 Jul 2023 09:44:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689957875; cv=none; d=google.com; s=arc-20160816; b=O9p2A65Wgwhi1ub1fU7VAZafpDfKhXEp0wUPUj/M3NbpMAtgbR0kqfJM4meW++0Z8i OhWaRmYdCy2jprRuiKlEvDc2s0eSjFUhcAs3Y9BgwIe11T6iEZ9H1QV6N7D5me3ohVmZ kXYtzoECiG4eoCYxILoWqP30eI2Yb6u2fdkdCylXJ3A1x2sNCVUYiQYEdhV/iiGeOkJE 3pKobZC7C8bp7dtEEkizTjP7lN9+at7c2WtVlquA1Is1ctVs5F/6muqpKwaoE028YH2x hzWgIMsPyQXP6N5tJ8RJcgdrXUOvOXc+WOEYp9rq3Cf3eC8MfxI9yvGQvoUMMrI1fPPo hW2A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=nqlcArJ7pr2Ye0DP2pmrrt4BrDW6PuUnROz8GJV0xrw=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=y4nsd4WjMc0GbTzsCIKcfVlJ6fhhIoLzNrDzbca19DdyLBJvb6r/oTR0Kl8i2qRlXc SrWqiWFyeQPA4YFFg8UBa/Tzrn9xiTSg6O+n5Pz2tdjhKlbD0eXMc+r5WSV9wfpAbQM/ 6R941GutGk/mEm4lZmMM5Ft+Zo2eXapZjgPw6HGjj5z7N5IstMqbymuPlCr/NQVNOjsZ tRQq87jX8W6NRflhLyuASd32Sj0k1q4l/RiJzODKv9rfaMUJdb6PZK2uXMz7TgbPH19g 1kYay30fZJSOKWQZAc6YuKQ7RyMAt9V1V2FbPBPCpb2PtKBM2RgD85WHrs/bTiTBE0P/ o2UA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=ECngbeNJ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id bt19-20020a056a00439300b00682acfc5dacsi3375089pfb.53.2023.07.21.09.44.22; Fri, 21 Jul 2023 09:44:35 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=ECngbeNJ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232721AbjGUQX0 (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51894 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232833AbjGUQWV (ORCPT ); Fri, 21 Jul 2023 12:22:21 -0400 Received: from mail-lj1-x233.google.com (mail-lj1-x233.google.com [IPv6:2a00:1450:4864:20::233]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9920349F5 for ; Fri, 21 Jul 2023 09:19:54 -0700 (PDT) Received: by mail-lj1-x233.google.com with SMTP id 38308e7fff4ca-2b9540031acso31104151fa.3 for ; Fri, 21 Jul 2023 09:19:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956392; x=1690561192; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=nqlcArJ7pr2Ye0DP2pmrrt4BrDW6PuUnROz8GJV0xrw=; b=ECngbeNJGAlUdg1OVrFX6nW1z2/RkE3nyrsx7iWHHrGsko5u9q/5s8CwxyrL7WmvuJ uy8hDIrgaJzVfMW3Iv5p2jysqSHkpW6uprJ67/NFCvkGihVhtUMY2C1MiDdUpG7Y8NVG d0brgRvGrVjjBjtaru1KkUoW4TZAdr2pz9LHk40gScZSa5tBzHNdN0oqP8EMGY8MMAQh eScfXDNSFCw2GYd3n6LWifcNU7DrvCBm7RuslsMjBQ40OkkdIKhMNLxd0sRT1DJu3Npp KDSixxNDckxFgPgg2CZxJ+7t7H4XiEGavbl0lpAeGeUPa7znzmPWpxPKoMDIxPCCSzJ4 uIkQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956392; x=1690561192; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=nqlcArJ7pr2Ye0DP2pmrrt4BrDW6PuUnROz8GJV0xrw=; b=klXmk3d/vNVwFiBYoXUEuh9W9sAc0Dsq1C/UOsBzenLVoRgydXEL1SyXeE8Auhhz/H NQrs/auKYoBqSn9f49teY0xTStW2agjlXq+3LwRlxmjGCvXoqVIrfZf6U8yYAsliKo/x LiM922T23NlpcZqRhbOAw5N9sHnEnZzfTfC/nJqeKVdJ1qiR2oIN5o4ZoavrG9RoGiYI SZLTWWmae9/Q6i6Co+kVZTZKTLQrPnY32EvmFT49E2bUy7osPNH1SnovK0fxFB99Pdvr YN/Gw21JIylbe8TbH8jM5eBvHPpCJ15xSHUXgrzBhwUm+DzMAxGkuObVEKx952LVw8b8 E6Ig== X-Gm-Message-State: ABy/qLZVIS8WCFkDX/eQJp5jClbtPMJmh7g9YxG6jWHwjoNvvrZlb+9j RQafPAx2XD29wAZu0rzNUyTtbQ== X-Received: by 2002:a2e:b055:0:b0:2b6:d495:9467 with SMTP id d21-20020a2eb055000000b002b6d4959467mr1908708ljl.6.1689956392300; Fri, 21 Jul 2023 09:19:52 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:51 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 16/23] net/tcp: Ignore specific ICMPs for TCP-AO connections Date: Fri, 21 Jul 2023 17:19:07 +0100 Message-ID: <20230721161916.542667-17-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772049269626642807 X-GMAIL-MSGID: 1772049269626642807 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/tcp_ao.h | 10 ++++++- include/uapi/linux/snmp.h | 1 + include/uapi/linux/tcp.h | 4 ++- net/ipv4/proc.c | 1 + net/ipv4/tcp_ao.c | 61 +++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 5 ++++ net/ipv6/tcp_ipv6.c | 4 +++ 7 files changed, 84 insertions(+), 2 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 801dca405df7..cb16af2a9f39 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -24,6 +24,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 { @@ -93,7 +94,8 @@ struct tcp_ao_info { struct tcp_ao_key *rnext_key; struct tcp_ao_counters counters; u32 ao_required :1, - __unused :31; + accept_icmps :1, + __unused :30; __be32 lisn; __be32 risn; /* Sequence Number Extension (SNE) are upper 4 bytes for SEQ, @@ -188,6 +190,7 @@ 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_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(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, @@ -266,6 +269,11 @@ static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, { } +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 06ddf4cd295c..47a6b47da66f 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -300,6 +300,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 3fe0612ec59a..ca7ed18ce67b 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -392,7 +392,8 @@ struct tcp_ao_info_opt { /* setsockopt(TCP_AO_INFO) */ set_rnext :1, /* corresponding ::rnext */ ao_required :1, /* don't accept non-AO connects */ set_counters :1, /* set/clear ::pkt_* counters */ - reserved :28; /* must be 0 */ + accept_icmps :1, /* accept incoming ICMPs */ + reserved :27; /* must be 0 */ __u16 reserved2; /* padding, must be 0 */ __u8 current_key; /* KeyID to set as Current_key */ __u8 rnext; /* KeyID to set as Rnext_key */ @@ -400,6 +401,7 @@ struct tcp_ao_info_opt { /* setsockopt(TCP_AO_INFO) */ __u64 pkt_bad; /* failed verification */ __u64 pkt_key_not_found; /* could not find a key to verify */ __u64 pkt_ao_required; /* segments missing TCP-AO sign */ + __u64 pkt_dropped_icmp; /* ICMPs that were ignored */ } __attribute__((aligned(8))); /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 3f643cd29cfe..5d3c9c96773e 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -302,6 +302,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 9ee7fc9f6a76..4bde58ede63a 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -15,6 +15,7 @@ #include #include +#include int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, unsigned int len, struct tcp_sigpool *hp) @@ -44,6 +45,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->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; +} + /* Optimized version of tcp_ao_do_lookup(): only for sockets for which * it's known that the keys in ao_info are matching peer's * family/address/port/VRF/etc. @@ -1044,6 +1102,7 @@ int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, new_ao->lisn = htonl(tcp_rsk(req)->snt_isn); new_ao->risn = htonl(tcp_rsk(req)->rcv_isn); new_ao->ao_required = ao->ao_required; + new_ao->accept_icmps = ao->accept_icmps; if (family == AF_INET) { addr = (union tcp_ao_addr *)&newsk->sk_daddr; @@ -1764,9 +1823,11 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, atomic64_set(&ao_info->counters.pkt_bad, cmd.pkt_bad); atomic64_set(&ao_info->counters.key_not_found, cmd.pkt_key_not_found); atomic64_set(&ao_info->counters.ao_required, cmd.pkt_ao_required); + atomic64_set(&ao_info->counters.dropped_icmp, cmd.pkt_dropped_icmp); } ao_info->ao_required = cmd.ao_required; + ao_info->accept_icmps = cmd.accept_icmps; if (new_current) WRITE_ONCE(ao_info->current_key, new_current); if (new_rnext) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 37497dec5eb9..054897a13575 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -494,6 +494,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; } @@ -508,6 +510,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 873ce0d72f34..9a427c0d48ab 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -395,6 +395,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; } @@ -406,6 +408,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); From patchwork Fri Jul 21 16:19:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123957 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp320234vqg; Fri, 21 Jul 2023 09:24:10 -0700 (PDT) X-Google-Smtp-Source: APBJJlEjGKt3A8Vrx0nOi5fSbwaMl6wKcsrN5rUhx1ReCyE/EZ/w3cJ4DZ9ewQLkCRVcfAZR6iHH X-Received: by 2002:a05:6a00:23d4:b0:686:2b60:3348 with SMTP id g20-20020a056a0023d400b006862b603348mr607611pfc.21.1689956650116; Fri, 21 Jul 2023 09:24:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689956650; cv=none; d=google.com; s=arc-20160816; b=zvyhPunRfZjSOWsMDv5zriNiakueWjKsQ8ESrJ24awHUE010/22CwjE+M4+NJH9Qvh nFCAQJv8HW0i9YuM4ETqLEJMcpSX1ZhvyYxsUqSBFaS1XqEQ53eTHNgUaasEMTUPh1Ai Nik+z3Mz7d/RpyLYGotRQWCUXStX9rhWBDEfYRb9CzrJwk2+s7wSdlSmnOXkw55XcWaD DMMRoR3rYAU31D8QtOJpJz8YxNiNSAxT22MmWJpPpr+uFOmLNVM9CbFSlYfaw63PW1M4 s+gl5ETvvd8kDnnY4kXWdlAU5/5l7jmgLUGBeosCTFyZpXjvE1g7CyMPHvHcwabXxXol YG1g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=g//yS07dhHSKeyyRj/J57DFtZtZ+cyyolllpbeSyUP8=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=ww2aOmyJ76l3kyZTg+UZy3GRhoaMr/LUsnhAqEamFDB0sOHaLuwXU/QlbJXvQBt63p aAENNRMzfevRJnxeyQQd2eJLsq6sfL9P56LDtD1lVrb4B+ygdA4v8jzPUx+pcHa1lfyI LcEIwqMJaSo3/pKiKrZgXvnIkNsa2G7LrMyyedcmTswTfSUGznCYBq+p/tcHFiUOVEDu waGcMh+w2AemvWBM+480ZNfobf91FRXwHKnz3lk+BFxgRP2RedeRSRZGkkOO/hj5kqq3 gsO/EjtfH2cgkgHgyna3+FIkBmjkLDlmsbIM6FIbNs4OnZDQ7A1gJyTvCpa5WDBV7tEL tvCA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=ZnvJBCKf; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id 189-20020a6300c6000000b0055c7eb53d7fsi3265346pga.584.2023.07.21.09.23.52; Fri, 21 Jul 2023 09:24:10 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=ZnvJBCKf; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232685AbjGUQXW (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51898 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232834AbjGUQWW (ORCPT ); Fri, 21 Jul 2023 12:22:22 -0400 Received: from mail-wm1-x335.google.com (mail-wm1-x335.google.com [IPv6:2a00:1450:4864:20::335]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BA1CE49F9 for ; Fri, 21 Jul 2023 09:19:55 -0700 (PDT) Received: by mail-wm1-x335.google.com with SMTP id 5b1f17b1804b1-3fbf1b82d9cso18100735e9.2 for ; Fri, 21 Jul 2023 09:19:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956394; x=1690561194; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=g//yS07dhHSKeyyRj/J57DFtZtZ+cyyolllpbeSyUP8=; b=ZnvJBCKf/XBPcknxvEkwsWOiYNqQVNW6wZjPvODq1UjOCTFa/MdrgmmSudBm/I4yFi mcwK9mTJHJ17zWHgTpShPm/HJtlrf1OMEWZqh/MwfrlukLA91g8w5+bVHhF7l/yg9Exf 5idY65emZz73nfGpvUE4MtcazUgWUdkM0e2HmQrBwi5ICs8FZKNvZwb3fE28VboYPK4/ kCPIHMt4WfEB44fsaWeXpeZMhpDjZuh46BtW8ryqS0K4IR45aBE2P6mAlD9Pq6WU1UEH S2j3xsiav3/iORYbzVt5o7Nx2x80F6C6oFXn5wVP4MkZY/3KB9I2qCNcxQuThZknVLBJ wuWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956394; x=1690561194; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=g//yS07dhHSKeyyRj/J57DFtZtZ+cyyolllpbeSyUP8=; b=KyFGGT7kDa1NAtPchd11lATqyA45KzPZ9KH5nKs5I6zlJxb+DYSDPkimyH4qeuiXF+ SJOmDqZo+vXDELeSg0rY200OJEZdsIrkYTcqByuDl/fepK7iEATGppTcpAJVs0W4uzvw 2CpkOZ115QSR+O9Botyqs24qgs/Dgf0wWa+GzwqlJqwIouux1z8IrputglmpsyiVuZjs 4y6vdrC3GnpEPcxio2G4qae4hDuca/lmc+uydhTrpS5urR272zV0hzkxaAk+nGLyuGbC eaEI1w5D5rNso4AeSR4oUJqCzZ4s51meK/I7IQmqlQkI1PNkzh9cKDXwao7w+7mY0unr w6hg== X-Gm-Message-State: ABy/qLZ1WrbJCPRq4ycO+KxFTbwBQt/ZMcyP7vT0mXJY4pdkyCTYxZMJ stfgskNP3b9Lc1/KhKOUQwU7DQ== X-Received: by 2002:a05:600c:2158:b0:3fc:2dfb:3cd3 with SMTP id v24-20020a05600c215800b003fc2dfb3cd3mr1707117wml.41.1689956393936; Fri, 21 Jul 2023 09:19:53 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:53 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 17/23] net/tcp: Add option for TCP-AO to (not) hash header Date: Fri, 21 Jul 2023 17:19:08 +0100 Message-ID: <20230721161916.542667-18-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772047984247656828 X-GMAIL-MSGID: 1772047984247656828 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/uapi/linux/tcp.h | 5 +++++ net/ipv4/tcp_ao.c | 8 +++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index ca7ed18ce67b..3275ade3293a 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -354,6 +354,11 @@ struct tcp_diag_md5sig { #define TCP_AO_MAXKEYLEN 80 #define TCP_AO_KEYF_IFINDEX (1 << 0) /* L3 ifindex for VRF */ +#define TCP_AO_KEYF_EXCLUDE_OPT (1 << 1) /* "Indicates whether TCP + * options other than TCP-AO + * are included in the MAC + * calculation" + */ struct tcp_ao_add { /* setsockopt(TCP_AO_ADD_KEY) */ struct __kernel_sockaddr_storage addr; /* peer's address for the key */ diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 4bde58ede63a..4fabd3ec5c71 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -567,7 +567,8 @@ int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash, WARN_ON_ONCE(1); 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, hash_buf, 0); @@ -615,7 +616,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_sigpool_hash_skb_data(&hp, skb, th->doff << 2)) @@ -1430,7 +1432,7 @@ static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) return ERR_PTR(-ESOCKTNOSUPPORT); } -#define TCP_AO_KEYF_ALL (0) +#define TCP_AO_KEYF_ALL (TCP_AO_KEYF_EXCLUDE_OPT) static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk, struct tcp_ao_add *cmd) From patchwork Fri Jul 21 16:19:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123966 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp337311vqg; Fri, 21 Jul 2023 09:54:26 -0700 (PDT) X-Google-Smtp-Source: APBJJlHkyMeMYLzB8eVZDgzmznWE8QVsQkKnjd/OTub0OMT0RtYSSv9fQsm74c78nuRss9qVl+VR X-Received: by 2002:a05:6a20:1614:b0:131:54e4:4a30 with SMTP id l20-20020a056a20161400b0013154e44a30mr2439027pzj.40.1689958465873; Fri, 21 Jul 2023 09:54:25 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689958465; cv=none; d=google.com; s=arc-20160816; b=aRTDarBlibAAOBD4UHOVXn9NvWdbeeVvEcAWa57TY/MyaHwjByR9Dxmt/rtKhv95SS PvqrLMGH0sF5F0CECsVyTh/euJRiJc7nBbtgbmco2/DPZzAblP/yEkPjOJ7HGgqUNJkf hO7KjULGz+ZP4ZUHqK3Vp3ZvU6ZZpchO9lpWvioSaEMY08aqjBwi2vGc+BWThrVQ/3U7 w5AZQhNdQzhVkL6tzzBvLXG7Y4C5kbAjnk2MC5U8w7x8nK0ACpaabczvUPtyQCja3cg7 P4RUdYYB4fphbDJ3CGudznLwfQI2HdTSpfyYBud59djU1GLm+wMKUcU+iiOMH4DtGF7z PlnA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=we5h9E6lap4IThbQuQDO0anrlyaKhDltxrHY4kimr30=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=WrRJHcWUyceUxb7qCP/N37Og8JU1ijDPtsxA63ebSJqeJtBCA9RRzKirtXvWPlLkZD q8C04ucMn66cWzIYwhT6MWrG+RIwega8NdnXFpr6/ksuh9DqLvf+UoHU8TBPyj/hU3RS t3t33MSxz9aVJB7QJo4Ztzh09bJJBCZh1TpRhUcVYcBrbEdk9QPxNa8nQsC5gyHajrmP XtB5orXqtpBVzDk+vqyf/xI753GCjdKdvbDRRCZicYmnB6YmSBlleJklMqtaV0+ZGzre DJfIfFx7t6uVlnH0iGic1kcS6mBCwi6DkM3UxBmqnS6+26uBu1Pbj9rLC1uJkQvj6jfp EPfA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=kyn9JmgZ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id ks7-20020a170903084700b001b9dc05ec1esi3187872plb.291.2023.07.21.09.54.11; Fri, 21 Jul 2023 09:54:25 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=kyn9JmgZ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232781AbjGUQX2 (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51260 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232734AbjGUQWX (ORCPT ); Fri, 21 Jul 2023 12:22:23 -0400 Received: from mail-wm1-x32a.google.com (mail-wm1-x32a.google.com [IPv6:2a00:1450:4864:20::32a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 181C13AA2 for ; Fri, 21 Jul 2023 09:19:58 -0700 (PDT) Received: by mail-wm1-x32a.google.com with SMTP id 5b1f17b1804b1-3fb4146e8fcso13641615e9.0 for ; Fri, 21 Jul 2023 09:19:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956396; x=1690561196; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=we5h9E6lap4IThbQuQDO0anrlyaKhDltxrHY4kimr30=; b=kyn9JmgZ90K50uEhbKDCNSBifRpja8aFl+3P+uTaldjGDO/R24BXwprZcDe5ATYPad aVOLQU4pBTOwhvnTYC1YgVXCPGDQLQTYx5cdcLr/BbrG6vSgo5EYaV1lemCv34qmczpq v8qvD/6MfbMcPKMOpL5tVlwS7M0iEbMQHnuHuBfNSjXZUYN3g3aOqA7H8aCaxr0Bnr3/ cWBRVJu2XkmyeFLqZWooN0xag/Z9Ql5qbphc+LcFxKaPdA/Mtgh73xJD/Cezbbf8dkS4 T4IvOKPBGEe0zsPmj6v1K02gE8Uxg0e4FrCnoAI6RU2jnRn7YnUqMl4xiv/XujyqMXMe XZZg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956396; x=1690561196; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=we5h9E6lap4IThbQuQDO0anrlyaKhDltxrHY4kimr30=; b=H5jSf+D3l5JBXt5k4rjaYfoQUojvpOeGJfAX/OHQtv6ubLfegWAsNmkc+du02FddMS QOghLoOm2jMzfj8xpoPj9efXG5hazWqLzluFmF4CVgbodsfrchVS2OjMjkAJEoa5SZL+ vys2MypcRa43DFYeJWMiO37En6P6PByfD3htYoPaIli8oBKf5Qwn6eFCcfdot9jHPT0B QlJNG3drPd5309T3mt3WT0wbMolsDOwLKwc6J4Z/9kYdWWFxMMuyqUj9i0XrGhzvlrEI GTjrmUX63Eo2oal90mxOcvlR2xi2IaRN7fL9zQq0wjviq/IVp39JvEX3y5UZdr4mDkgP JelQ== X-Gm-Message-State: ABy/qLYCjypprYqp6y0OcKvDk9p4oGQzUt2f51fQ1usw4ZbOvVK63UjQ FHC5KKAKvwjnKAOWa2HehFbKoA== X-Received: by 2002:a7b:c8d7:0:b0:3fc:180:6463 with SMTP id f23-20020a7bc8d7000000b003fc01806463mr2123758wml.7.1689956395803; Fri, 21 Jul 2023 09:19:55 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:55 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 18/23] net/tcp: Add TCP-AO getsockopt()s Date: Fri, 21 Jul 2023 17:19:09 +0100 Message-ID: <20230721161916.542667-19-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772049888284587591 X-GMAIL-MSGID: 1772049888284587591 Introduce getsockopt(TCP_AO_GET_KEYS) that lets a user get TCP-AO keys and their properties from a socket. The user can provide a filter to match the specific key to be dumped or ::get_all = 1 may be used to dump all keys in one syscall. Add another getsockopt(TCP_AO_INFO) for providing per-socket/per-ao_info stats: packet counters, Current_key/RNext_key and flags like ::ao_required and ::accept_icmps. Co-developed-by: Francesco Ruggeri Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/net/tcp_ao.h | 12 ++ include/uapi/linux/tcp.h | 63 +++++++-- net/ipv4/tcp.c | 13 ++ net/ipv4/tcp_ao.c | 295 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 369 insertions(+), 14 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index cb16af2a9f39..9209343878e1 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -191,6 +191,8 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, 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(struct sock *sk, int type, int code); +int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen); +int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen); 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, @@ -304,6 +306,16 @@ static inline void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, static inline void tcp_ao_connect_init(struct sock *sk) { } + +static inline int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen) +{ + return -ENOPROTOOPT; +} + +static inline int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen) +{ + return -ENOPROTOOPT; +} #endif #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 3275ade3293a..1109093bbb24 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -131,7 +131,8 @@ enum { #define TCP_AO_ADD_KEY 38 /* Add/Set MKT */ #define TCP_AO_DEL_KEY 39 /* Delete MKT */ -#define TCP_AO_INFO 40 /* Modify TCP-AO per-socket options */ +#define TCP_AO_INFO 40 /* Set/list TCP-AO per-socket options */ +#define TCP_AO_GET_KEYS 41 /* List MKT(s) */ #define TCP_REPAIR_ON 1 #define TCP_REPAIR_OFF 0 @@ -392,21 +393,55 @@ struct tcp_ao_del { /* setsockopt(TCP_AO_DEL_KEY) */ __u8 keyflags; /* see TCP_AO_KEYF_ */ } __attribute__((aligned(8))); -struct tcp_ao_info_opt { /* setsockopt(TCP_AO_INFO) */ - __u32 set_current :1, /* corresponding ::current_key */ - set_rnext :1, /* corresponding ::rnext */ - ao_required :1, /* don't accept non-AO connects */ - set_counters :1, /* set/clear ::pkt_* counters */ - accept_icmps :1, /* accept incoming ICMPs */ +struct tcp_ao_info_opt { /* setsockopt(TCP_AO_INFO), getsockopt(TCP_AO_INFO) */ + /* Here 'in' is for setsockopt(), 'out' is for getsockopt() */ + __u32 set_current :1, /* in/out: corresponding ::current_key */ + set_rnext :1, /* in/out: corresponding ::rnext */ + ao_required :1, /* in/out: don't accept non-AO connects */ + set_counters :1, /* in: set/clear ::pkt_* counters */ + accept_icmps :1, /* in/out: accept incoming ICMPs */ reserved :27; /* must be 0 */ __u16 reserved2; /* padding, must be 0 */ - __u8 current_key; /* KeyID to set as Current_key */ - __u8 rnext; /* KeyID to set as Rnext_key */ - __u64 pkt_good; /* verified segments */ - __u64 pkt_bad; /* failed verification */ - __u64 pkt_key_not_found; /* could not find a key to verify */ - __u64 pkt_ao_required; /* segments missing TCP-AO sign */ - __u64 pkt_dropped_icmp; /* ICMPs that were ignored */ + __u8 current_key; /* in/out: KeyID of Current_key */ + __u8 rnext; /* in/out: keyid of RNext_key */ + __u64 pkt_good; /* in/out: verified segments */ + __u64 pkt_bad; /* in/out: failed verification */ + __u64 pkt_key_not_found; /* in/out: could not find a key to verify */ + __u64 pkt_ao_required; /* in/out: segments missing TCP-AO sign */ + __u64 pkt_dropped_icmp; /* in/out: ICMPs that were ignored */ +} __attribute__((aligned(8))); + +struct tcp_ao_getsockopt { /* getsockopt(TCP_AO_GET_KEYS) */ + struct __kernel_sockaddr_storage addr; /* in/out: dump keys for peer + * with this address/prefix + */ + char alg_name[64]; /* out: crypto hash algorithm */ + __u8 key[TCP_AO_MAXKEYLEN]; + __u32 nkeys; /* in: size of the userspace buffer + * @optval, measured in @optlen - the + * sizeof(struct tcp_ao_getsockopt) + * out: number of keys that matched + */ + __u16 is_current :1, /* in: match and dump Current_key, + * out: the dumped key is Current_key + */ + + is_rnext :1, /* in: match and dump RNext_key, + * out: the dumped key is RNext_key + */ + get_all :1, /* in: dump all keys */ + reserved :13; /* padding, must be 0 */ + __u8 sndid; /* in/out: dump keys with SendID */ + __u8 rcvid; /* in/out: dump keys with RecvID */ + __u8 prefix; /* in/out: dump keys with address/prefix */ + __u8 maclen; /* out: key's length of authentication + * code (hash) + */ + __u8 keyflags; /* in/out: see TCP_AO_KEYF_ */ + __u8 keylen; /* out: length of ::key */ + __s32 ifindex; /* in/out: L3 dev index for VRF */ + __u64 pkt_good; /* out: verified segments */ + __u64 pkt_bad; /* out: segments that failed verification */ } __attribute__((aligned(8))); /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 37d3eac0561b..82506300da6c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4289,6 +4289,19 @@ int do_tcp_getsockopt(struct sock *sk, int level, return err; } #endif + case TCP_AO_GET_KEYS: + case TCP_AO_INFO: { + int err; + + sockopt_lock_sock(sk); + if (optname == TCP_AO_GET_KEYS) + err = tcp_ao_get_mkts(sk, optval, optlen); + else + err = tcp_ao_get_sock_info(sk, optval, optlen); + sockopt_release_sock(sk); + + return err; + } default: return -ENOPROTOOPT; } diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 4fabd3ec5c71..faa6c5c0db28 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -1869,3 +1869,298 @@ 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_KEYS) 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_getsockopt::get_all is set, then all keys in the socket are + * returned, otherwise only keys matching + * 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). + */ +static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, + sockptr_t optval, sockptr_t optlen) +{ + struct tcp_ao_getsockopt opt_in, opt_out; + struct tcp_ao_key *key, *current_key; + bool do_address_matching = true; + union tcp_ao_addr *addr = NULL; + unsigned int max_keys; /* maximum number of keys to copy to user */ + size_t out_offset = 0; + size_t bytes_to_write; /* number of bytes to write to user level */ + int err, user_len; + u32 matched_keys; /* keys from ao_info matched so far */ + int optlen_out; + u16 port = 0; + + if (copy_from_sockptr(&user_len, optlen, sizeof(int))) + return -EFAULT; + + if (user_len <= 0) + return -EINVAL; + + memset(&opt_in, 0, sizeof(struct tcp_ao_getsockopt)); + err = copy_struct_from_sockptr(&opt_in, sizeof(opt_in), + optval, user_len); + if (err < 0) + return err; + + if (opt_in.pkt_good || opt_in.pkt_bad) + return -EINVAL; + + if (opt_in.reserved != 0) + return -EINVAL; + + max_keys = opt_in.nkeys; + + if (opt_in.get_all || opt_in.is_current || opt_in.is_rnext) { + if (opt_in.get_all && (opt_in.is_current || opt_in.is_rnext)) + return -EINVAL; + do_address_matching = false; + } + + switch (opt_in.addr.ss_family) { + case AF_INET: { + struct sockaddr_in *sin; + __be32 mask; + + sin = (struct sockaddr_in *)&opt_in.addr; + port = sin->sin_port; + addr = (union tcp_ao_addr *)&sin->sin_addr; + + if (opt_in.prefix > 32) + return -EINVAL; + + if (sin->sin_addr.s_addr == INADDR_ANY && + opt_in.prefix != 0) + return -EINVAL; + + mask = inet_make_mask(opt_in.prefix); + if (sin->sin_addr.s_addr & ~mask) + return -EINVAL; + + break; + } + case AF_INET6: { + struct sockaddr_in6 *sin6; + struct in6_addr *addr6; + + sin6 = (struct sockaddr_in6 *)&opt_in.addr; + addr = (union tcp_ao_addr *)&sin6->sin6_addr; + addr6 = &sin6->sin6_addr; + port = sin6->sin6_port; + + /* We don't have to change family and @addr here if + * ipv6_addr_v4mapped() like in key adding: + * tcp_ao_key_cmp() does it. Do the sanity checks though. + */ + if (opt_in.prefix != 0) { + if (ipv6_addr_v4mapped(addr6)) { + __be32 mask, addr4 = addr6->s6_addr32[3]; + + if (opt_in.prefix > 32 || + addr4 == INADDR_ANY) + return -EINVAL; + mask = inet_make_mask(opt_in.prefix); + if (addr4 & ~mask) + return -EINVAL; + } else { + struct in6_addr pfx; + + if (ipv6_addr_any(addr6) || + opt_in.prefix > 128) + return -EINVAL; + + ipv6_addr_prefix(&pfx, addr6, opt_in.prefix); + if (ipv6_addr_cmp(&pfx, addr6)) + return -EINVAL; + } + } else if (!ipv6_addr_any(addr6)) { + return -EINVAL; + } + break; + } + case 0: + if (!do_address_matching) + break; + fallthrough; + default: + return -EAFNOSUPPORT; + } + + if (!do_address_matching) { + /* We could just ignore those, but let's do stricter checks */ + if (addr || port) + return -EINVAL; + if (opt_in.prefix || opt_in.sndid || opt_in.rcvid) + return -EINVAL; + } + + bytes_to_write = min_t(int, user_len, sizeof(struct tcp_ao_getsockopt)); + matched_keys = 0; + /* May change in RX, while we're dumping, pre-fetch it */ + current_key = READ_ONCE(ao_info->current_key); + + hlist_for_each_entry_rcu(key, &ao_info->head, node) { + if (opt_in.get_all) + goto match; + + if (opt_in.is_current || opt_in.is_rnext) { + if (opt_in.is_current && key == current_key) + goto match; + if (opt_in.is_rnext && 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 (matched_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.is_current = (key == current_key); + opt_out.is_rnext = (key == ao_info->rnext_key); + opt_out.nkeys = 0; + opt_out.maclen = key->maclen; + opt_out.keylen = key->keylen; + 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); + + /* Copy key to user */ + if (copy_to_sockptr_offset(optval, out_offset, + &opt_out, bytes_to_write)) + return -EFAULT; + out_offset += user_len; + } + + optlen_out = (int)sizeof(struct tcp_ao_getsockopt); + if (copy_to_sockptr(optlen, &optlen_out, sizeof(int))) + return -EFAULT; + + out_offset = offsetof(struct tcp_ao_getsockopt, nkeys); + if (copy_to_sockptr_offset(optval, out_offset, + &matched_keys, sizeof(u32))) + return -EFAULT; + + return 0; +} + +int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen) +{ + struct tcp_ao_info *ao_info; + + ao_info = setsockopt_ao_info(sk); + if (IS_ERR(ao_info)) + return PTR_ERR(ao_info); + if (!ao_info) + return -ENOENT; + + return tcp_ao_copy_mkts_to_user(ao_info, optval, optlen); +} + +int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen) +{ + struct tcp_ao_info_opt out, in = {}; + struct tcp_ao_key *current_key; + struct tcp_ao_info *ao; + int err, len; + + if (copy_from_sockptr(&len, optlen, sizeof(int))) + return -EFAULT; + + if (len <= 0) + return -EINVAL; + + /* Copying this "in" only to check ::reserved, ::reserved2, + * that may be needed to extend (struct tcp_ao_info_opt) and + * what getsockopt() provides in future. + */ + err = copy_struct_from_sockptr(&in, sizeof(in), optval, len); + if (err) + return err; + + if (in.reserved != 0 || in.reserved2 != 0) + return -EINVAL; + + ao = setsockopt_ao_info(sk); + if (IS_ERR(ao)) + return PTR_ERR(ao); + if (!ao) + return -ENOENT; + + memset(&out, 0, sizeof(out)); + out.ao_required = ao->ao_required; + out.accept_icmps = ao->accept_icmps; + out.pkt_good = atomic64_read(&ao->counters.pkt_good); + out.pkt_bad = atomic64_read(&ao->counters.pkt_bad); + out.pkt_key_not_found = atomic64_read(&ao->counters.key_not_found); + out.pkt_ao_required = atomic64_read(&ao->counters.ao_required); + out.pkt_dropped_icmp = atomic64_read(&ao->counters.dropped_icmp); + + current_key = READ_ONCE(ao->current_key); + if (current_key) { + out.set_current = 1; + out.current_key = current_key->sndid; + } + if (ao->rnext_key) { + out.set_rnext = 1; + out.rnext = ao->rnext_key->rcvid; + } + + if (copy_to_sockptr(optval, &out, min_t(int, len, sizeof(out)))) + return -EFAULT; + + return 0; +} + From patchwork Fri Jul 21 16:19:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 124005 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp345653vqg; Fri, 21 Jul 2023 10:06:55 -0700 (PDT) X-Google-Smtp-Source: APBJJlGWy2kO3uINOCpLd8sY6cEETkbXolfotTjNF+gwA9Kos3YbEkIh57KIZQom8ZOTMXBQ7Svf X-Received: by 2002:a17:902:d342:b0:1b8:9b66:8532 with SMTP id l2-20020a170902d34200b001b89b668532mr2458803plk.52.1689959215289; Fri, 21 Jul 2023 10:06:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689959215; cv=none; d=google.com; s=arc-20160816; b=IfmLgO8VMngfKIK88GDFxEnYVYv5AnNoQQ3Qo+sX2LNbPiNO+zWHAKQXqjb8KmhVmo CUssKgBWYgmQFbhVW4X9P1j+HSjliVBzW66cVmNjvckH4xWT9/og7B8qKkhYxs7L/yeY IUJ+Zu5zld6BPfS5gt/NZI3MetmG9OHPue7z8z9+t0RMEEu2vEXF1dNKOhY82wYicfgZ SgAbQIW8ySft6ShUN7fjFeJPM0IJj0k+/2aKYiLjflAgEyO/Le0UVjFKC5+XXmy6rRFw aBNzWD4VP5HmfZ0FtatFotceBRLjdd5Hk/ag21XEiI5NmZm/VHFYHUJ6P5WGe2400hdS nQmg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=1/W9xFvX6fMqz70zcOle30rc8tQkMB+vSAf+sEu95vI=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=PWQ6V53BQAzK0Bhyo0o/xNR4Y5FzUTBkCxFmyVIiHl7sjVyKUGbewoMPb+yNAr/VAm /D3VXgQvHtgNwqhE/VWaVzfS+h1FLx+sBI/Gkkovxih7BSuzrzNEB+JBnK9+Tc6tUah1 aaKDjfTVwwMZzgrQhkDm3TVkQ0Bh2Z6DnTt+CemOUSDqX4DUIGRzq+p/bQ3zIKh6dOAF E2WPT3tnZZEBeUnzNNgsygU0j2dNEtgMYuh4I8FkVSgUrEZEpJmIbfERZs0wvutADP+E oaqPeXvSCh3sxnTAEX/X3OoBEzRHrUm+oBdu4zelLQhOyNG+B3ZOu2quI8rc/0WIDLjw nLxw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=NSQFnxyj; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id lg15-20020a170902fb8f00b001b85ab52371si3097391plb.428.2023.07.21.10.06.41; Fri, 21 Jul 2023 10:06:55 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=NSQFnxyj; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232786AbjGUQXd (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51378 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232759AbjGUQWs (ORCPT ); Fri, 21 Jul 2023 12:22:48 -0400 Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 689A23AB2 for ; Fri, 21 Jul 2023 09:20:02 -0700 (PDT) Received: by mail-wm1-x332.google.com with SMTP id 5b1f17b1804b1-3fba8e2aa52so18815405e9.1 for ; Fri, 21 Jul 2023 09:20:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956397; x=1690561197; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=1/W9xFvX6fMqz70zcOle30rc8tQkMB+vSAf+sEu95vI=; b=NSQFnxyjyLat4FyBBhcMXxwWB5o+a7ScVLtmstvpZoPfmZnio4OY3V5Gs3cTrGaz83 P4aKSRxDig7Lp/9KWX1phg8mswS6WDtDACtJTd68TysWBXrEzCzeL/06LTrLJ4aqImf5 GxUDfKYfB5keDYiESVf3AWoK3WLBoDMCjFhIt5JDZgBQGcbyQdroKh4NEZVgHQJupvL+ XTMadP0NzEI0CpCEgV5itGj+Kf9yCSk8NbhGiYn5kuDsLEMZ8ro9TVSgM/DYHtzVS9fU rb3bkewHojSlMbaKao0gyHImEgCz45O3Wy8nxTPR8kmmh5K+RMYBFNkY2NLx7xLnj9ZY gGFw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956397; x=1690561197; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=1/W9xFvX6fMqz70zcOle30rc8tQkMB+vSAf+sEu95vI=; b=Wz9dtQPidW5tU6Y3ldeNEoJGPF8oNvy9KME1GoU6Tj+8Mvlrvmsls102bgfiI22Qs5 XZV4DgU9ikT0voOfsITZwULe7MRS3TynlXMBrubPqQ6FuSRSnl98ZZJ7xNAb6BWZfg9I VQRHO0M7KspBmMq1tGebSSshbbOvtzkEhrRdhVLOYYGkk9rVtH2KnmBA2e1BAcTC2LiM 8W9AleB+ZwN+DGO8n7tqWpteq0F/GHMbyI/EKtvLo9qLOdd1HgYMISPSQ9xMPtxAV/jR NNOKSR2W7xPGIp5EcEuE2gEb+8PVdgwy6sL2LTfJs/WBUDwhWyMVk+CzHRbwLzke1cVu kwYQ== X-Gm-Message-State: ABy/qLZmBactHvggSlwDhzkGDe/4IZVRtIms0kl2ccZslCUaoleCZGNZ FDqJykJWm1+46p0r/+Ot1N/1ng== X-Received: by 2002:a1c:f615:0:b0:3fb:c15c:698a with SMTP id w21-20020a1cf615000000b003fbc15c698amr1926795wmc.4.1689956397541; Fri, 21 Jul 2023 09:19:57 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:57 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 19/23] net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs) Date: Fri, 21 Jul 2023 17:19:10 +0100 Message-ID: <20230721161916.542667-20-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050674431564738 X-GMAIL-MSGID: 1772050674431564738 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 Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- include/uapi/linux/tcp.h | 3 ++- net/ipv4/tcp_ao.c | 21 ++++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 1109093bbb24..979ff960fddb 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -383,7 +383,8 @@ struct tcp_ao_del { /* setsockopt(TCP_AO_DEL_KEY) */ __s32 ifindex; /* L3 dev index for VRF */ __u32 set_current :1, /* corresponding ::current_key */ set_rnext :1, /* corresponding ::rnext */ - reserved :30; /* must be 0 */ + del_async :1, /* only valid for listen sockets */ + reserved :29; /* must be 0 */ __u16 reserved2; /* padding, must be 0 */ __u8 prefix; /* peer's address prefix */ __u8 sndid; /* SendID for outgoing segments */ diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index faa6c5c0db28..567aa9400f7d 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -1606,7 +1606,7 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, } static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, - struct tcp_ao_key *key, + bool del_async, struct tcp_ao_key *key, struct tcp_ao_key *new_current, struct tcp_ao_key *new_rnext) { @@ -1614,11 +1614,24 @@ static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, 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 (del_async) { + 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(); if (new_current) @@ -1689,6 +1702,8 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, if (!new_rnext) return -ENOENT; } + if (cmd.del_async && sk->sk_state != TCP_LISTEN) + return -EINVAL; if (family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.addr; @@ -1733,8 +1748,8 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, if (key == new_current || key == new_rnext) continue; - return tcp_ao_delete_key(sk, ao_info, key, - new_current, new_rnext); + return tcp_ao_delete_key(sk, ao_info, cmd.del_async, key, + new_current, new_rnext); } return -ENOENT; } From patchwork Fri Jul 21 16:19:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123958 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp320550vqg; Fri, 21 Jul 2023 09:24:45 -0700 (PDT) X-Google-Smtp-Source: APBJJlEE16488RWvr4T5mwdqUzbMueo1fC5DoKwi3aA4wXFwzHW1BsRm4JbZz+/QeAJMysRoBLDS X-Received: by 2002:a05:6a20:4d8:b0:137:23a2:2b3c with SMTP id 24-20020a056a2004d800b0013723a22b3cmr2604014pzd.49.1689956685506; Fri, 21 Jul 2023 09:24:45 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689956685; cv=none; d=google.com; s=arc-20160816; b=VYtGCpYvI4h5GKSeDdnKFJ3R7ASeNfNqs6TlqaC4TRjjtdNGos/wXm18aMeyZ3fDWi Qja6t1jItqa4OeiMJDdRfW2JEH+WgEszCLRHb+3DETpjcjRf0brFQjmDwpucnI5j2Ruy n4WZ7XJ3qhrTopxV1RUT/U921aEPs9NOnthKScD+HdhxekaWdcixwwT7nT54nsrXRlQG ZDwZcSIHE7wvW9NwsbgKTNW0ALs0klsPTJTVHANYRkpOemD2q0UPgwotxT7/LMzI+3La WNfpT9ItBELws6bNhV/FAmE4BtsijulE637Xlfqn5xJCJ+wIN0GwKVN6upTA2i8s/NwE ItkQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=YBrGKffyjTkycCwd+lM5fHU406znDgFk9HFzWebHaZ0=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=XEeIZk4v/V0l8VgukhlIOMo3VqDUSBcpxt8gONrRP5iCfFIH0+f/wR2HIQkdLyKohj Z1QknF2OanRntJ5+z7Uqhxmi7hAYgEKNd1l9dbVxYYOWanuFM/yGz7aJVxpueznofBxj TgEmTXoXF4ysF8u92UpoKni1C6oS3rrxQq96Hrai9YN05gSVtM3I4d9eB9IMZLnjxhZW uPikFUnXDhzNEJH63l99lrJpvx3J/AX4LbwFlFTr/KuqpkzOOzN6BMAuyMUOLcewxicq 7hqI3K2ziTNiRBAmsoBzYWthbqqJwpL6jkCmLxB/Vw+bgBeDYhH2s/H2ClncwkRhWUG+ ekfQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=Lx137Wrv; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id k11-20020a634b4b000000b0055ba8a7dea6si3201308pgl.519.2023.07.21.09.24.30; Fri, 21 Jul 2023 09:24:45 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=Lx137Wrv; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232798AbjGUQXh (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51464 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229736AbjGUQWv (ORCPT ); Fri, 21 Jul 2023 12:22:51 -0400 Received: from mail-lj1-x232.google.com (mail-lj1-x232.google.com [IPv6:2a00:1450:4864:20::232]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E3F1A4C09 for ; Fri, 21 Jul 2023 09:20:04 -0700 (PDT) Received: by mail-lj1-x232.google.com with SMTP id 38308e7fff4ca-2b6f97c7115so31215781fa.2 for ; Fri, 21 Jul 2023 09:20:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956399; x=1690561199; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=YBrGKffyjTkycCwd+lM5fHU406znDgFk9HFzWebHaZ0=; b=Lx137Wrvlvz2vXyuCyanT1JclkSyIH6Ua7N8+WJIzWgBG9j4nuMTEiW1uYbGuvuKxr LRhH7jK0RS6Sab/xbwQaIRZmtOzHrBSU5vooO1LeA1b/b2TJUdogJUTEtAz1IJmo+Bo9 K0OFNxifUjinMWcnRhWhwfxmtMBegJWkGZn+uQFTgjahdr8oWebzSxj+F1xyOF5jjc2K Xijla5UyNRfH5vSTY3Izyi6UAL9oenXPLOSIyM0HLIzGQO8LSY/K9ConuNcBi/eW5veU cBTuEGFaKkDh8bhxjxZPMp2lavVk0UinVy0G/kn3Teo7VJ8ntndZCzc+4k7385/N901F oXFA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956399; x=1690561199; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=YBrGKffyjTkycCwd+lM5fHU406znDgFk9HFzWebHaZ0=; b=clcQBNzG7DqdAtaPaNA+vi4XJWIZA00uwl0zA5GncI8gVAnFYXGoifUDhDzrvqJHQH xOU8TOz03Nty1zZkToe17KaVB+ZXWX0plPUQCSytELZgcfeo44RAAl+0DfrZNwKgi6H2 91jhx1cvQNHqBxYprNDAkJVy52Z3vLxsZ71Plomx/T9KHDRrr4oXnYK2aTEjMC8067W+ O9rZE65jd1XsX0SkQ8tptlaqqxRScO3jHG4bSz9egydVKl2/72HY0vvBX7WkDyV43m/z qfaFWiweGzOlh6/Yzkl6jWhKQM9dx1PkSAKlg3XbQg2bNWvx1P/ChjPCM1/SdgrhJ6W9 +gvg== X-Gm-Message-State: ABy/qLYVoQpJ6/J7D3jEei8aIvJvEYrNH12w9clhLSRAZwaOfOX89JJ6 o+UZsi/8fzUfnQiNEq/UFLyklQ== X-Received: by 2002:a2e:9810:0:b0:2b7:31a:9d7c with SMTP id a16-20020a2e9810000000b002b7031a9d7cmr2070328ljj.33.1689956399324; Fri, 21 Jul 2023 09:19:59 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:19:58 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 20/23] net/tcp: Add static_key for TCP-AO Date: Fri, 21 Jul 2023 17:19:11 +0100 Message-ID: <20230721161916.542667-21-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772048021345221288 X-GMAIL-MSGID: 1772048021345221288 Similarly to TCP-MD5, add a static key to TCP-AO that is patched out when there are no keys on a machine and dynamically enabled with the first setsockopt(TCP_AO) adds a key on any socket. The static key is as well dynamically disabled later when the socket is destructed. The lifetime of enabled static key here is the same as ao_info: it is enabled on allocation, passed over from full socket to twsk and destructed when ao_info is scheduled for destruction. Signed-off-by: Dmitry Safonov --- include/net/tcp.h | 3 +++ include/net/tcp_ao.h | 2 ++ net/ipv4/tcp_ao.c | 22 +++++++++++++++++++++ net/ipv4/tcp_input.c | 46 +++++++++++++++++++++++++++++--------------- 4 files changed, 57 insertions(+), 16 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 50636ee7ee2f..4df3ee6e2722 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2611,6 +2611,9 @@ static inline bool tcp_ao_required(struct sock *sk, const void *saddr, struct tcp_ao_info *ao_info; struct tcp_ao_key *ao_key; + if (!static_branch_unlikely(&tcp_ao_needed.key)) + return false; + ao_info = rcu_dereference_check(tcp_sk(sk)->ao_info, lockdep_sock_is_held(sk)); if (!ao_info) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 9209343878e1..e6d849d7179e 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -152,6 +152,8 @@ do { \ #ifdef CONFIG_TCP_AO /* TCP-AO structures and functions */ +#include +extern struct static_key_false_deferred tcp_ao_needed; struct tcp4_ao_context { __be32 saddr; diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 567aa9400f7d..54b35ee1edf1 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -17,6 +17,8 @@ #include #include +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) { @@ -50,6 +52,9 @@ bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code) struct tcp_ao_info *ao; bool ignore_icmp = false; + if (!static_branch_unlikely(&tcp_ao_needed.key)) + return false; + /* RFC5925, 7.8: * >> A TCP-AO implementation MUST default to ignore incoming ICMPv4 * messages of Type 3 (destination unreachable), Codes 2-4 (protocol @@ -188,6 +193,9 @@ static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk, struct tcp_ao_key *key; struct tcp_ao_info *ao; + if (!static_branch_unlikely(&tcp_ao_needed.key)) + return NULL; + ao = rcu_dereference_check(tcp_sk(sk)->ao_info, lockdep_sock_is_held(sk)); if (!ao) @@ -281,6 +289,7 @@ void tcp_ao_destroy_sock(struct sock *sk, bool twsk) } kfree_rcu(ao, rcu); + static_branch_slow_dec_deferred(&tcp_ao_needed); } void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp) @@ -1142,6 +1151,11 @@ int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, goto free_and_exit; } + if (!static_key_fast_inc_not_disabled(&tcp_ao_needed.key.key)) { + ret = -EUSERS; + goto free_and_exit; + } + key_head = rcu_dereference(hlist_first_rcu(&new_ao->head)); first_key = hlist_entry_safe(key_head, struct tcp_ao_key, node); @@ -1585,6 +1599,10 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, tcp_ao_link_mkt(ao_info, key); if (first) { + if (!static_branch_inc(&tcp_ao_needed.key)) { + ret = -EUSERS; + goto err_free_sock; + } sk_gso_disable(sk); rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); } @@ -1850,6 +1868,10 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, if (new_rnext) WRITE_ONCE(ao_info->rnext_key, new_rnext); if (first) { + if (!static_branch_inc(&tcp_ao_needed.key)) { + err = -EUSERS; + goto out; + } sk_gso_disable(sk); rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4b37e82ab612..777f5f5f721d 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3528,41 +3528,55 @@ static inline bool tcp_may_update_window(const struct tcp_sock *tp, (ack_seq == tp->snd_wl1 && nwin > tp->snd_wnd); } -/* If we update tp->snd_una, also update tp->bytes_acked */ -static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack) +static void tcp_snd_sne_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 + if (!static_branch_unlikely(&tcp_ao_needed.key)) + return; + ao = rcu_dereference_protected(tp->ao_info, lockdep_sock_is_held((struct sock *)tp)); if (ao && ack < tp->snd_una) ao->snd_sne++; #endif +} + +/* If we update tp->snd_una, also update tp->bytes_acked */ +static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack) +{ + u32 delta = ack - tp->snd_una; + + sock_owned_by_me((struct sock *)tp); + tp->bytes_acked += delta; + tcp_snd_sne_update(tp, ack); tp->snd_una = ack; } +static void tcp_rcv_sne_update(struct tcp_sock *tp, u32 seq) +{ +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao; + + if (!static_branch_unlikely(&tcp_ao_needed.key)) + return; + + ao = rcu_dereference_protected(tp->ao_info, + lockdep_sock_is_held((struct sock *)tp)); + if (ao && seq < tp->rcv_nxt) + ao->rcv_sne++; +#endif +} + /* If we update tp->rcv_nxt, also update tp->bytes_received */ static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq) { u32 delta = seq - tp->rcv_nxt; -#ifdef CONFIG_TCP_AO - struct tcp_ao_info *ao; -#endif sock_owned_by_me((struct sock *)tp); tp->bytes_received += delta; -#ifdef CONFIG_TCP_AO - ao = rcu_dereference_protected(tp->ao_info, - lockdep_sock_is_held((struct sock *)tp)); - if (ao && seq < tp->rcv_nxt) - ao->rcv_sne++; -#endif + tcp_rcv_sne_update(tp, seq); WRITE_ONCE(tp->rcv_nxt, seq); } From patchwork Fri Jul 21 16:19:12 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123997 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp343521vqg; Fri, 21 Jul 2023 10:04:14 -0700 (PDT) X-Google-Smtp-Source: APBJJlGKW1Jb/sNivfOX9WjXrsCT2d1lrrbGzbDhLs60OwA7pdu460c77cWcMYWAFZX8+bXLlc49 X-Received: by 2002:a2e:97da:0:b0:2b6:ec2b:7d77 with SMTP id m26-20020a2e97da000000b002b6ec2b7d77mr2113784ljj.6.1689959053997; Fri, 21 Jul 2023 10:04:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689959053; cv=none; d=google.com; s=arc-20160816; b=HLEwFhxrKdcOoIAu5/LU1sJF4MJBtJvgwF4eufBkSM8OCHkA7wWU2LEV7g/VP96Jrp sMB8OeOdR1x6Gc/e3JKCROqU3hwE/3AYiVLZhML0LDfxxjroQq9vzqNwB95VF+4iWUCC +emCFz35WzoWChLbUb6LX8gVf8HiW1p6AoAzhwJvwWoBoAbE7L1LecwZ14ZURcp32hoK oB+wIOfII0ocCs2y3oKDBVlguZt6uCy+ObUoxjV0Aj7aJCpYAUZPhR0MyQ9tcusioqsK qQWiN2gc/wLaBUJP0Q1etDOfNJCF/0kv6adKQD1nLc6FEdDUbf19FlxYap0VufuuTx0A XbJg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=m+gsV6wWNt6NGZOa/mJznhVIWMKzIdf4va8ro0ETMls=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=DZn6d6uaXBS09wL2tM/TLXJyb0H0s50FqhV1rz1vAc/+93197FfEmepuj5oX9M5dZP 2rsl3ABqUs7J0mOOMQEE4J4xdUWN66TtS6LUeI1f+ff9fTw5kHdxLqPNfMUiEUs3dRid y8EYsuLU/Py/BHjVp5MKxWncA77YYSehu/1mHZ/WV0oJ/AfuhsyNqDeTXvCLMzM2ajH0 vS+HdVns4RECR1Bpprmwz01HCMhmCYAmVFCIM5AE0pxEG1gQTslsjnxIXI0otkqMqh22 I961ItUF0KcAlBA8CI6uSu4IOZ1COsiBPsKzxyIOzPzTbJE4iFd6fI0AhYW2rw5j5Vkr IyMA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=hPM1SoiY; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id rs10-20020a170907036a00b009924806cd0asi2465919ejb.295.2023.07.21.10.03.42; Fri, 21 Jul 2023 10:04:13 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=hPM1SoiY; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232874AbjGUQXp (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51182 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232898AbjGUQWy (ORCPT ); Fri, 21 Jul 2023 12:22:54 -0400 Received: from mail-lf1-x133.google.com (mail-lf1-x133.google.com [IPv6:2a00:1450:4864:20::133]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C54814C11 for ; Fri, 21 Jul 2023 09:20:06 -0700 (PDT) Received: by mail-lf1-x133.google.com with SMTP id 2adb3069b0e04-4fbf09a9139so3493818e87.2 for ; Fri, 21 Jul 2023 09:20:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956401; x=1690561201; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=m+gsV6wWNt6NGZOa/mJznhVIWMKzIdf4va8ro0ETMls=; b=hPM1SoiYfqbbcDOdbdgvOx3abcWwRvQf/6PPGHipV4te2uP6tJWdxJaIEPDM+yQybJ a5HY/G0EX3WS9gwGhVDbPP9d3EmdFqPKc8oNTF3wvlF7nqpKR2LMxDdPak4iDFSZpjCs 5Ume+jLpUNnRBl7WgOGpY9/GmPK3gBt8cQJO4aRKrrLG5F9q6mc96ly+Fm1eUfnA0J02 jjn//R0Per8g5ou+LukfMgC8j1aqAJrTDNFIpf9uSaJA6ei8AHGM9NpWRBJkCHPKqb9P 0TKgPwDBhjQsmO2ASHl4gFGaK0M+R7lXtYLZXrvf6id1ouCwvIKGkmnRbjd2hjflGv7x teHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956401; x=1690561201; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=m+gsV6wWNt6NGZOa/mJznhVIWMKzIdf4va8ro0ETMls=; b=GmVaFAm7unnc80nUe7zmp707YJae1LdNQl40fktvZ5IxB2y+yEjzuVFSawT3rKpAmL 6Kad80MR+tcaLIx1qFGqVJEciQYs10ZrgfokeC2F0slZkISY0SmrRH8SxzzCKhwIECIa vEAloc2pSCcEDbMhZtKMfxpncdRh+s+SKk0ZUOC5foGn8jdsInRb/xr5suPQWYPCcKgL pJmRdux2J6ZY1rGWgLSpMH15F44QrDjluaTZzCxGOT1DGwWpJabrplFXSmVG+wxmHF4v AiIr557jy0gMO+GN1fZQEulY7RNDOv/vrCnozIDSDqoLA116wgP2gEJWBTSPNlMxhykP /G8g== X-Gm-Message-State: ABy/qLZBpl9/bKL04PkRnvZjHDe5m7UjKmy1W1vKQhq2kZflTU2Ai5Xq +4TX+N/43eAI3VhuRV2ANmanmA== X-Received: by 2002:a19:6541:0:b0:4f9:5404:af5 with SMTP id c1-20020a196541000000b004f954040af5mr1542225lfj.46.1689956401111; Fri, 21 Jul 2023 09:20:01 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.19.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:20:00 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 21/23] net/tcp: Wire up l3index to TCP-AO Date: Fri, 21 Jul 2023 17:19:12 +0100 Message-ID: <20230721161916.542667-22-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050505247362375 X-GMAIL-MSGID: 1772050505247362375 Similarly how TCP_MD5SIG_FLAG_IFINDEX works for TCP-MD5, TCP_AO_KEYF_IFINDEX is an AO-key flag that binds that MKT to a specified by L3 ifinndex. Similarly, without this flag the key will work in the default VRF l3index = 0 for connections. To prevent AO-keys from overlapping, it's restricted to add key B for a socket that has key A, which have the same sndid/rcvid and one of the following is true: - !(A.keyflags & TCP_AO_KEYF_IFINDEX) or !(B.keyflags & TCP_AO_KEYF_IFINDEX) so that any key is non-bound to a VRF - A.l3index == B.l3index both want to work for the same VRF Additionally, it's restricted to match TCP-MD5 keys for the same peer the following way: |--------------|--------------------|----------------|---------------| | | MD5 key without | MD5 key | MD5 key | | | l3index | l3index=0 | l3index=N | |--------------|--------------------|----------------|---------------| | TCP-AO key | | | | | without | reject | reject | reject | | l3index | | | | |--------------|--------------------|----------------|---------------| | TCP-AO key | | | | | l3index=0 | reject | reject | allow | |--------------|--------------------|----------------|---------------| | TCP-AO key | | | | | l3index=N | reject | allow | reject | |--------------|--------------------|----------------|---------------| This is done with the help of tcp_md5_do_lookup_any_l3index() to reject adding AO key without TCP_AO_KEYF_IFINDEX if there's TCP-MD5 in any VRF. This is important for case where sysctl_tcp_l3mdev_accept = 1 Similarly, for TCP-AO lookups tcp_ao_do_lookup() may be used with l3index < 0, so that __tcp_ao_key_cmp() will match TCP-AO key in any VRF. Signed-off-by: Dmitry Safonov --- include/net/tcp.h | 11 +-- include/net/tcp_ao.h | 17 +++-- net/ipv4/syncookies.c | 6 +- net/ipv4/tcp_ao.c | 164 +++++++++++++++++++++++++++++++----------- net/ipv4/tcp_ipv4.c | 13 ++-- net/ipv6/syncookies.c | 5 +- net/ipv6/tcp_ao.c | 20 +++--- net/ipv6/tcp_ipv6.c | 15 ++-- 8 files changed, 171 insertions(+), 80 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 4df3ee6e2722..4aa124ddde3a 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2605,7 +2605,7 @@ static inline int tcp_parse_auth_options(const struct tcphdr *th, } static inline bool tcp_ao_required(struct sock *sk, const void *saddr, - int family, bool stat_inc) + int family, int l3index, bool stat_inc) { #ifdef CONFIG_TCP_AO struct tcp_ao_info *ao_info; @@ -2619,7 +2619,7 @@ static inline bool tcp_ao_required(struct sock *sk, const void *saddr, if (!ao_info) return false; - ao_key = tcp_ao_do_lookup(sk, saddr, family, -1, -1, 0); + ao_key = tcp_ao_do_lookup(sk, l3index, saddr, family, -1, -1, 0); if (ao_info->ao_required || ao_key) { if (stat_inc) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOREQUIRED); @@ -2672,21 +2672,22 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req, * the last key is impossible to remove, so there's * always at least one current_key. */ - if (tcp_ao_required(sk, saddr, family, true)) { + if (tcp_ao_required(sk, saddr, family, l3index, true)) { tcp_hash_fail("AO hash is required, but not found", family, skb, "L3 index %d", l3index); return SKB_DROP_REASON_TCP_AONOTFOUND; } 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, ""); + tcp_hash_fail("MD5 Hash not found", + family, skb, "L3 index %d", l3index); 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_ao_hash(sk, skb, family, req, l3index, aoh); return tcp_inbound_md5_hash(sk, skb, saddr, daddr, family, l3index, md5_location); diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index e6d849d7179e..b56b49a98266 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -33,6 +33,7 @@ struct tcp_ao_key { u8 key[TCP_AO_MAXKEYLEN] __tcp_ao_key_align; unsigned int tcp_sigpool_id; unsigned int digest_size; + int l3index; u16 port; u8 prefixlen; u8 family; @@ -197,10 +198,10 @@ 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); 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 request_sock *req, int l3index, const struct tcp_ao_hdr *aoh); u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq); -struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, +struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, int l3index, 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, @@ -243,9 +244,6 @@ int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, __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); @@ -264,12 +262,12 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb); void tcp_ao_connect_init(struct sock *sk); void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, struct tcp_request_sock *treq, - unsigned short int family); + unsigned short int family, int l3index); #else /* CONFIG_TCP_AO */ static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, struct tcp_request_sock *treq, - unsigned short int family) + unsigned short int family, int l3index) { } @@ -280,13 +278,14 @@ static inline bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code) 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) + const struct request_sock *req, int l3index, + 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 l3index, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port) { return NULL; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 23fca22bc992..213accdc3600 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -337,6 +337,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) struct rtable *rt; __u8 rcv_wscale; struct flowi4 fl4; + int l3index; u32 tsoff = 0; if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies) || @@ -394,13 +395,14 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) treq->snt_synack = 0; treq->tfo_listener = false; - tcp_ao_syncookie(sk, skb, treq, AF_INET); - if (IS_ENABLED(CONFIG_SMC)) ireq->smc_ok = 0; ireq->ir_iif = inet_request_bound_dev_if(sk, skb); + l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); + tcp_ao_syncookie(sk, skb, treq, AF_INET, l3index); + /* We throwed the options of the initial SYN away, so we hope * the ACK carries the same options again (see RFC1122 4.2.3.8) */ diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 54b35ee1edf1..3490b590bff0 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -137,7 +137,7 @@ static inline int ipv4_prefix_cmp(const struct in_addr *addr1, return ((addr1->s_addr & mask) > (addr2->s_addr & mask)) ? 1 : -1; } -static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, +static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, int l3index, const union tcp_ao_addr *addr, u8 prefixlen, int family, int sndid, int rcvid, u16 port) { @@ -147,6 +147,10 @@ static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, return (key->rcvid > rcvid) ? 1 : -1; if (port != 0 && key->port != 0 && port != key->port) return (key->port > port) ? 1 : -1; + if (l3index >= 0 && (key->keyflags & TCP_AO_KEYF_IFINDEX)) { + if (key->l3index != l3index) + return (key->l3index > l3index) ? 1 : -1; + } if (family == AF_UNSPEC) return 0; @@ -171,7 +175,7 @@ static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, return -1; } -static int tcp_ao_key_cmp(const struct tcp_ao_key *key, +static int tcp_ao_key_cmp(const struct tcp_ao_key *key, int l3index, const union tcp_ao_addr *addr, u8 prefixlen, int family, int sndid, int rcvid, u16 port) { @@ -179,14 +183,16 @@ static int tcp_ao_key_cmp(const struct tcp_ao_key *key, 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, + return __tcp_ao_key_cmp(key, l3index, + (union tcp_ao_addr *)&addr4, prefixlen, AF_INET, sndid, rcvid, port); } #endif - return __tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid, port); + return __tcp_ao_key_cmp(key, l3index, addr, + prefixlen, family, sndid, rcvid, port); } -static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk, +static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk, int l3index, const union tcp_ao_addr *addr, int family, u8 prefix, int sndid, int rcvid, u16 port) { @@ -204,18 +210,18 @@ static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk, hlist_for_each_entry_rcu(key, &ao->head, node) { u8 prefixlen = min(prefix, key->prefixlen); - if (!tcp_ao_key_cmp(key, addr, prefixlen, + if (!tcp_ao_key_cmp(key, l3index, addr, prefixlen, family, sndid, rcvid, port)) return key; } return NULL; } -struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, +struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, int l3index, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port) { - return __tcp_ao_do_lookup(sk, addr, family, U8_MAX, + return __tcp_ao_do_lookup(sk, l3index, addr, family, U8_MAX, sndid, rcvid, port); } @@ -682,18 +688,21 @@ 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; + struct inet_request_sock *ireq = inet_rsk(req); + union tcp_ao_addr *addr = (union tcp_ao_addr *)&ireq->ir_rmt_addr; + int l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); - return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid, 0); + return tcp_ao_do_lookup(sk, l3index, 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) { union tcp_ao_addr *addr = (union tcp_ao_addr *)&addr_sk->sk_daddr; + int l3index = l3mdev_master_ifindex_by_index(sock_net(sk), + addr_sk->sk_bound_dev_if); - return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid, 0); + return tcp_ao_do_lookup(sk, l3index, addr, AF_INET, sndid, rcvid, 0); } int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, @@ -785,24 +794,26 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, 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) + int sndid, int rcvid, int l3index) { if (family == AF_INET) { const struct iphdr *iph = ip_hdr(skb); - return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr, - AF_INET, sndid, rcvid, 0); + return tcp_ao_do_lookup(sk, l3index, + (union tcp_ao_addr *)&iph->saddr, + AF_INET, sndid, rcvid, 0); } else { const struct ipv6hdr *iph = ipv6_hdr(skb); - return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr, - AF_INET6, sndid, rcvid, 0); + return tcp_ao_do_lookup(sk, l3index, + (union tcp_ao_addr *)&iph->saddr, + AF_INET6, sndid, rcvid, 0); } } void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, struct tcp_request_sock *treq, - unsigned short int family) + unsigned short int family, int l3index) { const struct tcphdr *th = tcp_hdr(skb); const struct tcp_ao_hdr *aoh; @@ -817,7 +828,7 @@ void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, if (!aoh) return; - key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid); + key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid, l3index); if (!key) /* Key not found, continue without TCP-AO */ return; @@ -831,7 +842,7 @@ 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) + u8 *traffic_key, u8 *phash, u32 sne, int l3index) { u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr); const struct tcphdr *th = tcp_hdr(skb); @@ -842,7 +853,8 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, 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)); + "%u != %d L3index: %d", maclen, + tcp_ao_maclen(key), l3index); return SKB_DROP_REASON_TCP_AOFAILURE; } @@ -857,7 +869,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 mismatch", family, skb, ""); + tcp_hash_fail("AO hash mismatch", family, skb, + "L3index: %d", l3index); kfree(hash_buf); return SKB_DROP_REASON_TCP_AOFAILURE; } @@ -871,7 +884,7 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, 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) + int l3index, const struct tcp_ao_hdr *aoh) { const struct tcphdr *th = tcp_hdr(skb); u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */ @@ -886,7 +899,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, if (!info) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); tcp_hash_fail("AO key not found", family, skb, - "keyid: %u", aoh->keyid); + "keyid: %u L3index: %d", aoh->keyid, l3index); return SKB_DROP_REASON_TCP_AOUNEXPECTED; } @@ -921,7 +934,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, /* 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); + traffic_key, phash, sne, l3index); if (err) return err; current_key = READ_ONCE(info->current_key); @@ -942,7 +955,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, * - 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); + key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid, l3index); if (!key) goto key_not_found; @@ -982,7 +995,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, return SKB_DROP_REASON_NOT_SPECIFIED; tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family); ret = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, - traffic_key, phash, sne); + traffic_key, phash, sne, l3index); kfree(traffic_key); return ret; @@ -990,7 +1003,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, 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, ""); + family, skb, "L3index: %d", l3index); return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; } @@ -1018,7 +1031,7 @@ void tcp_ao_connect_init(struct sock *sk) struct tcp_ao_key *key; struct tcp_sock *tp = tcp_sk(sk); union tcp_ao_addr *addr; - int family; + int family, l3index; ao_info = rcu_dereference_protected(tp->ao_info, lockdep_sock_is_held(sk)); @@ -1035,9 +1048,11 @@ void tcp_ao_connect_init(struct sock *sk) #endif else return; + l3index = l3mdev_master_ifindex_by_index(sock_net(sk), + sk->sk_bound_dev_if); hlist_for_each_entry_rcu(key, &ao_info->head, node) { - if (tcp_ao_key_cmp(key, addr, key->prefixlen, family, + if (tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, family, -1, -1, sk->sk_dport) == 0) continue; @@ -1097,7 +1112,7 @@ int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, struct hlist_node *key_head; union tcp_ao_addr *addr; bool match = false; - int ret = -ENOMEM; + int l3index, ret = -ENOMEM; ao = rcu_dereference(tcp_sk(sk)->ao_info); if (!ao) @@ -1125,9 +1140,11 @@ int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, ret = -EAFNOSUPPORT; goto free_ao; } + l3index = l3mdev_master_ifindex_by_index(sock_net(newsk), + newsk->sk_bound_dev_if); hlist_for_each_entry_rcu(key, &ao->head, node) { - if (tcp_ao_key_cmp(key, addr, key->prefixlen, family, + if (tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, family, -1, -1, 0)) continue; @@ -1446,7 +1463,8 @@ static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) return ERR_PTR(-ESOCKTNOSUPPORT); } -#define TCP_AO_KEYF_ALL (TCP_AO_KEYF_EXCLUDE_OPT) +#define TCP_AO_KEYF_ALL (TCP_AO_KEYF_IFINDEX | TCP_AO_KEYF_EXCLUDE_OPT) +#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) @@ -1510,7 +1528,7 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, union tcp_ao_addr *addr; struct tcp_ao_key *key; struct tcp_ao_add cmd; - int ret; + int ret, l3index = 0; bool first = false; u16 port; @@ -1542,9 +1560,46 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, return -EINVAL; } + if (cmd.ifindex && !(cmd.keyflags & TCP_AO_KEYF_IFINDEX)) + return -EINVAL; + + /* For cmd.tcp_ifindex = 0 the key will apply to the default VRF */ + if (cmd.keyflags & TCP_AO_KEYF_IFINDEX && cmd.ifindex) { + int bound_dev_if = READ_ONCE(sk->sk_bound_dev_if); + struct net_device *dev; + + rcu_read_lock(); + dev = dev_get_by_index_rcu(sock_net(sk), cmd.ifindex); + if (dev && netif_is_l3_master(dev)) + l3index = dev->ifindex; + rcu_read_unlock(); + + if (!dev || !l3index) + return -EINVAL; + + /* It's still possible to bind after adding keys or even + * re-bind to a different dev (with CAP_NET_RAW). + * So, no reason to return error here, rather try to be + * nice and warn the user. + */ + if (bound_dev_if && bound_dev_if != cmd.ifindex) + net_warn_ratelimited("AO key ifindex %d != sk bound ifindex %d\n", + cmd.ifindex, bound_dev_if); + } + /* 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; + if (cmd.keyflags & TCP_AO_KEYF_IFINDEX) { + /* Non-_exact version of tcp_md5_do_lookup() will + * as well match keys that aren't bound to a specific VRF + * (that will make them match AO key with + * sysctl_tcp_l3dev_accept = 1 + */ + if (tcp_md5_do_lookup(sk, l3index, addr, family)) + return -EKEYREJECTED; + } else { + if (tcp_md5_do_lookup_any_l3index(sk, addr, family)) + return -EKEYREJECTED; + } ao_info = setsockopt_ao_info(sk); if (IS_ERR(ao_info)) @@ -1561,10 +1616,10 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, * > The IDs of MKTs MUST NOT overlap where their * > TCP connection identifiers overlap. */ - if (__tcp_ao_do_lookup(sk, addr, family, + if (__tcp_ao_do_lookup(sk, l3index, addr, family, cmd.prefix, -1, cmd.rcvid, port)) return -EEXIST; - if (__tcp_ao_do_lookup(sk, addr, family, + if (__tcp_ao_do_lookup(sk, l3index, addr, family, cmd.prefix, cmd.sndid, -1, port)) return -EEXIST; } @@ -1584,6 +1639,7 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, key->keyflags = cmd.keyflags; key->sndid = cmd.sndid; key->rcvid = cmd.rcvid; + key->l3index = l3index; atomic64_set(&key->pkt_good, 0); atomic64_set(&key->pkt_bad, 0); @@ -1672,17 +1728,17 @@ static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, return err; } +#define TCP_AO_DEL_KEYF_ALL (TCP_AO_KEYF_IFINDEX) static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, sockptr_t optval, int optlen) { struct tcp_ao_key *key, *new_current = NULL, *new_rnext = NULL; + int err, addr_len, l3index = 0; struct tcp_ao_info *ao_info; union tcp_ao_addr *addr; struct tcp_ao_del cmd; - int err; __u8 prefix; __be16 port; - int addr_len; if (optlen < sizeof(cmd)) return -EINVAL; @@ -1699,6 +1755,17 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, return -EINVAL; } + if (cmd.keyflags & ~TCP_AO_DEL_KEYF_ALL) + return -EINVAL; + + /* No sanity check for TCP_AO_KEYF_IFINDEX as if a VRF + * was destroyed, there still should be a way to delete keys, + * that were bound to that l3intf. So, fail late at lookup stage + * if there is no key for that ifindex. + */ + if (cmd.ifindex && !(cmd.keyflags & TCP_AO_KEYF_IFINDEX)) + return -EINVAL; + ao_info = setsockopt_ao_info(sk); if (IS_ERR(ao_info)) return PTR_ERR(ao_info); @@ -1763,6 +1830,13 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, memcmp(addr, &key->addr, addr_len)) continue; + if ((cmd.keyflags & TCP_AO_KEYF_IFINDEX) != + (key->keyflags & TCP_AO_KEYF_IFINDEX)) + continue; + + if (key->l3index != l3index) + continue; + if (key == new_current || key == new_rnext) continue; @@ -1948,10 +2022,10 @@ static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, struct tcp_ao_key *key, *current_key; bool do_address_matching = true; union tcp_ao_addr *addr = NULL; + int err, l3index, user_len; unsigned int max_keys; /* maximum number of keys to copy to user */ size_t out_offset = 0; size_t bytes_to_write; /* number of bytes to write to user level */ - int err, user_len; u32 matched_keys; /* keys from ao_info matched so far */ int optlen_out; u16 port = 0; @@ -1970,11 +2044,16 @@ static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, if (opt_in.pkt_good || opt_in.pkt_bad) return -EINVAL; + if (opt_in.keyflags & ~TCP_AO_GET_KEYF_VALID) + return -EINVAL; + if (opt_in.ifindex && !(opt_in.keyflags & TCP_AO_KEYF_IFINDEX)) + return -EINVAL; if (opt_in.reserved != 0) return -EINVAL; max_keys = opt_in.nkeys; + l3index = (opt_in.keyflags & TCP_AO_KEYF_IFINDEX) ? opt_in.ifindex : -1; if (opt_in.get_all || opt_in.is_current || opt_in.is_rnext) { if (opt_in.get_all && (opt_in.is_current || opt_in.is_rnext)) @@ -2076,7 +2155,7 @@ static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, continue; } - if (tcp_ao_key_cmp(key, addr, opt_in.prefix, + if (tcp_ao_key_cmp(key, l3index, addr, opt_in.prefix, opt_in.addr.ss_family, opt_in.sndid, opt_in.rcvid, port) != 0) continue; @@ -2109,6 +2188,7 @@ static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, opt_out.nkeys = 0; opt_out.maclen = key->maclen; 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); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 054897a13575..c14075254bfa 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1076,6 +1076,7 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, struct tcp_ao_key *ao_key = NULL; const union tcp_md5_addr *addr; u8 keyid = 0; + int l3index; u8 *traffic_key = NULL; #ifdef CONFIG_TCP_AO const struct tcp_ao_hdr *aoh; @@ -1087,6 +1088,7 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, 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; + l3index = tcp_v4_sdif(skb) ? inet_iif(skb) : 0; if (tcp_rsk_used_ao(req)) { #ifdef CONFIG_TCP_AO @@ -1097,11 +1099,11 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, if (!aoh) return; - ao_key = tcp_ao_do_lookup(sk, addr, AF_INET, + ao_key = tcp_ao_do_lookup(sk, l3index, 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, + ao_key = tcp_ao_do_lookup(sk, l3index, addr, AF_INET, -1, -1, 0); /* Matching key disappeared (user removed the key?) * let the handshake timeout. @@ -1123,9 +1125,6 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, 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 @@ -1485,6 +1484,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname, const union tcp_md5_addr *addr; u8 prefixlen = 32; int l3index = 0; + bool l3flag; u8 flags; if (optlen < sizeof(cmd)) @@ -1497,6 +1497,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname, return -EINVAL; flags = cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX; + l3flag = cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX; if (optname == TCP_MD5SIG_EXT && cmd.tcpm_flags & TCP_MD5SIG_FLAG_PREFIX) { @@ -1534,7 +1535,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname, /* Don't allow keys for peers that have a matching TCP-AO key. * See the comment in tcp_ao_add_cmd() */ - if (tcp_ao_required(sk, addr, AF_INET, false)) + if (tcp_ao_required(sk, addr, AF_INET, l3flag ? l3index : -1, false)) return -EKEYREJECTED; return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index ad7a8caa7b2a..500f6ed3b8cf 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -140,6 +140,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) struct dst_entry *dst; __u8 rcv_wscale; u32 tsoff = 0; + int l3index; if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies) || !th->ack || th->rst) @@ -214,7 +215,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) treq->snt_isn = cookie; treq->ts_off = 0; treq->txhash = net_tx_rndhash(); - tcp_ao_syncookie(sk, skb, treq, AF_INET6); + + l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); + tcp_ao_syncookie(sk, skb, treq, AF_INET6, l3index); if (IS_ENABLED(CONFIG_SMC)) ireq->smc_ok = 0; diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index 984a75f96e20..ef163e8928d2 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -87,30 +87,28 @@ int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, 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) -{ - 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; + int l3index = l3mdev_master_ifindex_by_index(sock_net(sk), + addr_sk->sk_bound_dev_if); - return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); + return tcp_ao_do_lookup(sk, l3index, (union tcp_ao_addr *)addr, + AF_INET6, sndid, rcvid, 0); } 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; + struct inet_request_sock *ireq = inet_rsk(req); + struct in6_addr *addr = &ireq->ir_v6_rmt_addr; + int l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); - return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); + return tcp_ao_do_lookup(sk, l3index, (union tcp_ao_addr *)addr, + AF_INET6, sndid, rcvid, 0); } int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 9a427c0d48ab..dee42cba6c25 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -606,6 +606,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, union tcp_ao_addr *addr; int l3index = 0; u8 prefixlen; + bool l3flag; u8 flags; if (optlen < sizeof(cmd)) @@ -618,6 +619,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, return -EINVAL; flags = cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX; + l3flag = cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX; if (optname == TCP_MD5SIG_EXT && cmd.tcpm_flags & TCP_MD5SIG_FLAG_PREFIX) { @@ -664,7 +666,8 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, /* Don't allow keys for peers that have a matching TCP-AO key. * See the comment in tcp_ao_add_cmd() */ - if (tcp_ao_required(sk, addr, AF_INET, false)) + if (tcp_ao_required(sk, addr, AF_INET, + l3flag ? l3index : -1, false)) return -EKEYREJECTED; return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, @@ -676,7 +679,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, /* Don't allow keys for peers that have a matching TCP-AO key. * See the comment in tcp_ao_add_cmd() */ - if (tcp_ao_required(sk, addr, AF_INET6, false)) + if (tcp_ao_required(sk, addr, AF_INET6, l3flag ? l3index : -1, false)) return -EKEYREJECTED; return tcp_md5_do_add(sk, addr, AF_INET6, prefixlen, l3index, flags, @@ -1223,10 +1226,14 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, return; if (!aoh) return; - ao_key = tcp_v6_ao_do_lookup(sk, addr, aoh->rnext_keyid, -1); + ao_key = tcp_ao_do_lookup(sk, l3index, + (union tcp_ao_addr *)addr, AF_INET6, + aoh->rnext_keyid, -1, 0); if (unlikely(!ao_key)) { /* Send ACK with any matching MKT for the peer */ - ao_key = tcp_v6_ao_do_lookup(sk, addr, -1, -1); + ao_key = tcp_ao_do_lookup(sk, l3index, + (union tcp_ao_addr *)addr, + AF_INET6, -1, -1, 0); /* Matching key disappeared (user removed the key?) * let the handshake timeout. */ From patchwork Fri Jul 21 16:19:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123999 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp343598vqg; Fri, 21 Jul 2023 10:04:22 -0700 (PDT) X-Google-Smtp-Source: APBJJlFnDHbutOtzNssoMXehMdjdROi7ufx+oNty37YACfef1QiBZw4Db4ndhwgvrVGcHhiZrSNB X-Received: by 2002:a05:6a00:1410:b0:686:25fe:d575 with SMTP id l16-20020a056a00141000b0068625fed575mr652817pfu.11.1689959061609; Fri, 21 Jul 2023 10:04:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689959061; cv=none; d=google.com; s=arc-20160816; b=rhr+f+JMAKb531vsnY2q5CRnvD8AbWP4ArypO0pRfZhPRpGaNS740ZWykzxPCWGQKd 6O7X0vD7hfqKUDJXF3GN9InzPmqd2dvPz5eEV2m8QFOW5pGPOJfM3ctjL+i9SxUmKTu2 jX9JWVBjk04shXgPaIdINn7nHuij37FY5GoIrSVlHu4o4HW7+SjHIyoud5OAFKZTiCLH ZQziExNJb/4lCjSvdC7KQ9/Cj+FUmoWd5i+4PVFmLVCNi4TaYv6XfAY1UwXJpzE29ihr zMhTB2crXir0fgfNoNTDS35DDeGif5CqT69V33mGHURiMA24ew7ATUgwG4QMIZLIgcFj K9Tw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=u2iLFkSQ6CjrDnYeYY043iBnw60X+7/VaETIj3JEyss=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=rMloyExhUQjGCW40QnhW68h2gVku2hzmHhY/X2SiviONdJ4lDnVUplzNyrodOEpr3s mwLc9ieDGY2SYqYxlRnuUz4f/zrKM3AfSdMm/fJKDjOieHj4wWPhslw1xUityNZvvEo5 vIalgbkVMVF+gtQkkXIm1N3emJrYwNbN0b5Rcb78xdbPsWdm+aBxqEBUFsEccrMhZ0vA s5Xcxd2CwEXpy6qSs3HznznL5V8+cDJUsp+4R+Rp4yU2YafAcnjjfaYDaMYqdZAMnvgC XD/D2WRSy7oafXezvwnvvKPoG+cj9MeSqAQ5oG/ZRg5lSRWrsdqaBh2QZCXjZPeX+mDs 2mpQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=El0iJNi5; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id n19-20020a056a00213300b0064d42dddf9fsi3246082pfj.32.2023.07.21.10.04.02; Fri, 21 Jul 2023 10:04:21 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=El0iJNi5; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232830AbjGUQXn (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51152 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232888AbjGUQWx (ORCPT ); Fri, 21 Jul 2023 12:22:53 -0400 Received: from mail-wm1-x32b.google.com (mail-wm1-x32b.google.com [IPv6:2a00:1450:4864:20::32b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C466144B0 for ; Fri, 21 Jul 2023 09:20:07 -0700 (PDT) Received: by mail-wm1-x32b.google.com with SMTP id 5b1f17b1804b1-3fbc54cab6fso17939455e9.0 for ; Fri, 21 Jul 2023 09:20:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956403; x=1690561203; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=u2iLFkSQ6CjrDnYeYY043iBnw60X+7/VaETIj3JEyss=; b=El0iJNi5DVrpcV6mGntoHVWuGWPHIDiviZnRORiiwv6Lka15KnJIqCYa+4jEExsUCK zIwZrYrfbsXJAiqVSGSQOW1QjuzzAbb6Wy7bg9HOWs06x9gxauKf83fo3N1FR6ERfgqj 9cNOnWgSE6XVoF4pPG/3szx9AsKJg2qS/ei/5fSquty77i2DyhehzXLS/bNElRxlO2n/ +BmTFFvn/yWn+6chmTbbCsI5ARRhyimU4epZNBZgBwMijHFo3E9j6U/irMTZi1RwB3/n B9pelSF+jhlwcEnZMDbxJMoj8FZR5aDdXiUsQJv4+/5y+mYd5c6PbW2E+dMUpBuQ+8Ja yHGg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956403; x=1690561203; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=u2iLFkSQ6CjrDnYeYY043iBnw60X+7/VaETIj3JEyss=; b=a6EBlT50lK46jDp2/Uwf4h9Txxa97qAzl0vKH2p3WXjOtIBIRWlHSo7fM4KghDB+sk x1ReiJFyCqdyzJRJC/nJXqU2j71VII23GOkfLJAV47fpbLtvqS4e3vG+FjQTspslYetm yLELJKT/PTW/9ySsl76kA858joSGNAv8Om8jObVNB9b3ka6ToJEqYb/0qE6x8X2eJyFP YDAYLBMvLvQ+v2aAGbAnp2rrJMwWhXGt6PJNND+rIAVfwwOMAwZR3RprZ6VurNZzntPs Bk/OgHv/4Vpp7KOpIhjs0WZl57F+2tKCnMgUOrIWJTpJYoBBjwFRjRJY7fc78Fhxva6M R2zA== X-Gm-Message-State: ABy/qLYyKy9fcr9/N/+dA3pcMgqDWLkL4K3SlsPSKyeH8Yolbeu/hCXw DYcKRkfKaCcHGmPZJGScQUjZ3w== X-Received: by 2002:a05:600c:2258:b0:3fb:fef9:cdcd with SMTP id a24-20020a05600c225800b003fbfef9cdcdmr1841459wmm.22.1689956402909; Fri, 21 Jul 2023 09:20:02 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.20.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:20:02 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 22/23] net/tcp: Add TCP_AO_REPAIR Date: Fri, 21 Jul 2023 17:19:13 +0100 Message-ID: <20230721161916.542667-23-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050513081754544 X-GMAIL-MSGID: 1772050513081754544 Add TCP_AO_REPAIR setsockopt(), getsockopt(). They let a user to repair TCP-AO ISNs/SNEs. Also let the user hack around when (tp->repair) is on and add ao_info on a socket in any supported state. As SNEs now can be read/written at any moment, use WRITE_ONCE()/READ_ONCE() to set/read them. Signed-off-by: Dmitry Safonov --- include/net/tcp_ao.h | 14 ++++++ include/uapi/linux/tcp.h | 8 ++++ net/ipv4/tcp.c | 24 ++++++++--- net/ipv4/tcp_ao.c | 92 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 127 insertions(+), 11 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index b56b49a98266..0027f4b4683d 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -196,6 +196,8 @@ void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code); int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen); int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen); +int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen); +int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen); enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, unsigned short int family, const struct request_sock *req, int l3index, @@ -317,6 +319,18 @@ static inline int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockpt { return -ENOPROTOOPT; } + +static inline int tcp_ao_get_repair(struct sock *sk, + sockptr_t optval, sockptr_t optlen) +{ + return -ENOPROTOOPT; +} + +static inline int tcp_ao_set_repair(struct sock *sk, + sockptr_t optval, unsigned int optlen) +{ + return -ENOPROTOOPT; +} #endif #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 979ff960fddb..624198fa170d 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -133,6 +133,7 @@ enum { #define TCP_AO_DEL_KEY 39 /* Delete MKT */ #define TCP_AO_INFO 40 /* Set/list TCP-AO per-socket options */ #define TCP_AO_GET_KEYS 41 /* List MKT(s) */ +#define TCP_AO_REPAIR 42 /* Get/Set SNEs and ISNs */ #define TCP_REPAIR_ON 1 #define TCP_REPAIR_OFF 0 @@ -445,6 +446,13 @@ struct tcp_ao_getsockopt { /* getsockopt(TCP_AO_GET_KEYS) */ __u64 pkt_bad; /* out: segments that failed verification */ } __attribute__((aligned(8))); +struct tcp_ao_repair { /* {s,g}etsockopt(TCP_AO_REPAIR) */ + __be32 snt_isn; + __be32 rcv_isn; + __u32 snd_sne; + __u32 rcv_sne; +} __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 82506300da6c..95c9d3c9134e 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3608,20 +3608,28 @@ int do_tcp_setsockopt(struct sock *sk, int level, int optname, __tcp_sock_set_quickack(sk, val); break; + case TCP_AO_REPAIR: + err = tcp_ao_set_repair(sk, optval, optlen); + break; #ifdef CONFIG_TCP_AO case TCP_AO_ADD_KEY: case TCP_AO_DEL_KEY: case TCP_AO_INFO: { /* If this is the first TCP-AO setsockopt() on the socket, - * sk_state has to be LISTEN or CLOSE + * sk_state has to be LISTEN or CLOSE. Allow TCP_REPAIR + * in any state. */ - if (((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) || - rcu_dereference_protected(tcp_sk(sk)->ao_info, + if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) + goto ao_parse; + if (rcu_dereference_protected(tcp_sk(sk)->ao_info, lockdep_sock_is_held(sk))) - err = tp->af_specific->ao_parse(sk, optname, optval, - optlen); - else - err = -EISCONN; + goto ao_parse; + if (tp->repair) + goto ao_parse; + err = -EISCONN; + break; +ao_parse: + err = tp->af_specific->ao_parse(sk, optname, optval, optlen); break; } #endif @@ -4289,6 +4297,8 @@ int do_tcp_getsockopt(struct sock *sk, int level, return err; } #endif + case TCP_AO_REPAIR: + return tcp_ao_get_repair(sk, optval, optlen); case TCP_AO_GET_KEYS: case TCP_AO_INFO: { int err; diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 3490b590bff0..70ff7822f57a 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -1463,6 +1463,16 @@ static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) return ERR_PTR(-ESOCKTNOSUPPORT); } +static struct tcp_ao_info *getsockopt_ao_info(struct sock *sk) +{ + if (sk_fullsock(sk)) + return rcu_dereference(tcp_sk(sk)->ao_info); + else if (sk->sk_state == TCP_TIME_WAIT) + return rcu_dereference(tcp_twsk(sk)->ao_info); + + return ERR_PTR(-ESOCKTNOSUPPORT); +} + #define TCP_AO_KEYF_ALL (TCP_AO_KEYF_IFINDEX | TCP_AO_KEYF_EXCLUDE_OPT) #define TCP_AO_GET_KEYF_VALID (TCP_AO_KEYF_IFINDEX) @@ -1647,11 +1657,13 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, if (ret < 0) goto err_free_sock; - /* Change this condition if we allow adding keys in states - * like close_wait, syn_sent or fin_wait... - */ - if (sk->sk_state == TCP_ESTABLISHED) + if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) { tcp_ao_cache_traffic_keys(sk, ao_info, key); + if (first) { + ao_info->current_key = key; + ao_info->rnext_key = key; + } + } tcp_ao_link_mkt(ao_info, key); if (first) { @@ -1899,6 +1911,8 @@ static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, if (IS_ERR(ao_info)) return PTR_ERR(ao_info); if (!ao_info) { + if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) + return -EINVAL; ao_info = tcp_ao_alloc_info(GFP_KERNEL); if (!ao_info) return -ENOMEM; @@ -2281,3 +2295,73 @@ int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen) return 0; } +int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_ao_repair cmd; + struct tcp_ao_key *key; + struct tcp_ao_info *ao; + int err; + + if (optlen < sizeof(cmd)) + return -EINVAL; + + err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); + if (err) + return err; + + if (!tp->repair) + return -EPERM; + + ao = setsockopt_ao_info(sk); + if (IS_ERR(ao)) + return PTR_ERR(ao); + if (!ao) + return -ENOENT; + + WRITE_ONCE(ao->lisn, cmd.snt_isn); + WRITE_ONCE(ao->risn, cmd.rcv_isn); + WRITE_ONCE(ao->snd_sne, cmd.snd_sne); + WRITE_ONCE(ao->rcv_sne, cmd.rcv_sne); + + hlist_for_each_entry_rcu(key, &ao->head, node) + tcp_ao_cache_traffic_keys(sk, ao, key); + + return 0; +} + +int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_ao_repair opt; + struct tcp_ao_info *ao; + int len; + + if (copy_from_sockptr(&len, optlen, sizeof(int))) + return -EFAULT; + + if (len <= 0) + return -EINVAL; + + if (!tp->repair) + return -EPERM; + + rcu_read_lock(); + ao = getsockopt_ao_info(sk); + if (IS_ERR(ao)) + return PTR_ERR(ao); + if (!ao) { + rcu_read_unlock(); + return -ENOENT; + } + + opt.snt_isn = ao->lisn; + opt.rcv_isn = ao->risn; + opt.snd_sne = READ_ONCE(ao->snd_sne); + opt.rcv_sne = READ_ONCE(ao->rcv_sne); + rcu_read_unlock(); + + if (copy_to_sockptr(optval, &opt, min_t(int, len, sizeof(opt)))) + return -EFAULT; + return 0; +} From patchwork Fri Jul 21 16:19:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 123989 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9010:0:b0:3e4:2afc:c1 with SMTP id l16csp342519vqg; Fri, 21 Jul 2023 10:03:03 -0700 (PDT) X-Google-Smtp-Source: APBJJlEyD8ZSnJiCL/yqRZUOthgKy8VNZtkzIvRwrj/nzqECbDX0VrsHUt2MJRBdj72AwYsEMHs7 X-Received: by 2002:a17:907:c29:b0:993:f664:ce25 with SMTP id ga41-20020a1709070c2900b00993f664ce25mr11192818ejc.19.1689958982832; Fri, 21 Jul 2023 10:03:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1689958982; cv=none; d=google.com; s=arc-20160816; b=uEFgNEGnMBQ+x1PGKEmH56vCs5r9WVDRCz6SmnOYMhh91u0I15KwxMLm/fKbEQaRJd mT+63F7GYiTvYqRjAF7+/6ezb5ZtvjNI6vDuAjGlwZA0SLW2zF+03j/0wmFYgEbw8SSc 8RkHJe8hSj1xlcsW1lhVHie1q9Qarj7kR6+qsnlbWYgQEaCwl5gGfAmk+SQVhwiwkH72 aoIIZ1vCVPhkV/D2IQ9auuPZFGoVSnKjRePgt9otdFH0TgTVbKdUf+H6xPa/cyAIsJsd PZjuByADRyFMbBSevkqZBKekTmRvGUi+62UqNonm0lUekePWa/ItivXZD85pBhYOJHMS OI+w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=d2YS5vu9DZGoMCbuFGmvk4GxzIqJiJqogIBJL0vEpnc=; fh=+1v2wpWL3SwC67MevmNFbsOkJ0jlpe0KMhkxFTuop+s=; b=ghtFOFKAbim8xA4+UNvejf9Hm9MbWFUxav5jkntghlXOGYXOYXr88UytxKEqirYYrv c7oWMJAiyhJPngI/G0nqH7nzygWC9Wo/zFUqqSZ/1Yo66v9yodgoUm76ckWxlhCt0rYJ WJp6lr+cQypFgsFDKZJmF+xgdDuKkDw3FRWLgIZNSEeT1gxrLHlQEbN/ykzCocBtyBBf wXR8eWudIRdkXSqAv2tywbifoHRcxSkois04xNEnb7yeeV/Lk/nWLHlS+0QY+u9rAIaD IpneyOiSGSXBuCFh0kliOJp02jgiJYJlE1cXdmXHF8J+2Bjo/eg6yYaEENUgbEkKuLJX 2Qgw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=I6gHWtXA; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id jt11-20020a170906ca0b00b0099364d9f0e4si2458009ejb.278.2023.07.21.10.02.29; Fri, 21 Jul 2023 10:03:02 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@arista.com header.s=google header.b=I6gHWtXA; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=arista.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232890AbjGUQXt (ORCPT + 99 others); Fri, 21 Jul 2023 12:23:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51862 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232920AbjGUQWz (ORCPT ); Fri, 21 Jul 2023 12:22:55 -0400 Received: from mail-wm1-x32f.google.com (mail-wm1-x32f.google.com [IPv6:2a00:1450:4864:20::32f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 54BE14203 for ; Fri, 21 Jul 2023 09:20:11 -0700 (PDT) Received: by mail-wm1-x32f.google.com with SMTP id 5b1f17b1804b1-3fb4146e8fcso13642585e9.0 for ; Fri, 21 Jul 2023 09:20:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1689956405; x=1690561205; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=d2YS5vu9DZGoMCbuFGmvk4GxzIqJiJqogIBJL0vEpnc=; b=I6gHWtXARbAba66Aa8Jy26caf1OBDVvF58pFQfShjrT1uMLNVDeziB4z+JWk/CcOeh DK45xFvb3KZ1Lq11bobhNF8ugn8/MpuKGY8is3te7lq+ICu1YyhDtNISYEOZgqIu8Rq1 GMB1cbiR72w6PCqmPEQM72cfjawj2atjCuKgJgDDP3Q27j9Ojbi7g09gj5aGPz7WfvY9 kO6MrspPx71Z9EIDKezAblqCw/IUwF5k3jmp3SKdH9Dc/WM1APNS/qVrIKh0jfvTy2kX GylC8kXfZa621Xvmjg9FlJssJJ7KVY1jkfbxJBaKB6WzHyzhJXZM2FsgWvFWBWE0HReA zWfQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1689956405; x=1690561205; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=d2YS5vu9DZGoMCbuFGmvk4GxzIqJiJqogIBJL0vEpnc=; b=jd+ACteY2138yCo8T+63n981qowD4TNFGdfMzUyWN/2rXDga8E67IvBKYO865iuRgD Ra/rqjV83vGCsomrJKVjy1KIsw/YaAV1+A3kqWPijfuq5QKhVET5UqvOj5Sgx5aiSPNo nIcAEltyd6FcifICBakIv/Lzt+5hlCouPFazTz7skfLGXYU8EJJaibiqL8e2LewUUXZc IkMUi/c1IxSfFmEudvxj9VvZ1dbFk7qe2omzO0E8cmzf5msOiaRE2Bdoxqy7YC5K5C8H 6mYqmp44jhrbxbVE5X1HD9sjTx4b1eUM5/xoimfbXoMacn+Fi5Xoo8wZuJR3q9HGmYV0 C6+A== X-Gm-Message-State: ABy/qLb4Dh1o8ABd943xtcsBS/A21WTuyVmYpphvHYUoziZdOUXs5PRq w3zKto3w0HnnP2vZ8u0X7dbewg== X-Received: by 2002:a05:600c:1e12:b0:3fb:b075:8239 with SMTP id ay18-20020a05600c1e1200b003fbb0758239mr7580727wmb.4.1689956404619; Fri, 21 Jul 2023 09:20:04 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id l19-20020a7bc453000000b003fbc681c8d1sm6390210wmi.36.2023.07.21.09.20.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Jul 2023 09:20:04 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v8.1 net-next 23/23] Documentation/tcp: Add TCP-AO documentation Date: Fri, 21 Jul 2023 17:19:14 +0100 Message-ID: <20230721161916.542667-24-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230721161916.542667-1-dima@arista.com> References: <20230721161916.542667-1-dima@arista.com> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_NONE,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772050430563816948 X-GMAIL-MSGID: 1772050430563816948 It has Frequently Asked Questions (FAQ) on RFC 5925 - I found it very useful answering those before writing the actual code. It provides answers to common questions that arise on a quick read of the RFC, as well as how they were answered. There's also comparison to TCP-MD5 option, evaluation of per-socket vs in-kernel-DB approaches and description of uAPI provided. Hopefully, it will be as useful for reviewing the code as it was for writing. Signed-off-by: Dmitry Safonov --- Documentation/networking/index.rst | 1 + Documentation/networking/tcp_ao.rst | 422 ++++++++++++++++++++++++++++ 2 files changed, 423 insertions(+) create mode 100644 Documentation/networking/tcp_ao.rst diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst index 5b75c3f7a137..69c1e53ef88b 100644 --- a/Documentation/networking/index.rst +++ b/Documentation/networking/index.rst @@ -107,6 +107,7 @@ Contents: sysfs-tagging tc-actions-env-rules tc-queue-filters + tcp_ao tcp-thin team timestamping diff --git a/Documentation/networking/tcp_ao.rst b/Documentation/networking/tcp_ao.rst new file mode 100644 index 000000000000..819b4b630cbb --- /dev/null +++ b/Documentation/networking/tcp_ao.rst @@ -0,0 +1,422 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================================================== +TCP Authentication Option Linux implementation (RFC5925) +======================================================== + +TCP Authentication Option (TCP-AO) provides a TCP extension aimed at verifying +segments between trusted peers. It adds a new TCP header option with +a Message Authentication Code (MAC). MACs are produced from the content +of a TCP segment using a hashing function with a password known to both peers. +The intent of TCP-AO is to deprecate TCP-MD5 providing better security, +key rotation and support for variety of hashing algorithms. + +1. Introduction +=============== + +.. list-table:: Short and Limited Comparison of TCP-AO and TCP-MD5 + + * - + - TCP-MD5 + - TCP-AO + * - Supported hashing algorithms + - MD5 (cryptographically weak). + - Must support HMAC-SHA1 (chosen-prefix attacks) and CMAC-AES-128 + (only side-channel attacks). May support any hashing algorithm. + * - Length of MACs (bytes) + - 16 + - Typically 12-16. Possible other variants that fit TCP header. + * - Number of keys per TCP connection + - 1 + - Many + * - Possibility to change an active key + - Non-practical (both peers have to change them during MSL) + - Supported by protocol + * - Protection against ICMP 'hard errors' + - No + - Yes: ignoring them by default on established connections + * - Protection against traffic-crossing attack + - No + - Yes: pseudo-header includes TCP ports. + * - Protection against replayed TCP segments + - No + - Sequence Number Extension (SNE) and Initial Sequence Numbers (ISN) + * - Supports Connectionless Resets + - Yes + - No. ISNs+SNEs are needed to correctly sign RST. + * - Standards + - RFC 2385 + - RFC 5925, RFC 5926 + +1.1 Frequently Asked Questions (FAQ) with references to RFC 5925 +---------------------------------------------------------------- + +Q: Can either SendID or RecvID be non-unique for the same 4-tuple +(srcaddr, srcport, dstaddr, dstport)? + +A: No [3.1]:: + + >> The IDs of MKTs MUST NOT overlap where their TCP connection + identifiers overlap. + +Q: Can Master Key Tuple (MKT) for an active connection be removed? + +A: No, unless it's copied to Transport Control Block (TCB) [3.1]:: + + It is presumed that an MKT affecting a particular connection cannot + be destroyed during an active connection -- or, equivalently, that + its parameters are copied to an area local to the connection (i.e., + instantiated) and so changes would affect only new connections. + +Q: If an old MKT needs to be deleted, how should it be done in order +to not remove it for an active connection? (As it can be still in use +at any moment later) + +A: Not specified by RFC 5925, seems to be a problem for key management +to ensure that no one uses such MKT before trying to remove it. + +Q: Can an old MKT exist forever and be used by another peer? + +A: It can, it's a key management task to decide when to remove an old key [6.1]:: + + Deciding when to start using a key is a performance issue. Deciding + when to remove an MKT is a security issue. Invalid MKTs are expected + to be removed. TCP-AO provides no mechanism to coordinate their removal, + as we consider this a key management operation. + +also [6.1]:: + + The only way to avoid reuse of previously used MKTs is to remove the MKT + when it is no longer considered permitted. + +Linux TCP-AO will try its best to prevent you from removing a key that's +being used, considering it a key management failure. But sine keeping +an outdated key may become a security issue and as a peer may +unintentionally prevent the removal of an old key by always setting +it as RNextKeyID - a forced key removal mechanism is provided, where +userspace has to supply KeyID to use instead of the one that's being removed +and the kernel will atomically delete the old key, even if the peer is +still requesting it. There are no guarantees for force-delete as the peer +may yet not have the new key - the TCP connection may just break. +Alternatively, one may choose to shut down the socket. + +Q: What happens when a packet is received on a new connection with no known +MKT's RecvID? + +A: RFC 5925 specifies that by default it is accepted with a warning logged, but +the behaviour can be configured by the user [7.5.1.a]:: + + If the segment is a SYN, then this is the first segment of a new + connection. Find the matching MKT for this segment, using the segment's + socket pair and its TCP-AO KeyID, matched against the MKT's TCP connection + identifier and the MKT's RecvID. + + i. If there is no matching MKT, remove TCP-AO from the segment. + Proceed with further TCP handling of the segment. + NOTE: this presumes that connections that do not match any MKT + should be silently accepted, as noted in Section 7.3. + +[7.3]:: + + >> A TCP-AO implementation MUST allow for configuration of the behavior + of segments with TCP-AO but that do not match an MKT. The initial default + of this configuration SHOULD be to silently accept such connections. + If this is not the desired case, an MKT can be included to match such + connections, or the connection can indicate that TCP-AO is required. + Alternately, the configuration can be changed to discard segments with + the AO option not matching an MKT. + +[10.2.b]:: + + Connections not matching any MKT do not require TCP-AO. Further, incoming + segments with TCP-AO are not discarded solely because they include + the option, provided they do not match any MKT. + +Note that Linux TCP-AO implementation differs in this aspect. Currently, TCP-AO +segments with unknown key signatures are discarded with warnings logged. + +Q: Does the RFC imply centralized kernel key management in any way? +(i.e. that a key on all connections MUST be rotated at the same time?) + +A: Not specified. MKTs can be managed in userspace, the only relevant part to +key changes is [7.3]:: + + >> All TCP segments MUST be checked against the set of MKTs for matching + TCP connection identifiers. + +Q: What happens when RNextKeyID requested by a peer is unknown? Should +the connection be reset? + +A: It should not, no action needs to be performed [7.5.2.e]:: + + ii. If they differ, determine whether the RNextKeyID MKT is ready. + + 1. If the MKT corresponding to the segment’s socket pair and RNextKeyID + is not available, no action is required (RNextKeyID of a received + segment needs to match the MKT’s SendID). + +Q: How current_key is set and when does it change? It is a user-triggered +change, or is it by a request from the remote peer? Is it set by the user +explicitly, or by a matching rule? + +A: current_key is set by RNextKeyID [6.1]:: + + Rnext_key is changed only by manual user intervention or MKT management + protocol operation. It is not manipulated by TCP-AO. Current_key is updated + by TCP-AO when processing received TCP segments as discussed in the segment + processing description in Section 7.5. Note that the algorithm allows + the current_key to change to a new MKT, then change back to a previously + used MKT (known as "backing up"). This can occur during an MKT change when + segments are received out of order, and is considered a feature of TCP-AO, + because reordering does not result in drops. + +[7.5.2.e.ii]:: + + 2. If the matching MKT corresponding to the segment’s socket pair and + RNextKeyID is available: + + a. Set current_key to the RNextKeyID MKT. + +Q: If both peers have multiple MKTs matching the connection's socket pair +(with different KeyIDs), how should the sender/receiver pick KeyID to use? + +A: Some mechanism should pick the "desired" MKT [3.3]:: + + Multiple MKTs may match a single outgoing segment, e.g., when MKTs + are being changed. Those MKTs cannot have conflicting IDs (as noted + elsewhere), and some mechanism must determine which MKT to use for each + given outgoing segment. + + >> An outgoing TCP segment MUST match at most one desired MKT, indicated + by the segment’s socket pair. The segment MAY match multiple MKTs, provided + that exactly one MKT is indicated as desired. Other information in + the segment MAY be used to determine the desired MKT when multiple MKTs + match; such information MUST NOT include values in any TCP option fields. + +Q: Can TCP-MD5 connection migrate to TCP-AO (and vice-versa): + +A: No [1]:: + + TCP MD5-protected connections cannot be migrated to TCP-AO because TCP MD5 + does not support any changes to a connection’s security algorithm + once established. + +Q: If all MKTs are removed on a connection, can it become a non-TCP-AO signed +connection? + +A: [7.5.2] doesn't have the same choice as SYN packet handling in [7.5.1.i] +that would allow accepting segments without a sign (which would be insecure). +While switching to non-TCP-AO connection is not prohibited directly, it seems +what the RFC means. Also, there's a requirement for TCP-AO connections to +always have one current_key [3.3]:: + + TCP-AO requires that every protected TCP segment match exactly one MKT. + +[3.3]:: + + >> An incoming TCP segment including TCP-AO MUST match exactly one MKT, + indicated solely by the segment’s socket pair and its TCP-AO KeyID. + +[4.4]:: + + One or more MKTs. These are the MKTs that match this connection’s + socket pair. + +Q: Can a non-TCP-AO connection become a TCP-AO-enabled one? + +A: No: for already established non-TCP-AO connection it would be impossible +to switch using TCP-AO as the traffic key generation requires the initial +sequence numbers. Paraphrasing, starting using TCP-AO would require +re-establishing the TCP connection. + +2. In-kernel MKTs database vs database in userspace +=================================================== + +Linux TCP-AO support is implemented using ``setsockopt()s``, in a similar way +to TCP-MD5. It means that a userspace application that wants to use TCP-AO +should perform ``setsockopt()`` on a TCP socket when it wants to add, +remove or rotate MKTs. This approach moves the key management responsibility +to userspace as well as decisions on corner cases, i.e. what to do if +the peer doesn't respect RNextKeyID; moving more code to userspace, especially +responsible for the policy decisions. Besides, it's flexible and scales well +(with less locking needed than in the case of an in-kernel database). One also +should keep in mind that mainly intended users are BGP processes, not any +random applications, which means that compared to IPsec tunnels, +no transparency is really needed and modern BGP daemons already have +``setsockopt()s`` for TCP-MD5 support. + +.. list-table:: Considered pros and cons of the approaches + + * - + - ``setsockopt()`` + - in-kernel DB + * - Extendability + - ``setsockopt()`` commands should be extendable syscalls + - netlink messages are simple and extendable + * - Required userspace changes + - BGP or any application that wants TCP-AO needs to perform + ``setsockopt()s`` and do key management + - could be transparent as tunnels, providing something like + ``ip tcpao [add|delete|show|rotate] key`` + * - MKTs removal or adding + - harder for userspace + - harder for kernel + * - Dump-ability + - ``getsockopt()`` + - Netlink .dump() callback + * - Limits on kernel resources/memory + - equal + - equal + * - Scalability + - contention on ``TCP_LISTEN`` sockets + - contention on the whole database + * - Monitoring & warnings + - ``TCP_DIAG`` + - Same Netlink socket + * - Matching of MKTs + - Half-problem: only listen sockets + - Hard + +3. uAPI +======= + +Linux provides a set of ``setsockopt()s`` and ``getsockopt()s`` that let +userspace manage TCP-AO on a per-socket basis. In order to add/delete MKTs +``TCP_AO_ADD_KEY`` and ``TCP_AO_DEL_KEY`` TCP socket options must be used +It is not allowed to add a key on an established non-TCP-AO connection +as well as to remove the last key from TCP-AO connection. + +``setsockopt(TCP_AO_DEL_KEY)`` command may specify ``tcp_ao_del::current_key`` ++ ``tcp_ao_del::set_current`` and/or ``tcp_ao_del::rnext`` ++ ``tcp_ao_del::set_rnext`` which makes such delete "forced": it +provides userspace a way to delete a key that's being used and atomically set +another one instead. This is not intended for normal use and should be used +only when the peer ignores RNextKeyID and keeps requesting/using an old key. +It provides a way to force-delete a key that's not trusted but may break +the TCP-AO connection. + +The usual/normal key-rotation can be performed with ``setsockopt(TCP_AO_INFO)``. +It also provides a uAPI to change per-socket TCP-AO settings, such as +ignoring ICMPs, as well as clear per-socket TCP-AO packet counters. +The corresponding ``getsockopt(TCP_AO_INFO)`` can be used to get those +per-socket TCP-AO settings. + +Another useful command is ``getsockopt(TCP_AO_GET_KEYS)``. One can use it +to list all MKTs on a TCP socket or use a filter to get keys for a specific +peer and/or sndid/rcvid, VRF L3 interface or get current_key/rnext_key. + +To repair TCP-AO connections ``setsockopt(TCP_AO_REPAIR)`` is available, +provided that the user previously has checkpointed/dumped the socket with +``getsockopt(TCP_AO_REPAIR)``. + +A tip here for scaled TCP_LISTEN sockets, that may have some thousands TCP-AO +keys, is: use filters in ``getsockopt(TCP_AO_GET_KEYS)`` and asynchronous +delete with ``setsockopt(TCP_AO_DEL_KEY)``. + +Linux TCP-AO also provides a bunch of segment counters that can be helpful +with troubleshooting/debugging issues. Every MKT has good/bad counters +that reflect how many packets passed/failed verification. +Each TCP-AO socket has the following counters: +- for good segments (properly signed) +- for bad segments (failed TCP-AO verification) +- for segments with unknown keys +- for segments where an AO signature was expected, but wasn't found +- for the number of ignored ICMPs + +TCP-AO per-socket counters are also duplicated with per-netns counters, +exposed with SNMP. Those are ``TCPAOGood``, ``TCPAOBad``, ``TCPAOKeyNotFound``, +``TCPAORequired`` and ``TCPAODroppedIcmps``. + +4. ``setsockopt()`` vs ``accept()`` race +======================================== + +In contrast with TCP-MD5 established connection which has just one key, +TCP-AO connections may have many keys, which means that accepted connections +on a listen socket may have any amount of keys as well. As copying all those +keys on a first properly signed SYN would make the request socket bigger, that +would be undesirable. Currently, the implementation doesn't copy keys +to request sockets, but rather look them up on the "parent" listener socket. + +The result is that when userspace removes TCP-AO keys, that may break +not-yet-established connections on request sockets as well as not removing +keys from sockets that were already established, but not yet ``accept()``'ed, +hanging in the accept queue. + +The reverse is valid as well: if userspace adds a new key for a peer on +a listener socket, the established sockets in accept queue won't +have the new keys. + +At this moment, the resolution for the two races: +``setsockopt(TCP_AO_ADD_KEY)`` vs ``accept()`` +and ``setsockopt(TCP_AO_DEL_KEY)`` vs ``accept()`` is delegated to userspace. +This means that it's expected that userspace would check the MKTs on the socket +that was returned by ``accept()`` to verify that any key rotation that +happened on listen socket is reflected on the newly established connection. + +This is a similar "do-nothing" approach to TCP-MD5 from the kernel side and +may be changed later by introducing new flags to ``tcp_ao_add`` +and ``tcp_ao_del``. + +Note that this race is rare for it needs TCP-AO key rotation to happen +during the 3-way handshake for the new TCP connection. + +5. Interaction with TCP-MD5 +=========================== + +A TCP connection can not migrate between TCP-AO and TCP-MD5 options. The +established sockets that have either AO or MD5 keys are restricted for +adding keys of the other option. + +For listening sockets the picture is different: BGP server may want to receive +both TCP-AO and (deprecated) TCP-MD5 clients. As a result, both types of keys +may be added to TCP_CLOSED or TCP_LISTEN sockets. It's not allowed to add +different types of keys for the same peer. + +6. SNE Linux implementation +=========================== + +RFC 5925 [6.2] describes the algorithm of how to extend TCP sequence numbers +with SNE. In short: TCP has to track the previous sequence numbers and set +sne_flag when the current SEQ number rolls over. The flag is cleared when +both current and previous SEQ numbers cross 0x7fff, which is 32Kb. + +In times when sne_flag is set, the algorithm compares SEQ for each packet with +0x7fff and if it's higher than 32Kb, it assumes that the packet should be +verified with SNE before the increment. As a result, there's +this [0; 32Kb] window, when packets with (SNE - 1) can be accepted. + +Linux implementation simplifies this a bit: as the network stack already tracks +the first SEQ byte that ACK is wanted for (snd_una) and the next SEQ byte that +is wanted (rcv_nxt) - that's enough information for a rough estimation +on where in the 4GB SEQ number space both sender and receiver are. +When they roll over to zero, the corresponding SNE gets incremented. + +tcp_ao_compute_sne() is called for each TCP-AO segment. It compares SEQ numbers +from the segment with snd_una or rcv_nxt and fits the result into a 2GB window around them, +detecting SEQ numbers rolling over. That simplifies the code a lot and only +requires SNE numbers to be stored on every TCP-AO socket. + +The 2GB window at first glance seems much more permissive compared to +RFC 5926. But that is only used to pick the correct SNE before/after +a rollover. It allows more TCP segment replays, but yet all regular +TCP checks in tcp_sequence() are applied on the verified segment. +So, it trades a bit more permissive acceptance of replayed/retransmitted +segments for the simplicity of the algorithm and what seems better behaviour +for large TCP windows. + +7. Links +======== + +RFC 5925 The TCP Authentication Option + https://www.rfc-editor.org/rfc/pdfrfc/rfc5925.txt.pdf + +RFC 5926 Cryptographic Algorithms for the TCP Authentication Option (TCP-AO) + https://www.rfc-editor.org/rfc/pdfrfc/rfc5926.txt.pdf + +Draft "SHA-2 Algorithm for the TCP Authentication Option (TCP-AO)" + https://datatracker.ietf.org/doc/html/draft-nayak-tcp-sha2-03 + +RFC 2385 Protection of BGP Sessions via the TCP MD5 Signature Option + https://www.rfc-editor.org/rfc/pdfrfc/rfc2385.txt.pdf + +:Author: Dmitry Safonov