1 /* $OpenBSD: iocmd-limit.c,v 1.2 2023/07/10 17:45:17 anton Exp $ */ 2 3 /* 4 * Copyright (c) 2023 Alexandr Nedvedicky <sashan@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/ioctl.h> 21 #include <sys/socket.h> 22 #include <sys/stat.h> 23 24 #include <net/if.h> 25 #include <netinet/in.h> 26 #include <net/pfvar.h> 27 28 #include <err.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <limits.h> 32 #include <string.h> 33 #include <unistd.h> 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <stdint.h> 38 39 #define REGRESS_ANCHOR "regress" 40 41 static void 42 usage(const char *progname) 43 { 44 fprintf(stderr, 45 "%s -c iocmd [-i iterations ]\n" 46 "\t-c iocmd to test, currently DIOCGETRULES " 47 "and DIOCXEND are supported\n" 48 "\t-i number of iterations is 1 by default\n", progname); 49 exit(1); 50 } 51 52 static int 53 do_DIOCGETRULES_test(int dev) 54 { 55 struct pfioc_rule pr; 56 int rv; 57 58 memset(&pr, 0, sizeof(pr)); 59 memcpy(pr.anchor, REGRESS_ANCHOR, sizeof(REGRESS_ANCHOR)); 60 pr.rule.action = PF_PASS; 61 62 if ((rv = ioctl(dev, DIOCGETRULES, &pr)) == -1) { 63 /* 64 * we expect to see EBUSY anything else is odd and we should 65 * exit right away. 66 */ 67 if (errno != EBUSY) 68 err(1, "%s DIOCGETRULES", __func__); 69 } 70 71 return (rv); 72 } 73 74 static int 75 result_DIOCGETRULES(unsigned int iterations, unsigned int limit) 76 { 77 int rv; 78 /* 79 * DIOCGETRULES must see EBUSY before iterations reach limit 80 * to conclude test is successful. 81 */ 82 rv = (iterations < limit) ? 0 : 1; 83 if (rv) 84 printf( 85 "DIOCGETRULES could obtain %u tickets, reaching the limit " 86 "of %u tickets\n", 87 iterations, limit); 88 89 return (rv); 90 } 91 92 static int 93 do_DIOCXEND_test(int dev) 94 { 95 struct pfioc_rule pr; 96 int rv; 97 98 memset(&pr, 0, sizeof(pr)); 99 memcpy(pr.anchor, REGRESS_ANCHOR, sizeof(REGRESS_ANCHOR)); 100 pr.rule.action = PF_PASS; 101 102 if ((rv = ioctl(dev, DIOCGETRULES, &pr)) == -1) 103 warn("%s DIOCGETRULES", __func__); 104 else if ((rv = ioctl(dev, DIOCXEND, &pr.ticket)) == -1) 105 warn("%s DIOCXEND", __func__); 106 107 return (rv); 108 } 109 110 static int 111 result_DIOCXEND(unsigned int iterations, unsigned int limit) 112 { 113 int rv; 114 /* 115 * failing to reach limit when also closing tickets 116 * using DIOXXEND is an error. 117 */ 118 rv = (iterations < limit) ? 1 : 0; 119 if (rv) 120 printf( 121 "Although test is is using DIOCXEND it still" 122 "hits limit (%u)\n", iterations); 123 return (rv); 124 } 125 126 static struct iocmd_test { 127 const char *iocmd_name; 128 int (*iocmd_test)(int); 129 int (*iocmd_result)(unsigned int, unsigned int); 130 } iocmd_test_tab[] = { 131 { "DIOCGETRULES", do_DIOCGETRULES_test, result_DIOCGETRULES }, 132 { "DIOCXEND", do_DIOCXEND_test, result_DIOCXEND }, 133 { NULL, NULL } 134 }; 135 136 static struct iocmd_test * 137 parse_iocmd_name(const char *iocmd_name) 138 { 139 int i = 0; 140 141 while (iocmd_test_tab[i].iocmd_name != NULL) { 142 if (strcasecmp(iocmd_test_tab[i].iocmd_name, iocmd_name) == 0) 143 break; 144 i++; 145 } 146 147 return ((iocmd_test_tab[i].iocmd_name == NULL) ? 148 NULL : &iocmd_test_tab[i]); 149 } 150 151 int 152 main(int argc, char *const argv[]) 153 { 154 const char *errstr = NULL; 155 unsigned int iterations = 1; 156 unsigned int i = 0; 157 int dev; 158 int c; 159 struct iocmd_test *test_iocmd = NULL; 160 161 while ((c = getopt(argc, argv, "i:c:")) != -1) { 162 switch (c) { 163 case 'i': 164 iterations = strtonum(optarg, 1, UINT32_MAX, &errstr); 165 if (errstr != NULL) { 166 fprintf(stderr, 167 "%s: number of iteration (-i %s) " 168 "is invalid: %s\n", 169 argv[0], optarg, errstr); 170 usage(argv[0]); 171 } 172 break; 173 case 'c': 174 test_iocmd = parse_iocmd_name(optarg); 175 if (test_iocmd == NULL) { 176 fprintf(stderr, "%s invalid iocmd: %s\n", 177 argv[0], optarg); 178 usage(argv[0]); 179 } 180 break; 181 default: 182 usage(argv[0]); 183 } 184 } 185 186 if (test_iocmd == NULL) { 187 fprintf(stderr, "%s -c option is required\n", argv[0]); 188 usage(argv[0]); 189 } 190 191 dev = open("/dev/pf", O_RDONLY); 192 if (dev < 0) 193 err(1, "open(\"dev/pf\")"); 194 195 while (i < iterations) { 196 if (test_iocmd->iocmd_test(dev) != 0) 197 break; 198 i++; 199 } 200 201 return (test_iocmd->iocmd_result(i, iterations)); 202 } 203