From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=none (p=none dis=none) header.from=gibson.dropbear.id.au Authentication-Results: passt.top; dkim=pass (2048-bit key; secure) header.d=gibson.dropbear.id.au header.i=@gibson.dropbear.id.au header.a=rsa-sha256 header.s=202512 header.b=Z5ef2jZk; dkim-atps=neutral Received: from mail.ozlabs.org (mail.ozlabs.org [IPv6:2404:9400:2221:ea00::3]) by passt.top (Postfix) with ESMTPS id D72A25A0623 for ; Tue, 27 Jan 2026 09:39:59 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gibson.dropbear.id.au; s=202512; t=1769503195; bh=Ywt+MfczAth+cVnULigb0S++v7yu8Y3nFODsgZXu7WY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Z5ef2jZkXyApPM2I2rg1mDoVMGdI/gVsda9eFl6N7f/7Zhp1jdm5TwSlJAVZ8KiXk lEMBHnXR0pKr8yBIXXE8NwSuLNk9YCyD4xfd1/EmDc4aKPj0WKiqE4MLqGhRSg+fAj rh187WQasZGEHHyj3AevZl+JX4jAaoK5DWmmUOxU03qI9mgvu0V26uBrUISUJZ2DAN iNMB51huiPcGt3i3xlcRmaHt56Hr3MPoJmspvfnsXDJKv+UjlkMB4hw0eX6kCWOluP 17c3062VGD+crf0QbGq3oMuyaq28N6BewVMH+zD8pOCjO1T2BAcd03Ly4uhYWZOzrn icNKpgRBam3ng== Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4f0f3b1T1jz4w0L; Tue, 27 Jan 2026 19:39:55 +1100 (AEDT) From: David Gibson To: passt-dev@passt.top, Stefano Brivio Subject: [PATCH 1/3] doc: Add test program verifying socket RST behaviour Date: Tue, 27 Jan 2026 19:39:51 +1100 Message-ID: <20260127083953.824556-2-david@gibson.dropbear.id.au> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260127083953.824556-1-david@gibson.dropbear.id.au> References: <20260127083953.824556-1-david@gibson.dropbear.id.au> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: LU4DXAMXR4O3JH2AXKXADQIZ5JZPLZZY X-Message-ID-Hash: LU4DXAMXR4O3JH2AXKXADQIZ5JZPLZZY X-MailFrom: dgibson@gandalf.ozlabs.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: David Gibson X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Add a program to doc/platform-requirements testing / documenting the RST related behaviour of TCP sockets under various conditions. This doesn't fix bug 191 of itself, but documents the kernel side behaviour we'll be using in order to fix it. Link: https://bugs.passt.top/show_bug.cgi?id=191 Signed-off-by: David Gibson --- doc/platform-requirements/.gitignore | 1 + doc/platform-requirements/Makefile | 8 +- doc/platform-requirements/tcp-close-rst.c | 204 ++++++++++++++++++++++ 3 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 doc/platform-requirements/tcp-close-rst.c diff --git a/doc/platform-requirements/.gitignore b/doc/platform-requirements/.gitignore index f6272cf0..b2a0069a 100644 --- a/doc/platform-requirements/.gitignore +++ b/doc/platform-requirements/.gitignore @@ -1,4 +1,5 @@ /listen-vs-repair /reuseaddr-priority /recv-zero +/tcp-close-rst /udp-close-dup diff --git a/doc/platform-requirements/Makefile b/doc/platform-requirements/Makefile index 83930ef8..204341bb 100644 --- a/doc/platform-requirements/Makefile +++ b/doc/platform-requirements/Makefile @@ -3,8 +3,10 @@ # Copyright Red Hat # Author: David Gibson -TARGETS = reuseaddr-priority recv-zero udp-close-dup listen-vs-repair -SRCS = reuseaddr-priority.c recv-zero.c udp-close-dup.c listen-vs-repair.c +TARGETS = reuseaddr-priority recv-zero udp-close-dup listen-vs-repair \ + tcp-close-rst +SRCS = reuseaddr-priority.c recv-zero.c udp-close-dup.c listen-vs-repair.c \ + tcp-close-rst.c CFLAGS = -Wall all: cppcheck clang-tidy $(TARGETS:%=check-%) @@ -25,6 +27,7 @@ clang-tidy: clang-tidy --checks=*,\ -altera-id-dependent-backward-branch,\ -altera-unroll-loops,\ + -android-cloexec-accept,\ -bugprone-easily-swappable-parameters,\ -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,\ -concurrency-mt-unsafe,\ @@ -37,6 +40,7 @@ clang-tidy: -misc-include-cleaner,\ -modernize-macro-to-enum,\ -readability-braces-around-statements,\ + -readability-function-cognitive-complexity,\ -readability-identifier-length,\ -readability-isolate-declaration \ $(SRCS) diff --git a/doc/platform-requirements/tcp-close-rst.c b/doc/platform-requirements/tcp-close-rst.c new file mode 100644 index 00000000..0e508f67 --- /dev/null +++ b/doc/platform-requirements/tcp-close-rst.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* tcp-close-rst.c + * + * Check what operations on a TCP socket will trigger an RST. + * + * Copyright Red Hat + * Author: David Gibson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#define DSTPORT 13258U + +#define SRCADDR(n) \ + (0x7f000000U | (n) << 16U | (n) << 8U | 0x1U) + +#define BASENUM 100 + +/* 127.0.0.1:DSTPORT */ +static const struct sockaddr_in lo_dst = SOCKADDR_INIT(INADDR_LOOPBACK, DSTPORT); + +#define LINGER 0x01U +#define SHUT_CLIENT 0x02U +#define SHUT_SERVER 0x04U + +#define NUM_OPTIONS (SHUT_SERVER << 1U) + +static void client_close(int sl, unsigned flags) +{ + struct sockaddr_in src = SOCKADDR_INIT(SRCADDR(flags), 0); + struct linger linger0 = { + .l_onoff = 1, + .l_linger = 0, + }; + int sockerr, sc, sa; + socklen_t errlen = sizeof(sockerr); + + printf("Client close %u:%s%s%s\n", flags, + flags & LINGER ? " LINGER" : "", + flags & SHUT_CLIENT ? " SHUT_CLIENT" : "", + flags & SHUT_SERVER ? " SHUT_SERVER" : ""); + + sc = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sc < 0) + die("socket() for connect(): %s\n", strerror(errno)); + + if (bind(sc, (struct sockaddr *)&src, sizeof(src)) < 0) + die("bind() for connect: %s\n", strerror(errno)); + + if (connect(sc, (struct sockaddr *)&lo_dst, sizeof(lo_dst)) < 0) + die("connect(): %s\n", strerror(errno)); + + /* cppcheck-suppress [android-cloexec-accept,unmatchedSuppression] */ + sa = accept(sl, NULL, NULL); + if (sa < 0) + die("accept(): %s\n", strerror(errno)); + + if (flags & SHUT_SERVER) + if (shutdown(sa, SHUT_WR) < 0) + die("shutdown() server: %s\n", strerror(errno)); + + if (flags & SHUT_CLIENT) + if (shutdown(sc, SHUT_WR) < 0) + die("shutdown() client: %s\n", strerror(errno)); + + if (flags & LINGER) + if (setsockopt(sc, SOL_SOCKET, SO_LINGER, + &linger0, sizeof(linger0)) < 0) + die("SO_LINGER: %s\n", strerror(errno)); + + close(sc); + + if (getsockopt(sa, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) < 0) + die("SO_ERROR: %s\n", strerror(errno)); + + if (errlen != sizeof(sockerr)) + die("SO_ERROR: bad option length\n"); + + printf("Server error: %s\n", strerror(sockerr)); + + if (flags & LINGER) { + if (!(flags & SHUT_SERVER) || !(flags & SHUT_CLIENT)) { + if (sockerr == 0) + die("No error after abrupt close(), no RST?\n"); + } else { + if (sockerr != 0) + die("Error after full shutdown, bogus RST?\n"); + } + } + + close(sa); +} + +static void server_close(int sl, unsigned flags) +{ + struct sockaddr_in src = SOCKADDR_INIT(SRCADDR(flags), 0); + struct linger linger0 = { + .l_onoff = 1, + .l_linger = 0, + }; + int sockerr, sc, sa; + socklen_t errlen = sizeof(sockerr); + + printf("Server close %u:%s%s%s\n", flags, + flags & LINGER ? " LINGER" : "", + flags & SHUT_CLIENT ? " SHUT_CLIENT" : "", + flags & SHUT_SERVER ? " SHUT_SERVER" : ""); + + sc = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sc < 0) + die("socket() for connect(): %s\n", strerror(errno)); + + if (bind(sc, (struct sockaddr *)&src, sizeof(src)) < 0) + die("bind() for connect: %s\n", strerror(errno)); + + if (connect(sc, (struct sockaddr *)&lo_dst, sizeof(lo_dst)) < 0) + die("connect(): %s\n", strerror(errno)); + + /* cppcheck-suppress [android-cloexec-accept,unmatchedSuppression] */ + sa = accept(sl, NULL, NULL); + if (sa < 0) + die("accept(): %s\n", strerror(errno)); + + if (flags & SHUT_SERVER) + if (shutdown(sa, SHUT_WR) < 0) + die("shutdown() server: %s\n", strerror(errno)); + + if (flags & SHUT_CLIENT) + if (shutdown(sc, SHUT_WR) < 0) + die("shutdown() client: %s\n", strerror(errno)); + + if (flags & LINGER) + if (setsockopt(sa, SOL_SOCKET, SO_LINGER, + &linger0, sizeof(linger0)) < 0) + die("SO_LINGER: %s\n", strerror(errno)); + + close(sa); + + if (getsockopt(sc, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) < 0) + die("SO_ERROR: %s\n", strerror(errno)); + + if (errlen != sizeof(sockerr)) + die("SO_ERROR: bad option length\n"); + + printf("Client error: %s\n", strerror(sockerr)); + + if (flags & LINGER) { + if (!(flags & SHUT_SERVER) || !(flags & SHUT_CLIENT)) { + if (sockerr == 0) + die("No error after abrupt close(), no RST?\n"); + } else { + if (sockerr != 0) + die("Error after full shutdown, bogus RST?\n"); + } + } + + close(sc); +} + +int main(int argc, char *argv[]) +{ + unsigned flags; + int y = 1; + int sl; + + (void)argc; + (void)argv; + + sl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sl < 0) + die("socket() for listen: %s\n", strerror(errno)); + + if (setsockopt(sl, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y)) < 0) + die("SO_REUSEADDR for listen: %s\n", strerror(errno)); + + if (bind(sl, (struct sockaddr *)&lo_dst, sizeof(lo_dst)) < 0) + die("bind() for listen: %s\n", strerror(errno)); + + if (listen(sl, 1) < 0) + die("listen(): %s\n", strerror(errno)); + + printf("Listening on port %u\n", DSTPORT); + + for (flags = 0; flags < NUM_OPTIONS; flags++) { + client_close(sl, flags); + server_close(sl, flags); + } + + close(sl); + exit(0); +} -- 2.52.0