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
usage(const char * progname)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
do_DIOCGETRULES_test(int dev)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
result_DIOCGETRULES(unsigned int iterations,unsigned int limit)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
do_DIOCXEND_test(int dev)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
result_DIOCXEND(unsigned int iterations,unsigned int limit)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 *
parse_iocmd_name(const char * iocmd_name)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
main(int argc,char * const argv[])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