xref: /openbsd-src/regress/sys/net/pf_trans/iocmd-limit.c (revision b6a86f14c6598dcc721d84c18dbd2e97378970e7)
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