diff options
Diffstat (limited to 'regress/sys')
-rw-r--r-- | regress/sys/net/pf_trans/Makefile | 33 | ||||
-rw-r--r-- | regress/sys/net/pf_trans/dev-limit.c | 148 | ||||
-rw-r--r-- | regress/sys/net/pf_trans/iocmd-limit.c | 202 |
3 files changed, 383 insertions, 0 deletions
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 <bsd.regress.mk> 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 <sashan@openbsd.org> + * + * 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 <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdlib.h> +#include <signal.h> +#include <err.h> +#include <sys/wait.h> + +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 <sashan@openbsd.org> + * + * 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 <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <net/pfvar.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <string.h> +#include <unistd.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#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)); +} |