npftest.c revision 1.22 1 /* $NetBSD: npftest.c,v 1.22 2018/09/29 14:41:36 rmind Exp $ */
2
3 /*
4 * NPF testing framework.
5 *
6 * Public Domain.
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdbool.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <assert.h>
15 #include <fcntl.h>
16 #include <err.h>
17
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 #if !defined(_NPF_STANDALONE)
21 #include <sys/ioctl.h>
22 #include <net/if.h>
23 #include <arpa/inet.h>
24
25 #include <dnv.h>
26 #include <nv.h>
27
28 #include <rump/rump.h>
29 #include <rump/rump_syscalls.h>
30 #endif
31
32 #include <cdbw.h>
33
34 #include "npftest.h"
35
36 static bool verbose, quiet;
37
38 __dead static void
39 usage(const char *progname)
40 {
41 printf("usage:\n"
42 " %s [ -q | -v ] [ -c <config> ] "
43 "[ -i <interface> ] < -b | -t | -s file >\n"
44 " %s -T <testname> -c <config>\n"
45 " %s -L\n"
46 "where:\n"
47 "\t-b: benchmark\n"
48 "\t-t: regression test\n"
49 "\t-T <testname>: specific test\n"
50 "\t-s <file>: pcap stream\n"
51 "\t-c <config>: NPF configuration file\n"
52 "\t-i <interface>: primary interface\n"
53 "\t-L: list testnames and description for -T\n"
54 "\t-q: quiet mode\n"
55 "\t-v: verbose mode\n",
56 progname, progname, progname);
57 exit(EXIT_FAILURE);
58 }
59
60 __dead static void
61 describe_tests(void)
62 {
63 printf( "nbuf\tbasic npf mbuf handling\n"
64 "bpf\tBPF coprocessor\n"
65 "table\ttable handling\n"
66 "state\tstate handling and processing\n"
67 "rule\trule processing\n"
68 "nat\tNAT rule processing\n");
69 exit(EXIT_SUCCESS);
70 }
71
72 static bool
73 result(const char *testcase, bool ok)
74 {
75 if (!quiet) {
76 printf("NPF %-10s\t%s\n", testcase, ok ? "OK" : "fail");
77 }
78 if (verbose) {
79 puts("-----");
80 }
81 return !ok;
82 }
83
84 static void
85 load_npf_config(const char *fpath)
86 {
87 struct stat sb;
88 int error, fd;
89 size_t len;
90 void *buf;
91
92 /*
93 * Read the configuration from the specified file.
94 */
95 if ((fd = open(fpath, O_RDONLY)) == -1) {
96 err(EXIT_FAILURE, "open");
97 }
98 if (fstat(fd, &sb) == -1) {
99 err(EXIT_FAILURE, "fstat");
100 }
101 len = sb.st_size;
102 buf = mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
103 if (buf == MAP_FAILED) {
104 err(EXIT_FAILURE, "mmap");
105 }
106 close(fd);
107
108 /*
109 * Load the NPF configuration.
110 */
111 error = rumpns_npf_test_load(buf, len, verbose);
112 if (error) {
113 errx(EXIT_FAILURE, "npf_test_load: %s\n", strerror(error));
114 }
115 munmap(buf, len);
116
117 if (verbose) {
118 printf("Loaded NPF config at '%s'\n", fpath);
119 }
120 }
121
122 static void *
123 generate_test_cdb(size_t *size)
124 {
125 in_addr_t addr;
126 struct cdbw *cdbw;
127 struct stat sb;
128 char sfn[32];
129 int alen, fd;
130 void *cdb;
131
132 if ((cdbw = cdbw_open()) == NULL) {
133 err(EXIT_FAILURE, "cdbw_open");
134 }
135 strlcpy(sfn, "/tmp/npftest_cdb.XXXXXX", sizeof(sfn));
136 if ((fd = mkstemp(sfn)) == -1) {
137 err(EXIT_FAILURE, "mkstemp");
138 }
139 unlink(sfn);
140
141 addr = inet_addr("192.168.1.1"), alen = sizeof(struct in_addr);
142 if (cdbw_put(cdbw, &addr, alen, &addr, alen) == -1)
143 err(EXIT_FAILURE, "cdbw_put");
144
145 addr = inet_addr("10.0.0.2"), alen = sizeof(struct in_addr);
146 if (cdbw_put(cdbw, &addr, alen, &addr, alen) == -1)
147 err(EXIT_FAILURE, "cdbw_put");
148
149 if (cdbw_output(cdbw, fd, "npf-table-cdb", NULL) == -1) {
150 err(EXIT_FAILURE, "cdbw_output");
151 }
152 cdbw_close(cdbw);
153
154 if (fstat(fd, &sb) == -1) {
155 err(EXIT_FAILURE, "fstat");
156 }
157 if ((cdb = mmap(NULL, sb.st_size, PROT_READ,
158 MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
159 err(EXIT_FAILURE, "mmap");
160 }
161 close(fd);
162
163 *size = sb.st_size;
164 return cdb;
165 }
166
167 static void
168 npf_kern_init(void)
169 {
170 #if !defined(_NPF_STANDALONE)
171 /* XXX rn_init */
172 extern int rumpns_max_keylen;
173 rumpns_max_keylen = 1;
174
175 rump_init();
176 rump_schedule();
177 #endif
178 }
179
180 static void
181 npf_kern_fini(void)
182 {
183 #if !defined(_NPF_STANDALONE)
184 rump_unschedule();
185 #endif
186 }
187
188 int
189 main(int argc, char **argv)
190 {
191 bool test, ok, fail, tname_matched;
192 char *benchmark, *config, *interface, *stream, *testname;
193 unsigned nthreads = 0;
194 ifnet_t *ifp = NULL;
195 int ch;
196
197 benchmark = NULL;
198 test = false;
199
200 tname_matched = false;
201 testname = NULL;
202 config = NULL;
203 interface = NULL;
204 stream = NULL;
205
206 verbose = false;
207 quiet = false;
208
209 while ((ch = getopt(argc, argv, "b:qvc:i:s:tT:Lp:")) != -1) {
210 switch (ch) {
211 case 'b':
212 benchmark = optarg;
213 break;
214 case 'q':
215 quiet = true;
216 break;
217 case 'v':
218 verbose = true;
219 break;
220 case 'c':
221 config = optarg;
222 break;
223 case 'i':
224 interface = optarg;
225 break;
226 case 's':
227 stream = optarg;
228 break;
229 case 't':
230 test = true;
231 break;
232 case 'T':
233 test = true;
234 testname = optarg;
235 break;
236 case 'L':
237 describe_tests();
238 break;
239 case 'p':
240 /* Note: RUMP_NCPU must be high enough. */
241 if ((nthreads = atoi(optarg)) > 0 &&
242 getenv("RUMP_NCPU") == NULL) {
243 static char nthr[64];
244 sprintf(nthr, "%u", nthreads + 1);
245 setenv("RUMP_NCPU", nthr, 1);
246 }
247 break;
248 default:
249 usage(argv[0]);
250 }
251 }
252
253 /*
254 * Either benchmark or test. If stream analysis, then the
255 * interface should be specified. If benchmark, then the
256 * config should be loaded.
257 */
258 if ((benchmark != NULL) == test && (stream && !interface)) {
259 usage(argv[0]);
260 }
261 if (benchmark && (!config || !nthreads)) {
262 errx(EXIT_FAILURE, "missing config for the benchmark or "
263 "invalid thread count");
264 }
265
266 /*
267 * Initialise the NPF kernel component.
268 */
269 npf_kern_init();
270 rumpns_npf_test_init(inet_pton, inet_ntop, random);
271
272 if (config) {
273 load_npf_config(config);
274 }
275 if (interface && (ifp = rumpns_npf_test_getif(interface)) == 0) {
276 errx(EXIT_FAILURE, "failed to find the interface");
277 }
278
279 srandom(1);
280 fail = false;
281
282 if (test) {
283 if (!testname || strcmp("nbuf", testname) == 0) {
284 ok = rumpns_npf_nbuf_test(verbose);
285 fail |= result("nbuf", ok);
286 tname_matched = true;
287 }
288
289 if (!testname || strcmp("bpf", testname) == 0) {
290 ok = rumpns_npf_bpf_test(verbose);
291 fail |= result("bpf", ok);
292 tname_matched = true;
293 }
294
295 if (!testname || strcmp("table", testname) == 0) {
296 void *cdb;
297 size_t len;
298
299 cdb = generate_test_cdb(&len);
300 ok = rumpns_npf_table_test(verbose, cdb, len);
301 fail |= result("table", ok);
302 tname_matched = true;
303 munmap(cdb, len);
304 }
305
306 if (!testname || strcmp("state", testname) == 0) {
307 ok = rumpns_npf_state_test(verbose);
308 fail |= result("state", ok);
309 tname_matched = true;
310 }
311 }
312
313 if (test && config) {
314 if (!testname || strcmp("rule", testname) == 0) {
315 ok = rumpns_npf_rule_test(verbose);
316 fail |= result("rule", ok);
317 tname_matched = true;
318 }
319
320 if (!testname || strcmp("nat", testname) == 0) {
321 srandom(1);
322 ok = rumpns_npf_nat_test(verbose);
323 fail |= result("nat", ok);
324 tname_matched = true;
325 }
326 }
327
328 if (stream) {
329 process_stream(stream, NULL, ifp);
330 }
331
332 if (benchmark) {
333 if (strcmp("rule", benchmark) == 0) {
334 rumpns_npf_test_conc(false, nthreads);
335 }
336 if (strcmp("state", benchmark) == 0) {
337 rumpns_npf_test_conc(true, nthreads);
338 }
339 }
340
341 rumpns_npf_test_fini();
342 npf_kern_fini();
343
344 if (testname && !tname_matched)
345 errx(EXIT_FAILURE, "test \"%s\" unknown", testname);
346
347 return fail ? EXIT_FAILURE : EXIT_SUCCESS;
348 }
349