1*18140ab7Sriastradh /* $NetBSD: npftest.c,v 1.27 2023/08/08 10:35:48 riastradh Exp $ */
29ffbe6bdSrmind
39ffbe6bdSrmind /*
49ffbe6bdSrmind * NPF testing framework.
59ffbe6bdSrmind *
69ffbe6bdSrmind * Public Domain.
79ffbe6bdSrmind */
89ffbe6bdSrmind
99ffbe6bdSrmind #include <stdio.h>
109ffbe6bdSrmind #include <stdlib.h>
119ffbe6bdSrmind #include <stdbool.h>
12fb07f475Srmind #include <string.h>
139ffbe6bdSrmind #include <unistd.h>
149ffbe6bdSrmind #include <assert.h>
15fb07f475Srmind #include <fcntl.h>
16fb07f475Srmind #include <err.h>
17fb07f475Srmind
18ffcdc4afSrmind #include <sys/mman.h>
19f75d79ebSchristos #include <sys/stat.h>
20f75d79ebSchristos #if !defined(_NPF_STANDALONE)
21fb07f475Srmind #include <sys/ioctl.h>
22fb07f475Srmind #include <net/if.h>
23fb07f475Srmind #include <arpa/inet.h>
249ffbe6bdSrmind
2539013e66Srmind #include <dnv.h>
2639013e66Srmind #include <nv.h>
2776f0658bSpooka
289ffbe6bdSrmind #include <rump/rump.h>
29fb07f475Srmind #include <rump/rump_syscalls.h>
30f75d79ebSchristos #endif
319ffbe6bdSrmind
32ffcdc4afSrmind #include <cdbw.h>
33ffcdc4afSrmind
349ffbe6bdSrmind #include "npftest.h"
359ffbe6bdSrmind
36fb07f475Srmind static bool verbose, quiet;
379ffbe6bdSrmind
38c4eabd7bSjoerg __dead static void
usage(const char * progname)39f75d79ebSchristos usage(const char *progname)
409ffbe6bdSrmind {
41a76a87c0Smartin printf("usage:\n"
42a76a87c0Smartin " %s [ -q | -v ] [ -c <config> ] "
4363f44833Srmind "[ -i <interface> ] < -b | -t | -s file >\n"
44a76a87c0Smartin " %s -T <testname> -c <config>\n"
45a76a87c0Smartin " %s -L\n"
46a76a87c0Smartin "where:\n"
47fb07f475Srmind "\t-b: benchmark\n"
48fb07f475Srmind "\t-t: regression test\n"
49a76a87c0Smartin "\t-T <testname>: specific test\n"
50fb07f475Srmind "\t-s <file>: pcap stream\n"
5163f44833Srmind "\t-c <config>: NPF configuration file\n"
5263f44833Srmind "\t-i <interface>: primary interface\n"
53a76a87c0Smartin "\t-L: list testnames and description for -T\n"
5463f44833Srmind "\t-q: quiet mode\n"
55fb07f475Srmind "\t-v: verbose mode\n",
56f75d79ebSchristos progname, progname, progname);
57fb07f475Srmind exit(EXIT_FAILURE);
589ffbe6bdSrmind }
599ffbe6bdSrmind
60c4eabd7bSjoerg __dead static void
describe_tests(void)61a76a87c0Smartin describe_tests(void)
62a76a87c0Smartin {
63a76a87c0Smartin printf( "nbuf\tbasic npf mbuf handling\n"
647b5edfdcSrmind "bpf\tBPF coprocessor\n"
65a76a87c0Smartin "table\ttable handling\n"
66a76a87c0Smartin "state\tstate handling and processing\n"
67b899bfd9Srmind "gc\tconnection G/C\n"
68a76a87c0Smartin "rule\trule processing\n"
69a76a87c0Smartin "nat\tNAT rule processing\n");
70a76a87c0Smartin exit(EXIT_SUCCESS);
71a76a87c0Smartin }
72a76a87c0Smartin
73b8c27e4aSrmind static bool
result(const char * testcase,bool ok)74fb07f475Srmind result(const char *testcase, bool ok)
759ffbe6bdSrmind {
769ffbe6bdSrmind if (!quiet) {
77fb07f475Srmind printf("NPF %-10s\t%s\n", testcase, ok ? "OK" : "fail");
789ffbe6bdSrmind }
799ffbe6bdSrmind if (verbose) {
809ffbe6bdSrmind puts("-----");
819ffbe6bdSrmind }
82b8c27e4aSrmind return !ok;
839ffbe6bdSrmind }
849ffbe6bdSrmind
85fb07f475Srmind static void
load_npf_config(const char * fpath)8639013e66Srmind load_npf_config(const char *fpath)
87fb07f475Srmind {
8839013e66Srmind struct stat sb;
8939013e66Srmind int error, fd;
9039013e66Srmind size_t len;
9139013e66Srmind void *buf;
92fb07f475Srmind
9339013e66Srmind /*
9439013e66Srmind * Read the configuration from the specified file.
9539013e66Srmind */
9639013e66Srmind if ((fd = open(fpath, O_RDONLY)) == -1) {
9739013e66Srmind err(EXIT_FAILURE, "open");
98fb07f475Srmind }
9939013e66Srmind if (fstat(fd, &sb) == -1) {
10039013e66Srmind err(EXIT_FAILURE, "fstat");
101fb07f475Srmind }
10239013e66Srmind len = sb.st_size;
10339013e66Srmind buf = mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
10439013e66Srmind if (buf == MAP_FAILED) {
10539013e66Srmind err(EXIT_FAILURE, "mmap");
106fb07f475Srmind }
10739013e66Srmind close(fd);
10863f44833Srmind
10939013e66Srmind /*
11039013e66Srmind * Load the NPF configuration.
11139013e66Srmind */
11239013e66Srmind error = rumpns_npf_test_load(buf, len, verbose);
113fb07f475Srmind if (error) {
114f75d79ebSchristos errx(EXIT_FAILURE, "npf_test_load: %s\n", strerror(error));
115fb07f475Srmind }
11639013e66Srmind munmap(buf, len);
117fb07f475Srmind
118fb07f475Srmind if (verbose) {
11939013e66Srmind printf("Loaded NPF config at '%s'\n", fpath);
120fb07f475Srmind }
121fb07f475Srmind }
122fb07f475Srmind
123ffcdc4afSrmind static void *
generate_test_cdb(size_t * size)124ffcdc4afSrmind generate_test_cdb(size_t *size)
125ffcdc4afSrmind {
126ffcdc4afSrmind in_addr_t addr;
127ffcdc4afSrmind struct cdbw *cdbw;
128ffcdc4afSrmind struct stat sb;
129ffcdc4afSrmind char sfn[32];
130ffcdc4afSrmind int alen, fd;
131ffcdc4afSrmind void *cdb;
132ffcdc4afSrmind
133ffcdc4afSrmind if ((cdbw = cdbw_open()) == NULL) {
134ffcdc4afSrmind err(EXIT_FAILURE, "cdbw_open");
135ffcdc4afSrmind }
136ffcdc4afSrmind strlcpy(sfn, "/tmp/npftest_cdb.XXXXXX", sizeof(sfn));
137ffcdc4afSrmind if ((fd = mkstemp(sfn)) == -1) {
138ffcdc4afSrmind err(EXIT_FAILURE, "mkstemp");
139ffcdc4afSrmind }
140ffcdc4afSrmind unlink(sfn);
141ffcdc4afSrmind
142ffcdc4afSrmind addr = inet_addr("192.168.1.1"), alen = sizeof(struct in_addr);
143ffcdc4afSrmind if (cdbw_put(cdbw, &addr, alen, &addr, alen) == -1)
144ffcdc4afSrmind err(EXIT_FAILURE, "cdbw_put");
145ffcdc4afSrmind
146ffcdc4afSrmind addr = inet_addr("10.0.0.2"), alen = sizeof(struct in_addr);
147ffcdc4afSrmind if (cdbw_put(cdbw, &addr, alen, &addr, alen) == -1)
148ffcdc4afSrmind err(EXIT_FAILURE, "cdbw_put");
149ffcdc4afSrmind
150*18140ab7Sriastradh if (cdbw_output(cdbw, fd, "npf-table-cdb", NULL) == -1) {
151ffcdc4afSrmind err(EXIT_FAILURE, "cdbw_output");
152ffcdc4afSrmind }
153ffcdc4afSrmind cdbw_close(cdbw);
154ffcdc4afSrmind
155ffcdc4afSrmind if (fstat(fd, &sb) == -1) {
156ffcdc4afSrmind err(EXIT_FAILURE, "fstat");
157ffcdc4afSrmind }
158ffcdc4afSrmind if ((cdb = mmap(NULL, sb.st_size, PROT_READ,
159ffcdc4afSrmind MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
160ffcdc4afSrmind err(EXIT_FAILURE, "mmap");
161ffcdc4afSrmind }
162ffcdc4afSrmind close(fd);
163ffcdc4afSrmind
164ffcdc4afSrmind *size = sb.st_size;
165ffcdc4afSrmind return cdb;
166ffcdc4afSrmind }
167ffcdc4afSrmind
168f75d79ebSchristos static void
npf_kern_init(void)169f75d79ebSchristos npf_kern_init(void)
170f75d79ebSchristos {
171f75d79ebSchristos #if !defined(_NPF_STANDALONE)
172f75d79ebSchristos /* XXX rn_init */
173f75d79ebSchristos extern int rumpns_max_keylen;
174f75d79ebSchristos rumpns_max_keylen = 1;
175f75d79ebSchristos
176f75d79ebSchristos rump_init();
177f75d79ebSchristos rump_schedule();
178f75d79ebSchristos #endif
179f75d79ebSchristos }
180f75d79ebSchristos
181f75d79ebSchristos static void
npf_kern_fini(void)182f75d79ebSchristos npf_kern_fini(void)
183f75d79ebSchristos {
184f75d79ebSchristos #if !defined(_NPF_STANDALONE)
185f75d79ebSchristos rump_unschedule();
186f75d79ebSchristos #endif
187f75d79ebSchristos }
188f75d79ebSchristos
1899ffbe6bdSrmind int
main(int argc,char ** argv)1909ffbe6bdSrmind main(int argc, char **argv)
1919ffbe6bdSrmind {
192a99ac628Srmind bool test, ok, fail, tname_matched;
193a99ac628Srmind char *benchmark, *config, *interface, *stream, *testname;
194a4841052Srmind unsigned nthreads = 0;
195a79812eaSrmind ifnet_t *ifp = NULL;
196a79812eaSrmind int ch;
1979ffbe6bdSrmind
198a99ac628Srmind benchmark = NULL;
199fb07f475Srmind test = false;
200fb07f475Srmind
201a76a87c0Smartin tname_matched = false;
202a76a87c0Smartin testname = NULL;
203fb07f475Srmind config = NULL;
20463f44833Srmind interface = NULL;
205fb07f475Srmind stream = NULL;
206fb07f475Srmind
2079ffbe6bdSrmind verbose = false;
2089ffbe6bdSrmind quiet = false;
2099ffbe6bdSrmind
210a99ac628Srmind while ((ch = getopt(argc, argv, "b:qvc:i:s:tT:Lp:")) != -1) {
2119ffbe6bdSrmind switch (ch) {
2129ffbe6bdSrmind case 'b':
213a99ac628Srmind benchmark = optarg;
2149ffbe6bdSrmind break;
2159ffbe6bdSrmind case 'q':
2169ffbe6bdSrmind quiet = true;
2179ffbe6bdSrmind break;
2189ffbe6bdSrmind case 'v':
2199ffbe6bdSrmind verbose = true;
2209ffbe6bdSrmind break;
221fb07f475Srmind case 'c':
222fb07f475Srmind config = optarg;
223fb07f475Srmind break;
224fb07f475Srmind case 'i':
22563f44833Srmind interface = optarg;
226fb07f475Srmind break;
227fb07f475Srmind case 's':
228fb07f475Srmind stream = optarg;
229fb07f475Srmind break;
230fb07f475Srmind case 't':
231fb07f475Srmind test = true;
232fb07f475Srmind break;
233a76a87c0Smartin case 'T':
234a76a87c0Smartin test = true;
235a76a87c0Smartin testname = optarg;
236a76a87c0Smartin break;
237a76a87c0Smartin case 'L':
238a76a87c0Smartin describe_tests();
239a4841052Srmind break;
240a4841052Srmind case 'p':
241a4841052Srmind /* Note: RUMP_NCPU must be high enough. */
242a4841052Srmind if ((nthreads = atoi(optarg)) > 0 &&
243a4841052Srmind getenv("RUMP_NCPU") == NULL) {
244f75d79ebSchristos static char nthr[64];
245f75d79ebSchristos sprintf(nthr, "%u", nthreads + 1);
246f75d79ebSchristos setenv("RUMP_NCPU", nthr, 1);
247a4841052Srmind }
248a4841052Srmind break;
2499ffbe6bdSrmind default:
250f75d79ebSchristos usage(argv[0]);
2519ffbe6bdSrmind }
2529ffbe6bdSrmind }
2539ffbe6bdSrmind
25463f44833Srmind /*
255a4841052Srmind * Either benchmark or test. If stream analysis, then the
256a4841052Srmind * interface should be specified. If benchmark, then the
257a4841052Srmind * config should be loaded.
25863f44833Srmind */
259a99ac628Srmind if ((benchmark != NULL) == test && (stream && !interface)) {
260f75d79ebSchristos usage(argv[0]);
261fb07f475Srmind }
262a4841052Srmind if (benchmark && (!config || !nthreads)) {
263a4841052Srmind errx(EXIT_FAILURE, "missing config for the benchmark or "
264a4841052Srmind "invalid thread count");
265a4841052Srmind }
266fb07f475Srmind
267f75d79ebSchristos /*
268f75d79ebSchristos * Initialise the NPF kernel component.
269f75d79ebSchristos */
270f75d79ebSchristos npf_kern_init();
271068cee29Srmind rumpns_npf_test_init(inet_pton, inet_ntop, random);
272e0cfa502Srmind
273fb07f475Srmind if (config) {
274fb07f475Srmind load_npf_config(config);
275fb07f475Srmind }
276a79812eaSrmind if (interface && (ifp = rumpns_npf_test_getif(interface)) == 0) {
27763f44833Srmind errx(EXIT_FAILURE, "failed to find the interface");
27863f44833Srmind }
27963f44833Srmind
280b8c27e4aSrmind fail = false;
281fb07f475Srmind
282fb07f475Srmind if (test) {
283a76a87c0Smartin if (!testname || strcmp("nbuf", testname) == 0) {
2849ffbe6bdSrmind ok = rumpns_npf_nbuf_test(verbose);
285b8c27e4aSrmind fail |= result("nbuf", ok);
286a76a87c0Smartin tname_matched = true;
287a76a87c0Smartin }
2889ffbe6bdSrmind
2894e592132Srmind if (!testname || strcmp("bpf", testname) == 0) {
2904e592132Srmind ok = rumpns_npf_bpf_test(verbose);
2914e592132Srmind fail |= result("bpf", ok);
2924e592132Srmind tname_matched = true;
2934e592132Srmind }
2944e592132Srmind
295a76a87c0Smartin if (!testname || strcmp("table", testname) == 0) {
296ffcdc4afSrmind void *cdb;
297ffcdc4afSrmind size_t len;
298ffcdc4afSrmind
299ffcdc4afSrmind cdb = generate_test_cdb(&len);
300ffcdc4afSrmind ok = rumpns_npf_table_test(verbose, cdb, len);
301b8c27e4aSrmind fail |= result("table", ok);
302a76a87c0Smartin tname_matched = true;
303ffcdc4afSrmind munmap(cdb, len);
304a76a87c0Smartin }
30579afee64Srmind
306a76a87c0Smartin if (!testname || strcmp("state", testname) == 0) {
30779afee64Srmind ok = rumpns_npf_state_test(verbose);
308b8c27e4aSrmind fail |= result("state", ok);
309a76a87c0Smartin tname_matched = true;
310a76a87c0Smartin }
3113d9a792dSrmind
312b899bfd9Srmind if (!testname || strcmp("gc", testname) == 0) {
313b899bfd9Srmind ok = rumpns_npf_gc_test(verbose);
314b899bfd9Srmind fail |= result("gc", ok);
3153d9a792dSrmind tname_matched = true;
3163d9a792dSrmind }
317fb07f475Srmind }
318fb07f475Srmind
31963f44833Srmind if (test && config) {
320a76a87c0Smartin if (!testname || strcmp("rule", testname) == 0) {
32163f44833Srmind ok = rumpns_npf_rule_test(verbose);
322b8c27e4aSrmind fail |= result("rule", ok);
323a76a87c0Smartin tname_matched = true;
324a76a87c0Smartin }
32563f44833Srmind
326a76a87c0Smartin if (!testname || strcmp("nat", testname) == 0) {
327f75d79ebSchristos srandom(1);
32863f44833Srmind ok = rumpns_npf_nat_test(verbose);
329b8c27e4aSrmind fail |= result("nat", ok);
330a76a87c0Smartin tname_matched = true;
331a76a87c0Smartin }
332fb07f475Srmind }
33363f44833Srmind
33463f44833Srmind if (stream) {
335a79812eaSrmind process_stream(stream, NULL, ifp);
336fb07f475Srmind }
3379ffbe6bdSrmind
338a4841052Srmind if (benchmark) {
339a99ac628Srmind if (strcmp("rule", benchmark) == 0) {
340a99ac628Srmind rumpns_npf_test_conc(false, nthreads);
341a99ac628Srmind }
342a99ac628Srmind if (strcmp("state", benchmark) == 0) {
343a99ac628Srmind rumpns_npf_test_conc(true, nthreads);
344a99ac628Srmind }
345a4841052Srmind }
346a4841052Srmind
347f75d79ebSchristos rumpns_npf_test_fini();
348f75d79ebSchristos npf_kern_fini();
3499ffbe6bdSrmind
350a76a87c0Smartin if (testname && !tname_matched)
351a76a87c0Smartin errx(EXIT_FAILURE, "test \"%s\" unknown", testname);
352a76a87c0Smartin
353b8c27e4aSrmind return fail ? EXIT_FAILURE : EXIT_SUCCESS;
3549ffbe6bdSrmind }
355