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