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