xref: /netbsd-src/usr.sbin/npf/npftest/npftest.c (revision 18140ab7dae75bd3e5ebd44530a8f87b51bc7fbe)
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