/* $OpenBSD: iocmd-limit.c,v 1.2 2023/07/10 17:45:17 anton 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)); }