From patchwork Thu Oct 27 20:43:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022720 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 72A9FFA3740 for ; Thu, 27 Oct 2022 20:44:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236658AbiJ0UoB (ORCPT ); Thu, 27 Oct 2022 16:44:01 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58866 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236587AbiJ0Un7 (ORCPT ); Thu, 27 Oct 2022 16:43:59 -0400 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 F05E98A1E8 for ; Thu, 27 Oct 2022 13:43:57 -0700 (PDT) Received: by mail-wm1-x32d.google.com with SMTP id r186-20020a1c44c3000000b003cf4d389c41so2258103wma.3 for ; Thu, 27 Oct 2022 13:43:57 -0700 (PDT) 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=jeig38pg5Ad0EZXlXTqJ0MQi46fD4pFQ/0gtedu7p7U=; b=iY0WT0yOQG5DF6L5OZsnXqDvQDr2c4TmeMPGblV3v0pCxfvcJ69svNDqyKvzbkKuB7 crdh9qJ/rRVmhPYyChTmEcEK9MG/j2TMdWWc2LMNwk0J+rIWCRLhoQA1eA1bc5Au7N19 vfHjl5mueyO3j28RGIqCAP1ln61YYzzVunN/zJtJxYv22vQhQK4MSR0bysAzjR261RWU 1b5X29khPb+sYPIfAjLpfCbdJRrjg0kYF06h98HcSsGYVVaUi+nQe8N/Yg6VYHeR8aTt MsiOFiz8ocNLbvlqVYZl6oMnkFlxiQkTIVplSBuVNOMxYzQsQp0Gj9LC6VrgCI9Wlfa/ AAKg== 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=jeig38pg5Ad0EZXlXTqJ0MQi46fD4pFQ/0gtedu7p7U=; b=aqS4Eq0GrzDHva7DKRZqkdo6Hc3DMVjjUwcc1b4sRG5U5yvkJlPl09fOfqm3h7IduU bA83fugjvA4JfeZcmf85m+ttnHDHpo6vaXEWrzzvBwUOt6JHumB9ib8TK4XShMJGSZTb 7fjT/hf5wrrCf21725qPeZdNms7cRL7F4gmuccsVZ5cURE+95itXyEHrYJnGzac0Zkxp NryxgliHMvISnX/zufjJjr9qJXRW2pkCEKfolYv430n6QNaLiTDPxjngcUwIFhM50xe7 K0eBpqNyX4ZOTKoYpC/CeGp995kpql4B2m3SZN5FT0qxK1HP7hPzqx3poSQxQzuyv/94 8Uww== X-Gm-Message-State: ACrzQf2lV1ZXbaMtjgIogyUziBYLQsfRIhOYqtkcU4FAgUsWl+YhWCUY KrB2rLozHVwcsSsXzEXkMNRs7w== X-Google-Smtp-Source: AMsMyM78wkj1+k6GtkcTY7U46z9d8vrFBk8i5JSnvLoC/FMMeXlwDUoRc6QZ2OEPu6GLeKznnVJSGg== X-Received: by 2002:a05:600c:4e52:b0:3ce:3f91:70b9 with SMTP id e18-20020a05600c4e5200b003ce3f9170b9mr6943063wmq.131.1666903436405; Thu, 27 Oct 2022 13:43:56 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.43.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:43:56 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 01/36] crypto: Introduce crypto_pool Date: Thu, 27 Oct 2022 21:43:12 +0100 Message-Id: <20221027204347.529913-2-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Introduce a per-CPU pool of async crypto requests that can be used in bh-disabled contexts (designed with net RX/TX softirqs as users in mind). Allocation can sleep and is a slow-path. Initial implementation has only ahash as a backend and a fix-sized array of possible algorithms used in parallel. Signed-off-by: Dmitry Safonov --- crypto/Kconfig | 6 + crypto/Makefile | 1 + crypto/crypto_pool.c | 291 ++++++++++++++++++++++++++++++++++++++++++ include/crypto/pool.h | 34 +++++ 4 files changed, 332 insertions(+) create mode 100644 crypto/crypto_pool.c create mode 100644 include/crypto/pool.h diff --git a/crypto/Kconfig b/crypto/Kconfig index d779667671b2..768d331e626b 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1391,6 +1391,12 @@ endmenu config CRYPTO_HASH_INFO bool +config CRYPTO_POOL + tristate "Per-CPU crypto pool" + default n + help + Per-CPU pool of crypto requests ready for usage in atomic contexts. + if !KMSAN # avoid false positives from assembly if ARM source "arch/arm/crypto/Kconfig" diff --git a/crypto/Makefile b/crypto/Makefile index 303b21c43df0..5abc033538ea 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_CRYPTO_ACOMP2) += crypto_acompress.o cryptomgr-y := algboss.o testmgr.o obj-$(CONFIG_CRYPTO_MANAGER2) += cryptomgr.o +obj-$(CONFIG_CRYPTO_POOL) += crypto_pool.o obj-$(CONFIG_CRYPTO_USER) += crypto_user.o crypto_user-y := crypto_user_base.o crypto_user-$(CONFIG_CRYPTO_STATS) += crypto_user_stat.o diff --git a/crypto/crypto_pool.c b/crypto/crypto_pool.c new file mode 100644 index 000000000000..37131952c5a7 --- /dev/null +++ b/crypto/crypto_pool.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include + +static unsigned long scratch_size = DEFAULT_CRYPTO_POOL_SCRATCH_SZ; +static DEFINE_PER_CPU(void *, crypto_pool_scratch); + +struct crypto_pool_entry { + struct ahash_request * __percpu *req; + const char *alg; + struct kref kref; + bool needs_key; +}; + +#define CPOOL_SIZE (PAGE_SIZE/sizeof(struct crypto_pool_entry)) +static struct crypto_pool_entry cpool[CPOOL_SIZE]; +static unsigned int cpool_populated; +static DEFINE_MUTEX(cpool_mutex); + +static int crypto_pool_scratch_alloc(void) +{ + int cpu; + + lockdep_assert_held(&cpool_mutex); + + for_each_possible_cpu(cpu) { + void *scratch = per_cpu(crypto_pool_scratch, cpu); + + if (scratch) + continue; + + scratch = kmalloc_node(scratch_size, GFP_KERNEL, + cpu_to_node(cpu)); + if (!scratch) + return -ENOMEM; + per_cpu(crypto_pool_scratch, cpu) = scratch; + } + return 0; +} + +static void crypto_pool_scratch_free(void) +{ + int cpu; + + lockdep_assert_held(&cpool_mutex); + + for_each_possible_cpu(cpu) { + void *scratch = per_cpu(crypto_pool_scratch, cpu); + + if (!scratch) + continue; + per_cpu(crypto_pool_scratch, cpu) = NULL; + kfree(scratch); + } +} + +static int __cpool_alloc_ahash(struct crypto_pool_entry *e, const char *alg) +{ + struct crypto_ahash *hash; + int cpu, ret = -ENOMEM; + + e->alg = kstrdup(alg, GFP_KERNEL); + if (!e->alg) + return -ENOMEM; + + e->req = alloc_percpu(struct ahash_request *); + if (!e->req) + goto out_free_alg; + + hash = crypto_alloc_ahash(alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(hash)) { + ret = PTR_ERR(hash); + goto out_free_req; + } + + /* If hash has .setkey(), allocate ahash per-cpu, not only request */ + e->needs_key = crypto_ahash_get_flags(hash) & CRYPTO_TFM_NEED_KEY; + + for_each_possible_cpu(cpu) { + struct ahash_request *req; + + if (!hash) + hash = crypto_alloc_ahash(alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(hash)) + goto out_free; + + req = ahash_request_alloc(hash, GFP_KERNEL); + if (!req) + goto out_free; + + ahash_request_set_callback(req, 0, NULL, NULL); + + *per_cpu_ptr(e->req, cpu) = req; + + if (e->needs_key) + hash = NULL; + } + kref_init(&e->kref); + return 0; + +out_free: + if (!IS_ERR_OR_NULL(hash) && e->needs_key) + crypto_free_ahash(hash); + + for_each_possible_cpu(cpu) { + if (*per_cpu_ptr(e->req, cpu) == NULL) + break; + hash = crypto_ahash_reqtfm(*per_cpu_ptr(e->req, cpu)); + ahash_request_free(*per_cpu_ptr(e->req, cpu)); + if (e->needs_key) { + crypto_free_ahash(hash); + hash = NULL; + } + } + + if (hash) + crypto_free_ahash(hash); +out_free_req: + free_percpu(e->req); +out_free_alg: + kfree(e->alg); + e->alg = NULL; + return ret; +} + +/** + * crypto_pool_alloc_ahash - allocates pool for ahash requests + * @alg: name of async hash algorithm + */ +int crypto_pool_alloc_ahash(const char *alg) +{ + int i, ret; + + /* slow-path */ + mutex_lock(&cpool_mutex); + + 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); + ret = i; + goto out; + } else { + break; + } + } + } + + 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(crypto_pool_alloc_ahash); + +static void __cpool_free_entry(struct crypto_pool_entry *e) +{ + struct crypto_ahash *hash = NULL; + int cpu; + + for_each_possible_cpu(cpu) { + if (*per_cpu_ptr(e->req, cpu) == NULL) + continue; + + hash = crypto_ahash_reqtfm(*per_cpu_ptr(e->req, cpu)); + ahash_request_free(*per_cpu_ptr(e->req, cpu)); + if (e->needs_key) { + crypto_free_ahash(hash); + hash = NULL; + } + } + if (hash) + crypto_free_ahash(hash); + free_percpu(e->req); + kfree(e->alg); + memset(e, 0, sizeof(*e)); +} + +static void cpool_cleanup_work_cb(struct work_struct *work) +{ + unsigned int i; + bool free_scratch = true; + + mutex_lock(&cpool_mutex); + for (i = 0; i < 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) + crypto_pool_scratch_free(); + mutex_unlock(&cpool_mutex); +} + +static DECLARE_WORK(cpool_cleanup_work, cpool_cleanup_work_cb); +static void cpool_schedule_cleanup(struct kref *kref) +{ + schedule_work(&cpool_cleanup_work); +} + +/** + * crypto_pool_release - decreases number of users for a pool. If it was + * the last user of the pool, releases any memory that was consumed. + * @id: crypto_pool that was previously allocated by crypto_pool_alloc_ahash() + */ +void crypto_pool_release(unsigned int id) +{ + if (WARN_ON_ONCE(id > cpool_populated || !cpool[id].alg)) + return; + + /* slow-path */ + kref_put(&cpool[id].kref, cpool_schedule_cleanup); +} +EXPORT_SYMBOL_GPL(crypto_pool_release); + +/** + * crypto_pool_add - increases number of users (refcounter) for a pool + * @id: crypto_pool that was previously allocated by crypto_pool_alloc_ahash() + */ +void crypto_pool_add(unsigned int id) +{ + if (WARN_ON_ONCE(id > cpool_populated || !cpool[id].alg)) + return; + kref_get(&cpool[id].kref); +} +EXPORT_SYMBOL_GPL(crypto_pool_add); + +/** + * crypto_pool_get - disable bh and start using crypto_pool + * @id: crypto_pool that was previously allocated by crypto_pool_alloc_ahash() + * @c: returned crypto_pool for usage (uninitialized on failure) + */ +int crypto_pool_get(unsigned int id, struct crypto_pool *c) +{ + struct crypto_pool_ahash *ret = (struct crypto_pool_ahash *)c; + + local_bh_disable(); + if (WARN_ON_ONCE(id > cpool_populated || !cpool[id].alg)) { + local_bh_enable(); + return -EINVAL; + } + ret->req = *this_cpu_ptr(cpool[id].req); + ret->base.scratch = this_cpu_read(crypto_pool_scratch); + return 0; +} +EXPORT_SYMBOL_GPL(crypto_pool_get); + +/** + * crypto_pool_algo - return algorithm of crypto_pool + * @id: crypto_pool that was previously allocated by crypto_pool_alloc_ahash() + * @buf: buffer to return name of algorithm + * @buf_len: size of @buf + */ +size_t crypto_pool_algo(unsigned int id, char *buf, size_t buf_len) +{ + size_t ret = 0; + + /* slow-path */ + mutex_lock(&cpool_mutex); + if (cpool[id].alg) + ret = strscpy(buf, cpool[id].alg, buf_len); + mutex_unlock(&cpool_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(crypto_pool_algo); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Per-CPU pool of crypto requests"); diff --git a/include/crypto/pool.h b/include/crypto/pool.h new file mode 100644 index 000000000000..2c61aa45faff --- /dev/null +++ b/include/crypto/pool.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _CRYPTO_POOL_H +#define _CRYPTO_POOL_H + +#include + +#define DEFAULT_CRYPTO_POOL_SCRATCH_SZ 128 + +struct crypto_pool { + void *scratch; +}; + +/* + * struct crypto_pool_ahash - per-CPU pool of ahash_requests + * @base: common members that can be used by any async crypto ops + * @req: pre-allocated ahash request + */ +struct crypto_pool_ahash { + struct crypto_pool base; + struct ahash_request *req; +}; + +int crypto_pool_alloc_ahash(const char *alg); +void crypto_pool_add(unsigned int id); +void crypto_pool_release(unsigned int id); + +int crypto_pool_get(unsigned int id, struct crypto_pool *c); +static inline void crypto_pool_put(void) +{ + local_bh_enable(); +} +size_t crypto_pool_algo(unsigned int id, char *buf, size_t buf_len); + +#endif /* _CRYPTO_POOL_H */ From patchwork Thu Oct 27 20:43:13 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022721 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 A1132FA3743 for ; Thu, 27 Oct 2022 20:44:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236752AbiJ0UoF (ORCPT ); Thu, 27 Oct 2022 16:44:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58866 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236615AbiJ0UoA (ORCPT ); Thu, 27 Oct 2022 16:44:00 -0400 Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7DA677173C for ; Thu, 27 Oct 2022 13:43:59 -0700 (PDT) Received: by mail-wr1-x42d.google.com with SMTP id a14so4172738wru.5 for ; Thu, 27 Oct 2022 13:43:59 -0700 (PDT) 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=RxfT3udHjY/1MFp4/+Y9TLXrCrikupuEdzT2r1qucRk=; b=WObTEuSpCZ6kMlzBm/Y1HjE9FvJSeykn2DUTcsVTC2eDycf2ZDGPUcrb6r7L/423El ZuPxAEamLFG8SeagtCXwBy5pSPP+SPHLPCAZ69QkfrsuhUAbe8J3usllgZj0Wb0pak7D NvVxE45Ve5j4+4at2dVO+/HKBYzr1h1+M4dDQtm3VDf0fEnOOhBaQRI4rBw80CwPGew8 5GnuzdG1g15c5nYNOEiTVTrMlgrL4s9YKXC0J6XyJKcYiXhBSzPr/nY7HlQVFjw+fvvx aMFfDbEp+su/jwS7NkHAVa08sXoo//f0GGNa0Zbsa6mccpRHxksq0jQcP4biMrmUBdgj uUuw== 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=RxfT3udHjY/1MFp4/+Y9TLXrCrikupuEdzT2r1qucRk=; b=1Xpc+ZOmCdP4ZJqVcK5DncGUKRjCcxngrT8MnNsvqVYn5e6Ob2p4KS0oqQUPnwJoNu jc3PietiJgebfGLCK6wjQ8g7yE7rVqX+v6/tKJz22b2dLhj1q7mdsYVulgaBe9lMjPLt bbi+XJnhK4S9CIe94Xp/leEeh3ie6NOAmI2PkyNqE0Zaqc1rInt8XqaKzp3IIeSBYBfv PtsTCIY2S2Lcj8XNtsCMf/1cF96EwfQT52wUdY2RHf9yw+XiEesyT/jd/1grvA6iW6Cd 3ADt6ZQD19ihcDlXeJxBG/Q9x/0Ni2fyYjZ1bZj7B7Z6AB5tkugvkhjcZT1ltkjf//5V csjQ== X-Gm-Message-State: ACrzQf2jcjU7dFb17/FNTkN7KlnnksiJ3SWF24CylSxDzLyrR7PLwS1G hMouzshd0vxMcwEOjtCZ6Rea0w== X-Google-Smtp-Source: AMsMyM5uB1RtXoPm8/baWtcX8vcSmcCs4lg1PeNXA3NJ9eLfk5R01WukaeXAdIrN4jxeBP3JtYByLQ== X-Received: by 2002:a05:6000:811:b0:236:622d:a7d6 with SMTP id bt17-20020a056000081100b00236622da7d6mr19178175wrb.258.1666903438049; Thu, 27 Oct 2022 13:43:58 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.43.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:43:57 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 02/36] crypto_pool: Add crypto_pool_reserve_scratch() Date: Thu, 27 Oct 2022 21:43:13 +0100 Message-Id: <20221027204347.529913-3-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-1-dima@arista.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Instead of having build-time hardcoded constant, reallocate scratch area, if needed by user. Different algos, different users may need different size of temp per-CPU buffer. Only up-sizing supported for simplicity. Signed-off-by: Dmitry Safonov --- crypto/Kconfig | 6 ++++ crypto/crypto_pool.c | 77 ++++++++++++++++++++++++++++++++++--------- include/crypto/pool.h | 3 +- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/crypto/Kconfig b/crypto/Kconfig index 768d331e626b..e002cd321e79 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1397,6 +1397,12 @@ config CRYPTO_POOL help Per-CPU pool of crypto requests ready for usage in atomic contexts. +config CRYPTO_POOL_DEFAULT_SCRATCH_SIZE + hex "Per-CPU default scratch area size" + depends on CRYPTO_POOL + default 0x100 + range 0x100 0x10000 + if !KMSAN # avoid false positives from assembly if ARM source "arch/arm/crypto/Kconfig" diff --git a/crypto/crypto_pool.c b/crypto/crypto_pool.c index 37131952c5a7..2f1deb3f5218 100644 --- a/crypto/crypto_pool.c +++ b/crypto/crypto_pool.c @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include #include #include -static unsigned long scratch_size = DEFAULT_CRYPTO_POOL_SCRATCH_SZ; +static unsigned long scratch_size = CONFIG_CRYPTO_POOL_DEFAULT_SCRATCH_SIZE; static DEFINE_PER_CPU(void *, crypto_pool_scratch); struct crypto_pool_entry { @@ -22,26 +23,69 @@ static struct crypto_pool_entry cpool[CPOOL_SIZE]; static unsigned int cpool_populated; static DEFINE_MUTEX(cpool_mutex); -static int crypto_pool_scratch_alloc(void) +/* Slow-path */ +/** + * crypto_pool_reserve_scratch - re-allocates scratch buffer, slow-path + * @size: request size for the scratch/temp buffer + */ +#define FREE_BATCH_SIZE 64 +int crypto_pool_reserve_scratch(unsigned long size) { - int cpu; - - lockdep_assert_held(&cpool_mutex); + void *free_batch[FREE_BATCH_SIZE]; + int cpu, err = 0; + unsigned int i = 0; + mutex_lock(&cpool_mutex); + if (size == scratch_size) { + for_each_possible_cpu(cpu) { + if (per_cpu(crypto_pool_scratch, cpu)) + continue; + goto allocate_scratch; + } + mutex_unlock(&cpool_mutex); + return 0; + } +allocate_scratch: + size = max(size, scratch_size); + cpus_read_lock(); for_each_possible_cpu(cpu) { - void *scratch = per_cpu(crypto_pool_scratch, cpu); + void *scratch, *old_scratch; - if (scratch) + scratch = kmalloc_node(size, GFP_KERNEL, cpu_to_node(cpu)); + if (!scratch) { + err = -ENOMEM; + break; + } + + old_scratch = per_cpu(crypto_pool_scratch, cpu); + /* Pairs with crypto_pool_get() */ + WRITE_ONCE(*per_cpu_ptr(&crypto_pool_scratch, cpu), scratch); + if (!cpu_online(cpu)) { + kfree(old_scratch); continue; + } + free_batch[i++] = old_scratch; + if (i == FREE_BATCH_SIZE) { + cpus_read_unlock(); + synchronize_rcu(); + while (i > 0) + kfree(free_batch[--i]); + cpus_read_lock(); + } + } + cpus_read_unlock(); + if (!err) + scratch_size = size; + mutex_unlock(&cpool_mutex); - scratch = kmalloc_node(scratch_size, GFP_KERNEL, - cpu_to_node(cpu)); - if (!scratch) - return -ENOMEM; - per_cpu(crypto_pool_scratch, cpu) = scratch; + if (i > 0) { + synchronize_rcu(); + while (i > 0) + kfree(free_batch[--i]); } - return 0; + return err; } +EXPORT_SYMBOL_GPL(crypto_pool_reserve_scratch); static void crypto_pool_scratch_free(void) { @@ -138,7 +182,6 @@ int crypto_pool_alloc_ahash(const char *alg) /* slow-path */ mutex_lock(&cpool_mutex); - for (i = 0; i < cpool_populated; i++) { if (cpool[i].alg && !strcmp(cpool[i].alg, alg)) { if (kref_read(&cpool[i].kref) > 0) { @@ -263,7 +306,11 @@ int crypto_pool_get(unsigned int id, struct crypto_pool *c) return -EINVAL; } ret->req = *this_cpu_ptr(cpool[id].req); - ret->base.scratch = this_cpu_read(crypto_pool_scratch); + /* + * Pairs with crypto_pool_reserve_scratch(), scartch area is + * valid (allocated) until crypto_pool_put(). + */ + ret->base.scratch = READ_ONCE(*this_cpu_ptr(&crypto_pool_scratch)); return 0; } EXPORT_SYMBOL_GPL(crypto_pool_get); diff --git a/include/crypto/pool.h b/include/crypto/pool.h index 2c61aa45faff..c7d817860cc3 100644 --- a/include/crypto/pool.h +++ b/include/crypto/pool.h @@ -4,8 +4,6 @@ #include -#define DEFAULT_CRYPTO_POOL_SCRATCH_SZ 128 - struct crypto_pool { void *scratch; }; @@ -20,6 +18,7 @@ struct crypto_pool_ahash { struct ahash_request *req; }; +int crypto_pool_reserve_scratch(unsigned long size); int crypto_pool_alloc_ahash(const char *alg); void crypto_pool_add(unsigned int id); void crypto_pool_release(unsigned int id); From patchwork Thu Oct 27 20:43:14 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022723 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 04AE1ECAAA1 for ; Thu, 27 Oct 2022 20:44:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236684AbiJ0UoG (ORCPT ); Thu, 27 Oct 2022 16:44:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58896 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236552AbiJ0UoB (ORCPT ); Thu, 27 Oct 2022 16:44:01 -0400 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 23AA57173C for ; Thu, 27 Oct 2022 13:44:01 -0700 (PDT) Received: by mail-wr1-x42c.google.com with SMTP id v1so4145731wrt.11 for ; Thu, 27 Oct 2022 13:44:01 -0700 (PDT) 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=mqJckVrQg7Ntll0dZ6IL+1cA9p9Fyu8BflY8RBnW50o=; b=adksfXU+IhKWZkTCsv/Jcv4ToprOrvbwTBksLrI9oR+mVHGGcv2M+bzWDg6c3Ei0LL YIDu6kSyWeVF1gxECYDffcL+Gm66Tiow7qbitsojkjI6rNtCXi8rjSgDZ/7GYuGQ29Ga bdHtqQrdJqgNW5QLmAjWCqndg+pHDaCwhajmklTkWp46KOZb720UmH6+b6mrPcP5tUlS vmh1nXjO5l26AiTBzQDW43ew8nwJ3kObZfz8tYr9oFNv2FC6V8Eu/daou6yxzUY5ImYb De2OexjjNkF1d7lW4nu9vveS3ONmHaH8CSus/o+4VGJbqmOoae7gTjO8NmsyJa7AsERN skJw== 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=mqJckVrQg7Ntll0dZ6IL+1cA9p9Fyu8BflY8RBnW50o=; b=33+vvEEvucQocjPWb8cOEmbmY8bWETx7planq5HwyM0gk4T75LghbfIxpWPxm1KBH4 yvqW6F6zAJvL/qgXPU9EpIdwEMzQjiPMc8UTNFnvIlNPqtd2ZkEdaVqPlro7nGlxoYy8 as2kNrKiSxqLvTgjCtBFMA25AshgOyXIExsAjUxGX4VoJBe/kNH5ar41qxl5Aka9Eu2I hAQEJjNntSZkIrCdEH7IZyPPKMQSLZha5LmJwBp2w0Y3Aadi18ExTy0eYayxMUS/SCoa rdAe4kcZOwzZjRij62JRW89P8VbXR4OmJzkWuHQavihzUe6bDqGrhu+cwnxVdpIHin2E yhzA== X-Gm-Message-State: ACrzQf1K81DGyxvnQW8zekIvpBRhE9kaW4VG0EdfOqWahuHeY1fZd2jQ hnsDsBMT+l6a92I6Q7Nd9olZ7w== X-Google-Smtp-Source: AMsMyM7ufBiXSUdy6XA2PSxE6SbX0e3VGqoCT3nF6lUpJF9Ii1Vt9YuHAtQ+Zj0E3hZOb8dRT0BRiw== X-Received: by 2002:adf:f98a:0:b0:236:677c:2407 with SMTP id f10-20020adff98a000000b00236677c2407mr17998847wrr.578.1666903439683; Thu, 27 Oct 2022 13:43:59 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.43.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:43:59 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 03/36] net/tcp: Separate tcp_md5sig_info allocation into tcp_md5sig_info_add() Date: Thu, 27 Oct 2022 21:43:14 +0100 Message-Id: <20221027204347.529913-4-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 to allocate tcp_md5sig_info, that will help later to do/allocate things when info allocated, once per socket. Signed-off-by: Dmitry Safonov --- net/ipv4/tcp_ipv4.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 87d440f47a70..fae80b1a1796 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1172,6 +1172,24 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, } EXPORT_SYMBOL(tcp_v4_md5_lookup); +static int tcp_md5sig_info_add(struct sock *sk, gfp_t gfp) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_md5sig_info *md5sig; + + if (rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) + return 0; + + md5sig = kmalloc(sizeof(*md5sig), gfp); + if (!md5sig) + return -ENOMEM; + + sk_gso_disable(sk); + INIT_HLIST_HEAD(&md5sig->head); + rcu_assign_pointer(tp->md5sig_info, md5sig); + return 0; +} + /* This can be called on a newly created socket, from other files */ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, int family, u8 prefixlen, int l3index, u8 flags, @@ -1202,17 +1220,11 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, return 0; } + if (tcp_md5sig_info_add(sk, gfp)) + return -ENOMEM; + md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk)); - if (!md5sig) { - md5sig = kmalloc(sizeof(*md5sig), gfp); - if (!md5sig) - return -ENOMEM; - - sk_gso_disable(sk); - INIT_HLIST_HEAD(&md5sig->head); - rcu_assign_pointer(tp->md5sig_info, md5sig); - } key = sock_kmalloc(sk, sizeof(*key), gfp | __GFP_ZERO); if (!key) From patchwork Thu Oct 27 20:43:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022724 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 EB83FECAAA1 for ; Thu, 27 Oct 2022 20:44:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236870AbiJ0UoV (ORCPT ); Thu, 27 Oct 2022 16:44:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58916 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235239AbiJ0UoC (ORCPT ); Thu, 27 Oct 2022 16:44:02 -0400 Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9352382770 for ; Thu, 27 Oct 2022 13:44:01 -0700 (PDT) Received: by mail-wr1-x42d.google.com with SMTP id a14so4172934wru.5 for ; Thu, 27 Oct 2022 13:44:01 -0700 (PDT) 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=R3wIxoYA4kskQ3aLfFPM/CR6odt8lxzksVtdpk10xSU=; b=ReJ+/PljiD439SapPfcS3EetYUKCYneXp//hjDtiJKRKKeUmJQ1deaxxU2nuRQNuoG gtzd7XCyS9OQHH3cm1jHCdgSIEl37f/Fa0RZScT2yCSAHMoFCbKyDOTyJPAZxJ+pzlLb mXQm4+bR62BL2SFiiRFQp1b/NzrOrBurdQchevmbnNQxoVgSFb9UTLriwxo9ADeEHlmS GwTckPaW7y/evz8dB4mACrrbGk/YBR8DE0ior1LSd5NHxavKlflH8mc8qam5gm8rtETI NprhcvIYsIJFLjIl5N7hYXgSfvIxM9tOSUG9UnhMCJVJ0/Ifcz+T2WNOBky3WVdeUk3W KTuA== 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=R3wIxoYA4kskQ3aLfFPM/CR6odt8lxzksVtdpk10xSU=; b=ACIAv2y0w5MnYTxlMAkL5hsa1FDDVy2E3WU1wLScwVGEc9iV6jFZZckvGKDKbM5HxC L8yWYOjEGcsFI+4lthUlIBOSZm5DF8GAxV+vQAHVhrBYIXeYyvqo8M/FKUkWsyVagMal d0v2jTjvUVbguFA9eTMKn8Le+D6ml/J1bWAXItUQsWBLt2o1xiRTkV4Fqo0HcZfiNPJc LVHmgCh3+dUCMwkunckE31LkvfkM3RCP2NzTKM+AiHjPeDZwkNMeHxpg1MFEDATYmPeA UF1ZTzkLyxUJCMYyzXlL7cYDzAfPN7zoXwRiup4U0ex1/OxIcndFL4V+i02+kHLQXn8u 20UQ== X-Gm-Message-State: ACrzQf3IvhUpRC04Ak4CZ1LA60xikJPSmrngY/QEyDkuTAR6/E1uaY9y D/SgN7u8lOK7VaPiOvQ1PnQJoA== X-Google-Smtp-Source: AMsMyM7CZuEh0KUSGm4oc51L4RdOghbTu8lNFiU1h7cGY9afy+7FQ3oeTbM2XQ2rk752jiDcrHrobw== X-Received: by 2002:adf:e64e:0:b0:236:76a9:446 with SMTP id b14-20020adfe64e000000b0023676a90446mr14064279wrn.696.1666903441130; Thu, 27 Oct 2022 13:44:01 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.43.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:00 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 04/36] net/tcp: Disable TCP-MD5 static key on tcp_md5sig_info destruction Date: Thu, 27 Oct 2022 21:43:15 +0100 Message-Id: <20221027204347.529913-5-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 To do that, separate two scenarios: - where it's the first MD5 key on the system, which means that enabling of the static key may need to sleep; - copying of an existing key from a listening socket to the request socket upon receiving a signed TCP segment, where static key was already enabled (when the key was added to the listening socket). Now the life-time of the static branch for TCP-MD5 is until: - last tcp_md5sig_info is destroyed - last socket in time-wait state with MD5 key is closed. Which means that after all sockets with TCP-MD5 keys are gone, the system gets back the performance of disabled md5-key static branch. Signed-off-by: Dmitry Safonov --- include/net/tcp.h | 10 ++++--- net/ipv4/tcp.c | 5 +--- net/ipv4/tcp_ipv4.c | 56 ++++++++++++++++++++++++++++++---------- net/ipv4/tcp_minisocks.c | 9 ++++--- net/ipv4/tcp_output.c | 4 +-- net/ipv6/tcp_ipv6.c | 10 +++---- 6 files changed, 63 insertions(+), 31 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 14d45661a84d..a0cdf013782a 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1675,7 +1675,11 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key, const struct sock *sk, const struct sk_buff *skb); int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, int family, u8 prefixlen, int l3index, u8 flags, - const u8 *newkey, u8 newkeylen, gfp_t gfp); + const u8 *newkey, u8 newkeylen); +int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr, + int family, u8 prefixlen, int l3index, + struct tcp_md5sig_key *key); + int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family, u8 prefixlen, int l3index, u8 flags); struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, @@ -1683,7 +1687,7 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk, #ifdef CONFIG_TCP_MD5SIG #include -extern struct static_key_false tcp_md5_needed; +extern struct static_key_false_deferred tcp_md5_needed; struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index, const union tcp_md5_addr *addr, int family); @@ -1691,7 +1695,7 @@ static inline struct tcp_md5sig_key * tcp_md5_do_lookup(const struct sock *sk, int l3index, const union tcp_md5_addr *addr, int family) { - if (!static_branch_unlikely(&tcp_md5_needed)) + if (!static_branch_unlikely(&tcp_md5_needed.key)) return NULL; return __tcp_md5_do_lookup(sk, l3index, addr, family); } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ef14efa1fb70..936ed566cc89 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4460,11 +4460,8 @@ bool tcp_alloc_md5sig_pool(void) if (unlikely(!READ_ONCE(tcp_md5sig_pool_populated))) { mutex_lock(&tcp_md5sig_mutex); - if (!tcp_md5sig_pool_populated) { + if (!tcp_md5sig_pool_populated) __tcp_alloc_md5sig_pool(); - if (tcp_md5sig_pool_populated) - static_branch_inc(&tcp_md5_needed); - } mutex_unlock(&tcp_md5sig_mutex); } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index fae80b1a1796..f812d507fc9a 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1064,7 +1064,7 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req) * We need to maintain these in the sk structure. */ -DEFINE_STATIC_KEY_FALSE(tcp_md5_needed); +DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_md5_needed, HZ); EXPORT_SYMBOL(tcp_md5_needed); static bool better_md5_match(struct tcp_md5sig_key *old, struct tcp_md5sig_key *new) @@ -1177,9 +1177,6 @@ static int tcp_md5sig_info_add(struct sock *sk, gfp_t gfp) struct tcp_sock *tp = tcp_sk(sk); struct tcp_md5sig_info *md5sig; - if (rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) - return 0; - md5sig = kmalloc(sizeof(*md5sig), gfp); if (!md5sig) return -ENOMEM; @@ -1191,9 +1188,9 @@ static int tcp_md5sig_info_add(struct sock *sk, gfp_t gfp) } /* This can be called on a newly created socket, from other files */ -int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, - int family, u8 prefixlen, int l3index, u8 flags, - const u8 *newkey, u8 newkeylen, gfp_t gfp) +static int __tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, + int family, u8 prefixlen, int l3index, u8 flags, + const u8 *newkey, u8 newkeylen, gfp_t gfp) { /* Add Key to the list */ struct tcp_md5sig_key *key; @@ -1220,9 +1217,6 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, return 0; } - if (tcp_md5sig_info_add(sk, gfp)) - return -ENOMEM; - md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk)); @@ -1246,8 +1240,44 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, hlist_add_head_rcu(&key->node, &md5sig->head); return 0; } + +int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, + int family, u8 prefixlen, int l3index, u8 flags, + const u8 *newkey, u8 newkeylen) +{ + 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)) + return -ENOMEM; + + static_branch_inc(&tcp_md5_needed.key); + } + + return __tcp_md5_do_add(sk, addr, family, prefixlen, l3index, flags, + newkey, newkeylen, GFP_KERNEL); +} EXPORT_SYMBOL(tcp_md5_do_add); +int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr, + int family, u8 prefixlen, int l3index, + struct tcp_md5sig_key *key) +{ + 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))) + return -ENOMEM; + + atomic_inc(&tcp_md5_needed.key.key.enabled); + } + + return __tcp_md5_do_add(sk, addr, family, prefixlen, l3index, + key->flags, key->key, key->keylen, + sk_gfp_mask(sk, GFP_ATOMIC)); +} +EXPORT_SYMBOL(tcp_md5_key_copy); + int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family, u8 prefixlen, int l3index, u8 flags) { @@ -1334,7 +1364,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname, return -EINVAL; return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, - cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); + cmd.tcpm_key, cmd.tcpm_keylen); } static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp, @@ -1591,8 +1621,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb, * memory, then we end up not copying the key * across. Shucks. */ - tcp_md5_do_add(newsk, addr, AF_INET, 32, l3index, key->flags, - key->key, key->keylen, GFP_ATOMIC); + tcp_md5_key_copy(newsk, addr, AF_INET, 32, l3index, key); sk_gso_disable(newsk); } #endif @@ -2284,6 +2313,7 @@ void tcp_v4_destroy_sock(struct sock *sk) tcp_clear_md5_list(sk); kfree_rcu(rcu_dereference_protected(tp->md5sig_info, 1), rcu); tp->md5sig_info = NULL; + static_branch_slow_dec_deferred(&tcp_md5_needed); } #endif diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index c375f603a16c..fb500160b8d2 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -291,13 +291,14 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) */ do { tcptw->tw_md5_key = NULL; - if (static_branch_unlikely(&tcp_md5_needed)) { + if (static_branch_unlikely(&tcp_md5_needed.key)) { struct tcp_md5sig_key *key; key = tp->af_specific->md5_lookup(sk, sk); if (key) { tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC); BUG_ON(tcptw->tw_md5_key && !tcp_alloc_md5sig_pool()); + atomic_inc(&tcp_md5_needed.key.key.enabled); } } } while (0); @@ -337,11 +338,13 @@ EXPORT_SYMBOL(tcp_time_wait); void tcp_twsk_destructor(struct sock *sk) { #ifdef CONFIG_TCP_MD5SIG - if (static_branch_unlikely(&tcp_md5_needed)) { + if (static_branch_unlikely(&tcp_md5_needed.key)) { struct tcp_timewait_sock *twsk = tcp_twsk(sk); - if (twsk->tw_md5_key) + if (twsk->tw_md5_key) { kfree_rcu(twsk->tw_md5_key, rcu); + static_branch_slow_dec_deferred(&tcp_md5_needed); + } } #endif } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index c69f4d966024..86e71c8c76bc 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -766,7 +766,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, *md5 = NULL; #ifdef CONFIG_TCP_MD5SIG - if (static_branch_unlikely(&tcp_md5_needed) && + if (static_branch_unlikely(&tcp_md5_needed.key) && rcu_access_pointer(tp->md5sig_info)) { *md5 = tp->af_specific->md5_lookup(sk, sk); if (*md5) { @@ -922,7 +922,7 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb *md5 = NULL; #ifdef CONFIG_TCP_MD5SIG - if (static_branch_unlikely(&tcp_md5_needed) && + if (static_branch_unlikely(&tcp_md5_needed.key) && rcu_access_pointer(tp->md5sig_info)) { *md5 = tp->af_specific->md5_lookup(sk, sk); if (*md5) { diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 2a3f9296df1e..f528702185c4 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -677,12 +677,11 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, if (ipv6_addr_v4mapped(&sin6->sin6_addr)) return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], AF_INET, prefixlen, l3index, flags, - cmd.tcpm_key, cmd.tcpm_keylen, - GFP_KERNEL); + cmd.tcpm_key, cmd.tcpm_keylen); return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr, AF_INET6, prefixlen, l3index, flags, - cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); + cmd.tcpm_key, cmd.tcpm_keylen); } static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp, @@ -1382,9 +1381,8 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * * memory, then we end up not copying the key * across. Shucks. */ - tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, - AF_INET6, 128, l3index, key->flags, key->key, key->keylen, - sk_gfp_mask(sk, GFP_ATOMIC)); + tcp_md5_key_copy(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, + AF_INET6, 128, l3index, key); } #endif From patchwork Thu Oct 27 20:43:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022727 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 E8BD9ECAAA1 for ; Thu, 27 Oct 2022 20:44:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236933AbiJ0Uoa (ORCPT ); Thu, 27 Oct 2022 16:44:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59352 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236849AbiJ0UoT (ORCPT ); Thu, 27 Oct 2022 16:44:19 -0400 Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6F7539082D for ; Thu, 27 Oct 2022 13:44:03 -0700 (PDT) Received: by mail-wr1-x42d.google.com with SMTP id w14so4154320wru.8 for ; Thu, 27 Oct 2022 13:44:03 -0700 (PDT) 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=ZrgLizh1C804U+r+l0m4Xy4SPh4jwBzIG8ijp90cT4Y=; b=cMCDxmxxsZyQWMfJGv73+N3+AZl+yd10NZc02gpC82WJ/iyUbuSbuABQi/4InmIsjN kYL2x/3+oCy7hQZktcSpgkt6XWivU/rbc5ZY782YsRkR2ZNhHXI7HUVo04/IS0PYk/OY 6kxmhI/7aoJLLWBnk7Y1pxvLhr89RBEQKsRj1OkCoSSzrA2SsXrjF9P43vp2nDMO0AOB fq+40H7RszKEYf/ikNxqqmAFvN3Jj5f7FQfvN0BDuddCEyondAd7gxqAmVKCJGyzDQhN OnXk4Hf0wJHq9KSJKtN4d5Zxn2Yf3wnQbRpRDstixVYBR9pCmprfxrQEt2Z9boJ1OPYy PxVg== 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=ZrgLizh1C804U+r+l0m4Xy4SPh4jwBzIG8ijp90cT4Y=; b=zg5u5GHDh3kKcZQ92fT3bcVoOztA1M2Y+Jv6O5EUcQSI3J+JYhIRAL6zyOO8k/N2AX vWpdlKheGdbOhlRLb1jr0OuLYwmKIX16wjdatu3RJ/ktylehVTepIUEBZGwhZKAGaRy7 d6B34IcHoQuLQrnVvUQlejmOYvp/6ZIZ9tqjt3qaJsZSGLFUbt1/gOEkYryniVf+0RrI fC/Cy3XJ89PmtTCJvp1hzufnINEkrHXqvw3u5iC3EmOlj5WVdnTogKyNfYnVvg0+Jac2 SWK64hbjDIjo12ex6mlDEbXrx/xrdYG/8GC3d4Aa9eFCsoMDn/eCoav5C01nsLqEKjs/ NpZw== X-Gm-Message-State: ACrzQf1SkKoP0EZBXuR5VmBvnowzTeWiU1wqn6VioPMWQiZO3XimmPUQ R+ndbL6kMYz3MXodM3i3yc0WkQ== X-Google-Smtp-Source: AMsMyM7ZrH+7+mXNrgMwKDU7op6xsT4TMj7wvnEbEnL2Ud9S1EYWoNJCzURdfCybIlZQKSuWkqoYlg== X-Received: by 2002:a05:6000:1866:b0:236:715a:16c1 with SMTP id d6-20020a056000186600b00236715a16c1mr15060104wri.537.1666903442852; Thu, 27 Oct 2022 13:44:02 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:02 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 05/36] net/tcp: Use crypto_pool for TCP-MD5 Date: Thu, 27 Oct 2022 21:43:16 +0100 Message-Id: <20221027204347.529913-6-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 Use crypto_pool API that was designed with tcp_md5sig_pool in mind. The conversion to use crypto_pool will allow: - to reuse ahash_request(s) for different users - to allocate only one per-CPU scratch buffer rather than a new one for each user - to have a common API for net/ users that need ahash on RX/TX fast path Signed-off-by: Dmitry Safonov --- include/net/tcp.h | 24 +++------ net/ipv4/Kconfig | 2 +- net/ipv4/tcp.c | 105 ++++++++++----------------------------- net/ipv4/tcp_ipv4.c | 90 +++++++++++++++++++-------------- net/ipv4/tcp_minisocks.c | 22 ++++++-- net/ipv6/tcp_ipv6.c | 53 +++++++++----------- 6 files changed, 129 insertions(+), 167 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index a0cdf013782a..03dbe1940fec 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1664,12 +1664,6 @@ union tcp_md5sum_block { #endif }; -/* - pool: digest algorithm, hash description and scratch buffer */ -struct tcp_md5sig_pool { - struct ahash_request *md5_req; - void *scratch; -}; - /* - functions */ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key, const struct sock *sk, const struct sk_buff *skb); @@ -1725,17 +1719,15 @@ tcp_inbound_md5_hash(const struct sock *sk, const struct sk_buff *skb, #define tcp_twsk_md5_key(twsk) NULL #endif -bool tcp_alloc_md5sig_pool(void); - -struct tcp_md5sig_pool *tcp_get_md5sig_pool(void); -static inline void tcp_put_md5sig_pool(void) -{ - local_bh_enable(); -} +struct crypto_pool_ahash; +int tcp_md5_alloc_crypto_pool(void); +void tcp_md5_release_crypto_pool(void); +void tcp_md5_add_crypto_pool(void); +extern int tcp_md5_crypto_pool_id; -int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *, - unsigned int header_len); -int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, +int tcp_md5_hash_skb_data(struct crypto_pool_ahash *hp, + const struct sk_buff *skb, unsigned int header_len); +int tcp_md5_hash_key(struct crypto_pool_ahash *hp, const struct tcp_md5sig_key *key); /* From tcp_fastopen.c */ diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index e983bb0c5012..c341864e4398 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -733,7 +733,7 @@ config DEFAULT_TCP_CONG config TCP_MD5SIG bool "TCP: MD5 Signature Option support (RFC2385)" - select CRYPTO + select CRYPTO_POOL select CRYPTO_MD5 help RFC2385 specifies a method of giving MD5 protection to TCP sessions. diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 936ed566cc89..d6f494f2c350 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -244,6 +244,7 @@ #define pr_fmt(fmt) "TCP: " fmt #include +#include #include #include #include @@ -4407,98 +4408,45 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, EXPORT_SYMBOL(tcp_getsockopt); #ifdef CONFIG_TCP_MD5SIG -static DEFINE_PER_CPU(struct tcp_md5sig_pool, tcp_md5sig_pool); -static DEFINE_MUTEX(tcp_md5sig_mutex); -static bool tcp_md5sig_pool_populated = false; +int tcp_md5_crypto_pool_id = -1; +EXPORT_SYMBOL(tcp_md5_crypto_pool_id); -static void __tcp_alloc_md5sig_pool(void) +int tcp_md5_alloc_crypto_pool(void) { - struct crypto_ahash *hash; - int cpu; - - hash = crypto_alloc_ahash("md5", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(hash)) - return; - - for_each_possible_cpu(cpu) { - void *scratch = per_cpu(tcp_md5sig_pool, cpu).scratch; - struct ahash_request *req; - - if (!scratch) { - scratch = kmalloc_node(sizeof(union tcp_md5sum_block) + - sizeof(struct tcphdr), - GFP_KERNEL, - cpu_to_node(cpu)); - if (!scratch) - return; - per_cpu(tcp_md5sig_pool, cpu).scratch = scratch; - } - if (per_cpu(tcp_md5sig_pool, cpu).md5_req) - continue; - - req = ahash_request_alloc(hash, GFP_KERNEL); - if (!req) - return; + int ret; - ahash_request_set_callback(req, 0, NULL, NULL); + ret = crypto_pool_reserve_scratch(sizeof(union tcp_md5sum_block) + + sizeof(struct tcphdr)); + if (ret) + return ret; - per_cpu(tcp_md5sig_pool, cpu).md5_req = req; + ret = crypto_pool_alloc_ahash("md5"); + if (ret >= 0) { + tcp_md5_crypto_pool_id = ret; + return 0; } - /* before setting tcp_md5sig_pool_populated, we must commit all writes - * to memory. See smp_rmb() in tcp_get_md5sig_pool() - */ - smp_wmb(); - /* 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_crypto_pool); -bool tcp_alloc_md5sig_pool(void) +void tcp_md5_release_crypto_pool(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); + crypto_pool_release(tcp_md5_crypto_pool_id); } -EXPORT_SYMBOL(tcp_alloc_md5sig_pool); +EXPORT_SYMBOL(tcp_md5_release_crypto_pool); - -/** - * tcp_get_md5sig_pool - get md5sig_pool for this user - * - * We use percpu structure, so if we succeed, we exit with preemption - * and BH disabled, to make sure another thread or softirq handling - * wont try to get same context. - */ -struct tcp_md5sig_pool *tcp_get_md5sig_pool(void) +void tcp_md5_add_crypto_pool(void) { - local_bh_disable(); - - /* 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; + crypto_pool_add(tcp_md5_crypto_pool_id); } -EXPORT_SYMBOL(tcp_get_md5sig_pool); +EXPORT_SYMBOL(tcp_md5_add_crypto_pool); -int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp, +int tcp_md5_hash_skb_data(struct crypto_pool_ahash *hp, const struct sk_buff *skb, unsigned int header_len) { struct scatterlist sg; const struct tcphdr *tp = tcp_hdr(skb); - struct ahash_request *req = hp->md5_req; + struct ahash_request *req = hp->req; unsigned int i; const unsigned int head_data_len = skb_headlen(skb) > header_len ? skb_headlen(skb) - header_len : 0; @@ -4532,16 +4480,17 @@ int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp, } EXPORT_SYMBOL(tcp_md5_hash_skb_data); -int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *key) +int tcp_md5_hash_key(struct crypto_pool_ahash *hp, + const struct tcp_md5sig_key *key) { u8 keylen = READ_ONCE(key->keylen); /* paired with WRITE_ONCE() in tcp_md5_do_add */ struct scatterlist sg; sg_init_one(&sg, key->key, keylen); - ahash_request_set_crypt(hp->md5_req, &sg, NULL, keylen); + ahash_request_set_crypt(hp->req, &sg, NULL, keylen); /* We use data_race() because tcp_md5_do_add() might change key->key under us */ - return data_race(crypto_ahash_update(hp->md5_req)); + return data_race(crypto_ahash_update(hp->req)); } EXPORT_SYMBOL(tcp_md5_hash_key); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index f812d507fc9a..08ffba6c33f4 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -79,6 +79,7 @@ #include #include +#include #include #include @@ -1223,10 +1224,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; @@ -1248,8 +1245,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_crypto_pool()) + return -ENOMEM; + + if (tcp_md5sig_info_add(sk, GFP_KERNEL)) { + tcp_md5_release_crypto_pool(); return -ENOMEM; + } static_branch_inc(&tcp_md5_needed.key); } @@ -1266,8 +1268,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_crypto_pool(); + + if (tcp_md5sig_info_add(sk, sk_gfp_mask(sk, GFP_ATOMIC))) { + tcp_md5_release_crypto_pool(); return -ENOMEM; + } atomic_inc(&tcp_md5_needed.key.key.enabled); } @@ -1367,7 +1373,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname, cmd.tcpm_key, cmd.tcpm_keylen); } -static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp, +static int tcp_v4_md5_hash_headers(struct crypto_pool_ahash *hp, __be32 daddr, __be32 saddr, const struct tcphdr *th, int nbytes) { @@ -1375,7 +1381,7 @@ static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp, struct scatterlist sg; struct tcphdr *_th; - bp = hp->scratch; + bp = hp->base.scratch; bp->saddr = saddr; bp->daddr = daddr; bp->pad = 0; @@ -1387,37 +1393,34 @@ static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp, _th->check = 0; sg_init_one(&sg, bp, sizeof(*bp) + sizeof(*th)); - ahash_request_set_crypt(hp->md5_req, &sg, NULL, + ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp) + sizeof(*th)); - return crypto_ahash_update(hp->md5_req); + return crypto_ahash_update(hp->req); } static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, __be32 daddr, __be32 saddr, const struct tcphdr *th) { - struct tcp_md5sig_pool *hp; - struct ahash_request *req; + struct crypto_pool_ahash hp; - hp = tcp_get_md5sig_pool(); - if (!hp) + if (crypto_pool_get(tcp_md5_crypto_pool_id, (struct crypto_pool *)&hp)) goto clear_hash_noput; - req = hp->md5_req; - if (crypto_ahash_init(req)) + if (crypto_ahash_init(hp.req)) goto clear_hash; - if (tcp_v4_md5_hash_headers(hp, daddr, saddr, th, th->doff << 2)) + if (tcp_v4_md5_hash_headers(&hp, daddr, saddr, th, th->doff << 2)) goto clear_hash; - if (tcp_md5_hash_key(hp, key)) + if (tcp_md5_hash_key(&hp, key)) goto clear_hash; - ahash_request_set_crypt(req, NULL, md5_hash, 0); - if (crypto_ahash_final(req)) + ahash_request_set_crypt(hp.req, NULL, md5_hash, 0); + if (crypto_ahash_final(hp.req)) goto clear_hash; - tcp_put_md5sig_pool(); + crypto_pool_put(); return 0; clear_hash: - tcp_put_md5sig_pool(); + crypto_pool_put(); clear_hash_noput: memset(md5_hash, 0, 16); return 1; @@ -1427,8 +1430,7 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key, const struct sock *sk, const struct sk_buff *skb) { - struct tcp_md5sig_pool *hp; - struct ahash_request *req; + struct crypto_pool_ahash hp; const struct tcphdr *th = tcp_hdr(skb); __be32 saddr, daddr; @@ -1441,29 +1443,27 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key, daddr = iph->daddr; } - hp = tcp_get_md5sig_pool(); - if (!hp) + if (crypto_pool_get(tcp_md5_crypto_pool_id, (struct crypto_pool *)&hp)) goto clear_hash_noput; - req = hp->md5_req; - if (crypto_ahash_init(req)) + if (crypto_ahash_init(hp.req)) goto clear_hash; - if (tcp_v4_md5_hash_headers(hp, daddr, saddr, th, skb->len)) + if (tcp_v4_md5_hash_headers(&hp, daddr, saddr, th, skb->len)) goto clear_hash; - if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2)) + if (tcp_md5_hash_skb_data(&hp, skb, th->doff << 2)) goto clear_hash; - if (tcp_md5_hash_key(hp, key)) + if (tcp_md5_hash_key(&hp, key)) goto clear_hash; - ahash_request_set_crypt(req, NULL, md5_hash, 0); - if (crypto_ahash_final(req)) + ahash_request_set_crypt(hp.req, NULL, md5_hash, 0); + if (crypto_ahash_final(hp.req)) goto clear_hash; - tcp_put_md5sig_pool(); + crypto_pool_put(); return 0; clear_hash: - tcp_put_md5sig_pool(); + crypto_pool_put(); clear_hash_noput: memset(md5_hash, 0, 16); return 1; @@ -2286,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_crypto_pool(); +} +#endif + void tcp_v4_destroy_sock(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); @@ -2310,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 fb500160b8d2..f7a75dce140b 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -297,8 +297,10 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) key = tp->af_specific->md5_lookup(sk, sk); if (key) { tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC); - BUG_ON(tcptw->tw_md5_key && !tcp_alloc_md5sig_pool()); + if (WARN_ON(!tcptw->tw_md5_key)) + break; atomic_inc(&tcp_md5_needed.key.key.enabled); + tcp_md5_add_crypto_pool(); } } } while (0); @@ -335,16 +337,26 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) } EXPORT_SYMBOL(tcp_time_wait); +#ifdef CONFIG_TCP_MD5SIG +static void tcp_md5_twsk_free_rcu(struct rcu_head *head) +{ + struct tcp_md5sig_key *key; + + key = container_of(head, struct tcp_md5sig_key, rcu); + kfree(key); + static_branch_slow_dec_deferred(&tcp_md5_needed); + tcp_md5_release_crypto_pool(); +} +#endif + void tcp_twsk_destructor(struct sock *sk) { #ifdef CONFIG_TCP_MD5SIG if (static_branch_unlikely(&tcp_md5_needed.key)) { struct tcp_timewait_sock *twsk = tcp_twsk(sk); - if (twsk->tw_md5_key) { - kfree_rcu(twsk->tw_md5_key, rcu); - static_branch_slow_dec_deferred(&tcp_md5_needed); - } + if (twsk->tw_md5_key) + call_rcu(&twsk->tw_md5_key->rcu, tcp_md5_twsk_free_rcu); } #endif } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f528702185c4..2c5c0ca7de67 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -64,6 +64,7 @@ #include #include +#include #include #include @@ -684,7 +685,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, cmd.tcpm_key, cmd.tcpm_keylen); } -static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp, +static int tcp_v6_md5_hash_headers(struct crypto_pool_ahash *hp, const struct in6_addr *daddr, const struct in6_addr *saddr, const struct tcphdr *th, int nbytes) @@ -693,7 +694,7 @@ static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp, struct scatterlist sg; struct tcphdr *_th; - bp = hp->scratch; + bp = hp->base.scratch; /* 1. TCP pseudo-header (RFC2460) */ bp->saddr = *saddr; bp->daddr = *daddr; @@ -705,38 +706,35 @@ static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp, _th->check = 0; sg_init_one(&sg, bp, sizeof(*bp) + sizeof(*th)); - ahash_request_set_crypt(hp->md5_req, &sg, NULL, + ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp) + sizeof(*th)); - return crypto_ahash_update(hp->md5_req); + return crypto_ahash_update(hp->req); } static int tcp_v6_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, const struct in6_addr *daddr, struct in6_addr *saddr, const struct tcphdr *th) { - struct tcp_md5sig_pool *hp; - struct ahash_request *req; + struct crypto_pool_ahash hp; - hp = tcp_get_md5sig_pool(); - if (!hp) + if (crypto_pool_get(tcp_md5_crypto_pool_id, (struct crypto_pool *)&hp)) goto clear_hash_noput; - req = hp->md5_req; - if (crypto_ahash_init(req)) + if (crypto_ahash_init(hp.req)) goto clear_hash; - if (tcp_v6_md5_hash_headers(hp, daddr, saddr, th, th->doff << 2)) + if (tcp_v6_md5_hash_headers(&hp, daddr, saddr, th, th->doff << 2)) goto clear_hash; - if (tcp_md5_hash_key(hp, key)) + if (tcp_md5_hash_key(&hp, key)) goto clear_hash; - ahash_request_set_crypt(req, NULL, md5_hash, 0); - if (crypto_ahash_final(req)) + ahash_request_set_crypt(hp.req, NULL, md5_hash, 0); + if (crypto_ahash_final(hp.req)) goto clear_hash; - tcp_put_md5sig_pool(); + crypto_pool_put(); return 0; clear_hash: - tcp_put_md5sig_pool(); + crypto_pool_put(); clear_hash_noput: memset(md5_hash, 0, 16); return 1; @@ -748,8 +746,7 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, const struct sk_buff *skb) { const struct in6_addr *saddr, *daddr; - struct tcp_md5sig_pool *hp; - struct ahash_request *req; + struct crypto_pool_ahash hp; const struct tcphdr *th = tcp_hdr(skb); if (sk) { /* valid for establish/request sockets */ @@ -761,29 +758,27 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, daddr = &ip6h->daddr; } - hp = tcp_get_md5sig_pool(); - if (!hp) + if (crypto_pool_get(tcp_md5_crypto_pool_id, (struct crypto_pool *)&hp)) goto clear_hash_noput; - req = hp->md5_req; - if (crypto_ahash_init(req)) + if (crypto_ahash_init(hp.req)) goto clear_hash; - if (tcp_v6_md5_hash_headers(hp, daddr, saddr, th, skb->len)) + if (tcp_v6_md5_hash_headers(&hp, daddr, saddr, th, skb->len)) goto clear_hash; - if (tcp_md5_hash_skb_data(hp, skb, th->doff << 2)) + if (tcp_md5_hash_skb_data(&hp, skb, th->doff << 2)) goto clear_hash; - if (tcp_md5_hash_key(hp, key)) + if (tcp_md5_hash_key(&hp, key)) goto clear_hash; - ahash_request_set_crypt(req, NULL, md5_hash, 0); - if (crypto_ahash_final(req)) + ahash_request_set_crypt(hp.req, NULL, md5_hash, 0); + if (crypto_ahash_final(hp.req)) goto clear_hash; - tcp_put_md5sig_pool(); + crypto_pool_put(); return 0; clear_hash: - tcp_put_md5sig_pool(); + crypto_pool_put(); clear_hash_noput: memset(md5_hash, 0, 16); return 1; From patchwork Thu Oct 27 20:43:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022726 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 172CBFA3740 for ; Thu, 27 Oct 2022 20:44:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236887AbiJ0Uo0 (ORCPT ); Thu, 27 Oct 2022 16:44:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59408 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236875AbiJ0UoV (ORCPT ); Thu, 27 Oct 2022 16:44:21 -0400 Received: from mail-wm1-x335.google.com (mail-wm1-x335.google.com [IPv6:2a00:1450:4864:20::335]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D37F38E468 for ; Thu, 27 Oct 2022 13:44:04 -0700 (PDT) Received: by mail-wm1-x335.google.com with SMTP id m29-20020a05600c3b1d00b003c6bf423c71so5066977wms.0 for ; Thu, 27 Oct 2022 13:44:04 -0700 (PDT) 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=g/boZ+XBaBrbcS/8OvW3aj43gQiREbGbh2c9kPTSYHE=; b=HfoviNZKNfNmq+PdWniq7QZ5tfGX4DtVu8hBR1zuLhIPcs2bGRxA2lTlW+mfLRGMDG HaI2XbYyZ1B5+3X0f1L4pqU9lUJZ4C+p48mti1LXZL/XNG57mu0Ys5dMRu7P6w1j91mG JInexDGg3t+O3GMgcG3rY8LpU2O0ZB/25iLrFy2mgS30lT4M9IjoNjk/fRztRASr6fxy +qQGtokSNw33ChOZseOowz3NpijkV6/tHdAUR84n+7fxJSLW5hAw95FrCYcUqfSZddmN 44Z+9XHj0HC8U/GaQ3G967C1SI3tOA/EJLdncLRxTD8yKNl0bBdFgsmuni77UIpzewIh spxg== 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=g/boZ+XBaBrbcS/8OvW3aj43gQiREbGbh2c9kPTSYHE=; b=OCbkUw8cCsOqthO/7UW3hf7Zx6HzA26HNSD50+XQdr267Ji0VI/5ncLb+jkTDdy5ak nDEeZAWP9EhacJBx2JnmAiUsIcb+ZxV12Z8TK4bIDTyzcw4K9XINm6xvIZkSlq5P7LQ6 IUuUzFYXCoNVn/QIeEFeRkTkXsH1gqSSJbt83Hzu+3ch0JkGlRC/u8qZldmAv1Yj5he3 JVPsUVrs25SVQLnEhdn3R0goBWmLW1quiRQPrSFf9WTJpd9S8ryaOD6SKPLsO30pGxWI zg13ulwaosSqlyRtiU2DqUhA7Wz7Oanyz3JdrTrhcno7seebR3QKmgfcPsn6MbTcOGbw H8Hg== X-Gm-Message-State: ACrzQf3Gb4NgpeguEz3Syck6VsmDdQzIbdV4uG58eQ4+P9+GgCYHgGKP 3KWkLPewUrrDMyi1h7v1EhIDNg== X-Google-Smtp-Source: AMsMyM6LPAOiaC1tHbFbDPQry9siLZbi/Xrua+LbRGDA/Fan1CptbcHDJyIMTYiNgOIT9RjTrlcSug== X-Received: by 2002:a05:600c:4e41:b0:3c7:a5d1:bda5 with SMTP id e1-20020a05600c4e4100b003c7a5d1bda5mr7108227wmq.173.1666903444389; Thu, 27 Oct 2022 13:44:04 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:03 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 06/36] net/ipv6: sr: Switch to using crypto_pool Date: Thu, 27 Oct 2022 21:43:17 +0100 Message-Id: <20221027204347.529913-7-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 The conversion to use crypto_pool has the following upsides: - now SR uses asynchronous API which may potentially free CPU cycles and improve performance for of CPU crypto algorithm providers; - hash descriptors now don't have to be allocated on boot, but only at the moment SR starts using HMAC and until the last HMAC secret is deleted; - potentially reuse ahash_request(s) for different users - allocate only one per-CPU scratch buffer rather than a new one for each user - have a common API for net/ users that need ahash on RX/TX fast path Signed-off-by: Dmitry Safonov --- include/net/seg6_hmac.h | 7 -- net/ipv6/Kconfig | 2 +- net/ipv6/seg6.c | 3 - net/ipv6/seg6_hmac.c | 204 ++++++++++++++++------------------------ 4 files changed, 80 insertions(+), 136 deletions(-) diff --git a/include/net/seg6_hmac.h b/include/net/seg6_hmac.h index 2b5d2ee5613e..d6b7820ecda2 100644 --- a/include/net/seg6_hmac.h +++ b/include/net/seg6_hmac.h @@ -32,13 +32,6 @@ struct seg6_hmac_info { u8 alg_id; }; -struct seg6_hmac_algo { - u8 alg_id; - char name[64]; - struct crypto_shash * __percpu *tfms; - struct shash_desc * __percpu *shashs; -}; - extern int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr, struct in6_addr *saddr, u8 *output); diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 658bfed1df8b..5be1dab0f178 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -304,7 +304,7 @@ config IPV6_SEG6_LWTUNNEL config IPV6_SEG6_HMAC bool "IPv6: Segment Routing HMAC support" depends on IPV6 - select CRYPTO + select CRYPTO_POOL select CRYPTO_HMAC select CRYPTO_SHA1 select CRYPTO_SHA256 diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c index 29346a6eec9f..3d66bf6d4c66 100644 --- a/net/ipv6/seg6.c +++ b/net/ipv6/seg6.c @@ -558,9 +558,6 @@ int __init seg6_init(void) void seg6_exit(void) { -#ifdef CONFIG_IPV6_SEG6_HMAC - seg6_hmac_exit(); -#endif #ifdef CONFIG_IPV6_SEG6_LWTUNNEL seg6_iptunnel_exit(); #endif diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c index d43c50a7310d..3732dd993925 100644 --- a/net/ipv6/seg6_hmac.c +++ b/net/ipv6/seg6_hmac.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -70,6 +71,12 @@ static const struct rhashtable_params rht_params = { .obj_cmpfn = seg6_hmac_cmpfn, }; +struct seg6_hmac_algo { + u8 alg_id; + char name[64]; + int crypto_pool_id; +}; + static struct seg6_hmac_algo hmac_algos[] = { { .alg_id = SEG6_HMAC_ALGO_SHA1, @@ -115,55 +122,17 @@ static struct seg6_hmac_algo *__hmac_get_algo(u8 alg_id) return NULL; } -static int __do_hmac(struct seg6_hmac_info *hinfo, const char *text, u8 psize, - u8 *output, int outlen) -{ - struct seg6_hmac_algo *algo; - struct crypto_shash *tfm; - struct shash_desc *shash; - int ret, dgsize; - - algo = __hmac_get_algo(hinfo->alg_id); - if (!algo) - return -ENOENT; - - tfm = *this_cpu_ptr(algo->tfms); - - dgsize = crypto_shash_digestsize(tfm); - if (dgsize > outlen) { - pr_debug("sr-ipv6: __do_hmac: digest size too big (%d / %d)\n", - dgsize, outlen); - return -ENOMEM; - } - - ret = crypto_shash_setkey(tfm, hinfo->secret, hinfo->slen); - if (ret < 0) { - pr_debug("sr-ipv6: crypto_shash_setkey failed: err %d\n", ret); - goto failed; - } - - shash = *this_cpu_ptr(algo->shashs); - shash->tfm = tfm; - - ret = crypto_shash_digest(shash, text, psize, output); - if (ret < 0) { - pr_debug("sr-ipv6: crypto_shash_digest failed: err %d\n", ret); - goto failed; - } - - return dgsize; - -failed: - return ret; -} - int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr, struct in6_addr *saddr, u8 *output) { __be32 hmackeyid = cpu_to_be32(hinfo->hmackeyid); - u8 tmp_out[SEG6_HMAC_MAX_DIGESTSIZE]; + struct crypto_pool_ahash hp; + struct seg6_hmac_algo *algo; int plen, i, dgsize, wrsize; + struct crypto_ahash *tfm; + struct scatterlist sg; char *ring, *off; + int err; /* a 160-byte buffer for digest output allows to store highest known * hash function (RadioGatun) with up to 1216 bits @@ -176,6 +145,10 @@ int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr, if (plen >= SEG6_HMAC_RING_SIZE) return -EMSGSIZE; + algo = __hmac_get_algo(hinfo->alg_id); + if (!algo) + return -ENOENT; + /* Let's build the HMAC text on the ring buffer. The text is composed * as follows, in order: * @@ -186,8 +159,36 @@ int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr, * 5. All segments in the segments list (n * 128 bits) */ - local_bh_disable(); + err = crypto_pool_get(algo->crypto_pool_id, (struct crypto_pool *)&hp); + if (err) + return err; + ring = this_cpu_ptr(hmac_ring); + + sg_init_one(&sg, ring, plen); + + tfm = crypto_ahash_reqtfm(hp.req); + dgsize = crypto_ahash_digestsize(tfm); + if (dgsize > SEG6_HMAC_MAX_DIGESTSIZE) { + pr_debug("digest size too big (%d / %d)\n", + dgsize, SEG6_HMAC_MAX_DIGESTSIZE); + err = -ENOMEM; + goto err_put_pool; + } + + err = crypto_ahash_setkey(tfm, hinfo->secret, hinfo->slen); + if (err) { + pr_debug("crypto_ahash_setkey failed: err %d\n", err); + goto err_put_pool; + } + + err = crypto_ahash_init(hp.req); + if (err) + goto err_put_pool; + + ahash_request_set_crypt(hp.req, &sg, + hp.base.scratch, SEG6_HMAC_MAX_DIGESTSIZE); + off = ring; /* source address */ @@ -210,21 +211,25 @@ int seg6_hmac_compute(struct seg6_hmac_info *hinfo, struct ipv6_sr_hdr *hdr, off += 16; } - dgsize = __do_hmac(hinfo, ring, plen, tmp_out, - SEG6_HMAC_MAX_DIGESTSIZE); - local_bh_enable(); + err = crypto_ahash_update(hp.req); + if (err) + goto err_put_pool; - if (dgsize < 0) - return dgsize; + err = crypto_ahash_final(hp.req); + if (err) + goto err_put_pool; wrsize = SEG6_HMAC_FIELD_LEN; if (wrsize > dgsize) wrsize = dgsize; memset(output, 0, SEG6_HMAC_FIELD_LEN); - memcpy(output, tmp_out, wrsize); + memcpy(output, hp.base.scratch, wrsize); - return 0; +err_put_pool: + crypto_pool_put(); + + return err; } EXPORT_SYMBOL(seg6_hmac_compute); @@ -291,12 +296,24 @@ EXPORT_SYMBOL(seg6_hmac_info_lookup); int seg6_hmac_info_add(struct net *net, u32 key, struct seg6_hmac_info *hinfo) { struct seg6_pernet_data *sdata = seg6_pernet(net); - int err; + struct seg6_hmac_algo *algo; + int ret; - err = rhashtable_lookup_insert_fast(&sdata->hmac_infos, &hinfo->node, + algo = __hmac_get_algo(hinfo->alg_id); + if (!algo) + return -ENOENT; + + ret = crypto_pool_alloc_ahash(algo->name); + if (ret < 0) + return ret; + algo->crypto_pool_id = ret; + + ret = rhashtable_lookup_insert_fast(&sdata->hmac_infos, &hinfo->node, rht_params); + if (ret) + crypto_pool_release(algo->crypto_pool_id); - return err; + return ret; } EXPORT_SYMBOL(seg6_hmac_info_add); @@ -304,6 +321,7 @@ int seg6_hmac_info_del(struct net *net, u32 key) { struct seg6_pernet_data *sdata = seg6_pernet(net); struct seg6_hmac_info *hinfo; + struct seg6_hmac_algo *algo; int err = -ENOENT; hinfo = rhashtable_lookup_fast(&sdata->hmac_infos, &key, rht_params); @@ -315,6 +333,12 @@ int seg6_hmac_info_del(struct net *net, u32 key) if (err) goto out; + algo = __hmac_get_algo(hinfo->alg_id); + if (algo) + crypto_pool_release(algo->crypto_pool_id); + else + WARN_ON_ONCE(1); + seg6_hinfo_release(hinfo); out: @@ -348,56 +372,9 @@ int seg6_push_hmac(struct net *net, struct in6_addr *saddr, } EXPORT_SYMBOL(seg6_push_hmac); -static int seg6_hmac_init_algo(void) -{ - struct seg6_hmac_algo *algo; - struct crypto_shash *tfm; - struct shash_desc *shash; - int i, alg_count, cpu; - - alg_count = ARRAY_SIZE(hmac_algos); - - for (i = 0; i < alg_count; i++) { - struct crypto_shash **p_tfm; - int shsize; - - algo = &hmac_algos[i]; - algo->tfms = alloc_percpu(struct crypto_shash *); - if (!algo->tfms) - return -ENOMEM; - - for_each_possible_cpu(cpu) { - tfm = crypto_alloc_shash(algo->name, 0, 0); - if (IS_ERR(tfm)) - return PTR_ERR(tfm); - p_tfm = per_cpu_ptr(algo->tfms, cpu); - *p_tfm = tfm; - } - - p_tfm = raw_cpu_ptr(algo->tfms); - tfm = *p_tfm; - - shsize = sizeof(*shash) + crypto_shash_descsize(tfm); - - algo->shashs = alloc_percpu(struct shash_desc *); - if (!algo->shashs) - return -ENOMEM; - - for_each_possible_cpu(cpu) { - shash = kzalloc_node(shsize, GFP_KERNEL, - cpu_to_node(cpu)); - if (!shash) - return -ENOMEM; - *per_cpu_ptr(algo->shashs, cpu) = shash; - } - } - - return 0; -} - int __init seg6_hmac_init(void) { - return seg6_hmac_init_algo(); + return crypto_pool_reserve_scratch(SEG6_HMAC_MAX_DIGESTSIZE); } int __net_init seg6_hmac_net_init(struct net *net) @@ -407,29 +384,6 @@ int __net_init seg6_hmac_net_init(struct net *net) return rhashtable_init(&sdata->hmac_infos, &rht_params); } -void seg6_hmac_exit(void) -{ - struct seg6_hmac_algo *algo = NULL; - int i, alg_count, cpu; - - alg_count = ARRAY_SIZE(hmac_algos); - for (i = 0; i < alg_count; i++) { - algo = &hmac_algos[i]; - for_each_possible_cpu(cpu) { - struct crypto_shash *tfm; - struct shash_desc *shash; - - shash = *per_cpu_ptr(algo->shashs, cpu); - kfree(shash); - tfm = *per_cpu_ptr(algo->tfms, cpu); - crypto_free_shash(tfm); - } - free_percpu(algo->tfms); - free_percpu(algo->shashs); - } -} -EXPORT_SYMBOL(seg6_hmac_exit); - void __net_exit seg6_hmac_net_exit(struct net *net) { struct seg6_pernet_data *sdata = seg6_pernet(net); From patchwork Thu Oct 27 20:43:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022725 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 9DD9FFA3747 for ; Thu, 27 Oct 2022 20:44:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236572AbiJ0UoZ (ORCPT ); Thu, 27 Oct 2022 16:44:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59360 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236893AbiJ0UoW (ORCPT ); Thu, 27 Oct 2022 16:44:22 -0400 Received: from mail-wr1-x436.google.com (mail-wr1-x436.google.com [IPv6:2a00:1450:4864:20::436]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3B07592589 for ; Thu, 27 Oct 2022 13:44:06 -0700 (PDT) Received: by mail-wr1-x436.google.com with SMTP id k8so4192960wrh.1 for ; Thu, 27 Oct 2022 13:44:06 -0700 (PDT) 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=Tx4iE3mqN7DCr9Gvb0u5N1VFE5hSDH0Bo2werB0KHqI=; b=k/PqpZoEJSu3wMUA6lPEaH88OBn0IqxNQfwO/2dDSQ+1EcR22Qg34IPOk63pQcxTlY g2GjidiGSLKBI0OBRwrz9lIAOerZjca92TFAR2ca4Fjbu5ZuZgRug902F3LgLCo6qSbt M1le4bcsFsq/NrGwroXAqyGIiIErdhlA7i/TdPXjd83fsl6GNslOp67DTt5Ak2o5Kxc9 vZ8YIR3ByEVGYG+2OYhAKZFJrFa42EvIt24MV0hKSnrSVlzCg8kV1ufd93tUxgtQlCIx cv4I4DLBFVVo6FS7SNxweD4eWm8jDvOOT3ERxFHSBwgA5xoLaIrSBCr1Gu/Xaw3KS54k eXgw== 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=Tx4iE3mqN7DCr9Gvb0u5N1VFE5hSDH0Bo2werB0KHqI=; b=OhtArDzaB3KZtSrw/Ssrbz3l7uDR46ER6N15feOuqf2fhfViY4w9cshsdC2aGIrN2v h2dTAcPU8vwKhbaAmnVehT4eIdBh6E3G4SNVbs21iZjM2rU1nfBocPViaTCmTIZvdKg5 aLLKe1KXAgMeqoBcHDrkE33eWyeGozhocF401WyldO4c+74qOgid+CZW7eZpefNFQ3wV krTakGcxvLCU+1aOBMTuXlt0eeLsaEvwM+YiafvJxhTWJyh862oCa6hiIJnH1yUTBEag dPlIi//L9oSeSLNsMG9C4Cvx3zk6b7XgCKPrnkDlf2vw5TYhsmtzwMkkrsH0ZtqRc+yq hBhQ== X-Gm-Message-State: ACrzQf24AN7hQGL/bnIB+ni8297BnrShCKRjPXl+Rkk7byrWCOrgkzrI JYSWuhtQE3AXLTeGf3rUAo6Dew== X-Google-Smtp-Source: AMsMyM4ssOcJNh0i5IJBtAtszTpkSoBGAqeg5vcYzmvVp0UgUym6nlNFgCiBwnX0xmpjrZqx+B/OAg== X-Received: by 2002:adf:e510:0:b0:235:de50:72ff with SMTP id j16-20020adfe510000000b00235de5072ffmr25535605wrm.100.1666903445792; Thu, 27 Oct 2022 13:44:05 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:05 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 07/36] tcp: Add TCP-AO config and structures Date: Thu, 27 Oct 2022 21:43:18 +0100 Message-Id: <20221027204347.529913-8-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 12 ++++++ 5 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 include/net/tcp_ao.h diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 41b1da621a45..fd248875b0a9 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -434,13 +434,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 03dbe1940fec..3395a925dc6e 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..39b3fc31e5a1 --- /dev/null +++ b/include/net/tcp_ao.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _TCP_AO_H +#define _TCP_AO_H + +#define TCP_AO_MAX_HASH_SIZE 64 +#define TCP_AO_KEY_ALIGN 1 +#define __tcp_ao_key_align __aligned(TCP_AO_KEY_ALIGN) + +union tcp_ao_addr { + struct in_addr a4; +#if IS_ENABLED(CONFIG_IPV6) + struct in6_addr a6; +#endif +}; + +struct tcp_ao_hdr { + u8 kind; + u8 length; + u8 keyid; + u8 rnext_keyid; +}; + +struct tcp_ao_key { + struct hlist_node node; + union tcp_ao_addr addr; + u8 key[TCP_AO_MAXKEYLEN] __tcp_ao_key_align; + unsigned int crypto_pool_id; + u16 port; + u8 prefixlen; + u8 family; + u8 keylen; + u8 keyflags; + u8 sndid; + u8 rcvid; + u8 maclen; + u8 digest_size; + struct rcu_head rcu; + u8 traffic_keys[]; +}; + +static inline u8 *rcv_other_key(struct tcp_ao_key *key) +{ + return key->traffic_keys; +} + +static inline u8 *snd_other_key(struct tcp_ao_key *key) +{ + return key->traffic_keys + key->digest_size; +} + +static inline int tcp_ao_maclen(const struct tcp_ao_key *key) +{ + return key->maclen; +} + +static inline int tcp_ao_sizeof_key(const struct tcp_ao_key *key) +{ + return sizeof(struct tcp_ao_key) + (TCP_AO_MAX_HASH_SIZE << 1); +} + +static inline int tcp_ao_len(const struct tcp_ao_key *key) +{ + return tcp_ao_maclen(key) + sizeof(struct tcp_ao_hdr); +} + +static inline unsigned int tcp_ao_digest_size(struct tcp_ao_key *key) +{ + return key->digest_size; +} + +struct tcp_ao_info { + struct hlist_head head; + struct rcu_head rcu; + /* current_key and rnext_key aren't maintained on listen sockets. + * Their purpose is to cache keys on established connections, + * saving needless lookups. Never dereference any of them from + * listen sockets. + */ + struct tcp_ao_key *volatile current_key; + struct tcp_ao_key *rnext_key; + u8 ao_flags; + __be32 lisn; + __be32 risn; + u32 snd_sne; + u32 snd_sne_seq; + u32 rcv_sne; + u32 rcv_sne_seq; +}; + +#endif /* _TCP_AO_H */ diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 8fc09e8638b3..849bbf2d3c38 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -342,6 +342,8 @@ struct tcp_diag_md5sig { __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; }; +#define TCP_AO_MAXKEYLEN 80 + /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index c341864e4398..4a78f3e0199e 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -731,6 +731,18 @@ config DEFAULT_TCP_CONG default "bbr" if DEFAULT_BBR default "cubic" +config TCP_AO + bool "TCP: Authentication Option (RFC5925)" + select CRYPTO + select CRYPTO_POOL + depends on 64BIT # seq-number extension needs WRITE_ONCE(u64) + default y + help + TCP-AO specifies the use of stronger Message Authentication Codes (MACs), + protects against replays for long-lived TCP connections, and + provides more details on the association of security with TCP + connections than TCP MD5 (See RFC5925) + config TCP_MD5SIG bool "TCP: MD5 Signature Option support (RFC2385)" select CRYPTO_POOL From patchwork Thu Oct 27 20:43:19 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022729 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 AEA07FA3740 for ; Thu, 27 Oct 2022 20:45:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237042AbiJ0UpG (ORCPT ); Thu, 27 Oct 2022 16:45:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59466 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236997AbiJ0UoX (ORCPT ); Thu, 27 Oct 2022 16:44:23 -0400 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 D4E5E92596 for ; Thu, 27 Oct 2022 13:44:08 -0700 (PDT) Received: by mail-wm1-x32d.google.com with SMTP id c3-20020a1c3503000000b003bd21e3dd7aso5044841wma.1 for ; Thu, 27 Oct 2022 13:44:08 -0700 (PDT) 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=Pyg7zVeT0DLAd3wO3N/RfT0Ly7CDFBKTiWa/B68qrUQ=; b=MWnD5JC/7TXe3+jhVA3MvYSvXXbAYsRETmjA8UUTXNfDCZc/kE24TxoWQW5KRASUfL v5DfZtQmpa3iSsKYX8czhYXXoX89lut0JC6QtZ5qhL0d3es2PxWib3fmXVQgwmtvNwkq VXHGXOoGYQfXueUpzJxN3i8T95APWJh/byhZft/Lo9UIZeBvmejD9Jm2n9b3rShMgzsq JGyb35XaZ6Hr4LDHTwfO8TsxKCPWD2/OhxaoDwc9CW7OcpmaU9zxHUxc7oOjwMYHV86p SaLSIKBLeBg8jgJjXK54h1YzeQDuJ/PCHN2UFjqv8GR9Z7ptUOtBq1KGXNxWl6exyGM+ x3tA== 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=Pyg7zVeT0DLAd3wO3N/RfT0Ly7CDFBKTiWa/B68qrUQ=; b=i7hcwzLj4Rg6s09jzCDAsD8dIQtYgP7Om8qzjpI08ooeBVYRMpT+EOj6lfhturpKYb qbojs2KXwrebSrITuvRelVspBQ38ngeQ61Jdkh+mxV5wApYKB+lM7JixD9MKuPmUYJUG yNNn+9HhJjhAcsDoxRk0RuTZNxgm3d5pKXnxjzK8rG5vxdwvmmiH50QtjvA8IsDNzONF t/qvedOAsFhZlLr5jwJZZWVTl8muRNYKAUGCO6wwro1TBa8o8tOQbGiMK8YqqBPpWgrn pUH/sH/QXrC8ZmrEFtJc0iwmMbTLyEA1QAthAJPVVVwjqTV7jgdEQFOUN+WpBEtwSag8 CSIg== X-Gm-Message-State: ACrzQf0bOvpslvJN2fnn2UPhuNGNqarp5RtdLv/wZS+6E4a/w6lGqbzB /4gLR4g3IsjXsiLR5CbyAjDUOQ== X-Google-Smtp-Source: AMsMyM5qbBfieSbg0mRyy9SqfmSIjB0AoeKYWt1/p/7GcE5wWl+CO4lYDlEwy7EELiyTA8pXEDj1jA== X-Received: by 2002:a1c:2543:0:b0:3c6:b7bd:a474 with SMTP id l64-20020a1c2543000000b003c6b7bda474mr6846655wml.95.1666903447235; Thu, 27 Oct 2022 13:44:07 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:06 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 08/36] net/tcp: Introduce TCP_AO setsockopt()s Date: Thu, 27 Oct 2022 21:43:19 +0100 Message-Id: <20221027204347.529913-9-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 35 ++ net/ipv4/Makefile | 1 + net/ipv4/tcp.c | 17 + net/ipv4/tcp_ao.c | 849 +++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 10 +- net/ipv6/Makefile | 1 + net/ipv6/tcp_ao.c | 20 + net/ipv6/tcp_ipv6.c | 39 +- 11 files changed, 999 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 3395a925dc6e..dc0ceeb20886 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2064,6 +2064,12 @@ struct tcp_sock_af_ops { sockptr_t optval, int optlen); #endif +#ifdef CONFIG_TCP_AO + int (*ao_parse)(struct sock *sk, + int optname, + sockptr_t optval, + int optlen); +#endif }; struct tcp_request_sock_ops { diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 39b3fc31e5a1..6d0d30e5542b 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -87,4 +87,19 @@ struct tcp_ao_info { u32 rcv_sne_seq; }; +#ifdef CONFIG_TCP_AO +int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, + sockptr_t optval, int optlen); +void tcp_ao_destroy_sock(struct sock *sk); +/* ipv4 specific functions */ +int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen); +/* ipv6 specific functions */ +int tcp_v6_parse_ao(struct sock *sk, int cmd, + sockptr_t optval, int optlen); +#else +static inline void tcp_ao_destroy_sock(struct sock *sk) +{ +} +#endif + #endif /* _TCP_AO_H */ diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 849bbf2d3c38..5369458ae89f 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -129,6 +129,9 @@ enum { #define TCP_TX_DELAY 37 /* delay outgoing packets by XX usec */ +#define TCP_AO 38 /* (Add/Set MKT) */ +#define TCP_AO_DEL 39 /* (Delete MKT) */ +#define TCP_AO_MOD 40 /* (Modify MKT) */ #define TCP_REPAIR_ON 1 #define TCP_REPAIR_OFF 0 @@ -344,6 +347,38 @@ struct tcp_diag_md5sig { #define TCP_AO_MAXKEYLEN 80 +#define TCP_AO_CMDF_CURR (1 << 0) /* Only checks field sndid */ +#define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */ + +struct tcp_ao { /* setsockopt(TCP_AO) */ + struct __kernel_sockaddr_storage tcpa_addr; + char tcpa_alg_name[64]; + __u16 tcpa_flags; + __u8 tcpa_prefix; + __u8 tcpa_sndid; + __u8 tcpa_rcvid; + __u8 tcpa_maclen; + __u8 tcpa_keyflags; + __u8 tcpa_keylen; + __u8 tcpa_key[TCP_AO_MAXKEYLEN]; +} __attribute__((aligned(8))); + +struct tcp_ao_del { /* setsockopt(TCP_AO_DEL) */ + struct __kernel_sockaddr_storage tcpa_addr; + __u16 tcpa_flags; + __u8 tcpa_prefix; + __u8 tcpa_sndid; + __u8 tcpa_rcvid; + __u8 tcpa_current; + __u8 tcpa_rnext; +} __attribute__((aligned(8))); + +struct tcp_ao_mod { /* setsockopt(TCP_AO_MOD) */ + __u16 tcpa_flags; + __u8 tcpa_current; + __u8 tcpa_rnext; +} __attribute__((aligned(8))); + /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index bbdd9c44f14e..6d0b3e228b8a 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_NETLABEL) += cipso_ipv4.o obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ xfrm4_output.o xfrm4_protocol.o +obj-$(CONFIG_TCP_AO) += tcp_ao.o ifeq ($(CONFIG_BPF_JIT),y) obj-$(CONFIG_BPF_SYSCALL) += bpf_tcp_ca.o diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index d6f494f2c350..27662c9b0870 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3712,6 +3712,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..a573b630ed9d --- /dev/null +++ b/net/ipv4/tcp_ao.c @@ -0,0 +1,849 @@ +// 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 + +static struct tcp_ao_key *tcp_ao_do_lookup_rcvid(struct sock *sk, u8 keyid) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_ao_key *key; + struct tcp_ao_info *ao; + + ao = rcu_dereference_check(tp->ao_info, lockdep_sock_is_held(sk)); + + if (!ao) + return NULL; + + hlist_for_each_entry_rcu(key, &ao->head, node) { + if (key->rcvid == keyid) + return key; + } + return NULL; +} + +struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid) +{ + struct tcp_ao_key *key; + struct tcp_ao_info *ao; + + ao = rcu_dereference_check(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (!ao) + return NULL; + + hlist_for_each_entry_rcu(key, &ao->head, node) { + if (key->sndid == keyid) + return key; + } + return NULL; +} + +static inline int ipv4_prefix_cmp(const struct in_addr *addr1, + const struct in_addr *addr2, + unsigned int prefixlen) +{ + __be32 mask = inet_make_mask(prefixlen); + + if ((addr1->s_addr & mask) == (addr2->s_addr & mask)) + return 0; + return ((addr1->s_addr & mask) > (addr2->s_addr & mask)) ? 1 : -1; +} + +static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, + const union tcp_ao_addr *addr, u8 prefixlen, + int family, int sndid, int rcvid, u16 port) +{ + if (sndid >= 0 && key->sndid != sndid) + return (key->sndid > sndid) ? 1 : -1; + if (rcvid >= 0 && key->rcvid != rcvid) + return (key->rcvid > rcvid) ? 1 : -1; + if (port != 0 && key->port != 0 && port != key->port) + return (key->port > port) ? 1 : -1; + + if (family == AF_UNSPEC) + return 0; + if (key->family != family) + return (key->family > family) ? 1 : -1; + + if (family == AF_INET) { + if (key->addr.a4.s_addr == INADDR_ANY) + return 0; + if (addr->a4.s_addr == INADDR_ANY) + return 0; + return ipv4_prefix_cmp(&key->addr.a4, &addr->a4, prefixlen); +#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); + + crypto_pool_release(key->crypto_pool_id); + kfree(key); +} + +void tcp_ao_destroy_sock(struct sock *sk) +{ + struct tcp_ao_info *ao; + struct tcp_ao_key *key; + struct hlist_node *n; + + ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1); + tcp_sk(sk)->ao_info = NULL; + + if (!ao) + return; + + hlist_for_each_entry_safe(key, n, &ao->head, node) { + hlist_del_rcu(&key->node); + atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); + call_rcu(&key->rcu, tcp_ao_key_free_rcu); + } + + kfree_rcu(ao, rcu); +} + +static int tcp_ao_current_rnext(struct sock *sk, u16 tcpa_flags, + u8 tcpa_sndid, u8 tcpa_rcvid) +{ + struct tcp_ao_info *ao_info; + struct tcp_ao_key *key; + + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if ((tcpa_flags & (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT)) && !ao_info) + return -EINVAL; + + if (tcpa_flags & TCP_AO_CMDF_CURR) { + key = tcp_ao_do_lookup_sndid(sk, tcpa_sndid); + if (!key) + return -ENOENT; + if (ao_info->current_key != key) + WRITE_ONCE(ao_info->current_key, key); + } + + if (tcpa_flags & TCP_AO_CMDF_NEXT) { + key = tcp_ao_do_lookup_rcvid(sk, tcpa_rcvid); + if (!key) + return -ENOENT; + if (ao_info->rnext_key != key) + WRITE_ONCE(ao_info->rnext_key, key); + } + + return 0; +} + +static int tcp_ao_verify_port(struct sock *sk, u16 port) +{ + struct inet_sock *inet = inet_sk(sk); + + if (port != 0) /* FIXME */ + return -EINVAL; + + /* Check that MKT port is consistent with socket */ + if (port != 0 && inet->inet_dport != 0 && port != inet->inet_dport) + return -EINVAL; + + return 0; +} + +static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao *cmd, + union tcp_md5_addr **addr, u16 *port) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->tcpa_addr; + struct inet_sock *inet = inet_sk(sk); + + if (sin->sin_family != AF_INET) + return -EINVAL; + + if (tcp_ao_verify_port(sk, ntohs(sin->sin_port))) + return -EINVAL; + + /* Check prefix and trailing 0's in addr */ + if (cmd->tcpa_prefix != 0) { + __be32 mask; + + if (sin->sin_addr.s_addr == INADDR_ANY) + return -EINVAL; + if (cmd->tcpa_prefix > 32) + return -EINVAL; + + mask = inet_make_mask(cmd->tcpa_prefix); + if (sin->sin_addr.s_addr & ~mask) + return -EINVAL; + + /* Check that MKT address is consistent with socket */ + if (inet->inet_daddr != INADDR_ANY && + (inet->inet_daddr & mask) != sin->sin_addr.s_addr) + return -EINVAL; + } else { + if (sin->sin_addr.s_addr != INADDR_ANY) + return -EINVAL; + } + + *addr = (union tcp_md5_addr *)&sin->sin_addr; + *port = ntohs(sin->sin_port); + return 0; +} + +static int tcp_ao_parse_crypto(struct tcp_ao *cmd, struct tcp_ao_key *key) +{ + unsigned int syn_tcp_option_space; + struct crypto_pool_ahash hp; + bool is_kdf_aes_128_cmac = false; + struct crypto_ahash *tfm; + int err, pool_id; + + /* Force null-termination of tcpa_alg_name */ + cmd->tcpa_alg_name[ARRAY_SIZE(cmd->tcpa_alg_name) - 1] = '\0'; + + /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ + if (!strcmp("cmac(aes128)", cmd->tcpa_alg_name)) { + strcpy(cmd->tcpa_alg_name, "cmac(aes)"); + is_kdf_aes_128_cmac = (cmd->tcpa_keylen != 16); + } + + key->maclen = cmd->tcpa_maclen ?: 12; /* 12 is the default in RFC5925 */ + + /* Check: maclen + tcp-ao header <= (MAX_TCP_OPTION_SPACE - mss + * - tstamp - wscale - sackperm), + * see tcp_syn_options(), tcp_synack_options(), commit 33ad798c924b. + * + * In order to allow D-SACK with TCP-AO, the header size should be: + * (MAX_TCP_OPTION_SPACE - TCPOLEN_TSTAMP_ALIGNED + * - TCPOLEN_SACK_BASE_ALIGNED + * - 2 * TCPOLEN_SACK_PERBLOCK) = 8 (maclen = 4), + * see tcp_established_options(). + * + * RFC5925, 2.2: + * Typical MACs are 96-128 bits (12-16 bytes), but any length + * that fits in the header of the segment being authenticated + * is allowed. + * + * RFC5925, 7.6: + * TCP-AO continues to consume 16 bytes in non-SYN segments, + * leaving a total of 24 bytes for other options, of which + * the timestamp consumes 10. This leaves 14 bytes, of which 10 + * are used for a single SACK block. When two SACK blocks are used, + * such as to handle D-SACK, a smaller TCP-AO MAC would be required + * to make room for the additional SACK block (i.e., to leave 18 + * bytes for the D-SACK variant of the SACK option) [RFC2883]. + * Note that D-SACK is not supportable in TCP MD5 in the presence + * of timestamps, because TCP MD5’s MAC length is fixed and too + * large to leave sufficient option space. + */ + syn_tcp_option_space = MAX_TCP_OPTION_SPACE; + syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED; + syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED; + syn_tcp_option_space -= TCPOLEN_SACKPERM_ALIGNED; + if (tcp_ao_len(key) > syn_tcp_option_space) + return -EMSGSIZE; + + key->keylen = cmd->tcpa_keylen; + memcpy(key->key, cmd->tcpa_key, cmd->tcpa_keylen); + + pool_id = crypto_pool_alloc_ahash(cmd->tcpa_alg_name); + if (pool_id < 0) + return pool_id; + + if (is_kdf_aes_128_cmac) { + err = crypto_pool_reserve_scratch(16); + if (err) + goto err_free_pool; + } + + err = crypto_pool_get(pool_id, (struct crypto_pool *)&hp); + if (err) + goto err_free_pool; + + tfm = crypto_ahash_reqtfm(hp.req); + if (crypto_ahash_alignmask(tfm) > TCP_AO_KEY_ALIGN) { + err = -EOPNOTSUPP; + goto err_put_pool; + } + + if (is_kdf_aes_128_cmac) { + void *scratch = hp.base.scratch; + struct scatterlist sg; + + /* Using zero-key of 16 bytes as described in RFC5926 */ + memset(scratch, 0, 16); + sg_init_one(&sg, cmd->tcpa_key, cmd->tcpa_keylen); + + err = crypto_ahash_setkey(tfm, scratch, 16); + if (err) + goto err_put_pool; + + err = crypto_ahash_init(hp.req); + if (err) + goto err_put_pool; + + ahash_request_set_crypt(hp.req, &sg, key->key, cmd->tcpa_keylen); + err = crypto_ahash_update(hp.req); + if (err) + goto err_put_pool; + + err |= crypto_ahash_final(hp.req); + if (err) + goto err_put_pool; + key->keylen = 16; + } + + err = crypto_ahash_setkey(tfm, key->key, key->keylen); + if (err) + goto err_put_pool; + + key->digest_size = crypto_ahash_digestsize(tfm); + crypto_pool_put(); + + err = crypto_pool_reserve_scratch(sizeof(struct tcphdr) + + sizeof(struct tcp_ao_hdr) + + key->digest_size); + if (err) + goto err_free_pool; + + if (key->digest_size > TCP_AO_MAX_HASH_SIZE) { + err = -ENOBUFS; + goto err_free_pool; + } + if (key->maclen > key->digest_size) { + err = -EINVAL; + goto err_free_pool; + } + + key->crypto_pool_id = pool_id; + return 0; + +err_put_pool: + crypto_pool_put(); +err_free_pool: + crypto_pool_release(pool_id); + return err; +} + +/* tcp_ao_mkt_overlap_v4() assumes cmd already went through tcp_ao_verify_ipv4. + * RFC5925 3.1 The IDs of MKTs MUST NOT overlap where their TCP connection + * identifiers overlap. + */ +static bool tcp_ao_mkt_overlap_v4(struct tcp_ao *cmd, + struct tcp_ao_info *ao_info) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->tcpa_addr; + __be32 addr = sin->sin_addr.s_addr; + __u8 prefix = cmd->tcpa_prefix; + __u16 port = ntohs(sin->sin_port); + __u8 sndid = cmd->tcpa_sndid; + __u8 rcvid = cmd->tcpa_rcvid; + struct tcp_ao_key *key; + + /* Check for TCP connection identifiers overlap */ + + hlist_for_each_entry_rcu(key, &ao_info->head, node) { + __be32 key_addr; + __be32 mask; + + /* Check for overlapping ids */ + if (key->sndid != sndid && key->rcvid != rcvid) + continue; + + key_addr = key->addr.a4.s_addr; + mask = inet_make_mask(min(prefix, key->prefixlen)); + + /* Check for overlapping addresses */ + if (addr == INADDR_ANY || key_addr == INADDR_ANY || + (addr & mask) == (key_addr & mask)) { + /* Check for overlapping ports */ + if (port == 0 || key->port == 0 || port == key->port) + return true; + } + } + + return false; +} + +#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 (family == AF_INET) + ret = tcp_ao_verify_ipv4(sk, &cmd, &addr, &port); + else + ret = tcp_ao_verify_ipv6(sk, &cmd, &addr, &port, &family); + if (ret) + return ret; + + if (cmd.tcpa_keyflags & ~TCP_AO_KEYF_ALL) + return -EINVAL; + + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + + if (!ao_info) { + ao_info = tcp_ao_alloc_info(GFP_KERNEL, NULL); + if (!ao_info) + return -ENOMEM; + first = true; + } else { + if (family == AF_INET) { + if (tcp_ao_mkt_overlap_v4(&cmd, ao_info)) + return -EEXIST; + } else { + if (tcp_ao_mkt_overlap_v6(&cmd, ao_info)) + return -EEXIST; + } + } + + /* TODO: We should add twice the key->diget_size instead of the max + * so rework this in a way to know the digest_size before allocating + * the tcp_ao_key struct. + */ + size = sizeof(struct tcp_ao_key) + (TCP_AO_MAX_HASH_SIZE << 1); + key = sock_kmalloc(sk, size, GFP_KERNEL); + if (!key) { + ret = -ENOMEM; + goto err_free_ao; + } + + INIT_HLIST_NODE(&key->node); + memcpy(&key->addr, addr, (family == AF_INET) ? sizeof(struct in_addr) : + sizeof(struct in6_addr)); + key->port = port; + key->prefixlen = cmd.tcpa_prefix; + key->family = family; + key->keyflags = cmd.tcpa_keyflags; + key->sndid = cmd.tcpa_sndid; + key->rcvid = cmd.tcpa_rcvid; + + ret = tcp_ao_parse_crypto(&cmd, key); + if (ret < 0) + goto err_free_sock; + + tcp_ao_link_mkt(ao_info, key); + if (first) { + sk_gso_disable(sk); + rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); + } + + /* Can't fail: the key with sndid/rcvid was just added */ + WARN_ON_ONCE(tcp_ao_current_rnext(sk, cmd.tcpa_flags, + cmd.tcpa_sndid, cmd.tcpa_rcvid)); + return 0; + +err_free_sock: + atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); + kfree(key); +err_free_ao: + if (first) + kfree(ao_info); + return ret; +} + +static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_key *key, + struct tcp_ao_info *ao_info, + struct tcp_ao_del *cmd) +{ + int err; + + hlist_del_rcu(&key->node); + + /* At this moment another CPU could have looked this key up + * while it was unlinked from the list. Wait for RCU grace period, + * after which the key is off-list and can't be looked up again; + * the rx path [just before RCU came] might have used it and set it + * as current_key (very unlikely). + */ + synchronize_rcu(); + err = tcp_ao_current_rnext(sk, cmd->tcpa_flags, + cmd->tcpa_current, cmd->tcpa_rnext); + if (err) + goto add_key; + + if (unlikely(READ_ONCE(ao_info->current_key) == key || + READ_ONCE(ao_info->rnext_key) == key)) { + err = -EBUSY; + goto add_key; + } + + atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); + call_rcu(&key->rcu, tcp_ao_key_free_rcu); + + return 0; +add_key: + hlist_add_head_rcu(&key->node, &ao_info->head); + return err; +} + +static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, + sockptr_t optval, int optlen) +{ + struct tcp_ao_info *ao_info; + struct tcp_ao_key *key; + struct tcp_ao_del cmd; + int err; + union tcp_md5_addr *addr; + __u8 prefix; + __be16 port; + int addr_len; + + if (optlen < sizeof(cmd)) + return -EINVAL; + + err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); + if (err) + return err; + + if (cmd.tcpa_flags & ~TCP_AO_CMDF_DEL_VALID) + return -EINVAL; + + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (!ao_info) + return -ENOENT; + + if (family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.tcpa_addr; + + addr = (union tcp_md5_addr *)&sin->sin_addr; + addr_len = sizeof(struct in_addr); + port = ntohs(sin->sin_port); + } else { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpa_addr; + struct in6_addr *addr6 = &sin6->sin6_addr; + + if (ipv6_addr_v4mapped(addr6)) { + addr = (union tcp_md5_addr *)&addr6->s6_addr32[3]; + addr_len = sizeof(struct in_addr); + family = AF_INET; + } else { + addr = (union tcp_md5_addr *)addr6; + addr_len = sizeof(struct in6_addr); + } + port = ntohs(sin6->sin6_port); + } + prefix = cmd.tcpa_prefix; + + /* We could choose random present key here for current/rnext + * but that's less predictable. Let's be strict and don't + * allow removing a key that's in use. RFC5925 doesn't + * specify how-to coordinate key removal, but says: + * "It is presumed that an MKT affecting a particular + * connection cannot be destroyed during an active connection" + */ + hlist_for_each_entry_rcu(key, &ao_info->head, node) { + if (cmd.tcpa_sndid != key->sndid || + cmd.tcpa_rcvid != key->rcvid) + continue; + + if (family != key->family || + prefix != key->prefixlen || + port != key->port || + memcmp(addr, &key->addr, addr_len)) + continue; + + return tcp_ao_delete_key(sk, key, ao_info, &cmd); + } + return -ENOENT; +} + +static int tcp_ao_mod_cmd(struct sock *sk, unsigned short int family, + sockptr_t optval, int optlen) +{ + struct tcp_ao_info *ao_info; + struct tcp_ao_mod cmd; + int err; + + if (optlen < sizeof(cmd)) + return -EINVAL; + + err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); + if (err) + return err; + + if (cmd.tcpa_flags & ~TCP_AO_CMDF_ADDMOD_VALID) + return -EINVAL; + + ao_info = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (!ao_info) + return -ENOENT; + /* TODO: make tcp_ao_current_rnext() and flags set atomic */ + return tcp_ao_current_rnext(sk, cmd.tcpa_flags, + cmd.tcpa_current, cmd.tcpa_rnext); +} + +int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, + sockptr_t optval, int optlen) +{ + if (WARN_ON_ONCE(family != AF_INET && family != AF_INET6)) + return -EOPNOTSUPP; + + switch (cmd) { + case TCP_AO: + return tcp_ao_add_cmd(sk, family, optval, optlen); + case TCP_AO_DEL: + return tcp_ao_del_cmd(sk, family, optval, optlen); + case TCP_AO_MOD: + return tcp_ao_mod_cmd(sk, family, optval, optlen); + default: + WARN_ON_ONCE(1); + return -EINVAL; + } +} + +int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) +{ + return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen); +} + diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 08ffba6c33f4..3bafa31b066d 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..f9f242a7e0f2 --- /dev/null +++ b/net/ipv6/tcp_ao.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * INET An implementation of the TCP Authentication Option (TCP-AO). + * See RFC5925. + * + * Authors: Dmitry Safonov + * Francesco Ruggeri + * Salam Noureddine + */ +#include +#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 2c5c0ca7de67..ba60c1273517 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -77,16 +77,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. @@ -244,7 +237,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 @@ -257,7 +250,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; @@ -783,7 +776,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, @@ -1243,7 +1242,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 @@ -1907,11 +1906,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 @@ -1933,11 +1937,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 @@ -1952,7 +1961,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 Thu Oct 27 20:43:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022728 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 3EF7FFA3740 for ; Thu, 27 Oct 2022 20:45:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237035AbiJ0UpC (ORCPT ); Thu, 27 Oct 2022 16:45:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59464 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236994AbiJ0UoX (ORCPT ); Thu, 27 Oct 2022 16:44:23 -0400 Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4960C92F6C for ; Thu, 27 Oct 2022 13:44:09 -0700 (PDT) Received: by mail-wr1-x42d.google.com with SMTP id w14so4154679wru.8 for ; Thu, 27 Oct 2022 13:44:09 -0700 (PDT) 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=zgezUKU9eYVuaEtomd8esL+2n06uzlCfbXGBDPmLvss=; b=ZkZhSFlHlE7ltJujnu9AmKFElZM2dK3HgVcvPNbyA1Wd9VVS5nk5A8FhrTz9hyEI8A awErbeApUUN6Unf9kE2Zfuhyv9G+g8+yaR7wEBgyfk2d19Gzrg+6VPADv4xOWXGMo2ee RCfJo4n+mDN8xJmUBUuTCvGCPX1aXFisxVI0aYhtRd6t6K75oIP1ttuB12EzPopncqcD yLx44ZScNWFMhkFh7I6Ubw1Ol9JSUsYjnqgKImyFWLZnEn1CflfpQ0qdvjR3eVU7VMaO dQUpYT8LymNtEiZYl8emLtJyEsBEXr0lEvf+S5lge83WdM6iTxcFVw7/hclm+OsErdJa KUMg== 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=zgezUKU9eYVuaEtomd8esL+2n06uzlCfbXGBDPmLvss=; b=2vV7vx1AjaSxFRzs2zThPWtFfwDivS4xyoJhqPPriqc2LB3wG3+Ts12meOBMEeWPQg 2LmOKJ6QCN90cQEQzTyLiDb0ylqw7w5loaE8gW51lxOVRsvQGw0rGJWGnU5dfTxNgY0a 8+j4ry4XYlHkRwS0pyToUd0lmV1xrVeTeJjZP+NnMhl8erwFsw7uSz6xU0MMJa0pcknw 6FJwzGePeJT/mvXiDI8ZMjLkDSakLfaoRAZhTDPKqNDF9xX8EY9SGC4/bmr4OtzFYEJd lQL2g+nrJCDwDs/hUqhaYbLYy6spW9DdMxXZpCr0jmcErjYAh/Mdc7zWdCVDZ+CaAGbZ fyRA== X-Gm-Message-State: ACrzQf2oR8U5W0sXGneZhhrN7/vUjTYB7FVteRzYhHn7rZ2+23MUv6f6 8J220999UzWIw9pwN8yVRHsDMw== X-Google-Smtp-Source: AMsMyM4GkqApJN4JnViNhw5xLPNDYK4DvGTh2WGX6+/T3y371vhjODyMMoHeA1WbOTjY2Zh/qjYrNQ== X-Received: by 2002:a5d:64aa:0:b0:236:8dd2:b6ae with SMTP id m10-20020a5d64aa000000b002368dd2b6aemr5917640wrp.420.1666903448665; Thu, 27 Oct 2022 13:44:08 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:08 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 09/36] net/tcp: Prevent TCP-MD5 with TCP-AO being set Date: Thu, 27 Oct 2022 21:43:20 +0100 Message-Id: <20221027204347.529913-10-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 23 +++++++++++++++++++++-- include/net/tcp_ao.h | 15 +++++++++++++++ net/ipv4/tcp_ao.c | 12 ++++++++++++ net/ipv4/tcp_ipv4.c | 10 ++++++++-- net/ipv4/tcp_output.c | 22 ++++++++++++++++++++++ net/ipv6/tcp_ao.c | 17 +++++++++++++++++ net/ipv6/tcp_ipv6.c | 22 ++++++++++++++++++---- 7 files changed, 113 insertions(+), 8 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index dc0ceeb20886..e5ccc58562a7 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1680,14 +1680,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 @@ -1705,6 +1714,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, @@ -2069,6 +2085,9 @@ struct tcp_sock_af_ops { int optname, sockptr_t optval, int optlen); + struct tcp_ao_key *(*ao_lookup)(const struct sock *sk, + struct sock *addr_sk, + int sndid, int rcvid); #endif }; diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 6d0d30e5542b..c550f1a6f5fd 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -91,12 +91,27 @@ struct tcp_ao_info { int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, sockptr_t optval, int optlen); void tcp_ao_destroy_sock(struct sock *sk); +struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, + const union tcp_ao_addr *addr, + int family, int sndid, int rcvid, u16 port); /* ipv4 specific functions */ int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen); +struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, + int sndid, int rcvid); /* ipv6 specific functions */ +struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, + struct sock *addr_sk, + int sndid, int rcvid); int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen); #else +static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, + const union tcp_ao_addr *addr, + int family, int sndid, int rcvid, u16 port) +{ + return NULL; +} + static inline void tcp_ao_destroy_sock(struct sock *sk) { } diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index a573b630ed9d..27cf844ed8a5 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -181,6 +181,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) { @@ -627,6 +635,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 3bafa31b066d..2fb41e5e90ef 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1085,7 +1085,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; @@ -1104,7 +1104,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); @@ -1369,6 +1370,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 86e71c8c76bc..3ad0dcf98083 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3842,6 +3842,28 @@ int tcp_connect(struct sock *sk) tcp_call_bpf(sk, BPF_SOCK_OPS_TCP_CONNECT_CB, 0, NULL); +#if defined(CONFIG_TCP_MD5SIG) && defined(CONFIG_TCP_AO) + /* Has to be checked late, after setting daddr/saddr/ops */ + if (unlikely(rcu_dereference_protected(tp->md5sig_info, + lockdep_sock_is_held(sk)))) { + bool needs_md5 = !!tp->af_specific->md5_lookup(sk, sk); + bool needs_ao = !!tp->af_specific->ao_lookup(sk, sk, -1, -1); + + if (needs_md5 && needs_ao) + return -EKEYREJECTED; + } +#endif +#ifdef CONFIG_TCP_AO + if (unlikely(rcu_dereference_protected(tp->ao_info, + lockdep_sock_is_held(sk)))) { + /* Don't allow connecting if ao is configured but no + * matching key is found. + */ + if (!tp->af_specific->ao_lookup(sk, sk, -1, -1)) + return -EKEYREJECTED; + } +#endif + if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk)) return -EHOSTUNREACH; /* Routing failure or similar. */ diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index f9f242a7e0f2..221b8adb4f73 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -13,6 +13,23 @@ #include #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 ba60c1273517..6f71a2855753 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -614,6 +614,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; @@ -668,13 +669,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); } @@ -1914,6 +1926,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 }; @@ -1945,6 +1958,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 Thu Oct 27 20:43:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022730 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 BCA73ECAAA1 for ; Thu, 27 Oct 2022 20:45:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236993AbiJ0UpJ (ORCPT ); Thu, 27 Oct 2022 16:45:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60202 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237011AbiJ0Uog (ORCPT ); Thu, 27 Oct 2022 16:44:36 -0400 Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C35299378E for ; Thu, 27 Oct 2022 13:44:10 -0700 (PDT) Received: by mail-wr1-x42d.google.com with SMTP id l14so4186150wrw.2 for ; Thu, 27 Oct 2022 13:44:10 -0700 (PDT) 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=jUMa2Gliid5lZtTENog6NYtqlP/XJ5pNqHn2dFiBt9A=; b=PpyUyJI+ONoe+fYiEJfw8Fgsf3CvmoZaVVZMXEFs3bkcUtcSIbSF2gG/61iXRtUOV0 5Z83fVmaAIPPQ4bccOa7TN/r1lODKgIO6SzXjNb+kd0vyDvQg76Oqhx1uV3gsemT65wB IjHZ/rRu8ilOuEETAqOPwq3MspupLxTW1YrcgPomzU06D61qX7tp+6Oump/TkODgptGl A8ibwdt9x4mA9ZwQ82TY/7sR7eOT2Lv25qo7caNXQ+G6lTGCxYOPbhqB9Suf7xPW0ugF uZvQN+PsVUBLT5MGsk27W0/Eby4bOr+oBrk/SmfoN016ZPypWy6dUWssa9bG7UOfJlhD WigA== 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=jUMa2Gliid5lZtTENog6NYtqlP/XJ5pNqHn2dFiBt9A=; b=SYSNhWdr2rXUEa2aVtI5gZgVRmTB1cBuaPrVtT3lzG9NUCyJzmspkbIr5WxndGqL4b RlDR2v+iJ2LdW4Jm051MfG2u7tm4FjvLVYwvWYJnkD8fxgCjEUP6LMzYun8ogiQNry/I qo4fN1/bTxEIy4MOgbbOAr0MjJY+8THddXIBWOk+xYapGnf7nErWdUKEA+PjjEb5fZNb twB3BFoQWED0ISP37Qx4frPXz2sHbwdwQwiyW3lh8ZZclUlSsFemMFLOkqF9uGmcajVd d6GacUWXXVZM1S9DrOkjvF0LF6ZP5IeRYnnn4GJD09HQyfuegqYRnrxKtF2Y0OA2cbsS EJyw== X-Gm-Message-State: ACrzQf3SwoAIsGBtQRKjiZWmGb3Ovx5Q+YlM5z2HdNgTWyXfPYF+Oo2w N3D7NuWiM63h54vobPQCjDxflQ== X-Google-Smtp-Source: AMsMyM5s7/fOjiPPbAGp+EzB7hol+MlUsBNTL2GEDRqrSq+Ik7kU9qkiV/GPtlJ+o5C1tY+HpNs4YA== X-Received: by 2002:a05:6000:1d81:b0:232:bd08:3b8 with SMTP id bk1-20020a0560001d8100b00232bd0803b8mr32621783wrb.60.1666903450319; Thu, 27 Oct 2022 13:44:10 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:09 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 10/36] net/tcp: Calculate TCP-AO traffic keys Date: Thu, 27 Oct 2022 21:43:21 +0100 Message-Id: <20221027204347.529913-11-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 180 ++++++++++++++++++++++++++++++++++++++++++ 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, 272 insertions(+), 1 deletion(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index e5ccc58562a7..a61235437326 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2088,6 +2088,11 @@ struct tcp_sock_af_ops { struct tcp_ao_key *(*ao_lookup)(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); + int (*ao_calc_key_sk)(struct tcp_ao_key *mkt, + u8 *key, + const struct sock *sk, + __be32 sisn, __be32 disn, + bool send); #endif }; diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index c550f1a6f5fd..f83a4d09a4ce 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -88,9 +88,33 @@ struct tcp_ao_info { }; #ifdef CONFIG_TCP_AO +/* TCP-AO structures and functions */ + +struct tcp4_ao_context { + __be32 saddr; + __be32 daddr; + __be16 sport; + __be16 dport; + __be32 sisn; + __be32 disn; +}; + +struct tcp6_ao_context { + struct in6_addr saddr; + struct in6_addr daddr; + __be16 sport; + __be16 dport; + __be32 sisn; + __be32 disn; +}; + int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, sockptr_t optval, int optlen); +int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, + unsigned int len); void tcp_ao_destroy_sock(struct sock *sk); +int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, + struct tcp_ao_key *ao_key); struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port); @@ -98,13 +122,23 @@ struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, int tcp_v4_parse_ao(struct sock *sk, int optname, sockptr_t optval, int optlen); struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); +int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, + __be32 sisn, __be32 disn, bool send); /* ipv6 specific functions */ +int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, __be32 sisn, + __be32 disn, bool send); struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen); -#else +void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb); +void tcp_ao_connect_init(struct sock *sk); + +#else /* CONFIG_TCP_AO */ + static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port) @@ -115,6 +149,14 @@ static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, static inline void tcp_ao_destroy_sock(struct sock *sk) { } + +static inline void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb) +{ +} + +static inline void tcp_ao_connect_init(struct sock *sk) +{ +} #endif #endif /* _TCP_AO_H */ diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 27cf844ed8a5..d0f5021c4e0f 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 crypto_pool_ahash hp; + struct scatterlist sg; + int ret; + + if (crypto_pool_get(mkt->crypto_pool_id, (struct crypto_pool *)&hp)) + goto clear_hash_noput; + + if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), + mkt->key, mkt->keylen)) + goto clear_hash; + + ret = crypto_ahash_init(hp.req); + if (ret) + goto clear_hash; + + sg_init_one(&sg, ctx, len); + ahash_request_set_crypt(hp.req, &sg, key, len); + crypto_ahash_update(hp.req); + + /* TODO: Revisit on how to get different output length */ + ret = crypto_ahash_final(hp.req); + if (ret) + goto clear_hash; + + crypto_pool_put(); + return 0; +clear_hash: + crypto_pool_put(); +clear_hash_noput: + memset(key, 0, tcp_ao_digest_size(mkt)); + return 1; +} + static struct tcp_ao_key *tcp_ao_do_lookup_rcvid(struct sock *sk, u8 keyid) { struct tcp_sock *tp = tcp_sk(sk); @@ -181,6 +217,47 @@ void tcp_ao_destroy_sock(struct sock *sk) kfree_rcu(ao, rcu); } +/* 4 tuple and ISNs are expected in NBO */ +static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, + __be32 saddr, __be32 daddr, + __be16 sport, __be16 dport, + __be32 sisn, __be32 disn) +{ + /* See RFC5926 3.1.1 */ + struct kdf_input_block { + u8 counter; + u8 label[6]; + struct tcp4_ao_context ctx; + __be16 outlen; + } __packed tmp; + + tmp.counter = 1; + memcpy(tmp.label, "TCP-AO", 6); + tmp.ctx.saddr = saddr; + tmp.ctx.daddr = daddr; + tmp.ctx.sport = sport; + tmp.ctx.dport = dport; + tmp.ctx.sisn = sisn; + tmp.ctx.disn = disn; + tmp.outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ + + return tcp_ao_calc_traffic_key(mkt, key, &tmp, sizeof(tmp)); +} + +int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, + const struct sock *sk, + __be32 sisn, __be32 disn, bool send) +{ + if (send) + return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr, + sk->sk_daddr, htons(sk->sk_num), + sk->sk_dport, sisn, disn); + else + return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr, + sk->sk_rcv_saddr, sk->sk_dport, + htons(sk->sk_num), disn, sisn); +} + struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid) { @@ -189,6 +266,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_sk(sk)->af_specific->ao_calc_key_sk(ao_key, traffic_key, sk, + ao->lisn, ao->risn, true); + if (ret) + return ret; + + traffic_key = rcv_other_key(ao_key); + return tcp_sk(sk)->af_specific->ao_calc_key_sk(ao_key, traffic_key, sk, + ao->lisn, ao->risn, + false); +} + +void tcp_ao_connect_init(struct sock *sk) +{ + struct tcp_ao_info *ao_info; + struct tcp_ao_key *key; + struct tcp_sock *tp = tcp_sk(sk); + union tcp_ao_addr *addr; + int family; + + ao_info = rcu_dereference_protected(tp->ao_info, + lockdep_sock_is_held(sk)); + if (!ao_info) + return; + + /* Remove all keys that don't match the peer */ + family = sk->sk_family; + if (family == AF_INET) + addr = (union tcp_ao_addr *)&sk->sk_daddr; +#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); + crypto_pool_release(key->crypto_pool_id); + atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); + kfree_rcu(key, rcu); + } + + key = tp->af_specific->ao_lookup(sk, sk, -1, -1); + if (key) { + /* if current_key or rnext_key were not provided, + * use the first key matching the peer + */ + if (!ao_info->current_key) + ao_info->current_key = key; + if (!ao_info->rnext_key) + ao_info->rnext_key = key; + tp->tcp_header_len += tcp_ao_len(key); + + ao_info->lisn = htonl(tp->write_seq); + ao_info->snd_sne = 0; + ao_info->snd_sne_seq = tp->write_seq; + } else { + WARN_ON_ONCE(1); + 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) { @@ -682,6 +856,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 0640453fce54..0e753a2f84e4 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6052,6 +6052,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 2fb41e5e90ef..feeece12ec9a 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 3ad0dcf98083..6f32169df180 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3666,6 +3666,7 @@ static void tcp_connect_init(struct sock *sk) if (tp->af_specific->md5_lookup(sk, sk)) tp->tcp_header_len += TCPOLEN_MD5SIG_ALIGNED; #endif + tcp_ao_connect_init(sk); /* If user gave his TCP_MAXSEG, record it to clamp */ if (tp->rx_opt.user_mss) diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index 221b8adb4f73..888ee6242334 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -13,6 +13,46 @@ #include #include +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 6f71a2855753..cce2cae402bd 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1928,6 +1928,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 Thu Oct 27 20:43:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022743 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 B115DECAAA1 for ; Thu, 27 Oct 2022 20:47:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236966AbiJ0UrG (ORCPT ); Thu, 27 Oct 2022 16:47:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60202 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237034AbiJ0UpC (ORCPT ); Thu, 27 Oct 2022 16:45:02 -0400 Received: from mail-wr1-x42a.google.com (mail-wr1-x42a.google.com [IPv6:2a00:1450:4864:20::42a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BB0829410A for ; Thu, 27 Oct 2022 13:44:13 -0700 (PDT) Received: by mail-wr1-x42a.google.com with SMTP id l14so4186262wrw.2 for ; Thu, 27 Oct 2022 13:44:13 -0700 (PDT) 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=j9mM2iTROqs7EW7oDNOFbYtspmf4NhCy5tVW20FtkIo=; b=A8n/bW+yH4XNWlquRlmHwZnpT6zbxi4u0PyDCiiIVHWsmRaq32CfLB3XX9+SHtXm0t +rqN2qKthUrAmiNuWmQ5kKzbDNoD2PpU6k8NFdifWeXp4nRTjWzhu8mgDEENhyh7Qi0L JivHnq83kyP4ZHCCi4uRo/Zzo8OKLckzyYFeUxqWbQBeng4nDK1RYjraHzOVjNo4vCKr EtBComLkVo6mSAp6v+M3e5V5UUiHCessutt3tQoxShtWcLzCO1PMrEsykoI6CXAg5dq+ Oq7/d49JfAJSrryxV+f4lI4jvk3+u2xCHL3vqge0F2mNIOh+CKEYvDc/sfXYXdIuU9R3 B1ZQ== 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=j9mM2iTROqs7EW7oDNOFbYtspmf4NhCy5tVW20FtkIo=; b=v+hj9AcI734ooXzmYRy5TpYhAv0IEqORYvlav2ObHwzDWCoaMsVs/JOhYBc/jRGSMq c6ITKRPTveru7mP+7mMvUNb4GXvMTK3zUd24dfRpzf30TJJDsYcTcGkm/xDIebsU4j+Y LkmELI3/cX0/SEnpcEOo6CJW2EByWJuyLrXGxI89QhVHDrEBR0+JGCYKvZ+WAbMTzNPF sj8l8Sb5ZXZX290voVuUq9OxjOBY9sHdlaLa67M9o5709QEltl+gJiqnXCu3aAAj4aoI oySgeTi1QWxP4qyiEKdW7UAvr9K5L74Coo3Pi0l52XukClBhMQAnNAfU084o3iWXW044 ErRw== X-Gm-Message-State: ACrzQf0QyY0zegUb3HPATF5Lza7sf2UbUFBVPniz+jG9eI9UyctEoFmD WEi35WoQJlqhlIzO07dqRKmQ4g== X-Google-Smtp-Source: AMsMyM72IpccvbQRVmxl7g48gWSTNionQUuB+0ClcmiIoBjJ/1Xd228fbF9ZfyzV46dvPUiBcZoiVg== X-Received: by 2002:a5d:590d:0:b0:236:4ddd:1869 with SMTP id v13-20020a5d590d000000b002364ddd1869mr23912779wrd.709.1666903451948; Thu, 27 Oct 2022 13:44:11 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:11 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 11/36] net/tcp: Add TCP-AO sign to outgoing packets Date: Thu, 27 Oct 2022 21:43:22 +0100 Message-Id: <20221027204347.529913-12-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 8 ++ include/net/tcp_ao.h | 14 +++ net/ipv4/tcp_ao.c | 216 ++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 118 ++++++++++++++++++++--- net/ipv6/tcp_ao.c | 27 ++++++ net/ipv6/tcp_ipv6.c | 2 + 7 files changed, 375 insertions(+), 11 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index a61235437326..f3d1e7370461 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -187,6 +188,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOPT_SACK 5 /* SACK Block */ #define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ +#define TCPOPT_AO 29 /* Authentication Option (RFC5925) */ #define TCPOPT_MPTCP 30 /* Multipath TCP (RFC6824) */ #define TCPOPT_FASTOPEN 34 /* Fast open (RFC7413) */ #define TCPOPT_EXP 254 /* Experimental */ @@ -2088,6 +2090,12 @@ struct tcp_sock_af_ops { struct tcp_ao_key *(*ao_lookup)(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); + int (*calc_ao_hash)(char *location, + struct tcp_ao_key *ao, + const struct sock *sk, + const struct sk_buff *skb, + const u8 *tkey, + int hash_offset, u32 sne); int (*ao_calc_key_sk)(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index f83a4d09a4ce..f840b693d038 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -108,11 +108,16 @@ struct tcp6_ao_context { __be32 disn; }; +int tcp_ao_hash_skb(unsigned short int family, + char *ao_hash, struct tcp_ao_key *key, + const struct sock *sk, const struct sk_buff *skb, + const u8 *tkey, int hash_offset, u32 sne); int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, sockptr_t optval, int optlen); int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, unsigned int len); void tcp_ao_destroy_sock(struct sock *sk); +u32 tcp_ao_compute_sne(u32 sne, u32 seq, u32 new_seq); int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key); struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, @@ -125,13 +130,22 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send); +int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, + const struct sock *sk, const struct sk_buff *skb, + const u8 *tkey, int hash_offset, u32 sne); /* ipv6 specific functions */ +int tcp_v6_ao_hash_pseudoheader(struct crypto_pool_ahash *hp, + const struct in6_addr *daddr, + const struct in6_addr *saddr, int nbytes); int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send); struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); +int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, + const struct sock *sk, const struct sk_buff *skb, + const u8 *tkey, int hash_offset, u32 sne); int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen); void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb); diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index d0f5021c4e0f..bae6cca26fc5 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -258,6 +258,222 @@ int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, htons(sk->sk_num), disn, sisn); } +static int tcp_v4_ao_hash_pseudoheader(struct crypto_pool_ahash *hp, + __be32 daddr, __be32 saddr, + int nbytes) +{ + struct tcp4_pseudohdr *bp; + struct scatterlist sg; + + bp = hp->base.scratch; + bp->saddr = saddr; + bp->daddr = daddr; + bp->pad = 0; + bp->protocol = IPPROTO_TCP; + bp->len = cpu_to_be16(nbytes); + + sg_init_one(&sg, bp, sizeof(*bp)); + ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); + return crypto_ahash_update(hp->req); +} + +static int tcp_ao_hash_pseudoheader(unsigned short int family, + const struct sock *sk, + const struct sk_buff *skb, + struct crypto_pool_ahash *hp, int nbytes) +{ + const struct tcphdr *th = tcp_hdr(skb); + + /* TODO: Can we rely on checksum being zero to mean outbound pkt? */ + if (!th->check) { + if (family == AF_INET) + return tcp_v4_ao_hash_pseudoheader(hp, sk->sk_daddr, + sk->sk_rcv_saddr, skb->len); +#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 -EOPNOTSUPP; + } + + 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 -EOPNOTSUPP; +} + +u32 tcp_ao_compute_sne(u32 prev_sne, u32 prev_seq, u32 seq) +{ + u32 sne = prev_sne; + + if (before(seq, prev_seq)) { + if (seq > prev_seq) + sne--; + } else { + if (seq < prev_seq) + sne++; + } + + return sne; +} + +/* tcp_ao_hash_sne(struct crypto_pool_ahash *hp) + * @hp - used for hashing + * @sne - sne value + */ +static int tcp_ao_hash_sne(struct crypto_pool_ahash *hp, u32 sne) +{ + struct scatterlist sg; + __be32 *bp; + + bp = (__be32 *)hp->base.scratch; + *bp = htonl(sne); + + sg_init_one(&sg, bp, sizeof(*bp)); + ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); + return crypto_ahash_update(hp->req); +} + +static int tcp_ao_hash_header(struct crypto_pool_ahash *hp, + const struct tcphdr *th, + bool exclude_options, u8 *hash, + int hash_offset, int hash_len) +{ + struct scatterlist sg; + u8 *hdr = hp->base.scratch; + int err, len = th->doff << 2; + + /* We are not allowed to change tcphdr, make a local copy */ + if (exclude_options) { + len = sizeof(*th) + sizeof(struct tcp_ao_hdr) + hash_len; + memcpy(hdr, th, sizeof(*th)); + memcpy(hdr + sizeof(*th), + (u8 *)th + hash_offset - sizeof(struct tcp_ao_hdr), + sizeof(struct tcp_ao_hdr)); + memset(hdr + sizeof(*th) + sizeof(struct tcp_ao_hdr), + 0, hash_len); + ((struct tcphdr *)hdr)->check = 0; + } else { + len = th->doff << 2; + memcpy(hdr, th, len); + /* zero out tcp-ao hash */ + ((struct tcphdr *)hdr)->check = 0; + memset(hdr + hash_offset, 0, hash_len); + } + + sg_init_one(&sg, hdr, len); + ahash_request_set_crypt(hp->req, &sg, NULL, len); + err = crypto_ahash_update(hp->req); + WARN_ON_ONCE(err != 0); + return err; +} + +static int tcp_ao_hash_skb_data(struct crypto_pool_ahash *hp, + const struct sk_buff *skb, + unsigned int header_len) +{ + struct scatterlist sg; + const struct tcphdr *tp = tcp_hdr(skb); + struct ahash_request *req = hp->req; + unsigned int i; + const unsigned int head_data_len = skb_headlen(skb) > header_len ? + skb_headlen(skb) - header_len : 0; + const struct skb_shared_info *shi = skb_shinfo(skb); + struct sk_buff *frag_iter; + + WARN_ON(skb_headlen(skb) < header_len); + + sg_init_table(&sg, 1); + + sg_set_buf(&sg, ((u8 *)tp) + header_len, head_data_len); + ahash_request_set_crypt(req, &sg, NULL, head_data_len); + if (crypto_ahash_update(req)) + return 1; + + for (i = 0; i < shi->nr_frags; ++i) { + const skb_frag_t *f = &shi->frags[i]; + unsigned int offset = skb_frag_off(f); + struct page *page = skb_frag_page(f) + (offset >> PAGE_SHIFT); + + sg_set_page(&sg, page, skb_frag_size(f), + offset_in_page(offset)); + ahash_request_set_crypt(req, &sg, NULL, skb_frag_size(f)); + if (crypto_ahash_update(req)) + return 1; + } + + skb_walk_frags(skb, frag_iter) + if (tcp_ao_hash_skb_data(hp, frag_iter, 0)) + return 1; + + return 0; +} + +int tcp_ao_hash_skb(unsigned short int family, + char *ao_hash, struct tcp_ao_key *key, + const struct sock *sk, const struct sk_buff *skb, + const u8 *tkey, int hash_offset, u32 sne) +{ + const struct tcphdr *th = tcp_hdr(skb); + int tkey_len = tcp_ao_digest_size(key); + __u8 tmp_hash[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + struct crypto_pool_ahash hp; + + if (crypto_pool_get(key->crypto_pool_id, (struct crypto_pool *)&hp)) + goto clear_hash_noput; + + if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) + goto clear_hash; + + /* For now use sha1 by default. Depends on alg in tcp_ao_key */ + if (crypto_ahash_init(hp.req)) + goto clear_hash; + + if (tcp_ao_hash_sne(&hp, sne)) + goto clear_hash; + if (tcp_ao_hash_pseudoheader(family, sk, skb, &hp, skb->len)) + goto clear_hash; + if (tcp_ao_hash_header(&hp, th, false, + ao_hash, hash_offset, tcp_ao_maclen(key))) + goto clear_hash; + if (tcp_ao_hash_skb_data(&hp, skb, th->doff << 2)) + goto clear_hash; + ahash_request_set_crypt(hp.req, NULL, tmp_hash, 0); + if (crypto_ahash_final(hp.req)) + goto clear_hash; + + memcpy(ao_hash, tmp_hash, tcp_ao_maclen(key)); + crypto_pool_put(); + return 0; + +clear_hash: + crypto_pool_put(); +clear_hash_noput: + memset(ao_hash, 0, tcp_ao_maclen(key)); + return 1; +} +EXPORT_SYMBOL(tcp_ao_hash_skb); + +int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, + const struct sock *sk, const struct sk_buff *skb, + const u8 *tkey, int hash_offset, u32 sne) +{ + return tcp_ao_hash_skb(AF_INET, ao_hash, key, sk, skb, + tkey, hash_offset, sne); +} + struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index feeece12ec9a..ee9efaa9d8d5 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 6f32169df180..3aea90605034 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; @@ -1245,6 +1292,10 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, unsigned int tcp_options_size, tcp_header_size; struct sk_buff *oskb = NULL; struct tcp_md5sig_key *md5; +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao; +#endif + struct tcp_ao_key *ao_key = NULL; struct tcphdr *th; u64 prior_wstamp; int err; @@ -1276,11 +1327,17 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, tcb = TCP_SKB_CB(skb); memset(&opts, 0, sizeof(opts)); +#ifdef CONFIG_TCP_AO + ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (ao) + ao_key = ao->current_key; +#endif if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) { - tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5); + tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5, ao_key); } else { tcp_options_size = tcp_established_options(sk, skb, &opts, - &md5); + &md5, ao_key); /* Force a PSH flag on all (GSO) packets to expedite GRO flush * at receiver : This slightly improve GRO performance. * Note that we do not force the PSH flag for non GSO packets, @@ -1354,7 +1411,7 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, th->window = htons(min(tp->rcv_wnd, 65535U)); } - tcp_options_write(th, tp, &opts); + tcp_options_write(th, tp, &opts, ao_key); #ifdef CONFIG_TCP_MD5SIG /* Calculate the MD5 hash, as we have all we need now */ @@ -1364,6 +1421,32 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, md5, sk, skb); } #endif +#ifdef CONFIG_TCP_AO + if (ao) { + u8 *traffic_key; + u8 key_buf[TCP_AO_MAX_HASH_SIZE]; + u32 sne; + __u32 disn; + + sk_gso_disable(sk); + if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) { + if (tcb->tcp_flags & TCPHDR_ACK) + disn = ao->risn; + else + disn = 0; + traffic_key = key_buf; + tp->af_specific->ao_calc_key_sk(ao_key, traffic_key, + sk, ao->lisn, disn, true); + } else { + traffic_key = snd_other_key(ao_key); + } + sne = tcp_ao_compute_sne(ao->snd_sne, ao->snd_sne_seq, + ntohl(th->seq)); + tp->af_specific->calc_ao_hash(opts.hash_location, ao_key, sk, skb, + traffic_key, + opts.hash_location - (u8 *)th, sne); + } +#endif /* BPF prog is the last one writing header option */ bpf_skops_write_hdr_opt(sk, skb, NULL, NULL, 0, &opts); @@ -1825,6 +1908,10 @@ unsigned int tcp_current_mss(struct sock *sk) unsigned int header_len; struct tcp_out_options opts; struct tcp_md5sig_key *md5; + struct tcp_ao_key *ao_key = NULL; +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao_info; +#endif mss_now = tp->mss_cache; @@ -1833,8 +1920,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 @@ -3585,7 +3681,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); @@ -3606,7 +3702,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */ th->window = htons(min(req->rsk_rcv_wnd, 65535U)); - tcp_options_write(th, NULL, &opts); + tcp_options_write(th, NULL, &opts, NULL); th->doff = (tcp_header_size >> 2); __TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS); diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index 888ee6242334..7fd31c60488a 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -70,6 +70,33 @@ struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); } +int tcp_v6_ao_hash_pseudoheader(struct crypto_pool_ahash *hp, + const struct in6_addr *daddr, + const struct in6_addr *saddr, int nbytes) +{ + struct tcp6_pseudohdr *bp; + struct scatterlist sg; + + bp = hp->base.scratch; + /* 1. TCP pseudo-header (RFC2460) */ + bp->saddr = *saddr; + bp->daddr = *daddr; + bp->len = cpu_to_be32(nbytes); + bp->protocol = cpu_to_be32(IPPROTO_TCP); + + sg_init_one(&sg, bp, sizeof(*bp)); + ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); + return crypto_ahash_update(hp->req); +} + +int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, + const struct sock *sk, const struct sk_buff *skb, + const u8 *tkey, int hash_offset, u32 sne) +{ + return tcp_ao_hash_skb(AF_INET6, ao_hash, key, sk, skb, tkey, + hash_offset, sne); +} + int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) { diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index cce2cae402bd..bb2b21965911 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1927,6 +1927,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 @@ -1960,6 +1961,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = { #endif #ifdef CONFIG_TCP_AO .ao_lookup = tcp_v6_ao_lookup, + .calc_ao_hash = tcp_v6_ao_hash_skb, .ao_parse = tcp_v6_parse_ao, #endif }; From patchwork Thu Oct 27 20:43:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022744 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 BE2B2ECAAA1 for ; Thu, 27 Oct 2022 20:47:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237066AbiJ0UrH (ORCPT ); Thu, 27 Oct 2022 16:47:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59430 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237033AbiJ0UpC (ORCPT ); Thu, 27 Oct 2022 16:45:02 -0400 Received: from mail-wr1-x434.google.com (mail-wr1-x434.google.com [IPv6:2a00:1450:4864:20::434]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E08E49410C for ; Thu, 27 Oct 2022 13:44:13 -0700 (PDT) Received: by mail-wr1-x434.google.com with SMTP id bk15so4129817wrb.13 for ; Thu, 27 Oct 2022 13:44:13 -0700 (PDT) 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=SvpBIzB80zoncTXVKn2CMKV32VOSx9DN7/sSdVAlkPs=; b=QTfH1kNbwMAOzIOlKpQ2ZFUaUXuFcG3OdLZPDukYMQfGig6egfkav0ANfV+4PL8u3o It01Ejali4WJrEV9s65nRhcYOSxkzVV6mckE3QyC9nP3MqNsyDIhuFEvO8uEi0O0cJQs 4oaOOUJObeww0OOa5Cz79ktrciz3KOVWGbsrMg2vPI8sgYBmVh9h5SOIuK+xkfEumrE0 0NPr9w1t2/rGcWBnqOOd9MiiNtk8hZIKHzXFijTUuoF6etPpiA0PS/SJ5ca9aoVXaynm VAPALPf5AjWfiDn6RFViZvqEwXsq+G+SzwQKvHzzvTViCI2/vLHfFtFeiwOa4pnSOKwu Tpsw== 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=SvpBIzB80zoncTXVKn2CMKV32VOSx9DN7/sSdVAlkPs=; b=hMFXVfzfmnJ30K7EuGL3ee8bYBt8xCXIwhBIK/Mh5Ci5rC8OUgknEIaqf66amJzEtX C4Bu/PLes3OZkDiFAEpIhK7BxD2XQWM/WNoy56Wmv7hLQ1Rhvfxr43ElRBmzj0EMRF+l S6NqnhjfB34VIAmgWopyMctOsIXjBVZdrZiRD/t0x4u3DpAerkFPVrNu1yZR/QX1LJcs zqwI40dF/9eo9lctmolfxMWqkHy1U7p25wO2yO98tEtgTswUSBHiR492CprSNrwlzksP RNgll7nv4XvQPp9p01cRicDCiRRAEjEkfpJ6EkVN+iesVj5XHeoKUqbtXAgjLV/5qtj0 df3Q== X-Gm-Message-State: ACrzQf1mDGHrVuxkZyynsnrZHaITyefFb/JSOQB9G8YJJoM/pARPiiz8 8AqC0HepdjLGNv2SKsCV8eRQMg== X-Google-Smtp-Source: AMsMyM7bDFixi+UAZKIDLMS2w2UmQ3xw8J8pj2dZE1zZYN0GuRZyOnt5sO+kZuaFmvcppZisBIHQRA== X-Received: by 2002:a05:6000:1f8e:b0:236:86de:6fff with SMTP id bw14-20020a0560001f8e00b0023686de6fffmr8648410wrb.387.1666903453378; Thu, 27 Oct 2022 13:44:13 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:12 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 12/36] net/tcp: Add tcp_parse_auth_options() Date: Thu, 27 Oct 2022 21:43:23 +0100 Message-Id: <20221027204347.529913-13-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 f3d1e7370461..d2395ae7484c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -435,7 +435,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 @@ -2477,4 +2476,27 @@ static inline u64 tcp_transmit_time(const struct sock *sk) return 0; } +static inline int tcp_parse_auth_options(const struct tcphdr *th, + const u8 **md5_hash, const struct tcp_ao_hdr **aoh) +{ + const u8 *md5_tmp, *ao_tmp; + int ret; + + ret = tcp_do_parse_auth_options(th, &md5_tmp, &ao_tmp); + if (ret) + return ret; + + if (md5_hash) + *md5_hash = md5_tmp; + + if (aoh) { + if (!ao_tmp) + *aoh = NULL; + else + *aoh = (struct tcp_ao_hdr *)(ao_tmp - 2); + } + + return 0; +} + #endif /* _TCP_H */ diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index f840b693d038..7b38ac70416f 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -150,7 +150,9 @@ int tcp_v6_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen); void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb); void tcp_ao_connect_init(struct sock *sk); - +void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, + struct tcp_request_sock *treq, + unsigned short int family); #else /* CONFIG_TCP_AO */ static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, @@ -173,4 +175,17 @@ static inline void tcp_ao_connect_init(struct sock *sk) } #endif +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) +int tcp_do_parse_auth_options(const struct tcphdr *th, + const u8 **md5_hash, const u8 **ao_hash); +#else +static int tcp_do_parse_auth_options(const struct tcphdr *th, + const u8 **md5_hash, const u8 **ao_hash) +{ + *md5_hash = NULL; + *ao_hash = NULL; + return 0; +} +#endif + #endif /* _TCP_AO_H */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 27662c9b0870..875149817036 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4538,7 +4538,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 0e753a2f84e4..a7c3823cd052 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4201,39 +4201,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 ee9efaa9d8d5..1c014b326ee6 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -680,7 +680,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) @@ -696,7 +698,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; @@ -736,8 +738,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; @@ -748,7 +753,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); @@ -780,7 +785,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 bb2b21965911..58a77515aa27 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1000,7 +1000,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; @@ -1022,8 +1022,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; @@ -1032,7 +1035,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; @@ -1061,7 +1064,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 Thu Oct 27 20:43:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022745 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 0ED98FA3740 for ; Thu, 27 Oct 2022 20:47:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237098AbiJ0Urd (ORCPT ); Thu, 27 Oct 2022 16:47:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38946 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236565AbiJ0UqE (ORCPT ); Thu, 27 Oct 2022 16:46:04 -0400 Received: from mail-wr1-x432.google.com (mail-wr1-x432.google.com [IPv6:2a00:1450:4864:20::432]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 76CC9951FD for ; Thu, 27 Oct 2022 13:44:16 -0700 (PDT) Received: by mail-wr1-x432.google.com with SMTP id z14so4155510wrn.7 for ; Thu, 27 Oct 2022 13:44:16 -0700 (PDT) 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=axN8ZmDNzhMnIzIaFx1++LOhn5A1Ycz/AuCMC//ovzw=; b=EI1H4V68rgpTul2737VoMS3IEksZve3+Sk9POOuAWuPesjx9wxZTNgmv58DiZpDGqx 2a0QCF+/Gfi26Eu7OTXyz5cxPbkIk53zYHyzNvYyz7XiN+I11owcWulTHPtcAOwd0Ukw CGHZFdCp91SciD3Tv2gHZ56SCT+Yn7WBDcBROUmjJ1+t7Hk/Xl7fQhhulwqjBo6l3TQv LJO6VNeQUg/H6qYn93EQ2AE8PFUlELCeUcmQeVBihUhEH+XlETLC21lWUdz8kussKwhj 4KvCf4o42e0T384Q3EVFb3uI0zCByGG3mYJabeP9jc19nmjUqDlU5JYtRs8muuG2hW1t vH/Q== 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=axN8ZmDNzhMnIzIaFx1++LOhn5A1Ycz/AuCMC//ovzw=; b=oUHxf7QQ0nnKyn5XUsh3XocrZZUxuGmiKqap1asT5EWwTULFH5fpiuNMiuV6gW7XUV epUt3UFzuivmZSNxFY8yrr3sOrhwhD06J7yfOatRh8CI1mMlAbCP975Sf+i2BfDg/+z5 gL78Q7jMqzQbk0ip5spv46ynVjK6J5JjG4EXwU86r3fKAl7W0pDnp4ZPnoM39Jgkcgwh iNx2cUNLZS/eIAeQTFMmb7TgVDH7rFSYYa3ASCGI/ZegTxaRX1Tdmr6ZAs8UnrjrIAMh 1zzlhuAEp91CC0VF2DyjiQ42jYSzR7WsFe1xDpAYEQqpGfotfKGS323VfEphvH1V6u99 vofQ== X-Gm-Message-State: ACrzQf3S5tkPlBMCMOb/MmXa2AcKho5VBWQj2ikHzB1lrj6Vv0peio2k +We64+Tl+DbKLriysX31fvfqiw== X-Google-Smtp-Source: AMsMyM5L7h5AkmuKXXRj0oI92h8HdgiB0wT3aYdjSIm6poCDBwqSpOZ7TX5UU1TJYQ+YGcroBroJSA== X-Received: by 2002:adf:db10:0:b0:231:bcaa:313b with SMTP id s16-20020adfdb10000000b00231bcaa313bmr31368901wri.142.1666903454890; Thu, 27 Oct 2022 13:44:14 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:14 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 13/36] net/tcp: Add AO sign to RST packets Date: Thu, 27 Oct 2022 21:43:24 +0100 Message-Id: <20221027204347.529913-14-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 7 ++++ net/ipv4/tcp_ao.c | 53 ++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 68 +++++++++++++++++++++++++++---- net/ipv6/tcp_ipv6.c | 96 ++++++++++++++++++++++++++++++++++++++------ 4 files changed, 203 insertions(+), 21 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 7b38ac70416f..d359fbf89da8 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -114,6 +114,7 @@ int tcp_ao_hash_skb(unsigned short int family, const u8 *tkey, int hash_offset, u32 sne); int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, sockptr_t optval, int optlen); +struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid); int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, unsigned int len); void tcp_ao_destroy_sock(struct sock *sk); @@ -123,6 +124,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 bae6cca26fc5..e20e3b435ce1 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -421,6 +421,59 @@ static int tcp_ao_hash_skb_data(struct crypto_pool_ahash *hp, return 0; } +int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash, + struct tcp_ao_key *key, const u8 *tkey, + const union tcp_ao_addr *daddr, + const union tcp_ao_addr *saddr, + const struct tcphdr *th, u32 sne) +{ + struct crypto_pool_ahash hp; + int tkey_len = tcp_ao_digest_size(key); + int hash_offset = ao_hash - (char *)th; + + if (crypto_pool_get(key->crypto_pool_id, (struct crypto_pool *)&hp)) + goto clear_hash_noput; + + if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) + goto clear_hash; + + if (crypto_ahash_init(hp.req)) + goto clear_hash; + + if (tcp_ao_hash_sne(&hp, sne)) + goto clear_hash; + if (family == AF_INET) { + if (tcp_v4_ao_hash_pseudoheader(&hp, daddr->a4.s_addr, + saddr->a4.s_addr, th->doff * 4)) + goto clear_hash; +#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; + + crypto_pool_put(); + return 0; + +clear_hash: + crypto_pool_put(); +clear_hash_noput: + memset(ao_hash, 0, tcp_ao_maclen(key)); + return 1; +} +EXPORT_SYMBOL(tcp_ao_hash_hdr); + int tcp_ao_hash_skb(unsigned short int family, char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 1c014b326ee6..b76933bb073e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -696,16 +696,24 @@ 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 +#ifdef CONFIG_TCP_AO + char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + struct tcp_ao_key *ao_key = NULL; + u32 ao_sne; +#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) @@ -737,12 +745,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; @@ -803,6 +813,48 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) key, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, &rep.th); } +#endif +#ifdef CONFIG_TCP_AO + if (sk && aoh && sk->sk_state != TCP_LISTEN) { + /* lookup key based on peer address and rcv_next*/ + ao_key = tcp_ao_do_lookup_sndid(sk, aoh->rnext_keyid); + + if (ao_key) { + struct tcp_ao_info *ao_info; + u8 keyid; + + ao_info = rcu_dereference(tcp_sk(sk)->ao_info); + + /* XXX: optimize by using cached traffic key depending + * on socket state + */ + 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; + } + } #endif /* Can't co-exist with TCPMD5, hence check rep.opt[0] */ if (rep.opt[0] == 0) { @@ -860,7 +912,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 58a77515aa27..23e0d4ffc007 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -865,7 +865,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; @@ -884,6 +886,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) { @@ -935,6 +944,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; @@ -999,17 +1023,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; @@ -1021,12 +1056,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; @@ -1075,6 +1111,39 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) ack_seq = ntohl(th->seq) + th->syn + th->fin + skb->len - (th->doff << 2); +#ifdef CONFIG_TCP_AO + /* XXX: Not implemented for listening sockets yet. How do we + * get the initial sequence numbers? Might need to pass in + * the request socket. + */ + if (sk && aoh && sk->sk_state != TCP_LISTEN) { + struct tcp_ao_info *ao_info; + + if (WARN_ON_ONCE(sk->sk_state == TCP_NEW_SYN_RECV)) + goto out; + + /* rcv_next is the peer's here */ + ao_key = tcp_ao_do_lookup_sndid(sk, aoh->rnext_keyid); + + if (ao_key) { + ao_info = rcu_dereference(tcp_sk(sk)->ao_info); + + /* XXX: optimize by using cached traffic key depending + * on socket state + */ + 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); + } + } +#endif + if (sk) { oif = sk->sk_bound_dev_if; if (sk_fullsock(sk)) { @@ -1097,9 +1166,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 @@ -1111,7 +1181,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 Thu Oct 27 20:43:25 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022746 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 C913BFA3740 for ; Thu, 27 Oct 2022 20:47:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236613AbiJ0Urg (ORCPT ); Thu, 27 Oct 2022 16:47:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32836 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234868AbiJ0Uqt (ORCPT ); Thu, 27 Oct 2022 16:46:49 -0400 Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EB9A89411F for ; Thu, 27 Oct 2022 13:44:16 -0700 (PDT) Received: by mail-wr1-x42d.google.com with SMTP id w14so4155121wru.8 for ; Thu, 27 Oct 2022 13:44:16 -0700 (PDT) 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=ZzygEftNO9uBdJknMQ3dhxwVGxhJy8qR9pjIa567u0c=; b=JNp/w2XJApiAeK7vMWWUdv/UbmD0LTMBL6M3w9fA0WXnyA4Mm0lRgekv/8jYJ7p18y FvKDHoXPmWN+OVGkg//1cKy66LggGeimv6xdNszL0KxKsucAQKuHeJjLBGXUL3v9znd5 RflDxQ9X55gcRckyft1/q9jIPHmT+ukquplV66tVgvD5uCFmxFM8AczKuwKHM0MWDZOK xw8oCc/2Nony+P0EX50yxoSic3Ocwt5Rj36w8V3lorICqgx3Id5GarxbtnyS4Q1V0oMm nhpuneU7l6C9juebD1jjKANYgVAGYT6KAlmQcUbGnEkoXfGo+hiP4FpXIbob044VZUbZ 5+oQ== 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=ZzygEftNO9uBdJknMQ3dhxwVGxhJy8qR9pjIa567u0c=; b=iB95utkEkMHSrQ/kimVFHxk157ity9yUGNLKI7+iRq31vkPPeuj7YRsF66tZOnUC4A wwIJPBYr2o8hQiunNDsdy2QGeDcjDxgV3nHHeconXSyrjz5MLQVCOQcYRF2c7gPS/gu9 4eLKwmHm5f1rB+aUG6qHpsMbKqMVrB+hSlBtzALyDltiyeDEkibIEqXldGIGBmLquDCH lUWlEpDnaFSTi+TsY1iFy3L0+tpU0FGkjo4eF53KxBTQVk1RBnexHfRGnTAc8JwM297b OSkmYrfZhNoZqufplcmsWbmO13xLYc8n1QNEq6F+AQxGpdULI0kd13MDtoAHwNjOyLOO d2Kw== X-Gm-Message-State: ACrzQf2Uwv5aAejqvXZPhYQ1PCTz4GQN+optgGI43FwZc5fehK090ItE bfY9iwjQL6lMsIaC4TLLKV0crA== X-Google-Smtp-Source: AMsMyM48M1KauTSA3pgUBI4zJ/+sM//1uN+9fzykaMfEh2Fs0cBjL5jpdnCtCEKsDft3DVUxuq6R5Q== X-Received: by 2002:a05:6000:184:b0:236:7685:7e6d with SMTP id p4-20020a056000018400b0023676857e6dmr13904166wrx.305.1666903456416; Thu, 27 Oct 2022 13:44:16 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:15 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 14/36] net/tcp: Add TCP-AO sign to twsk Date: Thu, 27 Oct 2022 21:43:25 +0100 Message-Id: <20221027204347.529913-15-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 47 ++++++++++++++++++++++---- net/ipv4/tcp_ipv4.c | 71 ++++++++++++++++++++++++++++++++++++---- net/ipv4/tcp_minisocks.c | 4 ++- net/ipv6/tcp_ipv6.c | 45 ++++++++++++++++++++++--- 6 files changed, 159 insertions(+), 22 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index fd248875b0a9..6630a8195ea6 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -497,6 +497,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 d359fbf89da8..9804881fd2bf 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 @@ -117,8 +118,9 @@ int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid); int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, unsigned int len); -void tcp_ao_destroy_sock(struct sock *sk); +void tcp_ao_destroy_sock(struct sock *sk, bool twsk); u32 tcp_ao_compute_sne(u32 sne, u32 seq, u32 new_seq); +void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp); int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key); struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, @@ -169,7 +171,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) { } @@ -177,6 +179,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 e20e3b435ce1..09d5cee7c6d7 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -75,8 +75,13 @@ struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid) struct tcp_ao_key *key; struct tcp_ao_info *ao; - ao = rcu_dereference_check(tcp_sk(sk)->ao_info, - lockdep_sock_is_held(sk)); + if (sk->sk_state == TCP_TIME_WAIT) + ao = rcu_dereference_check(tcp_twsk(sk)->ao_info, + lockdep_sock_is_held(sk)); + else + ao = rcu_dereference_check(tcp_sk(sk)->ao_info, + lockdep_sock_is_held(sk)); + if (!ao) return NULL; @@ -177,6 +182,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; @@ -196,27 +202,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 b76933bb073e..fcf61b5bc581 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -823,7 +823,10 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) struct tcp_ao_info *ao_info; u8 keyid; - ao_info = rcu_dereference(tcp_sk(sk)->ao_info); + if (sk->sk_state == TCP_TIME_WAIT) + ao_info = rcu_dereference(tcp_twsk(sk)->ao_info); + else + ao_info = rcu_dereference(tcp_sk(sk)->ao_info); /* XXX: optimize by using cached traffic key depending * on socket state @@ -926,16 +929,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; @@ -981,6 +984,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 */ @@ -1014,6 +1036,36 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb) { struct inet_timewait_sock *tw = inet_twsk(sk); struct tcp_timewait_sock *tcptw = tcp_twsk(sk); + struct tcp_ao_key *ao_key = NULL; + u8 *traffic_key = NULL; + u8 rcv_next = 0; + u32 ao_sne = 0; +#ifdef CONFIG_TCP_AO + struct tcp_ao_info *ao_info = NULL; + const struct tcp_ao_hdr *aoh; +#endif + +#ifdef CONFIG_TCP_AO + if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh)) + goto out; /* something is wrong with the sign */ + + /* FIXME: we haven't verified the segment to-be-acked */ + if (aoh) + ao_key = tcp_ao_do_lookup_sndid(sk, aoh->rnext_keyid); + + if (ao_key) { + traffic_key = snd_other_key(ao_key); + ao_info = rcu_dereference(tcptw->ao_info); + /* It's possible we can get rid of computing the sne + * below since sne probably doesn't change once we are + * in timewait state. + */ + ao_sne = tcp_ao_compute_sne(ao_info->snd_sne, + ao_info->snd_sne_seq, + tcptw->tw_snd_nxt); + rcv_next = ao_info->rnext_key->rcvid; + } +#endif tcp_v4_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, @@ -1022,10 +1074,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); } @@ -1055,6 +1111,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); } @@ -2400,7 +2457,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 f7a75dce140b..87cf3073b6c2 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -246,7 +246,7 @@ EXPORT_SYMBOL(tcp_timewait_state_process); void tcp_time_wait(struct sock *sk, int state, int timeo) { const struct inet_connection_sock *icsk = inet_csk(sk); - const struct tcp_sock *tp = tcp_sk(sk); + struct tcp_sock *tp = tcp_sk(sk); struct net *net = sock_net(sk); struct inet_timewait_sock *tw; @@ -305,6 +305,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) } } while (0); #endif + tcp_ao_time_wait(tcptw, tp); /* Get the TIME_WAIT timeout firing. */ if (timeo < rto) @@ -359,6 +360,7 @@ void tcp_twsk_destructor(struct sock *sk) call_rcu(&twsk->tw_md5_key->rcu, tcp_md5_twsk_free_rcu); } #endif + tcp_ao_destroy_sock(sk, true); } EXPORT_SYMBOL_GPL(tcp_twsk_destructor); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 23e0d4ffc007..7bd4d0bb77cd 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1126,7 +1126,10 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) ao_key = tcp_ao_do_lookup_sndid(sk, aoh->rnext_keyid); if (ao_key) { - ao_info = rcu_dereference(tcp_sk(sk)->ao_info); + if (sk->sk_state == TCP_TIME_WAIT) + ao_info = rcu_dereference(tcp_twsk(sk)->ao_info); + else + ao_info = rcu_dereference(tcp_sk(sk)->ao_info); /* XXX: optimize by using cached traffic key depending * on socket state @@ -1178,24 +1181,56 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) static void tcp_v6_send_ack(const struct sock *sk, struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32 tsval, u32 tsecr, int oif, struct tcp_md5sig_key *key, u8 tclass, - __be32 label, u32 priority, 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; + const struct tcp_ao_hdr *aoh; + + ao_info = rcu_dereference(tcptw->ao_info); + if (ao_info) { + /* Invalid TCP option size or twice included auth */ + if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh)) + goto out; + /* FIXME: we haven't verified the segment to-be-acked */ + if (aoh) + ao_key = tcp_ao_do_lookup_sndid(sk, aoh->rnext_keyid); + if (ao_key) { + traffic_key = snd_other_key(ao_key); + /* rcv_next switches to our rcv_next */ + rcv_next = ao_info->rnext_key->rcvid; + ao_sne = tcp_ao_compute_sne(ao_info->snd_sne, + ao_info->snd_sne_seq, + tcptw->tw_snd_nxt); + } + } +#endif tcp_v6_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, tcp_time_stamp_raw() + tcptw->tw_ts_offset, tcptw->tw_ts_recent, tw->tw_bound_dev_if, tcp_twsk_md5_key(tcptw), tw->tw_tclass, cpu_to_be32(tw->tw_flowlabel), tw->tw_priority, - tw->tw_txhash); + tw->tw_txhash, ao_key, traffic_key, rcv_next, ao_sne); +#ifdef CONFIG_TCP_AO +out: +#endif inet_twsk_put(tw); } @@ -1222,7 +1257,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 Thu Oct 27 20:43:26 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022780 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 9B8FCECAAA1 for ; Thu, 27 Oct 2022 20:51:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237109AbiJ0Uvi (ORCPT ); Thu, 27 Oct 2022 16:51:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46880 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237107AbiJ0Uto (ORCPT ); Thu, 27 Oct 2022 16:49:44 -0400 Received: from mail-wr1-x436.google.com (mail-wr1-x436.google.com [IPv6:2a00:1450:4864:20::436]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1D0AB3894 for ; Thu, 27 Oct 2022 13:44:49 -0700 (PDT) Received: by mail-wr1-x436.google.com with SMTP id g12so4147133wrs.10 for ; Thu, 27 Oct 2022 13:44:49 -0700 (PDT) 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=4PP0OLhh07vilXdlAovYbN9T6dBjH8VyMkYPWjgeglo=; b=gxv07KHmaKisMM5bqRhwOe60KSEVhm/5lnqPOYdM9OK4fu5/KEGKCFK2Ij78zWe+EI +8TkJSK3SxuNgp8iLEidpUQdZDBN37vmzlUXv7mx9NOr9r16Ejdys+kpf5totE3WaJ79 kPcf4M1c4GS2Fes3ESnnlwt/0H2WwEWsXk1hSpxORKzqStRsS1O9U2vZWq4rLMiXsQgd HYvrOnxhEePcZitJhDdOEAHI9PFomZOTJzAyx/4gsqL+Wona78e3uOp3QdXp4zTNGn9M E+V/pE6j5P1F6qdjwXur2+QFnGTmEmDc4tYOrSbyNBBtQL96Ul4WUn5M1FAroumdxjXC FcKw== 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=4PP0OLhh07vilXdlAovYbN9T6dBjH8VyMkYPWjgeglo=; b=oTUtlGBGCfOpELF77oMAi07Fl2wcPrbZHPBXKo1cc2epEcxMB2wvbnrPTc8frd1zA3 8PUvLnfb+AcyIxFSS0pm7x+v3/lbozo5B7c0znoLi5DLUQCcF00+tTnQ+h6za5OfYSi3 eJg/iLUysF20wU0XKsWFjEl991uIA7FeE20068hb1EaPEc5h5HDwe2x+IsgHjyEc2kSs q9ZiMwddb93gM+d4CRzndYz73XWxXZzvRi7TpsQnk7xXHuiFc+8N47pSda1K5Gyj4I0/ YUrLPwWZ+VCNq7em2pXYB7dN6FYz+XZgQsC/Y7yqeORaD1QHV6Y3q5F86lUufBDyw/Cv BL5w== X-Gm-Message-State: ACrzQf1pVlNG29RJB3yqLqeEIXuHpPhlwe0AkZi5SvbU6+R82eHuNuAj MXHdgrpA6klFSGtwTaiQC8NEdfIcSFrM0ONf X-Google-Smtp-Source: AMsMyM61L3wr9mMdHaJOCY6aCvJ8T4J4zaucDPD9IlPX2Lpc5yPXsGBw04KRsgpSqrIl+zRmQ5jM2w== X-Received: by 2002:a05:6000:1548:b0:22e:3469:b726 with SMTP id 8-20020a056000154800b0022e3469b726mr32837935wry.10.1666903457933; Thu, 27 Oct 2022 13:44:17 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:17 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 15/36] net/tcp: Wire TCP-AO to request sockets Date: Thu, 27 Oct 2022 21:43:26 +0100 Message-Id: <20221027204347.529913-16-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 187 +++++++++++++++++++++++++++++++++++++++ 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 | 78 +++++++++++++--- 12 files changed, 433 insertions(+), 29 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 6630a8195ea6..cbaf9ca25974 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 d2395ae7484c..bda03d4925ed 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2113,6 +2113,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 9804881fd2bf..e93db866bd44 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -116,6 +116,9 @@ int tcp_ao_hash_skb(unsigned short int family, int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, sockptr_t optval, int optlen); struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid); +int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, + struct request_sock *req, struct sk_buff *skb, + int family); int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, unsigned int len); void tcp_ao_destroy_sock(struct sock *sk, bool twsk); @@ -139,6 +142,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); @@ -149,9 +157,17 @@ int tcp_v6_ao_hash_pseudoheader(struct crypto_pool_ahash *hp, int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send); +int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, + struct request_sock *req); +struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk, + const struct in6_addr *addr, + int sndid, int rcvid); struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); +struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk, + struct request_sock *req, + int sndid, int rcvid); int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, const u8 *tkey, int hash_offset, u32 sne); @@ -164,6 +180,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 942d2dfa1115..83801d7c5b02 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -395,6 +395,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 09d5cee7c6d7..c9d3e29a28f1 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -52,6 +52,21 @@ int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, return 1; } +static struct tcp_ao_key *tcp_ao_do_lookup_keyid(struct tcp_ao_info *ao, + int sndid, int rcvid) +{ + struct tcp_ao_key *key; + + hlist_for_each_entry_rcu(key, &ao->head, node) { + if ((sndid >= 0 && key->sndid != sndid) || + (rcvid >= 0 && key->rcvid != rcvid)) + continue; + return key; + } + + return NULL; +} + static struct tcp_ao_key *tcp_ao_do_lookup_rcvid(struct sock *sk, u8 keyid) { struct tcp_sock *tp = tcp_sk(sk); @@ -194,6 +209,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); + crypto_pool_add(new_key->crypto_pool_id); + + return new_key; +} + static void tcp_ao_key_free_rcu(struct rcu_head *head) { struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu); @@ -291,6 +323,18 @@ int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, htons(sk->sk_num), disn, sisn); } +int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, + struct request_sock *req) +{ + struct inet_request_sock *ireq = inet_rsk(req); + + return tcp_v4_ao_calc_key(mkt, key, + ireq->ir_loc_addr, ireq->ir_rmt_addr, + htons(ireq->ir_num), ireq->ir_rmt_port, + htonl(tcp_rsk(req)->snt_isn), + htonl(tcp_rsk(req)->rcv_isn)); +} + static int tcp_v4_ao_hash_pseudoheader(struct crypto_pool_ahash *hp, __be32 daddr, __be32 saddr, int nbytes) @@ -560,6 +604,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) { @@ -568,6 +622,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_md5_addr *)&iph->saddr, + AF_INET, sndid, rcvid, 0); + } else { + const struct ipv6hdr *iph = ipv6_hdr(skb); + + return tcp_ao_do_lookup(sk, (union tcp_md5_addr *)&iph->saddr, + AF_INET6, sndid, rcvid, 0); + } +} + +/* 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) { @@ -665,6 +764,94 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb) } } +int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, + struct request_sock *req, struct sk_buff *skb, + int family) +{ + struct tcp_ao_info *ao; + struct tcp_ao_info *new_ao; + struct tcp_ao_key *key, *new_key, *first_key; + struct hlist_node *n; + union tcp_ao_addr *addr; + bool match = false; + + ao = rcu_dereference(tcp_sk(sk)->ao_info); + if (!ao) + return 0; + + /* New socket without TCP-AO on it */ + if (!tcp_rsk_used_ao(req)) + return 0; + + new_ao = tcp_ao_alloc_info(GFP_ATOMIC, ao); + if (!new_ao) + return -ENOMEM; + new_ao->lisn = htonl(tcp_rsk(req)->snt_isn); + new_ao->risn = htonl(tcp_rsk(req)->rcv_isn); + + if (family == AF_INET) + addr = (union tcp_ao_addr *)&newsk->sk_daddr; +#if IS_ENABLED(CONFIG_IPV6) + else if (family == AF_INET6) + addr = (union tcp_ao_addr *)&newsk->sk_v6_daddr; +#endif + else + return -EOPNOTSUPP; + + hlist_for_each_entry_rcu(key, &ao->head, node) { + if (tcp_ao_key_cmp(key, addr, key->prefixlen, family, + -1, -1, 0)) + continue; + + new_key = tcp_ao_copy_key(newsk, key); + if (!new_key) + goto free_and_exit; + + tcp_ao_cache_traffic_keys(newsk, new_ao, new_key); + tcp_ao_link_mkt(new_ao, new_key); + match = true; + } + + if (match) { + struct hlist_node *key_head; + + key_head = rcu_dereference(hlist_first_rcu(&new_ao->head)); + first_key = hlist_entry_safe(key_head, struct tcp_ao_key, node); + + /* set current_key */ + key = tcp_ao_do_lookup_keyid(new_ao, tcp_rsk(req)->ao_keyid, -1); + if (key) + new_ao->current_key = key; + else + new_ao->current_key = first_key; + + /* set rnext_key */ + key = tcp_ao_do_lookup_keyid(new_ao, -1, tcp_rsk(req)->ao_rcv_next); + if (key) + new_ao->rnext_key = key; + else + new_ao->rnext_key = first_key; + + new_ao->snd_sne_seq = tcp_rsk(req)->snt_isn; + new_ao->rcv_sne_seq = tcp_rsk(req)->rcv_isn; + + sk_gso_disable(newsk); + rcu_assign_pointer(tcp_sk(newsk)->ao_info, new_ao); + } + + return 0; + +free_and_exit: + hlist_for_each_entry_safe(key, n, &new_ao->head, node) { + hlist_del(&key->node); + crypto_pool_release(key->crypto_pool_id); + atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc); + kfree(key); + } + kfree(new_ao); + return -ENOMEM; +} + static int tcp_ao_current_rnext(struct sock *sk, u16 tcpa_flags, u8 tcpa_sndid, u8 tcpa_rcvid) { diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a7c3823cd052..df3087c8179f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6935,6 +6935,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 @@ -7020,6 +7024,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 fcf61b5bc581..47325f148f29 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1088,30 +1088,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); } @@ -1632,6 +1675,10 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { .req_md5_lookup = tcp_v4_md5_lookup, .calc_md5_hash = tcp_v4_md5_hash_skb, #endif +#ifdef CONFIG_TCP_AO + .ao_lookup = tcp_v4_ao_lookup_rsk, + .ao_calc_key = tcp_v4_ao_calc_key_rsk, +#endif #ifdef CONFIG_SYN_COOKIES .cookie_init_seq = cookie_v4_init_sequence, #endif @@ -1733,7 +1780,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb, /* Copy over the MD5 key from the original socket */ addr = (union tcp_md5_addr *)&newinet->inet_daddr; key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET); - if (key) { + if (key && !tcp_rsk_used_ao(req)) { /* * We're using one, so create a matching key * on the newsk structure. If we fail to get @@ -1744,6 +1791,10 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb, sk_gso_disable(newsk); } #endif +#ifdef CONFIG_TCP_AO + if (tcp_ao_copy_all_matching(sk, newsk, req, skb, AF_INET)) + goto put_and_exit; /* OOM, release back memory */ +#endif if (__inet_inherit_port(sk, newsk) < 0) goto put_and_exit; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 87cf3073b6c2..8de62d76c663 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -494,6 +494,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; @@ -572,6 +575,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 3aea90605034..758c4a6e851b 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,32 @@ 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; + if (unlikely(OPTION_AO & options)) { u8 maclen; - 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; + 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)); + } else if (tcprsk) { + u8 aolen = tcprsk->maclen + sizeof(struct tcp_ao_hdr); + + maclen = tcprsk->maclen; + *ptr++ = htonl((TCPOPT_AO << 24) | (aolen << 16) | + (tcprsk->ao_keyid << 8) | + (tcprsk->ao_rcv_next)); + } 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))) { @@ -1411,7 +1425,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 */ @@ -3702,7 +3716,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 7fd31c60488a..31ae504af8e6 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -53,6 +53,18 @@ int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, htons(sk->sk_num), disn, sisn); } +int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, + struct request_sock *req) +{ + struct inet_request_sock *ireq = inet_rsk(req); + + return tcp_v6_ao_calc_key(mkt, key, + &ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr, + htons(ireq->ir_num), ireq->ir_rmt_port, + htonl(tcp_rsk(req)->snt_isn), + htonl(tcp_rsk(req)->rcv_isn)); +} + struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk, const struct in6_addr *addr, int sndid, int rcvid) @@ -70,6 +82,15 @@ struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); } +struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk, + struct request_sock *req, + int sndid, int rcvid) +{ + struct in6_addr *addr = &inet_rsk(req)->ir_v6_rmt_addr; + + return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); +} + int tcp_v6_ao_hash_pseudoheader(struct crypto_pool_ahash *hp, const struct in6_addr *daddr, const struct in6_addr *saddr, int nbytes) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 7bd4d0bb77cd..88a8e6ce41dc 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -853,6 +853,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 @@ -1237,9 +1241,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. @@ -1255,9 +1301,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); } @@ -1487,18 +1533,26 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * #ifdef CONFIG_TCP_MD5SIG l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); - /* Copy over the MD5 key from the original socket */ - key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr, l3index); - if (key) { - /* We're using one, so create a matching key - * on the newsk structure. If we fail to get - * memory, then we end up not copying the key - * across. Shucks. - */ - tcp_md5_key_copy(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, - AF_INET6, 128, l3index, key); + if (!tcp_rsk_used_ao(req)) { + const struct in6_addr *daddr = &newsk->sk_v6_daddr; + /* Copy over the MD5 key from the original socket */ + key = tcp_v6_md5_do_lookup(sk, daddr, l3index); + if (key) { + /* We're using one, so create a matching key + * on the newsk structure. If we fail to get + * memory, then we end up not copying the key + * across. Shucks. + */ + tcp_md5_key_copy(newsk, (union tcp_md5_addr *)daddr, + AF_INET6, 128, l3index, key); + } } #endif +#ifdef CONFIG_TCP_AO + /* Copy over tcp_ao_info if any */ + if (tcp_ao_copy_all_matching(sk, newsk, req, skb, AF_INET6)) + goto out; /* OOM */ +#endif if (__inet_inherit_port(sk, newsk) < 0) { inet_csk_prepare_forced_close(newsk); From patchwork Thu Oct 27 20:43:27 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022747 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 3F19FFA3740 for ; Thu, 27 Oct 2022 20:48:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237122AbiJ0UsP (ORCPT ); Thu, 27 Oct 2022 16:48:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42346 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237057AbiJ0UrD (ORCPT ); Thu, 27 Oct 2022 16:47:03 -0400 Received: from mail-wm1-x336.google.com (mail-wm1-x336.google.com [IPv6:2a00:1450:4864:20::336]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4E5E695272 for ; Thu, 27 Oct 2022 13:44:19 -0700 (PDT) Received: by mail-wm1-x336.google.com with SMTP id l32so1793985wms.2 for ; Thu, 27 Oct 2022 13:44:19 -0700 (PDT) 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=gvBXVhpcez3XO6Nwqh+zOqiYrLDIacbTKCXk4BjRv5s=; b=Z8PpdAqqLG+AGpd66BAet9X6Fv80W/8P937UE8S+yQNaeJMuJrIi1kPg5bS2RI1OPD 09AvAHVlARgtYke5uCesAim0uJbWxSBWRw+o2hBejZh/MFJ+kV29R96EuEON/1+bYZtT wji29Zh36zeZU4dXFtF4OWAB9o89wezEVDNn8nZhDxgPsVCXL6CNA8NFWoTnnPR+LxZi Y9V01B8k0vNg4H3HBGoYHSwATqESwLRojHSR6XB021D05rF/5bQdB/n0DbPxKt4f6je4 AKOESphu0YkETf1SZAN10+18mSdzqCN7Yxox5Vc1jGkUZ/XQM89w92fjojjzeHBUvH+k 5/HQ== 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=gvBXVhpcez3XO6Nwqh+zOqiYrLDIacbTKCXk4BjRv5s=; b=3uWHqRxixvI3ZYAjZvcN1ilFApGyhBQtxLcEk+KoSUwF8RCVAr+9lFpRDE37Af6OfZ Oswv6uxT11VK13c6zpdUxlCbHTrRtUesPPBFTnuSV2icwBVwTPsHyI3LZ/qdDLJsMeca TI/QO9wcWyHjXSDBvg30YDI5uBjGuwDh2cZcUUSHmHKgwbnb1Ngz/1PwzQolAXq3tDn/ yuz4L2BQyLk9HaNCdaqlzaVvsfMYwhPmVW1dVhL0KltfpEp7jfFkn7x0fbd4yKrHXfih vBYvb1CkovEstjYFIXLZx9x//B0JUQz4iTkHPvkKOBh3cVe8iyUBESKE65mI/TsAWkjC VC6Q== X-Gm-Message-State: ACrzQf287Yry95FsZf/lEVs4a/Fp9EfXFd5j9SleOKv81wOHHhtC9u39 OaRqqRAtafIY7syZMTIZd0jUbg== X-Google-Smtp-Source: AMsMyM7vEz5hsLAmtl7hI8821p0XsCoWUDY9fREyn9UuivLByQo+kHXLcj59zWAkmK9Ml3k2eekqTA== X-Received: by 2002:a05:600c:1e2a:b0:3c8:353b:253f with SMTP id ay42-20020a05600c1e2a00b003c8353b253fmr7307325wmb.51.1666903459436; Thu, 27 Oct 2022 13:44:19 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:19 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 16/36] net/tcp: Sign SYN-ACK segments with TCP-AO Date: Thu, 27 Oct 2022 21:43:27 +0100 Message-Id: <20221027204347.529913-17-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 37 +++++++++++++++++++++++++++++++------ net/ipv6/tcp_ao.c | 14 ++++++++++++++ net/ipv6/tcp_ipv6.c | 1 + 7 files changed, 71 insertions(+), 6 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index bda03d4925ed..5855ca6f1437 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2119,6 +2119,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 e93db866bd44..731f0d6263db 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -139,6 +139,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); @@ -173,6 +176,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 c9d3e29a28f1..e24a90505f08 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -604,6 +604,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 47325f148f29..37f344e2ae6b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1678,6 +1678,7 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { #ifdef CONFIG_TCP_AO .ao_lookup = tcp_v4_ao_lookup_rsk, .ao_calc_key = tcp_v4_ao_calc_key_rsk, + .ao_synack_hash = tcp_v4_ao_synack_hash, #endif #ifdef CONFIG_SYN_COOKIES .cookie_init_seq = cookie_v4_init_sequence, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 758c4a6e851b..3a121d9cd6dd 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3637,6 +3637,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; @@ -3687,16 +3688,32 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, tcp_rsk(req)->snt_synack = tcp_skb_timestamp_us(skb); } -#ifdef CONFIG_TCP_MD5SIG +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) rcu_read_lock(); - md5 = tcp_rsk(req)->af_specific->req_md5_lookup(sk, req_to_sk(req)); #endif + if (tcp_rsk_used_ao(req)) { +#ifdef CONFIG_TCP_AO + /* TODO: what should we do if the key is no longer available on + * the listening socket? Maybe we can try a different matching + * key (without sndid match). If that also fails what should + * we do? We currently send an unsigned synack. It's probably + * better to not send anything. + */ + ao_key = tcp_sk(sk)->af_specific->ao_lookup(sk, req_to_sk(req), + tcp_rsk(req)->ao_keyid, -1); +#endif + } else { +#ifdef CONFIG_TCP_MD5SIG + md5 = tcp_rsk(req)->af_specific->req_md5_lookup(sk, + req_to_sk(req)); +#endif + } skb_set_hash(skb, tcp_rsk(req)->txhash, PKT_HASH_TYPE_L4); /* bpf program will be interested in the tcp_flags */ TCP_SKB_CB(skb)->tcp_flags = TCPHDR_SYN | TCPHDR_ACK; tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, md5, - NULL, foc, synack_type, - syn_skb) + sizeof(*th); + ao_key, foc, synack_type, syn_skb) + + sizeof(*th); skb_push(skb, tcp_header_size); skb_reset_transport_header(skb); @@ -3716,7 +3733,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */ th->window = htons(min(req->rsk_rcv_wnd, 65535U)); - tcp_options_write(th, NULL, NULL, &opts, NULL); + tcp_options_write(th, NULL, tcp_rsk(req), &opts, NULL); th->doff = (tcp_header_size >> 2); __TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS); @@ -3724,7 +3741,15 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, /* Okay, we have all we need - do the md5 hash if needed */ if (md5) tcp_rsk(req)->af_specific->calc_md5_hash(opts.hash_location, - md5, req_to_sk(req), skb); + md5, req_to_sk(req), skb); +#endif +#ifdef CONFIG_TCP_AO + if (ao_key) + tcp_rsk(req)->af_specific->ao_synack_hash(opts.hash_location, + ao_key, req, skb, + opts.hash_location - (u8 *)th, 0); +#endif +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) rcu_read_unlock(); #endif diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index 31ae504af8e6..526bbe232a64 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -123,3 +123,17 @@ int tcp_v6_parse_ao(struct sock *sk, int cmd, { return tcp_parse_ao(sk, cmd, AF_INET6, optval, optlen); } + +int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key, + struct request_sock *req, const struct sk_buff *skb, + int hash_offset, u32 sne) +{ + char traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + + tcp_v6_ao_calc_key_rsk(ao_key, traffic_key, req); + + tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb, + traffic_key, hash_offset, sne); + + return 0; +} diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 88a8e6ce41dc..10ac715217cb 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -856,6 +856,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 Thu Oct 27 20:43:28 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022749 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 6D38DECAAA1 for ; Thu, 27 Oct 2022 20:48:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237141AbiJ0Usg (ORCPT ); Thu, 27 Oct 2022 16:48:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59434 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237072AbiJ0Ur0 (ORCPT ); Thu, 27 Oct 2022 16:47:26 -0400 Received: from mail-wr1-x429.google.com (mail-wr1-x429.google.com [IPv6:2a00:1450:4864:20::429]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B55C28E468 for ; Thu, 27 Oct 2022 13:44:22 -0700 (PDT) Received: by mail-wr1-x429.google.com with SMTP id v1so4147146wrt.11 for ; Thu, 27 Oct 2022 13:44:22 -0700 (PDT) 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=fbuCQXNmjpdxh8FOIMx6bq5L+gdQS8nq6z0bs9vBOpo=; b=SE+giiJiPpn058cfpePjqbtVBXgDVSB+yvMhKsc3unnKer/NE0YTIRonqeLvMQPaiW G2W3xKcGd52ED7o2Hneyko4Y7LhFwz1w3nXrQlMeS8GUYvxb6ordp/fEG6AN0GtlwG2A gEUxitYkAkTnDvlgeSNXJ71IIlJan2xc/6dd1ZOAX9HEe5FqiDvrsTeYogaxTaWOnhaR yOGP7Z8RW1XdBunt2IL2Wd0hBoUU/+gBR/gM7YWoX+pyf7eAWFaWJKa2IztQcfdyHoML zf8jz9OuGiV18NFEOix/WDVVDJxPRz24U079mW9SLAhapmTkCwz6l5jsljQ9hGC0fZdG 2zsA== 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=fbuCQXNmjpdxh8FOIMx6bq5L+gdQS8nq6z0bs9vBOpo=; b=vcgtkZiNixDv+YmRY/gZw/z6+WOP8Pc+4748Doi4u10KXOK06M4kFRF3piKNlbhGno DqVB6+24OdE7hxMFbqlx1pVVsKtlxWqIvpZwhxtYGPBhAAeOqUGBSLcxjQax0mFhOsS+ im8cnx3OJDnQMucPS9y+SShXTCzVXDTzKS7s4XGPYc9Q4y6RLyxhqtJOYrdIdDgPJxfY c7zApj7AkBocmwV+/nextXKVnum70PvHO7lBjIJKeTLqf/CU/NuNscS4HqiCIiYNuW7H 0aCObxr5aDi5GSWwupH0Z0f1E/sV2m7niIHOc2Y63j2yHbnEHkmeYJJMldDzEZlUd3Xo bVlg== X-Gm-Message-State: ACrzQf1KEmH5F5pgRKlafPL3ON1lxL/eVuAdrcZhHBrt5wSxLVYI9xqV 7k1/qUSeQxbys78QPDqma06r7w== X-Google-Smtp-Source: AMsMyM57tsRQzX6oKwbZRLbVtJqJWgfqRscC2ZWdfeL8s53XgtDgKxMzmOASFSWJf9764sMKjZCBzg== X-Received: by 2002:adf:e405:0:b0:236:6e0d:6ed2 with SMTP id g5-20020adfe405000000b002366e0d6ed2mr16694068wrm.338.1666903460951; Thu, 27 Oct 2022 13:44:20 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:20 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 17/36] net/tcp: Verify inbound TCP-AO signed segments Date: Thu, 27 Oct 2022 21:43:28 +0100 Message-Id: <20221027204347.529913-18-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 59 +++++++++++++++- include/net/tcp_ao.h | 22 ++++++ net/ipv4/tcp.c | 39 +++-------- net/ipv4/tcp_ao.c | 143 +++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 11 +-- net/ipv6/tcp_ao.c | 12 ++++ net/ipv6/tcp_ipv6.c | 11 +-- 8 files changed, 272 insertions(+), 43 deletions(-) diff --git a/include/net/dropreason.h b/include/net/dropreason.h index c1cbcdbaf149..1359b15a53f3 100644 --- a/include/net/dropreason.h +++ b/include/net/dropreason.h @@ -127,6 +127,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 @@ -142,6 +147,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 5855ca6f1437..454401452a93 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1703,7 +1703,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) @@ -1725,7 +1725,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; } @@ -2100,6 +2100,10 @@ struct tcp_sock_af_ops { const struct sock *sk, __be32 sisn, __be32 disn, bool send); + int (*ao_calc_key_skb)(struct tcp_ao_key *mkt, + u8 *key, + const struct sk_buff *skb, + __be32 sisn, __be32 disn); #endif }; @@ -2510,4 +2514,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 731f0d6263db..5eb4ae84b333 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, @@ -126,6 +129,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); @@ -147,9 +154,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); @@ -157,6 +169,9 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, int tcp_v6_ao_hash_pseudoheader(struct crypto_pool_ahash *hp, const struct in6_addr *daddr, const struct in6_addr *saddr, int nbytes); +int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, __be32 sisn, + __be32 disn); int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send); @@ -192,6 +207,13 @@ static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, { } +static inline enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, + const struct sk_buff *skb, unsigned short int family, + const struct request_sock *req, const struct tcp_ao_hdr *aoh) +{ + return SKB_NOT_DROPPED_YET; +} + static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 875149817036..7bfbb6330752 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4515,42 +4515,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; } @@ -4560,14 +4541,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 e24a90505f08..93bba5e791dd 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -335,6 +335,17 @@ int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, htonl(tcp_rsk(req)->rcv_isn)); } +int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, __be32 sisn, + __be32 disn) +{ + const struct iphdr *iph = ip_hdr(skb); + const struct tcphdr *th = tcp_hdr(skb); + + return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr, + th->source, th->dest, sisn, disn); +} + static int tcp_v4_ao_hash_pseudoheader(struct crypto_pool_ahash *hp, __be32 daddr, __be32 saddr, int nbytes) @@ -681,6 +692,138 @@ 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) +{ + const struct tcp_sock_af_ops *ops = tcp_sk(sk)->af_specific; + u8 key_buf[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; + const struct tcphdr *th = tcp_hdr(skb); + u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */ + struct tcp_ao_info *info; + struct tcp_ao_key *key; + __be32 sisn, disn; + u8 *traffic_key; + u32 sne = 0; + + info = rcu_dereference(tcp_sk(sk)->ao_info); + if (!info) + return SKB_DROP_REASON_TCP_AOUNEXPECTED; + + /* Fast-path */ + /* TODO: fix fastopen and simultaneous open (TCPF_SYN_RECV) */ + if (likely((1 << sk->sk_state) & (TCP_AO_ESTABLISHED | TCPF_SYN_RECV))) { + enum skb_drop_reason err; + + /* Check if this socket's rnext_key matches the keyid in the + * packet. If not we lookup the key based on the keyid + * matching the rcvid in the mkt. + */ + key = info->rnext_key; + if (key->rcvid != aoh->keyid) { + key = tcp_ao_do_lookup_rcvid(sk, aoh->keyid); + if (!key) + goto key_not_found; + } + + if (unlikely(th->syn && !th->ack)) { + /* Delayed retransmitted syn */ + sisn = th->seq; + disn = 0; + 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_do_lookup_sndid(sk, aoh->rnext_keyid); + if (key) + /* pairs with tcp_ao_del_cmd */ + WRITE_ONCE(info->current_key, key); + } + return SKB_NOT_DROPPED_YET; + } + + /* 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) { + sisn = th->seq; + disn = 0; + 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 (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; + ops->ao_calc_key_skb(key, traffic_key, skb, sisn, disn); + return tcp_ao_verify_hash(sk, skb, family, info, aoh, key, + traffic_key, phash, sne); + +key_not_found: + return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; +} + int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 37f344e2ae6b..ea1e15b28f8d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2205,9 +2205,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); @@ -2283,8 +2283,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; @@ -2443,6 +2443,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = { .calc_ao_hash = tcp_v4_ao_hash_skb, .ao_parse = tcp_v4_parse_ao, .ao_calc_key_sk = tcp_v4_ao_calc_key_sk, + .ao_calc_key_skb = tcp_v4_ao_calc_key_skb, #endif }; #endif diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index 526bbe232a64..f23c817166bb 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -39,6 +39,18 @@ int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, return tcp_ao_calc_traffic_key(mkt, key, &tmp, sizeof(tmp)); } +int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, + __be32 sisn, __be32 disn) +{ + const struct ipv6hdr *iph = ipv6_hdr(skb); + const struct tcphdr *th = tcp_hdr(skb); + + return tcp_v6_ao_calc_key(mkt, key, &iph->saddr, + &iph->daddr, th->source, + th->dest, sisn, disn); +} + int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 10ac715217cb..574c379bbaae 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1825,9 +1825,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); @@ -1899,8 +1899,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; @@ -2093,6 +2093,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = { .calc_ao_hash = tcp_v6_ao_hash_skb, .ao_parse = tcp_v6_parse_ao, .ao_calc_key_sk = tcp_v6_ao_calc_key_sk, + .ao_calc_key_skb = tcp_v6_ao_calc_key_skb, #endif }; #endif From patchwork Thu Oct 27 20:43:29 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022748 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 A74DCFA3744 for ; Thu, 27 Oct 2022 20:48:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237138AbiJ0Usd (ORCPT ); Thu, 27 Oct 2022 16:48:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35806 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236527AbiJ0UrZ (ORCPT ); Thu, 27 Oct 2022 16:47:25 -0400 Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D394F923CC for ; Thu, 27 Oct 2022 13:44:22 -0700 (PDT) Received: by mail-wr1-x42d.google.com with SMTP id a14so4174347wru.5 for ; Thu, 27 Oct 2022 13:44:22 -0700 (PDT) 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=yVvKx6rttXZkca0HaXrdqXjpHWYJcjtHtNTSTvO7FpY=; b=Ia827T2aSIcWAOc+9BbUNG5ygdPexvYQeIwBE+pbBwjvFfz8I3ze6DxS/XSWVhO6cP 6KF5wtBa5QTeLS+9OEBRSdXzNw/E4lu/nqVz/nEGQ4WbP9NtSpvx1W3lghxYnZOpJPid tEVncbxb/wY2VEAkbPbcd6sFHRQHu/XjN4zDc/jQQv/1OepfvZgW0HhUKPI9k3vS0hqN nRvRlqD+U6ow38DSTktGGmateeUnXNyiO5HtsK5wmoYVLrqkc/DyAio5hFMZakxz06v6 /gtO1dKCBd96BUWM3BLf0Wk7DP7OuEDAF/viGY006CRVTYsfzB7CEfDG5a2o/zabwNfz cjjQ== 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=yVvKx6rttXZkca0HaXrdqXjpHWYJcjtHtNTSTvO7FpY=; b=X6M1YyaZrvKhm9H73NHUeJoIrbN3NQjNpK0UMGw6bwMt9Siw7P4miTjfR0yYQ1up/7 T28Rx0HJstckcQLS+KkJNTjcZHhTm1r9TB8Uzlj8Izs5zfxJ92RajToW1BUysxwc6PaP XCZlIp6GOZ+eopaclq+dz914zRWrV1fCzX9M9OgPrUhfZMUAu68l8Cwesq1NrcG3nism Uc615qhhibckdVmQqiGMlvSxevComZ7ihwl+7VpF4sGaYXHH/b6yMXxYWDQ/y2gMQMMs 1q4RnrzwMBjR0wsUWFkXsQ2wMrTV6NfNaM6vFX0QlYkCSW7vY8GTVRCWPqbPWDOcnhKW /WBA== X-Gm-Message-State: ACrzQf0W2X8Aqtwbs1NNAgJmE6c9PPsgFb9WYGYTSCTRWxtj4/bCf4CG AYFnLMcKujHYQigzv6wT36eIsg== X-Google-Smtp-Source: AMsMyM55XJR71DgHzmialAs0tN1phVZkJa0x01m6S86iT9hD+TFygCM3efrE837m2EMAHy8kSsjhVQ== X-Received: by 2002:a5d:4f12:0:b0:22e:3920:a09c with SMTP id c18-20020a5d4f12000000b0022e3920a09cmr32216682wru.95.1666903462413; Thu, 27 Oct 2022 13:44:22 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:22 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 18/36] net/tcp: Add TCP-AO segments counters Date: Thu, 27 Oct 2022 21:43:29 +0100 Message-Id: <20221027204347.529913-19-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 1359b15a53f3..bd92bb1a0d94 100644 --- a/include/net/dropreason.h +++ b/include/net/dropreason.h @@ -148,17 +148,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 454401452a93..72a1fe015c57 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2548,8 +2548,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 5eb4ae84b333..cc9925644118 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -20,6 +20,13 @@ struct tcp_ao_hdr { u8 rnext_keyid; }; +struct tcp_ao_counters { + atomic64_t pkt_good; + atomic64_t pkt_bad; + atomic64_t key_not_found; + atomic64_t ao_required; +}; + struct tcp_ao_key { struct hlist_node node; union tcp_ao_addr addr; @@ -35,6 +42,8 @@ struct tcp_ao_key { u8 maclen; u8 digest_size; struct rcu_head rcu; + atomic64_t pkt_good; + atomic64_t pkt_bad; u8 traffic_keys[]; }; @@ -78,6 +87,7 @@ struct tcp_ao_info { */ struct tcp_ao_key *volatile current_key; struct tcp_ao_key *rnext_key; + struct tcp_ao_counters counters; u8 ao_flags; __be32 lisn; __be32 risn; diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index 4d7470036a8b..f09119db8b40 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -292,6 +292,10 @@ enum LINUX_MIB_TCPDSACKIGNOREDDUBIOUS, /* TCPDSACKIgnoredDubious */ LINUX_MIB_TCPMIGRATEREQSUCCESS, /* TCPMigrateReqSuccess */ LINUX_MIB_TCPMIGRATEREQFAILURE, /* TCPMigrateReqFailure */ + LINUX_MIB_TCPAOREQUIRED, /* TCPAORequired */ + LINUX_MIB_TCPAOBAD, /* TCPAOBad */ + LINUX_MIB_TCPAOKEYNOTFOUND, /* TCPAOKeyNotFound */ + LINUX_MIB_TCPAOGOOD, /* TCPAOGood */ __LINUX_MIB_MAX }; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 5386f460bd20..689957a81553 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -297,6 +297,10 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPDSACKIgnoredDubious", LINUX_MIB_TCPDSACKIGNOREDDUBIOUS), SNMP_MIB_ITEM("TCPMigrateReqSuccess", LINUX_MIB_TCPMIGRATEREQSUCCESS), SNMP_MIB_ITEM("TCPMigrateReqFailure", LINUX_MIB_TCPMIGRATEREQFAILURE), + SNMP_MIB_ITEM("TCPAORequired", LINUX_MIB_TCPAOREQUIRED), + SNMP_MIB_ITEM("TCPAOBad", LINUX_MIB_TCPAOBAD), + SNMP_MIB_ITEM("TCPAOKeyNotFound", LINUX_MIB_TCPAOKEYNOTFOUND), + SNMP_MIB_ITEM("TCPAOGood", LINUX_MIB_TCPAOGOOD), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 93bba5e791dd..cdd4e4ed69cf 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -222,6 +222,8 @@ static struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, *new_key = *key; INIT_HLIST_NODE(&new_key->node); crypto_pool_add(new_key->crypto_pool_id); + atomic64_set(&new_key->pkt_good, 0); + atomic64_set(&new_key->pkt_bad, 0); return new_key; } @@ -702,14 +704,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; } @@ -729,8 +742,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; + } /* Fast-path */ /* TODO: fix fastopen and simultaneous open (TCPF_SYN_RECV) */ @@ -821,6 +836,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; } @@ -1497,6 +1514,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 Thu Oct 27 20:43:30 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022750 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 B79B8FA3744 for ; Thu, 27 Oct 2022 20:48:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237088AbiJ0Usr (ORCPT ); Thu, 27 Oct 2022 16:48:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42806 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237081AbiJ0Ur1 (ORCPT ); Thu, 27 Oct 2022 16:47:27 -0400 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 97FBB95E7C for ; Thu, 27 Oct 2022 13:44:25 -0700 (PDT) Received: by mail-wm1-x330.google.com with SMTP id v130-20020a1cac88000000b003bcde03bd44so5031054wme.5 for ; Thu, 27 Oct 2022 13:44:25 -0700 (PDT) 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=LVCplO2RHnjfrC7RtgmpM1L5+zWhF42z97+0t0sfJN8=; b=QL6SzRszsclV++kFI3bieAAVbkGgC2T/1v4taDU6gePMi0RQi/NEtAR7EBP0VuvzFX laiQ8UKgVGa/jhuL3yrkzunNKcqpMhg8x/w0tkOX+GBCqM1CNsKtTAiCh8eWYouRGsey VbwOC8LiruovXfGGw3u2DgxDXh58qAGHskwp37i5DYqtfJIZw4LkIxDIObyYjECbuBbY xlAaWqi+XSynChBBRleFOE65Dqg8qUX/9rKMzmrNmRHYTU4jjI0gSUtrs1oTjV2Oxptm PCuBbVnACirSe1tRG7OpgftGKmSD/FNqrw3C/R3YUloHoRQjm6v3Kfcvf2HbAazSXzW6 SuUg== 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=LVCplO2RHnjfrC7RtgmpM1L5+zWhF42z97+0t0sfJN8=; b=xtufXprBGFvnjGTg1KZYQMK3i7PJnPRSTBy/07HWkeQPi+jqI3ffmu1mugWmCOTlfx wuUCeIhxCNb3ZcKZSiKweizQ70mxUacqVl1uW6Hc9Bs3ahSCnmQxVM0IpMoQjucBwL3l xZYeZO/DtV+A2xZDdzI6KZwvw/k2BkvQLsdvIG79MPCxUnJ6KWAIJF001svwXID3jnHn p7XiyD9NEZQrQLl5a25JczxaQq00wAGAugq9t7e3/vX+XaiFw60LBTR2uRFNQWsqpWip SPcWTg/C81b11L5KMt0R7RrVwC4SAclBVBYKPf533UOqF5HHy7EPx6bh24Oz67CToRHR xWnw== X-Gm-Message-State: ACrzQf3u+N4dMwQi9CyGru92Felj17LTdYBemPQo8Xnxfp+xDEGi66pE eL+aAP5Ci1OUAEigM0dkZXNFvQ== X-Google-Smtp-Source: AMsMyM5Bkna4YGn0LvtL0EQZ6lzgKMDWqAV1GMSG6lQWuxQ0sfo5KsmMWIlF3iwOR1XtxMvZwI6ZXg== X-Received: by 2002:a05:600c:3592:b0:3c6:f9db:a954 with SMTP id p18-20020a05600c359200b003c6f9dba954mr7176562wmq.170.1666903463838; Thu, 27 Oct 2022 13:44:23 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:23 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 19/36] net/tcp: Add TCP-AO SNE support Date: Thu, 27 Oct 2022 21:43:30 +0100 Message-Id: <20221027204347.529913-20-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 df3087c8179f..2ba46d5db421 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); } @@ -6357,6 +6381,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 Thu Oct 27 20:43:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022752 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 37F4CFA3743 for ; Thu, 27 Oct 2022 20:49:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237092AbiJ0UtK (ORCPT ); Thu, 27 Oct 2022 16:49:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37480 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235239AbiJ0Ur3 (ORCPT ); Thu, 27 Oct 2022 16:47:29 -0400 Received: from mail-wr1-x436.google.com (mail-wr1-x436.google.com [IPv6:2a00:1450:4864:20::436]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C285596202 for ; Thu, 27 Oct 2022 13:44:25 -0700 (PDT) Received: by mail-wr1-x436.google.com with SMTP id k8so4194102wrh.1 for ; Thu, 27 Oct 2022 13:44:25 -0700 (PDT) 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=zKqpBppUUTfj8qPMla2g/eqAPUAs1l844TLIW4L/wKM=; b=aZ4J6HLyTTtGQQ3ADZYb5vbN8t8obXuP8POZjn8duiWtHgn9fBwnmHEQSRrwL94vk1 c+rGmQZI1E69hkTQaJW/EDCo9lF3LmKXtzk8xE/XzrgrLWgMnv2q2dqtjgJZ0ZHwp92t uF+lXT3lbGRsZ3F32JPkmlYcDqQqwPKD/FGvoYSPRcG41IWF2ZafRJll1rrI3EJ2OCwL IL6tSdLYnnuok3K6cRzjgu2gaElPqBamWPU+auDQ2nT40gGMqqDnZJskjajmS76+anCq Oes34gjuUr5I4xRMPjMffXDWAY+59+dgVZRqzgZg2PUVenbTwEfrZDmB+jNweyXvt86w S1VQ== 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=zKqpBppUUTfj8qPMla2g/eqAPUAs1l844TLIW4L/wKM=; b=qcUo8QiA6ifeYxS6ZS+XVK5lOls5bU4bf6X12NYksMj1Ce4AiJjZgK0klHT4mT1YL2 Kha5fBPEoWFZNmNHrz2pVBmyl/6oJX2fINTza3mAX8ZaoUoUI9OhYwIjm3kypnpStDya KUtkKjs8Rwa7KrZX+VcZrTFAz0riBf0dWYAcJ1c1K7+4Kxfzm3BIxuyk7gV71vAVFmxl KXY9mc1g6mkwyDYrpjZ18magh07pEeHVMalL/RGkkJExsnzeggKKXI1H1uvjJGLg7O6b Ve8RmPkivmo0D4+HSXVhw+Rw8HsvHpnQQfX5SGG/ERL13DEexwWGhy76qhoL0el1GalT 2P+Q== X-Gm-Message-State: ACrzQf2t2jW4Aq4Ehzxyy0JCYHcqxJ3v0wPxLiY+6QKqI6GosFF/EP7s mA4VHOOePI/dPb/JCFLxkyYlkA== X-Google-Smtp-Source: AMsMyM7Z5vqlZqy5fRHJB0X8pJfRxiOkaNIUHdsp+IDiax0jjvjbsv3Nl0jPqc60AydiovlC6uYS4g== X-Received: by 2002:a05:6000:811:b0:236:622d:a7d6 with SMTP id bt17-20020a056000081100b00236622da7d6mr19178998wrb.258.1666903465241; Thu, 27 Oct 2022 13:44:25 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:24 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 20/36] net/tcp: Add tcp_hash_fail() ratelimited logs Date: Thu, 27 Oct 2022 21:43:31 +0100 Message-Id: <20221027204347.529913-21-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 72a1fe015c57..5512eb940441 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2527,12 +2527,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 @@ -2555,11 +2562,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 cc9925644118..b5ebc133399e 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 7bfbb6330752..8d64bdec3af8 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4523,7 +4523,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; @@ -4533,6 +4532,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; } @@ -4548,16 +4548,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 cdd4e4ed69cf..307b279d55f5 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -708,6 +708,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; } @@ -718,6 +720,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); @@ -744,6 +747,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; } @@ -838,6 +843,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 Thu Oct 27 20:43:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022756 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 44D69FA3743 for ; Thu, 27 Oct 2022 20:50:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237079AbiJ0Uuq (ORCPT ); Thu, 27 Oct 2022 16:50:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:42812 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237152AbiJ0Usp (ORCPT ); Thu, 27 Oct 2022 16:48:45 -0400 Received: from mail-wr1-x42a.google.com (mail-wr1-x42a.google.com [IPv6:2a00:1450:4864:20::42a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8471611809 for ; Thu, 27 Oct 2022 13:44:47 -0700 (PDT) Received: by mail-wr1-x42a.google.com with SMTP id w14so4155724wru.8 for ; Thu, 27 Oct 2022 13:44:47 -0700 (PDT) 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=Y+GcHsG8qLVjG0rSiBASFRWY0wPZfYV0BHIEeu38I0o=; b=JUM54AHgrzF2dn8bTnwmKirKzDr79VWp7/BneNsD0qZpTiZM+7JGWrlgwUXbc4u6GB iFJvEYb8hLAOiWKCUqvw5YkI3wLVjPzbUCdhd0d34w8NrzCq6Blmf0yuMlS6yx+sWC1Y 8KSD3pCEnNQvXx8xt4rhrEqyE6l4oDihazmmaL1jiXStS8l8ZWiJaiRhtNEorEdN9Fx8 lJpZDNSuLYZhC8okMOEaeZhwggsSukntd+GPgWL6LTCFDEd36GN5ULJN4tZTYQxat+a6 QCQnfGM2K/cUVZZ6RS6gqKOO36aMJKl+qk3z/9e0jQ7tMU5zcd0W79UpgaL1mcabxP7E mn2Q== 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=Y+GcHsG8qLVjG0rSiBASFRWY0wPZfYV0BHIEeu38I0o=; b=T6+xJveIOJaKUbU5M1xhRq69Ei+OPQkH0dxHg0KUSz68HZgb1gMcWj5lSdOT29QLFI UQ5SAAnCvM1B8YzwmLB7RJSYp2DJteCLk0TA+Gfo0n+VUuCTFXKq8T/1lTSwH5hf2AHT /sMbTeVqDCoixVnO+A8JmhE14yME38/ycfsflee6hNQFnbrtCDKaessiY83oeiR/LuhP Q4Lt/YjyK0HGr/VcVTf2Z4AASg30j4EWqBuCyKVO0rahnamz7KxD+5lAKuv67ZTe2Mrl icsAhb0YqgF4xh2krMaBwiG7RePfzovSPQ5qDai7Gsfwja/v3O54RnxfdrfnmD/em2wB /TVA== X-Gm-Message-State: ACrzQf2zlygR2wzfa9YVwAipr7it5g7nKi8NzXPUuMYuvjXd/OAIvL+h skrPotkd9TPYb5qtHs5VIZ2L/Q== X-Google-Smtp-Source: AMsMyM7mQa+VOR446hEmboKgPeHDivV1YpHGwCto2pTR1PVmMX28/V1ij92hz6nQveZcRKeYuJD1Cw== X-Received: by 2002:adf:f98a:0:b0:236:677c:2407 with SMTP id f10-20020adff98a000000b00236677c2407mr17999689wrr.578.1666903466772; Thu, 27 Oct 2022 13:44:26 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:26 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 21/36] net/tcp: Ignore specific ICMPs for TCP-AO connections Date: Thu, 27 Oct 2022 21:43:32 +0100 Message-Id: <20221027204347.529913-22-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 b5ebc133399e..6e87d36fc1c3 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; @@ -166,6 +170,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, @@ -244,6 +249,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 f09119db8b40..bc7655394e9a 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -296,6 +296,7 @@ enum LINUX_MIB_TCPAOBAD, /* TCPAOBad */ LINUX_MIB_TCPAOKEYNOTFOUND, /* TCPAOKeyNotFound */ LINUX_MIB_TCPAOGOOD, /* TCPAOGood */ + LINUX_MIB_TCPAODROPPEDICMPS, /* TCPAODroppedIcmps */ __LINUX_MIB_MAX }; diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 5369458ae89f..508bedbc6ad8 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -349,6 +349,7 @@ struct tcp_diag_md5sig { #define TCP_AO_CMDF_CURR (1 << 0) /* Only checks field sndid */ #define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */ +#define TCP_AO_CMDF_ACCEPT_ICMP (1 << 2) /* Accept incoming ICMPs */ struct tcp_ao { /* setsockopt(TCP_AO) */ struct __kernel_sockaddr_storage tcpa_addr; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 689957a81553..82ff83a9afaa 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -301,6 +301,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPAOBad", LINUX_MIB_TCPAOBAD), SNMP_MIB_ITEM("TCPAOKeyNotFound", LINUX_MIB_TCPAOKEYNOTFOUND), SNMP_MIB_ITEM("TCPAOGood", LINUX_MIB_TCPAOGOOD), + SNMP_MIB_ITEM("TCPAODroppedIcmps", LINUX_MIB_TCPAODROPPEDICMPS), SNMP_MIB_SENTINEL }; diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 307b279d55f5..e5b4e9f1e83e 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; +} + static struct tcp_ao_key *tcp_ao_do_lookup_keyid(struct tcp_ao_info *ao, int sndid, int rcvid) { @@ -1441,7 +1499,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) @@ -1524,6 +1582,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; @@ -1683,6 +1746,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 ea1e15b28f8d..782cc790c6fa 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -504,6 +504,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; } @@ -518,6 +520,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 574c379bbaae..2a0477899c50 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -411,6 +411,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; } @@ -422,6 +424,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 Thu Oct 27 20:43:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022751 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 9794FECAAA1 for ; Thu, 27 Oct 2022 20:48:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237153AbiJ0Usp (ORCPT ); Thu, 27 Oct 2022 16:48:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59452 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237083AbiJ0Ur2 (ORCPT ); Thu, 27 Oct 2022 16:47:28 -0400 Received: from mail-wr1-x42a.google.com (mail-wr1-x42a.google.com [IPv6:2a00:1450:4864:20::42a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0B86CDE6 for ; Thu, 27 Oct 2022 13:44:28 -0700 (PDT) Received: by mail-wr1-x42a.google.com with SMTP id l14so4187257wrw.2 for ; Thu, 27 Oct 2022 13:44:28 -0700 (PDT) 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=MoGdmlHHpEUPuXthWjW8u1iP64OMQnsBPTKyfVuhzXI=; b=gSAoOgAv2U5kJtyYr7yDdRnMNeIyd8FzGikQ98Di2J6T1ItIL6ZS7GHQKzHKdPPYKd +8ruIARt0FKlU/ZUA3yWYh5WA5tb5Ewekp8C6bFUOTRHypdAcBI4RNf5z1duywraGfQc +znM2TcBP5AMUY+8iu94mJECQWwO7BeRw20D3ywcHjHkECx3pz9e7GMpwln4EgzTlSPu X4m6PFwa7d/J+znya2T3k/IxWaPAeZ8v9NfmbuBL04iSsWxQsFzNeEDveG4N1YBubKte IP0+9DtaAt6hKJxAyxfOrKY4qVMoYPwfqA+s8SDg/kH6chE6FYJaj0WP9Jzn3aJGFl+2 F7pA== 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=MoGdmlHHpEUPuXthWjW8u1iP64OMQnsBPTKyfVuhzXI=; b=4mup3Ln18eRhUgNoZGbZM6ps01KdpP3AV/Y0RTdlepYs5qwqcom6EZWbE22KzZgL3V ShgKKbs4C+OnRXedw5qVJNIpJnQozF20+xaqtsGxyPZCq78KHfyRzH5FFuDD1eMqw8qv YezJa/hMsbV7CuJUtYf0DOpQ0C8+U0VuD66jvJrSEvStX6RsiYq0fVey7r9NFljNU1Sn 9benKwemhlVv/EX+jQNWGcyTjy6CsqwD4JBzHGH/bhTbAF3zEkHQo060uqRNHrWdMaBk zleYciTWw5Lit72CV4gDRU/4Tkfds29nyaGYZCvkdcWKpyIZEM2zDknHhsPSMCBXZVL7 VKqg== X-Gm-Message-State: ACrzQf3AsMd7ksmH2LlhWjEPEq/zacQbQDsN+zffqv7ns+CRJ/BHZ2OO snuXkHHbn6pBqsOUWFgPyqN+Ew== X-Google-Smtp-Source: AMsMyM51y4XSGK/cdyuNHhoEc2nyqHNHVj8t5fp9PknmjCaJ0QeKC/pAd/WipxL7dQK12trDfHqLqg== X-Received: by 2002:adf:e64e:0:b0:236:76a9:446 with SMTP id b14-20020adfe64e000000b0023676a90446mr14065104wrn.696.1666903468407; Thu, 27 Oct 2022 13:44:28 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:27 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 22/36] net/tcp: Add option for TCP-AO to (not) hash header Date: Thu, 27 Oct 2022 21:43:33 +0100 Message-Id: <20221027204347.529913-23-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 2 ++ net/ipv4/tcp_ao.c | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 508bedbc6ad8..b60933ee2a27 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -347,6 +347,8 @@ struct tcp_diag_md5sig { #define TCP_AO_MAXKEYLEN 80 +#define TCP_AO_KEYF_EXCLUDE_OPT (1 << 0) + #define TCP_AO_CMDF_CURR (1 << 0) /* Only checks field sndid */ #define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */ #define TCP_AO_CMDF_ACCEPT_ICMP (1 << 2) /* Accept incoming ICMPs */ diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index e5b4e9f1e83e..90b864d4ee7d 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -604,7 +604,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); @@ -646,7 +647,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)) @@ -1497,7 +1499,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 Thu Oct 27 20:43:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022758 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 AC0C3FA3740 for ; Thu, 27 Oct 2022 20:51:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236535AbiJ0UvP (ORCPT ); Thu, 27 Oct 2022 16:51:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39058 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237096AbiJ0Ute (ORCPT ); Thu, 27 Oct 2022 16:49:34 -0400 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 B15FD1A04C for ; Thu, 27 Oct 2022 13:44:52 -0700 (PDT) Received: by mail-wr1-x42c.google.com with SMTP id k8so4194420wrh.1 for ; Thu, 27 Oct 2022 13:44:52 -0700 (PDT) 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=6VUyfsSLg6lAD6Q4sT0EE4CVv2owqStEeQ0Ke6FGmkM=; b=LivEWePsBpdEbnRrI85OptieqNJfD1OvVHU1egwKLYHUrL1uJIg95KqRrhA5LnRowP LeGMgTvu1r0+gHVpw7e2urZCNpRt9hHuD2nB8f41evDT8CQ2X6xYbgjIyNwz/zNPLKlD xsv/fooPRr1dgugEBi2McR/YpZieP/scsyyvy9yQdrRS4wQ0PmyEofqo8WghY9/AhHl6 10OYeDWBS/IoYlZwkPTDQ84oD5kFoACbv3t5/uChX+2WGPNRq++m5WTVYJ7J+y7BwWOP 7Di6011TMdzDIcEtq2CLrJ6FPuHSwLypmm78EpTxYmeAbZP6wtFrWHbopomeigHvmuK1 wP7g== 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=6VUyfsSLg6lAD6Q4sT0EE4CVv2owqStEeQ0Ke6FGmkM=; b=yqT1xisNEp9gIAXe8U9dSmX4CCcZNpJxe7sJwLU7ikd5isMhxZ+FjXaPWc/B9V5wLN poyLoJiwSXsZHfBHipm2MQqi4Vj22RYhNUcV8exahZX+Q46rSmVkizIqqnHJQVXupOVW blgKn+i08bSqN09cs1gVQaGuH+vnl3SxcBqA1PV1H6LPnFP3w4Xs+CCdOtrZzEM4ohal T9VjuFR88VxrgBWbKwAJbdN1sqtF+5Wjx1SOXDhqZ0/y+ZKC+l3YBeeRWZZHmNA6SOXZ A9xPAQw+KAEsRJ83fOE+pE9AJv1i9kR/B75penwJaI8GQCH6YasjYS8ZBSa9AnMqp3qh RgEA== X-Gm-Message-State: ACrzQf0ltjOKCz7VwSi4wP63tVOjG2NQFK1tA/qcEp6BzFlAQyHcCk3y 6j8nf3DWRC2V2zZ5OPaQexwvKg== X-Google-Smtp-Source: AMsMyM4zBPTcftyrKOxqW/i8CUlJILhqjLqbRgtU/2esL8kmjghk1jnCtMhHVRqxZhe4HZo54vPoyg== X-Received: by 2002:adf:e609:0:b0:235:f4e6:c7c1 with SMTP id p9-20020adfe609000000b00235f4e6c7c1mr26802106wrm.217.1666903470117; Thu, 27 Oct 2022 13:44:30 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:29 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 23/36] net/tcp: Add getsockopt(TCP_AO_GET) Date: Thu, 27 Oct 2022 21:43:34 +0100 Message-Id: <20221027204347.529913-24-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 19 ++++ net/ipv4/tcp.c | 11 ++ net/ipv4/tcp_ao.c | 225 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 256 insertions(+) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 6e87d36fc1c3..67f5273396ec 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -171,6 +171,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 b60933ee2a27..453187d21da8 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -132,6 +132,7 @@ enum { #define TCP_AO 38 /* (Add/Set MKT) */ #define TCP_AO_DEL 39 /* (Delete MKT) */ #define TCP_AO_MOD 40 /* (Modify MKT) */ +#define TCP_AO_GET 41 /* (Get MKTs) */ #define TCP_REPAIR_ON 1 #define TCP_REPAIR_OFF 0 @@ -353,6 +354,10 @@ struct tcp_diag_md5sig { #define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */ #define TCP_AO_CMDF_ACCEPT_ICMP (1 << 2) /* Accept incoming ICMPs */ +#define TCP_AO_GET_CURR TCP_AO_CMDF_CURR +#define TCP_AO_GET_NEXT TCP_AO_CMDF_NEXT +#define TCP_AO_GET_ALL (1 << 2) + struct tcp_ao { /* setsockopt(TCP_AO) */ struct __kernel_sockaddr_storage tcpa_addr; char tcpa_alg_name[64]; @@ -382,6 +387,20 @@ struct tcp_ao_mod { /* setsockopt(TCP_AO_MOD) */ __u8 tcpa_rnext; } __attribute__((aligned(8))); +struct tcp_ao_getsockopt { /* getsockopt(TCP_AO_GET) */ + struct __kernel_sockaddr_storage addr; + __u8 sndid; + __u8 rcvid; + __u32 nkeys; + char alg_name[64]; + __u16 flags; + __u8 prefix; + __u8 maclen; + __u8 keyflags; + __u8 keylen; + __u8 key[TCP_AO_MAXKEYLEN]; +} __attribute__((aligned(8))); + /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ #define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8d64bdec3af8..b8c7104cf0c0 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -4386,6 +4386,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 90b864d4ee7d..a29c5bb8586b 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -1504,6 +1504,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) @@ -1781,3 +1783,226 @@ 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; + + ss_family = opt_in.addr.ss_family; + + BUILD_BUG_ON(TCP_AO_GET_ALL & (TCP_AO_GET_CURR | TCP_AO_GET_NEXT)); + if (opt_in.flags & ~TCP_AO_GETF_VALID) + return -EINVAL; + + max_keys = opt_in.nkeys; + copy_all = !!(opt_in.flags & TCP_AO_GET_ALL); + copy_current = !!(opt_in.flags & TCP_AO_GET_CURR); + copy_next = !!(opt_in.flags & TCP_AO_GET_NEXT); + + if (!(copy_all || copy_current || copy_next)) { + prefix_in = opt_in.prefix; + + switch (ss_family) { + case AF_INET: { + sin = (struct sockaddr_in *)&opt_in.addr; + port = sin->sin_port; + addr = (union tcp_ao_addr *)&sin->sin_addr; + + if (prefix_in > 32) + return -EINVAL; + + if (sin->sin_addr.s_addr == INADDR_ANY && + prefix_in != 0) + return -EINVAL; + + break; + } + case AF_INET6: { + sin6 = (struct sockaddr_in6 *)&opt_in.addr; + addr = (union tcp_ao_addr *)&sin6->sin6_addr; + addr6 = &sin6->sin6_addr; + port = sin6->sin6_port; + + if (prefix_in != 0) { + if (ipv6_addr_v4mapped(addr6)) { + __be32 addr4 = addr6->s6_addr32[3]; + + if (prefix_in > 32 || + addr4 == INADDR_ANY) + return -EINVAL; + } else { + if (ipv6_addr_any(addr6) || + prefix_in > 128) + return -EINVAL; + } + } else if (!ipv6_addr_any(addr6)) { + return -EINVAL; + } + + break; + } + default: + return -EINVAL; + } + } + + bytes_to_write = min(user_len, (int)sizeof(struct tcp_ao_getsockopt)); + copied_keys = 0; + matched_keys = 0; + + hlist_for_each_entry_rcu(key, &ao_info->head, node) { + if (copy_all) + goto match; + + if (copy_current || copy_next) { + if (copy_current && key == ao_info->current_key) + goto match; + if (copy_next && key == ao_info->rnext_key) + goto match; + continue; + } + + if (tcp_ao_key_cmp(key, addr, opt_in.prefix, + opt_in.addr.ss_family, + opt_in.sndid, opt_in.rcvid, port) != 0) + continue; +match: + matched_keys++; + if (copied_keys >= max_keys) + continue; + + memset(&opt_out, 0, sizeof(struct tcp_ao_getsockopt)); + + if (key->family == AF_INET) { + struct sockaddr_in *sin_out = (struct sockaddr_in *)&opt_out.addr; + + sin_out->sin_family = key->family; + sin_out->sin_port = ntohs(key->port); + memcpy(&sin_out->sin_addr, &key->addr, sizeof(struct in_addr)); + } else { + struct sockaddr_in6 *sin6_out = (struct sockaddr_in6 *)&opt_out.addr; + + sin6_out->sin6_family = key->family; + sin6_out->sin6_port = ntohs(key->port); + memcpy(&sin6_out->sin6_addr, &key->addr, sizeof(struct in6_addr)); + } + opt_out.sndid = key->sndid; + opt_out.rcvid = key->rcvid; + opt_out.prefix = key->prefixlen; + opt_out.keyflags = key->keyflags; + opt_out.flags = 0; + if (key == ao_info->current_key) + opt_out.flags |= TCP_AO_GET_CURR; + if (key == ao_info->rnext_key) + opt_out.flags |= TCP_AO_GET_NEXT; + opt_out.nkeys = 0; + opt_out.maclen = key->maclen; + opt_out.keylen = key->keylen; + memcpy(&opt_out.key, key->key, key->keylen); + crypto_pool_algo(key->crypto_pool_id, opt_out.alg_name, 64); + + /* Copy key to user */ + if (copy_to_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 Thu Oct 27 20:43:35 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022781 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 61712FA3740 for ; Thu, 27 Oct 2022 20:51:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237204AbiJ0Uvz (ORCPT ); Thu, 27 Oct 2022 16:51:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54436 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237090AbiJ0UvK (ORCPT ); Thu, 27 Oct 2022 16:51:10 -0400 Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 93E5750FB9 for ; Thu, 27 Oct 2022 13:45:12 -0700 (PDT) Received: by mail-wm1-x332.google.com with SMTP id l14-20020a05600c1d0e00b003c6ecc94285so4501937wms.1 for ; Thu, 27 Oct 2022 13:45:12 -0700 (PDT) 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=W8i/gl7wAgw3adUScG+83KXrsRY+kJ2aoxLCf0MMJXM=; b=Xam3LaxAeiTB1zvHvzhans3VEgoaEcognLyeZZUJYqWSgbnPd4ExdVXjvAkwyBz5yO rKmW9Pc/0jQhqIgfdQ9QuHfCm6yS2P197cjG5MVsQBBO1zm33UJaDK6dXIjLTkLQM4Ua vL1e8IKfw8SifzkEmV7KRqmlUlgTyuid1Qhr1+VqPSxEneC+RxBXQCwfQkkdHVRhvtGf 1DdpxtuWTMIxgYX1iWIJ8NFBfgRrXRY+HV5l37c5Ee6OVROsuXO85rrGx8O+W4sCiqbi euGSH+iww3mHEPmyubwlrq827rfLlWpeZU8Ro/yFuqGcgx4X9xwkwxpdXaOlE9NDHPAI hjSg== 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=W8i/gl7wAgw3adUScG+83KXrsRY+kJ2aoxLCf0MMJXM=; b=kK5pOT+jdxcfVPYQnhQaMcJkeQmdU+OngpN4kUMTuyMvEaDS1vG8IzCmZS9NBp7wix iLMzxP97fWNvuIzFqmwaNURt4MUXKnJR3RwOdf54o8c4cc6hLsRp3+TLAKwSm3g8HURm Tqnr0J16e6f04kvBlJXtHmTbTi4FzTGISlOsAzG/VJG+4wtxbBPpk5Thv4t7QW1dLAeF 9By6HKg+TVRqCDjqOTsAYXsi1XmNk9jaC4XpQFSFKSmJttQDwao+66hfZ1xjYe3CTSOe WbZS1x2xTDAFtQyKchjY7IlgJwZ3uOeRjTomq755MDwu2V0V6SoMfrHDhKCYbiB1yBP1 v3wQ== X-Gm-Message-State: ACrzQf1UirQF3nkLItGZu+isPEOr6KFESpD/3FVSNI1z/CetuLcAn9aD n43jmqOd5BCl/iGn05LIusVi/A== X-Google-Smtp-Source: AMsMyM4YHmm8g7cbjtPxeWmHmHYTUdfHDi0MtcWyV5hSvf3PxdBYYnCOxEEF8w5Pkder3UfJUWGXAA== X-Received: by 2002:a7b:cbce:0:b0:3c6:fb29:6084 with SMTP id n14-20020a7bcbce000000b003c6fb296084mr7290569wmi.131.1666903471567; Thu, 27 Oct 2022 13:44:31 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:31 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 24/36] net/tcp: Allow asynchronous delete for TCP-AO keys (MKTs) Date: Thu, 27 Oct 2022 21:43:35 +0100 Message-Id: <20221027204347.529913-25-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 453187d21da8..42850ae6e99d 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -353,6 +353,9 @@ struct tcp_diag_md5sig { #define TCP_AO_CMDF_CURR (1 << 0) /* Only checks field sndid */ #define TCP_AO_CMDF_NEXT (1 << 1) /* Only checks field rcvid */ #define TCP_AO_CMDF_ACCEPT_ICMP (1 << 2) /* Accept incoming ICMPs */ +#define TCP_AO_CMDF_DEL_ASYNC (1 << 3) /* Asynchronious delete, valid + * only for listen sockets + */ #define TCP_AO_GET_CURR TCP_AO_CMDF_CURR #define TCP_AO_GET_NEXT TCP_AO_CMDF_NEXT diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index a29c5bb8586b..46df1d455889 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -1503,7 +1503,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) @@ -1629,11 +1629,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 Thu Oct 27 20:43:36 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022782 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 02F1BECAAA1 for ; Thu, 27 Oct 2022 20:52:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237220AbiJ0Uv5 (ORCPT ); Thu, 27 Oct 2022 16:51:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47194 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237122AbiJ0Uvh (ORCPT ); Thu, 27 Oct 2022 16:51:37 -0400 Received: from mail-wm1-x336.google.com (mail-wm1-x336.google.com [IPv6:2a00:1450:4864:20::336]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 035C2541B3 for ; Thu, 27 Oct 2022 13:45:12 -0700 (PDT) Received: by mail-wm1-x336.google.com with SMTP id b20-20020a05600c4e1400b003cc28585e2fso2118970wmq.1 for ; Thu, 27 Oct 2022 13:45:12 -0700 (PDT) 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=BO0mm+QEf5Np62JXxlh17iSFdxt4l5JfCFy1h9DfxFc=; b=bZ51BqO1op2Q3t8/SRCIkOpd+PNFcHn5mgtntOQdkUDwjAB0msiKkN7hquaoJxMIwV G32tPbu0ooycFl1It+qSg2dq/iXmlcZ+PSZdF1KQE5/1ax+zAV2LArjka0h0A+rlN9J+ JpbS+X3doweyYS4edNiE7hDAhe1H1lleuUYvVYrKI1Nf/wAt/xZXIGcYRmiMijvs82VP VrV2b2NZbUjMuiDSa5Vl87PWduM86RTN/h645x8ItldiZSoo8VFPykijL3DznD2QasK5 OEV580zOZRGh1U7eWQ9D+pIVmPYt7yL6IwFK05wOYq/1rY36NxgboaJGoSpb7GMyfHBx SnfQ== 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=BO0mm+QEf5Np62JXxlh17iSFdxt4l5JfCFy1h9DfxFc=; b=YSpyOFf7U/mOys6/zSqp307PCHWcHdaHOmwlEpiCZmtWTtPQMyKi/B4dpTrsax0vlG NZNz/ydQDUNS6PB+QYShRokiho2Wv1pKglKGqeYCAWaV/Ixa8bTCuOCUNKXPHiBttqbB C9vNJzsIz6Mcyj84BGORt/MVY4sXk6GKgs1A3pVHfSYWmOCeU3gazBrHbZwlqBFeEnaa FDkZpAak58VVrNetkVf2WspjKs3phfc0Mte21oUbT2EkP8HJ9hlpPcD25dfF1cTk3hLf /kK3+hiNYwSdBfeshQymAShTofnSjebETNwyL78AZZZc1LetpvAnO4DqfTAPU5AF+Cpz VWiA== X-Gm-Message-State: ACrzQf0wQhlSrXgj3HEvs61T/jEmDwjRMjU7I05YJpzxuAk26f3a5d6Y m9qrUIMpi4jsCvPrl/knswZecg== X-Google-Smtp-Source: AMsMyM7FOvCPRtKVdKEvgmd9b2ktktfqpf2g7L2IHRD1LA64D5sffuliYpA0bbFh0ci53XNDuvA8WQ== X-Received: by 2002:a05:600c:4f93:b0:3cc:9bc5:c454 with SMTP id n19-20020a05600c4f9300b003cc9bc5c454mr7249457wmq.84.1666903473022; Thu, 27 Oct 2022 13:44:33 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:32 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 25/36] net/tcp-ao: Add static_key for TCP-AO Date: Thu, 27 Oct 2022 21:43:36 +0100 Message-Id: <20221027204347.529913-26-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 | 15 +++++++++++++++ net/ipv4/tcp_input.c | 42 ++++++++++++++++++++++++++++-------------- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 67f5273396ec..0923cd3b6b45 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 46df1d455889..494fa5e1428c 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 @@ -148,6 +154,9 @@ struct tcp_ao_key *tcp_ao_do_lookup_sndid(const struct sock *sk, u8 keyid) struct tcp_ao_key *key; struct tcp_ao_info *ao; + if (!static_branch_unlikely(&tcp_ao_needed.key)) + return NULL; + if (sk->sk_state == TCP_TIME_WAIT) ao = rcu_dereference_check(tcp_twsk(sk)->ao_info, lockdep_sock_is_held(sk)); @@ -232,6 +241,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) @@ -319,6 +331,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) @@ -1078,6 +1091,7 @@ int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, sk_gso_disable(newsk); rcu_assign_pointer(tcp_sk(newsk)->ao_info, new_ao); + atomic_inc(&tcp_ao_needed.key.key.enabled); } return 0; @@ -1605,6 +1619,7 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, if (first) { sk_gso_disable(sk); rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); + static_branch_inc(&tcp_ao_needed.key); } /* Can't fail: the key with sndid/rcvid was just added */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2ba46d5db421..3f1f5c0e78ae 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 Thu Oct 27 20:43:37 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022755 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 B1F2FFA3744 for ; Thu, 27 Oct 2022 20:49:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237180AbiJ0Utw (ORCPT ); Thu, 27 Oct 2022 16:49:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35806 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237061AbiJ0Usc (ORCPT ); Thu, 27 Oct 2022 16:48:32 -0400 Received: from mail-wr1-x42e.google.com (mail-wr1-x42e.google.com [IPv6:2a00:1450:4864:20::42e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BF64126CF for ; Thu, 27 Oct 2022 13:44:35 -0700 (PDT) Received: by mail-wr1-x42e.google.com with SMTP id g12so4147473wrs.10 for ; Thu, 27 Oct 2022 13:44:35 -0700 (PDT) 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=XrwV/BvgmubH2PxmQ36KDwW16WqC0kC7l6V2d+dbzlY=; b=WQfqshj0oJmr1HDA66v4NIW1w2UOY1H8clCf6sVwSESCS8VHGVL3eqpJYTeBTEqjsv HCdFah2opsvyRvwM7TTbKNJHtHFE9Xy1Ucy5jSNfvAVtTk+tMWIfm75srzyppsRKsfME QQ4q34HYbdZQ5TnDy4MnqVO7SUuoBfM0KwtlbLllfFcZB/N4+Rue9qtHwnrKKL/YiVdz RKHywrQTGoVUnNhb13Sd/9oqXPwjJ+pJGW0lNBJxwRqvPaVIk5vHhQJoWV1j7QLcJRCG I0PEhBmmqHxd6ZV0m96PKPA+CjC8I78uNF+DELyRKkSpznr/Ymo+QjAK7n7wHKnmSG+q bv6w== 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=XrwV/BvgmubH2PxmQ36KDwW16WqC0kC7l6V2d+dbzlY=; b=v8f04aIBqMkr2wh/SNweULqO1Kf21hp1qAvc6yvLBqykGKqt7DPZM0SWv51JNijnQc kYiAtQp87uUsCD02veTiIY4svTUGLoTbyk7KHLWfiGLbqZ4+S8UhODI6NKXy+ZeRyxuf m3nbqrRD4Pcz1L3TEMCtAb8buGQaoU1H5mmnQqtnEjKBNoJDkSbCh/Ls0by2d/ygGN47 a7bvHMkFRdHTosVDOh9gQVGTNSGxQvMLgmPkJlL9pnutbHRrrGtcbti+fzTbP8EwethW nPLez+cMx/XmzYM6mg3MoqCZ69U1Xon+fjCy07McMPiaKqMd4YoeIwgl89roZ8D8nHW8 TA8Q== X-Gm-Message-State: ACrzQf23l10u7xFmGLhKIup1mFono9Wha2LWnNfH3rTmQI8vnwF1vBKI lOiPPeaRzj8zryHmUpxm0b3sDw== X-Google-Smtp-Source: AMsMyM42SL8o1PftI0ymDiqTvYh3ou5B0e5N43STVYhEJZ+91fxx3YuZ8LcPZgQcz/pEcm9qex3VYA== X-Received: by 2002:a05:6000:1561:b0:22f:7201:74ad with SMTP id 1-20020a056000156100b0022f720174admr33275103wrz.340.1666903474663; Thu, 27 Oct 2022 13:44:34 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:34 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 26/36] selftests/net: Add TCP-AO library Date: Thu, 27 Oct 2022 21:43:37 +0100 Message-Id: <20221027204347.529913-27-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 functions to create selftests dedicated to TCP-AO. They can run in parallel, as they use temporary net namespaces. They can be very specific to the feature being tested. This will allow to create a lot of TCP-AO tests, without complicating one binary with many --options and to create scenarios, that are hard to put in bash script that uses one binary. Signed-off-by: Dmitry Safonov --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/net/tcp_ao/.gitignore | 2 + tools/testing/selftests/net/tcp_ao/Makefile | 45 +++ tools/testing/selftests/net/tcp_ao/connect.c | 81 +++++ .../testing/selftests/net/tcp_ao/lib/aolib.h | 336 +++++++++++++++++ .../selftests/net/tcp_ao/lib/netlink.c | 341 +++++++++++++++++ tools/testing/selftests/net/tcp_ao/lib/proc.c | 267 ++++++++++++++ .../testing/selftests/net/tcp_ao/lib/setup.c | 343 ++++++++++++++++++ tools/testing/selftests/net/tcp_ao/lib/sock.c | 294 +++++++++++++++ .../testing/selftests/net/tcp_ao/lib/utils.c | 30 ++ 10 files changed, 1740 insertions(+) create mode 100644 tools/testing/selftests/net/tcp_ao/.gitignore create mode 100644 tools/testing/selftests/net/tcp_ao/Makefile create mode 100644 tools/testing/selftests/net/tcp_ao/connect.c create mode 100644 tools/testing/selftests/net/tcp_ao/lib/aolib.h create mode 100644 tools/testing/selftests/net/tcp_ao/lib/netlink.c create mode 100644 tools/testing/selftests/net/tcp_ao/lib/proc.c create mode 100644 tools/testing/selftests/net/tcp_ao/lib/setup.c create mode 100644 tools/testing/selftests/net/tcp_ao/lib/sock.c create mode 100644 tools/testing/selftests/net/tcp_ao/lib/utils.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 0464b2c6c1e4..8dc5941de83d 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -49,6 +49,7 @@ TARGETS += net TARGETS += net/af_unix TARGETS += net/forwarding TARGETS += net/mptcp +TARGETS += net/tcp_ao TARGETS += netfilter TARGETS += nsfs TARGETS += pidfd diff --git a/tools/testing/selftests/net/tcp_ao/.gitignore b/tools/testing/selftests/net/tcp_ao/.gitignore new file mode 100644 index 000000000000..e8bb81b715b7 --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/.gitignore @@ -0,0 +1,2 @@ +*_ipv4 +*_ipv6 diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile new file mode 100644 index 000000000000..cb23d67944d7 --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/Makefile @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0 +TEST_BOTH_AF := connect + +TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4) +TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6) + +TEST_GEN_PROGS := $(TEST_IPV4_PROGS) $(TEST_IPV6_PROGS) + +top_srcdir := ../../../../.. +KSFT_KHDR_INSTALL := 1 +include ../../lib.mk + +HOSTAR ?= ar + +# Drop it on port to linux/master with commit 8ce72dc32578 +.DEFAULT_GOAL := all + +LIBDIR := $(OUTPUT)/lib +LIB := $(LIBDIR)/libaotst.a +LDLIBS += $(LIB) -pthread +LIBDEPS := lib/aolib.h Makefile + +CFLAGS := -Wall -O2 -g -D_GNU_SOURCE -fno-strict-aliasing +CFLAGS += -I ../../../../../usr/include/ -iquote $(LIBDIR) +CFLAGS += -I ../../../../include/ + +# Library +LIBSRC := setup.c netlink.c utils.c sock.c proc.c +LIBOBJ := $(LIBSRC:%.c=$(LIBDIR)/%.o) +EXTRA_CLEAN += $(LIBOBJ) $(LIB) + +$(LIB): $(LIBOBJ) + $(HOSTAR) rcs $@ $^ + +$(LIBDIR)/%.o: ./lib/%.c $(LIBDEPS) + $(CC) $< $(CFLAGS) $(CPPFLAGS) -o $@ -c + +$(TEST_GEN_PROGS): $(LIB) + +$(OUTPUT)/%_ipv4: %.c + $(LINK.c) $^ $(LDLIBS) -o $@ + +$(OUTPUT)/%_ipv6: %.c + $(LINK.c) -DIPV6_TEST $^ $(LDLIBS) -o $@ + diff --git a/tools/testing/selftests/net/tcp_ao/connect.c b/tools/testing/selftests/net/tcp_ao/connect.c new file mode 100644 index 000000000000..02aa50f0266c --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/connect.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Author: Dmitry Safonov */ +#include +#include "aolib.h" + +static void *server_fn(void *arg) +{ + int err, sk, lsk; + ssize_t bytes; + + lsk = test_listen_socket(this_ip_addr, test_server_port, 1); + + if (test_set_ao(lsk, "password", 0, this_ip_dest, -1, 100, 100)) + test_error("setsockopt(TCP_AO)"); + synchronize_threads(); + + err = test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0); + if (!err) + test_error("timeouted for accept()"); + else if (err < 0) + test_error("test_wait_fd()"); + + sk = accept(lsk, NULL, NULL); + if (sk < 0) + test_error("accept()"); + + synchronize_threads(); + + bytes = test_server_run(sk, 0, 0); + + test_fail("server served: %zd", bytes); + return NULL; +} + +static void *client_fn(void *arg) +{ + int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); + uint64_t before_aogood, after_aogood; + const size_t nr_packets = 20; + struct netstat *ns_before, *ns_after; + + if (sk < 0) + test_error("socket()"); + + if (test_set_ao(sk, "password", 0, this_ip_dest, -1, 100, 100)) + test_error("setsockopt(TCP_AO)"); + + synchronize_threads(); + if (test_connect_socket(sk, this_ip_dest, test_server_port) <= 0) + test_error("failed to connect()"); + synchronize_threads(); + + ns_before = netstat_read(); + before_aogood = netstat_get(ns_before, "TCPAOGood", NULL); + if (test_client_verify(sk, 100, nr_packets, TEST_TIMEOUT_SEC)) { + test_fail("verify failed"); + return NULL; + } + + ns_after = netstat_read(); + after_aogood = netstat_get(ns_after, "TCPAOGood", NULL); + netstat_print_diff(ns_before, ns_after); + netstat_free(ns_before); + netstat_free(ns_after); + + if (nr_packets > (after_aogood - before_aogood)) { + test_fail("TCPAOGood counter mismatch: %zu > (%zu - %zu)", + nr_packets, after_aogood, before_aogood); + return NULL; + } + + test_ok("connect TCPAOGood %" PRIu64 " => %" PRIu64 ", sent %" PRIu64, + before_aogood, after_aogood, nr_packets); + return NULL; +} + +int main(int argc, char *argv[]) +{ + test_init(1, server_fn, client_fn); + return 0; +} diff --git a/tools/testing/selftests/net/tcp_ao/lib/aolib.h b/tools/testing/selftests/net/tcp_ao/lib/aolib.h new file mode 100644 index 000000000000..308b32b3d5fe --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/lib/aolib.h @@ -0,0 +1,336 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * TCP-AO selftest library. Provides helpers to unshare network + * namespaces, create veth, assign ip addresses, set routes, + * manipulate socket options, read network counter and etc. + * Author: Dmitry Safonov + */ +#ifndef _AOLIB_H_ +#define _AOLIB_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../../../include/linux/stringify.h" + +/* Working around ksft, see the comment in lib/setup.c */ +extern void __test_msg(const char *buf); +extern void __test_ok(const char *buf); +extern void __test_fail(const char *buf); +extern void __test_error(const char *buf); +extern void __test_skip(const char *buf); + +__attribute__((__format__(__printf__, 2, 3))) +static inline void __test_print(void (*fn)(const char *), const char *fmt, ...) +{ +#define TEST_MSG_BUFFER_SIZE 4096 + char buf[TEST_MSG_BUFFER_SIZE]; + va_list arg; + + va_start(arg, fmt); + vsnprintf(buf, sizeof(buf), fmt, arg); + va_end(arg); + fn(buf); +} + +#define test_print(fmt, ...) \ + __test_print(__test_msg, "%ld[%s:%u] " fmt "\n", \ + syscall(SYS_gettid), \ + __FILE__, __LINE__, ##__VA_ARGS__) + +#define test_ok(fmt, ...) \ + __test_print(__test_ok, fmt "\n", ##__VA_ARGS__) +#define test_skip(fmt, ...) \ + __test_print(__test_skip, fmt "\n", ##__VA_ARGS__) + +#define test_fail(fmt, ...) \ +do { \ + if (errno) \ + __test_print(__test_fail, fmt ": %m\n", ##__VA_ARGS__); \ + else \ + __test_print(__test_fail, fmt "\n", ##__VA_ARGS__); \ + test_failed(); \ +} while (0) + +#define KSFT_FAIL 1 +#define test_error(fmt, ...) \ +do { \ + if (errno) \ + __test_print(__test_error, "%ld[%s:%u] " fmt ": %m\n", \ + syscall(SYS_gettid), __FILE__, __LINE__, \ + ##__VA_ARGS__); \ + else \ + __test_print(__test_error, "%ld[%s:%u] " fmt "\n", \ + syscall(SYS_gettid), __FILE__, __LINE__, \ + ##__VA_ARGS__); \ + exit(KSFT_FAIL); \ +} while (0) + +union tcp_addr { + struct in_addr a4; + struct in6_addr a6; +}; + +typedef void *(*thread_fn)(void *); +extern void test_failed(void); +extern void __test_init(unsigned int ntests, int family, unsigned int prefix, + union tcp_addr addr1, union tcp_addr addr2, + thread_fn peer1, thread_fn peer2); + +static inline void test_init2(unsigned int ntests, + thread_fn peer1, thread_fn peer2, + int family, unsigned int prefix, + const char *addr1, const char *addr2) +{ + union tcp_addr taddr1, taddr2; + + if (inet_pton(family, addr1, &taddr1) != 1) + test_error("Can't convert ip address %s", addr1); + if (inet_pton(family, addr2, &taddr2) != 1) + test_error("Can't convert ip address %s", addr2); + + __test_init(ntests, family, prefix, taddr1, taddr2, peer1, peer2); +} +extern void test_add_destructor(void (*d)(void)); +extern void test_set_optmem(size_t value); + +extern const struct sockaddr_in6 addr_any6; +extern const struct sockaddr_in addr_any4; + +#ifdef IPV6_TEST +# define __TEST_CLIENT_IP(n) ("2001:db8:" __stringify(n) "::1") +# define TEST_CLIENT_IP __TEST_CLIENT_IP(1) +# define TEST_WRONG_IP "2001:db8:253::1" +# define TEST_SERVER_IP "2001:db8:254::1" +# define TEST_NETWORK "2001::" +# define TEST_PREFIX 128 +# define TEST_FAMILY AF_INET6 +# define SOCKADDR_ANY addr_any6 +#else +# define __TEST_CLIENT_IP(n) ("10.0." __stringify(n) ".1") +# define TEST_CLIENT_IP __TEST_CLIENT_IP(1) +# define TEST_WRONG_IP "10.0.253.1" +# define TEST_SERVER_IP "10.0.254.1" +# define TEST_NETWORK "10.0.0.0" +# define TEST_PREFIX 32 +# define TEST_FAMILY AF_INET +# define SOCKADDR_ANY addr_any4 +#endif + +static inline void test_init(unsigned int ntests, + thread_fn peer1, thread_fn peer2) +{ + test_init2(ntests, peer1, peer2, TEST_FAMILY, TEST_PREFIX, + TEST_SERVER_IP, TEST_CLIENT_IP); +} +extern void synchronize_threads(void); +extern void switch_ns(int fd); + +extern __thread union tcp_addr this_ip_addr; +extern __thread union tcp_addr this_ip_dest; +extern int test_family; + +extern void randomize_buffer(void *buf, size_t buflen); +extern const char veth_name[]; +extern int add_veth(const char *name, int nsfda, int nsfdb); +extern int ip_addr_add(const char *intf, int family, + union tcp_addr addr, uint8_t prefix); +extern int ip_route_add(const char *intf, int family, + union tcp_addr src, union tcp_addr dst); +extern int link_set_up(const char *intf); + +extern const unsigned int test_server_port; +extern int test_wait_fd(int sk, time_t sec, bool write); +extern int __test_connect_socket(int sk, void *addr, size_t addr_sz, + time_t timeout); +extern int __test_listen_socket(int backlog, void *addr, size_t addr_sz); + +static inline int test_listen_socket(const union tcp_addr taddr, + unsigned int port, int backlog) +{ +#ifdef IPV6_TEST + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6, + .sin6_port = htons(port), + .sin6_addr = taddr.a6, + }; +#else + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(port), + .sin_addr = taddr.a4, + }; +#endif + return __test_listen_socket(backlog, (void *)&addr, sizeof(addr)); +} + +#ifndef DEFAULT_TEST_ALGO +#define DEFAULT_TEST_ALGO "cmac(aes128)" +#endif + +#ifdef IPV6_TEST +#define DEFAULT_TEST_PREFIX 128 +#else +#define DEFAULT_TEST_PREFIX 32 +#endif + +/* + * Timeout on syscalls where failure is not expected. + * You may want to rise it if the test machine is very busy. + */ +#ifndef TEST_TIMEOUT_SEC +#define TEST_TIMEOUT_SEC 5 +#endif + +/* + * Timeout on connect() where a failure is expected. + * If set to 0 - kernel will try to retransmit SYN number of times, set in + * /proc/sys/net/ipv4/tcp_syn_retries + * By default set to 1 to make tests pass faster on non-busy machine. + */ +#ifndef TEST_RETRANSMIT_SEC +#define TEST_RETRANSMIT_SEC 1 +#endif + + +static inline int _test_connect_socket(int sk, const union tcp_addr taddr, + unsigned int port, time_t timeout) +{ +#ifdef IPV6_TEST + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6, + .sin6_port = htons(port), + .sin6_addr = taddr.a6, + }; +#else + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(port), + .sin_addr = taddr.a4, + }; +#endif + return __test_connect_socket(sk, (void *)&addr, sizeof(addr), timeout); +} + +static inline int test_connect_socket(int sk, const union tcp_addr taddr, + unsigned int port) +{ + return _test_connect_socket(sk, taddr, port, TEST_TIMEOUT_SEC); +} + +extern int test_prepare_ao_sockaddr(struct tcp_ao *ao, + const char *alg, uint16_t flags, + void *addr, size_t addr_sz, uint8_t prefix, + uint8_t sndid, uint8_t rcvid, uint8_t maclen, + uint8_t keyflags, uint8_t keylen, const char *key); + +static inline int test_prepare_ao(struct tcp_ao *ao, + const char *alg, uint16_t flags, + union tcp_addr in_addr, uint8_t prefix, + uint8_t sndid, uint8_t rcvid, uint8_t maclen, + uint8_t keyflags, uint8_t keylen, const char *key) +{ +#ifdef IPV6_TEST + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = in_addr.a6, + }; +#else + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = in_addr.a4, + }; +#endif + + return test_prepare_ao_sockaddr(ao, alg, flags, + (void *)&addr, sizeof(addr), prefix, sndid, rcvid, + maclen, keyflags, keylen, key); +} + +static inline int test_prepare_def_ao(struct tcp_ao *ao, + const char *key, uint16_t flags, + union tcp_addr in_addr, uint8_t prefix, + uint8_t sndid, uint8_t rcvid) +{ + if (prefix > DEFAULT_TEST_PREFIX) + prefix = DEFAULT_TEST_PREFIX; + + return test_prepare_ao(ao, DEFAULT_TEST_ALGO, flags, in_addr, + prefix, sndid, rcvid, 0, 0, strlen(key), key); +} + +extern int test_get_one_ao(int sk, struct tcp_ao_getsockopt *out, + uint16_t flags, void *addr, size_t addr_sz, + uint8_t prefix, uint8_t sndid, uint8_t rcvid); +extern int test_cmp_getsockopt_setsockopt(const struct tcp_ao *a, + const struct tcp_ao_getsockopt *b); + +static inline int test_verify_socket_ao(int sk, struct tcp_ao *ao) +{ + struct tcp_ao_getsockopt tmp; + int err; + + err = test_get_one_ao(sk, &tmp, 0, &ao->tcpa_addr, + sizeof(ao->tcpa_addr), ao->tcpa_prefix, + ao->tcpa_sndid, ao->tcpa_rcvid); + if (err) + return err; + + return test_cmp_getsockopt_setsockopt(ao, &tmp); +} + +static inline int test_set_ao(int sk, const char *key, uint16_t flags, + union tcp_addr in_addr, uint8_t prefix, + uint8_t sndid, uint8_t rcvid) +{ + struct tcp_ao tmp; + int err; + + err = test_prepare_def_ao(&tmp, key, flags, in_addr, + prefix, sndid, rcvid); + if (err) + return err; + + if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &tmp, sizeof(tmp)) < 0) + return -errno; + + return test_verify_socket_ao(sk, &tmp); +} + +extern ssize_t test_server_run(int sk, ssize_t quota, time_t timeout_sec); +extern ssize_t test_client_loop(int sk, char *buf, size_t buf_sz, + const size_t msg_len, time_t timeout_sec); +extern int test_client_verify(int sk, const size_t msg_len, const size_t nr, + time_t timeout_sec); + +struct netstat; +extern struct netstat *netstat_read(void); +extern void netstat_free(struct netstat *ns); +extern void netstat_print_diff(struct netstat *nsa, struct netstat *nsb); +extern uint64_t netstat_get(struct netstat *ns, + const char *name, bool *not_found); + +static inline uint64_t netstat_get_one(const char *name, bool *not_found) +{ + struct netstat *ns = netstat_read(); + uint64_t ret; + + ret = netstat_get(ns, name, not_found); + + netstat_free(ns); + return ret; +} + +#endif /* _AOLIB_H_ */ diff --git a/tools/testing/selftests/net/tcp_ao/lib/netlink.c b/tools/testing/selftests/net/tcp_ao/lib/netlink.c new file mode 100644 index 000000000000..e6ad828f58dc --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/lib/netlink.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Original from tools/testing/selftests/net/ipsec.c */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aolib.h" + +#define MAX_PAYLOAD 2048 + +static int netlink_sock(int *sock, uint32_t *seq_nr, int proto) +{ + if (*sock > 0) { + seq_nr++; + return 0; + } + + *sock = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, proto); + if (*sock <= 0) { + test_print("socket(AF_NETLINK)"); + return -1; + } + + randomize_buffer(seq_nr, sizeof(*seq_nr)); + + return 0; +} + +static int netlink_check_answer(int sock, bool quite) +{ + struct nlmsgerror { + struct nlmsghdr hdr; + int error; + struct nlmsghdr orig_msg; + } answer; + + if (recv(sock, &answer, sizeof(answer), 0) < 0) { + test_print("recv()"); + return -1; + } else if (answer.hdr.nlmsg_type != NLMSG_ERROR) { + test_print("expected NLMSG_ERROR, got %d", + (int)answer.hdr.nlmsg_type); + return -1; + } else if (answer.error) { + if (!quite) { + test_print("NLMSG_ERROR: %d: %s", + answer.error, strerror(-answer.error)); + } + return answer.error; + } + + return 0; +} + +static inline struct rtattr *rtattr_hdr(struct nlmsghdr *nh) +{ + return (struct rtattr *)((char *)(nh) + RTA_ALIGN((nh)->nlmsg_len)); +} + +static int rtattr_pack(struct nlmsghdr *nh, size_t req_sz, + unsigned short rta_type, const void *payload, size_t size) +{ + /* NLMSG_ALIGNTO == RTA_ALIGNTO, nlmsg_len already aligned */ + struct rtattr *attr = rtattr_hdr(nh); + size_t nl_size = RTA_ALIGN(nh->nlmsg_len) + RTA_LENGTH(size); + + if (req_sz < nl_size) { + test_print("req buf is too small: %zu < %zu", req_sz, nl_size); + return -1; + } + nh->nlmsg_len = nl_size; + + attr->rta_len = RTA_LENGTH(size); + attr->rta_type = rta_type; + memcpy(RTA_DATA(attr), payload, size); + + return 0; +} + +static struct rtattr *_rtattr_begin(struct nlmsghdr *nh, size_t req_sz, + unsigned short rta_type, const void *payload, size_t size) +{ + struct rtattr *ret = rtattr_hdr(nh); + + if (rtattr_pack(nh, req_sz, rta_type, payload, size)) + return 0; + + return ret; +} + +static inline struct rtattr *rtattr_begin(struct nlmsghdr *nh, size_t req_sz, + unsigned short rta_type) +{ + return _rtattr_begin(nh, req_sz, rta_type, 0, 0); +} + +static inline void rtattr_end(struct nlmsghdr *nh, struct rtattr *attr) +{ + char *nlmsg_end = (char *)nh + nh->nlmsg_len; + + attr->rta_len = nlmsg_end - (char *)attr; +} + +static int veth_pack_peerb(struct nlmsghdr *nh, size_t req_sz, + const char *peer, int ns) +{ + struct ifinfomsg pi; + struct rtattr *peer_attr; + + memset(&pi, 0, sizeof(pi)); + pi.ifi_family = AF_UNSPEC; + pi.ifi_change = 0xFFFFFFFF; + + peer_attr = _rtattr_begin(nh, req_sz, VETH_INFO_PEER, &pi, sizeof(pi)); + if (!peer_attr) + return -1; + + if (rtattr_pack(nh, req_sz, IFLA_IFNAME, peer, strlen(peer))) + return -1; + + if (rtattr_pack(nh, req_sz, IFLA_NET_NS_FD, &ns, sizeof(ns))) + return -1; + + rtattr_end(nh, peer_attr); + + return 0; +} + +static int __add_veth(int sock, uint32_t seq, const char *name, + int ns_a, int ns_b) +{ + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE; + struct { + struct nlmsghdr nh; + struct ifinfomsg info; + char attrbuf[MAX_PAYLOAD]; + } req; + static const char veth_type[] = "veth"; + struct rtattr *link_info, *info_data; + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.info)); + req.nh.nlmsg_type = RTM_NEWLINK; + req.nh.nlmsg_flags = flags; + req.nh.nlmsg_seq = seq; + req.info.ifi_family = AF_UNSPEC; + req.info.ifi_change = 0xFFFFFFFF; + + if (rtattr_pack(&req.nh, sizeof(req), IFLA_IFNAME, name, strlen(name))) + return -1; + + if (rtattr_pack(&req.nh, sizeof(req), IFLA_NET_NS_FD, &ns_a, sizeof(ns_a))) + return -1; + + link_info = rtattr_begin(&req.nh, sizeof(req), IFLA_LINKINFO); + if (!link_info) + return -1; + + if (rtattr_pack(&req.nh, sizeof(req), IFLA_INFO_KIND, veth_type, sizeof(veth_type))) + return -1; + + info_data = rtattr_begin(&req.nh, sizeof(req), IFLA_INFO_DATA); + if (!info_data) + return -1; + + if (veth_pack_peerb(&req.nh, sizeof(req), name, ns_b)) + return -1; + + rtattr_end(&req.nh, info_data); + rtattr_end(&req.nh, link_info); + + if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { + test_print("send()"); + return -1; + } + return netlink_check_answer(sock, false); +} + +int add_veth(const char *name, int nsfda, int nsfdb) +{ + int route_sock = -1, ret; + uint32_t route_seq; + + if (netlink_sock(&route_sock, &route_seq, NETLINK_ROUTE)) + test_error("Failed to open netlink route socket\n"); + + ret = __add_veth(route_sock, route_seq++, name, nsfda, nsfdb); + close(route_sock); + return ret; +} + +static int __ip_addr_add(int sock, uint32_t seq, const char *intf, + int family, union tcp_addr addr, uint8_t prefix) +{ + uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE; + struct { + struct nlmsghdr nh; + struct ifaddrmsg info; + char attrbuf[MAX_PAYLOAD]; + } req; + size_t addr_len = (family == AF_INET) ? sizeof(struct in_addr) : + sizeof(struct in6_addr); + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.info)); + req.nh.nlmsg_type = RTM_NEWADDR; + req.nh.nlmsg_flags = flags; + req.nh.nlmsg_seq = seq; + req.info.ifa_family = family; + req.info.ifa_prefixlen = prefix; + req.info.ifa_index = if_nametoindex(intf); + req.info.ifa_flags = IFA_F_NODAD; + + if (rtattr_pack(&req.nh, sizeof(req), IFA_LOCAL, &addr, addr_len)) + return -1; + + if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { + test_print("send()"); + return -1; + } + return netlink_check_answer(sock, true); +} + +int ip_addr_add(const char *intf, int family, + union tcp_addr addr, uint8_t prefix) +{ + int route_sock = -1, ret; + uint32_t route_seq; + + if (netlink_sock(&route_sock, &route_seq, NETLINK_ROUTE)) + test_error("Failed to open netlink route socket\n"); + + ret = __ip_addr_add(route_sock, route_seq++, intf, + family, addr, prefix); + + close(route_sock); + return ret; +} + +static int __ip_route_add(int sock, uint32_t seq, const char *intf, int family, + union tcp_addr src, union tcp_addr dst) +{ + struct { + struct nlmsghdr nh; + struct rtmsg rt; + char attrbuf[MAX_PAYLOAD]; + } req; + unsigned int index = if_nametoindex(intf); + size_t addr_len = (family == AF_INET) ? sizeof(struct in_addr) : + sizeof(struct in6_addr); + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.rt)); + req.nh.nlmsg_type = RTM_NEWROUTE; + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE; + req.nh.nlmsg_seq = seq; + req.rt.rtm_family = family; + req.rt.rtm_dst_len = (family == AF_INET) ? 32 : 128; + req.rt.rtm_table = RT_TABLE_MAIN; + req.rt.rtm_protocol = RTPROT_BOOT; + req.rt.rtm_scope = RT_SCOPE_UNIVERSE; + req.rt.rtm_type = RTN_UNICAST; + + if (rtattr_pack(&req.nh, sizeof(req), RTA_DST, &dst, addr_len)) + return -1; + + if (rtattr_pack(&req.nh, sizeof(req), RTA_PREFSRC, &src, addr_len)) + return -1; + + if (rtattr_pack(&req.nh, sizeof(req), RTA_OIF, &index, sizeof(index))) + return -1; + + if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { + test_print("send()"); + return -1; + } + + return netlink_check_answer(sock, true); +} + +int ip_route_add(const char *intf, int family, + union tcp_addr src, union tcp_addr dst) +{ + int route_sock = -1, ret; + uint32_t route_seq; + + if (netlink_sock(&route_sock, &route_seq, NETLINK_ROUTE)) + test_error("Failed to open netlink route socket\n"); + + ret = __ip_route_add(route_sock, route_seq++, intf, family, src, dst); + if (ret == -EEXIST) /* ignoring */ + ret = 0; + + close(route_sock); + return ret; +} + +static int __link_set_up(int sock, uint32_t seq, const char *intf) +{ + struct { + struct nlmsghdr nh; + struct ifinfomsg info; + char attrbuf[MAX_PAYLOAD]; + } req; + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.info)); + req.nh.nlmsg_type = RTM_NEWLINK; + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.nh.nlmsg_seq = seq; + req.info.ifi_family = AF_UNSPEC; + req.info.ifi_change = 0xFFFFFFFF; + req.info.ifi_index = if_nametoindex(intf); + req.info.ifi_flags = IFF_UP; + req.info.ifi_change = IFF_UP; + + if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { + test_print("send()"); + return -1; + } + return netlink_check_answer(sock, false); +} + +int link_set_up(const char *intf) +{ + int route_sock = -1, ret; + uint32_t route_seq; + + if (netlink_sock(&route_sock, &route_seq, NETLINK_ROUTE)) + test_error("Failed to open netlink route socket\n"); + + ret = __link_set_up(route_sock, route_seq++, intf); + + close(route_sock); + return ret; +} diff --git a/tools/testing/selftests/net/tcp_ao/lib/proc.c b/tools/testing/selftests/net/tcp_ao/lib/proc.c new file mode 100644 index 000000000000..675acf00b50e --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/lib/proc.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include "../../../../../include/linux/compiler.h" +#include "../../../../../include/linux/kernel.h" +#include "aolib.h" + +struct netstat_counter { + uint64_t val; + char *name; +}; + +struct netstat { + char *header_name; + struct netstat *next; + size_t counters_nr; + struct netstat_counter *counters; +}; + +static struct netstat *lookup_type(struct netstat *ns, + const char *type, size_t len) +{ + while (ns != NULL) { + size_t cmp = max(len, strlen(ns->header_name)); + + if (!strncmp(ns->header_name, type, cmp)) + return ns; + ns = ns->next; + } + return NULL; +} + +static struct netstat *lookup_get(struct netstat *ns, + const char *type, const size_t len) +{ + struct netstat *ret; + + ret = lookup_type(ns, type, len); + if (ret != NULL) + return ret; + + ret = malloc(sizeof(struct netstat)); + if (!ret) + test_error("malloc()"); + + ret->header_name = strndup(type, len); + if (ret->header_name == NULL) + test_error("strndup()"); + ret->next = ns; + ret->counters_nr = 0; + ret->counters = NULL; + + return ret; +} + +static struct netstat *lookup_get_column(struct netstat *ns, const char *line) +{ + char *column; + + column = strchr(line, ':'); + if (!column) + test_error("can't parse netstat file"); + + return lookup_get(ns, line, column - line); +} + +static void netstat_read_type(FILE *fnetstat, struct netstat **dest, char *line) +{ + struct netstat *type = lookup_get_column(*dest, line); + const char *pos = line; + size_t i, nr_elems = 0; + char tmp; + + while ((pos = strchr(pos, ' '))) { + nr_elems++; + pos++; + } + + *dest = type; + type->counters = reallocarray(type->counters, + type->counters_nr + nr_elems, + sizeof(struct netstat_counter)); + if (!type->counters) + test_error("reallocarray()"); + + pos = strchr(line, ' ') + 1; + + if (fscanf(fnetstat, type->header_name) == EOF) + test_error("fscanf(%s)", type->header_name); + if (fread(&tmp, 1, 1, fnetstat) != 1 || tmp != ':') + test_error("Unexpected netstat format (%c)", tmp); + + for (i = type->counters_nr; i < type->counters_nr + nr_elems; i++) { + struct netstat_counter *nc = &type->counters[i]; + const char *new_pos = strchr(pos, ' '); + const char *fmt = " %" PRIu64; + + if (new_pos == NULL) + new_pos = strchr(pos, '\n'); + + nc->name = strndup(pos, new_pos - pos); + if (nc->name == NULL) + test_error("strndup()"); + + if (unlikely(!strcmp(nc->name, "MaxConn"))) + fmt = " %" PRId64; /* MaxConn is signed, RFC 2012 */ + if (fscanf(fnetstat, fmt, &nc->val) != 1) + test_error("fscanf(%s)", nc->name); + pos = new_pos + 1; + } + type->counters_nr += nr_elems; + + if (fread(&tmp, 1, 1, fnetstat) != 1 || tmp != '\n') + test_error("Unexpected netstat format"); +} + +static const char *snmp6_name = "Snmp6"; +static void snmp6_read(FILE *fnetstat, struct netstat **dest) +{ + struct netstat *type = lookup_get(*dest, snmp6_name, strlen(snmp6_name)); + char *counter_name; + size_t i; + + for (i = type->counters_nr;; i++) { + struct netstat_counter *nc; + uint64_t counter; + + if (fscanf(fnetstat, "%ms", &counter_name) == EOF) + break; + if (fscanf(fnetstat, "%" PRIu64, &counter) == EOF) + test_error("Unexpected snmp6 format"); + type->counters = reallocarray(type->counters, i + 1, + sizeof(struct netstat_counter)); + if (!type->counters) + test_error("reallocarray()"); + nc = &type->counters[i]; + nc->name = counter_name; + nc->val = counter; + } + type->counters_nr = i; + *dest = type; +} + +struct netstat *netstat_read(void) +{ + struct netstat *ret = 0; + size_t line_sz = 0; + char *line = NULL; + FILE *fnetstat; + + errno = 0; + fnetstat = fopen("/proc/net/netstat", "r"); + if (fnetstat == NULL) + test_error("failed to open /proc/net/netstat"); + + while (getline(&line, &line_sz, fnetstat) != -1) + netstat_read_type(fnetstat, &ret, line); + fclose(fnetstat); + + errno = 0; + fnetstat = fopen("/proc/net/snmp", "r"); + if (fnetstat == NULL) + test_error("failed to open /proc/net/snmp"); + + while (getline(&line, &line_sz, fnetstat) != -1) + netstat_read_type(fnetstat, &ret, line); + fclose(fnetstat); + + errno = 0; + fnetstat = fopen("/proc/net/snmp6", "r"); + if (fnetstat == NULL) + test_error("failed to open /proc/net/snmp6"); + + snmp6_read(fnetstat, &ret); + fclose(fnetstat); + + free(line); + return ret; +} + +void netstat_free(struct netstat *ns) +{ + while (ns != NULL) { + struct netstat *prev = ns; + size_t i; + + free(ns->header_name); + for (i = 0; i < ns->counters_nr; i++) + free(ns->counters[i].name); + free(ns->counters); + ns = ns->next; + free(prev); + } +} + +static inline void +__netstat_print_diff(uint64_t a, struct netstat *nsb, size_t i) +{ + if (unlikely(!strcmp(nsb->header_name, "MaxConn"))) { + test_print("%8s %25s: %" PRId64 " => %" PRId64, + nsb->header_name, nsb->counters[i].name, + a, nsb->counters[i].val); + return; + } + + test_print("%8s %25s: %" PRIu64 " => %" PRIu64, nsb->header_name, + nsb->counters[i].name, a, nsb->counters[i].val); +} + +void netstat_print_diff(struct netstat *nsa, struct netstat *nsb) +{ + size_t i, j; + + while (nsb != NULL) { + if (unlikely(strcmp(nsb->header_name, nsa->header_name))) { + for (i = 0; i < nsb->counters_nr; i++) + __netstat_print_diff(0, nsb, i); + nsb = nsb->next; + continue; + } + + if (nsb->counters_nr < nsa->counters_nr) + test_error("Unexpected: some counters dissapeared!"); + + for (j = 0, i = 0; i < nsb->counters_nr; i++) { + if (strcmp(nsb->counters[i].name, nsa->counters[j].name)) { + __netstat_print_diff(0, nsb, i); + continue; + } + + if (nsa->counters[j].val == nsb->counters[i].val) { + j++; + continue; + } + + __netstat_print_diff(nsa->counters[j].val, nsb, i); + j++; + } + if (j != nsa->counters_nr) + test_error("Unexpected: some counters dissapeared!"); + + nsb = nsb->next; + nsa = nsa->next; + } +} + +uint64_t netstat_get(struct netstat *ns, const char *name, bool *not_found) +{ + if (not_found) + *not_found = false; + + while (ns != NULL) { + size_t i; + + for (i = 0; i < ns->counters_nr; i++) { + if (!strcmp(name, ns->counters[i].name)) + return ns->counters[i].val; + } + + ns = ns->next; + } + + if (not_found) + *not_found = true; + return 0; +} diff --git a/tools/testing/selftests/net/tcp_ao/lib/setup.c b/tools/testing/selftests/net/tcp_ao/lib/setup.c new file mode 100644 index 000000000000..5c100761058c --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/lib/setup.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include "aolib.h" + +/* + * Can't be included in the header: it defines static variables which + * will be unique to every object. Let's include it only once here. + */ +#include "../../../kselftest.h" + +/* Prevent overriding of one thread's output by another */ +static pthread_mutex_t ksft_print_lock = PTHREAD_MUTEX_INITIALIZER; + +void __test_msg(const char *buf) +{ + pthread_mutex_lock(&ksft_print_lock); + ksft_print_msg(buf); + pthread_mutex_unlock(&ksft_print_lock); +} +void __test_ok(const char *buf) +{ + pthread_mutex_lock(&ksft_print_lock); + ksft_test_result_pass(buf); + pthread_mutex_unlock(&ksft_print_lock); +} +void __test_fail(const char *buf) +{ + pthread_mutex_lock(&ksft_print_lock); + ksft_test_result_fail(buf); + pthread_mutex_unlock(&ksft_print_lock); +} + +void __test_error(const char *buf) +{ + pthread_mutex_lock(&ksft_print_lock); + ksft_test_result_error(buf); + pthread_mutex_unlock(&ksft_print_lock); +} + +void __test_skip(const char *buf) +{ + pthread_mutex_lock(&ksft_print_lock); + ksft_test_result_skip(buf); + pthread_mutex_unlock(&ksft_print_lock); +} + +static volatile int failed; +static volatile int skipped; + +void test_failed(void) +{ + failed = 1; +} + +static void test_exit(void) +{ + if (failed) { + ksft_exit_fail(); + } else if (skipped) { + /* ksft_exit_skip() is different from ksft_exit_*() */ + ksft_print_cnts(); + exit(KSFT_SKIP); + } else { + ksft_exit_pass(); + } +} + +struct dlist_t { + void (*destruct)(void); + struct dlist_t *next; +}; +static struct dlist_t *destructors_list; + +void test_add_destructor(void (*d)(void)) +{ + struct dlist_t *p; + + p = malloc(sizeof(struct dlist_t)); + if (p == NULL) + test_error("malloc() failed"); + + p->next = destructors_list; + p->destruct = d; + destructors_list = p; +} + +static void test_destructor(void) __attribute__((destructor)); +static void test_destructor(void) +{ + while (destructors_list) { + struct dlist_t *p = destructors_list->next; + + destructors_list->destruct(); + free(destructors_list); + destructors_list = p; + } + test_exit(); +} + +static void sig_int(int signo) +{ + test_error("Caught SIGINT - exiting"); +} + +static int open_netns(void) +{ + const char *netns_path = "/proc/self/ns/net"; + int fd; + + fd = open(netns_path, O_RDONLY); + if (fd <= 0) + test_error("open(%s)", netns_path); + return fd; +} + +static int unshare_open(void) +{ + if (unshare(CLONE_NEWNET) != 0) + test_error("unshare()"); + + return open_netns(); +} + +void switch_ns(int fd) +{ + if (setns(fd, CLONE_NEWNET)) + test_error("setns()"); +} + +int switch_save_ns(int new_ns) +{ + int ret = open_netns(); + + switch_ns(new_ns); + return ret; +} + +static int nsfd_outside = -1; +static int nsfd_parent = -1; +static int nsfd_child = -1; +const char veth_name[] = "ktst-veth"; + +static void init_namespaces(void) +{ + nsfd_outside = open_netns(); + nsfd_parent = unshare_open(); + nsfd_child = unshare_open(); +} + +static void link_init(const char *veth, int family, uint8_t prefix, + union tcp_addr addr, union tcp_addr dest) +{ + if (link_set_up(veth)) + test_error("Failed to set link up"); + if (ip_addr_add(veth, family, addr, prefix)) + test_error("Failed to add ip address"); + if (ip_route_add(veth, family, addr, dest)) + test_error("Failed to add route"); +} + +static unsigned int nr_threads = 1; + +static pthread_mutex_t sync_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t sync_cond = PTHREAD_COND_INITIALIZER; +static volatile unsigned int stage_threads[2]; +static volatile unsigned int stage_nr; + +/* synchronize all threads in the same stage */ +void synchronize_threads(void) +{ + unsigned int q = stage_nr; + + pthread_mutex_lock(&sync_lock); + stage_threads[q]++; + if (stage_threads[q] == nr_threads) { + stage_nr ^= 1; + stage_threads[stage_nr] = 0; + pthread_cond_signal(&sync_cond); + } + while (stage_threads[q] < nr_threads) + pthread_cond_wait(&sync_cond, &sync_lock); + pthread_mutex_unlock(&sync_lock); +} + +__thread union tcp_addr this_ip_addr; +__thread union tcp_addr this_ip_dest; +int test_family; + +struct new_pthread_arg { + thread_fn func; + union tcp_addr my_ip; + union tcp_addr dest_ip; +}; +static void *new_pthread_entry(void *arg) +{ + struct new_pthread_arg *p = arg; + + this_ip_addr = p->my_ip; + this_ip_dest = p->dest_ip; + p->func(NULL); /* shouldn't return */ + exit(KSFT_FAIL); +} + +static void check_tcp_ao_support(void) +{ + struct sockaddr_in addr = { + .sin_family = test_family, + }; + struct tcp_ao tmp = {}; + const char *password = "password"; + int sk; + + sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); + if (sk < 0) + test_error("socket()"); + + tmp.tcpa_sndid = 100; + tmp.tcpa_rcvid = 100; + memcpy(tmp.tcpa_key, password, strlen(password)); + strcpy(tmp.tcpa_alg_name, "cmac(aes128)"); + memcpy(&tmp.tcpa_addr, &addr, sizeof(addr)); + if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &tmp, sizeof(tmp)) < 0) { + if (errno == ENOPROTOOPT) { + ksft_set_plan(1); + ksft_print_header(); + skipped = 1; + test_skip("setsockopt(TCP_AO) is not supported"); + exit(KSFT_SKIP); + } else { + test_error("setsockopt(TCP_AO)"); + } + } + close(sk); +} + +void __test_init(unsigned int ntests, int family, unsigned int prefix, + union tcp_addr addr1, union tcp_addr addr2, + thread_fn peer1, thread_fn peer2) +{ + struct sigaction sa = { + .sa_handler = sig_int, + .sa_flags = SA_RESTART, + }; + time_t seed = time(NULL); + + test_family = family; + check_tcp_ao_support(); + ksft_set_plan(ntests); + + test_print("rand seed %u", (unsigned int)seed); + srand(seed); + + sigemptyset(&sa.sa_mask); + if (sigaction(SIGINT, &sa, NULL)) + test_error("Can't set SIGINT handler"); + + ksft_print_header(); + init_namespaces(); + + if (add_veth(veth_name, nsfd_parent, nsfd_child)) + test_error("Failed to add veth"); + + switch_ns(nsfd_child); + link_init(veth_name, family, prefix, addr2, addr1); + if (peer2) { + struct new_pthread_arg targ; + pthread_t t; + + targ.my_ip = addr2; + targ.dest_ip = addr1; + targ.func = peer2; + nr_threads++; + if (pthread_create(&t, NULL, new_pthread_entry, &targ)) + test_error("Failed to create pthread"); + } + switch_ns(nsfd_parent); + link_init(veth_name, family, prefix, addr1, addr2); + + this_ip_addr = addr1; + this_ip_dest = addr2; + peer1(NULL); + if (failed) + exit(KSFT_FAIL); + else + exit(KSFT_PASS); +} + +/* /proc/sys/net/core/optmem_max artifically limits the amount of memory + * that can be allocated with sock_kmalloc() on each socket in the system. + * It is not virtualized, so it has to written outside test namespaces. + * To be nice a test will revert optmem back to the old value. + * Keeping it simple without any file lock, which means the tests that + * need to set/increase optmem value shouldn't run in parallel. + * Also, not re-entrant. + */ +static const char *optmem_file = "/proc/sys/net/core/optmem_max"; +static size_t saved_optmem; + +static void __test_set_optmem(size_t new, size_t *old) +{ + FILE *foptmem; + int old_ns; + + old_ns = switch_save_ns(nsfd_outside); + foptmem = fopen(optmem_file, "r+"); + if (!foptmem) + test_error("failed to open %s", optmem_file); + + if (old != NULL) { + if (fscanf(foptmem, "%zu", old) != 1) + test_error("can't read from %s", optmem_file); + fclose(foptmem); + foptmem = fopen(optmem_file, "w"); + if (!foptmem) + test_error("failed to open %s", optmem_file); + } + + if (fprintf(foptmem, "%zu", new) <= 0) + test_error("can't write %zu to %s", new, optmem_file); + fclose(foptmem); + switch_ns(old_ns); +} + +static void test_revert_optmem(void) +{ + if (saved_optmem == 0) + return; + + __test_set_optmem(saved_optmem, NULL); +} + +void test_set_optmem(size_t value) +{ + if (saved_optmem == 0) { + __test_set_optmem(value, &saved_optmem); + test_add_destructor(test_revert_optmem); + } else { + __test_set_optmem(value, NULL); + } +} diff --git a/tools/testing/selftests/net/tcp_ao/lib/sock.c b/tools/testing/selftests/net/tcp_ao/lib/sock.c new file mode 100644 index 000000000000..47480797c9b6 --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/lib/sock.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include "../../../../../include/linux/kernel.h" +#include "../../../../../include/linux/stringify.h" +#include "aolib.h" + +const unsigned int test_server_port = 7010; +int __test_listen_socket(int backlog, void *addr, size_t addr_sz) +{ + int err, sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); + long flags; + + if (sk < 0) + test_error("socket()"); + + err = setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, veth_name, + strlen(veth_name) + 1); + if (err < 0) + test_error("setsockopt(SO_BINDTODEVICE)"); + + if (bind(sk, (struct sockaddr *)addr, addr_sz) < 0) + test_error("bind()"); + + flags = fcntl(sk, F_GETFL); + if ((flags < 0) || (fcntl(sk, F_SETFL, flags | O_NONBLOCK) < 0)) + test_error("fcntl()"); + + if (listen(sk, backlog)) + test_error("listen()"); + + return sk; +} + +int test_wait_fd(int sk, time_t sec, bool write) +{ + struct timeval tv = { .tv_sec = sec }; + struct timeval *ptv = NULL; + fd_set fds, efds; + int ret; + socklen_t slen = sizeof(ret); + + FD_ZERO(&fds); + FD_SET(sk, &fds); + FD_ZERO(&efds); + FD_SET(sk, &efds); + + if (sec) + ptv = &tv; + + errno = 0; + if (write) + ret = select(sk + 1, NULL, &fds, &efds, ptv); + else + ret = select(sk + 1, &fds, NULL, &efds, ptv); + if (ret <= 0) + return -errno; + + if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &slen) || ret) + return -ret; + return sk; +} + +int __test_connect_socket(int sk, void *addr, size_t addr_sz, time_t timeout) +{ + long flags; + int err; + + err = setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, veth_name, + strlen(veth_name) + 1); + if (err < 0) + test_error("setsockopt(SO_BINDTODEVICE)"); + + if (!timeout) { + err = connect(sk, addr, addr_sz); + if (err) { + err = -errno; + goto out; + } + return 0; + } + + flags = fcntl(sk, F_GETFL); + if ((flags < 0) || (fcntl(sk, F_SETFL, flags | O_NONBLOCK) < 0)) + test_error("fcntl()"); + + if (connect(sk, addr, addr_sz) < 0) { + if (errno != EINPROGRESS) { + err = -errno; + goto out; + } + err = test_wait_fd(sk, timeout, 1); + if (err <= 0) + goto out; + } + return sk; + +out: + close(sk); + return err; +} + +int test_prepare_ao_sockaddr(struct tcp_ao *ao, const char *alg, uint16_t flags, + void *addr, size_t addr_sz, uint8_t prefix, + uint8_t sndid, uint8_t rcvid, uint8_t maclen, + uint8_t keyflags, uint8_t keylen, const char *key) +{ + memset(ao, 0, sizeof(struct tcp_ao)); + + ao->tcpa_flags = flags; + ao->tcpa_prefix = prefix; + ao->tcpa_sndid = sndid; + ao->tcpa_rcvid = rcvid; + ao->tcpa_maclen = maclen; + ao->tcpa_keyflags = keyflags; + ao->tcpa_keylen = keylen; + + memcpy(&ao->tcpa_addr, addr, addr_sz); + + if (strlen(alg) > 64) + return -ENOBUFS; + strncpy(ao->tcpa_alg_name, alg, 64); + + memcpy(ao->tcpa_key, key, + (keylen > TCP_AO_MAXKEYLEN ) ? TCP_AO_MAXKEYLEN : keylen); + return 0; +} + +int test_get_one_ao(int sk, struct tcp_ao_getsockopt *out, uint16_t flags, + void *addr, size_t addr_sz, uint8_t prefix, + uint8_t sndid, uint8_t rcvid) +{ + struct tcp_ao_getsockopt tmp = {}; + socklen_t tmp_sz = sizeof(tmp); + int ret; + + memcpy(&tmp.addr, addr, addr_sz); + tmp.prefix = prefix; + tmp.sndid = sndid; + tmp.rcvid = rcvid; + tmp.flags = flags; + tmp.nkeys = 1; + + ret = getsockopt(sk, IPPROTO_TCP, TCP_AO_GET, &tmp, &tmp_sz); + if (ret) + return ret; + if (tmp.nkeys != 1) + return -ENOENT; + *out = tmp; + return 0; +} + +int test_cmp_getsockopt_setsockopt(const struct tcp_ao *a, + const struct tcp_ao_getsockopt *b) +{ + bool is_kdf_aes_128_cmac = false; + + if (!strcmp("cmac(aes128)", a->tcpa_alg_name)) + is_kdf_aes_128_cmac = (a->tcpa_keylen != 16); + +#define __cmp_ao(member) \ + if (b->member != a->tcpa_##member) { \ + test_fail("getsockopt(): " __stringify(member) " %u != %u", \ + b->member, a->tcpa_##member); \ + return -1; \ + } + __cmp_ao(sndid); + __cmp_ao(rcvid); + __cmp_ao(prefix); + __cmp_ao(keyflags); + if (a->tcpa_maclen) { + __cmp_ao(maclen); + } else if (b->maclen != 12) { + test_fail("getsockopt(): expected default maclen 12, but it's %u", + b->maclen); + return -1; + } + if (!is_kdf_aes_128_cmac) { + __cmp_ao(keylen); + } else if (b->keylen != 16) { + test_fail("getsockopt(): expected keylen 16 for cmac(aes128), but it's %u", + b->keylen); + return -1; + } +#undef __cmp_ao + if (!is_kdf_aes_128_cmac && memcmp(b->key, a->tcpa_key, a->tcpa_keylen)) { + test_fail("getsockopt(): returned key is different `%s' != `%s'", + b->key, a->tcpa_key); + return -1; + } + if (memcmp(&b->addr, &a->tcpa_addr, sizeof(b->addr))) { + test_fail("getsockopt(): returned address is different"); + return -1; + } + if (!is_kdf_aes_128_cmac && strcmp(b->alg_name, a->tcpa_alg_name)) { + test_fail("getsockopt(): returned algorithm is different"); + return -1; + } + if (is_kdf_aes_128_cmac && strcmp(b->alg_name, "cmac(aes)")) { + test_fail("getsockopt(): returned algorithm is different"); + return -1; + } + return 0; +} + +#define TEST_BUF_SIZE 4096 +ssize_t test_server_run(int sk, ssize_t quota, time_t timeout_sec) +{ + ssize_t total = 0; + + do { + char buf[TEST_BUF_SIZE]; + ssize_t bytes, sent; + int ret; + + ret = test_wait_fd(sk, timeout_sec, 0); + if (ret <= 0) + return ret; + + bytes = recv(sk, buf, sizeof(buf), 0); + + if (bytes < 0) + test_error("recv(): %zd", bytes); + if (bytes == 0) + break; + + ret = test_wait_fd(sk, timeout_sec, 1); + if (ret <= 0) + return ret; + + sent = send(sk, buf, bytes, 0); + if (sent == 0) + break; + if (sent != bytes) + test_error("send()"); + total += bytes; + } while (!quota || total < quota); + + return total; +} + +ssize_t test_client_loop(int sk, char *buf, size_t buf_sz, + const size_t msg_len, time_t timeout_sec) +{ + char msg[msg_len]; + int nodelay = 1; + size_t i; + + if (setsockopt(sk, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay))) + test_error("setsockopt(TCP_NODELAY)"); + + for (i = 0; i < buf_sz; i += min(msg_len, buf_sz - i)) { + size_t sent, bytes = min(msg_len, buf_sz - i); + int ret; + + ret = test_wait_fd(sk, timeout_sec, 1); + if (ret <= 0) + return ret; + + sent = send(sk, buf + i, bytes, 0); + if (sent == 0) + break; + if (sent != bytes) + test_error("send()"); + + ret = test_wait_fd(sk, timeout_sec, 0); + if (ret <= 0) + return ret; + + bytes = recv(sk, msg, sizeof(msg), 0); + if (bytes < 0) + test_error("recv(): %zd", bytes); + if (bytes != sent) + test_error("recv(): %zd != %zd", bytes, sent); + if (memcmp(buf + i, msg, bytes) != 0) { + test_fail("received message differs"); + return -1; + } + } + return i; +} + +int test_client_verify(int sk, const size_t msg_len, const size_t nr, + time_t timeout_sec) +{ + size_t buf_sz = msg_len * nr; + char *buf = alloca(buf_sz); + + randomize_buffer(buf, buf_sz); + if (test_client_loop(sk, buf, buf_sz, msg_len, timeout_sec) != buf_sz) + return -1; + return 0; +} diff --git a/tools/testing/selftests/net/tcp_ao/lib/utils.c b/tools/testing/selftests/net/tcp_ao/lib/utils.c new file mode 100644 index 000000000000..372daca525f5 --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/lib/utils.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "aolib.h" +#include + +void randomize_buffer(void *buf, size_t buflen) +{ + int *p = (int *)buf; + size_t words = buflen / sizeof(int); + size_t leftover = buflen % sizeof(int); + + if (!buflen) + return; + + while (words--) + *p++ = rand(); + + if (leftover) { + int tmp = rand(); + + memcpy(buf + buflen - leftover, &tmp, leftover); + } +} + +const struct sockaddr_in6 addr_any6 = { + .sin6_family = AF_INET6, +}; + +const struct sockaddr_in addr_any4 = { + .sin_family = AF_INET, +}; From patchwork Thu Oct 27 20:43:38 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022753 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 11C20FA3743 for ; Thu, 27 Oct 2022 20:49:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237160AbiJ0Utr (ORCPT ); Thu, 27 Oct 2022 16:49:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59350 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237017AbiJ0UsD (ORCPT ); Thu, 27 Oct 2022 16:48:03 -0400 Received: from mail-wr1-x434.google.com (mail-wr1-x434.google.com [IPv6:2a00:1450:4864:20::434]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C0D0C388B for ; Thu, 27 Oct 2022 13:44:36 -0700 (PDT) Received: by mail-wr1-x434.google.com with SMTP id y16so4138722wrt.12 for ; Thu, 27 Oct 2022 13:44:36 -0700 (PDT) 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=+dNDpMZ6fkxvZpNHtx9aVoLiGrB6s55r98QT6/SQMCk=; b=UDIb+teWdBTTzpSz4KNaCmDTn/PcZjDBYNlVKwqDHo1CxlUUn9IKS+xK5ypQjZF65F hfNUj9e4eWzyPrR/C5WDI3ZW4MmT0JXLa6pygmZ+7adeJk12fkFfwISH4jzrLSalX5j7 jSqkgWIhGre/XBs/GmQh9vLMCDDcJJfzicfj81+iQLMQejn/ZwfB73eNiqLNgZpk7b3+ uOW7/S01YK9oZDObuinsRBS7xaAovzQliqF5jgA3e8v71hHYlwIDxojPRiZ+tomkQ0AN 6omOy8BQFyrcnlukd02rxwd/0e8Hc6wpqVVdyAg2HQpsnGdJKQiQQkoAUPzumJCV4rzx 0BZQ== 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=+dNDpMZ6fkxvZpNHtx9aVoLiGrB6s55r98QT6/SQMCk=; b=ZFfNYNsUZLzBpzH89vybTy2WXmxHqY3hAy3KlT6w24g5hc7fGBdfcJATLkHtTj5ENJ FJwBbZ+qigrE27sOqE8+/nQNfS2QzWxslU4oOJifP81BV247hZj9it3yCbbxJCLkU3aF eMhno7soQD8CFZiGqC4hy2WraLgQtaZkPCNn/N+qaf+tDppMxAUNuAOfRlp227fjSb66 hlGUyvzsaOHZ1dwozJlVQbEJ3ZPT6Nk3G2+BGlhF9qPCJ0TKfwqj6tH5CP2IucPeOQeR fFWqLyY4g/cb7tOlZjZfyTgH9Eg+qn1m42g0uEPO9QrwjAl8CgqVO4/EVrRobAabeYhT Kaww== X-Gm-Message-State: ACrzQf0564vYB0eUyxdMkHBssh1Lj3E5jbejctRAlFHBhOm/w3nuT/gX zfx1IugolL8cYLefWpv5uahISw== X-Google-Smtp-Source: AMsMyM4Nk3nTpr6hklDn4r8EEA6ikmLzfNDIdJvSTOzHysUPScBD+aQE6UXBqf/zAP9PtyR1JfJ7lA== X-Received: by 2002:a5d:64c4:0:b0:231:3d94:6eb2 with SMTP id f4-20020a5d64c4000000b002313d946eb2mr32457144wri.580.1666903476174; Thu, 27 Oct 2022 13:44:36 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:35 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 27/36] selftests/net: Verify that TCP-AO complies with ignoring ICMPs Date: Thu, 27 Oct 2022 21:43:38 +0100 Message-Id: <20221027204347.529913-28-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 Hand-crafted ICMP packets are sent to the server, the server checks for hard/soft errors and fails if any. Expected output for ipv4 version: > # ./icmps-discard_ipv4 > 1..3 > # 3164[lib/setup.c:166] rand seed 1642623745 > TAP version 13 > # 3164[lib/proc.c:207] Snmp6 Ip6InReceives: 0 => 1 > # 3164[lib/proc.c:207] Snmp6 Ip6InNoRoutes: 0 => 1 > # 3164[lib/proc.c:207] Snmp6 Ip6InOctets: 0 => 76 > # 3164[lib/proc.c:207] Snmp6 Ip6InNoECTPkts: 0 => 1 > # 3164[lib/proc.c:207] Tcp InSegs: 2 => 203 > # 3164[lib/proc.c:207] Tcp OutSegs: 1 => 202 > # 3164[lib/proc.c:207] IcmpMsg InType3: 0 => 543 > # 3164[lib/proc.c:207] Icmp InMsgs: 0 => 543 > # 3164[lib/proc.c:207] Icmp InDestUnreachs: 0 => 543 > # 3164[lib/proc.c:207] Ip InReceives: 2 => 746 > # 3164[lib/proc.c:207] Ip InDelivers: 2 => 746 > # 3164[lib/proc.c:207] Ip OutRequests: 1 => 202 > # 3164[lib/proc.c:207] IpExt InOctets: 132 => 61684 > # 3164[lib/proc.c:207] IpExt OutOctets: 68 => 31324 > # 3164[lib/proc.c:207] IpExt InNoECTPkts: 2 => 744 > # 3164[lib/proc.c:207] TcpExt TCPPureAcks: 1 => 2 > # 3164[lib/proc.c:207] TcpExt TCPOrigDataSent: 0 => 200 > # 3164[lib/proc.c:207] TcpExt TCPDelivered: 0 => 199 > # 3164[lib/proc.c:207] TcpExt TCPAOGood: 2 => 203 > # 3164[lib/proc.c:207] TcpExt TCPAODroppedIcmps: 0 => 541 > ok 1 InDestUnreachs delivered 543 > ok 2 Server survived 20000 bytes of traffic > ok 3 ICMPs ignored 541 > # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0 Expected output for ipv6 version: > # ./icmps-discard_ipv6 > 1..3 > # 3186[lib/setup.c:166] rand seed 1642623803 > TAP version 13 > # 3186[lib/proc.c:207] Snmp6 Ip6InReceives: 4 => 568 > # 3186[lib/proc.c:207] Snmp6 Ip6InDelivers: 3 => 564 > # 3186[lib/proc.c:207] Snmp6 Ip6OutRequests: 2 => 204 > # 3186[lib/proc.c:207] Snmp6 Ip6InMcastPkts: 1 => 4 > # 3186[lib/proc.c:207] Snmp6 Ip6OutMcastPkts: 0 => 1 > # 3186[lib/proc.c:207] Snmp6 Ip6InOctets: 320 => 70420 > # 3186[lib/proc.c:207] Snmp6 Ip6OutOctets: 160 => 35512 > # 3186[lib/proc.c:207] Snmp6 Ip6InMcastOctets: 72 => 336 > # 3186[lib/proc.c:207] Snmp6 Ip6OutMcastOctets: 0 => 76 > # 3186[lib/proc.c:207] Snmp6 Ip6InNoECTPkts: 4 => 568 > # 3186[lib/proc.c:207] Snmp6 Icmp6InMsgs: 1 => 361 > # 3186[lib/proc.c:207] Snmp6 Icmp6OutMsgs: 1 => 2 > # 3186[lib/proc.c:207] Snmp6 Icmp6InDestUnreachs: 0 => 360 > # 3186[lib/proc.c:207] Snmp6 Icmp6OutMLDv2Reports: 0 => 1 > # 3186[lib/proc.c:207] Snmp6 Icmp6InType1: 0 => 360 > # 3186[lib/proc.c:207] Snmp6 Icmp6OutType143: 0 => 1 > # 3186[lib/proc.c:207] Tcp InSegs: 2 => 203 > # 3186[lib/proc.c:207] Tcp OutSegs: 1 => 202 > # 3186[lib/proc.c:207] TcpExt TCPPureAcks: 1 => 2 > # 3186[lib/proc.c:207] TcpExt TCPOrigDataSent: 0 => 200 > # 3186[lib/proc.c:207] TcpExt TCPDelivered: 0 => 199 > # 3186[lib/proc.c:207] TcpExt TCPAOGood: 2 => 203 > # 3186[lib/proc.c:207] TcpExt TCPAODroppedIcmps: 0 => 360 > ok 1 Icmp6InDestUnreachs delivered 360 > ok 2 Server survived 20000 bytes of traffic > ok 3 ICMPs ignored 360 > # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Dmitry Safonov --- tools/testing/selftests/net/tcp_ao/Makefile | 2 +- .../selftests/net/tcp_ao/icmps-discard.c | 433 ++++++++++++++++++ 2 files changed, 434 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/net/tcp_ao/icmps-discard.c diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile index cb23d67944d7..9acfd782c20f 100644 --- a/tools/testing/selftests/net/tcp_ao/Makefile +++ b/tools/testing/selftests/net/tcp_ao/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -TEST_BOTH_AF := connect +TEST_BOTH_AF := connect icmps-discard TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4) TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6) diff --git a/tools/testing/selftests/net/tcp_ao/icmps-discard.c b/tools/testing/selftests/net/tcp_ao/icmps-discard.c new file mode 100644 index 000000000000..07eba1308b4e --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/icmps-discard.c @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Selftest that verifies that incomping ICMPs are ignored, + * the TCP connection stays alive, no hard or soft errors get reported + * to the usespace and the counter for ignored ICMPs is updated. + * + * RFC5925, 7.8: + * >> A TCP-AO implementation MUST default to ignore incoming ICMPv4 + * messages of Type 3 (destination unreachable), Codes 2-4 (protocol + * unreachable, port unreachable, and fragmentation needed -- ’hard + * errors’), and ICMPv6 Type 1 (destination unreachable), Code 1 + * (administratively prohibited) and Code 4 (port unreachable) intended + * for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN- + * WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs. + * + * Author: Dmitry Safonov + */ +#include +#include +#include +#include +#include +#include +#include +#include "aolib.h" + +#ifndef SOL_TCP +/* can't include as including */ +# define SOL_TCP 6 /* TCP level */ +#endif + +const size_t packets_nr = 20; +const size_t packet_size = 100; +const char *tcpao_icmps = "TCPAODroppedIcmps"; + +#ifdef IPV6_TEST +const char *dst_unreach = "Icmp6InDestUnreachs"; +const int sk_ip_level = SOL_IPV6; +const int sk_recverr = IPV6_RECVERR; +#else +const char *dst_unreach = "InDestUnreachs"; +const int sk_ip_level = SOL_IP; +const int sk_recverr = IP_RECVERR; +#endif + +#define test_icmps_fail test_fail +#define test_icmps_ok test_ok + +static void serve_interfered(int sk) +{ + ssize_t test_quota = packet_size * packets_nr * 10; + uint64_t dest_unreach_a, dest_unreach_b; + uint64_t icmp_ignored_a, icmp_ignored_b; + bool counter_not_found; + struct netstat *ns_after, *ns_before; + ssize_t bytes; + + ns_before = netstat_read(); + dest_unreach_a = netstat_get(ns_before, dst_unreach, NULL); + icmp_ignored_a = netstat_get(ns_before, tcpao_icmps, NULL); + bytes = test_server_run(sk, test_quota, 0); + ns_after = netstat_read(); + netstat_print_diff(ns_before, ns_after); + dest_unreach_b = netstat_get(ns_after, dst_unreach, NULL); + icmp_ignored_b = netstat_get(ns_after, tcpao_icmps, + &counter_not_found); + + netstat_free(ns_before); + netstat_free(ns_after); + + if (dest_unreach_a >= dest_unreach_b) { + test_fail("%s counter didn't change: %" PRIu64 " >= %" PRIu64, + dst_unreach, dest_unreach_a, dest_unreach_b); + return; + } + test_ok("%s delivered %" PRIu64, + dst_unreach, dest_unreach_b - dest_unreach_a); + if (bytes < 0) + test_icmps_fail("Server failed with %zd: %s", bytes, strerrordesc_np(-bytes)); + else + test_icmps_ok("Server survived %zd bytes of traffic", test_quota); + if (counter_not_found) { + test_fail("Not found %s counter", tcpao_icmps); + return; + } + if (icmp_ignored_a >= icmp_ignored_b) { + test_icmps_fail("%s counter didn't change: %" PRIu64 " >= %" PRIu64, + tcpao_icmps, icmp_ignored_a, icmp_ignored_b); + return; + } + test_icmps_ok("ICMPs ignored %" PRIu64, icmp_ignored_b - icmp_ignored_a); +} + +static void *server_fn(void *arg) +{ + int val, err, sk, lsk; + uint16_t flags = 0; + + lsk = test_listen_socket(this_ip_addr, test_server_port, 1); + + if (test_set_ao(lsk, "password", flags, this_ip_dest, -1, 100, 100)) + test_error("setsockopt(TCP_AO)"); + synchronize_threads(); + + err = test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0); + if (!err) + test_error("timeouted for accept()"); + else if (err < 0) + test_error("test_wait_fd()"); + + sk = accept(lsk, NULL, NULL); + if (sk < 0) + test_error("accept()"); + + /* Fail on hard ip errors, such as dest unreachable (RFC1122) */ + val = 1; + if (setsockopt(sk, sk_ip_level, sk_recverr, &val, sizeof(val))) + test_error("setsockopt()"); + + synchronize_threads(); + + serve_interfered(sk); + return NULL; +} + +static size_t packets_sent; +static size_t icmps_sent; + +static uint32_t checksum4_nofold(void *data, size_t len, uint32_t sum) +{ + uint16_t *words = data; + size_t i; + + for (i = 0; i < len / sizeof(uint16_t); i++) + sum += words[i]; + if (len & 1) + sum += ((char *)data)[len - 1]; + return sum; +} + +static uint16_t checksum4_fold(void *data, size_t len, uint32_t sum) +{ + sum = checksum4_nofold(data, len, sum); + while (sum > 0xFFFF) + sum = (sum & 0xFFFF) + (sum >> 16); + return ~sum; +} + +static void set_ip4hdr(struct iphdr *iph, size_t packet_len, int proto, + struct sockaddr_in *src, struct sockaddr_in *dst) +{ + iph->version = 4; + iph->ihl = 5; + iph->tos = 0; + iph->tot_len = htons(packet_len); + iph->ttl = 2; + iph->protocol = proto; + iph->saddr = src->sin_addr.s_addr; + iph->daddr = dst->sin_addr.s_addr; + iph->check = checksum4_fold((void *)iph, iph->ihl << 1, 0); +} + +static void icmp_interfere4(uint8_t type, uint8_t code, uint32_t rcv_nxt, + struct sockaddr_in *src, struct sockaddr_in *dst) +{ + int sk = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + struct { + struct iphdr iph; + struct icmphdr icmph; + struct iphdr iphe; + struct { + uint16_t sport; + uint16_t dport; + uint32_t seq; + } tcph; + } packet = {}; + size_t packet_len; + ssize_t bytes; + + if (sk < 0) + test_error("socket(AF_INET, SOCK_RAW, IPPROTO_RAW)"); + + packet_len = sizeof(packet); + set_ip4hdr(&packet.iph, packet_len, IPPROTO_ICMP, src, dst); + + packet.icmph.type = type; + packet.icmph.code = code; + if (code == ICMP_FRAG_NEEDED) { + randomize_buffer(&packet.icmph.un.frag.mtu, + sizeof(packet.icmph.un.frag.mtu)); + } + + packet_len = sizeof(packet.iphe) + sizeof(packet.tcph); + set_ip4hdr(&packet.iphe, packet_len, IPPROTO_TCP, dst, src); + + packet.tcph.sport = dst->sin_port; + packet.tcph.dport = src->sin_port; + packet.tcph.seq = htonl(rcv_nxt); + + packet_len = sizeof(packet) - sizeof(packet.iph); + packet.icmph.checksum = checksum4_fold((void *)&packet.icmph, + packet_len, 0); + + bytes = sendto(sk, &packet, sizeof(packet), 0, + (struct sockaddr *)dst, sizeof(*dst)); + if (bytes != sizeof(packet)) + test_error("send(): %zd", bytes); + icmps_sent++; + + close(sk); +} + +static void set_ip6hdr(struct ipv6hdr *iph, size_t packet_len, int proto, + struct sockaddr_in6 *src, struct sockaddr_in6 *dst) +{ + iph->version = 6; + iph->payload_len = htons(packet_len); + iph->nexthdr = proto; + iph->hop_limit = 2; + iph->saddr = src->sin6_addr; + iph->daddr = dst->sin6_addr; +} + +static inline uint16_t csum_fold(uint32_t csum) +{ + uint32_t sum = csum; + + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + return (uint16_t)~sum; +} + +static inline uint32_t csum_add(uint32_t csum, uint32_t addend) +{ + uint32_t res = csum; + + res += addend; + return res + (res < addend); +} + +__attribute__ ((noinline)) uint32_t checksum6_nofold(void *data, size_t len, uint32_t sum) +{ + uint16_t *words = data; + size_t i; + + for (i = 0; i < len / sizeof(uint16_t); i++) + sum = csum_add(sum, words[i]); + if (len & 1) + sum = csum_add(sum, ((char *)data)[len - 1]); + return sum; +} + +__attribute__ ((noinline)) uint16_t icmp6_checksum(struct sockaddr_in6 *src, + struct sockaddr_in6 *dst, + void *ptr, size_t len, uint8_t proto) +{ + struct { + struct in6_addr saddr; + struct in6_addr daddr; + uint32_t payload_len; + uint8_t zero[3]; + uint8_t nexthdr; + } pseudo_header = {}; + uint32_t sum; + + pseudo_header.saddr = src->sin6_addr; + pseudo_header.daddr = dst->sin6_addr; + pseudo_header.payload_len = htonl(len); + pseudo_header.nexthdr = proto; + + sum = checksum6_nofold(&pseudo_header, sizeof(pseudo_header), 0); + sum = checksum6_nofold(ptr, len, sum); + + return csum_fold(sum); +} + +static void icmp6_interfere(int type, int code, uint32_t rcv_nxt, + struct sockaddr_in6 *src, struct sockaddr_in6 *dst) +{ + int sk = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + struct sockaddr_in6 dst_raw = *dst; + struct { + struct ipv6hdr iph; + struct icmp6hdr icmph; + struct ipv6hdr iphe; + struct { + uint16_t sport; + uint16_t dport; + uint32_t seq; + } tcph; + } packet = {}; + size_t packet_len; + ssize_t bytes; + + + if (sk < 0) + test_error("socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)"); + + packet_len = sizeof(packet) - sizeof(packet.iph); + set_ip6hdr(&packet.iph, packet_len, IPPROTO_ICMPV6, src, dst); + + packet.icmph.icmp6_type = type; + packet.icmph.icmp6_code = code; + + packet_len = sizeof(packet.iphe) + sizeof(packet.tcph); + set_ip6hdr(&packet.iphe, packet_len, IPPROTO_TCP, dst, src); + + packet.tcph.sport = dst->sin6_port; + packet.tcph.dport = src->sin6_port; + packet.tcph.seq = htonl(rcv_nxt); + + packet_len = sizeof(packet) - sizeof(packet.iph); + + packet.icmph.icmp6_cksum = icmp6_checksum(src, dst, + (void *)&packet.icmph, packet_len, IPPROTO_ICMPV6); + + dst_raw.sin6_port = htons(IPPROTO_RAW); + bytes = sendto(sk, &packet, sizeof(packet), 0, + (struct sockaddr *)&dst_raw, sizeof(dst_raw)); + if (bytes != sizeof(packet)) + test_error("send(): %zd", bytes); + icmps_sent++; + + close(sk); +} + +static uint32_t get_rcv_nxt(int sk) +{ + int val = TCP_REPAIR_ON; + uint32_t ret; + socklen_t sz = sizeof(ret); + + if (setsockopt(sk, SOL_TCP, TCP_REPAIR, &val, sizeof(val))) + test_error("setsockopt(TCP_REPAIR)"); + val = TCP_RECV_QUEUE; + if (setsockopt(sk, SOL_TCP, TCP_REPAIR_QUEUE, &val, sizeof(val))) + test_error("setsockopt(TCP_REPAIR_QUEUE)"); + if (getsockopt(sk, SOL_TCP, TCP_QUEUE_SEQ, &ret, &sz)) + test_error("getsockopt(TCP_QUEUE_SEQ)"); + val = TCP_REPAIR_OFF_NO_WP; + if (setsockopt(sk, SOL_TCP, TCP_REPAIR, &val, sizeof(val))) + test_error("setsockopt(TCP_REPAIR)"); + return ret; +} + +static void icmp_interfere(const size_t nr, uint32_t rcv_nxt, void *src, void *dst) +{ + struct sockaddr_in *saddr4 = src; + struct sockaddr_in *daddr4 = dst; + struct sockaddr_in6 *saddr6 = src; + struct sockaddr_in6 *daddr6 = dst; + size_t i; + + if (saddr4->sin_family != daddr4->sin_family) + test_error("Different address families"); + + for (i = 0; i < nr; i++) { + if (saddr4->sin_family == AF_INET) { + icmp_interfere4(ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, + rcv_nxt, saddr4, daddr4); + icmp_interfere4(ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, + rcv_nxt, saddr4, daddr4); + icmp_interfere4(ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, + rcv_nxt, saddr4, daddr4); + icmps_sent += 3; + } else if (saddr4->sin_family == AF_INET6) { + icmp6_interfere(ICMPV6_DEST_UNREACH, + ICMPV6_ADM_PROHIBITED, + rcv_nxt, saddr6, daddr6); + icmp6_interfere(ICMPV6_DEST_UNREACH, + ICMPV6_PORT_UNREACH, + rcv_nxt, saddr6, daddr6); + icmps_sent += 2; + } else { + test_error("Not ip address family"); + } + } +} + +static void send_interfered(int sk) +{ + const unsigned int timeout = TEST_TIMEOUT_SEC; + struct sockaddr_in6 src, dst; + socklen_t addr_sz; + + addr_sz = sizeof(src); + if (getsockname(sk, &src, &addr_sz)) + test_error("getsockname()"); + addr_sz = sizeof(dst); + if (getpeername(sk, &dst, &addr_sz)) + test_error("getpeername()"); + + while (1) { + uint32_t rcv_nxt; + + if (test_client_verify(sk, packet_size, packets_nr, timeout)) { + test_fail("client: connection is broken"); + return; + } + packets_sent += packets_nr; + rcv_nxt = get_rcv_nxt(sk); + icmp_interfere(packets_nr, rcv_nxt, (void *)&src, (void *)&dst); + } +} + +static void *client_fn(void *arg) +{ + int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); + + if (sk < 0) + test_error("socket()"); + + if (test_set_ao(sk, "password", 0, this_ip_dest, -1, 100, 100)) + test_error("setsockopt(TCP_AO)"); + + synchronize_threads(); + if (test_connect_socket(sk, this_ip_dest, test_server_port) <= 0) + test_error("failed to connect()"); + synchronize_threads(); + + send_interfered(sk); + + /* Not expecting client to quit */ + test_fail("client disconnected"); + + return NULL; +} + +int main(int argc, char *argv[]) +{ + test_init(3, server_fn, client_fn); + return 0; +} From patchwork Thu Oct 27 20:43:39 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022784 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 2C837FA3744 for ; Thu, 27 Oct 2022 20:52:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237164AbiJ0Uwg (ORCPT ); Thu, 27 Oct 2022 16:52:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54578 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237163AbiJ0Uvz (ORCPT ); Thu, 27 Oct 2022 16:51:55 -0400 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 ABB747AB12 for ; Thu, 27 Oct 2022 13:45:24 -0700 (PDT) Received: by mail-wr1-x42c.google.com with SMTP id k8so4194841wrh.1 for ; Thu, 27 Oct 2022 13:45:24 -0700 (PDT) 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=kP+SAh+F64ZkLFr52URQ+wzrGshBfXjUpIMRDp+w/YQ=; b=cjMFkXCN4OjefEPDADI83YvwiOmM1qytWHgGNelAN009vdYvGIkY/Q7uaAdtyFEzE8 paYJTrR5gn0aUFKNDEsNIy1KbtVTChRSbBHjTOeXWj+IpauZaycnbpOJCwPuf9+wUxZm 4NCYOREUuXnpfaOQX+letnek2oWN4OUe8Oq3uOhM7HE+m41ri79jqVH+JP/E1BtUkC/9 H52jpASiiO4tr7hNkSVdD+AIl3AKLW0JkY7anJ7C6kT2AbDyUeX2cmMhHr9OIO9K1N+/ vjsHW0ebaiPJa90XBql9eGGp0iVpEMGL94eO3d+xKkk+fn9mB7LBpji+Ex5hF4WBt6q5 B1cw== 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=kP+SAh+F64ZkLFr52URQ+wzrGshBfXjUpIMRDp+w/YQ=; b=Bp1tmEOZVpaR4SGnA+OXSTjZ3WobM1fRYymxeUJ0qIm+XxlKgAlAuFG+pOfSBjLaUc 6GxW/B/SLsAW28bMuPZCd+DiULD/uEXplZzDHLcQl02slQEun3bWnRjm9EuxcS9wqXa9 7XmC3uVB6bKWVacyybO9fGkPXqR0g6UI/UOaUcoYA/UqHRtZYXLLZkEmjyWzkNVLMByN Ik6Ebu/w0bVQZf+JSOTEo9T3kb6q3ygMufyHyAAUzd22WZZjABCNxxn94mY7iolR1ddQ Is+X0Sjah8ZRmLXAf/IGzZA/s1I/asr/e/x3i7TeFh5SbPFQqtsAaBpcOnbPDsn2LJuU zW3w== X-Gm-Message-State: ACrzQf0MDXs4oJVNJ+dMO8pcZmdSH/J1PRiQtDWHIokLnbxCfO/nyKO/ 0X/OdnHGCkutJpx98Ruk0aD2OA== X-Google-Smtp-Source: AMsMyM5vCqk71RZPJ/r8tzBefdkb+G+uw3UohC97chYG1D+N0POj8bODmJ3NxPNdNtv2JPvA2He2eg== X-Received: by 2002:a5d:590d:0:b0:236:4ddd:1869 with SMTP id v13-20020a5d590d000000b002364ddd1869mr23913510wrd.709.1666903477662; Thu, 27 Oct 2022 13:44:37 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:37 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 28/36] selftest/net: Add TCP-AO ICMPs accept test Date: Thu, 27 Oct 2022 21:43:39 +0100 Message-Id: <20221027204347.529913-29-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 Reverse to icmps-discard test: the server accepts ICMPs, using TCP_AO_CMDF_ACCEPT_ICMP and it is expected to fail under ICMP flood from client. Test that the default pre-TCP-AO behaviour functions when TCP_AO_CMDF_ACCEPT_ICMP is set. Expected output for ipv4 version (in case it receives ICMP_PROT_UNREACH): > # ./icmps-accept_ipv4 > 1..3 > # 3209[lib/setup.c:166] rand seed 1642623870 > TAP version 13 > # 3209[lib/proc.c:207] Snmp6 Ip6InReceives: 0 => 1 > # 3209[lib/proc.c:207] Snmp6 Ip6InNoRoutes: 0 => 1 > # 3209[lib/proc.c:207] Snmp6 Ip6InOctets: 0 => 76 > # 3209[lib/proc.c:207] Snmp6 Ip6InNoECTPkts: 0 => 1 > # 3209[lib/proc.c:207] Tcp InSegs: 3 => 23 > # 3209[lib/proc.c:207] Tcp OutSegs: 2 => 22 > # 3209[lib/proc.c:207] IcmpMsg InType3: 0 => 4 > # 3209[lib/proc.c:207] Icmp InMsgs: 0 => 4 > # 3209[lib/proc.c:207] Icmp InDestUnreachs: 0 => 4 > # 3209[lib/proc.c:207] Ip InReceives: 3 => 27 > # 3209[lib/proc.c:207] Ip InDelivers: 3 => 27 > # 3209[lib/proc.c:207] Ip OutRequests: 2 => 22 > # 3209[lib/proc.c:207] IpExt InOctets: 288 => 3420 > # 3209[lib/proc.c:207] IpExt OutOctets: 124 => 3244 > # 3209[lib/proc.c:207] IpExt InNoECTPkts: 3 => 25 > # 3209[lib/proc.c:207] TcpExt TCPPureAcks: 1 => 2 > # 3209[lib/proc.c:207] TcpExt TCPOrigDataSent: 0 => 20 > # 3209[lib/proc.c:207] TcpExt TCPDelivered: 0 => 19 > # 3209[lib/proc.c:207] TcpExt TCPAOGood: 3 => 23 > ok 1 InDestUnreachs delivered 4 > ok 2 server failed with -92: Protocol not available > ok 3 TCPAODroppedIcmps counter didn't change: 0 >= 0 > # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0 Expected output for ipv6 version (in case it receives ADM_PROHIBITED): > # ./icmps-accept_ipv6 > 1..3 > # 3277[lib/setup.c:166] rand seed 1642624035 > TAP version 13 > # 3277[lib/proc.c:207] Snmp6 Ip6InReceives: 6 => 31 > # 3277[lib/proc.c:207] Snmp6 Ip6InDelivers: 4 => 29 > # 3277[lib/proc.c:207] Snmp6 Ip6OutRequests: 4 => 24 > # 3277[lib/proc.c:207] Snmp6 Ip6InOctets: 592 => 4492 > # 3277[lib/proc.c:207] Snmp6 Ip6OutOctets: 332 => 3852 > # 3277[lib/proc.c:207] Snmp6 Ip6InNoECTPkts: 6 => 31 > # 3277[lib/proc.c:207] Snmp6 Icmp6InMsgs: 1 => 6 > # 3277[lib/proc.c:207] Snmp6 Icmp6InDestUnreachs: 0 => 5 > # 3277[lib/proc.c:207] Snmp6 Icmp6InType1: 0 => 5 > # 3277[lib/proc.c:207] Tcp InSegs: 3 => 23 > # 3277[lib/proc.c:207] Tcp OutSegs: 2 => 22 > # 3277[lib/proc.c:207] TcpExt TCPPureAcks: 1 => 2 > # 3277[lib/proc.c:207] TcpExt TCPOrigDataSent: 0 => 20 > # 3277[lib/proc.c:207] TcpExt TCPDelivered: 0 => 19 > # 3277[lib/proc.c:207] TcpExt TCPAOGood: 3 => 23 > ok 1 Icmp6InDestUnreachs delivered 5 > ok 2 server failed with -13: Permission denied > ok 3 TCPAODroppedIcmps counter didn't change: 0 >= 0 > # Totals: pass:3 fail:0 xfail:0 xpass:0 skip:0 error:0 With some luck the server may fail with ECONNREFUSED (depending on what icmp packet was delivered firstly). For the kernel error handlers see: tab_unreach[] and icmp_err_convert[]. Signed-off-by: Dmitry Safonov --- tools/testing/selftests/net/tcp_ao/Makefile | 4 +++- .../testing/selftests/net/tcp_ao/icmps-accept.c | 1 + .../selftests/net/tcp_ao/icmps-discard.c | 17 +++++++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) create mode 120000 tools/testing/selftests/net/tcp_ao/icmps-accept.c diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile index 9acfd782c20f..a178bde0af08 100644 --- a/tools/testing/selftests/net/tcp_ao/Makefile +++ b/tools/testing/selftests/net/tcp_ao/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -TEST_BOTH_AF := connect icmps-discard +TEST_BOTH_AF := connect icmps-discard icmps-accept TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4) TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6) @@ -43,3 +43,5 @@ $(OUTPUT)/%_ipv4: %.c $(OUTPUT)/%_ipv6: %.c $(LINK.c) -DIPV6_TEST $^ $(LDLIBS) -o $@ +$(OUTPUT)/icmps-accept_ipv4: CFLAGS+= -DTEST_ICMPS_ACCEPT +$(OUTPUT)/icmps-accept_ipv6: CFLAGS+= -DTEST_ICMPS_ACCEPT diff --git a/tools/testing/selftests/net/tcp_ao/icmps-accept.c b/tools/testing/selftests/net/tcp_ao/icmps-accept.c new file mode 120000 index 000000000000..0a5bb85eb260 --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/icmps-accept.c @@ -0,0 +1 @@ +icmps-discard.c \ No newline at end of file diff --git a/tools/testing/selftests/net/tcp_ao/icmps-discard.c b/tools/testing/selftests/net/tcp_ao/icmps-discard.c index 07eba1308b4e..d90017dfc20d 100644 --- a/tools/testing/selftests/net/tcp_ao/icmps-discard.c +++ b/tools/testing/selftests/net/tcp_ao/icmps-discard.c @@ -43,8 +43,17 @@ const int sk_ip_level = SOL_IP; const int sk_recverr = IP_RECVERR; #endif -#define test_icmps_fail test_fail -#define test_icmps_ok test_ok +/* + * Server is expected to fail with hard error if + * TCP_AO_CMDF_ACCEPT_ICMP is set + */ +#ifdef TEST_ICMPS_ACCEPT +# define test_icmps_fail test_ok +# define test_icmps_ok test_fail +#else +# define test_icmps_fail test_fail +# define test_icmps_ok test_ok +#endif static void serve_interfered(int sk) { @@ -98,6 +107,10 @@ static void *server_fn(void *arg) lsk = test_listen_socket(this_ip_addr, test_server_port, 1); +#ifdef TEST_ICMPS_ACCEPT + flags = TCP_AO_CMDF_ACCEPT_ICMP; +#endif + if (test_set_ao(lsk, "password", flags, this_ip_dest, -1, 100, 100)) test_error("setsockopt(TCP_AO)"); synchronize_threads(); From patchwork Thu Oct 27 20:43:40 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022783 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 BE510FA3743 for ; Thu, 27 Oct 2022 20:52:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237199AbiJ0Uwf (ORCPT ); Thu, 27 Oct 2022 16:52:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54424 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237191AbiJ0Uvy (ORCPT ); Thu, 27 Oct 2022 16:51:54 -0400 Received: from mail-wm1-x335.google.com (mail-wm1-x335.google.com [IPv6:2a00:1450:4864:20::335]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AD30C923DA for ; Thu, 27 Oct 2022 13:45:25 -0700 (PDT) Received: by mail-wm1-x335.google.com with SMTP id l14-20020a05600c1d0e00b003c6ecc94285so4502073wms.1 for ; Thu, 27 Oct 2022 13:45:24 -0700 (PDT) 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=lp2VxXZrnF/kw40YEQ4TVBQpSqwfLCviQOCTAxoI/HQ=; b=TY/MbBMiIMB3eZjGCYvQoY0BlWIrl7u4BfmqoJbw+su0aVxHFBVYk/5H9FIbQy9oeU grras9Br2xg6oTogFbxkBxqLVTH7zvbLMYu3TG5B81UhKzOCGuaESe9nI0LiH3ZhtDb5 43+luKtGhD9QZFBRfzQqOIKc2b6ThuwrvI9whwmFNvaeO2QDyYX+prsMSZ/U7LENOFW2 k7V9Bf27h/lhdxtCrWCKw8HGQ47d7C2AYzLE2uoXqOhns826Q+RLH0wtTnkzV7kNOU42 P8OqTay2Y353lxY2KLQefhf6ROz0jWlzxnQ38kdHs3PxxoXkqhaERpf5bhTxZU9fThZP m44Q== 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=lp2VxXZrnF/kw40YEQ4TVBQpSqwfLCviQOCTAxoI/HQ=; b=B/Hc3Y/cgXStMzr1Ub3muD8+H+D+oEWpLTIIZ/6yICuELy2Ye8fyvMUiDz91hc1diR 0JhHQH9VfaAn+FoxOcAJtiQ1OWxk3F9pqZclJE3NIMxLwi/S8F0zXxO3+eQtyBhnHrU1 Tw2nMgOifCDYSCwB5EtWYZYkcDeNArYwoqGL/2oXaErwpD98znSxbc3E5I+zV3IC7Lym fgwvI6ZjitseBGgtJsIE0Gwkx1t0J3NfxSqerM6bras6H/2LlTuR/L90FJ+9flO+gsmH OVDJxLumeb/fOkWs2d7tV8nlwKhN0z3AbxD2poWG8NkbQvrPm1cI1fityG6LkatpaBlc Ijeg== X-Gm-Message-State: ACrzQf25vQayaIQ7V5bsvpOHfu5Y0xZx6plp5a5T3X3HS318JBjATxqu iEQLdQprSUgrAJiv/rkBV3Yz+Q== X-Google-Smtp-Source: AMsMyM4WOMAC/5xDPJPc5+fsnhnpk/mcZMNbCH7XTeLgtC4oI/4jA2tl34CfUIiOLYHbsSFmUQkhHw== X-Received: by 2002:a1c:7215:0:b0:3c7:130c:a77f with SMTP id n21-20020a1c7215000000b003c7130ca77fmr7446309wmc.151.1666903479106; Thu, 27 Oct 2022 13:44:39 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:38 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 29/36] selftest/tcp-ao: Add a test for MKT matching Date: Thu, 27 Oct 2022 21:43:40 +0100 Message-Id: <20221027204347.529913-30-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 TCP-AO tests on connect()/accept() pair. SNMP counters exposed by kernel are very useful here to verify the expected behavior of TCP-AO. Signed-off-by: Dmitry Safonov --- tools/testing/selftests/net/tcp_ao/Makefile | 2 +- .../selftests/net/tcp_ao/connect-deny.c | 217 ++++++++++++++++++ 2 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/net/tcp_ao/connect-deny.c diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile index a178bde0af08..5064e34ebe38 100644 --- a/tools/testing/selftests/net/tcp_ao/Makefile +++ b/tools/testing/selftests/net/tcp_ao/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -TEST_BOTH_AF := connect icmps-discard icmps-accept +TEST_BOTH_AF := connect icmps-discard icmps-accept connect-deny TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4) TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6) diff --git a/tools/testing/selftests/net/tcp_ao/connect-deny.c b/tools/testing/selftests/net/tcp_ao/connect-deny.c new file mode 100644 index 000000000000..8f1b067e611f --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/connect-deny.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Author: Dmitry Safonov */ +#include +#include "aolib.h" + +typedef uint8_t fault_t; +#define F_TIMEOUT 1 +#define F_KEYREJECT 2 + +#define fault(type) (inj == type) + +static void try_accept(const char *tst_name, unsigned int port, const char *pwd, + union tcp_addr addr, uint8_t prefix, + uint8_t sndid, uint8_t rcvid, const char *cnt_name, + fault_t inj) +{ + uint64_t before_cnt, after_cnt; + int lsk, err, sk = 0; + time_t timeout; + + lsk = test_listen_socket(this_ip_addr, port, 1); + + if (pwd && test_set_ao(lsk, pwd, 0, addr, prefix, sndid, rcvid)) + test_error("setsockopt(TCP_AO)"); + + if (cnt_name) + before_cnt = netstat_get_one(cnt_name, NULL); + + synchronize_threads(); /* preparations done */ + + timeout = fault(F_TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC; + err = test_wait_fd(lsk, timeout, 0); + if (err < 0) + test_error("test_wait_fd()"); + else if (!err) { + if (!fault(F_TIMEOUT)) + test_fail("timeouted for accept()"); + } else { + if (fault(F_TIMEOUT)) + test_fail("ready to accept"); + + sk = accept(lsk, NULL, NULL); + if (sk < 0) { + test_error("accept()"); + } else { + if (fault(F_TIMEOUT)) + test_fail("%s: accepted", tst_name); + } + } + + close(lsk); + + if (!cnt_name) + goto out; + + after_cnt = netstat_get_one(cnt_name, NULL); + + if (after_cnt <= before_cnt) { + test_fail("%s: %s counter did not increase: %zu <= %zu", + tst_name, cnt_name, after_cnt, before_cnt); + } else { + test_ok("%s: counter %s increased %zu => %zu", + tst_name, cnt_name, before_cnt, after_cnt); + } + +out: + synchronize_threads(); /* close() */ + if (sk > 0) + close(sk); +} + +static void *server_fn(void *arg) +{ + union tcp_addr wrong_addr, network_addr; + unsigned int port = test_server_port; + + if (inet_pton(TEST_FAMILY, TEST_WRONG_IP, &wrong_addr) != 1) + test_error("Can't convert ip address %s", TEST_WRONG_IP); + + try_accept("Non-AO server + AO client", port++, NULL, + this_ip_dest, -1, 100, 100, "TCPAOKeyNotFound", F_TIMEOUT); + + try_accept("AO server + Non-AO client", port++, "password", + this_ip_dest, -1, 100, 100, "TCPAORequired", F_TIMEOUT); + + try_accept("Wrong password", port++, "password2", + this_ip_dest, -1, 100, 100, "TCPAOBad", F_TIMEOUT); + + try_accept("Wrong rcv id", port++, "password", + this_ip_dest, -1, 100, 101, "TCPAOKeyNotFound", F_TIMEOUT); + + try_accept("Wrong snd id", port++, "password", + this_ip_dest, -1, 101, 100, "TCPAOGood", F_TIMEOUT); + + try_accept("Server: Wrong addr", port++, "password", + wrong_addr, -1, 100, 100, "TCPAOKeyNotFound", F_TIMEOUT); + + try_accept("Client: Wrong addr", port++, NULL, + this_ip_dest, -1, 100, 100, NULL, F_TIMEOUT); + + try_accept("rcv id != snd id", port++, "password", + this_ip_dest, -1, 200, 100, "TCPAOGood", 0); + + if (inet_pton(TEST_FAMILY, TEST_NETWORK, &network_addr) != 1) + test_error("Can't convert ip address %s", TEST_NETWORK); + + try_accept("Server: prefix match", port++, "password", + network_addr, 16, 100, 100, "TCPAOGood", 0); + + try_accept("Client: prefix match", port++, "password", + this_ip_dest, -1, 100, 100, "TCPAOGood", 0); + + /* client exits */ + synchronize_threads(); + return NULL; +} + +static void try_connect(const char *tst_name, unsigned int port, + const char *pwd, union tcp_addr addr, uint8_t prefix, + uint8_t sndid, uint8_t rcvid, fault_t inj) +{ + time_t timeout; + int sk, ret; + + sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); + if (sk < 0) + test_error("socket()"); + + if (pwd && test_set_ao(sk, pwd, 0, addr, prefix, sndid, rcvid)) + test_error("setsockopt(TCP_AO)"); + + synchronize_threads(); /* preparations done */ + + timeout = fault(F_TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC; + ret = _test_connect_socket(sk, this_ip_dest, port, timeout); + + if (ret < 0) { + if (fault(F_KEYREJECT) && ret == -EKEYREJECTED) { + test_ok("%s: connect() was prevented", tst_name); + goto out; + } else if (ret == -ECONNREFUSED && + (fault(F_TIMEOUT) || fault(F_KEYREJECT))) { + test_ok("%s: refused to connect", tst_name); + goto out; + } else { + test_error("%s: connect() returned %d", tst_name, ret); + } + } + + if (ret == 0) { + if (fault(F_TIMEOUT)) + test_ok("%s", tst_name); + else + test_fail("%s: failed to connect()", tst_name); + } else { + if (fault(F_TIMEOUT) || fault(F_KEYREJECT)) + test_fail("%s: connected", tst_name); + else + test_ok("%s: connected", tst_name); + } + +out: + synchronize_threads(); /* close() */ + + if (ret > 0) + close(sk); +} + +static void *client_fn(void *arg) +{ + union tcp_addr wrong_addr, network_addr; + unsigned int port = test_server_port; + + if (inet_pton(TEST_FAMILY, TEST_WRONG_IP, &wrong_addr) != 1) + test_error("Can't convert ip address %s", TEST_WRONG_IP); + + try_connect("Non-AO server + AO client", port++, "password", + this_ip_dest, -1, 100, 100, F_TIMEOUT); + + try_connect("AO server + Non-AO client", port++, NULL, + this_ip_dest, -1, 100, 100, F_TIMEOUT); + + try_connect("Wrong password", port++, "password", + this_ip_dest, -1, 100, 100, F_TIMEOUT); + + try_connect("Wrong rcv id", port++, "password", + this_ip_dest, -1, 100, 100, F_TIMEOUT); + + try_connect("Wrong snd id", port++, "password", + this_ip_dest, -1, 100, 100, F_TIMEOUT); + + try_connect("Server: Wrong addr", port++, "password", + this_ip_dest, -1, 100, 100, F_TIMEOUT); + + try_connect("Client: Wrong addr", port++, "password", + wrong_addr, -1, 100, 100, F_KEYREJECT); + + try_connect("rcv id != snd id", port++, "password", + this_ip_dest, -1, 100, 200, 0); + + if (inet_pton(TEST_FAMILY, TEST_NETWORK, &network_addr) != 1) + test_error("Can't convert ip address %s", TEST_NETWORK); + + try_connect("Server: prefix match", port++, "password", + this_ip_dest, -1, 100, 100, 0); + + try_connect("Client: prefix match", port++, "password", + network_addr, 16, 100, 100, 0); + + return NULL; +} + +int main(int argc, char *argv[]) +{ + test_init(19, server_fn, client_fn); + return 0; +} From patchwork Thu Oct 27 20:43:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022754 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 D4A37FA3743 for ; Thu, 27 Oct 2022 20:49:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237130AbiJ0Utv (ORCPT ); Thu, 27 Oct 2022 16:49:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33978 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237129AbiJ0UsZ (ORCPT ); Thu, 27 Oct 2022 16:48:25 -0400 Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7AE2D2AE0 for ; Thu, 27 Oct 2022 13:44:41 -0700 (PDT) Received: by mail-wm1-x332.google.com with SMTP id jb18so1782235wmb.4 for ; Thu, 27 Oct 2022 13:44:41 -0700 (PDT) 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=RiUzQY/KDOVQ9OpO7U/pjYWKIDHSHLqGcD95KlhxZRE=; b=cCJ7TNvnRDYM8NVUP7gAR+/UvYLpXy8bhDJB+F+2c8SxLYPWwFOnMqWyvzrsKBRWpQ ajD1KUi0H+UQIQoXweFZ/g15baxLVxe4QfNJb6/H/tXRZG58x4/sBcbvyRG6ybVtNDML wWqZtw2qn4XaaIuF9XrEVliJk45Bt4zRrarh4SANuJ9Crg7J43ztNsCgbhL8jybg0mK4 ZzBV+Juh41FLKorJDTRfqJ+iG37yhhn6+e5kVOnIXLOUBZPsYOpS3MhSexqo6gNFfBns MrGXZKGtAN7LZPMLWD7mnqp4AfcuNTl7XcvG9jriee5qhAzfNIE087AgyACP56GUT4Pm 1QUA== 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=RiUzQY/KDOVQ9OpO7U/pjYWKIDHSHLqGcD95KlhxZRE=; b=f/SJ7heG/StkJ4/9bQUGOfhfZ82HB3EjsvWjoiLhFibE3A97HDDpUz11iv73QzDhdq WhbSeyKJ/Fiy+POwinp307eN1Easzzj5Z4lA9vXB0SXt5biV5UyD6pLUoMzpPEuasJ3w n3NW8GZe5Jtyw25ipu5Kuu4pMrOz0lBczYPrHRcDulVQJc4CButrT7Dtf5RnAHoSzFlU IJjjk/5Du/FOwJylrYIwYdX4xoPh3rzTSGRS8kcUia3D5csMbYnjx9gY0XI20AfLOAFn 6EkqV4CK1WKfK80cXcC+s3p3axjtHYwyim1QMncEOxlvYgbwWujHjGiy/JTxeOkFNoho E5Zw== X-Gm-Message-State: ACrzQf2SgY0i7XJaqNUli8qstVdbhO1xph3ocyuHud34xtsLWWlE15PM YYsS3DanY1nhGvVw63+aCbD41w== X-Google-Smtp-Source: AMsMyM5Hav1UL9exmQ7gJ56RA3Xu8SsGKY9DmS/6nfnlhg3pgZgYwcgV0syDrqZl7lrauKdTco6LZQ== X-Received: by 2002:a05:600c:5127:b0:3c6:47ff:5d33 with SMTP id o39-20020a05600c512700b003c647ff5d33mr7500384wms.68.1666903480665; Thu, 27 Oct 2022 13:44:40 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:40 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 30/36] selftest/tcp-ao: Add test for TCP-AO add setsockopt() command Date: Thu, 27 Oct 2022 21:43:41 +0100 Message-Id: <20221027204347.529913-31-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 Verify corner-cases for UAPI. Sample output: > # ./setsockopt-closed_ipv6 > 1..16 > # 9508[lib/setup.c:173] rand seed 1643819055 > TAP version 13 > ok 1 minimum size > ok 2 extended size > ok 3 bad algo > ok 4 bad ao flags > ok 5 empty prefix > ok 6 prefix, any addr > ok 7 no prefix, any addr > ok 8 too short prefix > ok 9 too big prefix > ok 10 too big maclen > ok 11 bad key flags > ok 12 too big keylen > not ok 13 duplicate: full copy: setsockopt() was expected to fail with 17 > ok 14 duplicate: any addr key on the socket > ok 15 duplicate: add any addr key > not ok 16 duplicate: add any addr for the same subnet: setsockopt() was expected to fail with 17 Signed-off-by: Dmitry Safonov --- tools/testing/selftests/net/tcp_ao/Makefile | 3 +- .../selftests/net/tcp_ao/setsockopt-closed.c | 191 ++++++++++++++++++ 2 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/net/tcp_ao/setsockopt-closed.c diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile index 5064e34ebe38..a001dc2aed4e 100644 --- a/tools/testing/selftests/net/tcp_ao/Makefile +++ b/tools/testing/selftests/net/tcp_ao/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -TEST_BOTH_AF := connect icmps-discard icmps-accept connect-deny +TEST_BOTH_AF := connect icmps-discard icmps-accept connect-deny \ + setsockopt-closed TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4) TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6) diff --git a/tools/testing/selftests/net/tcp_ao/setsockopt-closed.c b/tools/testing/selftests/net/tcp_ao/setsockopt-closed.c new file mode 100644 index 000000000000..be2cbc407f60 --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/setsockopt-closed.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Author: Dmitry Safonov */ +#include +#include "../../../../include/linux/kernel.h" +#include "aolib.h" + +static void clean_ao(int sk, struct tcp_ao *ao) +{ + struct tcp_ao_del ao_del = {}; + + ao_del.tcpa_sndid = ao->tcpa_sndid; + ao_del.tcpa_rcvid = ao->tcpa_rcvid; + ao_del.tcpa_prefix = ao->tcpa_prefix; + memcpy(&ao_del.tcpa_addr, &ao->tcpa_addr, sizeof(ao->tcpa_addr)); + + if (setsockopt(sk, IPPROTO_TCP, TCP_AO_DEL, &ao_del, sizeof(ao_del))) + test_error("setsockopt(TCP_AO_DEL) failed to clean"); + close(sk); +} + +static void setsockopt_checked(int sk, int optname, struct tcp_ao *ao, + int err, const char *tst) +{ + int ret; + + errno = 0; + ret = setsockopt(sk, IPPROTO_TCP, optname, ao, sizeof(*ao)); + if (ret == -1) { + if (errno == err) { + test_ok("%s", tst); + return; + } + test_fail("%s: setsockopt() returned %d", tst, err); + return; + } + + if (err) { + test_fail("%s: setsockopt() was expected to fail with %d", tst, err); + } else { + test_ok("%s", tst); + test_verify_socket_ao(sk, ao); + } + clean_ao(sk, ao); +} + +static int prepare_defs(struct tcp_ao *ao) +{ + int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); + + if (sk < 0) + test_error("socket()"); + + if (test_prepare_def_ao(ao, "password", 0, this_ip_dest, -1, 100, 100)) + test_error("prepare default tcp_ao"); + + return sk; +} + +static void test_extend(void) +{ + struct tcp_ao ao; + struct { + struct tcp_ao ao; + char *extend[100]; + } ao_big = {}; + int ret, sk; + + sk = prepare_defs(&ao); + errno = 0; + ret = setsockopt(sk, IPPROTO_TCP, TCP_AO, + &ao, offsetof(struct tcp_ao, tcpa_key)); + if (!ret) { + test_fail("minminum size: accepted invalid size"); + clean_ao(sk, &ao); + } else if (errno != EINVAL) { + test_fail("minminum size: failed with %d", errno); + } else { + test_ok("minimum size"); + } + + sk = prepare_defs(&ao_big.ao); + errno = 0; + ret = setsockopt(sk, IPPROTO_TCP, TCP_AO, &ao_big.ao, sizeof(ao_big)); + if (ret) { + test_fail("extended size: returned %d", ret); + } else { + test_ok("extended size"); + clean_ao(sk, &ao_big.ao); + } +} + +static void einval_tests(void) +{ + struct tcp_ao ao; + int sk; + + sk = prepare_defs(&ao); + strcpy(ao.tcpa_alg_name, "imaginary hash algo"); + setsockopt_checked(sk, TCP_AO, &ao, ENOENT, "bad algo"); + + sk = prepare_defs(&ao); + ao.tcpa_flags = (uint16_t)(-1); + setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "bad ao flags"); + + sk = prepare_defs(&ao); + ao.tcpa_prefix = 0; + setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "empty prefix"); + + sk = prepare_defs(&ao); + ao.tcpa_prefix = 32; + memcpy(&ao.tcpa_addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY)); + setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "prefix, any addr"); + + sk = prepare_defs(&ao); + ao.tcpa_prefix = 0; + memcpy(&ao.tcpa_addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY)); + setsockopt_checked(sk, TCP_AO, &ao, 0, "no prefix, any addr"); + + sk = prepare_defs(&ao); + ao.tcpa_prefix = 2; + setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "too short prefix"); + + sk = prepare_defs(&ao); + ao.tcpa_prefix = 129; + setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "too big prefix"); + + sk = prepare_defs(&ao); + ao.tcpa_maclen = 100; + setsockopt_checked(sk, TCP_AO, &ao, EMSGSIZE, "too big maclen"); + + sk = prepare_defs(&ao); + ao.tcpa_keyflags = (uint8_t)(-1); + setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "bad key flags"); + + sk = prepare_defs(&ao); + ao.tcpa_keylen = TCP_AO_MAXKEYLEN + 1; + setsockopt_checked(sk, TCP_AO, &ao, EINVAL, "too big keylen"); +} + +static void duplicate_tests(void) +{ + union tcp_addr network_dup; + struct tcp_ao ao, ao2; + int sk; + + sk = prepare_defs(&ao); + if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &ao, sizeof(ao))) + test_error("setsockopt()"); + setsockopt_checked(sk, TCP_AO, &ao, EEXIST, "duplicate: full copy"); + + sk = prepare_defs(&ao); + ao2 = ao; + memcpy(&ao2.tcpa_addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY)); + ao2.tcpa_prefix = 0; + if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &ao2, sizeof(ao))) + test_error("setsockopt()"); + setsockopt_checked(sk, TCP_AO, &ao, EEXIST, "duplicate: any addr key on the socket"); + + sk = prepare_defs(&ao); + if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &ao, sizeof(ao))) + test_error("setsockopt()"); + memcpy(&ao.tcpa_addr, &SOCKADDR_ANY, sizeof(SOCKADDR_ANY)); + ao.tcpa_prefix = 0; + setsockopt_checked(sk, TCP_AO, &ao, EEXIST, "duplicate: add any addr key"); + + + if (inet_pton(TEST_FAMILY, TEST_NETWORK, &network_dup) != 1) + test_error("Can't convert ip address %s", TEST_NETWORK); + sk = prepare_defs(&ao); + if (setsockopt(sk, IPPROTO_TCP, TCP_AO, &ao, sizeof(ao))) + test_error("setsockopt()"); + if (test_prepare_def_ao(&ao, "password", 0, network_dup, 16, 100, 100)) + test_error("prepare default tcp_ao"); + setsockopt_checked(sk, TCP_AO, &ao, EEXIST, "duplicate: add any addr for the same subnet"); +} + + +static void *client_fn(void *arg) +{ + test_extend(); + einval_tests(); + duplicate_tests(); + + return NULL; +} + +int main(int argc, char *argv[]) +{ + test_init(16, client_fn, NULL); + return 0; +} From patchwork Thu Oct 27 20:43:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022785 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 71CD7ECAAA1 for ; Thu, 27 Oct 2022 20:52:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237235AbiJ0Uwv (ORCPT ); Thu, 27 Oct 2022 16:52:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46880 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237098AbiJ0Uv5 (ORCPT ); Thu, 27 Oct 2022 16:51:57 -0400 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 0A2BD92583 for ; Thu, 27 Oct 2022 13:45:25 -0700 (PDT) Received: by mail-wm1-x334.google.com with SMTP id y10so1808205wma.0 for ; Thu, 27 Oct 2022 13:45:25 -0700 (PDT) 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=dAlT835uh7pg484KnVv+j5fh9swZek7eVfQGvMY4P/0=; b=YBbQY0aB1GmkcBIR/Sx6YCKYGPfmq80VHYtSMrMivX+S09pRlbBsKezSxflwTbD3P/ t6wYT/8xmYx2kApfUXOvR86G4WTWXt/NIow6IT+3KqAvv+cmolsux+NY98MqiLe6ldXV bBu9m/RnnV4fnpTXdIyc6017LnGPX5FMYlIKKkVUir7SPCGV4Ec8tPDXlYBwVwvBMTWG bg6oNbXQ8nh3biNjp0lLDYQITQPB6dF5dtiomscg+RRDPqHm0LhybG5gJo8leHRSz1AJ 2objdXxF5nk5fxOAz1b4GxixQ068K+4JqABg3lUCmOMmCTQJAnrNxdK3vlo+Eii2W8HG pLJw== 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=dAlT835uh7pg484KnVv+j5fh9swZek7eVfQGvMY4P/0=; b=PpJTrxVpuMNZ9GKZDHpn6eeonWDHn2PgLULeNvJrBAVaht9daBPMBFwGDqiscWQfZ0 gOBm9IohkLM3qR7R0OjcspzCF1HQ4DBr5FHt1BPMwAugY0CtSkzYyTwx91iNrO7Wn8dC FqAytS3w//LHv/N4N6aPJ3j0Zv/XcZ1HTtFdlzho5HuUqSlwbAb++y//cfRT6AMWIzXE NcfzEUvcWn8iAg1uwF7eR+jNpp3IYqg2NyrCKfMV0IxHQBgWxDXEu8F+9pr1v62y5IZw MUhFvVddF9BGcz161ykiEVfkgHPQGN4c6dxks1o35ptfhQ8zlWaiY34hDlL9YUJxHLTC gq9g== X-Gm-Message-State: ACrzQf04PLmePg3X2JMGEmYx7NmEzvEROq1mL5Xthx/IKTh4WbIYU7iY hdoDogw8qb0alAjuKjEf6+tGTw== X-Google-Smtp-Source: AMsMyM6UkcGnUluKlXcwiRb0c4ArdnJ/oY+LDZsTPZ13HuFjBr8+ksMo3tQHd2wpHkmjHks3NeoMBw== X-Received: by 2002:a1c:2743:0:b0:3b3:4066:fa61 with SMTP id n64-20020a1c2743000000b003b34066fa61mr7352679wmn.79.1666903482214; Thu, 27 Oct 2022 13:44:42 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:41 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 31/36] selftests/tcp-ao: Add TCP-AO + TCP-MD5 + no sign listen socket tests Date: Thu, 27 Oct 2022 21:43:42 +0100 Message-Id: <20221027204347.529913-32-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 The test plan was (most of tests have all 3 client types): 1. TCP-AO listen (INADDR_ANY) 2. TCP-MD5 listen (INADDR_ANY) 3. non-signed listen (INADDR_ANY) 4. TCP-AO + TCP-MD5 listen (prefix) 5. TCP-AO subprefix add failure [checked in setsockopt-closed.c] 6. TCP-AO out of prefix connect [checked in connect-deny.c] 7. TCP-AO + TCP-MD5 on connect() 8. TCP-AO intersect with TCP-MD5 failure 9. Established TCP-AO: add TCP-MD5 key 10. Established TCP-MD5: add TCP-AO key 11. Established non-signed: add TCP-AO key Output produced: 1..42 TAP version 13 ok 1 AO server (INADDR_ANY): AO client: connected ok 2 AO server (INADDR_ANY): AO client: counter TCPAOGood increased 0 => 2 ok 3 AO server (INADDR_ANY): MD5 client ok 4 AO server (INADDR_ANY): MD5 client: counter TCPMD5Unexpected increased 0 => 1 ok 5 AO server (INADDR_ANY): no sign client: counter TCPAORequired increased 0 => 1 ok 6 AO server (INADDR_ANY): unsigned client ok 7 MD5 server (INADDR_ANY): AO client: counter TCPAOKeyNotFound increased 0 => 1 ok 8 MD5 server (INADDR_ANY): AO client ok 9 MD5 server (INADDR_ANY): MD5 client: connected ok 10 MD5 server (INADDR_ANY): no sign client: counter TCPMD5NotFound increased 0 => 1 ok 11 MD5 server (INADDR_ANY): no sign client ok 12 no sign server: AO client ok 13 no sign server: AO client: counter TCPAOKeyNotFound increased 1 => 2 ok 14 no sign server: MD5 client ok 15 no sign server: MD5 client: counter TCPMD5Unexpected increased 1 => 2 ok 16 no sign server: no sign client: connected ok 17 no sign server: no sign client: counter CurrEstab increased 0 => 1 ok 18 AO+MD5 server: AO client (matching): connected ok 19 AO+MD5 server: AO client (matching): counter TCPAOGood increased 4 => 6 ok 20 AO+MD5 server: AO client (misconfig, matching MD5) ok 21 AO+MD5 server: AO client (misconfig, matching MD5): counter TCPAOKeyNotFound increased 2 => 3 ok 22 AO+MD5 server: AO client (misconfig, non-matching): counter TCPAOKeyNotFound increased 3 => 4 ok 23 AO+MD5 server: AO client (misconfig, non-matching) ok 24 AO+MD5 server: MD5 client (matching): connected ok 25 AO+MD5 server: MD5 client (misconfig, matching AO) ok 26 AO+MD5 server: MD5 client (misconfig, matching AO): counter TCPMD5Unexpected increased 2 => 3 ok 27 AO+MD5 server: MD5 client (misconfig, non-matching): counter TCPMD5Unexpected increased 3 => 4 ok 28 AO+MD5 server: MD5 client (misconfig, non-matching) ok 29 AO+MD5 server: no sign client (unmatched): connected ok 30 AO+MD5 server: no sign client (unmatched): counter CurrEstab increased 0 => 1 ok 31 AO+MD5 server: no sign client (misconfig, matching AO) ok 32 AO+MD5 server: no sign client (misconfig, matching AO): counter TCPAORequired increased 1 => 2 ok 33 AO+MD5 server: no sign client (misconfig, matching MD5) ok 34 AO+MD5 server: no sign client (misconfig, matching MD5): counter TCPMD5NotFound increased 1 => 2 ok 35 AO+MD5 server: client with both [TCP-MD5] and TCP-AO keys: connect() was prevented ok 36 AO+MD5 server: client with both TCP-MD5 and [TCP-AO] keys: connect() was prevented ok 37 TCP-AO established: add TCP-MD5 key: postfailed as expected ok 38 TCP-AO established: add TCP-MD5 key: counter TCPAOGood increased 7 => 9 ok 39 TCP-MD5 established: add TCP-AO key: postfailed as expected ok 40 non-signed established: add TCP-AO key: postfailed as expected ok 41 non-signed established: add TCP-AO key: counter CurrEstab increased 0 => 1 ok 42 TCP-AO key intersects with TCP-MD5 key: prefailed as expected Signed-off-by: Dmitry Safonov --- tools/testing/selftests/net/tcp_ao/Makefile | 2 +- .../selftests/net/tcp_ao/unsigned-md5.c | 524 ++++++++++++++++++ 2 files changed, 525 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/net/tcp_ao/unsigned-md5.c diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile index a001dc2aed4e..da44966f3687 100644 --- a/tools/testing/selftests/net/tcp_ao/Makefile +++ b/tools/testing/selftests/net/tcp_ao/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 TEST_BOTH_AF := connect icmps-discard icmps-accept connect-deny \ - setsockopt-closed + setsockopt-closed unsigned-md5 TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4) TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6) diff --git a/tools/testing/selftests/net/tcp_ao/unsigned-md5.c b/tools/testing/selftests/net/tcp_ao/unsigned-md5.c new file mode 100644 index 000000000000..1fd24c010588 --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/unsigned-md5.c @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Author: Dmitry Safonov */ +#include +#include "aolib.h" +#include "../../../../include/linux/bits.h" + +typedef uint8_t fault_t; +#define F_TIMEOUT 1 +#define F_KEYREJECT 2 +#define F_PREINSTALL 3 +#define F_POSTINSTALL 4 + +#define fault(type) (inj == type) + +static const char *md5_password = "Some evil genius, enemy to mankind, must have been the first contriver."; +static const char *ao_password = "In this hour, I do not believe that any darkness will endure."; + +static union tcp_addr client2; +static union tcp_addr client3; + +static int test_set_md5(int sk, const union tcp_addr in_addr, uint8_t prefix) +{ + size_t pwd_len = strlen(md5_password); + struct tcp_md5sig md5sig = {}; +#ifdef IPV6_TEST + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = in_addr.a6, + }; +#else + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = in_addr.a4, + }; +#endif + + if (prefix > DEFAULT_TEST_PREFIX) + prefix = DEFAULT_TEST_PREFIX; + + md5sig.tcpm_keylen = pwd_len; + memcpy(md5sig.tcpm_key, md5_password, pwd_len); + md5sig.tcpm_flags = TCP_MD5SIG_FLAG_PREFIX; + md5sig.tcpm_prefixlen = prefix; + memcpy(&md5sig.tcpm_addr, &addr, sizeof(addr)); + + return setsockopt(sk, IPPROTO_TCP, TCP_MD5SIG_EXT, + &md5sig, sizeof(md5sig)); +} + +static bool tcp_md5_enabled = true; +static void check_tcp_md5_support(void) +{ + int sk; + union tcp_addr addr_any = {}; + + sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); + if (sk < 0) + test_error("socket()"); + + if (test_set_md5(sk, addr_any, 0)) { + if (errno == ENOPROTOOPT) + tcp_md5_enabled = false; + else + test_error("setsockopt(TCP_MD5SIG_EXT)"); + } + close(sk); +} + +static void try_accept(const char *tst_name, unsigned int port, + union tcp_addr *md5_addr, uint8_t md5_prefix, + union tcp_addr *ao_addr, uint8_t ao_prefix, + uint8_t sndid, uint8_t rcvid, const char *cnt_name, + bool needs_md5, fault_t inj) +{ + uint64_t before_cnt, after_cnt; + int lsk, err, sk = 0; + time_t timeout; + + if (needs_md5 && !tcp_md5_enabled) { + test_skip("%s: setsockopt(TCP_MD5SIG_EXT) is not supported", tst_name); + return; + } + + lsk = test_listen_socket(this_ip_addr, port, 1); + + if (md5_addr && test_set_md5(lsk, *md5_addr, md5_prefix)) + test_error("setsockopt(TCP_MD5SIG_EXT)"); + + if (ao_addr && test_set_ao(lsk, ao_password, 0, *ao_addr, + ao_prefix, sndid, rcvid)) + test_error("setsockopt(TCP_AO)"); + + if (cnt_name) + before_cnt = netstat_get_one(cnt_name, NULL); + + synchronize_threads(); /* preparations done */ + + timeout = fault(F_TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC; + err = test_wait_fd(lsk, timeout, 0); + if (err < 0) + test_error("test_wait_fd()"); + else if (!err) { + if (!fault(F_TIMEOUT)) + test_fail("timeouted for accept()"); + } else { + if (fault(F_TIMEOUT)) + test_fail("ready to accept"); + + sk = accept(lsk, NULL, NULL); + if (sk < 0) { + test_error("accept()"); + } else { + if (fault(F_TIMEOUT)) + test_fail("%s: accepted", tst_name); + } + } + + close(lsk); + + if (!cnt_name) + goto out; + + after_cnt = netstat_get_one(cnt_name, NULL); + + if (after_cnt <= before_cnt) { + test_fail("%s: %s counter did not increase: %zu <= %zu", + tst_name, cnt_name, after_cnt, before_cnt); + } else { + test_ok("%s: counter %s increased %zu => %zu", + tst_name, cnt_name, before_cnt, after_cnt); + } + +out: + synchronize_threads(); /* close() */ + if (sk > 0) + close(sk); +} + +static void server_add_routes(void) +{ + int family = TEST_FAMILY; + + check_tcp_md5_support(); + synchronize_threads(); /* client_add_ips() */ + + if (ip_route_add(veth_name, family, this_ip_addr, client2)) + test_error("Failed to add route"); + if (ip_route_add(veth_name, family, this_ip_addr, client3)) + test_error("Failed to add route"); +} + +static void server_add_fail_tests(unsigned int *port) +{ + union tcp_addr addr_any = {}; + + try_accept("TCP-AO established: add TCP-MD5 key", (*port)++, NULL, 0, + &addr_any, 0, 100, 100, "TCPAOGood", true, 0); + try_accept("TCP-MD5 established: add TCP-AO key", (*port)++, &addr_any, 0, + NULL, 0, 0, 0, NULL, true, 0); + try_accept("non-signed established: add TCP-AO key", (*port)++, NULL, 0, + NULL, 0, 0, 0, "CurrEstab", false, 0); +} + +static void *server_fn(void *arg) +{ + unsigned int port = test_server_port; + union tcp_addr addr_any = {}; + + server_add_routes(); + + try_accept("AO server (INADDR_ANY): AO client", port++, NULL, 0, + &addr_any, 0, 100, 100, "TCPAOGood", false, 0); + try_accept("AO server (INADDR_ANY): MD5 client", port++, NULL, 0, + &addr_any, 0, 100, 100, "TCPMD5Unexpected", true, F_TIMEOUT); + try_accept("AO server (INADDR_ANY): no sign client", port++, NULL, 0, + &addr_any, 0, 100, 100, "TCPAORequired", false, F_TIMEOUT); + + try_accept("MD5 server (INADDR_ANY): AO client", port++, &addr_any, 0, + NULL, 0, 0, 0, "TCPAOKeyNotFound", true, F_TIMEOUT); + try_accept("MD5 server (INADDR_ANY): MD5 client", port++, &addr_any, 0, + NULL, 0, 0, 0, NULL, true, 0); + try_accept("MD5 server (INADDR_ANY): no sign client", port++, &addr_any, 0, + NULL, 0, 0, 0, "TCPMD5NotFound", true, F_TIMEOUT); + + try_accept("no sign server: AO client", port++, NULL, 0, + NULL, 0, 0, 0, "TCPAOKeyNotFound", false, F_TIMEOUT); + try_accept("no sign server: MD5 client", port++, NULL, 0, + NULL, 0, 0, 0, "TCPMD5Unexpected", true, F_TIMEOUT); + try_accept("no sign server: no sign client", port++, NULL, 0, + NULL, 0, 0, 0, "CurrEstab", false, 0); + + try_accept("AO+MD5 server: AO client (matching)", port++, + &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX, + 100, 100, "TCPAOGood", true, 0); + try_accept("AO+MD5 server: AO client (misconfig, matching MD5)", port++, + &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX, + 100, 100, "TCPAOKeyNotFound", true, F_TIMEOUT); + try_accept("AO+MD5 server: AO client (misconfig, non-matching)", port++, + &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX, + 100, 100, "TCPAOKeyNotFound", true, F_TIMEOUT); + try_accept("AO+MD5 server: MD5 client (matching)", port++, + &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX, + 100, 100, NULL, true, 0); + try_accept("AO+MD5 server: MD5 client (misconfig, matching AO)", port++, + &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX, + 100, 100, "TCPMD5Unexpected", true, F_TIMEOUT); + try_accept("AO+MD5 server: MD5 client (misconfig, non-matching)", port++, + &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX, + 100, 100, "TCPMD5Unexpected", true, F_TIMEOUT); + try_accept("AO+MD5 server: no sign client (unmatched)", port++, + &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX, + 100, 100, "CurrEstab", true, 0); + try_accept("AO+MD5 server: no sign client (misconfig, matching AO)", + port++, &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX, + 100, 100, "TCPAORequired", true, F_TIMEOUT); + try_accept("AO+MD5 server: no sign client (misconfig, matching MD5)", + port++, &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX, + 100, 100, "TCPMD5NotFound", true, F_TIMEOUT); + + try_accept("AO+MD5 server: client with both [TCP-MD5] and TCP-AO keys", + port++, &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX, + 100, 100, NULL, true, F_TIMEOUT); + try_accept("AO+MD5 server: client with both TCP-MD5 and [TCP-AO] keys", + port++, &this_ip_dest, TEST_PREFIX, &client2, TEST_PREFIX, + 100, 100, NULL, true, F_TIMEOUT); + + server_add_fail_tests(&port); + + /* client exits */ + synchronize_threads(); + return NULL; +} + +static int client_bind(int sk, union tcp_addr bind_addr) +{ +#ifdef IPV6_TEST + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = bind_addr.a6, + }; +#else + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = bind_addr.a4, + }; +#endif + return bind(sk, &addr, sizeof(addr)); +} + +static void try_connect(const char *tst_name, unsigned int port, + union tcp_addr *md5_addr, uint8_t md5_prefix, + union tcp_addr *ao_addr, uint8_t ao_prefix, + uint8_t sndid, uint8_t rcvid, fault_t inj, + bool needs_md5, union tcp_addr *bind_addr) +{ + time_t timeout; + int sk, ret; + + if (needs_md5 && !tcp_md5_enabled) { + test_skip("%s: setsockopt(TCP_MD5SIG_EXT) is not supported", tst_name); + return; + } + + sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); + if (sk < 0) + test_error("socket()"); + + if (bind_addr && client_bind(sk, *bind_addr)) + test_error("bind()"); + + if (md5_addr && test_set_md5(sk, *md5_addr, md5_prefix)) + test_error("setsockopt(TCP_MD5SIG_EXT)"); + + if (ao_addr && test_set_ao(sk, ao_password, 0, *ao_addr, + ao_prefix, sndid, rcvid)) + test_error("setsockopt(TCP_AO)"); + + synchronize_threads(); /* preparations done */ + + timeout = fault(F_TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC; + ret = _test_connect_socket(sk, this_ip_dest, port, timeout); + + if (ret < 0) { + if (fault(F_KEYREJECT) && ret == -EKEYREJECTED) { + test_ok("%s: connect() was prevented", tst_name); + goto out; + } else if (ret == -ECONNREFUSED && + (fault(F_TIMEOUT) || fault(F_KEYREJECT))) { + test_ok("%s: refused to connect", tst_name); + goto out; + } else { + test_error("%s: connect() returned %d", tst_name, ret); + } + } + + if (ret == 0) { + if (fault(F_TIMEOUT)) + test_ok("%s", tst_name); + else + test_fail("%s: failed to connect()", tst_name); + } else { + if (fault(F_TIMEOUT) || fault(F_KEYREJECT)) + test_fail("%s: connected", tst_name); + else + test_ok("%s: connected", tst_name); + } + +out: + synchronize_threads(); /* close() */ + /* _test_connect_socket() cleans up on failure */ + if (ret > 0) + close(sk); +} + +#define PREINSTALL_MD5 BIT(1) +#define POSTINSTALL_MD5 BIT(2) +#define PREINSTALL_AO BIT(3) +#define POSTINSTALL_AO BIT(4) + +static void try_to_add(const char *tst_name, unsigned int port, + unsigned int strategy, + union tcp_addr md5_addr, uint8_t md5_prefix, + union tcp_addr ao_addr, uint8_t ao_prefix, + uint8_t sndid, uint8_t rcvid, + bool needs_md5, fault_t inj) +{ + time_t timeout; + int sk, ret; + + if (needs_md5 && !tcp_md5_enabled) { + test_skip("%s: setsockopt(TCP_MD5SIG_EXT) is not supported", tst_name); + return; + } + + sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); + if (sk < 0) + test_error("socket()"); + + if (client_bind(sk, this_ip_addr)) + test_error("bind()"); + + if (strategy & PREINSTALL_MD5) { + if (test_set_md5(sk, md5_addr, md5_prefix)) + test_error("setsockopt(TCP_MD5SIG_EXT)"); + } + + if (strategy & PREINSTALL_AO) { + if (test_set_ao(sk, ao_password, 0, ao_addr, + ao_prefix, sndid, rcvid)) { + if (fault(F_PREINSTALL)) { + test_ok("%s: prefailed as expected", tst_name); + goto out_no_sync; + } else { + test_error("setsockopt(TCP_AO)"); + } + } else if (fault(F_PREINSTALL)) { + test_fail("%s: setsockopt()s were expected to fail", tst_name); + goto out_no_sync; + } + } + + synchronize_threads(); /* preparations done */ + + timeout = fault(F_TIMEOUT) ? TEST_RETRANSMIT_SEC : TEST_TIMEOUT_SEC; + ret = _test_connect_socket(sk, this_ip_dest, port, timeout); + + if (ret <= 0) { + test_error("%s: connect() returned %d", tst_name, ret); + goto out; + } + + if (strategy & POSTINSTALL_MD5) { + if (test_set_md5(sk, md5_addr, md5_prefix)) { + if (fault(F_POSTINSTALL)) { + test_ok("%s: postfailed as expected", tst_name); + goto out; + } else { + test_error("setsockopt(TCP_MD5SIG_EXT)"); + } + } else if (fault(F_POSTINSTALL)) { + test_fail("%s: post setsockopt() was expected to fail", tst_name); + goto out; + } + } + + if (strategy & POSTINSTALL_AO) { + if (test_set_ao(sk, ao_password, 0, ao_addr, + ao_prefix, sndid, rcvid)) { + if (fault(F_POSTINSTALL)) { + test_ok("%s: postfailed as expected", tst_name); + goto out; + } else { + test_error("setsockopt(TCP_AO)"); + } + } else if (fault(F_POSTINSTALL)) { + test_fail("%s: post setsockopt() was expected to fail", tst_name); + goto out; + } + } + +out: + synchronize_threads(); /* close() */ +out_no_sync: + /* _test_connect_socket() cleans up on failure */ + if (ret > 0) + close(sk); +} + +static void client_add_ip(union tcp_addr *client, const char *ip) +{ + int family = TEST_FAMILY; + + if (inet_pton(family, ip, client) != 1) + test_error("Can't convert ip address %s", ip); + + if (ip_addr_add(veth_name, family, *client, TEST_PREFIX)) + test_error("Failed to add ip address"); + if (ip_route_add(veth_name, family, *client, this_ip_dest)) + test_error("Failed to add route"); +} + +static void client_add_ips(void) +{ + client_add_ip(&client2, __TEST_CLIENT_IP(2)); + client_add_ip(&client3, __TEST_CLIENT_IP(3)); + synchronize_threads(); /* server_add_routes() */ +} + +static void client_add_fail_tests(unsigned int *port) +{ + try_to_add("TCP-AO established: add TCP-MD5 key", + (*port)++, POSTINSTALL_MD5 | PREINSTALL_AO, + this_ip_dest, TEST_PREFIX, this_ip_dest, TEST_PREFIX, + 100, 100, true, F_POSTINSTALL); + try_to_add("TCP-MD5 established: add TCP-AO key", + (*port)++, PREINSTALL_MD5 | POSTINSTALL_AO, + this_ip_dest, TEST_PREFIX, this_ip_dest, TEST_PREFIX, + 100, 100, true, F_POSTINSTALL); + try_to_add("non-signed established: add TCP-AO key", + (*port)++, POSTINSTALL_AO, + this_ip_dest, TEST_PREFIX, this_ip_dest, TEST_PREFIX, + 100, 100, false, F_POSTINSTALL); + + try_to_add("TCP-AO key intersects with TCP-MD5 key", + (*port), PREINSTALL_MD5 | PREINSTALL_AO, + this_ip_addr, TEST_PREFIX, this_ip_addr, TEST_PREFIX, + 100, 100, true, F_PREINSTALL); +} + +static void *client_fn(void *arg) +{ + unsigned int port = test_server_port; + union tcp_addr addr_any = {}; + + client_add_ips(); + + try_connect("AO server (INADDR_ANY): AO client", port++, NULL, 0, + &addr_any, 0, 100, 100, 0, false, &this_ip_addr); + try_connect("AO server (INADDR_ANY): MD5 client", port++, &addr_any, 0, + NULL, 0, 100, 100, F_TIMEOUT, true, &this_ip_addr); + try_connect("AO server (INADDR_ANY): unsigned client", port++, NULL, 0, + NULL, 0, 100, 100, F_TIMEOUT, false, &this_ip_addr); + + try_connect("MD5 server (INADDR_ANY): AO client", port++, NULL, 0, + &addr_any, 0, 100, 100, F_TIMEOUT, true, &this_ip_addr); + try_connect("MD5 server (INADDR_ANY): MD5 client", port++, &addr_any, 0, + NULL, 0, 100, 100, 0, true, &this_ip_addr); + try_connect("MD5 server (INADDR_ANY): no sign client", port++, NULL, 0, + NULL, 0, 100, 100, F_TIMEOUT, true, &this_ip_addr); + + try_connect("no sign server: AO client", port++, NULL, 0, + &addr_any, 0, 100, 100, F_TIMEOUT, false, &this_ip_addr); + try_connect("no sign server: MD5 client", port++, &addr_any, 0, + NULL, 0, 100, 100, F_TIMEOUT, true, &this_ip_addr); + try_connect("no sign server: no sign client", port++, NULL, 0, + NULL, 0, 100, 100, 0, false, &this_ip_addr); + + try_connect("AO+MD5 server: AO client (matching)", port++, NULL, 0, + &addr_any, 0, 100, 100, 0, true, &client2); + try_connect("AO+MD5 server: AO client (misconfig, matching MD5)", + port++, NULL, 0, &addr_any, 0, 100, 100, + F_TIMEOUT, true, &this_ip_addr); + try_connect("AO+MD5 server: AO client (misconfig, non-matching)", + port++, NULL, 0, &addr_any, 0, 100, 100, + F_TIMEOUT, true, &client3); + try_connect("AO+MD5 server: MD5 client (matching)", port++, &addr_any, 0, + NULL, 0, 100, 100, 0, true, &this_ip_addr); + try_connect("AO+MD5 server: MD5 client (misconfig, matching AO)", + port++, &addr_any, 0, NULL, 0, 100, 100, F_TIMEOUT, + true, &client2); + try_connect("AO+MD5 server: MD5 client (misconfig, non-matching)", + port++, &addr_any, 0, NULL, 0, 100, 100, F_TIMEOUT, + true, &client3); + try_connect("AO+MD5 server: no sign client (unmatched)", + port++, NULL, 0, NULL, 0, 100, 100, 0, true, &client3); + try_connect("AO+MD5 server: no sign client (misconfig, matching AO)", + port++, NULL, 0, NULL, 0, 100, 100, F_TIMEOUT, + true, &client2); + try_connect("AO+MD5 server: no sign client (misconfig, matching MD5)", + port++, NULL, 0, NULL, 0, 100, 100, F_TIMEOUT, + true, &this_ip_addr); + + try_connect("AO+MD5 server: client with both [TCP-MD5] and TCP-AO keys", + port++, &this_ip_addr, TEST_PREFIX, + &client2, TEST_PREFIX, 100, 100, F_KEYREJECT, + true, &this_ip_addr); + try_connect("AO+MD5 server: client with both TCP-MD5 and [TCP-AO] keys", + port++, &this_ip_addr, TEST_PREFIX, + &client2, TEST_PREFIX, 100, 100, F_KEYREJECT, true, &client2); + + client_add_fail_tests(&port); + + return NULL; +} + +int main(int argc, char *argv[]) +{ + test_init(42, server_fn, client_fn); + return 0; +} From patchwork Thu Oct 27 20:43:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022786 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 231A0FA3740 for ; Thu, 27 Oct 2022 20:53:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237248AbiJ0Uxo (ORCPT ); Thu, 27 Oct 2022 16:53:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:57664 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237035AbiJ0UwK (ORCPT ); Thu, 27 Oct 2022 16:52:10 -0400 Received: from mail-wm1-x32f.google.com (mail-wm1-x32f.google.com [IPv6:2a00:1450:4864:20::32f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AF81F92597 for ; Thu, 27 Oct 2022 13:45:25 -0700 (PDT) Received: by mail-wm1-x32f.google.com with SMTP id r186-20020a1c44c3000000b003cf4d389c41so2259672wma.3 for ; Thu, 27 Oct 2022 13:45:25 -0700 (PDT) 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=gcnCCWJ3sL3TtkhLDR3iQ6YWDc5hGDSAP+7E0rj/5N8=; b=Q51oFphuhArXiss7MWYKI3XIAKHdzgaQOZ840/iKcKQTKTQLp69qm8IeLrEfgBqn+W da26eefmJTUL0LD7tDKR+Zy7JyjFOTQbwRNpXlvPVkQmF9D4TIkwNL1LQtT0wg5Mm4i8 oLIGOavmc3oGNJ87t5bz2FwIaK5eiGZ47lG2pY8Hffo0knjfNfDG8jl3PZRB5B9+bxew iWFdk6/GbviJwIRg3nTXlSdBzIskwwPty79iS2NeQkMEj6SHfsX7l+UhmMGa1qHlaSJ9 w6FHaoXvggqmZIIjTZm7e2su5QgkC89+vG7MCKSv8APtuhiZbNzyKSb2PMNW8JY6CIFx Vvpw== 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=gcnCCWJ3sL3TtkhLDR3iQ6YWDc5hGDSAP+7E0rj/5N8=; b=rFsTpvf03a5+Xi5cey0PuN1ymJ1nc7M5tiQQgVP8GMTAHujc4250dpz4bB5D+V7Ap6 EwCbDXPnjqfaAWRZ1bCiECHGBlNHpZovpjFH6RcJD/qlkBYNa+GS/CUcOxWbVurmi/Jx Cv0heiFKhShjvc9gnagCNF+1iblYfxoiuYkRTpElKbXMYAEZKkHxRPHUgR6HrpA+5UVP jQPMzYUU7Bapi3yFjMWb8zQpSpBH7mDpmQt33T1mcKjIYzhWdXicSHgNvUNh7vJ9feMC F4sjRqFKf2zpyGvBTQ5Lt9EvF5O3wg6einCO+n+qG79RNfdgOk/BrLVQYm/srU3ful5G uVUA== X-Gm-Message-State: ACrzQf1JYHwdt2Urd3woG3Nv1NLyAmTTpeT3M5apsmPkOVCtwalLU3zC 0TjgVrK1J2w44Ge/h9qaZ0GfYA== X-Google-Smtp-Source: AMsMyM5CKs6mrkR2wwd00sYgLN2MOkIZ9Ty0D+Ida0bIjuVVO8GnQCnrVyplRFrObh5jT45RMhFNSQ== X-Received: by 2002:a05:600c:418b:b0:3cf:4733:e0b6 with SMTP id p11-20020a05600c418b00b003cf4733e0b6mr7377502wmh.186.1666903483761; Thu, 27 Oct 2022 13:44:43 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:43 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 32/36] selftests/aolib: Add test/benchmark for removing MKTs Date: Thu, 27 Oct 2022 21:43:43 +0100 Message-Id: <20221027204347.529913-33-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 Sample output: > 1..36 > # 1106[lib/setup.c:207] rand seed 1660754406 > TAP version 13 > ok 1 Worst case connect 512 keys: min=0ms max=1ms mean=0.583329ms stddev=0.076376 > ok 2 Connect random-search 512 keys: min=0ms max=1ms mean=0.53412ms stddev=0.0516779 > ok 3 Worst case delete 512 keys: min=2ms max=11ms mean=6.04139ms stddev=0.245792 > ok 4 Add a new key 512 keys: min=0ms max=13ms mean=0.673415ms stddev=0.0820618 > ok 5 Remove random-search 512 keys: min=5ms max=9ms mean=6.65969ms stddev=0.258064 > ok 6 Remove async 512 keys: min=0ms max=0ms mean=0.041825ms stddev=0.0204512 > ok 7 Worst case connect 1024 keys: min=0ms max=2ms mean=0.520357ms stddev=0.0721358 > ok 8 Connect random-search 1024 keys: min=0ms max=2ms mean=0.535312ms stddev=0.0517355 > ok 9 Worst case delete 1024 keys: min=5ms max=9ms mean=8.27219ms stddev=0.287614 > ok 10 Add a new key 1024 keys: min=0ms max=1ms mean=0.688121ms stddev=0.0829531 > ok 11 Remove random-search 1024 keys: min=5ms max=9ms mean=8.37649ms stddev=0.289422 > ok 12 Remove async 1024 keys: min=0ms max=0ms mean=0.0457096ms stddev=0.0213798 > ok 13 Worst case connect 2048 keys: min=0ms max=2ms mean=0.748804ms stddev=0.0865335 > ok 14 Connect random-search 2048 keys: min=0ms max=2ms mean=0.782993ms stddev=0.0625697 > ok 15 Worst case delete 2048 keys: min=5ms max=10ms mean=8.23106ms stddev=0.286898 > ok 16 Add a new key 2048 keys: min=0ms max=1ms mean=0.812988ms stddev=0.0901658 > ok 17 Remove random-search 2048 keys: min=8ms max=9ms mean=8.84949ms stddev=0.297481 > ok 18 Remove async 2048 keys: min=0ms max=0ms mean=0.0297223ms stddev=0.0172402 > ok 19 Worst case connect 4096 keys: min=1ms max=5ms mean=1.53352ms stddev=0.123836 > ok 20 Connect random-search 4096 keys: min=1ms max=5ms mean=1.52226ms stddev=0.0872429 > ok 21 Worst case delete 4096 keys: min=5ms max=9ms mean=8.25874ms stddev=0.28738 > ok 22 Add a new key 4096 keys: min=0ms max=3ms mean=1.67382ms stddev=0.129376 > ok 23 Remove random-search 4096 keys: min=5ms max=10ms mean=8.26178ms stddev=0.287433 > ok 24 Remove async 4096 keys: min=0ms max=0ms mean=0.0340009ms stddev=0.0184393 > ok 25 Worst case connect 8192 keys: min=2ms max=4ms mean=2.86208ms stddev=0.169177 > ok 26 Connect random-search 8192 keys: min=2ms max=4ms mean=2.87592ms stddev=0.119915 > ok 27 Worst case delete 8192 keys: min=6ms max=11ms mean=7.55291ms stddev=0.274826 > ok 28 Add a new key 8192 keys: min=1ms max=5ms mean=2.56797ms stddev=0.160249 > ok 29 Remove random-search 8192 keys: min=5ms max=10ms mean=7.14002ms stddev=0.267208 > ok 30 Remove async 8192 keys: min=0ms max=0ms mean=0.0320066ms stddev=0.0178904 > ok 31 Worst case connect 16384 keys: min=5ms max=6ms mean=5.55334ms stddev=0.235655 > ok 32 Connect random-search 16384 keys: min=5ms max=6ms mean=5.52614ms stddev=0.166225 > ok 33 Worst case delete 16384 keys: min=5ms max=11ms mean=7.39109ms stddev=0.271866 > ok 34 Add a new key 16384 keys: min=2ms max=4ms mean=3.35799ms stddev=0.183248 > ok 35 Remove random-search 16384 keys: min=5ms max=8ms mean=6.86078ms stddev=0.261931 > ok 36 Remove async 16384 keys: min=0ms max=0ms mean=0.0302384ms stddev=0.0173892 > # Totals: pass:36 fail:0 xfail:0 xpass:0 skip:0 error:0 >From it it's visible that the current simplified approach with linked-list of MKTs scales quite fine even for thousands of keys. And that also means that the majority of the time for delete is eaten by synchronize_rcu() [which I can confirm separately by tracing]. Signed-off-by: Dmitry Safonov --- tools/testing/selftests/net/tcp_ao/Makefile | 4 +- .../selftests/net/tcp_ao/bench-lookups.c | 403 ++++++++++++++++++ 2 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/net/tcp_ao/bench-lookups.c diff --git a/tools/testing/selftests/net/tcp_ao/Makefile b/tools/testing/selftests/net/tcp_ao/Makefile index da44966f3687..a4af7d4da169 100644 --- a/tools/testing/selftests/net/tcp_ao/Makefile +++ b/tools/testing/selftests/net/tcp_ao/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 TEST_BOTH_AF := connect icmps-discard icmps-accept connect-deny \ - setsockopt-closed unsigned-md5 + setsockopt-closed unsigned-md5 bench-lookups TEST_IPV4_PROGS := $(TEST_BOTH_AF:%=%_ipv4) TEST_IPV6_PROGS := $(TEST_BOTH_AF:%=%_ipv6) @@ -46,3 +46,5 @@ $(OUTPUT)/%_ipv6: %.c $(OUTPUT)/icmps-accept_ipv4: CFLAGS+= -DTEST_ICMPS_ACCEPT $(OUTPUT)/icmps-accept_ipv6: CFLAGS+= -DTEST_ICMPS_ACCEPT +$(OUTPUT)/bench-lookups_ipv4: LDFLAGS+= -lm +$(OUTPUT)/bench-lookups_ipv6: LDFLAGS+= -lm diff --git a/tools/testing/selftests/net/tcp_ao/bench-lookups.c b/tools/testing/selftests/net/tcp_ao/bench-lookups.c new file mode 100644 index 000000000000..41456d85e06a --- /dev/null +++ b/tools/testing/selftests/net/tcp_ao/bench-lookups.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Author: Dmitry Safonov */ +#include +#include +#include +#include +#include +#include + +#include "../../../../include/linux/bits.h" +#include "../../../../include/linux/kernel.h" +#include "aolib.h" + +#define AO_KEY_SZ 300 /* ~= sizeof(struct tcp_ao_key) */ +#define NR_ITERS 100 /* number of times to run gathering statistics */ + +#ifdef IPV6_TEST +typedef struct in6_addr ipaddr_t; +static ipaddr_t get_ipaddr_t(ipaddr_t net, size_t n) +{ + ipaddr_t ret = net; + + ret.s6_addr32[3] = htonl(n & (BIT(32) - 1)); + ret.s6_addr32[2] = htonl((n >> 32) & (BIT(32) - 1)); + + return ret; +} +#else +typedef struct in_addr ipaddr_t; +static ipaddr_t get_ipaddr_t(ipaddr_t net, size_t n) +{ + ipaddr_t ret; + + ret.s_addr = htonl(ntohl(net.s_addr) + n); + return ret; +} +#endif + +static void gen_test_ips(ipaddr_t *ips, size_t ips_nr, bool use_rand) +{ + ipaddr_t net; + size_t i, j; + + if (inet_pton(TEST_FAMILY, TEST_NETWORK, &net) != 1) + test_error("Can't convert ip address %s", TEST_NETWORK); + + if (!use_rand) { + for (i = 0; i < ips_nr; i++) + ips[i] = get_ipaddr_t(net, 2 * i + 1); + return; + } + for (i = 0; i < ips_nr; i++) { + size_t r = (size_t)random() | 0x1; + + ips[i] = get_ipaddr_t(net, r); + + for (j = i - 1; j > 0 && i > 0; j--) { + if (!memcmp(&ips[i], &ips[j], sizeof(ipaddr_t))) { + i--; /* collision */ + break; + } + } + } +} + +static void test_add_routes(ipaddr_t *ips, size_t ips_nr) +{ + size_t i; + + for (i = 0; i < ips_nr; i++) { + union tcp_addr *p = (union tcp_addr *)&ips[i]; + + if (ip_route_add(veth_name, TEST_FAMILY, this_ip_addr, *p)) + test_error("Failed to add route"); + } +} + +static void server_apply_keys(int lsk, ipaddr_t *ips, size_t ips_nr) +{ + size_t i; + + for (i = 0; i < ips_nr; i++) { + union tcp_addr *p = (union tcp_addr *)&ips[i]; + + if (test_set_ao(lsk, "password", 0, *p, -1, 100, 100)) + test_error("setsockopt(TCP_AO)"); + } +} + +static const size_t nr_keys[] = { 512, 1024, 2048, 4096, 8192, 16384 }; +static ipaddr_t *test_ips; + +struct bench_stats { + uint64_t min; + uint64_t max; + uint64_t nr; + double mean; + double s2; +}; + +static struct bench_tests { + struct bench_stats delete_last_key; + struct bench_stats add_key; + struct bench_stats delete_rand_key; + struct bench_stats connect_last_key; + struct bench_stats connect_rand_key; + struct bench_stats delete_async; +} bench_results[ARRAY_SIZE(nr_keys)]; + +#define NSEC_PER_SEC 1000000000ULL + +static void measure_call(struct bench_stats *st, + void (*f)(int, void *), int sk, void *arg) +{ + struct timespec start = {}, end = {}; + double delta; + uint64_t nsec; + + if (clock_gettime(CLOCK_MONOTONIC, &start)) + test_error("clock_gettime()"); + + f(sk, arg); + + if (clock_gettime(CLOCK_MONOTONIC, &end)) + test_error("clock_gettime()"); + + nsec = (end.tv_sec - start.tv_sec) * NSEC_PER_SEC; + if (end.tv_nsec >= start.tv_nsec) + nsec += end.tv_nsec - start.tv_nsec; + else + nsec -= start.tv_nsec - end.tv_nsec; + + if (st->nr == 0) { + st->min = st->max = nsec; + } else { + if (st->min > nsec) + st->min = nsec; + if (st->max < nsec) + st->max = nsec; + } + + /* Welford-Knuth algorithm */ + st->nr++; + delta = (double)nsec - st->mean; + st->mean += delta / st->nr; + st->s2 += delta * ((double)nsec - st->mean); +} + +static void delete_mkt(int sk, void *arg) +{ + struct tcp_ao_del *ao = arg; + + if (setsockopt(sk, IPPROTO_TCP, TCP_AO_DEL, ao, sizeof(*ao))) + test_error("setsockopt(TCP_AO_DEL)"); +} + +static void add_back_mkt(int sk, void *arg) +{ + union tcp_addr *p = arg; + + if (test_set_ao(sk, "password", 0, *p, -1, 100, 100)) + test_error("setsockopt(TCP_AO)"); +} + +static void memcpy_sockaddr(void *dest, union tcp_addr *in_addr) +{ +#ifdef IPV6_TEST + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = in_addr->a6, + }; +#else + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = in_addr->a4, + }; +#endif + + memcpy(dest, &addr, sizeof(addr)); +} + +static void bench_delete(int lsk, struct bench_stats *add, + struct bench_stats *del, + ipaddr_t *ips, size_t ips_nr, + bool rand_order, bool async) +{ + struct tcp_ao_del ao_del = {}; + union tcp_addr *p; + size_t i; + + ao_del.tcpa_sndid = 100; + ao_del.tcpa_rcvid = 100; + if (async) + ao_del.tcpa_flags = TCP_AO_CMDF_DEL_ASYNC; + ao_del.tcpa_prefix = DEFAULT_TEST_PREFIX; + + /* Remove the first added */ + p = (union tcp_addr *)&ips[0]; + memcpy_sockaddr(&ao_del.tcpa_addr, p); + + for (i = 0; i < NR_ITERS; i++) { + measure_call(del, delete_mkt, lsk, (void *)&ao_del); + + /* Restore it back */ + measure_call(add, add_back_mkt, lsk, (void *)p); + + /* + * Slowest for FILO-linked-list: + * on (i) iteration removing ips[i] element. When it gets + * added to the list back - it becomes first to fetch, so + * on (i + 1) iteration go to ips[i + 1] element. + */ + if (rand_order) + p = (union tcp_addr *)&ips[rand() % ips_nr]; + else + p = (union tcp_addr *)&ips[i % ips_nr]; + memcpy_sockaddr(&ao_del.tcpa_addr, p); + } +} + +static void bench_connect_srv(int lsk, ipaddr_t *ips, size_t ips_nr) +{ + size_t i; + + for (i = 0; i < NR_ITERS; i++) { + int err, sk; + + synchronize_threads(); + + err = test_wait_fd(lsk, TEST_TIMEOUT_SEC, 0); + if (!err) + test_error("timeouted for accept()"); + else if (err < 0) + test_error("test_wait_fd()"); + + sk = accept(lsk, NULL, NULL); + if (sk < 0) + test_error("accept()"); + + close(sk); + } +} + +static void test_print_stats(const char *desc, size_t nr, struct bench_stats *bs) +{ + test_ok("%20s\t%zu keys: min=%" PRIu64 "ms max=%" PRIu64 "ms mean=%gms stddev=%g", + desc, nr, bs->min / 1000000, bs->max / 1000000, + bs->mean / 1000000, sqrt((bs->mean / 1000000) / bs->nr)); +} + +static void *server_fn(void *arg) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(nr_keys); i++) { + struct bench_tests *bt = &bench_results[i]; + int lsk; + + test_ips = malloc(nr_keys[i] * sizeof(ipaddr_t)); + if (!test_ips) + test_error("malloc()"); + + lsk = test_listen_socket(this_ip_addr, test_server_port + i, 1); + + gen_test_ips(test_ips, nr_keys[i], false); + test_add_routes(test_ips, nr_keys[i]); + test_set_optmem(AO_KEY_SZ * nr_keys[i]); + server_apply_keys(lsk, test_ips, nr_keys[i]); + + synchronize_threads(); + bench_connect_srv(lsk, test_ips, nr_keys[i]); + bench_connect_srv(lsk, test_ips, nr_keys[i]); + + /* The worst case for FILO-list */ + bench_delete(lsk, &bt->add_key, &bt->delete_last_key, + test_ips, nr_keys[i], false, false); + test_print_stats("Worst case delete", + nr_keys[i], &bt->delete_last_key); + test_print_stats("Add a new key", + nr_keys[i], &bt->add_key); + + bench_delete(lsk, &bt->add_key, &bt->delete_rand_key, + test_ips, nr_keys[i], true, false); + test_print_stats("Remove random-search", + nr_keys[i], &bt->delete_rand_key); + + bench_delete(lsk, &bt->add_key, &bt->delete_async, + test_ips, nr_keys[i], false, true); + test_print_stats("Remove async", nr_keys[i], &bt->delete_async); + + free(test_ips); + close(lsk); + } + + return NULL; +} + +static void connect_client(int sk, void *arg) +{ + size_t *p = arg; + + if (test_connect_socket(sk, this_ip_dest, test_server_port + *p) <= 0) + test_error("failed to connect()"); +} + +static void client_addr_setup(int sk, union tcp_addr taddr) +{ +#ifdef IPV6_TEST + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6, + .sin6_port = 0, + .sin6_addr = taddr.a6, + }; +#else + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = taddr.a4, + }; +#endif + int ret; + + ret = ip_addr_add(veth_name, TEST_FAMILY, taddr, TEST_PREFIX); + if (ret && ret != -EEXIST) + test_error("Failed to add ip address"); + ret = ip_route_add(veth_name, TEST_FAMILY, taddr, this_ip_dest); + if (ret && ret != -EEXIST) + test_error("Failed to add route"); + + if (bind(sk, &addr, sizeof(addr))) + test_error("bind()"); +} + +static void bench_connect_client(size_t port_off, struct bench_tests *bt, + ipaddr_t *ips, size_t ips_nr, bool rand_order) +{ + struct bench_stats *con; + union tcp_addr *p; + size_t i; + + if (rand_order) + con = &bt->connect_rand_key; + else + con = &bt->connect_last_key; + + p = (union tcp_addr *)&ips[0]; + + for (i = 0; i < NR_ITERS; i++) { + int sk = socket(test_family, SOCK_STREAM, IPPROTO_TCP); + + if (sk < 0) + test_error("socket()"); + + client_addr_setup(sk, *p); + if (test_set_ao(sk, "password", 0, this_ip_dest, -1, 100, 100)) + test_error("setsockopt(TCP_AO)"); + + synchronize_threads(); + + measure_call(con, connect_client, sk, (void *)&port_off); + + close(sk); + + /* + * Slowest for FILO-linked-list: + * on (i) iteration removing ips[i] element. When it gets + * added to the list back - it becomes first to fetch, so + * on (i + 1) iteration go to ips[i + 1] element. + */ + if (rand_order) + p = (union tcp_addr *)&ips[rand() % ips_nr]; + else + p = (union tcp_addr *)&ips[i % ips_nr]; + } +} + +static void *client_fn(void *arg) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(nr_keys); i++) { + struct bench_tests *bt = &bench_results[i]; + + synchronize_threads(); + bench_connect_client(i, bt, test_ips, nr_keys[i], false); + test_print_stats("Worst case connect", + nr_keys[i], &bt->connect_last_key); + + bench_connect_client(i, bt, test_ips, nr_keys[i], false); + test_print_stats("Connect random-search", + nr_keys[i], &bt->connect_last_key); + } + synchronize_threads(); + return NULL; +} + +int main(int argc, char *argv[]) +{ + test_init(36, server_fn, client_fn); + return 0; +} From patchwork Thu Oct 27 20:43:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022787 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 4AE15ECAAA1 for ; Thu, 27 Oct 2022 20:53:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237251AbiJ0Uxp (ORCPT ); Thu, 27 Oct 2022 16:53:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56790 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237042AbiJ0UwV (ORCPT ); Thu, 27 Oct 2022 16:52:21 -0400 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 B59A192F59 for ; Thu, 27 Oct 2022 13:45:25 -0700 (PDT) Received: by mail-wr1-x42c.google.com with SMTP id k8so4195316wrh.1 for ; Thu, 27 Oct 2022 13:45:25 -0700 (PDT) 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=fPlBRtPqc4jzRch7GuGBGnWek2G+euBxO7Wx+7kBlaA=; b=bVK6qkkmlZm2eCfia56EiPyJWBNvSCSN65vuXmZMwv60c1riUqwoyFm4oXCLyqdWEj HZJ2IyHeUII0+db20y55ElVHgVl+Me9JXeWiVips4MimCekYNrxcW5uwfADYtmFU170z kKgoH8tIctGOaKe05NevvYz028QTHVNviQLU0btxNJVmjM9Aw9trwGYLpctf+L3JBXkM o/9Rs0JCpJIhS9XPU4g7jeLUCdUPLEoF53RA3X+qS2sT4SD2VjiR3MRKoZ2KKJmORuil TgBfc1nWz8Ugk+G4Cr9K41yJ9ZLQzIROOdUUcxU7miwZVLrZ5zPnRmx8hui09mlg720C KuiQ== 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=fPlBRtPqc4jzRch7GuGBGnWek2G+euBxO7Wx+7kBlaA=; b=6hQ59g8Z9Qki8GVDRdH9aYC0AZaZymNHuIuFPNsePA/irXNhB5b7hn6lYkF8TD6SFk ccxysC2Hrcg8xZDt0gqm0308FGJ3cQm+pwXuwTyYUFVoffBJz5xsJeQrPGw4W3pvcsDU W5XIAOe8Y31gVtnLKDXeeQ0pyyWPrZOdBtmz2ktG/cp03/l2WaNQxy830P6X23yIOh5N s6UIWbLM31hOwuUTnlIw9mRaKxwcWkcaLu6H5SkI0MKLL7gMQDgSDhjQhEkoMyk6UdbS mkiPCY+dP1pVuOFqHpcIQBkBlcwK7TkBAap9N2ihVYHHEGwbLah1RCs9BbXkgrqFEvWT fDuA== X-Gm-Message-State: ACrzQf2l9k5WSManRpKMqYoo1uAYBY6hDp/aqdbKWm2p7s/o/dT2mZWK ZiF3YhsXUZl6hZDxK4K5wyY+lw== X-Google-Smtp-Source: AMsMyM6PjJo4hCL2xynqIB6BUfr++/O3XcYk16cYAEN2pOoiEFtkf0IAQpCLIfGfr9AYEXWinvpSeA== X-Received: by 2002:a5d:4b51:0:b0:236:88a2:267f with SMTP id w17-20020a5d4b51000000b0023688a2267fmr7863025wrs.461.1666903485174; Thu, 27 Oct 2022 13:44:45 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:44 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 33/36] selftests/nettest: Remove client_pw Date: Thu, 27 Oct 2022 21:43:44 +0100 Message-Id: <20221027204347.529913-34-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 Use -X to set md5 password string and -M to set authentication method as TCP-MD5. Remove client_pw as not needed. This will allow to add an option to use TCP-AO as authentication method. Note, that use_md5 is a bit, rather than an enum member like `authentication_method` - this will allow to call nettest the way that it'll try to connect with both tcp-md5 and tcp-ao setsocketopt() [which shouldn't be allowed]. Signed-off-by: Dmitry Safonov --- tools/testing/selftests/net/fcnal-test.sh | 232 +++++++++++----------- tools/testing/selftests/net/nettest.c | 26 ++- 2 files changed, 128 insertions(+), 130 deletions(-) diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh index 31c3b6ebd388..d4516c755858 100755 --- a/tools/testing/selftests/net/fcnal-test.sh +++ b/tools/testing/selftests/net/fcnal-test.sh @@ -843,9 +843,9 @@ ipv4_tcp_md5_novrf() # basic use case log_start - run_cmd nettest -s -M ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -M -X ${MD5_PW} -m ${NSB_IP} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 0 "MD5: Single address config" # client sends MD5, server not configured @@ -853,23 +853,23 @@ ipv4_tcp_md5_novrf() show_hint "Should timeout due to MD5 mismatch" run_cmd nettest -s & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 2 "MD5: Server no config, client uses password" # wrong password log_start show_hint "Should timeout since client uses wrong password" - run_cmd nettest -s -M ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -M -X ${MD5_PW} -m ${NSB_IP} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: Client uses wrong password" # client from different address log_start show_hint "Should timeout due to MD5 mismatch" - run_cmd nettest -s -M ${MD5_PW} -m ${NSB_LO_IP} & + run_cmd nettest -s -M -X ${MD5_PW} -m ${NSB_LO_IP} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 2 "MD5: Client address does not match address configured with password" # @@ -878,25 +878,25 @@ ipv4_tcp_md5_novrf() # client in prefix log_start - run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} & + run_cmd nettest -s -M -X ${MD5_PW} -m ${NS_NET} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 0 "MD5: Prefix config" # client in prefix, wrong password log_start show_hint "Should timeout since client uses wrong password" - run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} & + run_cmd nettest -s -M -X ${MD5_PW} -m ${NS_NET} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: Prefix config, client uses wrong password" # client outside of prefix log_start show_hint "Should timeout due to MD5 mismatch" - run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} & + run_cmd nettest -s -M -X ${MD5_PW} -m ${NS_NET} & sleep 1 - run_cmd_nsb nettest -c ${NSB_LO_IP} -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -c ${NSB_LO_IP} -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 2 "MD5: Prefix config, client address not in configured prefix" } @@ -911,9 +911,9 @@ ipv4_tcp_md5() # basic use case log_start - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: Single address config" # client sends MD5, server not configured @@ -921,23 +921,23 @@ ipv4_tcp_md5() show_hint "Should timeout since server does not have MD5 auth" run_cmd nettest -s -I ${VRF} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 2 "MD5: VRF: Server no config, client uses password" # wrong password log_start show_hint "Should timeout since client uses wrong password" - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: VRF: Client uses wrong password" # client from different address log_start show_hint "Should timeout since server config differs from client" - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_LO_IP} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_LO_IP} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 2 "MD5: VRF: Client address does not match address configured with password" # @@ -946,25 +946,25 @@ ipv4_tcp_md5() # client in prefix log_start - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: Prefix config" # client in prefix, wrong password log_start show_hint "Should timeout since client uses wrong password" - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: VRF: Prefix config, client uses wrong password" # client outside of prefix log_start show_hint "Should timeout since client address is outside of prefix" - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET} & sleep 1 - run_cmd_nsb nettest -c ${NSB_LO_IP} -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -c ${NSB_LO_IP} -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 2 "MD5: VRF: Prefix config, client address not in configured prefix" # @@ -972,74 +972,74 @@ ipv4_tcp_md5() # log_start - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} & - run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NSB_IP} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -M -X ${MD5_WRONG_PW} -m ${NSB_IP} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF" log_start - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} & - run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NSB_IP} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -M -X ${MD5_WRONG_PW} -m ${NSB_IP} & sleep 1 - run_cmd_nsc nettest -r ${NSA_IP} -X ${MD5_WRONG_PW} + run_cmd_nsc nettest -r ${NSA_IP} -M -X ${MD5_WRONG_PW} log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF" log_start show_hint "Should timeout since client in default VRF uses VRF password" - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} & - run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NSB_IP} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -M -X ${MD5_WRONG_PW} -m ${NSB_IP} & sleep 1 - run_cmd_nsc nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsc nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF with VRF pw" log_start show_hint "Should timeout since client in VRF uses default VRF password" - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} & - run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NSB_IP} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP} & + run_cmd nettest -s -M -X ${MD5_WRONG_PW} -m ${NSB_IP} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF with default VRF pw" log_start - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} & - run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET} & + run_cmd nettest -s -M -X ${MD5_WRONG_PW} -m ${NS_NET} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF" log_start - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} & - run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET} & + run_cmd nettest -s -M -X ${MD5_WRONG_PW} -m ${NS_NET} & sleep 1 - run_cmd_nsc nettest -r ${NSA_IP} -X ${MD5_WRONG_PW} + run_cmd_nsc nettest -r ${NSA_IP} -M -X ${MD5_WRONG_PW} log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF" log_start show_hint "Should timeout since client in default VRF uses VRF password" - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} & - run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET} & + run_cmd nettest -s -M -X ${MD5_WRONG_PW} -m ${NS_NET} & sleep 1 - run_cmd_nsc nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsc nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF with VRF pw" log_start show_hint "Should timeout since client in VRF uses default VRF password" - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} & - run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET} & + run_cmd nettest -s -M -X ${MD5_WRONG_PW} -m ${NS_NET} & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF with default VRF pw" # # negative tests # log_start - run_cmd nettest -s -I ${NSA_DEV} -M ${MD5_PW} -m ${NSB_IP} + run_cmd nettest -s -I ${NSA_DEV} -M -X ${MD5_PW} -m ${NSB_IP} log_test $? 1 "MD5: VRF: Device must be a VRF - single address" log_start - run_cmd nettest -s -I ${NSA_DEV} -M ${MD5_PW} -m ${NS_NET} + run_cmd nettest -s -I ${NSA_DEV} -M -X ${MD5_PW} -m ${NS_NET} log_test $? 1 "MD5: VRF: Device must be a VRF - prefix" test_ipv4_md5_vrf__vrf_server__no_bind_ifindex @@ -1050,16 +1050,16 @@ test_ipv4_md5_vrf__vrf_server__no_bind_ifindex() { log_start show_hint "Simulates applications using VRF without TCP_MD5SIG_FLAG_IFINDEX" - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} --no-bind-key-ifindex & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET} --no-bind-key-ifindex & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: VRF-bound server, unbound key accepts connection" log_start show_hint "Binding both the socket and the key is not required but it works" - run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} --force-bind-key-ifindex & + run_cmd nettest -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET} --force-bind-key-ifindex & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: VRF-bound server, bound key accepts connection" } @@ -1071,27 +1071,27 @@ test_ipv4_md5_vrf__global_server__bind_ifindex0() set_sysctl net.ipv4.tcp_l3mdev_accept=1 log_start - run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} --force-bind-key-ifindex & + run_cmd nettest -s -M -X ${MD5_PW} -m ${NS_NET} --force-bind-key-ifindex & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 2 "MD5: VRF: Global server, Key bound to ifindex=0 rejects VRF connection" log_start - run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} --force-bind-key-ifindex & + run_cmd nettest -s -M -X ${MD5_PW} -m ${NS_NET} --force-bind-key-ifindex & sleep 1 - run_cmd_nsc nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsc nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: Global server, key bound to ifindex=0 accepts non-VRF connection" log_start - run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} --no-bind-key-ifindex & + run_cmd nettest -s -M -X ${MD5_PW} -m ${NS_NET} --no-bind-key-ifindex & sleep 1 - run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsb nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: Global server, key not bound to ifindex accepts VRF connection" log_start - run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} --no-bind-key-ifindex & + run_cmd nettest -s -M -X ${MD5_PW} -m ${NS_NET} --no-bind-key-ifindex & sleep 1 - run_cmd_nsc nettest -r ${NSA_IP} -X ${MD5_PW} + run_cmd_nsc nettest -r ${NSA_IP} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: Global server, key not bound to ifindex accepts non-VRF connection" # restore value @@ -2454,9 +2454,9 @@ ipv6_tcp_md5_novrf() # basic use case log_start - run_cmd nettest -6 -s -M ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -M -X ${MD5_PW} -m ${NSB_IP6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 0 "MD5: Single address config" # client sends MD5, server not configured @@ -2464,23 +2464,23 @@ ipv6_tcp_md5_novrf() show_hint "Should timeout due to MD5 mismatch" run_cmd nettest -6 -s & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 2 "MD5: Server no config, client uses password" # wrong password log_start show_hint "Should timeout since client uses wrong password" - run_cmd nettest -6 -s -M ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -M -X ${MD5_PW} -m ${NSB_IP6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: Client uses wrong password" # client from different address log_start show_hint "Should timeout due to MD5 mismatch" - run_cmd nettest -6 -s -M ${MD5_PW} -m ${NSB_LO_IP6} & + run_cmd nettest -6 -s -M -X ${MD5_PW} -m ${NSB_LO_IP6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 2 "MD5: Client address does not match address configured with password" # @@ -2489,25 +2489,25 @@ ipv6_tcp_md5_novrf() # client in prefix log_start - run_cmd nettest -6 -s -M ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -M -X ${MD5_PW} -m ${NS_NET6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 0 "MD5: Prefix config" # client in prefix, wrong password log_start show_hint "Should timeout since client uses wrong password" - run_cmd nettest -6 -s -M ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -M -X ${MD5_PW} -m ${NS_NET6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: Prefix config, client uses wrong password" # client outside of prefix log_start show_hint "Should timeout due to MD5 mismatch" - run_cmd nettest -6 -s -M ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -M -X ${MD5_PW} -m ${NS_NET6} & sleep 1 - run_cmd_nsb nettest -6 -c ${NSB_LO_IP6} -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -c ${NSB_LO_IP6} -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 2 "MD5: Prefix config, client address not in configured prefix" } @@ -2522,9 +2522,9 @@ ipv6_tcp_md5() # basic use case log_start - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: Single address config" # client sends MD5, server not configured @@ -2532,23 +2532,23 @@ ipv6_tcp_md5() show_hint "Should timeout since server does not have MD5 auth" run_cmd nettest -6 -s -I ${VRF} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 2 "MD5: VRF: Server no config, client uses password" # wrong password log_start show_hint "Should timeout since client uses wrong password" - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: VRF: Client uses wrong password" # client from different address log_start show_hint "Should timeout since server config differs from client" - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_LO_IP6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_LO_IP6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 2 "MD5: VRF: Client address does not match address configured with password" # @@ -2557,25 +2557,25 @@ ipv6_tcp_md5() # client in prefix log_start - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: Prefix config" # client in prefix, wrong password log_start show_hint "Should timeout since client uses wrong password" - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: VRF: Prefix config, client uses wrong password" # client outside of prefix log_start show_hint "Should timeout since client address is outside of prefix" - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET6} & sleep 1 - run_cmd_nsb nettest -6 -c ${NSB_LO_IP6} -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -c ${NSB_LO_IP6} -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 2 "MD5: VRF: Prefix config, client address not in configured prefix" # @@ -2583,74 +2583,74 @@ ipv6_tcp_md5() # log_start - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} & - run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -M -X ${MD5_WRONG_PW} -m ${NSB_IP6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF" log_start - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} & - run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -M -X ${MD5_WRONG_PW} -m ${NSB_IP6} & sleep 1 - run_cmd_nsc nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW} + run_cmd_nsc nettest -6 -r ${NSA_IP6} -M -X ${MD5_WRONG_PW} log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF" log_start show_hint "Should timeout since client in default VRF uses VRF password" - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} & - run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -M -X ${MD5_WRONG_PW} -m ${NSB_IP6} & sleep 1 - run_cmd_nsc nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsc nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF with VRF pw" log_start show_hint "Should timeout since client in VRF uses default VRF password" - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} & - run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NSB_IP6} & + run_cmd nettest -6 -s -M -X ${MD5_WRONG_PW} -m ${NSB_IP6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF with default VRF pw" log_start - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} & - run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -M -X ${MD5_WRONG_PW} -m ${NS_NET6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF" log_start - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} & - run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -M -X ${MD5_WRONG_PW} -m ${NS_NET6} & sleep 1 - run_cmd_nsc nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW} + run_cmd_nsc nettest -6 -r ${NSA_IP6} -M -X ${MD5_WRONG_PW} log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF" log_start show_hint "Should timeout since client in default VRF uses VRF password" - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} & - run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -M -X ${MD5_WRONG_PW} -m ${NS_NET6} & sleep 1 - run_cmd_nsc nettest -6 -r ${NSA_IP6} -X ${MD5_PW} + run_cmd_nsc nettest -6 -r ${NSA_IP6} -M -X ${MD5_PW} log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF with VRF pw" log_start show_hint "Should timeout since client in VRF uses default VRF password" - run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} & - run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -I ${VRF} -M -X ${MD5_PW} -m ${NS_NET6} & + run_cmd nettest -6 -s -M -X ${MD5_WRONG_PW} -m ${NS_NET6} & sleep 1 - run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW} + run_cmd_nsb nettest -6 -r ${NSA_IP6} -M -X ${MD5_WRONG_PW} log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF with default VRF pw" # # negative tests # log_start - run_cmd nettest -6 -s -I ${NSA_DEV} -M ${MD5_PW} -m ${NSB_IP6} + run_cmd nettest -6 -s -I ${NSA_DEV} -M -X ${MD5_PW} -m ${NSB_IP6} log_test $? 1 "MD5: VRF: Device must be a VRF - single address" log_start - run_cmd nettest -6 -s -I ${NSA_DEV} -M ${MD5_PW} -m ${NS_NET6} + run_cmd nettest -6 -s -I ${NSA_DEV} -M -X ${MD5_PW} -m ${NS_NET6} log_test $? 1 "MD5: VRF: Device must be a VRF - prefix" } diff --git a/tools/testing/selftests/net/nettest.c b/tools/testing/selftests/net/nettest.c index 7900fa98eccb..b9e600899cf6 100644 --- a/tools/testing/selftests/net/nettest.c +++ b/tools/testing/selftests/net/nettest.c @@ -76,7 +76,8 @@ struct sock_args { has_grp:1, has_expected_laddr:1, has_expected_raddr:1, - bind_test_only:1; + bind_test_only:1, + use_md5:1; unsigned short port; @@ -95,7 +96,6 @@ struct sock_args { const char *serverns; const char *password; - const char *client_pw; /* prefix for MD5 password */ const char *md5_prefix_str; union { @@ -1546,7 +1546,7 @@ static int do_server(struct sock_args *args, int ipc_fd) return rc; } - if (args->password && tcp_md5_remote(lsd, args)) { + if (args->use_md5 && tcp_md5_remote(lsd, args)) { close(lsd); goto err_exit; } @@ -1670,7 +1670,7 @@ static int connectsock(void *addr, socklen_t alen, struct sock_args *args) if (args->type != SOCK_STREAM && !args->datagram_connect) goto out; - if (args->password && tcp_md5sig(sd, addr, alen, args)) + if (args->use_md5 && tcp_md5sig(sd, addr, alen, args)) goto err; if (args->bind_test_only) @@ -1751,8 +1751,6 @@ static int do_client(struct sock_args *args) break; } - args->password = args->client_pw; - if (args->has_grp) sd = msock_client(args); else @@ -1862,7 +1860,7 @@ static int ipc_parent(int cpid, int fd, struct sock_args *args) return client_status; } -#define GETOPT_STR "sr:l:c:p:t:g:P:DRn:M:X:m:d:I:BN:O:SUCi6xL:0:1:2:3:Fbqf" +#define GETOPT_STR "sr:l:c:p:t:g:P:DRn:MX:m:d:I:BN:O:SUCi6xL:0:1:2:3:Fbqf" #define OPT_FORCE_BIND_KEY_IFINDEX 1001 #define OPT_NO_BIND_KEY_IFINDEX 1002 @@ -1906,8 +1904,8 @@ static void print_usage(char *prog) " -L len send random message of given length\n" " -n num number of times to send message\n" "\n" - " -M password use MD5 sum protection\n" - " -X password MD5 password for client mode\n" + " -M use MD5 sum protection\n" + " -X password MD5 password\n" " -m prefix/len prefix and length to use for MD5 key\n" " --no-bind-key-ifindex: Force TCP_MD5SIG_FLAG_IFINDEX off\n" " --force-bind-key-ifindex: Force TCP_MD5SIG_FLAG_IFINDEX on\n" @@ -2019,7 +2017,7 @@ int main(int argc, char *argv[]) msg = random_msg(atoi(optarg)); break; case 'M': - args.password = optarg; + args.use_md5 = 1; break; case OPT_FORCE_BIND_KEY_IFINDEX: args.bind_key_ifindex = 1; @@ -2028,7 +2026,7 @@ int main(int argc, char *argv[]) args.bind_key_ifindex = -1; break; case 'X': - args.client_pw = optarg; + args.password = optarg; break; case 'm': args.md5_prefix_str = optarg; @@ -2092,14 +2090,14 @@ int main(int argc, char *argv[]) } } - if (args.password && - ((!args.has_remote_ip && !args.md5_prefix_str) || + if (args.password && (!args.use_md5 || + (!args.has_remote_ip && !args.md5_prefix_str) || args.type != SOCK_STREAM)) { log_error("MD5 passwords apply to TCP only and require a remote ip for the password\n"); return 1; } - if (args.md5_prefix_str && !args.password) { + if ((args.md5_prefix_str || args.use_md5) && !args.password) { log_error("Prefix range for MD5 protection specified without a password\n"); return 1; } From patchwork Thu Oct 27 20:43:45 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022789 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 B3844ECAAA1 for ; Thu, 27 Oct 2022 20:54:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237029AbiJ0Uyv (ORCPT ); Thu, 27 Oct 2022 16:54:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54404 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235785AbiJ0Uwd (ORCPT ); Thu, 27 Oct 2022 16:52:33 -0400 Received: from mail-wm1-x32f.google.com (mail-wm1-x32f.google.com [IPv6:2a00:1450:4864:20::32f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AFB4397EDD for ; Thu, 27 Oct 2022 13:45:31 -0700 (PDT) Received: by mail-wm1-x32f.google.com with SMTP id bg9-20020a05600c3c8900b003bf249616b0so2104824wmb.3 for ; Thu, 27 Oct 2022 13:45:31 -0700 (PDT) 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=+hdisk2huNybtWVXbtT77JtWTDX9XIc08M/YyYNQqQA=; b=MM8otwdC1LN6OlvEG3BsuhkUbKyfXU0Gw9ltqT8j5DlmSOuwH3akRiMcBkqr1LxZwv WVkTGPBIPKvb9LLxgN5dpr3kAxslA7vmqoVRPfb6BYsG3k+xHpVlqtEJ96EPIkbZ7obO aaang8AdmLd0ddkxrre13QxJyh97ceo38siZ6S4NNk/BSva1bMi/PwXklRVNOAHva9Ob FCJD5ODwXDWGUJuF3rNTCeWGDx4+yhI3cD6eN2cpsFBT6ZuxRYUQI54faEnwKvHgGr17 j5f+8d0Jti2Vga93K4Q1CrOXVrtZro8+lNuJ0oD+pUHZWrWuQ7Ty03k5B3yR+DOOiVS2 2IMg== 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=+hdisk2huNybtWVXbtT77JtWTDX9XIc08M/YyYNQqQA=; b=kITW2sJurCDjSTvp8IzPHTnb0iCAVgvVSjhnuytGtRGCRz+dZe4avXKXrmWTKlcZka e7w8MzAXXe00mkyBCsZXJkZZBNOyds9lZtD93LkbzEIRtioM+i6aXU2IIKt2DdtBbJNE B3W5Q0m4g5YIuLrHh/O0jHvSYeUCXopIBozd3EB+hZJnEdxLT9o31gRqZvCkwLDjYPtO brjXzxX7bEVzmuhnjwxZVPpZiperlSjtgq0TB30PpfmI3Z5N9ZdoxCOv+ZcouGCKvylT Fzyg93FUlX1vxbPZMMqBNdJMLKNB9kllACH0IYZWke3CoU7cLV3ke7EdJDHKv2qy5kh7 ovVg== X-Gm-Message-State: ACrzQf2qWDKk/OO0L3tjM3GGrIodVYcTWmvOnzbR2N+ZRaXSv9BfjsbK UHMG8UlJ4hWI7OR7VVNezYb/ng== X-Google-Smtp-Source: AMsMyM5SF49nP77j8TfEXrfOKQS7xRflyPat+mSDDrloLi2E6Ob4dMgZY72Rt5v2+BW3WzIkv7/s6g== X-Received: by 2002:a05:600c:19d2:b0:3c6:fbb9:ca9a with SMTP id u18-20020a05600c19d200b003c6fbb9ca9amr7330350wmq.110.1666903486720; Thu, 27 Oct 2022 13:44:46 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:46 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 34/36] selftest/nettest: Rename md5_prefix* => auth_prefix* Date: Thu, 27 Oct 2022 21:43:45 +0100 Message-Id: <20221027204347.529913-35-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 it's going to be used for TCP-AO testing too. Signed-off-by: Dmitry Safonov --- tools/testing/selftests/net/nettest.c | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tools/testing/selftests/net/nettest.c b/tools/testing/selftests/net/nettest.c index b9e600899cf6..3499d6aff9e4 100644 --- a/tools/testing/selftests/net/nettest.c +++ b/tools/testing/selftests/net/nettest.c @@ -97,11 +97,11 @@ struct sock_args { const char *password; /* prefix for MD5 password */ - const char *md5_prefix_str; + const char *auth_prefix_str; union { struct sockaddr_in v4; struct sockaddr_in6 v6; - } md5_prefix; + } auth_prefix; unsigned int prefix_len; /* 0: default, -1: force off, +1: force on */ int bind_key_ifindex; @@ -274,7 +274,7 @@ static int tcp_md5sig(int sd, void *addr, socklen_t alen, struct sock_args *args md5sig.tcpm_flags |= TCP_MD5SIG_FLAG_PREFIX; md5sig.tcpm_prefixlen = args->prefix_len; - addr = &args->md5_prefix; + addr = &args->auth_prefix; } memcpy(&md5sig.tcpm_addr, addr, alen); @@ -314,13 +314,13 @@ static int tcp_md5_remote(int sd, struct sock_args *args) switch (args->version) { case AF_INET: sin.sin_port = htons(args->port); - sin.sin_addr = args->md5_prefix.v4.sin_addr; + sin.sin_addr = args->auth_prefix.v4.sin_addr; addr = &sin; alen = sizeof(sin); break; case AF_INET6: sin6.sin6_port = htons(args->port); - sin6.sin6_addr = args->md5_prefix.v6.sin6_addr; + sin6.sin6_addr = args->auth_prefix.v6.sin6_addr; addr = &sin6; alen = sizeof(sin6); break; @@ -753,11 +753,11 @@ static int convert_addr(struct sock_args *args, const char *_str, case ADDR_TYPE_MD5_PREFIX: desc = "md5 prefix"; if (family == AF_INET) { - args->md5_prefix.v4.sin_family = AF_INET; - addr = &args->md5_prefix.v4.sin_addr; + args->auth_prefix.v4.sin_family = AF_INET; + addr = &args->auth_prefix.v4.sin_addr; } else if (family == AF_INET6) { - args->md5_prefix.v6.sin6_family = AF_INET6; - addr = &args->md5_prefix.v6.sin6_addr; + args->auth_prefix.v6.sin6_family = AF_INET6; + addr = &args->auth_prefix.v6.sin6_addr; } else return 1; @@ -840,8 +840,8 @@ static int validate_addresses(struct sock_args *args) convert_addr(args, args->remote_addr_str, ADDR_TYPE_REMOTE) < 0) return 1; - if (args->md5_prefix_str && - convert_addr(args, args->md5_prefix_str, + if (args->auth_prefix_str && + convert_addr(args, args->auth_prefix_str, ADDR_TYPE_MD5_PREFIX) < 0) return 1; @@ -2029,7 +2029,7 @@ int main(int argc, char *argv[]) args.password = optarg; break; case 'm': - args.md5_prefix_str = optarg; + args.auth_prefix_str = optarg; break; case 'S': args.use_setsockopt = 1; @@ -2091,13 +2091,13 @@ int main(int argc, char *argv[]) } if (args.password && (!args.use_md5 || - (!args.has_remote_ip && !args.md5_prefix_str) || + (!args.has_remote_ip && !args.auth_prefix_str) || args.type != SOCK_STREAM)) { log_error("MD5 passwords apply to TCP only and require a remote ip for the password\n"); return 1; } - if ((args.md5_prefix_str || args.use_md5) && !args.password) { + if ((args.auth_prefix_str || args.use_md5) && !args.password) { log_error("Prefix range for MD5 protection specified without a password\n"); return 1; } From patchwork Thu Oct 27 20:43:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022788 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 8E86BECAAA1 for ; Thu, 27 Oct 2022 20:54:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236710AbiJ0Uyt (ORCPT ); Thu, 27 Oct 2022 16:54:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46428 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235239AbiJ0Uwf (ORCPT ); Thu, 27 Oct 2022 16:52:35 -0400 Received: from mail-wm1-x32f.google.com (mail-wm1-x32f.google.com [IPv6:2a00:1450:4864:20::32f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2DEE57993B for ; Thu, 27 Oct 2022 13:45:34 -0700 (PDT) Received: by mail-wm1-x32f.google.com with SMTP id m29-20020a05600c3b1d00b003c6bf423c71so5068422wms.0 for ; Thu, 27 Oct 2022 13:45:34 -0700 (PDT) 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=dwtJ3yUpPWJmmUE5F3LoS4PKdhnx1qXfUagzNvltjAg=; b=MJppJJocAVD1udLbf7Ml91Z1RvMgYtufEIobXxw8y+LEFIt5MUz2X61gtPUXpgjQY9 LKxeQkW6kawSB/rHZ11ZZ8tRLTtpyB+D6cT+JzWFT6KysDtaQePNKVNTcPLq3dygeqxb ePRCSkhCuNiCs3VkFew2WNIKeEHRNgFOoVbreGxzznEm7305W9w3ikfmXaDzLTjhWDXD Rb9nmmhrlsTKrzcTpPGSaLaOhqnGagmm1FUbRjHQiTBPR9ZosgzSDA+7B+m5tC7qwNs1 1+UgoYW+eBDLDBLQWdcaIV5CyWIAjlGseu3BibcNfAKBXppDyg1Bd9yddIB32yBymG4m 0IkQ== 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=dwtJ3yUpPWJmmUE5F3LoS4PKdhnx1qXfUagzNvltjAg=; b=WzLfywWPWCOfoEWRZDrbIM5/wuEGN2y0ZX9wAZwJPZANcfK4Sj0YcC29pCAwLPaaO6 BJgL1x4NPUeN5hIB+RiaGoU2418i3ey+1wgvw1BUo/90XhklAmteFBkXQ1cJwFyQqMfR NXc8zvAc3uShB5suCQPgBNFD2FbSYgZ5GqIqOkwEIVjn6XrkSfF1Io7RKDbPFFd6A2ea odHhuWDTc5pukvxj6xL9zVFt0OmuQQNiMenQrQUCao5LjFwOBjHY0QqwYuD6wlmBTf/S JL1YLjRIrWtgpIPs/5zCuXcVDFy6bHlG8IPo2UDjwttNSyvowM8ZcQ6qNjlH54I1zjiV 55Tg== X-Gm-Message-State: ACrzQf0JuiKVfdfMXOSkqPd1DQAesk1p1eupnjrz7quAm4Yi4TMElj+1 rirGVDiZG76daEcO01z33Xg3cA== X-Google-Smtp-Source: AMsMyM4fnejBkCU0f691xbz7CzjpBLMN+lXSIezDH+0/8XINsCm+6EGJGTAS8aqCd0g0ebKEjE8jEQ== X-Received: by 2002:a1c:7c14:0:b0:3b5:1133:d2ed with SMTP id x20-20020a1c7c14000000b003b51133d2edmr7182857wmc.133.1666903488225; Thu, 27 Oct 2022 13:44:48 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:47 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 35/36] selftests/nettest: Add TCP-AO support Date: Thu, 27 Oct 2022 21:43:46 +0100 Message-Id: <20221027204347.529913-36-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 Roughly, the same as TCP-MD5 support. Signed-off-by: Dmitry Safonov --- tools/testing/selftests/net/nettest.c | 179 +++++++++++++++++++++++--- 1 file changed, 160 insertions(+), 19 deletions(-) diff --git a/tools/testing/selftests/net/nettest.c b/tools/testing/selftests/net/nettest.c index 3499d6aff9e4..c81d3e2ceae1 100644 --- a/tools/testing/selftests/net/nettest.c +++ b/tools/testing/selftests/net/nettest.c @@ -77,7 +77,9 @@ struct sock_args { has_expected_laddr:1, has_expected_raddr:1, bind_test_only:1, - use_md5:1; + use_md5:1, + use_tcpao:1, + tcp_ao_excopts:1; unsigned short port; @@ -96,7 +98,7 @@ struct sock_args { const char *serverns; const char *password; - /* prefix for MD5 password */ + /* prefix for MD5/TCP-AO password */ const char *auth_prefix_str; union { struct sockaddr_in v4; @@ -105,6 +107,8 @@ struct sock_args { unsigned int prefix_len; /* 0: default, -1: force off, +1: force on */ int bind_key_ifindex; + unsigned int tcp_ao_sndid, tcp_ao_rcvid, tcp_ao_maclen; + char *tcp_ao_algo; /* expected addresses and device index for connection */ const char *expected_dev; @@ -300,7 +304,67 @@ static int tcp_md5sig(int sd, void *addr, socklen_t alen, struct sock_args *args return rc; } -static int tcp_md5_remote(int sd, struct sock_args *args) +static int tcp_ao(int sd, void *addr, socklen_t alen, struct sock_args *args) +{ + int keylen = strlen(args->password); + struct tcp_ao ao = {}; + int opt = TCP_AO; + int rc; + + if (keylen > TCP_AO_MAXKEYLEN) { + log_error("key length is too big"); + return -1; + } + ao.tcpa_keylen = keylen; + memcpy(ao.tcpa_key, args->password, keylen); + if (args->tcp_ao_algo) + strcpy(ao.tcpa_alg_name, args->tcp_ao_algo); + else + strcpy(ao.tcpa_alg_name, "cmac(aes128)"); + if (args->tcp_ao_maclen) + ao.tcpa_maclen = args->tcp_ao_maclen; + + ao.tcpa_sndid = args->tcp_ao_sndid; + ao.tcpa_rcvid = args->tcp_ao_rcvid; + if (args->tcp_ao_excopts) + ao.tcpa_keyflags |= TCP_AO_KEYF_EXCLUDE_OPT; + + if (args->prefix_len) { + ao.tcpa_prefix = args->prefix_len; + } else { + switch (args->version) { + case AF_INET: + ao.tcpa_prefix = 32; + break; + case AF_INET6: + ao.tcpa_prefix = 128; + break; + default: + log_error("unknown address family\n"); + exit(1); + } + } + memcpy(&ao.tcpa_addr, addr, alen); + + /* FIXME: Remove once matching by port is supported */ + if (args->version == AF_INET) { + struct sockaddr_in *sin = (void *)&ao.tcpa_addr; + + sin->sin_port = htons(0); + } else if (args->version == AF_INET6) { + struct sockaddr_in6 *sin6 = (void *)&ao.tcpa_addr; + + sin6->sin6_port = htons(0); + } + + rc = setsockopt(sd, IPPROTO_TCP, opt, &ao, sizeof(ao)); + if (rc < 0) + log_err_errno("setsockopt(TCP_AO)"); + + return rc; +} + +static int tcp_auth_remote(int sd, struct sock_args *args) { struct sockaddr_in sin = { .sin_family = AF_INET, @@ -329,7 +393,10 @@ static int tcp_md5_remote(int sd, struct sock_args *args) exit(1); } - if (tcp_md5sig(sd, addr, alen, args)) + if (args->use_md5 && tcp_md5sig(sd, addr, alen, args)) + return -1; + + if (args->use_tcpao && tcp_ao(sd, addr, alen, args)) return -1; return 0; @@ -1546,10 +1613,8 @@ static int do_server(struct sock_args *args, int ipc_fd) return rc; } - if (args->use_md5 && tcp_md5_remote(lsd, args)) { - close(lsd); - goto err_exit; - } + if (tcp_auth_remote(lsd, args)) + goto err_close; ipc_write(ipc_fd, 1); while (1) { @@ -1598,6 +1663,8 @@ static int do_server(struct sock_args *args, int ipc_fd) close(lsd); return rc; +err_close: + close(lsd); err_exit: ipc_write(ipc_fd, 0); return 1; @@ -1673,6 +1740,9 @@ static int connectsock(void *addr, socklen_t alen, struct sock_args *args) if (args->use_md5 && tcp_md5sig(sd, addr, alen, args)) goto err; + if (args->use_tcpao && tcp_ao(sd, addr, alen, args)) + goto err; + if (args->bind_test_only) goto out; @@ -1799,6 +1869,44 @@ static char *random_msg(int len) return m; } +static void strip_newlines(char *str) +{ + size_t i = strlen(str); + + for (; i > 0; i--) { + if (str[i - 1] != '\n') + return; + str[i - 1] = '\0'; + } +} + +static int set_tcp_ao_param(struct sock_args *args, const char *opt) +{ + char *end, *sep = strstr(opt, ":"); + unsigned long tmp; + + errno = 0; + if (sep == NULL) + goto err_fail; + + tmp = strtoul(opt, &end, 0); + if (errno || tmp > 255 || end != sep) + goto err_fail; + args->tcp_ao_sndid = (unsigned int) tmp; + + tmp = strtoul(++sep, &end, 0); + if (errno || tmp > 255 || (*end != '\n' && *end != '\0')) + goto err_fail; + args->tcp_ao_rcvid = (unsigned int) tmp; + + return 0; + +err_fail: + fprintf(stderr, "TCP-AO argument format is sndid:rcvid where ids in [0,255]\n" + "Example: -T 100:200\n"); + return -1; +} + static int ipc_child(int fd, struct sock_args *args) { char *outbuf, *errbuf; @@ -1860,13 +1968,19 @@ static int ipc_parent(int cpid, int fd, struct sock_args *args) return client_status; } -#define GETOPT_STR "sr:l:c:p:t:g:P:DRn:MX:m:d:I:BN:O:SUCi6xL:0:1:2:3:Fbqf" -#define OPT_FORCE_BIND_KEY_IFINDEX 1001 -#define OPT_NO_BIND_KEY_IFINDEX 1002 +#define GETOPT_STR "sr:l:c:p:t:g:P:DRn:MT:X:m:d:I:BN:O:SUCi6xL:0:1:2:3:Fbqf" +#define OPT_FORCE_BIND_KEY_IFINDEX 1001 +#define OPT_NO_BIND_KEY_IFINDEX 1002 +#define OPT_TCPAO_ALGO 1003 +#define OPT_TCPAO_MACLEN 1004 +#define OPT_TCPAO_EXCOPTS 1005 static struct option long_opts[] = { - {"force-bind-key-ifindex", 0, 0, OPT_FORCE_BIND_KEY_IFINDEX}, - {"no-bind-key-ifindex", 0, 0, OPT_NO_BIND_KEY_IFINDEX}, + {"force-bind-key-ifindex", 0, 0, OPT_FORCE_BIND_KEY_IFINDEX}, + {"no-bind-key-ifindex", 0, 0, OPT_NO_BIND_KEY_IFINDEX}, + {"tcpao_algo", 1, 0, OPT_TCPAO_ALGO }, + {"tcpao_maclen", 1, 0, OPT_TCPAO_MACLEN }, + {"tcpao_excopts", 0, 0, OPT_TCPAO_EXCOPTS }, {0, 0, 0, 0} }; @@ -1905,8 +2019,12 @@ static void print_usage(char *prog) " -n num number of times to send message\n" "\n" " -M use MD5 sum protection\n" - " -X password MD5 password\n" - " -m prefix/len prefix and length to use for MD5 key\n" + " -T snd:rcvid use TCP authopt (RFC5925) with snd/rcv ids\n" + " --tcpao_algo=algo TCP-AO hashing algorithm [valid with -T]\n" + " --tcpao_maclen=maclen TCP-AO MAC length [valid with -T]\n" + " --tcpao_excopts Exclude TCP options [valid with -T]\n" + " -X password MD5/TCP-AO password\n" + " -m prefix/len prefix and length to use for MD5/TCP-AO key\n" " --no-bind-key-ifindex: Force TCP_MD5SIG_FLAG_IFINDEX off\n" " --force-bind-key-ifindex: Force TCP_MD5SIG_FLAG_IFINDEX on\n" " (default: only if -I is passed)\n" @@ -2025,6 +2143,29 @@ int main(int argc, char *argv[]) case OPT_NO_BIND_KEY_IFINDEX: args.bind_key_ifindex = -1; break; + case OPT_TCPAO_ALGO: + args.tcp_ao_algo = strdup(optarg); + strip_newlines(args.tcp_ao_algo); + if (strlen(args.tcp_ao_algo) == 0) { + fprintf(stderr, "Invalid argument --tcpao_algo=%s\n", optarg); + return 1; + } + break; + case OPT_TCPAO_MACLEN: + if (str_to_uint(optarg, 1, 255, &tmp) != 0) { + fprintf(stderr, "Invalid --tcpao_maclen=%s\n", optarg); + return 1; + } + args.tcp_ao_maclen = tmp; + break; + case OPT_TCPAO_EXCOPTS: + args.tcp_ao_excopts = 1; + break; + case 'T': + args.use_tcpao = 1; + if (set_tcp_ao_param(&args, optarg)) + return 1; + break; case 'X': args.password = optarg; break; @@ -2090,15 +2231,15 @@ int main(int argc, char *argv[]) } } - if (args.password && (!args.use_md5 || + if (args.password && ((!args.use_md5 && !args.use_tcpao) || (!args.has_remote_ip && !args.auth_prefix_str) || args.type != SOCK_STREAM)) { - log_error("MD5 passwords apply to TCP only and require a remote ip for the password\n"); + log_error("TCP-MD5/TCP-AO passwords apply to TCP only and require a remote ip for the password\n"); return 1; } - if ((args.auth_prefix_str || args.use_md5) && !args.password) { - log_error("Prefix range for MD5 protection specified without a password\n"); + if ((args.auth_prefix_str || args.use_md5 || args.use_tcpao) && !args.password) { + log_error("Prefix range for TCP-MD5/TCP-AO protection specified without a password\n"); return 1; } From patchwork Thu Oct 27 20:43:47 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Safonov X-Patchwork-Id: 13022757 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 3770CECAAA1 for ; Thu, 27 Oct 2022 20:51:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237211AbiJ0UvM (ORCPT ); Thu, 27 Oct 2022 16:51:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37610 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235922AbiJ0UtK (ORCPT ); Thu, 27 Oct 2022 16:49:10 -0400 Received: from mail-wr1-x431.google.com (mail-wr1-x431.google.com [IPv6:2a00:1450:4864:20::431]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2136838A5 for ; Thu, 27 Oct 2022 13:44:50 -0700 (PDT) Received: by mail-wr1-x431.google.com with SMTP id g12so4148477wrs.10 for ; Thu, 27 Oct 2022 13:44:50 -0700 (PDT) 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=Fds8a12jf9hfVLIPaQf1hCBYTA2Bs1gOOen7wI+4FKs=; b=eqMrDFj+3C4vgMdKJwg0YIgDKbiHptwg7llwlbnqfbe1638SE7MZvOmDpHQbP5O5SG ZY8YWWuJEFRk3N23/HNrzn+WGuemP7ndSwE5t79LY4WpspLO8ZBnAVf+E/WMpB6hPcq3 njCoc7tP3HVHMzjq2aeV5tvlkGoMBmLhbKpR/zT4bhulMYeT03PEUpQuzrERvyedU+/7 7nyy5rtYnZJhgXSFaah4w4nNhQJPYCRGRQqDctgcgtshZAQyI/mFhQvYXhQm3kE+YQLz aVvuyB7J7cEa/1a965O0EAx4bP3ycDKfl5igc/7AM/i61gQ11Qg9VnPfviYovuvtPMZz /UBA== 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=Fds8a12jf9hfVLIPaQf1hCBYTA2Bs1gOOen7wI+4FKs=; b=TA2bKYc1X5YOsPYFk/iEnB2ERKBwGIq6cR4sbqMJBA0SrymOuMS08BOyv7Gt/3Y5tF W+6i+Wc03RVoIQ2RthezPejjp5sIoJe5+vGzntuwP9Fo6lEvx0KRFvxyQmYCTJA5MIGp axZhcY99UUixSTLI0ODa2owOJ3/72580alKFmgJEYKcD7UtwEQdQL8lN+cJ0hU2zPQGw V3YwVwkGRBV1hW/wdsomWMHQsv7Gz3UsglAdehVmgLzsBf0oj5/ryYDiKv0tI+d6YWpv p9nsKBPVERvDCRbODJcdAYDFXL5hqb2iBMTbjZqNo5jBNFNTkka2bU7j7fX+iyi5WNif gt7g== X-Gm-Message-State: ACrzQf2nXyTRLSEEo9+1nkev5XZ9zjFeF+dwrMo6ODw02SouUd4IIGP8 S2wcjNOA0nsGxj0ntsQjPnKm9g== X-Google-Smtp-Source: AMsMyM6QTwh0/ag0+yjwplrhKXfyU19W8u1pjxwi+VBvRElwiQ+kPFUXfu7FOhwAScI6oPHWWjGprQ== X-Received: by 2002:adf:e38d:0:b0:236:7217:827e with SMTP id e13-20020adfe38d000000b002367217827emr15088546wrm.652.1666903489637; Thu, 27 Oct 2022 13:44:49 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id n3-20020a5d6b83000000b00236644228besm1968739wrx.40.2022.10.27.13.44.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Oct 2022 13:44:49 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , "David S. Miller" , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Jakub Kicinski , Leonard Crestez , Paolo Abeni , Salam Noureddine , Shuah Khan , netdev@vger.kernel.org, linux-crypto@vger.kernel.org Subject: [PATCH v3 36/36] selftests/fcnal-test.sh: Add TCP-AO tests Date: Thu, 27 Oct 2022 21:43:47 +0100 Message-Id: <20221027204347.529913-37-dima@arista.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221027204347.529913-1-dima@arista.com> References: <20221027204347.529913-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 These are basic TCP-AO functionality tests, more detailed coverage with functional testing is done by selftests/net/tcp_ao library and binaries. Sample output: > TEST: Global server - ns-A IP [ OK ] > TEST: Global server - ns-A loopback IP [ OK ] > TEST: Device server - ns-A IP [ OK ] > TEST: No server - ns-A IP [ OK ] > TEST: No server - ns-A loopback IP [ OK ] > TEST: Client - ns-B IP [ OK ] > TEST: Client, device bind - ns-B IP [ OK ] > TEST: No server, unbound client - ns-B IP [ OK ] > TEST: No server, device client - ns-B IP [ OK ] > TEST: Client - ns-B loopback IP [ OK ] > TEST: Client, device bind - ns-B loopback IP [ OK ] > TEST: No server, unbound client - ns-B loopback IP [ OK ] > TEST: No server, device client - ns-B loopback IP [ OK ] > TEST: Global server, local connection - ns-A IP [ OK ] > TEST: Global server, local connection - ns-A loopback IP [ OK ] > TEST: Global server, local connection - loopback [ OK ] > TEST: Device server, unbound client, local connection - ns-A IP [ OK ] > TEST: Device server, unbound client, local connection - ns-A loopback IP [ OK ] > TEST: Device server, unbound client, local connection - loopback [ OK ] > TEST: Global server, device client, local connection - ns-A IP [ OK ] > TEST: Global server, device client, local connection - ns-A loopback IP [ OK ] > TEST: Global server, device client, local connection - loopback [ OK ] > TEST: Device server, device client, local connection - ns-A IP [ OK ] > TEST: No server, device client, local conn - ns-A IP [ OK ] > TEST: MD5: Single address config [ OK ] > TEST: MD5: Server no config, client uses password [ OK ] > TEST: MD5: Client uses wrong password [ OK ] > TEST: MD5: Client address does not match address configured with password [ OK ] > TEST: MD5: Prefix config [ OK ] > TEST: MD5: Prefix config, client uses wrong password [ OK ] > TEST: MD5: Prefix config, client address not in configured prefix [ OK ] > TEST: TCP-AO [hmac(sha1):12]: Single address config [ OK ] > TEST: TCP-AO [hmac(sha1):12]: Server no config, client uses password [ OK ] > TEST: TCP-AO [hmac(sha1):12]: Client uses wrong password [ OK ] > TEST: TCP-AO [cmac(aes128):12]: Single address config [ OK ] > TEST: TCP-AO [cmac(aes128):12]: Server no config, client uses password [ OK ] > TEST: TCP-AO [cmac(aes128):12]: Client uses wrong password [ OK ] > TEST: TCP-AO [hmac(rmd160):12]: Single address config [ OK ] > TEST: TCP-AO [hmac(rmd160):12]: Server no config, client uses password [ OK ] > TEST: TCP-AO [hmac(rmd160):12]: Client uses wrong password [ OK ] > TEST: TCP-AO [hmac(sha512):12]: Single address config [ OK ] > TEST: TCP-AO [hmac(sha512):12]: Server no config, client uses password [ OK ] > TEST: TCP-AO [hmac(sha512):12]: Client uses wrong password [ OK ] > TEST: TCP-AO [hmac(sha384):12]: Single address config [ OK ] > TEST: TCP-AO [hmac(sha384):12]: Server no config, client uses password [ OK ] > TEST: TCP-AO [hmac(sha384):12]: Client uses wrong password [ OK ] > TEST: TCP-AO [hmac(sha256):12]: Single address config [ OK ] > TEST: TCP-AO [hmac(sha256):12]: Server no config, client uses password [ OK ] > TEST: TCP-AO [hmac(sha256):12]: Client uses wrong password [ OK ] > TEST: TCP-AO [hmac(md5):12]: Single address config [ OK ] > TEST: TCP-AO [hmac(md5):12]: Server no config, client uses password [ OK ] > TEST: TCP-AO [hmac(md5):12]: Client uses wrong password [ OK ] > TEST: TCP-AO [hmac(sha224):12]: Single address config [ OK ] > TEST: TCP-AO [hmac(sha224):12]: Server no config, client uses password [ OK ] > TEST: TCP-AO [hmac(sha224):12]: Client uses wrong password [ OK ] > TEST: TCP-AO [hmac(sha3-512):12]: Single address config [ OK ] > TEST: TCP-AO [hmac(sha3-512):12]: Server no config, client uses password [ OK ] > TEST: TCP-AO [hmac(sha3-512):12]: Client uses wrong password [ OK ] > TEST: TCP-AO: Client address does not match address configured with password [ OK ] > TEST: TCP-AO: Prefix config [ OK ] > TEST: TCP-AO: Prefix config, client uses wrong password [ OK ] > TEST: TCP-AO: Prefix config, client address not in configured prefix [ OK ] > TEST: TCP-AO: Different key ids [ OK ] > TEST: TCP-AO: Wrong keyid [ OK ] > TEST: TCP-AO [cmac(aes128):16]: Single address config [ OK ] > TEST: TCP-AO [cmac(aes128):16]: Server no config, client uses password [ OK ] > TEST: TCP-AO [cmac(aes128):16]: Client uses wrong password [ OK ] > TEST: TCP-AO [hmac(sha1):16]: Single address config [ OK ] > TEST: TCP-AO [hmac(sha1):16]: Server no config, client uses password [ OK ] > TEST: TCP-AO [hmac(sha1):16]: Client uses wrong password [ OK ] > TEST: TCP-AO [cmac(aes128):4]: Single address config [ OK ] > TEST: TCP-AO [cmac(aes128):4]: Server no config, client uses password [ OK ] > TEST: TCP-AO [cmac(aes128):4]: Client uses wrong password [ OK ] > TEST: TCP-AO [hmac(sha1):4]: Single address config [ OK ] > TEST: TCP-AO [hmac(sha1):4]: Server no config, client uses password [ OK ] > TEST: TCP-AO [hmac(sha1):4]: Client uses wrong password [ OK ] > TEST: TCP-AO: add MD5 and TCP-AO for the same peer address [ OK ] > TEST: TCP-AO: MD5 and TCP-AO on connect() [ OK ] > TEST: TCP-AO: Exclude TCP options [ OK ] Signed-off-by: Dmitry Safonov --- tools/testing/selftests/net/fcnal-test.sh | 239 ++++++++++++++++++++++ 1 file changed, 239 insertions(+) diff --git a/tools/testing/selftests/net/fcnal-test.sh b/tools/testing/selftests/net/fcnal-test.sh index d4516c755858..95718516b234 100755 --- a/tools/testing/selftests/net/fcnal-test.sh +++ b/tools/testing/selftests/net/fcnal-test.sh @@ -76,6 +76,12 @@ BCAST_IP=255.255.255.255 MD5_PW=abc123 MD5_WRONG_PW=abc1234 +AO_PW=abc123 +AO_WRONG_PW=abc1234 +AO_HASH_ALGOS="hmac(sha1) cmac(aes128)" +AO_HASH_ALGOS+=" hmac(rmd160) hmac(sha512)" +AO_HASH_ALGOS+=" hmac(sha384) hmac(sha256) hmac(md5)" +AO_HASH_ALGOS+=" hmac(sha224) hmac(sha3-512)" MCAST=ff02::1 # set after namespace create @@ -900,6 +906,123 @@ ipv4_tcp_md5_novrf() log_test $? 2 "MD5: Prefix config, client address not in configured prefix" } +# +# TCP-AO tests without VRF +# +ipv4_tcp_ao_algos() +{ + # basic use case + log_start + run_cmd nettest -s -T 100:100 --tcpao_algo=$1 --tcpao_maclen=$2 \ + -X ${AO_PW} -m ${NSB_IP} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -T 100:100 --tcpao_algo=$1 \ + --tcpao_maclen=$2 -X ${AO_PW} + log_test $? 0 "TCP-AO [$1:$2]: Single address config" + + # client sends TCP-AO, server not configured + log_start + show_hint "Should timeout due to TCP-AO password mismatch" + run_cmd nettest -s & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -T 100:100 --tcpao_algo=$1 \ + --tcpao_maclen=$2 -X ${AO_PW} + log_test $? 2 "TCP-AO [$1:$2]: Server no config, client uses password" + + # wrong password + log_start + show_hint "Should timeout since client uses wrong password" + run_cmd nettest -s -T 100:100 --tcpao_algo=$1 --tcpao_maclen=$2 \ + -X ${AO_PW} -m ${NSB_IP} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -T 100:100 --tcpao_algo=$1 \ + --tcpao_maclen=$2 -X ${AO_WRONG_PW} + log_test $? 2 "TCP-AO [$1:$2]: Client uses wrong password" +} + +ipv4_tcp_ao_novrf() +{ + # + # single address + # + for i in $AO_HASH_ALGOS ; do + ipv4_tcp_ao_algos $i 12 + done + + # client from different address + log_start + show_hint "Should timeout due to TCP-AO address mismatch" + run_cmd nettest -s -T 100:100 -X ${AO_PW} -m ${NSB_LO_IP} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -T 100:100 -X ${AO_PW} + log_test $? 2 "TCP-AO: Client address does not match address configured with password" + + # client in prefix + log_start + run_cmd nettest -s -T 100:100 -X ${AO_PW} -m ${NS_NET} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -T 100:100 -X ${AO_PW} + log_test $? 0 "TCP-AO: Prefix config" + + # client in prefix, wrong password + log_start + show_hint "Should timeout since client uses wrong password" + run_cmd nettest -s -T 100:100 -X ${AO_PW} -m ${NS_NET} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -T 100:100 -X ${AO_WRONG_PW} + log_test $? 2 "TCP-AO: Prefix config, client uses wrong password" + + # client outside of prefix + log_start + show_hint "Should timeout due to address out of TCP-AO prefix mismatch" + run_cmd nettest -s -T 100:100 -X ${AO_PW} -m ${NS_NET} & + sleep 1 + run_cmd_nsb nettest -c ${NSB_LO_IP} -r ${NSA_IP} -T 100:100 -X ${AO_PW} + log_test $? 2 "TCP-AO: Prefix config, client address not in configured prefix" + + # TCP-AO more specific tests + # sendid != rcvid + log_start + run_cmd nettest -s -T 100:101 -X ${AO_PW} -m ${NSB_IP} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -T 101:100 -X ${AO_PW} + log_test $? 0 "TCP-AO: Different key ids" + + # Wrong keyid + log_start + show_hint "Should timeout due to a wrong keyid" + run_cmd nettest -s -T 100:100 -X ${AO_PW} -m ${NSB_IP} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -T 101:101 -X ${AO_PW} + log_test $? 2 "TCP-AO: Wrong keyid" + + # Variable maclen + ipv4_tcp_ao_algos "cmac(aes128)" 16 + ipv4_tcp_ao_algos "hmac(sha1)" 16 + ipv4_tcp_ao_algos "cmac(aes128)" 4 + ipv4_tcp_ao_algos "hmac(sha1)" 4 + + # MD5 and TCP-AO for the same peer + log_start + run_cmd nettest -s -T 100:100 -M -X ${AO_PW} -m ${NSB_IP} + log_test $? 1 "TCP-AO: add MD5 and TCP-AO for the same peer address" + + # Connect with both TCP-AO and MD5 on the socket + log_start + show_hint "Should fail to connect with both MD5 and TCP-AO on the socket" + run_cmd nettest -s -T 100:100 -M -X ${AO_PW} -m ${NSB_IP} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -T 100:100 -M -X ${AO_PW} + log_test $? 1 "TCP-AO: MD5 and TCP-AO on connect()" + + # Exclude TCP options + log_start + run_cmd nettest -s -T 100:101 -X ${AO_PW} -m ${NSB_IP} --tcpao_excopts & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -T 101:100 -X ${AO_PW} --tcpao_excopts + log_test $? 0 "TCP-AO: Exclude TCP options" +} + # # MD5 tests with VRF # @@ -1217,6 +1340,7 @@ ipv4_tcp_novrf() log_test_addr ${a} $? 1 "No server, device client, local conn" ipv4_tcp_md5_novrf + ipv4_tcp_ao_novrf } ipv4_tcp_vrf() @@ -2511,6 +2635,120 @@ ipv6_tcp_md5_novrf() log_test $? 2 "MD5: Prefix config, client address not in configured prefix" } +ipv6_tcp_ao_algos() +{ + # basic use case + log_start + run_cmd nettest -6 -s -T 100:100 --tcpao_algo=$1 --tcpao_maclen=$2 \ + -X ${AO_PW} -m ${NSB_IP6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -T 100:100 --tcpao_algo=$1 \ + --tcpao_maclen=$2 -X ${AO_PW} + log_test $? 0 "TCP-AO [$1:$2]: Single address config" + + # client sends TCP-AO, server not configured + log_start + show_hint "Should timeout since server does not have TCP-AO auth" + run_cmd nettest -6 -s & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -T 100:100 --tcpao_algo=$1 \ + --tcpao_maclen=$2 -X ${AO_PW} + log_test $? 2 "TCP-AO [$1:$2]: Server no config, client uses password" + + # wrong password + log_start + show_hint "Should timeout since client uses wrong password" + run_cmd nettest -6 -s -T 100:100 --tcpao_algo=$1 --tcpao_maclen=$2 \ + -X ${AO_PW} -m ${NSB_IP6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -T 100:100 --tcpao_algo=$1 \ + --tcpao_maclen=$2 -X ${AO_WRONG_PW} + log_test $? 2 "TCP-AO [$1:$2]: Client uses wrong password" +} + +ipv6_tcp_ao_novrf() +{ + # + # single address + # + for i in $AO_HASH_ALGOS ; do + ipv6_tcp_ao_algos $i 12 + done + + # client from different address + log_start + show_hint "Should timeout since server config differs from client" + run_cmd nettest -6 -s -T 100:100 -X ${AO_PW} -m ${NSB_LO_IP6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -T 100:100 -X ${AO_PW} + log_test $? 2 "TCP-AO: Client address does not match address configured with password" + + # client in prefix + log_start + run_cmd nettest -6 -s -T 100:100 -X ${AO_PW} -m ${NS_NET6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -T 100:100 -X ${AO_PW} + log_test $? 0 "TCP-AO: Prefix config" + + # client in prefix, wrong password + log_start + show_hint "Should timeout since client uses wrong password" + run_cmd nettest -6 -s -T 100:100 -X ${AO_PW} -m ${NS_NET6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -T 100:100 -X ${AO_WRONG_PW} + log_test $? 2 "TCP-AO: Prefix config, client uses wrong password" + + # client outside of prefix + log_start + show_hint "Should timeout since client address is outside of prefix" + run_cmd nettest -6 -s -T 100:100 -X ${AO_PW} -m ${NS_NET6} & + sleep 1 + run_cmd_nsb nettest -6 -c ${NSB_LO_IP6} -r ${NSA_IP6} -T 100:100 -X ${AO_PW} + log_test $? 2 "TCP-AO: Prefix config, client address not in configured prefix" + + # TCP-AO more specific tests + # sendid != rcvid + log_start + run_cmd nettest -6 -s -T 100:101 -X ${AO_PW} -m ${NSB_IP6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -T 101:100 -X ${AO_PW} + log_test $? 0 "TCP-AO: Different key ids" + + # Wrong keyid + log_start + show_hint "Should timeout due to a wrong keyid" + run_cmd nettest -6 -s -T 100:100 -X ${AO_PW} -m ${NSB_IP6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -T 101:101 -X ${AO_PW} + log_test $? 2 "TCP-AO: Wrong keyid" + + # Variable maclen + ipv6_tcp_ao_algos "cmac(aes128)" 16 + ipv6_tcp_ao_algos "hmac(sha1)" 16 + ipv6_tcp_ao_algos "cmac(aes128)" 4 + ipv6_tcp_ao_algos "hmac(sha1)" 4 + + # MD5 and TCP-AO for the same peer + log_start + run_cmd nettest -6 -s -T 100:100 -M -X ${AO_PW} -m ${NSB_IP6} + log_test $? 1 "TCP-AO: add MD5 and TCP-AO for the same peer address" + + # Connect with both TCP-AO and MD5 on the socket + log_start + show_hint "Should fail to connect with both MD5 and TCP-AO on the socket" + run_cmd nettest -6 -s -T 100:100 -M -X ${AO_PW} -m ${NSB_IP6} & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -T 100:100 -M -X ${AO_PW} + log_test $? 1 "TCP-AO: MD5 and TCP-AO on connect()" + + # Exclude TCP options + log_start + run_cmd nettest -6 -s -T 100:101 -X ${AO_PW} -m ${NSB_IP6} --tcpao_excopts & + sleep 1 + run_cmd_nsb nettest -6 -r ${NSA_IP6} -T 101:100 -X ${AO_PW} --tcpao_excopts + log_test $? 0 "TCP-AO: Exclude TCP options" +} + # # MD5 tests with VRF # @@ -2773,6 +3011,7 @@ ipv6_tcp_novrf() done ipv6_tcp_md5_novrf + ipv6_tcp_ao_novrf } ipv6_tcp_vrf()