From e8c6dd7b2e4518b25d38102c2bb5d8c5f6c5f308 Mon Sep 17 00:00:00 2001 From: sashan Date: Thu, 6 Jul 2023 19:55:57 +0000 Subject: [PATCH] adding regression tests: to verify limit on tickets progam can retrieve by DIOCXGETRULES. Ad tests which verify DIOCXEND works as expected, that program can release ticket obtained by earlier call to DIOCGETRULES improvements from anton@ OK anton@, bluhm@ --- regress/sys/net/pf_trans/Makefile | 33 ++++ regress/sys/net/pf_trans/dev-limit.c | 148 ++++++++++++++++++ regress/sys/net/pf_trans/iocmd-limit.c | 202 +++++++++++++++++++++++++ 3 files changed, 383 insertions(+) create mode 100644 regress/sys/net/pf_trans/Makefile create mode 100644 regress/sys/net/pf_trans/dev-limit.c create mode 100644 regress/sys/net/pf_trans/iocmd-limit.c diff --git a/regress/sys/net/pf_trans/Makefile b/regress/sys/net/pf_trans/Makefile new file mode 100644 index 00000000000..9bcbc02142b --- /dev/null +++ b/regress/sys/net/pf_trans/Makefile @@ -0,0 +1,33 @@ +# $OpenBSD: Makefile,v 1.1 2023/07/06 19:55:57 sashan Exp $ + +PROGS+= dev-limit +PROGS+= iocmd-limit + +CFLAGS+= -Wall + +REGRESS_ROOT_TARGETS= run-regress-dev-limit \ + run-regress-iocmd-limit + + +# +# Create 2048 processes. Each child process will attempt +# open /dev/pf and waith for 10secs on success. If +# /dev/pf can not be opened child exits immediately. +# There should be 1023 children, which could open /dev/pf. +# +run-regress-dev-limit: + ${SUDO} ./dev-limit -c 2048 -s 1023 -t 10 + +# +# Open 1024 tickets for DIOCGETRULES without closing them. +# Program expects to see EBUSY and returns 0 in that case. +# +# Open and close 1024 tickets. Program closes ticket by +# DIOCXEND before issuing next DIOCGETRULES command. +# Program expects to see no error and returns 0 in tat case. +# +run-regress-iocmd-limit: + ${SUDO} ./iocmd-limit -c DIOCGETRULES -i 513 + ${SUDO} ./iocmd-limit -c DIOCXEND -i 1024 + +.include diff --git a/regress/sys/net/pf_trans/dev-limit.c b/regress/sys/net/pf_trans/dev-limit.c new file mode 100644 index 00000000000..b766a1e5a13 --- /dev/null +++ b/regress/sys/net/pf_trans/dev-limit.c @@ -0,0 +1,148 @@ +/* $OpenBSD: dev-limit.c,v 1.1 2023/07/06 19:55:58 sashan Exp $ */ + +/* + * Copyright (c) 2023 Alexandr Nedvedicky + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static int sigchild; + +static void +usage(const char *progname) +{ + fprintf(stderr, + "%s [-d] [-s success_count] [-c child_count] [-t timeout]\n" + "if no options are specified program opens '/dev/pf'\n" + "and waits for 5s before it exits\n" + "\t-s how many children should successfully open /dev/pf\n" + "\t-c children to fork, each child opens /dev/pf\n" + "\t-t timeout in seconds each child should wait\n" + "after successfully opening /dev/pf. Child exits immediately\n" + "if /dev/pf can not be opened\n", progname); + exit(1); +} + +static void +handle_sigchild(int signum) +{ + if (signum == SIGCHLD) + sigchild = 1; +} + +static void +open_pf_and_exit(unsigned int sleep_time) +{ + if (open("/dev/pf", O_RDONLY) == -1) + exit(1); + + sleep(sleep_time); + exit(0); +} + +int +main(int argc, char *const argv[]) +{ + pid_t *pids; + unsigned int chld_count = 0; + unsigned int sleep_time = 5; + unsigned int expect_success = 0; + unsigned int success, errors, i; + const char *errstr, *sleep_arg; + int status; + int c; + + while ((c = getopt(argc, argv, "t:c:s:")) != -1) { + switch (c) { + case 't': + sleep_arg = (char *const)optarg; + sleep_time = strtonum(optarg, 1, 60, &errstr); + if (errstr != NULL) { + fprintf(stderr, + "%s invalid sleep time %s: %s, must be in " + "range <1, 60>\n", argv[0], errstr, optarg); + usage(argv[0]); + } + break; + case 'c': + chld_count = strtonum(optarg, 1, 32768, &errstr); + if (errstr != NULL) { + fprintf(stderr, + "%s invalid children count %s: %s, must be " + "in range <1, 32768>\n", argv[0], optarg, + errstr); + usage(argv[0]); + } + break; + case 's': + expect_success = strtonum(optarg, 0, 32768, &errstr); + if (errstr != NULL) { + fprintf(stderr, + "%s invalid expect success count %s: %s " + "must be in range <1, 32768>\n", argv[0], + optarg, errstr); + usage(argv[0]); + } + break; + default: + usage(argv[0]); + } + } + + if (chld_count == 0) + open_pf_and_exit(sleep_time); + + signal(SIGCHLD, handle_sigchild); + pids = (pid_t *)malloc(sizeof(pid_t) * chld_count); + if (pids == 0) + err(1, "%s malloc: ", argv[0]); + + i = 0; + while ((sigchild == 0) && (i < chld_count)) { + if ((pids[i++] = fork()) == 0) + execl(argv[0], argv[0], "-t", sleep_arg, NULL); + } + chld_count = i; + + success = 0; + errors = 0; + for (i = 0; i < chld_count; i++) { + waitpid(pids[i], &status, 0); + if (status == 0) + success++; + else + errors++; + } + + free(pids); + + if (success != expect_success) { + printf("Successful opens: %u\n", success); + printf("Failures: %u\n", errors); + printf("Expected opens: %u\n", expect_success); + printf("%u vs %u = %u + %u\n", + chld_count, errors + success, errors, success); + return (1); + } + + return (0); +} diff --git a/regress/sys/net/pf_trans/iocmd-limit.c b/regress/sys/net/pf_trans/iocmd-limit.c new file mode 100644 index 00000000000..53d197a5454 --- /dev/null +++ b/regress/sys/net/pf_trans/iocmd-limit.c @@ -0,0 +1,202 @@ +/* $OpenBSD: iocmd-limit.c,v 1.1 2023/07/06 19:55:58 sashan Exp $ */ + +/* + * Copyright (c) 2023 Alexandr Nedvedicky + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define REGRESS_ANCHOR "regress" + +static void +usage(const char *progname) +{ + fprintf(stderr, + "%s -c iocmd [-i iterations ]\n" + "\t-c iocmd to test, currently DIOCGETRULES " + "and DIOCXEND are supported\n" + "\t-i number of iterations is 1 by default\n", progname); + exit(1); +} + +static int +do_DIOCGETRULES_test(int dev) +{ + struct pfioc_rule pr; + int rv; + + memset(&pr, 0, sizeof(pr)); + memcpy(pr.anchor, REGRESS_ANCHOR, sizeof(REGRESS_ANCHOR)); + pr.rule.action = PF_PASS; + + if ((rv = ioctl(dev, DIOCGETRULES, &pr)) == -1) { + /* + * we expect to see EBUSY anything else is odd and we should + * exit right away. + */ + if (errno != EBUSY) + err(1, "%s DIOCGETRULES: ", __func__); + } + + return (rv); +} + +static int +result_DIOCGETRULES(unsigned int iterations, unsigned int limit) +{ + int rv; + /* + * DIOCGETRULES must see EBUSY before iterations reach limit + * to conclude test is successful. + */ + rv = (iterations < limit) ? 0 : 1; + if (rv) + printf( + "DIOCGETRULES could obtain %u tickets, reaching the limit " + "of %u tickets\n", + iterations, limit); + + return (rv); +} + +static int +do_DIOCXEND_test(int dev) +{ + struct pfioc_rule pr; + int rv; + + memset(&pr, 0, sizeof(pr)); + memcpy(pr.anchor, REGRESS_ANCHOR, sizeof(REGRESS_ANCHOR)); + pr.rule.action = PF_PASS; + + if ((rv = ioctl(dev, DIOCGETRULES, &pr)) == -1) + warn("%s DIOCGETRULES: ", __func__); + else if ((rv = ioctl(dev, DIOCXEND, &pr.ticket)) == -1) + warn("%s DIOCXEND: ", __func__); + + return (rv); +} + +static int +result_DIOCXEND(unsigned int iterations, unsigned int limit) +{ + int rv; + /* + * failing to reach limit when also closing tickets + * using DIOXXEND is an error. + */ + rv = (iterations < limit) ? 1 : 0; + if (rv) + printf( + "Although test is is using DIOCXEND it still" + "hits limit (%u)\n", iterations); + return (rv); +} + +static struct iocmd_test { + const char *iocmd_name; + int (*iocmd_test)(int); + int (*iocmd_result)(unsigned int, unsigned int); +} iocmd_test_tab[] = { + { "DIOCGETRULES", do_DIOCGETRULES_test, result_DIOCGETRULES }, + { "DIOCXEND", do_DIOCXEND_test, result_DIOCXEND }, + { NULL, NULL } +}; + +static struct iocmd_test * +parse_iocmd_name(const char *iocmd_name) +{ + int i = 0; + + while (iocmd_test_tab[i].iocmd_name != NULL) { + if (strcasecmp(iocmd_test_tab[i].iocmd_name, iocmd_name) == 0) + break; + i++; + } + + return ((iocmd_test_tab[i].iocmd_name == NULL) ? + NULL : &iocmd_test_tab[i]); +} + +int +main(int argc, char *const argv[]) +{ + const char *errstr = NULL; + unsigned int iterations = 1; + unsigned int i = 0; + int dev; + int c; + struct iocmd_test *test_iocmd = NULL; + + while ((c = getopt(argc, argv, "i:c:")) != -1) { + switch (c) { + case 'i': + iterations = strtonum(optarg, 1, UINT32_MAX, &errstr); + if (errstr != NULL) { + fprintf(stderr, + "%s: number of iteration (-i %s) " + "is invalid: %s\n", + argv[0], optarg, errstr); + usage(argv[0]); + } + break; + case 'c': + test_iocmd = parse_iocmd_name(optarg); + if (test_iocmd == NULL) { + fprintf(stderr, "%s invalid iocmd: %s\n", + argv[0], optarg); + usage(argv[0]); + } + break; + default: + usage(argv[0]); + } + } + + if (test_iocmd == NULL) { + fprintf(stderr, "%s -c option is required\n", argv[0]); + usage(argv[0]); + } + + dev = open("/dev/pf", O_RDONLY); + if (dev < 0) + err(1, "open(\"dev/pf\"): "); + + while (i < iterations) { + if (test_iocmd->iocmd_test(dev) != 0) + break; + i++; + } + + return (test_iocmd->iocmd_result(i, iterations)); +} -- 2.20.1