From patchwork Wed Feb 15 18:33:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142038 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 98ADDC636CC for ; Wed, 15 Feb 2023 18:34:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230199AbjBOSeB (ORCPT ); Wed, 15 Feb 2023 13:34:01 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49028 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229695AbjBOSdy (ORCPT ); Wed, 15 Feb 2023 13:33:54 -0500 Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4EB043A0A1 for ; Wed, 15 Feb 2023 10:33:47 -0800 (PST) Received: by mail-wm1-x32d.google.com with SMTP id r18so14003365wmq.5 for ; Wed, 15 Feb 2023 10:33:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=mrTPrXN8vyxihQKYg5wgSRUqcIF2OSEfY/pO2VW01oY=; b=C0OuCI393E0cPeqSqAnxWJsbW8vi40Lj1B7JsGxr+E0x6z2Uebnnc/vUfBb1ZU3C2g 4RZsx9N6dQOKxy1CZi5rBusigCbVy4PefjyGSrgKbda6gjEAcR0+UYQyFlIlxkm6jjaC pm0neam1JCFSL+d//Ers8+DuvJPbp8P9mWVLbgSR1l6Ijvv3LEjQ74K97Wp0TCLO1YN2 ZEGjpP8Iri/7cX2Slhgtk5L4XDjP6YZNlp4BIIrpqX6Fg9OaxGNqIeSTCbQu+/l8JMiP nY2spirT0AMOaLMsYH7NC13co39x/u27HS3Odfr8j6NV36zUlnXOQkCe+lG1rUPvlzYr s+1A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=mrTPrXN8vyxihQKYg5wgSRUqcIF2OSEfY/pO2VW01oY=; b=ItRHXvp5C5P4ntOXbgAyQs6wSUef4YYoDteEezQAP70e21xyx4Dk00zoeb/ZX3Kn4x wWAwZzy6Msp6g+6C4x0UDTGTV0lNUsEHX4DYuI1G/i6gTjriaZgR98RZKSAr2fhp3Q/3 PwIDjXdQ5XOSfaWzysat4NHaN/PSf5Nh6CnIK2fJfI6eA8PfbK6TKVNDDvxlFkOoNlp3 HGJNft2IHasdTLixTBVm+5a/3Y8KarxTioC/p95+iWVX/SxfNdZ8usbJaDS7zbj3rzB7 2DYaJwQJlB87qfl7KAh6scrbUzws6rze6zLppjup2cEmQ7QL4oJWb55ZleWaTWg+QgEg /aMQ== X-Gm-Message-State: AO0yUKXpoHzmpGZlHVboOynqrWtSdjkGQWjb5qUIIl6+ViScjuPOhUpm Deu5uzv5hO2x1EIdY6AVpS9I2A== X-Google-Smtp-Source: AK7set+UswmxRc3ApfjlNIdMIKVtS+CNH8pg6DX8f0wS/er++kAcOzvodBaPw1YqeymW2F0vzWubSg== X-Received: by 2002:a05:600c:1c9a:b0:3df:fc69:e96b with SMTP id k26-20020a05600c1c9a00b003dffc69e96bmr2994631wms.5.1676486025612; Wed, 15 Feb 2023 10:33:45 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.33.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:33:45 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org Subject: [PATCH v4 01/21] net/tcp: Prepare tcp_md5sig_pool for TCP-AO Date: Wed, 15 Feb 2023 18:33:15 +0000 Message-Id: <20230215183335.800122-2-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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. It as well needs a temporary/scratch buffer for preparing the hashing request. Extend tcp_md5sig_pool to support other hashing algorithms than MD5. Move it in a separate file. This patch was previously submitted as more generic crypto_pool [1], but Herbert nacked making it generic crypto API. His view is that crypto requests should be atomically allocated on fast-paths. So, in this version I don't move this pool anywhere outside TCP, only extending it for TCP-AO use-case. It can be converted once there will be per-request hashing crypto keys. [1]: https://lore.kernel.org/all/20230118214111.394416-1-dima@arista.com/T/#u Signed-off-by: Dmitry Safonov --- include/net/tcp.h | 48 ++++-- net/ipv4/Kconfig | 4 + net/ipv4/Makefile | 1 + net/ipv4/tcp.c | 103 +++--------- net/ipv4/tcp_ipv4.c | 97 +++++++----- net/ipv4/tcp_minisocks.c | 21 ++- net/ipv4/tcp_sigpool.c | 333 +++++++++++++++++++++++++++++++++++++++ net/ipv6/tcp_ipv6.c | 58 +++---- 8 files changed, 493 insertions(+), 172 deletions(-) create mode 100644 net/ipv4/tcp_sigpool.c diff --git a/include/net/tcp.h b/include/net/tcp.h index db9f828e9d1e..e77080003800 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1664,12 +1664,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); +/** + * 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 + */ +static inline void tcp_sigpool_end(void) +{ + rcu_read_unlock_bh(); +} +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); @@ -1725,17 +1748,14 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, #define tcp_twsk_md5_key(twsk) NULL #endif -bool tcp_alloc_md5sig_pool(void); - -struct tcp_md5sig_pool *tcp_get_md5sig_pool(void); -static inline void tcp_put_md5sig_pool(void) -{ - local_bh_enable(); -} +int tcp_md5_alloc_sigpool(void); +void tcp_md5_release_sigpool(void); +void tcp_md5_add_sigpool(void); +extern int tcp_md5_sigpool_id; -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_skb_data(struct tcp_sigpool *hp, + const struct sk_buff *skb, unsigned int header_len); +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 af7d2cf490fb..de8ba8cdc056 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -61,6 +61,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 33f559f491c8..5522c5ac24bc 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4413,98 +4413,42 @@ 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(tcp_md5_sigpool_id); -static void __tcp_alloc_md5sig_pool(void) +int tcp_md5_alloc_sigpool(void) { - struct crypto_ahash *hash; - int cpu; - - hash = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(hash)) - return; - - for_each_possible_cpu(cpu) { - void *scratch = per_cpu(tcp_md5sig_pool, cpu).scratch; - struct ahash_request *req; - - if (!scratch) { - scratch = kmalloc_node(sizeof(union tcp_md5sum_block) + - sizeof(struct tcphdr), - GFP_KERNEL, - cpu_to_node(cpu)); - if (!scratch) - return; - per_cpu(tcp_md5sig_pool, cpu).scratch = scratch; - } - if (per_cpu(tcp_md5sig_pool, cpu).md5_req) - continue; - - req = ahash_request_alloc(hash, GFP_KERNEL); - if (!req) - return; - - ahash_request_set_callback(req, 0, NULL, NULL); + size_t scratch_size; + int ret; - 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; } +EXPORT_SYMBOL(tcp_md5_alloc_sigpool); -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); +EXPORT_SYMBOL(tcp_md5_release_sigpool); - -/** - * 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); +EXPORT_SYMBOL(tcp_md5_add_sigpool); -int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp, +int tcp_md5_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->md5_req; + struct ahash_request *req = hp->req; unsigned int i; const unsigned int head_data_len = skb_headlen(skb) > header_len ? skb_headlen(skb) - header_len : 0; @@ -4538,16 +4482,17 @@ int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp, } EXPORT_SYMBOL(tcp_md5_hash_skb_data); -int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *key) +int tcp_md5_hash_key(struct 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 8320d0ecb13a..6701af5922cb 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1212,10 +1212,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; @@ -1237,8 +1233,13 @@ 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; @@ -1246,6 +1247,7 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, 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; } } @@ -1262,8 +1264,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; @@ -1272,6 +1278,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; } } @@ -1371,7 +1378,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) { @@ -1391,38 +1398,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(); return 0; clear_hash: - tcp_put_md5sig_pool(); -clear_hash_noput: + tcp_sigpool_end(); +clear_hash_nostart: memset(md5_hash, 0, 16); return 1; } @@ -1431,8 +1435,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; @@ -1445,30 +1448,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_md5_hash_skb_data(&hp, skb, th->doff << 2)) goto clear_hash; - if (tcp_md5_hash_key(hp, key)) + if (tcp_md5_hash_key(&hp, key)) goto clear_hash; - ahash_request_set_crypt(req, NULL, md5_hash, 0); - if (crypto_ahash_final(req)) + ahash_request_set_crypt(hp.req, NULL, md5_hash, 0); + if (crypto_ahash_final(hp.req)) goto clear_hash; - tcp_put_md5sig_pool(); + tcp_sigpool_end(); return 0; clear_hash: - tcp_put_md5sig_pool(); -clear_hash_noput: + tcp_sigpool_end(); +clear_hash_nostart: memset(md5_hash, 0, 16); return 1; } @@ -2285,6 +2286,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); @@ -2309,10 +2322,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 e002f2e1d4f2..0219c0e5e2df 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..2fbd6795ced6 --- /dev/null +++ b/net/ipv4/tcp_sigpool.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned long __scratch_size; +static DEFINE_PER_CPU(void __rcu *, sigpool_scratch); + +struct sigpool_entry { + struct ahash_request * __percpu *req; + const char *alg; + struct kref kref; + bool needs_key; +}; + +#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_alloc_ahash(struct sigpool_entry *e, const char *alg) +{ + struct crypto_ahash *hash, *cpu0_hash; + int cpu, ret = -ENOMEM; + + e->alg = kstrdup(alg, GFP_KERNEL); + if (!e->alg) + return -ENOMEM; + + e->req = alloc_percpu(struct ahash_request *); + if (!e->req) + goto out_free_alg; + + cpu0_hash = crypto_alloc_ahash(alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(cpu0_hash)) { + ret = PTR_ERR(cpu0_hash); + goto out_free_req; + } + + /* If hash has .setkey(), allocate ahash per-CPU, not only request */ + e->needs_key = crypto_ahash_get_flags(cpu0_hash) & CRYPTO_TFM_NEED_KEY; + + hash = cpu0_hash; + for_each_possible_cpu(cpu) { + struct ahash_request *req; + + /* If ahash has a key - it has to be allocated per-CPU. + * In such case re-use for CPU0 hash that just have been + * allocated above. + */ + if (!hash) + hash = crypto_alloc_ahash(alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(hash)) + goto out_free_per_cpu; + + req = ahash_request_alloc(hash, GFP_KERNEL); + if (!req) + goto out_free_hash; + + ahash_request_set_callback(req, 0, NULL, NULL); + + *per_cpu_ptr(e->req, cpu) = req; + + if (e->needs_key) + hash = NULL; + } + kref_init(&e->kref); + return 0; + +out_free_hash: + if (hash != cpu0_hash) + crypto_free_ahash(hash); + +out_free_per_cpu: + for_each_possible_cpu(cpu) { + struct ahash_request *req = *per_cpu_ptr(e->req, cpu); + struct crypto_ahash *pcpu_hash; + + if (!req) + break; + pcpu_hash = crypto_ahash_reqtfm(req); + ahash_request_free(req); + /* hash per-CPU, e->needs_key == true */ + if (pcpu_hash != cpu0_hash) + crypto_free_ahash(pcpu_hash); + } + + crypto_free_ahash(cpu0_hash); +out_free_req: + free_percpu(e->req); +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 && !strcmp(cpool[i].alg, alg)) { + 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) +{ + struct crypto_ahash *hash = NULL; + int cpu; + + for_each_possible_cpu(cpu) { + if (*per_cpu_ptr(e->req, cpu) == NULL) + continue; + + hash = crypto_ahash_reqtfm(*per_cpu_ptr(e->req, cpu)); + ahash_request_free(*per_cpu_ptr(e->req, cpu)); + if (e->needs_key) { + crypto_free_ahash(hash); + hash = NULL; + } + } + if (hash) + crypto_free_ahash(hash); + free_percpu(e->req); + kfree(e->alg); + memset(e, 0, sizeof(*e)); +} + +static void cpool_cleanup_work_cb(struct work_struct *work) +{ + unsigned int i; + bool free_scratch = true; + + mutex_lock(&cpool_mutex); + for (i = 0; i < 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) +{ + rcu_read_lock_bh(); + if (WARN_ON_ONCE(id > cpool_populated || !cpool[id].alg)) { + rcu_read_unlock_bh(); + return -EINVAL; + } + c->req = *this_cpu_ptr(cpool[id].req); + /* 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); + +/** + * 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) +{ + size_t ret = 0; + + /* slow-path */ + mutex_lock(&cpool_mutex); + if (cpool[id].alg) + ret = strscpy(buf, cpool[id].alg, buf_len); + mutex_unlock(&cpool_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(tcp_sigpool_algo); + +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 11b736a76bd7..116319d56ab2 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -672,7 +672,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) @@ -693,39 +693,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(); return 0; clear_hash: - tcp_put_md5sig_pool(); -clear_hash_noput: + tcp_sigpool_end(); +clear_hash_nostart: memset(md5_hash, 0, 16); return 1; } @@ -736,8 +733,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 */ @@ -749,30 +745,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_md5_hash_skb_data(&hp, skb, th->doff << 2)) goto clear_hash; - if (tcp_md5_hash_key(hp, key)) + if (tcp_md5_hash_key(&hp, key)) goto clear_hash; - ahash_request_set_crypt(req, NULL, md5_hash, 0); - if (crypto_ahash_final(req)) + ahash_request_set_crypt(hp.req, NULL, md5_hash, 0); + if (crypto_ahash_final(hp.req)) goto clear_hash; - tcp_put_md5sig_pool(); + tcp_sigpool_end(); return 0; clear_hash: - tcp_put_md5sig_pool(); -clear_hash_noput: + tcp_sigpool_end(); +clear_hash_nostart: memset(md5_hash, 0, 16); return 1; } From patchwork Wed Feb 15 18:33:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142039 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 66913C64EC4 for ; Wed, 15 Feb 2023 18:34:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230208AbjBOSeF (ORCPT ); Wed, 15 Feb 2023 13:34:05 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49046 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229772AbjBOSdz (ORCPT ); Wed, 15 Feb 2023 13:33:55 -0500 Received: from mail-wm1-x330.google.com (mail-wm1-x330.google.com [IPv6:2a00:1450:4864:20::330]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9A8BC3A862 for ; Wed, 15 Feb 2023 10:33:48 -0800 (PST) Received: by mail-wm1-x330.google.com with SMTP id f47-20020a05600c492f00b003dc584a7b7eso2281658wmp.3 for ; Wed, 15 Feb 2023 10:33:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=e9pks7/AIVF620Cp0OiOwRGXcuPgKxA3s+/9rsq6ezA=; b=BYwUZEH9SkgOKulUPV2icH/2v6TruPfxPoFvMNm4CtyUZdcyvWLLTK2ADMchVLgatB 3tGFdRMceJi2GWFTOzIumlau/HdtmHjqOPmU9NHZY3kQK1lP/jBsE88pDzdz6bmh9dbx Bvw24VxIzKo8dK8hUHq6NsTP8CRmOSUxImjNaizeV4NeXWRKGJUqZEQDHMmREdfEs15o mI0WoqtqDQngRBWTfig1X1zh3Q5KiKYMDCp7n5oIOBMCRlphBwzGJXiyXrOK0FwZS6Jl v75cWDFUwlFMj/h1Wz9rlStwOXkCPcK0BOes1SnpygPhBIZ6VIJ1ESrRDjFekc1dFcmA dllg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=e9pks7/AIVF620Cp0OiOwRGXcuPgKxA3s+/9rsq6ezA=; b=LHdYYmP75OzwE0KPzLD/hd4CZwEkCApnmbxdKuEVEQMAeE2XgR15S4eBGNh4akPZEI ZrhhEJwpmJ4lxkQ7PUHRTcyUbWaNXRECpzbd8+AqfNWi6H1jKnXv6w3XOZdfKh6WfUN7 swekb10NdFntjKBGOCMYlQ/czIIHwEfqFpTVJXmrIhhuuLE907XLdYWr7d9Hh+Az3y4f Ce4o0JHY8iygWq6u2Fk1Jrjeza2sPiKXAwaWdZSTjBBJ058+vfbuXlSfrRjrUbzdsuhX Tg1WKz405PKX6Zt9kFXS0RYePeGLAUWuiGVWKKEUMuQT2hBHe3I3KwFu84yk9L3+7JHO KqdA== X-Gm-Message-State: AO0yUKUXyfQE3Ldfta3yZ7BzU2Okqs6ktAaCVaItVYu+BLXjl71qAeaa x7yUzfOaP52TLPYZA8RrQBr6Hw== X-Google-Smtp-Source: AK7set+GOMPZuE9rYYHkw5Tb9XLVrFCAJmby8fKm8q6SC8EYUxqD9eBq1JGsnAr237bg+RQFAGSiXg== X-Received: by 2002:a05:600c:444b:b0:3dd:1c46:b92 with SMTP id v11-20020a05600c444b00b003dd1c460b92mr2871329wmn.16.1676486027071; Wed, 15 Feb 2023 10:33:47 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.33.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:33:46 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 02/21] tcp: Add TCP-AO config and structures Date: Wed, 15 Feb 2023 18:33:16 +0000 Message-Id: <20230215183335.800122-3-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 | 90 ++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/tcp.h | 2 + net/ipv4/Kconfig | 13 ++++++ 5 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 include/net/tcp_ao.h diff --git a/include/linux/tcp.h b/include/linux/tcp.h index ca7f05a130d2..ac742427bb39 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -435,13 +435,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 e77080003800..2d9ca039edc7 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -1615,12 +1616,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..27d8b4abef22 --- /dev/null +++ b/include/net/tcp_ao.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _TCP_AO_H +#define _TCP_AO_H + +#define TCP_AO_MAX_HASH_SIZE 64 +#define TCP_AO_KEY_ALIGN 1 +#define __tcp_ao_key_align __aligned(TCP_AO_KEY_ALIGN) + +union tcp_ao_addr { + struct in_addr a4; +#if IS_ENABLED(CONFIG_IPV6) + struct in6_addr a6; +#endif +}; + +struct tcp_ao_hdr { + u8 kind; + u8 length; + u8 keyid; + u8 rnext_keyid; +}; + +struct tcp_ao_key { + struct hlist_node node; + union tcp_ao_addr addr; + u8 key[TCP_AO_MAXKEYLEN] __tcp_ao_key_align; + unsigned int tcp_sigpool_id; + u16 port; + u8 prefixlen; + u8 family; + u8 keylen; + u8 keyflags; + u8 sndid; + u8 rcvid; + u8 maclen; + u8 digest_size; + struct rcu_head rcu; + u8 traffic_keys[]; +}; + +static inline u8 *rcv_other_key(struct tcp_ao_key *key) +{ + return key->traffic_keys; +} + +static inline u8 *snd_other_key(struct tcp_ao_key *key) +{ + return key->traffic_keys + key->digest_size; +} + +static inline int tcp_ao_maclen(const struct tcp_ao_key *key) +{ + return key->maclen; +} + +static inline int tcp_ao_sizeof_key(const struct tcp_ao_key *key) +{ + return sizeof(struct tcp_ao_key) + (TCP_AO_MAX_HASH_SIZE << 1); +} + +static inline int tcp_ao_len(const struct tcp_ao_key *key) +{ + return tcp_ao_maclen(key) + sizeof(struct tcp_ao_hdr); +} + +static inline unsigned int tcp_ao_digest_size(struct tcp_ao_key *key) +{ + return key->digest_size; +} + +struct tcp_ao_info { + struct hlist_head head; + struct rcu_head rcu; + /* current_key and rnext_key aren't maintained on listen sockets. + * Their purpose is to cache keys on established connections, + * saving needless lookups. Never dereference any of them from + * listen sockets. + */ + struct tcp_ao_key *volatile current_key; + struct tcp_ao_key *rnext_key; + u8 ao_flags; + __be32 lisn; + __be32 risn; + u32 snd_sne; + u32 snd_sne_seq; + u32 rcv_sne; + u32 rcv_sne_seq; +}; + +#endif /* _TCP_AO_H */ diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 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..2bcda91462c8 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 # 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 Wed Feb 15 18:33:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142041 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 575ACC636D7 for ; Wed, 15 Feb 2023 18:34:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230234AbjBOSe2 (ORCPT ); Wed, 15 Feb 2023 13:34:28 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49024 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230190AbjBOSd6 (ORCPT ); Wed, 15 Feb 2023 13:33:58 -0500 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 383BC38EB6 for ; Wed, 15 Feb 2023 10:33:51 -0800 (PST) Received: by mail-wm1-x32e.google.com with SMTP id z13so14021407wmp.2 for ; Wed, 15 Feb 2023 10:33:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=5+MJzdTIu8Me9v+Y53q9ByutXPqUIvFtsOyAMuyNr9g=; b=aKZ17OJnRlQ2lw8vOnuZsaaAs/FCyE4uxJKyR7FmvXSe4BUxTIKcrBW2pslMFH87xt nfjZwzUTWxiFmUEY1E1lJyXKykmp85yIQ0udK8KQN2TnHhP/e3ojrwZAaTB3vQcjlu6A T1MKZu5VxCSHdCzRH+qfMGyc9+JIyJxMc0M0YPZAMzn951HNAF5oRArrkzTRW95swen5 MSp0o/kHuVx4EQlt9wgWVamsk26sH3mm8Cfq0jv3w8VZcbQwV+18Sm0umffopDPzIQxx EMRZciIu2slPV3BweTK6CuRql5443aLp4dy2Ejnwuxkb22kpQxDECp+08DiNdYxcmoj/ Mk+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=5+MJzdTIu8Me9v+Y53q9ByutXPqUIvFtsOyAMuyNr9g=; b=Wt7R3L82Wfnq9JtJCdiqCcbNCPAPLjPaLdz124NvbX1MrlGt4GVxSP9I9YeE8xx8QK uZqy3PtBYh3R10NV2jCNjiDLsu52v9MCmPjQ0HNY6E5eJYUv0vJDfGOqP93B8flEkTbX EY5SSB2L+0+MVPfLasR1wwXwe+/L51rqT81dQ/jLrHk81N2tO/85SUKOOA0OdaDsZCIy q95sdBPiCsLjpLKEHLczuYJCHSQCrSNl3+T71L4SvkOXy/Bt3qSe/w5WEvnUnIZOf9TV u6fBCrkXu8q06HvG8Hmua91KBY5WeBJDj/upMFjeKF/f9pQqOEumLn1/aM3l/fkW+l8t 4z/w== X-Gm-Message-State: AO0yUKXiT9yxpQaVerT7PVs9ScW+YsPpVNmsBsaAs0N19LrYwDr+D3Gk vpsLwS1L8tGcNm8myYk4oKr6nQ== X-Google-Smtp-Source: AK7set/crYb+i60i0Ua/MfFOAVpZQ/jJhdCj+EWnnYFT8twniAZYOu1ILk08JZcb07yixEaQUR24zQ== X-Received: by 2002:a05:600c:2ac8:b0:3df:eb5d:fbf with SMTP id t8-20020a05600c2ac800b003dfeb5d0fbfmr2653269wme.38.1676486029543; Wed, 15 Feb 2023 10:33:49 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.33.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:33:48 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 03/21] net/tcp: Introduce TCP_AO setsockopt()s Date: Wed, 15 Feb 2023 18:33:17 +0000 Message-Id: <20230215183335.800122-4-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Add 3 setsockopt()s: 1. to add a new Master Key Tuple (MKT) on a socket 2. to delete present MKT from a socket 3. to change flags of an MKT Userspace has to introduce keys on every socket it wants to use TCP-AO option on, similarly to TCP_MD5SIG/TCP_MD5SIG_EXT. RFC5925 prohibits definition of MKTs that would match the same peer, so do sanity checks on the data provided by userspace. Be as conservative as possible, including refusal of defining MKT on an established connection with no AO, removing the key in-use and etc. (1) and (2) are to be used by userspace key manager to add/remove keys. (3) main purpose is to set rnext_key, which (as prescribed by RFC5925) is the key id that will be requested in TCP-AO header from the peer to sign their segments with. At this moment the life of ao_info ends in tcp_v4_destroy_sock(). Co-developed-by: Francesco Ruggeri 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 | 15 + include/uapi/linux/tcp.h | 42 ++ net/ipv4/Makefile | 1 + net/ipv4/tcp.c | 17 + net/ipv4/tcp_ao.c | 844 +++++++++++++++++++++++++++++++++++++++ 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, 1000 insertions(+), 17 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 2d9ca039edc7..fb18558053cc 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2092,6 +2092,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 27d8b4abef22..92c8c3ed42db 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -87,4 +87,19 @@ struct tcp_ao_info { u32 rcv_sne_seq; }; +#ifdef CONFIG_TCP_AO +int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, + sockptr_t optval, int optlen); +void tcp_ao_destroy_sock(struct sock *sk); +/* ipv4 specific functions */ +int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen); +/* ipv6 specific functions */ +int tcp_v6_parse_ao(struct sock *sk, int cmd, + sockptr_t optval, int optlen); +#else +static inline void tcp_ao_destroy_sock(struct sock *sk) +{ +} +#endif + #endif /* _TCP_AO_H */ diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 5655bfe28b8d..7de4558469e1 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -129,6 +129,9 @@ enum { #define TCP_TX_DELAY 37 /* delay outgoing packets by XX usec */ +#define TCP_AO 38 /* (Add/Set MKT) */ +#define TCP_AO_DEL 39 /* (Delete MKT) */ +#define TCP_AO_MOD 40 /* (Modify MKT) */ #define TCP_REPAIR_ON 1 #define TCP_REPAIR_OFF 0 @@ -350,6 +353,45 @@ struct tcp_diag_md5sig { #define TCP_AO_MAXKEYLEN 80 +#define TCP_AO_KEYF_IFINDEX (1 << 0) /* L3 ifindex for VRF */ + +#define TCP_AO_CMDF_CURR (1 << 0) /* Only checks field sndid */ +#define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */ + +struct tcp_ao { /* setsockopt(TCP_AO) */ + struct __kernel_sockaddr_storage tcpa_addr; + char tcpa_alg_name[64]; + __s32 tcpa_ifindex; + __u16 tcpa_flags; + __u8 tcpa_prefix; + __u8 tcpa_sndid; + __u8 tcpa_rcvid; + __u8 tcpa_maclen; + __u8 tcpa_keyflags; + __u8 tcpa_keylen; + __u8 tcpa_key[TCP_AO_MAXKEYLEN]; + __u32 reserved; /* padding, must be 0 */ +} __attribute__((aligned(8))); + +struct tcp_ao_del { /* setsockopt(TCP_AO_DEL) */ + struct __kernel_sockaddr_storage tcpa_addr; + __s32 tcpa_ifindex; + __u16 tcpa_flags; + __u8 tcpa_prefix; + __u8 tcpa_sndid; + __u8 tcpa_rcvid; + __u8 tcpa_current; + __u8 tcpa_rnext; + __u8 tcpa_keyflags; + __u32 reserved; /* padding, must be 0 */ +} __attribute__((aligned(8))); + +struct tcp_ao_mod { /* setsockopt(TCP_AO_MOD) */ + __u16 tcpa_flags; + __u8 tcpa_current; + __u8 tcpa_rnext; +} __attribute__((aligned(8))); + /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index de8ba8cdc056..223ed0fd5eae 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -68,6 +68,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 5522c5ac24bc..6d66719df5a2 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3713,6 +3713,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: + case TCP_AO_DEL: + case TCP_AO_MOD: { + /* 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..2dc8a3e946b1 --- /dev/null +++ b/net/ipv4/tcp_ao.c @@ -0,0 +1,844 @@ +// 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. + */ +struct tcp_ao_key *tcp_ao_matched_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); +} + +struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, + const union tcp_ao_addr *addr, + int family, int sndid, int rcvid, u16 port) +{ + struct tcp_ao_key *key; + struct tcp_ao_info *ao; + + ao = rcu_dereference_check(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (!ao) + return NULL; + + hlist_for_each_entry_rcu(key, &ao->head, node) { + if (!tcp_ao_key_cmp(key, addr, key->prefixlen, + family, sndid, rcvid, port)) + return key; + } + return NULL; +} +EXPORT_SYMBOL(tcp_ao_do_lookup); + +static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags, + struct tcp_ao_info *cloned_from) +{ + struct tcp_ao_info *ao; + + ao = kzalloc(sizeof(*ao), flags); + if (!ao) + return NULL; + INIT_HLIST_HEAD(&ao->head); + + if (cloned_from) + ao->ao_flags = cloned_from->ao_flags; + return ao; +} + +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 int tcp_ao_current_rnext(struct sock *sk, u16 tcpa_flags, + u8 tcpa_sndid, u8 tcpa_rcvid) +{ + struct tcp_ao_info *ao_info; + struct tcp_ao_key *key; + + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if ((tcpa_flags & (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT)) && !ao_info) + return -EINVAL; + + /* 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 (tcpa_flags & TCP_AO_CMDF_CURR) { + /* There aren't current/rnext keys on TCP_LISTEN sockets */ + if (sk->sk_state == TCP_LISTEN) + return -EINVAL; + key = tcp_ao_matched_key(ao_info, tcpa_sndid, -1); + if (!key) + return -ENOENT; + if (ao_info->current_key != key) + WRITE_ONCE(ao_info->current_key, key); + } + + if (tcpa_flags & TCP_AO_CMDF_NEXT) { + /* There aren't current/rnext keys on TCP_LISTEN sockets */ + if (sk->sk_state == TCP_LISTEN) + return -EINVAL; + key = tcp_ao_matched_key(ao_info, -1, tcpa_rcvid); + if (!key) + return -ENOENT; + if (ao_info->rnext_key != key) + WRITE_ONCE(ao_info->rnext_key, key); + } + + return 0; +} + +static int tcp_ao_verify_port(struct sock *sk, u16 port) +{ + struct inet_sock *inet = inet_sk(sk); + + if (port != 0) /* FIXME */ + return -EINVAL; + + /* Check that MKT port is consistent with socket */ + if (port != 0 && inet->inet_dport != 0 && port != inet->inet_dport) + return -EINVAL; + + return 0; +} + +static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao *cmd, + union tcp_md5_addr **addr, u16 *port) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->tcpa_addr; + struct inet_sock *inet = inet_sk(sk); + + if (sin->sin_family != AF_INET) + return -EINVAL; + + if (tcp_ao_verify_port(sk, ntohs(sin->sin_port))) + return -EINVAL; + + /* Check prefix and trailing 0's in addr */ + if (cmd->tcpa_prefix != 0) { + __be32 mask; + + if (sin->sin_addr.s_addr == INADDR_ANY) + return -EINVAL; + if (cmd->tcpa_prefix > 32) + return -EINVAL; + + mask = inet_make_mask(cmd->tcpa_prefix); + if (sin->sin_addr.s_addr & ~mask) + return -EINVAL; + + /* Check that MKT address is consistent with socket */ + if (inet->inet_daddr != INADDR_ANY && + (inet->inet_daddr & mask) != sin->sin_addr.s_addr) + return -EINVAL; + } else { + if (sin->sin_addr.s_addr != INADDR_ANY) + return -EINVAL; + } + + *addr = (union tcp_md5_addr *)&sin->sin_addr; + *port = ntohs(sin->sin_port); + return 0; +} + +static int tcp_ao_parse_crypto(struct tcp_ao *cmd, struct tcp_ao_key *key) +{ + unsigned int syn_tcp_option_space; + bool is_kdf_aes_128_cmac = false; + struct tcp_sigpool hp; + struct crypto_ahash *tfm; + int err, pool_id; + + /* Force null-termination of tcpa_alg_name */ + cmd->tcpa_alg_name[ARRAY_SIZE(cmd->tcpa_alg_name) - 1] = '\0'; + + /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ + if (!strcmp("cmac(aes128)", cmd->tcpa_alg_name)) { + strcpy(cmd->tcpa_alg_name, "cmac(aes)"); + is_kdf_aes_128_cmac = (cmd->tcpa_keylen != 16); + } + + key->maclen = cmd->tcpa_maclen ?: 12; /* 12 is the default in RFC5925 */ + + /* Check: maclen + tcp-ao header <= (MAX_TCP_OPTION_SPACE - mss + * - tstamp - wscale - sackperm), + * see tcp_syn_options(), tcp_synack_options(), commit 33ad798c924b. + * + * In order to allow D-SACK with TCP-AO, the header size should be: + * (MAX_TCP_OPTION_SPACE - TCPOLEN_TSTAMP_ALIGNED + * - TCPOLEN_SACK_BASE_ALIGNED + * - 2 * TCPOLEN_SACK_PERBLOCK) = 8 (maclen = 4), + * see tcp_established_options(). + * + * RFC5925, 2.2: + * Typical MACs are 96-128 bits (12-16 bytes), but any length + * that fits in the header of the segment being authenticated + * is allowed. + * + * RFC5925, 7.6: + * TCP-AO continues to consume 16 bytes in non-SYN segments, + * leaving a total of 24 bytes for other options, of which + * the timestamp consumes 10. This leaves 14 bytes, of which 10 + * are used for a single SACK block. When two SACK blocks are used, + * such as to handle D-SACK, a smaller TCP-AO MAC would be required + * to make room for the additional SACK block (i.e., to leave 18 + * bytes for the D-SACK variant of the SACK option) [RFC2883]. + * Note that D-SACK is not supportable in TCP MD5 in the presence + * of timestamps, because TCP MD5’s MAC length is fixed and too + * large to leave sufficient option space. + */ + syn_tcp_option_space = MAX_TCP_OPTION_SPACE; + syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED; + syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED; + syn_tcp_option_space -= TCPOLEN_SACKPERM_ALIGNED; + if (tcp_ao_len(key) > syn_tcp_option_space) + return -EMSGSIZE; + + key->keylen = cmd->tcpa_keylen; + memcpy(key->key, cmd->tcpa_key, cmd->tcpa_keylen); + + /* Full TCP header (th->doff << 2) should fit into scratch area, + * see tcp_ao_hash_header(). + */ + pool_id = tcp_sigpool_alloc_ahash(cmd->tcpa_alg_name, 60); + if (pool_id < 0) + return 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; + } + + if (is_kdf_aes_128_cmac) { + void *scratch = hp.scratch; + struct scatterlist sg; + + /* Using zero-key of 16 bytes as described in RFC5926 */ + memset(scratch, 0, 16); + sg_init_one(&sg, cmd->tcpa_key, cmd->tcpa_keylen); + + err = crypto_ahash_setkey(tfm, scratch, 16); + if (err) + goto err_pool_end; + + err = crypto_ahash_init(hp.req); + if (err) + goto err_pool_end; + + ahash_request_set_crypt(hp.req, &sg, key->key, cmd->tcpa_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; + + key->digest_size = crypto_ahash_digestsize(tfm); + tcp_sigpool_end(); + + /* TODO: remove TCP_AO_MAX_HASH_SIZE in favor of dynamically + * allocated buffer. + */ + if (key->digest_size > TCP_AO_MAX_HASH_SIZE) { + err = -ENOBUFS; + goto err_free_pool; + } + if (key->maclen > key->digest_size) { + err = -EINVAL; + goto err_free_pool; + } + + key->tcp_sigpool_id = pool_id; + return 0; + +err_pool_end: + tcp_sigpool_end(); +err_free_pool: + tcp_sigpool_release(pool_id); + return err; +} + +/* tcp_ao_mkt_overlap_v4() assumes cmd already went through tcp_ao_verify_ipv4. + * RFC5925 3.1 The IDs of MKTs MUST NOT overlap where their TCP connection + * identifiers overlap. + */ +static bool tcp_ao_mkt_overlap_v4(struct tcp_ao *cmd, + struct tcp_ao_info *ao_info) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->tcpa_addr; + __be32 addr = sin->sin_addr.s_addr; + __u8 prefix = cmd->tcpa_prefix; + __u16 port = ntohs(sin->sin_port); + __u8 sndid = cmd->tcpa_sndid; + __u8 rcvid = cmd->tcpa_rcvid; + struct tcp_ao_key *key; + + /* Check for TCP connection identifiers overlap */ + + hlist_for_each_entry_rcu(key, &ao_info->head, node) { + __be32 key_addr; + __be32 mask; + + /* Check for overlapping ids */ + if (key->sndid != sndid && key->rcvid != rcvid) + continue; + + key_addr = key->addr.a4.s_addr; + mask = inet_make_mask(min(prefix, key->prefixlen)); + + /* Check for overlapping addresses */ + if (addr == INADDR_ANY || key_addr == INADDR_ANY || + (addr & mask) == (key_addr & mask)) { + /* Check for overlapping ports */ + if (port == 0 || key->port == 0 || port == key->port) + return true; + } + } + + return false; +} + +#if IS_ENABLED(CONFIG_IPV6) +static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao *cmd, + union tcp_md5_addr **paddr, u16 *port, + unsigned short int *family) +{ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->tcpa_addr; + struct in6_addr *addr = &sin6->sin6_addr; + u8 prefix = cmd->tcpa_prefix; + + if (sin6->sin6_family != AF_INET6) + return -EINVAL; + /* Not supposed to happen: here from af-specific callback */ + if (WARN_ON_ONCE(!sk_fullsock(sk))) + return -EINVAL; + + if (tcp_ao_verify_port(sk, ntohs(sin6->sin6_port))) + return -EINVAL; + + /* Check prefix and trailing 0's in addr */ + if (cmd->tcpa_prefix != 0 && ipv6_addr_v4mapped(addr)) { + __be32 addr4 = addr->s6_addr32[3]; + __be32 mask; + + if (prefix > 32 || addr4 == INADDR_ANY) + return -EINVAL; + + mask = inet_make_mask(prefix); + if (addr4 & ~mask) + return -EINVAL; + + /* Check that MKT address is consistent with socket */ + if (!ipv6_addr_any(&sk->sk_v6_daddr)) { + __be32 daddr4 = sk->sk_v6_daddr.s6_addr32[3]; + + if (!ipv6_addr_v4mapped(&sk->sk_v6_daddr)) + return -EINVAL; + if ((daddr4 & mask) != addr4) + return -EINVAL; + } + + *paddr = (union tcp_md5_addr *)&addr->s6_addr32[3]; + *family = AF_INET; + *port = ntohs(sin6->sin6_port); + return 0; + } else if (cmd->tcpa_prefix != 0) { + struct in6_addr pfx; + + if (ipv6_addr_any(addr) || prefix > 128) + return -EINVAL; + + ipv6_addr_prefix(&pfx, addr, prefix); + if (ipv6_addr_cmp(&pfx, addr)) + return -EINVAL; + + /* Check that MKT address is consistent with socket */ + if (!ipv6_addr_any(&sk->sk_v6_daddr) && + !ipv6_prefix_equal(&sk->sk_v6_daddr, addr, prefix)) + + return -EINVAL; + } else { + if (!ipv6_addr_any(addr)) + return -EINVAL; + } + + *paddr = (union tcp_md5_addr *)addr; + *port = ntohs(sin6->sin6_port); + return 0; +} + +/* tcp_ao_mkt_overlap_v6() assumes cmd already went through tcp_ao_verify_ipv6. + * RFC5925 3.1 The IDs of MKTs MUST NOT overlap where their TCP connection + * identifiers overlap. + */ +static bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd, + struct tcp_ao_info *ao_info) +{ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->tcpa_addr; + struct in6_addr *addr = &sin6->sin6_addr; + bool v4_mapped = ipv6_addr_v4mapped(addr); + __u8 prefix = cmd->tcpa_prefix; + __u16 port = ntohs(sin6->sin6_port); + __u8 sndid = cmd->tcpa_sndid; + __u8 rcvid = cmd->tcpa_rcvid; + struct tcp_ao_key *key; + __be32 addr4 = v4_mapped ? addr->s6_addr32[3] : 0; + + hlist_for_each_entry_rcu(key, &ao_info->head, node) { + struct in6_addr pfx, key_pfx; + struct in6_addr *key_addr; + int min_prefixlen; + + /* Check for overlapping ids */ + if (key->sndid != sndid && key->rcvid != rcvid) + continue; + + key_addr = &key->addr.a6; + + if (v4_mapped) { + __be32 key_addr4; + __be32 mask; + + if (!ipv6_addr_v4mapped(key_addr)) + continue; + + key_addr4 = key_addr->s6_addr32[3]; + mask = inet_make_mask(min(prefix, key->prefixlen)); + + /* Check for overlapping addresses */ + if (addr4 == INADDR_ANY || key_addr4 == INADDR_ANY || + (addr4 & mask) == (key_addr4 & mask)) { + /* Check for overlapping ports */ + if (port == 0 || key->port == 0 || + port == key->port) + return true; + } + } else { + min_prefixlen = min(prefix, key->prefixlen); + ipv6_addr_prefix(&pfx, addr, min_prefixlen); + ipv6_addr_prefix(&key_pfx, key_addr, min_prefixlen); + + /* Check for overlapping addresses */ + if (ipv6_addr_any(addr) || ipv6_addr_any(key_addr) || + !ipv6_addr_cmp(&pfx, &key_pfx)) { + /* Check for overlapping ports */ + if (port == 0 || key->port == 0 || + port == key->port) + return true; + } + } + } + + return false; +} +#else +static inline int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao *cmd, + union tcp_md5_addr **paddr, u16 *port, + unsigned short int *family) +{ + return -EOPNOTSUPP; +} + +static inline bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd, + struct tcp_ao_info *ao_info) +{ + return false; +} +#endif + +#define TCP_AO_KEYF_ALL (0) +#define TCP_AO_CMDF_ADDMOD_VALID \ + (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT) +#define TCP_AO_CMDF_DEL_VALID \ + (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT) + +static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, + sockptr_t optval, int optlen) +{ + struct tcp_ao_info *ao_info; + union tcp_md5_addr *addr; + struct tcp_ao_key *key; + bool first = false; + struct tcp_ao cmd; + int ret, size; + u16 port; + + if (optlen < sizeof(cmd)) + return -EINVAL; + + ret = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); + if (ret) + return ret; + + if (cmd.tcpa_keylen > TCP_AO_MAXKEYLEN) + return -EINVAL; + + if (cmd.tcpa_flags & ~TCP_AO_CMDF_ADDMOD_VALID) + return -EINVAL; + + if (cmd.reserved != 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.tcpa_keyflags & ~TCP_AO_KEYF_ALL) + return -EINVAL; + + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + + if (!ao_info) { + ao_info = tcp_ao_alloc_info(GFP_KERNEL, NULL); + if (!ao_info) + return -ENOMEM; + first = true; + } else { + if (family == AF_INET) { + if (tcp_ao_mkt_overlap_v4(&cmd, ao_info)) + return -EEXIST; + } else { + if (tcp_ao_mkt_overlap_v6(&cmd, ao_info)) + return -EEXIST; + } + } + + /* TODO: We should add twice the key->diget_size instead of the max + * so rework this in a way to know the digest_size before allocating + * the tcp_ao_key struct. + */ + size = sizeof(struct tcp_ao_key) + (TCP_AO_MAX_HASH_SIZE << 1); + key = sock_kmalloc(sk, size, GFP_KERNEL); + if (!key) { + ret = -ENOMEM; + goto err_free_ao; + } + + INIT_HLIST_NODE(&key->node); + memcpy(&key->addr, addr, (family == AF_INET) ? sizeof(struct in_addr) : + sizeof(struct in6_addr)); + key->port = port; + key->prefixlen = cmd.tcpa_prefix; + key->family = family; + key->keyflags = cmd.tcpa_keyflags; + key->sndid = cmd.tcpa_sndid; + key->rcvid = cmd.tcpa_rcvid; + + ret = tcp_ao_parse_crypto(&cmd, key); + if (ret < 0) + goto err_free_sock; + + tcp_ao_link_mkt(ao_info, key); + if (first) { + sk_gso_disable(sk); + rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); + } + + /* Can't fail: the key with sndid/rcvid was just added */ + WARN_ON_ONCE(tcp_ao_current_rnext(sk, cmd.tcpa_flags, + cmd.tcpa_sndid, cmd.tcpa_rcvid)); + return 0; + +err_free_sock: + atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); + kfree(key); +err_free_ao: + if (first) + kfree(ao_info); + return ret; +} + +static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_key *key, + struct tcp_ao_info *ao_info, + struct tcp_ao_del *cmd) +{ + int err; + + hlist_del_rcu(&key->node); + + /* At this moment another CPU could have looked this key up + * while it was unlinked from the list. Wait for RCU grace period, + * after which the key is off-list and can't be looked up again; + * the rx path [just before RCU came] might have used it and set it + * as current_key (very unlikely). + */ + synchronize_rcu(); + err = tcp_ao_current_rnext(sk, cmd->tcpa_flags, + cmd->tcpa_current, cmd->tcpa_rnext); + if (err) + goto add_key; + + if (unlikely(READ_ONCE(ao_info->current_key) == key || + READ_ONCE(ao_info->rnext_key) == key)) { + err = -EBUSY; + goto add_key; + } + + atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); + call_rcu(&key->rcu, tcp_ao_key_free_rcu); + + return 0; +add_key: + hlist_add_head_rcu(&key->node, &ao_info->head); + return err; +} + +static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, + sockptr_t optval, int optlen) +{ + struct tcp_ao_info *ao_info; + struct tcp_ao_key *key; + struct tcp_ao_del cmd; + int err; + union tcp_md5_addr *addr; + __u8 prefix; + __be16 port; + int addr_len; + + if (optlen < sizeof(cmd)) + return -EINVAL; + + err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); + if (err) + return err; + + if (cmd.tcpa_flags & ~TCP_AO_CMDF_DEL_VALID) + return -EINVAL; + + if (cmd.reserved != 0) + return -EINVAL; + + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (!ao_info) + return -ENOENT; + + if (family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpa_addr; + + addr = (union tcp_md5_addr *)&sin->sin_addr; + addr_len = sizeof(struct in_addr); + port = ntohs(sin->sin_port); + } else { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpa_addr; + struct in6_addr *addr6 = &sin6->sin6_addr; + + if (ipv6_addr_v4mapped(addr6)) { + addr = (union tcp_md5_addr *)&addr6->s6_addr32[3]; + addr_len = sizeof(struct in_addr); + family = AF_INET; + } else { + addr = (union tcp_md5_addr *)addr6; + addr_len = sizeof(struct in6_addr); + } + port = ntohs(sin6->sin6_port); + } + prefix = cmd.tcpa_prefix; + + /* We could choose random present key here for current/rnext + * but that's less predictable. Let's be strict and don't + * allow removing a key that's in use. RFC5925 doesn't + * specify how-to coordinate key removal, but says: + * "It is presumed that an MKT affecting a particular + * connection cannot be destroyed during an active connection" + */ + hlist_for_each_entry_rcu(key, &ao_info->head, node) { + if (cmd.tcpa_sndid != key->sndid || + cmd.tcpa_rcvid != key->rcvid) + continue; + + if (family != key->family || + prefix != key->prefixlen || + port != key->port || + memcmp(addr, &key->addr, addr_len)) + continue; + + return tcp_ao_delete_key(sk, key, ao_info, &cmd); + } + return -ENOENT; +} + +static int tcp_ao_mod_cmd(struct sock *sk, unsigned short int family, + sockptr_t optval, int optlen) +{ + struct tcp_ao_info *ao_info; + struct tcp_ao_mod cmd; + int err; + + if (optlen < sizeof(cmd)) + return -EINVAL; + + err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); + if (err) + return err; + + if (cmd.tcpa_flags & ~TCP_AO_CMDF_ADDMOD_VALID) + return -EINVAL; + + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (!ao_info) + return -ENOENT; + /* TODO: make tcp_ao_current_rnext() and flags set atomic */ + return tcp_ao_current_rnext(sk, cmd.tcpa_flags, + cmd.tcpa_current, cmd.tcpa_rnext); +} + +int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, + sockptr_t optval, int optlen) +{ + if (WARN_ON_ONCE(family != AF_INET && family != AF_INET6)) + return -EAFNOSUPPORT; + + switch (cmd) { + case TCP_AO: + return tcp_ao_add_cmd(sk, family, optval, optlen); + case TCP_AO_DEL: + return tcp_ao_del_cmd(sk, family, optval, optlen); + case TCP_AO_MOD: + return tcp_ao_mod_cmd(sk, family, optval, optlen); + default: + WARN_ON_ONCE(1); + return -EINVAL; + } +} + +int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) +{ + return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen); +} + diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 6701af5922cb..60695942ee2b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2260,11 +2260,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 @@ -2279,7 +2284,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 @@ -2330,6 +2335,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 116319d56ab2..9edb84eb457b 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. @@ -243,7 +236,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 @@ -256,7 +249,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; @@ -770,7 +763,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, @@ -1230,7 +1229,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 @@ -1895,11 +1894,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 @@ -1921,11 +1925,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 @@ -1940,7 +1949,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 Wed Feb 15 18:33:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142040 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 02F30C636CC for ; Wed, 15 Feb 2023 18:34:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230226AbjBOSe0 (ORCPT ); Wed, 15 Feb 2023 13:34:26 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49064 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230138AbjBOSd5 (ORCPT ); Wed, 15 Feb 2023 13:33:57 -0500 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 DB3F639CEF for ; Wed, 15 Feb 2023 10:33:52 -0800 (PST) Received: by mail-wm1-x332.google.com with SMTP id z13so14021448wmp.2 for ; Wed, 15 Feb 2023 10:33:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=TOlhgDIYmtKFs9SA0Zk35wqmjyypaHp/x6eyU8/miKA=; b=PrBN7vDEdRdziV62LMlKC/T3An+Q9cNrhJKL8xuYIf+inYBy+KAHMbVQ6CtWaK2G8a ///LvENxrBaIOP5wu59n8fC5LzV4+YBPmv5x3XDcTkkxmQswTEZja6GkyAe0VHrOLFpV 29Jhryri+ZU9tLXQzqfj/Ic5sdXf2MBvt/A5B2wYs3HZoasLzHqqTB8fyOb+9WDapQ8o /ovRN8wKiov/OA5fB9msGrKitk99nBGVPBR2Ao1BD6Dz76+d0fyibl+OkSV4nrYs5JUe YiNcwfARGQ2F5WDxuwwxqpduQBUwWCsjxjuPWX4xX8N8Y2P6ENOOLo9l4MLkIOtdf08n 3u9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=TOlhgDIYmtKFs9SA0Zk35wqmjyypaHp/x6eyU8/miKA=; b=4Os3sZLRF6T4FjxxQBQmFMcx92HooEDool4zjOKnMGcyfngXb9QGMVU6g1OnvDlbAt MlnpvCAB5LVxuztxVjSoJTM5EkbXks0NXNyLkfZ98C3DkPmmd9LwrBsmjuy4A50DkXAP hywl9vEf/cLKiZfETBXFhhDl8CixG89F2ApyJ35EAf8Jw+lvHKw7w62GE3NyStcwiZ6R TocMMDXXvheNprZ1f0mNWLPd88Kr+qC7GkMhTC1zOcD2p5L4FwrnZ7j8Abynnuy9m0MB 4nFeXjCasKdBe3+CT0jJ3t4H2yc74ceaT/FYcvh+I8pT7HmgMoDGu1P25nFn0kQ6t5YF 0g6Q== X-Gm-Message-State: AO0yUKXjrsdbW6fripQStu8jg6LId2Ba1elueuYvzMhDoyaWgGzMo/fr +hkmOQP51e0zirnCKbR01dLpmw== X-Google-Smtp-Source: AK7set8wmFHPPBOnWsgzLkeL9htLP0DVsPY5yz7OdPjivFZ8aFP87xVFCGCYOgc1h6SZRbkdHknUAA== X-Received: by 2002:a05:600c:4f07:b0:3df:7948:886b with SMTP id l7-20020a05600c4f0700b003df7948886bmr2843138wmq.31.1676486031335; Wed, 15 Feb 2023 10:33:51 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.33.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:33:50 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 04/21] net/tcp: Prevent TCP-MD5 with TCP-AO being set Date: Wed, 15 Feb 2023 18:33:18 +0000 Message-Id: <20230215183335.800122-5-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 | 24 ++++++++++++++++++++++-- include/net/tcp_ao.h | 15 +++++++++++++++ net/ipv4/tcp_ao.c | 12 ++++++++++++ net/ipv4/tcp_ipv4.c | 12 +++++++++--- net/ipv4/tcp_output.c | 36 ++++++++++++++++++++++++++++++++++++ net/ipv6/tcp_ao.c | 17 +++++++++++++++++ net/ipv6/tcp_ipv6.c | 22 ++++++++++++++++++---- 7 files changed, 129 insertions(+), 9 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index fb18558053cc..2673cc8e5ed5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1701,6 +1701,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); @@ -1709,14 +1710,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 @@ -1734,6 +1744,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, @@ -2097,6 +2114,9 @@ struct tcp_sock_af_ops { int optname, sockptr_t optval, int optlen); + struct tcp_ao_key *(*ao_lookup)(const struct sock *sk, + struct sock *addr_sk, + int sndid, int rcvid); #endif }; diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 92c8c3ed42db..d817dac3bb0a 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -91,12 +91,27 @@ struct tcp_ao_info { int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, sockptr_t optval, int optlen); void tcp_ao_destroy_sock(struct sock *sk); +struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, + const union tcp_ao_addr *addr, + int family, int sndid, int rcvid, u16 port); /* ipv4 specific functions */ int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen); +struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, + int sndid, int rcvid); /* ipv6 specific functions */ +struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, + struct sock *addr_sk, + int sndid, int rcvid); int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen); #else +static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, + const union tcp_ao_addr *addr, + int family, int sndid, int rcvid, u16 port) +{ + return NULL; +} + static inline void tcp_ao_destroy_sock(struct sock *sk) { } diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 2dc8a3e946b1..4861ba9c89b9 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -165,6 +165,14 @@ void tcp_ao_destroy_sock(struct sock *sk) kfree_rcu(ao, rcu); } +struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, + int sndid, int rcvid) +{ + union tcp_ao_addr *addr = (union tcp_ao_addr *)&addr_sk->sk_daddr; + + return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid, 0); +} + static int tcp_ao_current_rnext(struct sock *sk, u16 tcpa_flags, u8 tcpa_sndid, u8 tcpa_rcvid) { @@ -619,6 +627,10 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, if (cmd.tcpa_keyflags & ~TCP_AO_KEYF_ALL) return -EINVAL; + /* Don't allow keys for peers that have a matching TCP-MD5 key */ + if (tcp_md5_do_lookup_any_l3index(sk, addr, family)) + return -EKEYREJECTED; + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, lockdep_sock_is_held(sk)); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 60695942ee2b..b143f5f0ca38 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1073,7 +1073,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; @@ -1092,7 +1092,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); @@ -1304,7 +1305,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; @@ -1374,6 +1375,10 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname, if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) return -EINVAL; + /* Don't allow keys for peers that have a matching TCP-AO key */ + if (tcp_ao_do_lookup(sk, addr, AF_INET, -1, -1, 0)) + return -EKEYREJECTED; + return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, cmd.tcpm_key, cmd.tcpm_keylen); } @@ -2268,6 +2273,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 71d01cf3c13e..3fe291e4a6a2 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3839,6 +3839,42 @@ 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); + + 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 9edb84eb457b..8b38c112affb 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -601,6 +601,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; @@ -655,13 +656,24 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) return -EINVAL; - if (ipv6_addr_v4mapped(&sin6->sin6_addr)) - return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], + if (ipv6_addr_v4mapped(&sin6->sin6_addr)) { + addr = (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3]; + + /* Don't allow keys for peers that have a matching TCP-AO key */ + if (tcp_ao_do_lookup(sk, addr, AF_INET, -1, -1, 0)) + return -EKEYREJECTED; + return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, cmd.tcpm_key, cmd.tcpm_keylen); + } + + addr = (union tcp_md5_addr *)&sin6->sin6_addr; + + /* Don't allow keys for peers that have a matching TCP-AO key */ + if (tcp_ao_do_lookup(sk, addr, AF_INET6, -1, -1, 0)) + return -EKEYREJECTED; - return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr, - AF_INET6, prefixlen, l3index, flags, + return tcp_md5_do_add(sk, addr, AF_INET6, prefixlen, l3index, flags, cmd.tcpm_key, cmd.tcpm_keylen); } @@ -1902,6 +1914,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 }; @@ -1933,6 +1946,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 Wed Feb 15 18:33:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142042 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 55535C64ED6 for ; Wed, 15 Feb 2023 18:34:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230243AbjBOSea (ORCPT ); Wed, 15 Feb 2023 13:34:30 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49104 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230197AbjBOSd7 (ORCPT ); Wed, 15 Feb 2023 13:33:59 -0500 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 0F0333A868 for ; Wed, 15 Feb 2023 10:33:55 -0800 (PST) Received: by mail-wm1-x333.google.com with SMTP id p5so1602955wmg.4 for ; Wed, 15 Feb 2023 10:33:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=ENK1E31GU+/D2KTodoiJfwqWQNNijjPlqFDc3lqSi5Y=; b=TCtB87qlr6hEPjb41Hbb4fEw2sGrAc8nndZu/S7PSpTzo7bQ3DYN4knBAjuHoIEqrq G34S4R6N8OVQLz0x2nHhpbECgMPyotWDq6nc31A1+HzZcpS2zDdbuhkarTQHSGdfB0fD BI4G16pLu4znzi9jkcFWMrSUbIB5YAJbZVuRksCqLKG0TwoteP0XH4Bb9akoJlzPkOWm S0QkzbZpCvfI4Y/zBmIKlu0QWwNXdv3a9glvzKti/H/1wS4y1+b+SrYzZaRmZaBedZlB zOE9lQ/3l12/ENTEJQBYh+mVpT7wRAZHLuDueWyDLhNz+4epFv1M/M7bM1os5VVj879u Tf0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=ENK1E31GU+/D2KTodoiJfwqWQNNijjPlqFDc3lqSi5Y=; b=h+jBub3Lxbf17rGB0ziKMmQA2S0XjS4uM0yOSjaIFL0SmTFxs36Tv646BenM3sI/Ks CcobgeqLmIST2a/hhhE88UsAb3ngQScs2bONRRELcqw6aeyJlCXCCS59dXz3FSOExiBl fnbClT+nrz6MebxlZie37vjHTbRcVlGvvnnPTDr5Hu7aR+GAaSgNZrL5gZLB96ALgWhg DF14n24s7PHiWiHuEmQ7qcRjflrGnG5BSI6ibq9Hvo6Zg/+RFYCZE4vVowFiVf4NnFC2 9qsxqY5dz1aVixOIgNWLfvwLoDEaWtYY7RLDFNTYLcP3D1SroawByYyqE1N2SOxHix9O BPXQ== X-Gm-Message-State: AO0yUKXhT1H0vnnCHb0xf1ZIViGAnRZIbW8PPlgHcUdx7ia5afWc8K3X HQ14SE1PuZNsixnQcWMi94i/Rw== X-Google-Smtp-Source: AK7set/8BFLYki+4lw+y76Jt51yhQS5DvJxPuHTQnJRbu9mpZx1v9zXx+vXdmLbR+Fxs432yFXdrkQ== X-Received: by 2002:a05:600c:600a:b0:3dc:4316:52be with SMTP id az10-20020a05600c600a00b003dc431652bemr2474748wmb.10.1676486033427; Wed, 15 Feb 2023 10:33:53 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.33.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:33:52 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 05/21] net/tcp: Calculate TCP-AO traffic keys Date: Wed, 15 Feb 2023 18:33:19 +0000 Message-Id: <20230215183335.800122-6-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 | 44 +++++++++- 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 | 40 +++++++++ net/ipv6/tcp_ipv6.c | 1 + 8 files changed, 283 insertions(+), 1 deletion(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 2673cc8e5ed5..e88d505e15b5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2117,6 +2117,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 d817dac3bb0a..0be6e626bba4 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -88,9 +88,33 @@ struct tcp_ao_info { }; #ifdef CONFIG_TCP_AO +/* TCP-AO structures and functions */ + +struct tcp4_ao_context { + __be32 saddr; + __be32 daddr; + __be16 sport; + __be16 dport; + __be32 sisn; + __be32 disn; +}; + +struct tcp6_ao_context { + struct in6_addr saddr; + struct in6_addr daddr; + __be16 sport; + __be16 dport; + __be32 sisn; + __be32 disn; +}; + int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, sockptr_t optval, int optlen); +int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, + unsigned int len); void tcp_ao_destroy_sock(struct sock *sk); +int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, + struct tcp_ao_key *ao_key); struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port); @@ -98,13 +122,23 @@ struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen); struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); +int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, + __be32 sisn, __be32 disn, bool send); /* ipv6 specific functions */ +int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, __be32 sisn, + __be32 disn, bool send); struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen); -#else +void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb); +void tcp_ao_connect_init(struct sock *sk); + +#else /* CONFIG_TCP_AO */ + static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port) @@ -115,6 +149,14 @@ static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, static inline void tcp_ao_destroy_sock(struct sock *sk) { } + +static inline void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb) +{ +} + +static inline void tcp_ao_connect_init(struct sock *sk) +{ +} #endif #endif /* _TCP_AO_H */ diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 4861ba9c89b9..c1406c3c61b9 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -16,6 +16,42 @@ #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 (tcp_sigpool_start(mkt->tcp_sigpool_id, &hp)) + goto clear_hash_noput; + + if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), + mkt->key, mkt->keylen)) + goto clear_hash; + + ret = crypto_ahash_init(hp.req); + if (ret) + goto clear_hash; + + sg_init_one(&sg, ctx, len); + ahash_request_set_crypt(hp.req, &sg, key, len); + crypto_ahash_update(hp.req); + + /* TODO: Revisit on how to get different output length */ + ret = crypto_ahash_final(hp.req); + if (ret) + goto clear_hash; + + tcp_sigpool_end(); + return 0; +clear_hash: + tcp_sigpool_end(); +clear_hash_noput: + 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. @@ -165,6 +201,58 @@ void tcp_ao_destroy_sock(struct sock *sk) kfree_rcu(ao, rcu); } +/* 4 tuple and ISNs are expected in NBO */ +static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, + __be32 saddr, __be32 daddr, + __be16 sport, __be16 dport, + __be32 sisn, __be32 disn) +{ + /* See RFC5926 3.1.1 */ + struct kdf_input_block { + u8 counter; + u8 label[6]; + struct tcp4_ao_context ctx; + __be16 outlen; + } __packed tmp; + + tmp.counter = 1; + memcpy(tmp.label, "TCP-AO", 6); + tmp.ctx.saddr = saddr; + tmp.ctx.daddr = daddr; + tmp.ctx.sport = sport; + tmp.ctx.dport = dport; + tmp.ctx.sisn = sisn; + tmp.ctx.disn = disn; + tmp.outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ + + return tcp_ao_calc_traffic_key(mkt, key, &tmp, sizeof(tmp)); +} + +int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, + __be32 sisn, __be32 disn, bool send) +{ + if (send) + return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr, + sk->sk_daddr, htons(sk->sk_num), + sk->sk_dport, sisn, disn); + else + return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr, + sk->sk_rcv_saddr, sk->sk_dport, + htons(sk->sk_num), disn, sisn); +} + +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); + else + return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); +} + struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid) { @@ -173,6 +261,103 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid, 0); } +int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, + struct tcp_ao_key *ao_key) +{ + u8 *traffic_key = snd_other_key(ao_key); + int ret; + + ret = tcp_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); + ao_info->snd_sne = 0; + ao_info->snd_sne_seq = 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; + + ao->rcv_sne = 0; + ao->rcv_sne_seq = ntohl(tcp_hdr(skb)->seq); + + hlist_for_each_entry_rcu(key, &ao->head, node) { + tcp_ao_cache_traffic_keys(sk, ao, key); + } +} + static int tcp_ao_current_rnext(struct sock *sk, u16 tcpa_flags, u8 tcpa_sndid, u8 tcpa_rcvid) { @@ -674,6 +859,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 cc072d2cfcd8..6baeb3fe4352 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6064,6 +6064,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 b143f5f0ca38..0af5a3c97c43 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2275,6 +2275,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 3fe291e4a6a2..07f0521ffc79 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3663,6 +3663,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..554798647b78 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -12,6 +12,46 @@ #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; + + tmp.counter = 1; + memcpy(tmp.label, "TCP-AO", 6); + tmp.ctx.saddr = *saddr; + tmp.ctx.daddr = *daddr; + tmp.ctx.sport = sport; + tmp.ctx.dport = dport; + tmp.ctx.sisn = sisn; + tmp.ctx.disn = disn; + tmp.outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ + + return tcp_ao_calc_traffic_key(mkt, key, &tmp, sizeof(tmp)); +} + +int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, __be32 sisn, + __be32 disn, bool send) +{ + if (send) + return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_rcv_saddr, + &sk->sk_v6_daddr, htons(sk->sk_num), + sk->sk_dport, sisn, disn); + else + return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_daddr, + &sk->sk_v6_rcv_saddr, sk->sk_dport, + htons(sk->sk_num), disn, sisn); +} + struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk, const struct in6_addr *addr, int sndid, int rcvid) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 8b38c112affb..468ace0f872c 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 = { #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 Wed Feb 15 18:33:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142044 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D5E07C64EC4 for ; Wed, 15 Feb 2023 18:34:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230267AbjBOSec (ORCPT ); Wed, 15 Feb 2023 13:34:32 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49164 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230016AbjBOSeC (ORCPT ); Wed, 15 Feb 2023 13:34:02 -0500 Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7DBF53CE34 for ; Wed, 15 Feb 2023 10:33:55 -0800 (PST) Received: by mail-wm1-x32d.google.com with SMTP id f18-20020a7bcd12000000b003e206711347so1280408wmj.0 for ; Wed, 15 Feb 2023 10:33:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=UA3ZLiIdUx/XFvkT+v5Dr7ioOY0jPy3YWvKBSEvVI7o=; b=HRwddFs29X7hGWzUKms4xV8fBZna523hQuN/tYggr8ZrZ9uFPTKw93GnEyJLOb9VKQ 6I7Nt9rQILXCHUuEyb3k8uvEhleAoLddZkQznCMbeilIKQa7rkJZpok3iaUfiplE8nlo fkXiaHsICsw/9A12nndbjTxdUobbYzKTvmJMQkj8Gas1nl9XY2KO3axS+Ce2UZFiW5g8 hxn2davLNPlk4HUXxE1s5aUTVD2iXBTVZUMjn6ktsco4VSgt4V8a0XtomubjpzckIz3K t0/kbFblbq1j1sn7Q07mnVLq+osXGgfuT6TsUgmI4cPg7N5q+tCpIif5MR0efrBdzBe+ /YtQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=UA3ZLiIdUx/XFvkT+v5Dr7ioOY0jPy3YWvKBSEvVI7o=; b=3Ql9zbkYLpldaL2yC6yH95f+KWBD1JTcKYQloilNVa6Wc7Aycb+XHahBPEoK3Th+Lv rImY541rWylwNj13fbnD6CxoGlrNZY/PN/0IvuKQV3vfNt4LMzzDmTs6wsvXWDM+2fYC IoNEB5UTkbI4ShLVWa3MBb+vu2n404ztPmbpOlj3w6v3rCSUf3RB5jy7zjjD20kUcHhN BVZRfOqQwRgiVgEYv0636zduBYmP/R+hxPpvEYTp9gaSe7QeceFNgjrtpFTR3KIlhQIO L58bZOyeQ/S2Zv083iYNgd09UTO2SLzj/3lgevrArsTbnrjFLyajI7y28zF1vOjmkfQq 2gtw== X-Gm-Message-State: AO0yUKV26MbjK3hgN0N92HBVxd/r7apQR7o6tVDSj+Ro0ABlx+FUg8nz qLm0xYslGvBPLv88+EyWovHv5w== X-Google-Smtp-Source: AK7set/pkMeoul5nHcwjxDtlx/oMDqYURmrp2e4hKIeNoGHGBWIsOSkWDXZ53TKx3K4k3qDwZMowow== X-Received: by 2002:a05:600c:4a9b:b0:3dd:dd46:171b with SMTP id b27-20020a05600c4a9b00b003dddd46171bmr3293239wmp.10.1676486035005; Wed, 15 Feb 2023 10:33:55 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.33.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:33:54 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 06/21] net/tcp: Add TCP-AO sign to outgoing packets Date: Wed, 15 Feb 2023 18:33:20 +0000 Message-Id: <20230215183335.800122-7-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 | 15 +++ net/ipv4/tcp_ao.c | 216 ++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 118 ++++++++++++++++++++--- net/ipv6/tcp_ao.c | 28 ++++++ net/ipv6/tcp_ipv6.c | 2 + 7 files changed, 376 insertions(+), 11 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index e88d505e15b5..fa26e18528a1 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -187,6 +187,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 */ @@ -2117,6 +2118,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 0be6e626bba4..e233e7ff3184 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -108,11 +108,16 @@ struct tcp6_ao_context { __be32 disn; }; +int tcp_ao_hash_skb(unsigned short int family, + char *ao_hash, struct tcp_ao_key *key, + const struct sock *sk, const struct sk_buff *skb, + const u8 *tkey, int hash_offset, u32 sne); int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, sockptr_t optval, int optlen); int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, unsigned int len); void tcp_ao_destroy_sock(struct sock *sk); +u32 tcp_ao_compute_sne(u32 sne, u32 seq, u32 new_seq); int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key); struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, @@ -125,13 +130,23 @@ 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 */ +struct tcp_sigpool; +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 c1406c3c61b9..e8823f851bc9 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -253,6 +253,222 @@ int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); } +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; +} + +u32 tcp_ao_compute_sne(u32 prev_sne, u32 prev_seq, u32 seq) +{ + u32 sne = prev_sne; + + if (before(seq, prev_seq)) { + if (seq > prev_seq) + sne--; + } else { + if (seq < prev_seq) + sne++; + } + + return sne; +} + +/* tcp_ao_hash_sne(struct 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; +} + +static int tcp_ao_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; + + WARN_ON(skb_headlen(skb) < header_len); + + sg_init_table(&sg, 1); + + sg_set_buf(&sg, ((u8 *)tp) + header_len, head_data_len); + ahash_request_set_crypt(req, &sg, NULL, head_data_len); + if (crypto_ahash_update(req)) + return 1; + + for (i = 0; i < shi->nr_frags; ++i) { + const skb_frag_t *f = &shi->frags[i]; + unsigned int offset = skb_frag_off(f); + struct page *page = skb_frag_page(f) + (offset >> PAGE_SHIFT); + + sg_set_page(&sg, page, skb_frag_size(f), + offset_in_page(offset)); + ahash_request_set_crypt(req, &sg, NULL, skb_frag_size(f)); + if (crypto_ahash_update(req)) + return 1; + } + + skb_walk_frags(skb, frag_iter) + if (tcp_ao_hash_skb_data(hp, frag_iter, 0)) + return 1; + + return 0; +} + +int tcp_ao_hash_skb(unsigned short int family, + char *ao_hash, struct tcp_ao_key *key, + const struct sock *sk, const struct sk_buff *skb, + const u8 *tkey, int hash_offset, u32 sne) +{ + const struct tcphdr *th = tcp_hdr(skb); + int tkey_len = tcp_ao_digest_size(key); + __u8 tmp_hash[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + struct tcp_sigpool hp; + + 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_ao_hash_skb_data(&hp, skb, th->doff << 2)) + goto clear_hash; + ahash_request_set_crypt(hp.req, NULL, tmp_hash, 0); + if (crypto_ahash_final(hp.req)) + goto clear_hash; + + memcpy(ao_hash, tmp_hash, tcp_ao_maclen(key)); + tcp_sigpool_end(); + return 0; + +clear_hash: + tcp_sigpool_end(); +clear_hash_noput: + memset(ao_hash, 0, tcp_ao_maclen(key)); + return 1; +} +EXPORT_SYMBOL(tcp_ao_hash_skb); + +int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, + const struct sock *sk, const struct sk_buff *skb, + const u8 *tkey, int hash_offset, u32 sne) +{ + return tcp_ao_hash_skb(AF_INET, ao_hash, key, sk, skb, + tkey, hash_offset, sne); +} + struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 0af5a3c97c43..e999e00a5398 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2274,6 +2274,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 07f0521ffc79..23763938f931 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -413,6 +413,7 @@ static inline bool tcp_urg_mode(const struct tcp_sock *tp) #define OPTION_FAST_OPEN_COOKIE BIT(8) #define OPTION_SMC BIT(9) #define OPTION_MPTCP BIT(10) +#define OPTION_AO BIT(11) static void smc_options_write(__be32 *ptr, u16 *options) { @@ -605,7 +606,8 @@ static void bpf_skops_write_hdr_opt(struct sock *sk, struct sk_buff *skb, * (but it may well be that other scenarios fail similarly). */ static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp, - struct tcp_out_options *opts) + struct tcp_out_options *opts, + struct tcp_ao_key *ao_key) { __be32 *ptr = (__be32 *)(th + 1); u16 options = opts->options; /* mungable copy */ @@ -617,7 +619,29 @@ 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_info *ao_info; + u8 maclen; + + ao_info = rcu_dereference_check(tp->ao_info, + lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk)); + if (WARN_ON_ONCE(!ao_key || !ao_info || !ao_info->rnext_key)) + goto out_ao; + maclen = tcp_ao_maclen(ao_key); + *ptr++ = htonl((TCPOPT_AO << 24) | + (tcp_ao_len(ao_key) << 16) | + (ao_key->sndid << 8) | + (ao_info->rnext_key->rcvid)); + opts->hash_location = (__u8 *)ptr; + ptr += maclen / sizeof(*ptr); + if (unlikely(maclen % sizeof(*ptr))) { + memset(ptr, TCPOPT_NOP, sizeof(*ptr)); + ptr++; + } + } +out_ao: +#endif if (unlikely(opts->mss)) { *ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | @@ -758,7 +782,8 @@ static void mptcp_set_option_cond(const struct request_sock *req, */ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, struct tcp_out_options *opts, - struct tcp_md5sig_key **md5) + struct tcp_md5sig_key **md5, + struct tcp_ao_key *ao_key) { struct tcp_sock *tp = tcp_sk(sk); unsigned int remaining = MAX_TCP_OPTION_SPACE; @@ -775,6 +800,12 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, } } #endif +#ifdef CONFIG_TCP_AO + if (ao_key) { + opts->options |= OPTION_AO; + remaining -= tcp_ao_len(ao_key); + } +#endif /* We always get an MSS option. The option bytes which will be seen in * normal data packets should timestamps be used, must be in the MSS @@ -842,6 +873,7 @@ static unsigned int tcp_synack_options(const struct sock *sk, unsigned int mss, struct sk_buff *skb, struct tcp_out_options *opts, const struct tcp_md5sig_key *md5, + const struct tcp_ao_key *ao, struct tcp_fastopen_cookie *foc, enum tcp_synack_type synack_type, struct sk_buff *syn_skb) @@ -863,6 +895,14 @@ static unsigned int tcp_synack_options(const struct sock *sk, ireq->tstamp_ok &= !ireq->sack_ok; } #endif +#ifdef CONFIG_TCP_AO + if (ao) { + opts->options |= OPTION_AO; + remaining -= tcp_ao_len(ao); + ireq->tstamp_ok &= !ireq->sack_ok; + } +#endif + WARN_ON_ONCE(md5 && ao); /* We always send an MSS option. */ opts->mss = mss; @@ -912,7 +952,8 @@ static unsigned int tcp_synack_options(const struct sock *sk, */ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb, struct tcp_out_options *opts, - struct tcp_md5sig_key **md5) + struct tcp_md5sig_key **md5, + struct tcp_ao_key *ao_key) { struct tcp_sock *tp = tcp_sk(sk); unsigned int size = 0; @@ -931,6 +972,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; @@ -1242,6 +1289,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; @@ -1273,11 +1324,17 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, tcb = TCP_SKB_CB(skb); memset(&opts, 0, sizeof(opts)); +#ifdef CONFIG_TCP_AO + ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (ao) + ao_key = ao->current_key; +#endif if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) { - tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5); + tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5, ao_key); } else { tcp_options_size = tcp_established_options(sk, skb, &opts, - &md5); + &md5, ao_key); /* Force a PSH flag on all (GSO) packets to expedite GRO flush * at receiver : This slightly improve GRO performance. * Note that we do not force the PSH flag for non GSO packets, @@ -1351,7 +1408,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 */ @@ -1361,6 +1418,32 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, md5, sk, skb); } #endif +#ifdef CONFIG_TCP_AO + if (ao) { + u8 *traffic_key; + u8 key_buf[TCP_AO_MAX_HASH_SIZE]; + u32 sne; + __u32 disn; + + sk_gso_disable(sk); + if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) { + if (tcb->tcp_flags & TCPHDR_ACK) + disn = ao->risn; + else + disn = 0; + traffic_key = key_buf; + tp->af_specific->ao_calc_key_sk(ao_key, traffic_key, + sk, ao->lisn, disn, true); + } else { + traffic_key = snd_other_key(ao_key); + } + sne = tcp_ao_compute_sne(ao->snd_sne, ao->snd_sne_seq, + ntohl(th->seq)); + tp->af_specific->calc_ao_hash(opts.hash_location, ao_key, sk, skb, + traffic_key, + opts.hash_location - (u8 *)th, sne); + } +#endif /* BPF prog is the last one writing header option */ bpf_skops_write_hdr_opt(sk, skb, NULL, NULL, 0, &opts); @@ -1822,6 +1905,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; @@ -1830,8 +1917,17 @@ unsigned int tcp_current_mss(struct sock *sk) if (mtu != inet_csk(sk)->icsk_pmtu_cookie) mss_now = tcp_sync_mss(sk, mtu); } - - header_len = tcp_established_options(sk, NULL, &opts, &md5) + +#ifdef CONFIG_TCP_AO + ao_info = rcu_dereference_check(tp->ao_info, lockdep_sock_is_held(sk)); + if (ao_info) + /* TODO: verify if we can access current_key or we need to pass + * it from every caller of tcp_current_mss instead. The reason + * is that the current_key pointer can change asynchronously + * from the rx path. + */ + ao_key = ao_info->current_key; +#endif + header_len = tcp_established_options(sk, NULL, &opts, &md5, ao_key) + sizeof(struct tcphdr); /* The mss_cache is sized based on tp->tcp_header_len, which assumes * some common options. If this is an odd packet (because we have SACK @@ -3582,7 +3678,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); @@ -3603,7 +3699,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 554798647b78..061ca5369426 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -7,6 +7,7 @@ * Francesco Ruggeri * Salam Noureddine */ +#include #include #include @@ -69,6 +70,33 @@ struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); } +int tcp_v6_ao_hash_pseudoheader(struct 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 468ace0f872c..50d394d9d5fc 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1915,6 +1915,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 @@ -1948,6 +1949,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 Wed Feb 15 18:33:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142043 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 00809C636D7 for ; Wed, 15 Feb 2023 18:34:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230287AbjBOSed (ORCPT ); Wed, 15 Feb 2023 13:34:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49046 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230203AbjBOSeD (ORCPT ); Wed, 15 Feb 2023 13:34:03 -0500 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 29B97392AC for ; Wed, 15 Feb 2023 10:33:57 -0800 (PST) Received: by mail-wm1-x332.google.com with SMTP id z13so14021621wmp.2 for ; Wed, 15 Feb 2023 10:33:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=saDcL6Ex8q+zWnP2cZdXfAl26yMgKf0Lx6urXy6fa50=; b=jp2TK5i5jdqhsjCAPnTUX1gXLCW6jgXvIXQCpHjHGfMRL9ibhZEwDb8mmbN+iqbXLm oucvM1W2clAnFABvgq5amxbO/o+52S4m0Cyt+JjcwWqC8eTN+pqEs3ixpvHzIhA+JdMF +DQ9GlD4X5hDuZ6nUt7wA4m/yOIwoK0m3QK20f9rN8VEFJn4E68nlqZyXLdHMlWNg8LR H3ueV9xyqOvFp0Ka4fGSoRkpivNTsEchTRAbpBPq4nWku2GgJWxiwsrCNdQL2TavGBaH 9hYfUTGNOED6f3E0FKjc9k9FTiUSjTlsmpMFm6v8BhnLd6hxWBEImhztJjnwBrAf4vrk O+Kw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=saDcL6Ex8q+zWnP2cZdXfAl26yMgKf0Lx6urXy6fa50=; b=LvjmpqGwpNKwabnxj017KFDhxxWjZqYZb2dYNJMvfqjYulD2VkBFL3nTszLppIyk8m bn12N4PIqhtyH52tfhD53hkN8Zqkdm3lYXuzQkyDha/3Rh3qJ/YnwqnOkvtPBFJgzTXH EhmJEccwCA6U3CYcF5FdF4+9LttywWCNUUHFxWFzo0VjDgK0RdEL9qMpQrqf+qAUpadw HMLDdGCIPdKvSAEK8jRhYEtgpZvHsQBLkNKAfP7IMzP2pNSZoKI0jT70qAHB05D9SgFG q346PEsSMHm1YeZSANRzo1lU1Se06ixhN2gs0jiD9THLydhIrodUYykQ4jq5+Ih7eHsZ qQkg== X-Gm-Message-State: AO0yUKVdDIrnz257tIOmhv/BcezifeVO3b66AMIJwloMVz1UScqu2sw1 Q8uWsY0Ao603pJBwZJf0Xfs8Zw== X-Google-Smtp-Source: AK7set+X0B+ahm3KICDLsPtUUOPhjzI0r4KrKNQPWasW0IWyz7gbU81Z2WuFFAv/Q63QqWD9jiLvgQ== X-Received: by 2002:a05:600c:1d21:b0:3dc:5dd4:7d3f with SMTP id l33-20020a05600c1d2100b003dc5dd47d3fmr2568245wms.24.1676486036620; Wed, 15 Feb 2023 10:33:56 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.33.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:33:56 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 07/21] net/tcp: Add tcp_parse_auth_options() Date: Wed, 15 Feb 2023 18:33:21 +0000 Message-Id: <20230215183335.800122-8-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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/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 +++++++---- 6 files changed, 87 insertions(+), 22 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index fa26e18528a1..2bd9456572f7 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -434,7 +434,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 @@ -2533,4 +2532,27 @@ static inline u64 tcp_transmit_time(const struct sock *sk) return 0; } +static inline int tcp_parse_auth_options(const struct tcphdr *th, + const u8 **md5_hash, const struct tcp_ao_hdr **aoh) +{ + const u8 *md5_tmp, *ao_tmp; + int ret; + + ret = tcp_do_parse_auth_options(th, &md5_tmp, &ao_tmp); + if (ret) + return ret; + + if (md5_hash) + *md5_hash = md5_tmp; + + if (aoh) { + if (!ao_tmp) + *aoh = NULL; + else + *aoh = (struct tcp_ao_hdr *)(ao_tmp - 2); + } + + return 0; +} + #endif /* _TCP_H */ diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index e233e7ff3184..e600064379ae 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -151,7 +151,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, @@ -174,4 +176,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 6d66719df5a2..e4136e87a162 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4540,7 +4540,8 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, l3index = sdif ? dif : 0; hash_expected = tcp_md5_do_lookup(sk, l3index, saddr, family); - hash_location = tcp_parse_md5sig_option(th); + if (tcp_parse_auth_options(th, &hash_location, NULL)) + return true; /* We've parsed the options - do we have a hash? */ if (!hash_expected && !hash_location) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 6baeb3fe4352..78c2f238205f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4202,39 +4202,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 e999e00a5398..bda371f8c055 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -668,7 +668,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) @@ -684,7 +686,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; @@ -724,8 +726,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; @@ -736,7 +741,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); @@ -768,7 +773,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 50d394d9d5fc..2970c195c917 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -987,7 +987,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; @@ -1009,8 +1009,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; @@ -1019,7 +1022,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; @@ -1048,7 +1051,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 Wed Feb 15 18:33:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142045 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 93700C64ED9 for ; Wed, 15 Feb 2023 18:34:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230301AbjBOSee (ORCPT ); Wed, 15 Feb 2023 13:34:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49764 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229831AbjBOSeZ (ORCPT ); Wed, 15 Feb 2023 13:34:25 -0500 Received: from mail-wr1-x42f.google.com (mail-wr1-x42f.google.com [IPv6:2a00:1450:4864:20::42f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D8B073CE3E for ; Wed, 15 Feb 2023 10:33:59 -0800 (PST) Received: by mail-wr1-x42f.google.com with SMTP id s13so2388421wrw.3 for ; Wed, 15 Feb 2023 10:33:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=8JzIKRwKriezzDbbBZ20A5iBMf1tEJLY0xwadbyPsvA=; b=RZ1LWtr+R0w12qksUFK6fYpmIi883+6nnaKZdnSq5ED0YIq5lLCia0xfNU0oOQ97Bx Iv4MrJ7QlUx5pQ5h/0oLMxBkys42649bDF4v9PnxjD9+Q6Ongxp1hIqkAX6oj1BAFhJX mVXWbRuBcQ9ABw0lb2ivNhdQXnAEtyE1K+rDYEwGCC08IDcCfqqgQr7smjgE/Ibhafzc xiwWeuc1X6nqcqLElMsX+59tVMfx9wbfxN7kgYzTaG50Copmjrq99+CiF7JNPB45GXKy OieD+eiHq/r/kUjoJ+2Wpdz8LxHoBVSBPeQQJc5iOrEq20Fl6PU02HLrJZmCcL8uH6Lc ABYw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=8JzIKRwKriezzDbbBZ20A5iBMf1tEJLY0xwadbyPsvA=; b=lS6pFxIMLu6mAk1wJnDl5sW8/pAwnH3C9KkQgxNXzcqSq/xtE2htXTVg0qriG5AaOe 0/bbPCSD8OzVLNBzh8epJI6QwJKB9wO0yr3srh7d1i5s76wCdImQO51R+PxfcbLthfIL 9qQ8EEsmzDKcAiCuaAlUFYmbHgbVVyiDYnSuzKIXzuPD/qGzJvAyTC1JuBK0EFm2Tj09 /jeaSv4b1eh0Yz0+2qFuvfvuaSck5wXz5wre6RKZ33SBoz3pERIldF5/58o8ow+IxvJJ Cy9sPzeAwTgIMOvRcWxJLpywkabcHx7mHVgFuQcxKIYHgRBR/tqCWGGh8tYFkYpRBEia vUmw== X-Gm-Message-State: AO0yUKWIduOtGA1eaMreGhEOgiFg00FsTPBa4RrSubcdUblQ4uG0kxAd lPHLHXwd1Sy3MpHwEkAAHit+mg== X-Google-Smtp-Source: AK7set9XeKq0Hs+xypvtfqAzmqYP59k+AEG1xw/woVGjdMYt1Kdd3lYjPnXh8WAji/4V2UkmUolCEQ== X-Received: by 2002:adf:f145:0:b0:2bf:c518:b060 with SMTP id y5-20020adff145000000b002bfc518b060mr2136174wro.20.1676486038203; Wed, 15 Feb 2023 10:33:58 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.33.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:33:57 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 08/21] net/tcp: Add AO sign to RST packets Date: Wed, 15 Feb 2023 18:33:22 +0000 Message-Id: <20230215183335.800122-9-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 | 8 ++++ net/ipv4/tcp_ao.c | 53 +++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 77 +++++++++++++++++++++++++++++---- net/ipv6/tcp_ipv6.c | 101 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 218 insertions(+), 21 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index e600064379ae..22330ca8e58b 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -114,6 +114,8 @@ 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_matched_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); void tcp_ao_destroy_sock(struct sock *sk); @@ -123,6 +125,12 @@ int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port); +int tcp_ao_hash_hdr(unsigned short family, char *ao_hash, + struct tcp_ao_key *key, const u8 *tkey, + const union tcp_ao_addr *daddr, + const union tcp_ao_addr *saddr, + const struct tcphdr *th, u32 sne); + /* ipv4 specific functions */ int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen); struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index e8823f851bc9..f553b115c828 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -416,6 +416,59 @@ static int tcp_ao_hash_skb_data(struct tcp_sigpool *hp, return 0; } +int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash, + struct tcp_ao_key *key, const u8 *tkey, + const union tcp_ao_addr *daddr, + const union tcp_ao_addr *saddr, + const struct tcphdr *th, u32 sne) +{ + struct tcp_sigpool hp; + int tkey_len = tcp_ao_digest_size(key); + int hash_offset = ao_hash - (char *)th; + + 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, ao_hash, 0); + if (crypto_ahash_final(hp.req)) + goto clear_hash; + + tcp_sigpool_end(); + return 0; + +clear_hash: + tcp_sigpool_end(); +clear_hash_noput: + memset(ao_hash, 0, tcp_ao_maclen(key)); + return 1; +} +EXPORT_SYMBOL(tcp_ao_hash_hdr); + int tcp_ao_hash_skb(unsigned short int family, char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index bda371f8c055..32001b722319 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -684,16 +684,19 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) __be32 opt[OPTION_BYTES / sizeof(__be32)]; } rep; struct ip_reply_arg arg; + u64 transmit_time = 0; + struct sock *ctl_sk; + struct net *net; +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) + const __u8 *md5_hash_location = NULL; + const struct tcp_ao_hdr *aoh; #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 #endif - u64 transmit_time = 0; - struct sock *ctl_sk; - struct net *net; /* Never send a reset in response to a reset. */ if (th->rst) @@ -725,12 +728,14 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) arg.iov[0].iov_len = sizeof(rep.th); net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) /* Invalid TCP option size or twice included auth */ - if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, NULL)) + if (tcp_parse_auth_options(tcp_hdr(skb), &md5_hash_location, &aoh)) return; rcu_read_lock(); +#endif +#ifdef CONFIG_TCP_MD5SIG if (sk && sk_fullsock(sk)) { const union tcp_md5_addr *addr; int l3index; @@ -791,6 +796,62 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) key, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, &rep.th); } +#endif +#ifdef CONFIG_TCP_AO + /* if (!sk || sk->sk_state == TCP_LISTEN) then the initial sisn/disn + * are unknown. Skip TCP-AO signing. + * Contrary to TCP-MD5 unsigned RST will be sent if there was AO sign + * in segment, but TCP-AO signing isn't possible for reply. + */ + if (sk && aoh && sk->sk_state != TCP_LISTEN) { + char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + struct tcp_ao_info *ao_info; + struct tcp_ao_key *ao_key; + u32 ao_sne; + u8 keyid; + + /* TODO: reqsk support */ + if (sk->sk_state == TCP_NEW_SYN_RECV) + goto skip_ao_sign; + + ao_info = rcu_dereference(tcp_sk(sk)->ao_info); + + if (!ao_info) + goto skip_ao_sign; + + ao_key = tcp_ao_matched_key(ao_info, aoh->rnext_keyid, -1); + if (!ao_key) + goto skip_ao_sign; + + /* XXX: optimize by using cached traffic key depending + * on socket state + */ + if (tcp_v4_ao_calc_key_sk(ao_key, traffic_key, sk, + ao_info->lisn, ao_info->risn, true)) + goto out; + + /* rcv_next holds the rcv_next of the peer, make keyid + * hold our rcv_next + */ + keyid = ao_info->rnext_key->rcvid; + ao_sne = tcp_ao_compute_sne(ao_info->snd_sne, + ao_info->snd_sne_seq, + ntohl(rep.th.seq)); + + rep.opt[0] = htonl((TCPOPT_AO << 24) | + (tcp_ao_len(ao_key) << 16) | + (aoh->rnext_keyid << 8) | keyid); + arg.iov[0].iov_len += round_up(tcp_ao_len(ao_key), 4); + rep.th.doff = arg.iov[0].iov_len / 4; + + if (tcp_ao_hash_hdr(AF_INET, (char *)&rep.opt[1], + ao_key, traffic_key, + (union tcp_ao_addr *)&ip_hdr(skb)->saddr, + (union tcp_ao_addr *)&ip_hdr(skb)->daddr, + &rep.th, ao_sne)) + goto out; + } +skip_ao_sign: #endif /* Can't co-exist with TCPMD5, hence check rep.opt[0] */ if (rep.opt[0] == 0) { @@ -848,7 +909,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) __TCP_INC_STATS(net, TCP_MIB_OUTRSTS); local_bh_enable(); -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) out: rcu_read_unlock(); #endif diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 2970c195c917..6fcaadb9cefe 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -852,7 +852,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; @@ -871,6 +873,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) { @@ -922,6 +931,21 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 &ipv6_hdr(skb)->daddr, t1); } #endif +#ifdef CONFIG_TCP_AO + if (ao_key) { + *topt++ = htonl((TCPOPT_AO << 24) | (tcp_ao_len(ao_key) << 16) | + (ao_key->sndid << 8) | (rcv_next)); + + /* TODO: this is right now not going to work for listening + * sockets since the socket won't have the needed ipv6 + * addresses + */ + tcp_ao_hash_hdr(AF_INET6, (char *)topt, ao_key, tkey, + (union tcp_ao_addr *)&ipv6_hdr(skb)->saddr, + (union tcp_ao_addr *)&ipv6_hdr(skb)->daddr, + t1, ao_sne); + } +#endif memset(&fl6, 0, sizeof(fl6)); fl6.daddr = ipv6_hdr(skb)->saddr; @@ -986,17 +1010,28 @@ 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 - const __u8 *md5_hash_location = NULL; - unsigned char newhash[16]; - int genhash; - struct sock *sk1 = NULL; -#endif __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; +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) + const __u8 *md5_hash_location = NULL; + const struct tcp_ao_hdr *aoh; +#endif +#ifdef CONFIG_TCP_MD5SIG + unsigned char newhash[16]; + int genhash; + struct sock *sk1 = NULL; +#endif +#ifdef CONFIG_TCP_AO + char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; +#else + u8 *traffic_key = NULL; +#endif if (th->rst) return; @@ -1008,12 +1043,13 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) return; net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) /* Invalid TCP option size or twice included auth */ - if (tcp_parse_auth_options(th, &md5_hash_location, NULL)) + if (tcp_parse_auth_options(th, &md5_hash_location, &aoh)) return; - rcu_read_lock(); +#endif +#ifdef CONFIG_TCP_MD5SIG if (sk && sk_fullsock(sk)) { int l3index; @@ -1062,6 +1098,44 @@ 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 (!sk || sk->sk_state == TCP_LISTEN) then the initial sisn/disn + * are unknown. Skip TCP-AO signing. + * Contrary to TCP-MD5 unsigned RST will be sent if there was AO sign + * in segment, but TCP-AO signing isn't possible for reply. + */ + if (sk && aoh && sk->sk_state != TCP_LISTEN) { + struct tcp_ao_info *ao_info; + + /* TODO: reqsk support */ + if (sk->sk_state == TCP_NEW_SYN_RECV) + goto skip_ao_sign; + + ao_info = rcu_dereference(tcp_sk(sk)->ao_info); + if (!ao_info) + goto skip_ao_sign; + + /* rcv_next is the peer's here */ + ao_key = tcp_ao_matched_key(ao_info, aoh->rnext_keyid, -1); + if (!ao_key) + goto skip_ao_sign; + + + /* XXX: optimize by using cached traffic key depending + * on socket state + */ + if (tcp_v6_ao_calc_key_sk(ao_key, traffic_key, sk, + ao_info->lisn, ao_info->risn, true)) + goto out; + + /* rcv_next switches to our rcv_next */ + rcv_next = ao_info->rnext_key->rcvid; + ao_sne = tcp_ao_compute_sne(ao_info->snd_sne, + ao_info->snd_sne_seq, seq); + } +skip_ao_sign: +#endif + if (sk) { oif = sk->sk_bound_dev_if; if (sk_fullsock(sk)) { @@ -1084,9 +1158,10 @@ 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: rcu_read_unlock(); #endif @@ -1098,7 +1173,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 Wed Feb 15 18:33:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142046 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id ED96AC64ED6 for ; Wed, 15 Feb 2023 18:34:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229760AbjBOSef (ORCPT ); Wed, 15 Feb 2023 13:34:35 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49074 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230218AbjBOSeZ (ORCPT ); Wed, 15 Feb 2023 13:34:25 -0500 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 773733D088 for ; Wed, 15 Feb 2023 10:34:01 -0800 (PST) Received: by mail-wm1-x32b.google.com with SMTP id n33so8003465wms.0 for ; Wed, 15 Feb 2023 10:34:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=7nS1cLogGPeqjrU8C+N08XBH20fKoqBEH+hq17oTBY0=; b=UexG2CbzZPlzimT9Jbv3WSnxszSyEMxY269uPGR68kFhqzAj/2omwGWgKk4PAd4YOn 7Ub633MGq2smFJ7IHhcZauRxcVJihxWgGhvIkaYj10aR0cyW9B7PcXO7HxSY0gnfyhO8 /PIFDK6NAjCQDtaNbg+21TgggkWe73AMuZ60tmB8GJV5zc7fS5Mu8oH+l5/pQlxjYHWh MwbEHHUiVEDnRvajBe9GZZeJaAO5cSfw3ubs8SO0ZCwc7PyEq01CA1ErnIV9wM9Q+gdV fVdV1HBV7LdjQIcGl8+fTmf9Vf5LuYsRnkptMfFCg6HiYjMD0SZQa9enkWZ3hG/pQyDK mZHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=7nS1cLogGPeqjrU8C+N08XBH20fKoqBEH+hq17oTBY0=; b=MJB04mEQMxxPUDDsuSU7R9TBAh6Lt6yp/ViY6RinjjgrdfrBsAmacFiEhe7GKXY67f OtqJeIZ3+9SriphOtPntThToERfB7Lm7vWQIdLy0iN8fPg9o8hj+ZkjKXFX28yiKIwlL UXoBB2gANBijhcC7fcVSZ5LoZiiChlPdjQ7llQ8MVMfJQCj7SrxGym3NQuB4rD/EGfKQ oSdLZFlDvyd+Lqgt0qI2XhkXe1YZGJKwIBzn0SkEtw0M3jbQ9mef5gFHzNpdpQJOXEKI x/XhV6vaVBVPOP4Oi12/V+AX7A0xkAwfizEVcSfdwnv0wIhSzU2/5MuZntircc7t/59n 7dKg== X-Gm-Message-State: AO0yUKXaKxxefgLmeKEfSyQhfkOYeUeJES3qEZV54QUUYlu3JN0IoSZD mAd/RP+wKKpdIbeGdko1YiwttQ== X-Google-Smtp-Source: AK7set9Vyh5EDPiUNvHLjReY7qOCTgk0J3j+UZOPx2NpCXIuq6lfhPJmN64he80pwF/vq+GyfYGZOA== X-Received: by 2002:a05:600c:816:b0:3dc:557f:6123 with SMTP id k22-20020a05600c081600b003dc557f6123mr2854143wmp.1.1676486039707; Wed, 15 Feb 2023 10:33:59 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.33.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:33:59 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 09/21] net/tcp: Add TCP-AO sign to twsk Date: Wed, 15 Feb 2023 18:33:23 +0000 Message-Id: <20230215183335.800122-10-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 | 38 ++++++++++++++++++--- net/ipv4/tcp_ipv4.c | 72 +++++++++++++++++++++++++++++++++++----- net/ipv4/tcp_minisocks.c | 4 ++- net/ipv4/tcp_output.c | 2 +- net/ipv6/tcp_ipv6.c | 47 ++++++++++++++++++++++---- 7 files changed, 154 insertions(+), 23 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index ac742427bb39..80d450622474 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -498,6 +498,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 22330ca8e58b..a377d3bc5a7f 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -85,6 +85,7 @@ struct tcp_ao_info { u32 snd_sne_seq; u32 rcv_sne; u32 rcv_sne_seq; + atomic_t refcnt; /* Protects twsk destruction */ }; #ifdef CONFIG_TCP_AO @@ -118,8 +119,9 @@ struct tcp_ao_key *tcp_ao_matched_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); -void tcp_ao_destroy_sock(struct sock *sk); +void tcp_ao_destroy_sock(struct sock *sk, bool twsk); u32 tcp_ao_compute_sne(u32 sne, u32 seq, u32 new_seq); +void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key); struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, @@ -171,7 +173,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) { } @@ -179,6 +181,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 f553b115c828..5f158268a8b9 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); if (cloned_from) ao->ao_flags = cloned_from->ao_flags; @@ -180,27 +181,54 @@ static void tcp_ao_key_free_rcu(struct rcu_head *head) kfree(key); } -void tcp_ao_destroy_sock(struct sock *sk) +void tcp_ao_destroy_sock(struct sock *sk, bool twsk) { struct tcp_ao_info *ao; struct tcp_ao_key *key; struct hlist_node *n; - ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1); - tcp_sk(sk)->ao_info = NULL; + if (twsk) { + ao = rcu_dereference_protected(tcp_twsk(sk)->ao_info, 1); + tcp_twsk(sk)->ao_info = NULL; + } else { + ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1); + tcp_sk(sk)->ao_info = NULL; + } - if (!ao) + if (!ao || !atomic_dec_and_test(&ao->refcnt)) return; hlist_for_each_entry_safe(key, n, &ao->head, node) { hlist_del_rcu(&key->node); - atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); + if (!twsk) + atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); call_rcu(&key->rcu, tcp_ao_key_free_rcu); } kfree_rcu(ao, rcu); } +void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp) +{ + struct tcp_ao_info *ao_info = rcu_dereference_protected(tp->ao_info, 1); + + if (ao_info) { + struct tcp_ao_key *key; + struct hlist_node *n; + int omem = 0; + + hlist_for_each_entry_safe(key, n, &ao_info->head, node) { + omem += tcp_ao_sizeof_key(key); + } + + atomic_inc(&ao_info->refcnt); + atomic_sub(omem, &(((struct sock *)tp)->sk_omem_alloc)); + rcu_assign_pointer(tcptw->ao_info, ao_info); + } else { + tcptw->ao_info = NULL; + } +} + /* 4 tuple and ISNs are expected in NBO */ static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, __be32 saddr, __be32 daddr, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 32001b722319..f14987b6988f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -814,8 +814,10 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) if (sk->sk_state == TCP_NEW_SYN_RECV) goto skip_ao_sign; - ao_info = rcu_dereference(tcp_sk(sk)->ao_info); - + if (sk->sk_state == TCP_TIME_WAIT) + ao_info = rcu_dereference(tcp_twsk(sk)->ao_info); + else + ao_info = rcu_dereference(tcp_sk(sk)->ao_info); if (!ao_info) goto skip_ao_sign; @@ -923,16 +925,16 @@ static void tcp_v4_send_ack(const struct sock *sk, struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 tsval, u32 tsecr, int oif, struct tcp_md5sig_key *key, + struct tcp_ao_key *ao_key, + u8 *traffic_key, + u8 rcv_next, + u32 ao_sne, int reply_flags, u8 tos) { const struct tcphdr *th = tcp_hdr(skb); struct { struct tcphdr th; - __be32 opt[(TCPOLEN_TSTAMP_ALIGNED >> 2) -#ifdef CONFIG_TCP_MD5SIG - + (TCPOLEN_MD5SIG_ALIGNED >> 2) -#endif - ]; + __be32 opt[(MAX_TCP_OPTION_SPACE >> 2)]; } rep; struct net *net = sock_net(sk); struct ip_reply_arg arg; @@ -978,6 +980,25 @@ static void tcp_v4_send_ack(const struct sock *sk, ip_hdr(skb)->daddr, &rep.th); } #endif +#ifdef CONFIG_TCP_AO + if (ao_key) { + int offset = (tsecr) ? 3 : 0; + + rep.opt[offset++] = htonl((TCPOPT_AO << 24) | + (tcp_ao_len(ao_key) << 16) | + (ao_key->sndid << 8) | rcv_next); + arg.iov[0].iov_len += round_up(tcp_ao_len(ao_key), 4); + rep.th.doff = arg.iov[0].iov_len / 4; + + tcp_ao_hash_hdr(AF_INET, (char *)&rep.opt[offset], + ao_key, traffic_key, + (union tcp_ao_addr *)&ip_hdr(skb)->saddr, + (union tcp_ao_addr *)&ip_hdr(skb)->daddr, + &rep.th, ao_sne); + } + WARN_ON_ONCE(key && ao_key); +#endif + /* XXX: TCP-AO: hash ACK header */ arg.flags = reply_flags; arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, ip_hdr(skb)->saddr, /* XXX */ @@ -1011,6 +1032,36 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb) { struct inet_timewait_sock *tw = inet_twsk(sk); struct tcp_timewait_sock *tcptw = tcp_twsk(sk); + struct tcp_ao_key *ao_key = NULL; + u8 *traffic_key = NULL; + u8 rcv_next = 0; + u32 ao_sne = 0; +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao_info; + + /* 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_matched_key(ao_info, aoh->rnext_keyid, -1); + } + if (ao_key) { + traffic_key = snd_other_key(ao_key); + /* It's possible we can get rid of computing the sne + * below since sne probably doesn't change once we are + * in timewait state. + */ + ao_sne = tcp_ao_compute_sne(ao_info->snd_sne, + ao_info->snd_sne_seq, + tcptw->tw_snd_nxt); + rcv_next = ao_info->rnext_key->rcvid; + } +#endif tcp_v4_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, @@ -1019,10 +1070,14 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb) tcptw->tw_ts_recent, tw->tw_bound_dev_if, tcp_twsk_md5_key(tcptw), + ao_key, traffic_key, rcv_next, ao_sne, tw->tw_transparent ? IP_REPLY_ARG_NOSRCCHECK : 0, tw->tw_tos ); +#ifdef CONFIG_TCP_AO +out: +#endif inet_twsk_put(tw); } @@ -1052,6 +1107,7 @@ static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, req->ts_recent, 0, tcp_md5_do_lookup(sk, l3index, addr, AF_INET), + NULL, NULL, 0, 0, inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, ip_hdr(skb)->tos); } @@ -2409,7 +2465,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 0219c0e5e2df..03c87f786d1a 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 23763938f931..ff2f1b5ec5e2 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3953,7 +3953,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 6fcaadb9cefe..99ab802a37e4 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1111,7 +1111,10 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) if (sk->sk_state == TCP_NEW_SYN_RECV) goto skip_ao_sign; - ao_info = rcu_dereference(tcp_sk(sk)->ao_info); + if (sk->sk_state == TCP_TIME_WAIT) + ao_info = rcu_dereference(tcp_twsk(sk)->ao_info); + else + ao_info = rcu_dereference(tcp_sk(sk)->ao_info); if (!ao_info) goto skip_ao_sign; @@ -1120,7 +1123,6 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) if (!ao_key) goto skip_ao_sign; - /* XXX: optimize by using cached traffic key depending * on socket state */ @@ -1170,24 +1172,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_matched_key(ao_info, aoh->rnext_keyid, -1); + } + if (ao_key) { + traffic_key = snd_other_key(ao_key); + /* rcv_next switches to our rcv_next */ + rcv_next = ao_info->rnext_key->rcvid; + ao_sne = tcp_ao_compute_sne(ao_info->snd_sne, + ao_info->snd_sne_seq, + tcptw->tw_snd_nxt); + } +#endif tcp_v6_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, tcp_time_stamp_raw() + tcptw->tw_ts_offset, tcptw->tw_ts_recent, tw->tw_bound_dev_if, tcp_twsk_md5_key(tcptw), tw->tw_tclass, cpu_to_be32(tw->tw_flowlabel), tw->tw_priority, - tw->tw_txhash); + tw->tw_txhash, ao_key, traffic_key, rcv_next, ao_sne); +#ifdef CONFIG_TCP_AO +out: +#endif inet_twsk_put(tw); } @@ -1214,7 +1249,7 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, 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, - tcp_rsk(req)->txhash); + tcp_rsk(req)->txhash, NULL, NULL, 0, 0); } From patchwork Wed Feb 15 18:33:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142047 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 028DBC636D4 for ; Wed, 15 Feb 2023 18:34:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230244AbjBOSeh (ORCPT ); Wed, 15 Feb 2023 13:34:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49034 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230227AbjBOSe0 (ORCPT ); Wed, 15 Feb 2023 13:34:26 -0500 Received: from mail-wm1-x334.google.com (mail-wm1-x334.google.com [IPv6:2a00:1450:4864:20::334]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 130F43A862 for ; Wed, 15 Feb 2023 10:34:03 -0800 (PST) Received: by mail-wm1-x334.google.com with SMTP id m20-20020a05600c3b1400b003e1e754657aso2295504wms.2 for ; Wed, 15 Feb 2023 10:34:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=QQJc83rB/X/u+2Q48Cn5VxtgOx2Qoum2csgVHpTWBvs=; b=JNJtxyvt4BZoTXz/7d1T/vVCgfuHOtGDH/kQOC9h/7maxx43kP+jq1ttT31Zad8yvS roPhL+7MCVyMFuvqSVZphCIXLO4WsOmqcC6TFmWefL97zmNrv81zU7WEvg9Mz7Rdgm+L NhI2JZW0+F2UOBicZg6DSoKg6005PhYG33DnZ/DEnpcOXYDy8lLn6A+k+myonMeHc6ar VzflZXdpARwP+OnRDvaDmzD/uIDCWqzlLYdBRX6JCju6MrwNEP6Lf9sN/dXkTpNMhB3l 3fCikowuK+HXnNbEuyvwyBtXY7sL5cpcu5XupuDBmOAtLhvxlQLZtasaaMuJBanJeArq iCLQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=QQJc83rB/X/u+2Q48Cn5VxtgOx2Qoum2csgVHpTWBvs=; b=1C6HKz9+Ivoi+NCiEVIphjDR1m040fI8pj5MpgRwq/bYcba2hPVhLyh7sCu/oRfrLh 3m9DeRhYKQ/V2S0IOYzAN6oKGK74kABOCe7PpWgqYaQrDXqVV728MEB54OTNlQ+AZyeG qfllmYu3sayLGrkidPeqOizdw+LoJNDvSDrf3RpnAyDsWB5UrfqrJ+sQ29oX1ftTjYjl LLZCKtA5pxmmbN79e1jgU5TfTtNVDkhtI8mUutmuXnRa0xTKNnpfdcjUfR5PMcDuXzk+ f2lMW6l9utHYgpQl2MnGT57RjJ4AcxSuJTfwowR4RnYNbLeqXlKCq2mCOagOvOgFD6+Q BGtQ== X-Gm-Message-State: AO0yUKWnC3p+aDFmLV4yJwIi2OB6bBVwopP3CnWQLLqfsA2Xg6BkwAJB rCuP2M9TjZhWvLdccB1JTbQ+PA== X-Google-Smtp-Source: AK7set/gOJVq5CbKARyVPNsnn72SJ0aLNEUL+hbU0UXWeTiSXlFyJh92zkQEXKpYd4SyQ4aY1G2oaA== X-Received: by 2002:a05:600c:c06:b0:3df:f7f3:d007 with SMTP id fm6-20020a05600c0c0600b003dff7f3d007mr2752943wmb.17.1676486041501; Wed, 15 Feb 2023 10:34:01 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.33.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:00 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 10/21] net/tcp: Wire TCP-AO to request sockets Date: Wed, 15 Feb 2023 18:33:24 +0000 Message-Id: <20230215183335.800122-11-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 | 182 +++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_input.c | 16 ++++ net/ipv4/tcp_ipv4.c | 63 ++++++++++++-- net/ipv4/tcp_minisocks.c | 10 +++ net/ipv4/tcp_output.c | 36 +++++--- net/ipv6/syncookies.c | 2 + net/ipv6/tcp_ao.c | 21 +++++ net/ipv6/tcp_ipv6.c | 77 ++++++++++++++--- 12 files changed, 426 insertions(+), 30 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 80d450622474..28048908a485 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -165,6 +165,11 @@ struct tcp_request_sock { * after data-in-SYN. */ u8 syn_tos; +#ifdef CONFIG_TCP_AO + u8 ao_keyid; + u8 ao_rcv_next; + u8 maclen; +#endif }; static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) @@ -172,6 +177,19 @@ static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) return (struct tcp_request_sock *)req; } +static inline bool tcp_rsk_used_ao(const struct request_sock *req) +{ + /* The real length of MAC is saved in the request socket, + * signing anything with zero-length makes no sense, so here is + * a little hack.. + */ +#ifndef CONFIG_TCP_AO + return false; +#else + return tcp_rsk(req)->maclen != 0; +#endif +} + struct tcp_sock { /* inet_connection_sock has to be the first member of tcp_sock */ struct inet_connection_sock inet_conn; diff --git a/include/net/tcp.h b/include/net/tcp.h index 2bd9456572f7..d906fdbe93ec 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2141,6 +2141,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 a377d3bc5a7f..c7600b049e54 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -117,6 +117,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_matched_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); void tcp_ao_destroy_sock(struct sock *sk, bool twsk); @@ -140,6 +143,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); @@ -151,9 +159,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); @@ -166,6 +182,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 26fb97d1d4d9..9e3d7083af7d 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 5f158268a8b9..3bac6d3c43cb 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -173,6 +173,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); @@ -281,6 +298,18 @@ int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); } +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) @@ -550,6 +579,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) { @@ -558,6 +597,51 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid, 0); } +static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family, + const struct sock *sk, const struct sk_buff *skb, + int sndid, int rcvid) +{ + if (family == AF_INET) { + const struct iphdr *iph = ip_hdr(skb); + + return tcp_ao_do_lookup(sk, (union tcp_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); + } +} + +/* Returns maclen of requested key if found */ +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); +} + int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key) { @@ -655,6 +739,104 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb) } } +int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, + struct request_sock *req, struct sk_buff *skb, + int family) +{ + struct tcp_ao_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, ao); + if (!new_ao) + return -ENOMEM; + new_ao->lisn = htonl(tcp_rsk(req)->snt_isn); + new_ao->risn = htonl(tcp_rsk(req)->rcv_isn); + + if (family == AF_INET) { + addr = (union tcp_ao_addr *)&newsk->sk_daddr; +#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); + + /* set current_key */ + key = tcp_ao_matched_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_matched_key(new_ao, -1, tcp_rsk(req)->ao_rcv_next); + if (key) + new_ao->rnext_key = key; + else + new_ao->rnext_key = first_key; + + new_ao->snd_sne_seq = tcp_rsk(req)->snt_isn; + new_ao->rcv_sne_seq = tcp_rsk(req)->rcv_isn; + + sk_gso_disable(newsk); + rcu_assign_pointer(tcp_sk(newsk)->ao_info, new_ao); + + return 0; + +free_and_exit: + hlist_for_each_entry_safe(key, 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 int tcp_ao_current_rnext(struct sock *sk, u16 tcpa_flags, u8 tcpa_sndid, u8 tcpa_rcvid) { diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 78c2f238205f..264db761153b 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6955,6 +6955,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 @@ -7040,6 +7044,18 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, inet_rsk(req)->ecn_ok = 0; } +#ifdef CONFIG_TCP_AO + /* TODO: Add an option to require TCP-AO signature */ + 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 f14987b6988f..74d841233cfe 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1084,30 +1084,73 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb) static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, struct request_sock *req) { + struct tcp_md5sig_key *md5_key = NULL; + struct tcp_ao_key *ao_key = NULL; const union tcp_md5_addr *addr; - int l3index; + u8 keyid = 0; +#ifdef CONFIG_TCP_AO + u8 traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + const struct tcp_ao_hdr *aoh; +#else + u8 *traffic_key = NULL; +#endif /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV * sk->sk_state == TCP_SYN_RECV -> for Fast Open. */ u32 seq = (sk->sk_state == TCP_LISTEN) ? tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt; + addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr; + + if (tcp_rsk_used_ao(req)) { +#ifdef CONFIG_TCP_AO + /* Invalid TCP option size or twice included auth */ + if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh)) + return; + + if (!aoh) + return; + + ao_key = tcp_ao_do_lookup(sk, addr, AF_INET, + aoh->rnext_keyid, -1, 0); + if (unlikely(!ao_key)) { + /* Send ACK with any matching MKT for the peer */ + ao_key = tcp_ao_do_lookup(sk, addr, + AF_INET, -1, -1, 0); + /* Matching key disappeared (user removed the key?) + * let the handshake timeout. + */ + if (!ao_key) { + net_info_ratelimited("TCP-AO key for (%pI4, %d)->(%pI4, %d) suddenly disappeared, won't ACK new connection\n", + addr, + ntohs(tcp_hdr(skb)->source), + &ip_hdr(skb)->daddr, + ntohs(tcp_hdr(skb)->dest)); + return; + } + } + keyid = aoh->keyid; + tcp_v4_ao_calc_key_rsk(ao_key, traffic_key, req); +#endif + } else { + int l3index; + + l3index = tcp_v4_sdif(skb) ? inet_iif(skb) : 0; + md5_key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET); + } /* RFC 7323 2.3 * The window field (SEG.WND) of every outgoing segment, with the * exception of segments, MUST be right-shifted by * Rcv.Wind.Shift bits: */ - addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr; - l3index = tcp_v4_sdif(skb) ? inet_iif(skb) : 0; tcp_v4_send_ack(sk, skb, seq, tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale, tcp_time_stamp_raw() + tcp_rsk(req)->ts_off, req->ts_recent, 0, - tcp_md5_do_lookup(sk, l3index, addr, AF_INET), - NULL, NULL, 0, 0, + md5_key, ao_key, traffic_key, keyid, 0, inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, ip_hdr(skb)->tos); } @@ -1645,6 +1688,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 @@ -1746,12 +1793,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 03c87f786d1a..558710eb599b 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -505,6 +505,9 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, struct inet_connection_sock *newicsk; struct tcp_sock *oldtp, *newtp; u32 seq; +#ifdef CONFIG_TCP_AO + struct tcp_ao_key *ao_key; +#endif if (!newsk) return NULL; @@ -583,6 +586,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 ff2f1b5ec5e2..2346d90c6a31 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -606,6 +606,7 @@ static void bpf_skops_write_hdr_opt(struct sock *sk, struct sk_buff *skb, * (but it may well be that other scenarios fail similarly). */ static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp, + const struct tcp_request_sock *tcprsk, struct tcp_out_options *opts, struct tcp_ao_key *ao_key) { @@ -620,19 +621,30 @@ 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_info *ao_info; - u8 maclen; + if (unlikely(OPTION_AO & options)) { + u8 maclen = tcp_ao_maclen(ao_key); - ao_info = rcu_dereference_check(tp->ao_info, + if (tp) { + struct tcp_ao_info *ao_info; + + ao_info = rcu_dereference_check(tp->ao_info, lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk)); - if (WARN_ON_ONCE(!ao_key || !ao_info || !ao_info->rnext_key)) + if (WARN_ON_ONCE(!ao_key || !ao_info || !ao_info->rnext_key)) + goto out_ao; + *ptr++ = htonl((TCPOPT_AO << 24) | + (tcp_ao_len(ao_key) << 16) | + (ao_key->sndid << 8) | + (ao_info->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; - maclen = tcp_ao_maclen(ao_key); - *ptr++ = htonl((TCPOPT_AO << 24) | - (tcp_ao_len(ao_key) << 16) | - (ao_key->sndid << 8) | - (ao_info->rnext_key->rcvid)); + } opts->hash_location = (__u8 *)ptr; ptr += maclen / sizeof(*ptr); if (unlikely(maclen % sizeof(*ptr))) { @@ -1408,7 +1420,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 */ @@ -3699,7 +3711,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 061ca5369426..a6bc28ee7487 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -53,6 +53,18 @@ int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, htons(sk->sk_num), disn, sisn); } +int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, + struct request_sock *req) +{ + struct inet_request_sock *ireq = inet_rsk(req); + + return tcp_v6_ao_calc_key(mkt, key, + &ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr, + htons(ireq->ir_num), ireq->ir_rmt_port, + htonl(tcp_rsk(req)->snt_isn), + htonl(tcp_rsk(req)->rcv_isn)); +} + struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk, const struct in6_addr *addr, int sndid, int rcvid) @@ -70,6 +82,15 @@ struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); } +struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk, + struct request_sock *req, + int sndid, int rcvid) +{ + struct in6_addr *addr = &inet_rsk(req)->ir_v6_rmt_addr; + + return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); +} + int tcp_v6_ao_hash_pseudoheader(struct 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 99ab802a37e4..5e313185822a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -840,6 +840,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 @@ -1229,9 +1233,51 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, struct request_sock *req) { + struct tcp_md5sig_key *md5_key = NULL; + struct tcp_ao_key *ao_key = NULL; + const struct in6_addr *addr; + u8 keyid = 0; +#ifdef CONFIG_TCP_AO + char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + const struct tcp_ao_hdr *aoh; +#else + u8 *traffic_key = NULL; +#endif int l3index; l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0; + addr = &ipv6_hdr(skb)->saddr; + + if (tcp_rsk_used_ao(req)) { +#ifdef CONFIG_TCP_AO + /* Invalid TCP option size or twice included auth */ + if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh)) + return; + if (!aoh) + return; + ao_key = tcp_v6_ao_do_lookup(sk, addr, aoh->rnext_keyid, -1); + if (unlikely(!ao_key)) { + /* Send ACK with any matching MKT for the peer */ + ao_key = tcp_v6_ao_do_lookup(sk, addr, -1, -1); + /* Matching key disappeared (user removed the key?) + * let the handshake timeout. + */ + if (!ao_key) { + net_info_ratelimited("TCP-AO key for (%pI6, %d)->(%pI6, %d) suddenly disappeared, won't ACK new connection\n", + addr, + ntohs(tcp_hdr(skb)->source), + &ipv6_hdr(skb)->daddr, + ntohs(tcp_hdr(skb)->dest)); + return; + } + } + + keyid = aoh->keyid; + tcp_v6_ao_calc_key_rsk(ao_key, traffic_key, req); +#endif + } else { + md5_key = tcp_v6_md5_do_lookup(sk, addr, l3index); + } /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV * sk->sk_state == TCP_SYN_RECV -> for Fast Open. @@ -1247,9 +1293,9 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale, tcp_time_stamp_raw() + tcp_rsk(req)->ts_off, req->ts_recent, sk->sk_bound_dev_if, - tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr, l3index), + md5_key, ipv6_get_dsfield(ipv6_hdr(skb)), 0, sk->sk_priority, - tcp_rsk(req)->txhash, NULL, NULL, 0, 0); + tcp_rsk(req)->txhash, ao_key, traffic_key, keyid, 0); } @@ -1479,19 +1525,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; - - 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; + 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; + } } } #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 Wed Feb 15 18:33:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142048 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2763EC636D4 for ; Wed, 15 Feb 2023 18:34:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230325AbjBOSez (ORCPT ); Wed, 15 Feb 2023 13:34:55 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49868 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230250AbjBOSea (ORCPT ); Wed, 15 Feb 2023 13:34:30 -0500 Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DEB733D09E for ; Wed, 15 Feb 2023 10:34:03 -0800 (PST) Received: by mail-wm1-x32d.google.com with SMTP id j29-20020a05600c1c1d00b003dc52fed235so2245045wms.1 for ; Wed, 15 Feb 2023 10:34:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=vfQfRqhu/RTU6E1pzDrfzix5tBN9j9B/YnnniLfKKTo=; b=C0uiZPr7lQ71mgqGYJyqHVPX8mP4+vc4B4q93QDG4dBMQqg1oS6X7Lbhnq4TNRZ28v d1iA2NGSfyUtQ/i1TRH+BQ92N5Qcj4hG1lgrBs25oLWtnzMnGL/EtkkU7AC+QjeIuri9 EizB2hwq0Pr/y4XptywytV0bBJg/G8AAW+HTTIwuTb7uUb8N1V3OfLdU8x0nKPrgalTF 8BJHyMc1zZId/CJDgSpNvoyYvchyU9fluEfqmRWQ1zSTSrmH/D3JPyNvq8FM3CTcs1w8 ZHX9HrXkYtT9V9k8NLt+SCgRiN/oSAAIUZ01hM+1dwDy3RrTecCT3F5E8h0B+RGqWVA3 wJLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=vfQfRqhu/RTU6E1pzDrfzix5tBN9j9B/YnnniLfKKTo=; b=7AeffnPdRgqQavYQLNDrlEn57EE29Zqode6MaZps+ef0Qbr22GsVmgVwoez6tHH+1X eGH0AyNs/k714WtGmT6v3hWf25cBTaFkfpuqdCJB6Pv9v657hfhr2rwGnJp67j/AqDWc P2RbFE9NrTlmSt3dcTx4bZ0JCM/Fq5EF4+KckrXeGBzr1/CEBuukuHuebqzDigWhj/qT GlcIoSVbqEg+CgxarwDY5hFgxW65omG89WvD/mgEPnK/1BbFupdMwLCV1L9K8bfi1S5O 2s49Yn31U3y2Rjh0eBQC3PGsk5NtRjbzfTHBBiJFOBnkr6PTLV90qAV4qadEBhwJUt+2 twCg== X-Gm-Message-State: AO0yUKVmlCbLYR2xtDPQaAQmYpnnnhmV6J0j+iTRmlpuhNWatco7q6Hu qylRyUBsEyOiKaBQflk+dqnGtw== X-Google-Smtp-Source: AK7set/9JRwH8Efj0HLLNv+HWrwWz4UsnXz5MxkSboyVoj+gdp1hLlesFwCtCOnTCDgdJSoXxUtXww== X-Received: by 2002:a05:600c:3b93:b0:3e1:f8af:963f with SMTP id n19-20020a05600c3b9300b003e1f8af963fmr2931552wms.3.1676486043286; Wed, 15 Feb 2023 10:34:03 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:02 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 11/21] net/tcp: Sign SYN-ACK segments with TCP-AO Date: Wed, 15 Feb 2023 18:33:25 +0000 Message-Id: <20230215183335.800122-12-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 | 14 +++++++++++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 48 +++++++++++++++++++++++++++++++++++++------ net/ipv6/tcp_ao.c | 14 +++++++++++++ net/ipv6/tcp_ipv6.c | 1 + 7 files changed, 82 insertions(+), 6 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index d906fdbe93ec..1e5854a30e74 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2147,6 +2147,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 c7600b049e54..46b03e043d43 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -140,6 +140,9 @@ int tcp_ao_hash_hdr(unsigned short family, char *ao_hash, int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen); struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); +int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *mkt, + struct request_sock *req, const struct sk_buff *skb, + int hash_offset, u32 sne); int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send); @@ -175,6 +178,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 3bac6d3c43cb..624f6cffa123 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -579,6 +579,20 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, tkey, hash_offset, sne); } +int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key, + struct request_sock *req, const struct sk_buff *skb, + int hash_offset, u32 sne) +{ + char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + + tcp_v4_ao_calc_key_rsk(ao_key, traffic_key, req); + + tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb, + traffic_key, hash_offset, sne); + + return 0; +} + struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk, struct request_sock *req, int sndid, int rcvid) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 74d841233cfe..693260f00abc 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1691,6 +1691,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 2346d90c6a31..37999fa2f4df 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3632,6 +3632,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; @@ -3682,16 +3683,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, 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); @@ -3711,7 +3739,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); @@ -3719,7 +3747,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 a6bc28ee7487..0285488db9e8 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -123,3 +123,17 @@ int tcp_v6_parse_ao(struct sock *sk, int cmd, { return tcp_parse_ao(sk, cmd, AF_INET6, optval, optlen); } + +int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key, + struct request_sock *req, const struct sk_buff *skb, + int hash_offset, u32 sne) +{ + char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + + tcp_v6_ao_calc_key_rsk(ao_key, traffic_key, req); + + tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb, + traffic_key, hash_offset, sne); + + return 0; +} diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5e313185822a..ced00e934262 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -843,6 +843,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 Wed Feb 15 18:33:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142049 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C55A3C636D4 for ; Wed, 15 Feb 2023 18:35:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230256AbjBOSfK (ORCPT ); Wed, 15 Feb 2023 13:35:10 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49880 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230258AbjBOSeb (ORCPT ); Wed, 15 Feb 2023 13:34:31 -0500 Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 81CC93D916 for ; Wed, 15 Feb 2023 10:34:05 -0800 (PST) Received: by mail-wm1-x32d.google.com with SMTP id r18so14004029wmq.5 for ; Wed, 15 Feb 2023 10:34:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=tyDg/pc33MhxnVKm1XLGxqvupjAg0bEl/SFZ9vibQjA=; b=dZWlMUYpFjzKIM1fmXUefY+d25/1vjF0wVUUNAyQfNimVs7yj3ag80YkmVSWAqHzE6 zh55R8DoFNcEoHok5nLJzLlNei8BUviVpZUfEeENibfNZAXmeR72ukOm+mrxexEmEVHN uBr3c5HuQrHFFZwMJ5b0mEh5md5594+WXpWTYk1WYR8i2pjsyW3PWr5+E2eCg5dOBEQB djOS8GnLkmaLRlBNLaOM0bOcBhWpPc8HOn3fTpCgK3l+XHlMpqgyVzp32baMa/SpA24a GM9j4dpx2eZS9pXXvG7ZChxwWQ8S/jGjJvzrop6F9AOTyZppFuiuh2l60QUb/q7uP+pN l/mQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=tyDg/pc33MhxnVKm1XLGxqvupjAg0bEl/SFZ9vibQjA=; b=zfdwqTjxRUb398yOZW2V38HksP9lVWMQabzG7cB/VDXbEg+gSwDNumZ95wQWd5uP67 4+Ds4d8wNp5O7itGd2LgXvztRfrTIZ5/ZeyZaTelnx1np6aPETNlU2x/h4jyaYWVjLnB 6vP1VLNeKLEet18FhU4AUbD8c7Rn6kgIvRiqlsnuyr16dri3lRYbjEQYe9aGPt2gV/la RMAAFad4Jb8zzXQWsYYnwZWbWFAoY4wBjxwS+mLFp3U79oakwjLVoMeROZpgHMM0NAgS OifdVCZkeAXTW8EvrUw6y62JzUDUV6kW5kIrYkK921nqNctsIsXcVpg8yLBj0f8mWoaP ayuQ== X-Gm-Message-State: AO0yUKV6wTnreIYqbZP5jJ+7bUeBITEYJEFqzJmSjGOIPD2dFqlTYRdb KzGVeTBe+i441CpXzdsiRfsqPw== X-Google-Smtp-Source: AK7set8gFcKT7nd5mcAmaYYiohHBCUuiQHTzRZAWNmW0t9iaoa2Qj6kUJPqlae+x2s3rxAmlZx1lJA== X-Received: by 2002:a05:600c:997:b0:3df:e1cc:94ff with SMTP id w23-20020a05600c099700b003dfe1cc94ffmr2758919wmp.28.1676486044988; Wed, 15 Feb 2023 10:34:04 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:04 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 12/21] net/tcp: Verify inbound TCP-AO signed segments Date: Wed, 15 Feb 2023 18:33:26 +0000 Message-Id: <20230215183335.800122-13-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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.h | 18 +++++ include/net/tcp.h | 55 +++++++++++++- include/net/tcp_ao.h | 22 ++++++ net/ipv4/tcp.c | 39 ++-------- net/ipv4/tcp_ao.c | 154 +++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 10 +-- net/ipv6/tcp_ao.c | 12 +++ net/ipv6/tcp_ipv6.c | 11 +-- 8 files changed, 278 insertions(+), 43 deletions(-) diff --git a/include/net/dropreason.h b/include/net/dropreason.h index 70539288f995..848f4c5ab609 100644 --- a/include/net/dropreason.h +++ b/include/net/dropreason.h @@ -132,6 +132,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 @@ -147,6 +152,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 1e5854a30e74..3825b1352014 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1732,7 +1732,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) @@ -1754,7 +1754,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; } @@ -2566,4 +2566,55 @@ static inline int tcp_parse_auth_options(const struct tcphdr *th, return 0; } +/* Called with rcu_read_lock() */ +static inline enum skb_drop_reason +tcp_inbound_hash(struct sock *sk, const struct request_sock *req, + const struct sk_buff *skb, + const void *saddr, const void *daddr, + int family, int dif, int sdif) +{ + const struct tcphdr *th = tcp_hdr(skb); + const struct tcp_ao_hdr *aoh; + const __u8 *md5_location; + int l3index; + + /* Invalid option or two times meet any of auth options */ + if (tcp_parse_auth_options(th, &md5_location, &aoh)) + return SKB_DROP_REASON_TCP_AUTH_HDR; + + if (req) { + if (tcp_rsk_used_ao(req) != !!aoh) + return SKB_DROP_REASON_TCP_AOFAILURE; + } + + /* sdif set, means packet ingressed via a device + * in an L3 domain and dif is set to the l3mdev + */ + l3index = sdif ? dif : 0; + + /* Fast path: unsigned segments */ + if (likely(!md5_location && !aoh)) { + /* Drop if there's TCP-MD5 or TCP-AO key with any rcvid/sndid + * for the remote peer. On TCP-AO established connection + * the last key is impossible to remove, so there's + * always at least one current_key. + */ +#ifdef CONFIG_TCP_AO + if (unlikely(tcp_ao_do_lookup(sk, saddr, family, -1, -1, 0))) + return SKB_DROP_REASON_TCP_AONOTFOUND; +#endif + if (unlikely(tcp_md5_do_lookup(sk, l3index, saddr, family))) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); + return SKB_DROP_REASON_TCP_MD5NOTFOUND; + } + return SKB_NOT_DROPPED_YET; + } + + if (aoh) + return tcp_inbound_ao_hash(sk, skb, family, req, aoh); + + return tcp_inbound_md5_hash(sk, skb, saddr, daddr, family, + l3index, md5_location); +} + #endif /* _TCP_H */ diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 46b03e043d43..fbd4e2f6afff 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -109,6 +109,9 @@ struct tcp6_ao_context { __be32 disn; }; +#define TCP_AO_ESTABLISHED (TCPF_ESTABLISHED|TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|\ + TCPF_CLOSE|TCPF_CLOSE_WAIT|TCPF_LAST_ACK|TCPF_CLOSING) + int tcp_ao_hash_skb(unsigned short int family, char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, @@ -127,6 +130,10 @@ u32 tcp_ao_compute_sne(u32 sne, u32 seq, u32 new_seq); void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key); +enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, + const struct sk_buff *skb, unsigned short int family, + const struct request_sock *req, + const struct tcp_ao_hdr *aoh); struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port); @@ -148,9 +155,14 @@ int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, __be32 sisn, __be32 disn, bool send); int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, struct request_sock *req); +int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, __be32 sisn, __be32 disn); struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk, struct request_sock *req, int sndid, int rcvid); +bool tcp_v4_inbound_ao_hash(struct sock *sk, + struct request_sock *req, + const struct sk_buff *skb); int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, const u8 *tkey, int hash_offset, u32 sne); @@ -159,6 +171,9 @@ struct tcp_sigpool; 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); @@ -194,6 +209,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 e4136e87a162..3a2caa5eeee2 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4517,42 +4517,23 @@ EXPORT_SYMBOL(tcp_md5_hash_key); enum skb_drop_reason tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, const void *saddr, const void *daddr, - int family, int dif, int sdif) + int family, int l3index, const __u8 *hash_location) { - /* - * This gets called for each TCP segment that arrives - * so we want to be efficient. + /* This gets called for each TCP segment that has TCP-MD5 option. * We have 3 drop cases: * o No MD5 hash and one expected. * o MD5 hash and we're not expecting one. * o MD5 hash and its wrong. */ - const __u8 *hash_location = NULL; - struct tcp_md5sig_key *hash_expected; const struct tcphdr *th = tcp_hdr(skb); struct tcp_sock *tp = tcp_sk(sk); - int genhash, l3index; + struct tcp_md5sig_key *key; + int genhash; u8 newhash[16]; - /* sdif set, means packet ingressed via a device - * in an L3 domain and dif is set to the l3mdev - */ - l3index = sdif ? dif : 0; - - hash_expected = tcp_md5_do_lookup(sk, l3index, saddr, family); - if (tcp_parse_auth_options(th, &hash_location, NULL)) - return true; - - /* We've parsed the options - do we have a hash? */ - if (!hash_expected && !hash_location) - return SKB_NOT_DROPPED_YET; - - if (hash_expected && !hash_location) { - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); - return SKB_DROP_REASON_TCP_MD5NOTFOUND; - } + key = tcp_md5_do_lookup(sk, l3index, saddr, family); - if (!hash_expected && hash_location) { + if (!key && hash_location) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5UNEXPECTED); return SKB_DROP_REASON_TCP_MD5UNEXPECTED; } @@ -4562,14 +4543,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 624f6cffa123..d541bc136ae7 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -310,6 +310,27 @@ int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, htonl(tcp_rsk(req)->rcv_isn)); } +int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, __be32 sisn, + __be32 disn) +{ + const struct iphdr *iph = ip_hdr(skb); + const struct tcphdr *th = tcp_hdr(skb); + + return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr, + th->source, th->dest, sisn, disn); +} + +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); + else + return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn); +} + static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp, __be32 daddr, __be32 saddr, int nbytes) @@ -656,6 +677,139 @@ 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) +{ + unsigned char newhash[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr); + const struct tcphdr *th = tcp_hdr(skb); + + if (maclen != tcp_ao_maclen(key)) + return SKB_DROP_REASON_TCP_AOFAILURE; + + /* XXX: make it per-AF callback? */ + tcp_ao_hash_skb(family, newhash, key, sk, skb, traffic_key, + (phash - (u8 *)th), sne); + if (memcmp(phash, newhash, maclen)) + return SKB_DROP_REASON_TCP_AOFAILURE; + return SKB_NOT_DROPPED_YET; +} + +enum skb_drop_reason +tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, + unsigned short int family, const struct request_sock *req, + const struct tcp_ao_hdr *aoh) +{ + u8 key_buf[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + const struct tcphdr *th = tcp_hdr(skb); + u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */ + struct tcp_ao_info *info; + struct tcp_ao_key *key; + __be32 sisn, disn; + u8 *traffic_key; + u32 sne = 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; + + /* Check if this socket's rnext_key matches the keyid in the + * packet. If not we lookup the key based on the keyid + * matching the rcvid in the mkt. + */ + key = info->rnext_key; + if (key->rcvid != aoh->keyid) { + key = tcp_ao_matched_key(info, -1, aoh->keyid); + if (!key) + goto key_not_found; + } + + /* Delayed retransmitted SYN */ + if (unlikely(th->syn && !th->ack)) + goto verify_hash; + + sne = tcp_ao_compute_sne(info->rcv_sne, info->rcv_sne_seq, + ntohl(th->seq)); + /* Established socket, traffic key are cached */ + traffic_key = rcv_other_key(key); + err = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, + traffic_key, phash, sne); + if (err) + return err; + /* Key rotation: the peer asks us to use new key (RNext) */ + if (unlikely(aoh->rnext_keyid != info->current_key->sndid)) { + /* If the key is not found we do nothing. */ + key = tcp_ao_matched_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 (sk->sk_state == TCP_LISTEN) { + /* Make the initial syn the likely case here */ + if (unlikely(req)) { + sne = tcp_ao_compute_sne(0, tcp_rsk(req)->rcv_isn, + ntohl(th->seq)); + sisn = htonl(tcp_rsk(req)->rcv_isn); + disn = htonl(tcp_rsk(req)->snt_isn); + } else if (unlikely(th->ack && !th->syn)) { + /* Possible syncookie packet */ + sisn = htonl(ntohl(th->seq) - 1); + disn = htonl(ntohl(th->ack_seq) - 1); + 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; + } + } else if (sk->sk_state == TCP_SYN_SENT) { + disn = info->lisn; + if (th->syn) + sisn = th->seq; + else + sisn = info->risn; + } else { + WARN_ONCE(1, "TCP-AO: Unknown sk_state %d", sk->sk_state); + return SKB_DROP_REASON_TCP_AOFAILURE; + } +verify_hash: + traffic_key = key_buf; + tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family); + return tcp_ao_verify_hash(sk, skb, family, info, aoh, key, + traffic_key, phash, sne); + +key_not_found: + return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; +} + int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 693260f00abc..f6a3dde66c12 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2213,9 +2213,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); @@ -2291,8 +2291,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 0285488db9e8..77dfa706349c 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -39,6 +39,18 @@ static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, return tcp_ao_calc_traffic_key(mkt, key, &tmp, sizeof(tmp)); } +int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, + __be32 sisn, __be32 disn) +{ + const struct ipv6hdr *iph = ipv6_hdr(skb); + const struct tcphdr *th = tcp_hdr(skb); + + return tcp_v6_ao_calc_key(mkt, key, &iph->saddr, + &iph->daddr, th->source, + th->dest, sisn, disn); +} + int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index ced00e934262..96a4b62b1fef 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1817,9 +1817,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); @@ -1891,8 +1891,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; @@ -2118,6 +2118,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 Wed Feb 15 18:33:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142050 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CA16EC636D4 for ; Wed, 15 Feb 2023 18:35:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230221AbjBOSfo (ORCPT ); Wed, 15 Feb 2023 13:35:44 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49906 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230016AbjBOSec (ORCPT ); Wed, 15 Feb 2023 13:34:32 -0500 Received: from mail-wm1-x334.google.com (mail-wm1-x334.google.com [IPv6:2a00:1450:4864:20::334]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8209C3D936 for ; Wed, 15 Feb 2023 10:34:08 -0800 (PST) Received: by mail-wm1-x334.google.com with SMTP id az4-20020a05600c600400b003dff767a1f1so2237537wmb.2 for ; Wed, 15 Feb 2023 10:34:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=scP6JjSblAzGBLe2yEjZOn4CG1v0Zg3RQofDKm7H7+s=; b=URH7MH+zWLa/MN+LHkXZGeiFXnRj5UKNCpgejx5r+akTD0JqycgdZIgXR1jC+fPPFM w2adUhfoks6q04HzkuWudrHkgS4PlJIMGSd7r1z/UXO66VhWa51+EHy4nT8mBkV4Trt4 wZDvXxJ1iDEKETe2TY0ttDYIUSHkDde08KZPLe1pooqUtNik72FyTn0ElGA0diWpouiO CCAkAissJY7jnyqiJlKZObCMujDP9hyc6ZfhwBKISdpAWDKaAAN7O5fvx96TKdGHttv5 Y/HsXq8EGjyP0g53X7wxO2aIG++dEq7X7K7d/f/k8QpUYvA58w6QF2OCntvr6taX1STj jo6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=scP6JjSblAzGBLe2yEjZOn4CG1v0Zg3RQofDKm7H7+s=; b=fONx4I4uvPoUWtuVyi3jUjkq/akE+rOPDb3aK+j1YgOar9cTf6Fqvn5K2ziLYI7muq h/L1jSyOAUkXNtAGuGPxDW4FK+yWG1370edfr3xLWby7iGGOxpryergi2yUyGAZ55avk wsrRj+jWzeUDgQMk/tDQNptONTke3KglC0Z9M565cTChUjM3csH8wl2HkwADhG905H14 1CrSS/23Fo1cmj3/Bwj6T1a8ZkCuC3A1ihV5dPcF5DriEyc/eBqU3W+aOZD4ZGzzuvkA 7BPrZQSzzEAGa0trvoAO0ljzhkM1xFZUGG6AGev3dq23KxXn9YfurI+wn3pI1bu4VIz3 NKZA== X-Gm-Message-State: AO0yUKUtu//cpuANxe+up+N2fy6uZOlxUEGa4QWGeRtLEf7EkRSRJanA EA3oEkLfM4gq3jD9K/I7QYMyiw== X-Google-Smtp-Source: AK7set8yEsbmevqHD3PTB3f2JRhGaeA2+J0XMleUSbM20wITzAseh2FsGWNhNgJjKVb4jFwDyG3fgg== X-Received: by 2002:a05:600c:4d15:b0:3dc:5937:35a2 with SMTP id u21-20020a05600c4d1500b003dc593735a2mr2854190wmp.9.1676486046773; Wed, 15 Feb 2023 10:34:06 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:06 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 13/21] net/tcp: Add TCP-AO segments counters Date: Wed, 15 Feb 2023 18:33:27 +0000 Message-Id: <20230215183335.800122-14-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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.h | 15 +++++++++++---- include/net/tcp.h | 9 ++++++++- include/net/tcp_ao.h | 10 ++++++++++ include/uapi/linux/snmp.h | 4 ++++ net/ipv4/proc.c | 4 ++++ net/ipv4/tcp_ao.c | 25 ++++++++++++++++++++++--- 6 files changed, 59 insertions(+), 8 deletions(-) diff --git a/include/net/dropreason.h b/include/net/dropreason.h index 848f4c5ab609..36b2e513ab25 100644 --- a/include/net/dropreason.h +++ b/include/net/dropreason.h @@ -153,17 +153,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 3825b1352014..ff1680611530 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2600,8 +2600,15 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req, * always at least one current_key. */ #ifdef CONFIG_TCP_AO - if (unlikely(tcp_ao_do_lookup(sk, saddr, family, -1, -1, 0))) + if (unlikely(tcp_ao_do_lookup(sk, saddr, family, -1, -1, 0))) { + struct tcp_ao_info *ao_info; + + ao_info = rcu_dereference_check(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOREQUIRED); + atomic64_inc(&ao_info->counters.ao_required); return SKB_DROP_REASON_TCP_AONOTFOUND; + } #endif if (unlikely(tcp_md5_do_lookup(sk, l3index, saddr, family))) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index fbd4e2f6afff..07d05bb49e46 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -20,6 +20,13 @@ struct tcp_ao_hdr { u8 rnext_keyid; }; +struct tcp_ao_counters { + atomic64_t pkt_good; + atomic64_t pkt_bad; + atomic64_t key_not_found; + atomic64_t ao_required; +}; + struct tcp_ao_key { struct hlist_node node; union tcp_ao_addr addr; @@ -35,6 +42,8 @@ struct tcp_ao_key { u8 maclen; u8 digest_size; struct rcu_head rcu; + atomic64_t pkt_good; + atomic64_t pkt_bad; u8 traffic_keys[]; }; @@ -78,6 +87,7 @@ struct tcp_ao_info { */ struct tcp_ao_key *volatile current_key; struct tcp_ao_key *rnext_key; + struct tcp_ao_counters counters; u8 ao_flags; __be32 lisn; __be32 risn; diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index 6600cb0164c2..55176bf83320 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -293,6 +293,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/net/ipv4/proc.c b/net/ipv4/proc.c index f88daace9de3..a4e012afd378 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 d541bc136ae7..d34ff3682cf8 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -186,6 +186,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; } @@ -687,14 +689,25 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr); const struct tcphdr *th = tcp_hdr(skb); - if (maclen != tcp_ao_maclen(key)) + if (maclen != tcp_ao_maclen(key)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); + atomic64_inc(&info->counters.pkt_bad); + atomic64_inc(&key->pkt_bad); return SKB_DROP_REASON_TCP_AOFAILURE; + } /* XXX: make it per-AF callback? */ tcp_ao_hash_skb(family, newhash, key, sk, skb, traffic_key, (phash - (u8 *)th), sne); - if (memcmp(phash, newhash, maclen)) + if (memcmp(phash, newhash, maclen)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); + atomic64_inc(&info->counters.pkt_bad); + atomic64_inc(&key->pkt_bad); return SKB_DROP_REASON_TCP_AOFAILURE; + } + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD); + atomic64_inc(&info->counters.pkt_good); + atomic64_inc(&key->pkt_good); return SKB_NOT_DROPPED_YET; } @@ -713,8 +726,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; @@ -807,6 +822,8 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, traffic_key, phash, sne); key_not_found: + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); + atomic64_inc(&info->counters.key_not_found); return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; } @@ -1501,6 +1518,8 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, key->keyflags = cmd.tcpa_keyflags; key->sndid = cmd.tcpa_sndid; key->rcvid = cmd.tcpa_rcvid; + atomic64_set(&key->pkt_good, 0); + atomic64_set(&key->pkt_bad, 0); ret = tcp_ao_parse_crypto(&cmd, key); if (ret < 0) From patchwork Wed Feb 15 18:33:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142051 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6AF95C636CC for ; Wed, 15 Feb 2023 18:35:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230285AbjBOSfs (ORCPT ); Wed, 15 Feb 2023 13:35:48 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49918 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230286AbjBOSed (ORCPT ); Wed, 15 Feb 2023 13:34:33 -0500 Received: from mail-wr1-x42f.google.com (mail-wr1-x42f.google.com [IPv6:2a00:1450:4864:20::42f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E29BE3E090 for ; Wed, 15 Feb 2023 10:34:09 -0800 (PST) Received: by mail-wr1-x42f.google.com with SMTP id s13so2388889wrw.3 for ; Wed, 15 Feb 2023 10:34:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=OGskyb7pJ6ELCDekkQO9Fpll4hie2FaxctextYDdWfk=; b=CvkYkGojRTF1eT48EAhOGxpKFAfJuXXxWbY2N2vR/d8PSdGwxRi0RhW/cnfkVMsAZb UgHEANYfWje/SIme3N1mvQJEZSkYoSwbgquxz1tXaWeUi4PNJrl9un3ZY5OMUqjUY586 +msL/Q7rs8Wuc9bFSUsEEH7usaRTGp6bWjN/mRrqceDp5Lhr/kjq6tl1jvImVNoNV5On jJOLzL+XV2hTym2JzS0CDZwnAi2GDxFeZlJheO4GsR02YAiEkdHATqcmYeJUBrTiEXgX CXSkyvtQjYwMTZybAeuCRcwnfVb3cDKh5mVveIt6xB2uxt3QdXYVJMAIAesQ7jF4/Lz3 rcCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=OGskyb7pJ6ELCDekkQO9Fpll4hie2FaxctextYDdWfk=; b=C1JtjIv7diKI5jDYsofyhoM8zHzexPm45ad5cmrrt3+sGPe4wJ9tjjsVXL53Ao6x0n iyKECEQPMWN1iv/ClLiZG+JV9YWGWC8mO8D29DDHQis8l2xCBCnaiR2J7VH3or6lScpu rTlkz4/glEfkrxWw257X2E50ne03xOiy8FtqM85FJVJeDLGTAeQqAdvRfPtRIDTxfT7D 3XNULLfsp1CeEhdsIgAl7g9hN5Uww0LpCzm0QmQ2U/9xj+M4rdunqFfpRfOBDFBickBS amJQ+Nm31Ubd3dTo+u42SxTuEECBYXpR4D2ulYvGRl88nQ+T2A2BaxlHV+a0QlukdgEz eGgg== X-Gm-Message-State: AO0yUKX9ZwD2A6lRAmCk1y3mgBxbN6+RcF7c0Sefgc+xcqlw383l5G5T MYqrwk/bCZxZvjeP7JfdXAQKkg== X-Google-Smtp-Source: AK7set+zt7TRGbCDSYN9tlczhKXE0qGkcB3FKbyoZ66mwG4AUB7+4Kx8jy9YzFAjTaJOQC8rFW7NOg== X-Received: by 2002:adf:ef91:0:b0:2c5:4d16:8d73 with SMTP id d17-20020adfef91000000b002c54d168d73mr2693168wro.40.1676486049201; Wed, 15 Feb 2023 10:34:09 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:08 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 14/21] net/tcp: Add TCP-AO SNE support Date: Wed, 15 Feb 2023 18:33:28 +0000 Message-Id: <20230215183335.800122-15-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Add Sequence Number Extension (SNE) extension for TCP-AO. This is needed to protect long-living TCP-AO connections from replaying attacks after sequence number roll-over, see RFC5925 (6.2). Co-developed-by: Francesco Ruggeri Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov --- net/ipv4/tcp_input.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 264db761153b..9f23cab1e835 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3528,9 +3528,21 @@ static inline bool tcp_may_update_window(const struct tcp_sock *tp, static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack) { u32 delta = ack - tp->snd_una; +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao; +#endif sock_owned_by_me((struct sock *)tp); tp->bytes_acked += delta; +#ifdef CONFIG_TCP_AO + ao = rcu_dereference_protected(tp->ao_info, + lockdep_sock_is_held((struct sock *)tp)); + if (ao) { + if (ack < ao->snd_sne_seq) + ao->snd_sne++; + ao->snd_sne_seq = ack; + } +#endif tp->snd_una = ack; } @@ -3538,9 +3550,21 @@ static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack) static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq) { u32 delta = seq - tp->rcv_nxt; +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao; +#endif sock_owned_by_me((struct sock *)tp); tp->bytes_received += delta; +#ifdef CONFIG_TCP_AO + ao = rcu_dereference_protected(tp->ao_info, + lockdep_sock_is_held((struct sock *)tp)); + if (ao) { + if (seq < ao->rcv_sne_seq) + ao->rcv_sne++; + ao->rcv_sne_seq = seq; + } +#endif WRITE_ONCE(tp->rcv_nxt, seq); } @@ -6369,6 +6393,17 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, * simultaneous connect with crossed SYNs. * Particularly, it can be connect to self. */ +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao; + + ao = rcu_dereference_protected(tp->ao_info, + lockdep_sock_is_held(sk)); + if (ao) { + ao->risn = th->seq; + ao->rcv_sne = 0; + ao->rcv_sne_seq = ntohl(th->seq); + } +#endif tcp_set_state(sk, TCP_SYN_RECV); if (tp->rx_opt.saw_tstamp) { From patchwork Wed Feb 15 18:33:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142053 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7CFC5C636D4 for ; Wed, 15 Feb 2023 18:36:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230196AbjBOSgC (ORCPT ); Wed, 15 Feb 2023 13:36:02 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49856 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230297AbjBOSee (ORCPT ); Wed, 15 Feb 2023 13:34:34 -0500 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 8DD2A3D92D for ; Wed, 15 Feb 2023 10:34:11 -0800 (PST) Received: by mail-wm1-x329.google.com with SMTP id r18so14004250wmq.5 for ; Wed, 15 Feb 2023 10:34:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=x17E+eium4VyrfGc7M1myMUnIQM/HJYLttYEMQIU4XY=; b=drIBnfo8BmIYETYhR0pcax10wLOPxJ+wXDgHFy8Qm0ZQytfDYvylUeXrkqM2KMI29H uSrVZcpyJWe9h41LHuh5zJO3szcXDLqa/rLvcrVKcHFLzdgL8C1UYcAw6xapvCpBbwbX nFlzHxjxm+6+d7jlPTYKVtucoeINgm60UXJmKLxv6dnRpewaK+6d5oMTjtJnYx6jv+V1 Wm4CoUzAuzBLV0e0QIQ+R9sKPXpK5N9QahMBZaDdNXFfqXqwmPmM1cMBjCNvA4OSjSUz JEWMMTnPzwgUrBPkNvqpCoecl2XxUBYSbfrzuIJ3+Oy9+2D3yj9wYRT09QlblsuGVvf6 QVyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=x17E+eium4VyrfGc7M1myMUnIQM/HJYLttYEMQIU4XY=; b=jtWS+yVZZOzkPMxkb7TdSABlGdg1+mKZfq5xjEECnAo+SvMiLD5UYDTfKhKfHuHcCq ZpON6x57V1I2sWf/1pN21kT65b4jfcWfK2r065+cDLGBaHt1TdNy5EdV1WSo8E8sUQr6 HQdyn/95AFikfGo01D3HVPuGZFvlVTODmk9TxSICJBZaKec35lOSZnR95IqNinIkaNDy LIucitLUQWZLaDPMcSK6OWSb44p9FyXYz/dCAjCBZzNrtFDAzXRdgHOP18veeA7kyt5V xoLYZGLL7H3UbEDGCHQQ6s+McvbLqCjsJDD5RmgKzOFgZUEFIVITxu5UsWRWp3xKROIw w/3A== X-Gm-Message-State: AO0yUKXp28DMo1M4c9rfbKig7BejXfWmigXXKqyyIwh8/zCE77Gbti1k j0flnQzoI6ekv2NMsyuRzylyTA== X-Google-Smtp-Source: AK7set9o3pNND0YfqQtKqdaBS6DMHAI5qmU2BXmTeF8UsrS4Cz6utn7sdLFB2UpNmOlesyNqccJv7w== X-Received: by 2002:a05:600c:310b:b0:3df:f7ba:14e1 with SMTP id g11-20020a05600c310b00b003dff7ba14e1mr2944594wmo.8.1676486051002; Wed, 15 Feb 2023 10:34:11 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:10 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 15/21] net/tcp: Add tcp_hash_fail() ratelimited logs Date: Wed, 15 Feb 2023 18:33:29 +0000 Message-Id: <20230215183335.800122-16-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 | 27 +++++++++++++++++++++++++++ net/ipv4/tcp.c | 23 +++++++++++++---------- net/ipv4/tcp_ao.c | 7 +++++++ 4 files changed, 59 insertions(+), 12 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index ff1680611530..7b1d8b05e3bc 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2579,12 +2579,19 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req, int l3index; /* Invalid option or two times meet any of auth options */ - if (tcp_parse_auth_options(th, &md5_location, &aoh)) + if (tcp_parse_auth_options(th, &md5_location, &aoh)) { + tcp_hash_fail("TCP segment has incorrect auth options set", + family, skb, ""); return SKB_DROP_REASON_TCP_AUTH_HDR; + } if (req) { - if (tcp_rsk_used_ao(req) != !!aoh) + if (tcp_rsk_used_ao(req) != !!aoh) { + tcp_hash_fail("TCP connection can't start/end using TCP-AO", + family, skb, " %s", + !aoh ? "missing AO" : "AO signed"); return SKB_DROP_REASON_TCP_AOFAILURE; + } } /* sdif set, means packet ingressed via a device @@ -2607,11 +2614,14 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req, lockdep_sock_is_held(sk)); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOREQUIRED); atomic64_inc(&ao_info->counters.ao_required); + tcp_hash_fail("AO hash is required, but not found", + family, skb, ""); return SKB_DROP_REASON_TCP_AONOTFOUND; } #endif if (unlikely(tcp_md5_do_lookup(sk, l3index, saddr, family))) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); + tcp_hash_fail("MD5 Hash not found", family, skb, ""); return SKB_DROP_REASON_TCP_MD5NOTFOUND; } return SKB_NOT_DROPPED_YET; diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 07d05bb49e46..d953105f5f73 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -98,6 +98,33 @@ struct tcp_ao_info { atomic_t refcnt; /* Protects twsk destruction */ }; +#define tcp_hash_fail(msg, family, skb, fmt, ...) \ +do { \ + const struct tcphdr *th = tcp_hdr(skb); \ + char hdr_flags[5] = {}; \ + char *f = hdr_flags; \ + \ + if (th->fin) \ + *f++ = 'F'; \ + if (th->syn) \ + *f++ = 'S'; \ + if (th->rst) \ + *f++ = 'R'; \ + if (th->ack) \ + *f = 'A'; \ + if (family == AF_INET) { \ + net_info_ratelimited("%s for (%pI4, %d)->(%pI4, %d) %s" fmt "\n", \ + msg, &ip_hdr(skb)->saddr, ntohs(th->source), \ + &ip_hdr(skb)->daddr, ntohs(th->dest), \ + hdr_flags, ##__VA_ARGS__); \ + } else { \ + net_info_ratelimited("%s for [%pI6c]:%u->[%pI6c]:%u %s" fmt "\n", \ + msg, &ipv6_hdr(skb)->saddr, ntohs(th->source), \ + &ipv6_hdr(skb)->daddr, ntohs(th->dest), \ + hdr_flags, ##__VA_ARGS__); \ + } \ +} while (0) + #ifdef CONFIG_TCP_AO /* TCP-AO structures and functions */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 3a2caa5eeee2..d59e7422a481 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4525,7 +4525,6 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, * o MD5 hash and we're not expecting one. * o MD5 hash and its wrong. */ - const struct tcphdr *th = tcp_hdr(skb); struct tcp_sock *tp = tcp_sk(sk); struct tcp_md5sig_key *key; int genhash; @@ -4535,6 +4534,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; } @@ -4550,16 +4550,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 d34ff3682cf8..6f650ebe01c4 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -693,6 +693,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; } @@ -703,6 +705,7 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); atomic64_inc(&info->counters.pkt_bad); atomic64_inc(&key->pkt_bad); + tcp_hash_fail("AO hash mismatch", family, skb, ""); return SKB_DROP_REASON_TCP_AOFAILURE; } NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD); @@ -728,6 +731,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; } @@ -824,6 +829,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 Wed Feb 15 18:33:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142055 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1D46EC636D4 for ; Wed, 15 Feb 2023 18:36:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229524AbjBOSgG (ORCPT ); Wed, 15 Feb 2023 13:36:06 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50920 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229999AbjBOSfK (ORCPT ); Wed, 15 Feb 2023 13:35:10 -0500 Received: from mail-wm1-x334.google.com (mail-wm1-x334.google.com [IPv6:2a00:1450:4864:20::334]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6EE453E084 for ; Wed, 15 Feb 2023 10:34:13 -0800 (PST) Received: by mail-wm1-x334.google.com with SMTP id m20-20020a05600c3b1400b003e1e754657aso2295858wms.2 for ; Wed, 15 Feb 2023 10:34:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=hl7En8l9enlD7IV/XkIclALr8wUGcK1oLOcQr2oaW9I=; b=YkGKp2O5+DEX7jQLbdyq4NwtrvtRy/iKoqepJwivpUN0IBBJJ5Q1ljqX2XBupqK8cq 8TDzY28x4NnuLPFqx2n2C1wThLdQMSGsT/rXtBrJ8aGqauRLypJipWNtE0/WwU1vSxwU DZ0tqLI76NWv+7A1whqegfTdVbrKGYIGlBwst4SbVCTf5SfqtbYhpvL1cR2c/BEt2LH2 CAXxTmJ9YrYDCwh6ikTIrtj/lqnclkCCoOTTTnyL60/ZAZ2/GXPXlWJh9nk0IPiFPfIE Rc+FGVrxRD1d5k9LBqXEqxcBRM421o/yO8Ttf8xoBD4awDbAigYA6+9mVJo13+cyfjQA MIOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=hl7En8l9enlD7IV/XkIclALr8wUGcK1oLOcQr2oaW9I=; b=r9QnMhP7kFMGFp3Vl5o0L9h3ZBXGJIcmVdGxIDP7EyMGO7nisawQJAI7YEa+Y9CIVc TeGNdPoFHL8Roap0VMYgaipT6ME1zsJnPkhvmdu5Ki4HX4W6bhZyy6rjb6Pt1n1KwIuo MFoTFK2FNQvEVbWlYx+bSZ5uP7H66HbAGOUzfjd1xXqm/C5737dRfuMs+s3a4woxX7T0 2Cgsw6mQwBS6g3ksBlcxJh6OgTJmA3/1w2hgDPF3O6hXeH4m90wW7DmximWtwKB+IiyP 0Ukm1QR20nkKXCsiQXIDw5PIi5PVICFKLZ5gjQd7OkPglsMA0KV7TVWiT60jWsMXNOzS lwLw== X-Gm-Message-State: AO0yUKWS8LnUiM1MdXq1+ggmqBQ6btLdbmcwRK4AtxxgkZ34Pp0IbTMT 7oh1qD2owJyX3l/xRm5SGobdfw== X-Google-Smtp-Source: AK7set+BO3JePQRJYGb+7Qt3pr4fyYpY1zIlstAX60ZVv+IpsXvwBScczlK5TPxOusQ03HnjHIF9bQ== X-Received: by 2002:a05:600c:4447:b0:3da:28a9:a900 with SMTP id v7-20020a05600c444700b003da28a9a900mr2613322wmn.41.1676486052603; Wed, 15 Feb 2023 10:34:12 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:12 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 16/21] net/tcp: Ignore specific ICMPs for TCP-AO connections Date: Wed, 15 Feb 2023 18:33:30 +0000 Message-Id: <20230215183335.800122-17-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 | 1 + net/ipv4/proc.c | 1 + net/ipv4/tcp_ao.c | 70 ++++++++++++++++++++++++++++++++++++++- net/ipv4/tcp_ipv4.c | 5 +++ net/ipv6/tcp_ipv6.c | 4 +++ 7 files changed, 91 insertions(+), 1 deletion(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index d953105f5f73..01cdba46591c 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -25,6 +25,7 @@ struct tcp_ao_counters { atomic64_t pkt_bad; atomic64_t key_not_found; atomic64_t ao_required; + atomic64_t dropped_icmp; }; struct tcp_ao_key { @@ -77,6 +78,9 @@ static inline unsigned int tcp_ao_digest_size(struct tcp_ao_key *key) return key->digest_size; } +/* bits in 'ao_flags' */ +#define AO_ACCEPT_ICMPS BIT(0) + struct tcp_ao_info { struct hlist_head head; struct rcu_head rcu; @@ -167,6 +171,7 @@ u32 tcp_ao_compute_sne(u32 sne, u32 seq, u32 new_seq); void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key); +bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code); enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, unsigned short int family, const struct request_sock *req, @@ -246,6 +251,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 55176bf83320..7d9094df04a1 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -297,6 +297,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 7de4558469e1..6c8b4dcc51ee 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -357,6 +357,7 @@ struct tcp_diag_md5sig { #define TCP_AO_CMDF_CURR (1 << 0) /* Only checks field sndid */ #define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */ +#define TCP_AO_CMDF_ACCEPT_ICMP (1 << 2) /* Accept incoming ICMPs */ struct tcp_ao { /* setsockopt(TCP_AO) */ struct __kernel_sockaddr_storage tcpa_addr; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index a4e012afd378..e3d9ab1cfb08 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 6f650ebe01c4..07935e8a1066 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) @@ -52,6 +53,63 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, return 1; } +bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code) +{ + struct tcp_ao_info *ao; + bool ignore_icmp = false; + + /* RFC5925, 7.8: + * >> A TCP-AO implementation MUST default to ignore incoming ICMPv4 + * messages of Type 3 (destination unreachable), Codes 2-4 (protocol + * unreachable, port unreachable, and fragmentation needed -- ’hard + * errors’), and ICMPv6 Type 1 (destination unreachable), Code 1 + * (administratively prohibited) and Code 4 (port unreachable) intended + * for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN- + * WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs. + */ + if (sk->sk_family == AF_INET) { + if (type != ICMP_DEST_UNREACH) + return false; + if (code < ICMP_PROT_UNREACH || code > ICMP_FRAG_NEEDED) + return false; + } else if (sk->sk_family == AF_INET6) { + if (type != ICMPV6_DEST_UNREACH) + return false; + if (code != ICMPV6_ADM_PROHIBITED && code != ICMPV6_PORT_UNREACH) + return false; + } else { + WARN_ON_ONCE(1); + return false; + } + + rcu_read_lock(); + switch (sk->sk_state) { + case TCP_TIME_WAIT: + ao = rcu_dereference(tcp_twsk(sk)->ao_info); + break; + case TCP_SYN_SENT: + case TCP_SYN_RECV: + case TCP_LISTEN: + case TCP_NEW_SYN_RECV: + /* RFC5925 specifies to ignore ICMPs *only* on connections + * in synchronized states. + */ + rcu_read_unlock(); + return false; + default: + ao = rcu_dereference(tcp_sk(sk)->ao_info); + } + + if (ao && !(ao->ao_flags & AO_ACCEPT_ICMPS)) { + ignore_icmp = true; + __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAODROPPEDICMPS); + atomic64_inc(&ao->counters.dropped_icmp); + } + rcu_read_unlock(); + + return ignore_icmp; +} + /* 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. @@ -1442,7 +1500,7 @@ static inline bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd, #define TCP_AO_KEYF_ALL (0) #define TCP_AO_CMDF_ADDMOD_VALID \ - (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT) + (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP) #define TCP_AO_CMDF_DEL_VALID \ (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT) @@ -1528,6 +1586,11 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, atomic64_set(&key->pkt_good, 0); atomic64_set(&key->pkt_bad, 0); + if (cmd.tcpa_flags & TCP_AO_CMDF_ACCEPT_ICMP) + ao_info->ao_flags |= AO_ACCEPT_ICMPS; + else + ao_info->ao_flags &= ~AO_ACCEPT_ICMPS; + ret = tcp_ao_parse_crypto(&cmd, key); if (ret < 0) goto err_free_sock; @@ -1690,6 +1753,11 @@ static int tcp_ao_mod_cmd(struct sock *sk, unsigned short int family, if (!ao_info) return -ENOENT; /* TODO: make tcp_ao_current_rnext() and flags set atomic */ + if (cmd.tcpa_flags & TCP_AO_CMDF_ACCEPT_ICMP) + ao_info->ao_flags |= AO_ACCEPT_ICMPS; + else + ao_info->ao_flags &= ~AO_ACCEPT_ICMPS; + return tcp_ao_current_rnext(sk, cmd.tcpa_flags, cmd.tcpa_current, cmd.tcpa_rnext); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f6a3dde66c12..64a81f1dd4d4 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -492,6 +492,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; } @@ -506,6 +508,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 96a4b62b1fef..5c5c8509a6e2 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -398,6 +398,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; } @@ -409,6 +411,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 Wed Feb 15 18:33:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142060 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1265DC636CC for ; Wed, 15 Feb 2023 18:36:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229628AbjBOSgs (ORCPT ); Wed, 15 Feb 2023 13:36:48 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49850 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230286AbjBOSfu (ORCPT ); Wed, 15 Feb 2023 13:35:50 -0500 Received: from mail-wr1-x42c.google.com (mail-wr1-x42c.google.com [IPv6:2a00:1450:4864:20::42c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0CCF63E09C for ; Wed, 15 Feb 2023 10:34:22 -0800 (PST) Received: by mail-wr1-x42c.google.com with SMTP id bk16so20093749wrb.11 for ; Wed, 15 Feb 2023 10:34:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=p/EabCUhoiLUi7Ie+3BWi6GbQs9F1PDAGXW1eduw+s4=; b=bCdbPSwgztK9Ost3LLH6wgbJEBxHn4MfIxvC7+fwWjwbIhqcxWxmKAKtgtsSrMPQi5 v1fQ6tlYYjwEjD3GYUN4crLNRny231Og2Nf0YkYUnh1zERZZ4wA5pFrpmIFM5Mb1iJHo IkvSujdWtXzmnsxq6zaNaZ3cr6nFWDnWUG6xXiEzsNM1ZP5r371kle8ArMx99yM6SO87 ldGS4YtmAKgHGTsZOlGEnDieRpOCR2RTSF2x4+3R30Kk1Mf3H/LyOvMjYGRak84Mh7sa +1AlG1SS0Jd5l5ktWOlxQEtTvwMuSP/Y3ibQ97kK1Ax+LN72/FRQT5/PrcIRXXbWoT67 FjTw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=p/EabCUhoiLUi7Ie+3BWi6GbQs9F1PDAGXW1eduw+s4=; b=gRncM97wqushXtPforK/Z2gNIwxAI9eqr3rvDgjwB2ztJAD2Y4n+adJgZZ2lwmNo3w gyH2hAoSUQTwCPusbbTQOHIxNF7h7BUwSgnADiNsqSRGBv/AKMhLXF1GqjULajlvkq95 DoNb42GxijB/1LjUVQiNszEf6CmToAuVCPxtbAgw3Gj0nS4VKZAU6YkCffq6sgPHFcwD f6F4N6qJiSnPKseAaSGT2sGGUcfYSDLJEeRWlOIXTZWooTvIlwieUpeHAGxFPzsvFHIF S2++/ytn1vovU9gijwdvGslgxehZmyX1AJEktfR6XbP+qc9WpKwt9RkWalARn/rD7Pu9 5Gjg== X-Gm-Message-State: AO0yUKXhX657eI2vsUdWIchZ1unFZP8Uxshc+jF79RadMK3h/0PyqKLL oMfMazal1ia4RDLoA4db/tdMQA== X-Google-Smtp-Source: AK7set/y459HtAjc/HoitRB5KuaGjWyrvm2kWgPXTFvRTHGxZYbl2I7bch0bNHX21GubrmTq8w+V8A== X-Received: by 2002:a05:6000:3c7:b0:2c5:4c07:9593 with SMTP id b7-20020a05600003c700b002c54c079593mr245099wrg.16.1676486054397; Wed, 15 Feb 2023 10:34:14 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:13 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 17/21] net/tcp: Add option for TCP-AO to (not) hash header Date: Wed, 15 Feb 2023 18:33:31 +0000 Message-Id: <20230215183335.800122-18-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 6c8b4dcc51ee..323ee886634f 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" + */ #define TCP_AO_CMDF_CURR (1 << 0) /* Only checks field sndid */ #define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */ diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 07935e8a1066..d335023a1619 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -589,7 +589,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, ao_hash, 0); @@ -631,7 +632,8 @@ int tcp_ao_hash_skb(unsigned short int family, goto clear_hash; if (tcp_ao_hash_pseudoheader(family, sk, skb, &hp, skb->len)) goto clear_hash; - if (tcp_ao_hash_header(&hp, th, false, + if (tcp_ao_hash_header(&hp, th, + !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), ao_hash, hash_offset, tcp_ao_maclen(key))) goto clear_hash; if (tcp_ao_hash_skb_data(&hp, skb, th->doff << 2)) @@ -1498,7 +1500,7 @@ static inline bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd, } #endif -#define TCP_AO_KEYF_ALL (0) +#define TCP_AO_KEYF_ALL (TCP_AO_KEYF_EXCLUDE_OPT) #define TCP_AO_CMDF_ADDMOD_VALID \ (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP) #define TCP_AO_CMDF_DEL_VALID \ From patchwork Wed Feb 15 18:33:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142057 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2285DC636D4 for ; Wed, 15 Feb 2023 18:36:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230257AbjBOSg2 (ORCPT ); Wed, 15 Feb 2023 13:36:28 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51578 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230338AbjBOSfl (ORCPT ); Wed, 15 Feb 2023 13:35:41 -0500 Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 862533D08E for ; Wed, 15 Feb 2023 10:34:16 -0800 (PST) Received: by mail-wm1-x32d.google.com with SMTP id r18so14004420wmq.5 for ; Wed, 15 Feb 2023 10:34:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=r/VQdnYH46NbMmrwTqXavV51eMfPPXfb2u0jtaKfOjg=; b=Yiq3R37biK810S5TAiBmFo8HNfk5kQaEbA5+6uUJhgy4SgzaAn1Yumborg2N1P9WxY Z3filyZsmT48aMDJHWPgDAmv+0fQ3/evZ1QOJm/04LA45ucfQDkKMvGgs8KYX39N4RUB r51Fh9biGSvT2xF/zcbhcYc5fN96dyOHdXnW1aLkWMQlV5ehFyn29VEWFepV0+hE+O4U BLvzHWSkKXkWwjaFBWd3ieQWdbZ/0oyKuGKc/lNcxXKkfXxlrYUpPmrCtQ6+fTEDJnyq Ha3s/mFlbdcT30iEktCGZi+sRZDSzCNIcxML9sMpm7mYaKFy5Y5LLybv/lIW2BA/MK9m BCYQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=r/VQdnYH46NbMmrwTqXavV51eMfPPXfb2u0jtaKfOjg=; b=FLCvB/TfDe8XwVc8CdleTqZYGHw3PmoFFKcdqeo0ukwO79E2TXPfc3hjUE9uxdG3P4 IJkuKjaIbsXSUQehbgjRvHWgOUz6/Rj4CSOLMO+wbsvXtUNlBkdxSsopqNaqPCV/v0KQ XFuMbdjCa7q0xa7syXzXR+eN2+kHo5f4psM451Y9aKLEz11ok5aUpxr9NlbK/kELQ7zC nQtduwksJdNr1AYgm1UeMJHrpCDjC1c4AYpkZb8orJIuRbVqtnoAz2P1qVqsMIctrICI Dnl6Ta2xvlMI6NzSuw8v+tHlfvvuLfgvwikp+1pZh21zMR8gHYqLju8b7TcXz1aQK+kH 0bOg== X-Gm-Message-State: AO0yUKVwzIJ1LqhfK0a9Ft/lkjYRY49p/jH6fJinvEqOGzsf4X0KX/Ta 9hf8+wNfQSK/uvuKor/Qbqp7WQ== X-Google-Smtp-Source: AK7set/HiDnjESjdB4vTKqqqGD3B1Bzkha9ua8A+lj79URxSOOtKu2fnXD12F97dZ8MEyecBkfrTaA== X-Received: by 2002:a05:600c:340a:b0:3df:e752:35ca with SMTP id y10-20020a05600c340a00b003dfe75235camr3357626wmp.32.1676486056072; Wed, 15 Feb 2023 10:34:16 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:15 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 18/21] net/tcp: Add getsockopt(TCP_AO_GET) Date: Wed, 15 Feb 2023 18:33:32 +0000 Message-Id: <20230215183335.800122-19-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org Introduce getsockopt() that let user get TCP-AO keys and their properties from a socket. A user can provide a filter to match a specific key to be dumped or TCP_AO_GET_ALL flag may be used to dump all keys in one syscall. Co-developed-by: Francesco Ruggeri 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 | 1 + include/uapi/linux/tcp.h | 20 ++++ net/ipv4/tcp.c | 11 ++ net/ipv4/tcp_ao.c | 228 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 260 insertions(+) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 01cdba46591c..04e3bcee05f7 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -172,6 +172,7 @@ void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key); bool tcp_ao_ignore_icmp(struct sock *sk, int type, int code); +int tcp_ao_get_mkts(struct sock *sk, 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, diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 323ee886634f..eaf77c0a4425 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -132,6 +132,7 @@ enum { #define TCP_AO 38 /* (Add/Set MKT) */ #define TCP_AO_DEL 39 /* (Delete MKT) */ #define TCP_AO_MOD 40 /* (Modify MKT) */ +#define TCP_AO_GET 41 /* (Get MKTs) */ #define TCP_REPAIR_ON 1 #define TCP_REPAIR_OFF 0 @@ -364,6 +365,10 @@ struct tcp_diag_md5sig { #define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */ #define TCP_AO_CMDF_ACCEPT_ICMP (1 << 2) /* Accept incoming ICMPs */ +#define TCP_AO_GET_CURR TCP_AO_CMDF_CURR +#define TCP_AO_GET_NEXT TCP_AO_CMDF_NEXT +#define TCP_AO_GET_ALL (1 << 2) + struct tcp_ao { /* setsockopt(TCP_AO) */ struct __kernel_sockaddr_storage tcpa_addr; char tcpa_alg_name[64]; @@ -398,6 +403,21 @@ struct tcp_ao_mod { /* setsockopt(TCP_AO_MOD) */ __u8 tcpa_rnext; } __attribute__((aligned(8))); +struct tcp_ao_getsockopt { /* getsockopt(TCP_AO_GET) */ + struct __kernel_sockaddr_storage addr; + char alg_name[64]; + __u8 key[TCP_AO_MAXKEYLEN]; + __u32 nkeys; + __u16 flags; + __u8 sndid; + __u8 rcvid; + __u8 prefix; + __u8 maclen; + __u8 keyflags; + __u8 keylen; + __s32 ifindex; +} __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 d59e7422a481..7c71e4299a60 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4391,6 +4391,17 @@ int do_tcp_getsockopt(struct sock *sk, int level, err = -EFAULT; return err; } +#endif +#ifdef CONFIG_TCP_AO + case TCP_AO_GET: { + int err; + + sockopt_lock_sock(sk); + err = tcp_ao_get_mkts(sk, optval, optlen); + sockopt_release_sock(sk); + + return err; + } #endif default: return -ENOPROTOOPT; diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index d335023a1619..1cfcfab3e093 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -1505,6 +1505,8 @@ static inline bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd, (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP) #define TCP_AO_CMDF_DEL_VALID \ (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT) +#define TCP_AO_GETF_VALID \ + (TCP_AO_GET_ALL | TCP_AO_GET_CURR | TCP_AO_GET_NEXT) static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, sockptr_t optval, int optlen) @@ -1788,3 +1790,229 @@ int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen); } +/* tcp_ao_copy_mkts_to_user(ao_info, optval, optlen) + * + * @ao_info: struct tcp_ao_info on the socket that + * socket getsockopt(TCP_AO_GET) is executed on + * @optval: pointer to array of tcp_ao_getsockopt structures in user space. + * Must be != NULL. + * @optlen: pointer to size of tcp_ao_getsockopt structure. + * Must be != NULL. + * + * Return value: 0 on success, a negative error number otherwise. + * + * optval points to an array of tcp_ao_getsockopt structures in user space. + * optval[0] is used as both input and output to getsockopt. It determines + * which keys are returned by the kernel. + * optval[0].nkeys is the size of the array in user space. On return it contains + * the number of keys matching the search criteria. + * If TCP_AO_GET_ALL is set in "flags", then all keys in the socket are + * returned, otherwise only keys matching + * 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; + struct tcp_ao_getsockopt opt_out; + int user_len; + unsigned int max_keys; /* maximum number of keys to copy to user */ + u32 copied_keys; /* keys copied to user so far */ + int matched_keys; /* keys from ao_info matched so far */ + int bytes_to_write; /* number of bytes to write to user level */ + struct tcp_ao_key *key; + struct sockaddr_in *sin; /* (struct sockaddr_in *)&opt_in.addr */ + struct sockaddr_in6 *sin6; /* (struct sockaddr_in6 *)&opt_in.addr */ + struct in6_addr *addr6; /* &sin6->sin6_addr */ + __kernel_sa_family_t ss_family; + union tcp_ao_addr *addr; + size_t out_offset = 0; + int optlen_out; + u8 prefix_in; + u16 port = 0; + bool copy_all, copy_current, copy_next; + int err; + + 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.reserved != 0) + return -EINVAL; + + ss_family = opt_in.addr.ss_family; + + BUILD_BUG_ON(TCP_AO_GET_ALL & (TCP_AO_GET_CURR | TCP_AO_GET_NEXT)); + if (opt_in.flags & ~TCP_AO_GETF_VALID) + return -EINVAL; + + max_keys = opt_in.nkeys; + copy_all = !!(opt_in.flags & TCP_AO_GET_ALL); + copy_current = !!(opt_in.flags & TCP_AO_GET_CURR); + copy_next = !!(opt_in.flags & TCP_AO_GET_NEXT); + + if (!(copy_all || copy_current || copy_next)) { + prefix_in = opt_in.prefix; + + switch (ss_family) { + case AF_INET: { + sin = (struct sockaddr_in *)&opt_in.addr; + port = sin->sin_port; + addr = (union tcp_ao_addr *)&sin->sin_addr; + + if (prefix_in > 32) + return -EINVAL; + + if (sin->sin_addr.s_addr == INADDR_ANY && + prefix_in != 0) + return -EINVAL; + + break; + } + case AF_INET6: { + sin6 = (struct sockaddr_in6 *)&opt_in.addr; + addr = (union tcp_ao_addr *)&sin6->sin6_addr; + addr6 = &sin6->sin6_addr; + port = sin6->sin6_port; + + if (prefix_in != 0) { + if (ipv6_addr_v4mapped(addr6)) { + __be32 addr4 = addr6->s6_addr32[3]; + + if (prefix_in > 32 || + addr4 == INADDR_ANY) + return -EINVAL; + } else { + if (ipv6_addr_any(addr6) || + prefix_in > 128) + return -EINVAL; + } + } else if (!ipv6_addr_any(addr6)) { + return -EINVAL; + } + + break; + } + default: + return -EAFNOSUPPORT; + } + } + + bytes_to_write = min(user_len, (int)sizeof(struct tcp_ao_getsockopt)); + copied_keys = 0; + matched_keys = 0; + + hlist_for_each_entry_rcu(key, &ao_info->head, node) { + if (copy_all) + goto match; + + if (copy_current || copy_next) { + if (copy_current && key == ao_info->current_key) + goto match; + if (copy_next && key == ao_info->rnext_key) + goto match; + continue; + } + + if (tcp_ao_key_cmp(key, addr, opt_in.prefix, + opt_in.addr.ss_family, + opt_in.sndid, opt_in.rcvid, port) != 0) + continue; +match: + matched_keys++; + if (copied_keys >= max_keys) + continue; + + memset(&opt_out, 0, sizeof(struct tcp_ao_getsockopt)); + + if (key->family == AF_INET) { + struct sockaddr_in *sin_out = (struct sockaddr_in *)&opt_out.addr; + + sin_out->sin_family = key->family; + sin_out->sin_port = ntohs(key->port); + memcpy(&sin_out->sin_addr, &key->addr, sizeof(struct in_addr)); + } else { + struct sockaddr_in6 *sin6_out = (struct sockaddr_in6 *)&opt_out.addr; + + sin6_out->sin6_family = key->family; + sin6_out->sin6_port = ntohs(key->port); + memcpy(&sin6_out->sin6_addr, &key->addr, sizeof(struct in6_addr)); + } + opt_out.sndid = key->sndid; + opt_out.rcvid = key->rcvid; + opt_out.prefix = key->prefixlen; + opt_out.keyflags = key->keyflags; + opt_out.flags = 0; + if (key == ao_info->current_key) + opt_out.flags |= TCP_AO_GET_CURR; + if (key == ao_info->rnext_key) + opt_out.flags |= TCP_AO_GET_NEXT; + opt_out.nkeys = 0; + opt_out.maclen = key->maclen; + opt_out.keylen = key->keylen; + memcpy(&opt_out.key, key->key, key->keylen); + 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; + copied_keys++; + } + + 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; + u32 state; + + /* Check socket state */ + state = (1 << sk->sk_state) & + (TCPF_CLOSE | TCPF_ESTABLISHED | TCPF_LISTEN); + if (!state) + return -ESOCKTNOSUPPORT; + + /* Check ao_info */ + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (!ao_info) + return -ENOENT; + + return tcp_ao_copy_mkts_to_user(ao_info, optval, optlen); +} + From patchwork Wed Feb 15 18:33:33 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142058 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B42C6C636CC for ; Wed, 15 Feb 2023 18:36:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229934AbjBOSga (ORCPT ); Wed, 15 Feb 2023 13:36:30 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49902 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229963AbjBOSfm (ORCPT ); Wed, 15 Feb 2023 13:35:42 -0500 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 30B343E092 for ; Wed, 15 Feb 2023 10:34:18 -0800 (PST) Received: by mail-wm1-x32a.google.com with SMTP id bg5-20020a05600c3c8500b003e00c739ce4so2234478wmb.5 for ; Wed, 15 Feb 2023 10:34:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=swOWZLppMAkB1vbkTrWz71rxybXGJU2PFiJRKq0r0PU=; b=SlVGSwrkML4qpFCV2r1q0ZqErXrU/F33IAeYNRzTesWGsIKNe4pOL3xVcvN7yi3dBM vibGga9/80xUpJM9xHobRytqn1RaD8hpHJd8XPHcK6RcSooHb5W4309ck3PW2cL+djnw 2aIE8xA207ZK7ASTNqY+qZVfIVfBuzaQqRvmuBOz8HNYMrWKzjZHEWphgPi2OHcstSkF RysWRpQg0HG0ZaVTgeaz07s30T2YLI8p+Es1yO7nJZ5IO0pZr9gQVCsOQyRlNflYg6Fi 2W0Rbu6EfyXau7e9lPPYEZG1cNmMfWv0V4SRQ0jycFWSBzNgc7cZwXAhqxB8YgnywsRE QSaw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=swOWZLppMAkB1vbkTrWz71rxybXGJU2PFiJRKq0r0PU=; b=tJRcML9JE2/2RDMJvzcUB1JrtMutKHpMqsci2ncXt53XXMX46ywebDFZerQbJJEllj etLggneaBMPUsNUrdtTfU5LiqeDKfX+0XoJGA22s8sHLlz5xDNObQ1h7G5QHp4CGmv+Y 9dvwEODHziZgctnU/bCLVYgLw8Q1JyikJ4dpNEgkwEQBxGs9r4+sM5CB6dmq//APtXaS NX4t14shsw4tLGrc5RT+XWL7GNQv7q2U0aVVD/ofMDfATlQqcEg6Wqm4WsYSwKR+MeJw 8raS7NxISnW8fgqjl4ujJTqErU00ceQTOAU6G+BHHJsNPrxgMaxjur7vTXlzO1pMhrFV b37A== X-Gm-Message-State: AO0yUKUBQx7ww56/yile+8QASoy4GgENx8joXRz4JyL8VpLQ2FC90M02 n1iqbJG45K4zacHbyWUtQeLurQ== X-Google-Smtp-Source: AK7set9u4z2Tb9xERQO56PxCArzhKhCdNkagSkOGtIYkjumJJyrV2TRQTtvBPUXvt3IkxZpdjDBewg== X-Received: by 2002:a05:600c:30ca:b0:3df:12ac:7cc9 with SMTP id h10-20020a05600c30ca00b003df12ac7cc9mr2700156wmn.15.1676486057758; Wed, 15 Feb 2023 10:34:17 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:17 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org, Francesco Ruggeri Subject: [PATCH v4 19/21] net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs) Date: Wed, 15 Feb 2023 18:33:33 +0000 Message-Id: <20230215183335.800122-20-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index eaf77c0a4425..0c0caf810d6b 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -364,6 +364,9 @@ struct tcp_diag_md5sig { #define TCP_AO_CMDF_CURR (1 << 0) /* Only checks field sndid */ #define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */ #define TCP_AO_CMDF_ACCEPT_ICMP (1 << 2) /* Accept incoming ICMPs */ +#define TCP_AO_CMDF_DEL_ASYNC (1 << 3) /* Asynchronious delete, valid + * only for listen sockets + */ #define TCP_AO_GET_CURR TCP_AO_CMDF_CURR #define TCP_AO_GET_NEXT TCP_AO_CMDF_NEXT diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 1cfcfab3e093..2c38e991ecbd 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -1504,7 +1504,7 @@ static inline bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd, #define TCP_AO_CMDF_ADDMOD_VALID \ (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP) #define TCP_AO_CMDF_DEL_VALID \ - (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT) + (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_DEL_ASYNC) #define TCP_AO_GETF_VALID \ (TCP_AO_GET_ALL | TCP_AO_GET_CURR | TCP_AO_GET_NEXT) @@ -1633,11 +1633,26 @@ static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_key *key, hlist_del_rcu(&key->node); + /* Support for async delete on listening sockets: as they don't + * need current_key/rnext_key maintaining, we don't need to check + * them and we can just free all resources in RCU fashion. + */ + if (cmd->tcpa_flags & TCP_AO_CMDF_DEL_ASYNC) { + if (sk->sk_state != TCP_LISTEN) + return -EINVAL; + atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); + call_rcu(&key->rcu, tcp_ao_key_free_rcu); + return 0; + } + /* At this moment another CPU could have looked this key up * while it was unlinked from the list. Wait for RCU grace period, * after which the key is off-list and can't be looked up again; * the rx path [just before RCU came] might have used it and set it * as current_key (very unlikely). + * Free the key with next RCU grace period (in case it was + * current_key before tcp_ao_current_rnext() might have + * changed it in forced-delete). */ synchronize_rcu(); err = tcp_ao_current_rnext(sk, cmd->tcpa_flags, From patchwork Wed Feb 15 18:33:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142059 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5795EC636D4 for ; Wed, 15 Feb 2023 18:36:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230282AbjBOSgc (ORCPT ); Wed, 15 Feb 2023 13:36:32 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49796 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230345AbjBOSfn (ORCPT ); Wed, 15 Feb 2023 13:35:43 -0500 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 1A9793E09E for ; Wed, 15 Feb 2023 10:34:20 -0800 (PST) Received: by mail-wm1-x32e.google.com with SMTP id z13so14022400wmp.2 for ; Wed, 15 Feb 2023 10:34:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=+1FPQF1rXG4A/jQd/fcwDAUZHBiYXUCKbXLzG2fc0O0=; b=Et7AtUUbRO/Xw7WCuAirQIo9xegKW/u1xg1kzH6wy61oO+XbTefzV3XE5gahI+FJa0 w9ylBEopCWZwXrnML1c0eD3oEwIYTxq6vM5gM8N21hHWhyuCUmvAiC2JHZB9tr20sDgm 9vONNF8OaMv3eTyTp1Bk4PeUwzPBGraCjCzOurVCojhpptwW5hwCEej1ofP2g+00Dns4 sevoLJZSCCRzlGSg6M60JGihs8ek5MXcs4qdp42IXBI7fQbmh0F8QhB1n4q4JBBU0sEm 9DVPoN20n7gTaBdqhEUyunZHmpN7kCbeRL111YjBghHwJQQljipU4w6837yRXQHEo054 TpHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=+1FPQF1rXG4A/jQd/fcwDAUZHBiYXUCKbXLzG2fc0O0=; b=zPGeHIy50u7ySEEyXw1jpDEGXSoSb04DDLITClbkdxphfAmYTDc6HODrg6keUK1f+V e/ZBR/0ZqStUFA5HPVQlkyeBkJ3JYFEmGNgk43BLHJaeFLDx5tRSw3RGqF4PB6/SApfZ OwcFGPind4Wy1szdojBksnO1l89ViqIMRuYpy1yaunrQG9u5kO7KbHl+IyfmjeskhahV KP2d0Lboe8IwMcjmQu9S+0A/9IeDGS8zBojmiLs6LLt/SWWQvUWb5zlhlddM9EfBZfTW JPxfxMNJR6XaR4o77B/JyzcSI5xK/Vvn9pp1ypU7of0cix92T9LtCJqtdkPAd8/ic8+X 1cLg== X-Gm-Message-State: AO0yUKUIKULWqU7TxJl9cVzGKbww2rFJ3CCxDFVwBtiZtvTaEcMvE1AT yHdBbKTTDTLPHy9IY7Jh0cloWQ== X-Google-Smtp-Source: AK7set93rTIM8qwwjULLBamnBHxDqE2tF5W14lVEubw1JuOeFBC7NA3ID38FUydivAcdzfZAL/Dlyw== X-Received: by 2002:a05:600c:5024:b0:3df:fcbd:3159 with SMTP id n36-20020a05600c502400b003dffcbd3159mr2597578wmr.3.1676486059627; Wed, 15 Feb 2023 10:34:19 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:18 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org Subject: [PATCH v4 20/21] net/tcp-ao: Add static_key for TCP-AO Date: Wed, 15 Feb 2023 18:33:34 +0000 Message-Id: <20230215183335.800122-21-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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_ao.h | 2 ++ net/ipv4/tcp_ao.c | 17 +++++++++++++++++ net/ipv4/tcp_input.c | 42 ++++++++++++++++++++++++++++-------------- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 04e3bcee05f7..253cf2719aed 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -131,6 +131,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 2c38e991ecbd..adb25e42f64a 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -17,6 +17,9 @@ #include #include +DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_ao_needed, HZ); +EXPORT_SYMBOL(tcp_ao_needed); + int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, unsigned int len) { @@ -58,6 +61,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 @@ -196,6 +202,9 @@ 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) @@ -283,6 +292,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) @@ -1052,6 +1062,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); @@ -1607,6 +1622,8 @@ 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)) + goto err_free_sock; 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 9f23cab1e835..dd9ff507bbc9 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3524,17 +3524,14 @@ 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) { @@ -3543,20 +3540,27 @@ static void tcp_snd_una_update(struct tcp_sock *tp, u32 ack) ao->snd_sne_seq = ack; } #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; } -/* If we update tp->rcv_nxt, also update tp->bytes_received */ -static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq) +static void tcp_rcv_sne_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 + 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) { @@ -3565,6 +3569,16 @@ static void tcp_rcv_nxt_update(struct tcp_sock *tp, u32 seq) ao->rcv_sne_seq = seq; } #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; + + sock_owned_by_me((struct sock *)tp); + tp->bytes_received += delta; + tcp_rcv_sne_update(tp, seq); WRITE_ONCE(tp->rcv_nxt, seq); } From patchwork Wed Feb 15 18:33:35 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13142061 X-Patchwork-Delegate: kuba@kernel.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DCB14C636D7 for ; Wed, 15 Feb 2023 18:36:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229903AbjBOSgt (ORCPT ); Wed, 15 Feb 2023 13:36:49 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49164 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229680AbjBOSf7 (ORCPT ); Wed, 15 Feb 2023 13:35:59 -0500 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 B3D573CE36 for ; Wed, 15 Feb 2023 10:34:21 -0800 (PST) Received: by mail-wm1-x332.google.com with SMTP id z13so14022463wmp.2 for ; Wed, 15 Feb 2023 10:34:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; 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=56glGZIk8DVndoPnOZ1LZxON4qiQryUzSa5rkzkLnEg=; b=huUU9RDP0P7/VshnMi4S5ttTvf/I/uZ4K7cuNgNSvhPw2NrPpJZre4lUkHyJ9yKCcP uEy4NGXG65aUXB3RkWbliA/hp1IelN3EMwJPpVluEr770+UOMZNX9eStUu+xj+R2ZUZv vEmSOQANluxMs+nIDzQF62zRp1/iRYk8gVTf5gNA+a5TCcV/xBQzIqoOhTTLjKZ1Kno4 Sr6nYKbJORzqkgAMwzk+G2ULXpuP467gCUUoZaHawBxrkmhnZ6/8ZGnop6yRNFqTbhsJ hNl0mrgiL6MBdcWJuT3NvZEeZYWIiU3Y4l1KN4xEDM4tj49XPVf/0GjZmLdDdysDAURn +muw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=56glGZIk8DVndoPnOZ1LZxON4qiQryUzSa5rkzkLnEg=; b=d/4OyMOdOGYr2cWVJb1M6EzKstF0ZIJgMyZwfmedAMAYHohTJQH+OyLa1PBruviz6t p6y2J1mZuitOkwbrbP/mWqFeNKYrWSH6wLvpCCGzFJz9g8oKW2YdKdz3GTifpCePjZR0 xL37jGu/83qbP361vfI0FH2UgDAJHXwAgCR7tofiXeXb+ZQD5AvLzEheOyf8zp8FDj8r a+IzDcjVcFs5U/PYTjyIGPp8X3leEYN49gnnWRSl8LJApYgonOV2bpzcH+AKjdmNH5mZ bUi1MJKjwagzyEC0oFWmrM2Ro+4zpa8/eBhSqcJkUynHZ4Jq/z0ch4sH3KitLbHQ2UlD jcNg== X-Gm-Message-State: AO0yUKUaTjoO9q2kHSoj8EYpd3NO0AJ04nqAgETSN6x2t9n8PFiuuZd/ 7u06qMWudjajeARbulqHaABcQQ== X-Google-Smtp-Source: AK7set/VplH0NpwRvnAxScYqRmef+4VYSIg31Kn173o5XBfy2gj0x5nEYNsrIwqSZmi6Zc/VKlYuyg== X-Received: by 2002:a05:600c:244e:b0:3df:57aa:db52 with SMTP id 14-20020a05600c244e00b003df57aadb52mr2919390wmr.4.1676486061170; Wed, 15 Feb 2023 10:34:21 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:20 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org Subject: [PATCH v4 21/21] net/tcp-ao: Wire up l3index to TCP-AO Date: Wed, 15 Feb 2023 18:33:35 +0000 Message-Id: <20230215183335.800122-22-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org X-Patchwork-Delegate: kuba@kernel.org 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 tcpa_ifinndex L3 interface. 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 | 10 +-- include/net/tcp_ao.h | 17 +++-- net/ipv4/syncookies.c | 6 +- net/ipv4/tcp_ao.c | 162 +++++++++++++++++++++++++++++++----------- net/ipv4/tcp_ipv4.c | 18 +++-- net/ipv6/syncookies.c | 5 +- net/ipv6/tcp_ao.c | 20 +++--- net/ipv6/tcp_ipv6.c | 24 +++++-- 8 files changed, 181 insertions(+), 81 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 7b1d8b05e3bc..1576c5a1f154 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2607,7 +2607,8 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req, * always at least one current_key. */ #ifdef CONFIG_TCP_AO - if (unlikely(tcp_ao_do_lookup(sk, saddr, family, -1, -1, 0))) { + if (unlikely(tcp_ao_do_lookup(sk, l3index, saddr, + family, -1, -1, 0))) { struct tcp_ao_info *ao_info; ao_info = rcu_dereference_check(tcp_sk(sk)->ao_info, @@ -2615,20 +2616,21 @@ tcp_inbound_hash(struct sock *sk, const struct request_sock *req, NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOREQUIRED); atomic64_inc(&ao_info->counters.ao_required); tcp_hash_fail("AO hash is required, but not found", - family, skb, ""); + family, skb, "L3 index %d", l3index); return SKB_DROP_REASON_TCP_AONOTFOUND; } #endif if (unlikely(tcp_md5_do_lookup(sk, l3index, saddr, family))) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); - tcp_hash_fail("MD5 Hash not found", family, skb, ""); + 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 253cf2719aed..b783af572c02 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -33,6 +33,7 @@ struct tcp_ao_key { union tcp_ao_addr addr; u8 key[TCP_AO_MAXKEYLEN] __tcp_ao_key_align; unsigned int tcp_sigpool_id; + int l3index; u16 port; u8 prefixlen; u8 family; @@ -177,9 +178,9 @@ 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); 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); -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, @@ -224,9 +225,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); @@ -245,12 +243,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) { } @@ -261,13 +259,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 9e3d7083af7d..a994a3405a67 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 adb25e42f64a..b58aad7a47a7 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -146,7 +146,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) { @@ -156,6 +156,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; @@ -180,7 +184,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) { @@ -188,14 +192,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); } -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) { @@ -211,7 +217,7 @@ struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, return NULL; hlist_for_each_entry_rcu(key, &ao->head, node) { - if (!tcp_ao_key_cmp(key, addr, key->prefixlen, + if (!tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, family, sndid, rcvid, port)) return key; } @@ -690,41 +696,46 @@ 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); } 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); } } /* Returns maclen of requested key if found */ 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; @@ -739,7 +750,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; @@ -753,7 +764,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) { unsigned char newhash[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr); @@ -764,7 +775,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; } @@ -775,7 +787,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); return SKB_DROP_REASON_TCP_AOFAILURE; } NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD); @@ -787,7 +800,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) { u8 key_buf[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; const struct tcphdr *th = tcp_hdr(skb); @@ -802,7 +815,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; } @@ -836,7 +849,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; /* Key rotation: the peer asks us to use new key (RNext) */ @@ -856,7 +869,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; @@ -894,13 +907,13 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, traffic_key = key_buf; tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family); return tcp_ao_verify_hash(sk, skb, family, info, aoh, key, - traffic_key, phash, sne); + traffic_key, phash, sne, l3index); 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, ""); + family, skb, " L3index: %d", l3index); return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; } @@ -927,7 +940,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)); @@ -944,9 +957,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; @@ -1010,7 +1025,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) @@ -1036,9 +1051,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; @@ -1352,6 +1369,11 @@ static bool tcp_ao_mkt_overlap_v4(struct tcp_ao *cmd, if (key->sndid != sndid && key->rcvid != rcvid) continue; + if (key->keyflags & TCP_AO_KEYF_IFINDEX && + cmd->tcpa_keyflags & TCP_AO_KEYF_IFINDEX && + key->l3index != cmd->tcpa_ifindex) + continue; + key_addr = key->addr.a4.s_addr; mask = inet_make_mask(min(prefix, key->prefixlen)); @@ -1462,6 +1484,11 @@ static bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd, if (key->sndid != sndid && key->rcvid != rcvid) continue; + if (key->keyflags & TCP_AO_KEYF_IFINDEX && + cmd->tcpa_keyflags & TCP_AO_KEYF_IFINDEX && + key->l3index != cmd->tcpa_ifindex) + continue; + key_addr = &key->addr.a6; if (v4_mapped) { @@ -1515,23 +1542,24 @@ static inline bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd, } #endif -#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_CMDF_ADDMOD_VALID \ (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP) #define TCP_AO_CMDF_DEL_VALID \ (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_DEL_ASYNC) #define TCP_AO_GETF_VALID \ (TCP_AO_GET_ALL | TCP_AO_GET_CURR | TCP_AO_GET_NEXT) +#define TCP_AO_GET_KEYF_VALID (TCP_AO_KEYF_IFINDEX) static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, sockptr_t optval, int optlen) { struct tcp_ao_info *ao_info; + int ret, size, l3index = 0; union tcp_md5_addr *addr; struct tcp_ao_key *key; bool first = false; struct tcp_ao cmd; - int ret, size; u16 port; if (optlen < sizeof(cmd)) @@ -1560,9 +1588,33 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, if (cmd.tcpa_keyflags & ~TCP_AO_KEYF_ALL) return -EINVAL; + /* For cmd.tcp_ifindex = 0 the key will apply to the default VRF */ + if (cmd.tcpa_keyflags & TCP_AO_KEYF_IFINDEX && cmd.tcpa_ifindex) { + struct net_device *dev; + + rcu_read_lock(); + dev = dev_get_by_index_rcu(sock_net(sk), cmd.tcpa_ifindex); + if (dev && netif_is_l3_master(dev)) + l3index = dev->ifindex; + rcu_read_unlock(); + + if (!dev || !l3index) + 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; + if (cmd.tcpa_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 = rcu_dereference_protected(tcp_sk(sk)->ao_info, lockdep_sock_is_held(sk)); @@ -1602,6 +1654,7 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, key->keyflags = cmd.tcpa_keyflags; key->sndid = cmd.tcpa_sndid; key->rcvid = cmd.tcpa_rcvid; + key->l3index = l3index; atomic64_set(&key->pkt_good, 0); atomic64_set(&key->pkt_bad, 0); @@ -1692,17 +1745,17 @@ static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_key *key, 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) { + int err, addr_len, l3index = 0; struct tcp_ao_info *ao_info; struct tcp_ao_key *key; struct tcp_ao_del cmd; - int err; union tcp_md5_addr *addr; __u8 prefix; __be16 port; - int addr_len; if (optlen < sizeof(cmd)) return -EINVAL; @@ -1714,9 +1767,26 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, if (cmd.tcpa_flags & ~TCP_AO_CMDF_DEL_VALID) return -EINVAL; + if (cmd.tcpa_keyflags & ~TCP_AO_DEL_KEYF_ALL) + return -EINVAL; + if (cmd.reserved != 0) return -EINVAL; + /* For cmd.tcp_ifindex = 0 the key will apply to the default VRF */ + if (cmd.tcpa_keyflags & TCP_AO_KEYF_IFINDEX && cmd.tcpa_ifindex) { + struct net_device *dev; + + rcu_read_lock(); + dev = dev_get_by_index_rcu(sock_net(sk), cmd.tcpa_ifindex); + if (dev && netif_is_l3_master(dev)) + l3index = dev->ifindex; + rcu_read_unlock(); + + if (!dev || !l3index) + return -EINVAL; + } + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, lockdep_sock_is_held(sk)); if (!ao_info) @@ -1762,6 +1832,13 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, memcmp(addr, &key->addr, addr_len)) continue; + if ((cmd.tcpa_keyflags & TCP_AO_KEYF_IFINDEX) != + (key->keyflags & TCP_AO_KEYF_IFINDEX)) + continue; + + if (key->l3index != l3index) + continue; + return tcp_ao_delete_key(sk, key, ao_info, &cmd); } return -ENOENT; @@ -1878,7 +1955,7 @@ static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, u8 prefix_in; u16 port = 0; bool copy_all, copy_current, copy_next; - int err; + int err, l3index; if (copy_from_sockptr(&user_len, optlen, sizeof(int))) return -EFAULT; @@ -1892,19 +1969,21 @@ static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, if (err < 0) return err; - if (opt_in.reserved != 0) - return -EINVAL; - ss_family = opt_in.addr.ss_family; BUILD_BUG_ON(TCP_AO_GET_ALL & (TCP_AO_GET_CURR | TCP_AO_GET_NEXT)); if (opt_in.flags & ~TCP_AO_GETF_VALID) return -EINVAL; + if (opt_in.keyflags & ~TCP_AO_GET_KEYF_VALID) + return -EINVAL; + if (opt_in.ifindex < 0) + return -EINVAL; max_keys = opt_in.nkeys; copy_all = !!(opt_in.flags & TCP_AO_GET_ALL); copy_current = !!(opt_in.flags & TCP_AO_GET_CURR); copy_next = !!(opt_in.flags & TCP_AO_GET_NEXT); + l3index = (opt_in.keyflags & TCP_AO_KEYF_IFINDEX) ? opt_in.ifindex : -1; if (!(copy_all || copy_current || copy_next)) { prefix_in = opt_in.prefix; @@ -1969,7 +2048,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; @@ -2005,6 +2084,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; memcpy(&opt_out.key, key->key, key->keylen); tcp_sigpool_algo(key->tcp_sigpool_id, opt_out.alg_name, 64); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 64a81f1dd4d4..c1dcfc70930a 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1093,6 +1093,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; #ifdef CONFIG_TCP_AO u8 traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; const struct tcp_ao_hdr *aoh; @@ -1106,6 +1107,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 @@ -1116,11 +1118,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. @@ -1139,9 +1141,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 @@ -1497,6 +1496,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname, struct tcp_md5sig cmd; struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpm_addr; const union tcp_md5_addr *addr; + bool l3flag = false; u8 prefixlen = 32; int l3index = 0; u8 flags; @@ -1523,6 +1523,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname, cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX) { struct net_device *dev; + l3flag = true; rcu_read_lock(); dev = dev_get_by_index_rcu(sock_net(sk), cmd.tcpm_ifindex); if (dev && netif_is_l3_master(dev)) @@ -1545,8 +1546,11 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname, if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) return -EINVAL; - /* Don't allow keys for peers that have a matching TCP-AO key */ - if (tcp_ao_do_lookup(sk, addr, AF_INET, -1, -1, 0)) + /* Don't allow keys for peers that have a matching TCP-AO key. + * See the comment in tcp_ao_add_cmd() + */ + if (tcp_ao_do_lookup(sk, l3flag ? l3index : -1, addr, + AF_INET, -1, -1, 0)) 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 77dfa706349c..ac24721b6b76 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -77,30 +77,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 5c5c8509a6e2..b71bb2cde63b 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, struct tcp_md5sig cmd; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr; union tcp_ao_addr *addr; + bool l3flag = false; int l3index = 0; u8 prefixlen; u8 flags; @@ -635,6 +636,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX) { struct net_device *dev; + l3flag = true; rcu_read_lock(); dev = dev_get_by_index_rcu(sock_net(sk), cmd.tcpm_ifindex); if (dev && netif_is_l3_master(dev)) @@ -663,8 +665,11 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, if (ipv6_addr_v4mapped(&sin6->sin6_addr)) { addr = (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3]; - /* Don't allow keys for peers that have a matching TCP-AO key */ - if (tcp_ao_do_lookup(sk, addr, AF_INET, -1, -1, 0)) + /* Don't allow keys for peers that have a matching TCP-AO key. + * See the comment in tcp_ao_add_cmd() + */ + if (tcp_ao_do_lookup(sk, l3flag ? l3index : -1, addr, + AF_INET, -1, -1, 0)) return -EKEYREJECTED; return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, @@ -673,8 +678,11 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, addr = (union tcp_md5_addr *)&sin6->sin6_addr; - /* Don't allow keys for peers that have a matching TCP-AO key */ - if (tcp_ao_do_lookup(sk, addr, AF_INET6, -1, -1, 0)) + /* Don't allow keys for peers that have a matching TCP-AO key. + * See the comment in tcp_ao_add_cmd() + */ + if (tcp_ao_do_lookup(sk, l3flag ? l3index : -1, addr, + AF_INET6, -1, -1, 0)) return -EKEYREJECTED; return tcp_md5_do_add(sk, addr, AF_INET6, prefixlen, l3index, flags, @@ -1260,10 +1268,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. */