1/*	$NetBSD: npftest.c,v 1.3.2.4 2012/11/18 21:45:08 riz 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/ioctl.h>
19#include <net/if.h>
20#include <arpa/inet.h>
21
22#include <rump/rump.h>
23#include <rump/rump_syscalls.h>
24
25#include "npftest.h"
26
27static bool verbose, quiet;
28
29__dead static void
30usage(void)
31{
32	printf("usage:\n"
33	    "  %s [ -q | -v ] [ -c <config> ] "
34	        "[ -i <interface> ] < -b | -t | -s file >\n"
35	    "  %s -T <testname> -c <config>\n"
36	    "  %s -L\n"
37	    "where:\n"
38	    "\t-b: benchmark\n"
39	    "\t-t: regression test\n"
40	    "\t-T <testname>: specific test\n"
41	    "\t-s <file>: pcap stream\n"
42	    "\t-c <config>: NPF configuration file\n"
43	    "\t-i <interface>: primary interface\n"
44	    "\t-L: list testnames and description for -T\n"
45	    "\t-q: quiet mode\n"
46	    "\t-v: verbose mode\n",
47	    getprogname(), getprogname(), getprogname());
48	exit(EXIT_FAILURE);
49}
50
51__dead static void
52describe_tests(void)
53{
54	printf(	"nbuf\tbasic npf mbuf handling\n"
55		"processor\tncode processing\n"
56		"table\ttable handling\n"
57		"state\tstate handling and processing\n"
58		"rule\trule processing\n"
59		"nat\tNAT rule processing\n");
60	exit(EXIT_SUCCESS);
61}
62
63static bool
64result(const char *testcase, bool ok)
65{
66	if (!quiet) {
67		printf("NPF %-10s\t%s\n", testcase, ok ? "OK" : "fail");
68	}
69	if (verbose) {
70		puts("-----");
71	}
72	return !ok;
73}
74
75static void
76load_npf_config_ifs(prop_dictionary_t dbg_dict)
77{
78	prop_dictionary_t ifdict;
79	prop_object_iterator_t it;
80	prop_array_t iflist;
81
82	iflist = prop_dictionary_get(dbg_dict, "interfaces");
83	it = prop_array_iterator(iflist);
84	while ((ifdict = prop_object_iterator_next(it)) != NULL) {
85		const char *ifname;
86		unsigned if_idx;
87
88		prop_dictionary_get_cstring_nocopy(ifdict, "name", &ifname);
89		prop_dictionary_get_uint32(ifdict, "idx", &if_idx);
90		(void)rumpns_npf_test_addif(ifname, if_idx, verbose);
91	}
92	prop_object_iterator_release(it);
93}
94
95static void
96load_npf_config(const char *config)
97{
98	prop_dictionary_t npf_dict, dbg_dict;
99	void *xml;
100	int error;
101
102	/* Read the configuration from the specified file. */
103	npf_dict = prop_dictionary_internalize_from_file(config);
104	if (!npf_dict) {
105		err(EXIT_FAILURE, "prop_dictionary_internalize_from_file");
106	}
107	xml = prop_dictionary_externalize(npf_dict);
108
109	/* Inspect the debug data.  Create the interfaces, if any. */
110	dbg_dict = prop_dictionary_get(npf_dict, "debug");
111	if (dbg_dict) {
112		load_npf_config_ifs(dbg_dict);
113	}
114	prop_object_release(npf_dict);
115
116	/* Pass the XML configuration for NPF kernel component to load. */
117	error = rumpns_npf_test_load(xml);
118	if (error) {
119		errx(EXIT_FAILURE, "npf_test_load: %s\n", strerror(error));
120	}
121	free(xml);
122
123	if (verbose) {
124		printf("Loaded NPF config at '%s'\n", config);
125	}
126}
127
128/*
129 * Need to override for cprng_fast32(), since RUMP uses arc4random() for it.
130 */
131uint32_t
132arc4random(void)
133{
134	return random();
135}
136
137int
138main(int argc, char **argv)
139{
140	bool benchmark, test, ok, fail, tname_matched;
141	char *config, *interface, *stream, *testname;
142	int idx = -1, ch;
143
144	benchmark = false;
145	test = false;
146
147	tname_matched = false;
148	testname = NULL;
149	config = NULL;
150	interface = NULL;
151	stream = NULL;
152
153	verbose = false;
154	quiet = false;
155
156	while ((ch = getopt(argc, argv, "bqvc:i:s:tT:L")) != -1) {
157		switch (ch) {
158		case 'b':
159			benchmark = true;
160			break;
161		case 'q':
162			quiet = true;
163			break;
164		case 'v':
165			verbose = true;
166			break;
167		case 'c':
168			config = optarg;
169			break;
170		case 'i':
171			interface = optarg;
172			break;
173		case 's':
174			stream = optarg;
175			break;
176		case 't':
177			test = true;
178			break;
179		case 'T':
180			test = true;
181			testname = optarg;
182			break;
183		case 'L':
184			describe_tests();
185		default:
186			usage();
187		}
188	}
189
190	/*
191	 * Either benchmark or test.  If stream analysis, then the interface
192	 * is needed as well.
193	 */
194	if (benchmark == test && (stream && !interface)) {
195		usage();
196	}
197
198	/* XXX rn_init */
199	extern int rumpns_max_keylen;
200	rumpns_max_keylen = 1;
201
202	rump_init();
203	rump_schedule();
204
205	rumpns_npf_test_init();
206
207	if (config) {
208		load_npf_config(config);
209	}
210	if (interface && (idx = rumpns_npf_test_getif(interface)) == 0) {
211		errx(EXIT_FAILURE, "failed to find the interface");
212	}
213
214	srandom(1);
215	fail = false;
216
217	if (test) {
218		if (!testname || strcmp("nbuf", testname) == 0) {
219			ok = rumpns_npf_nbuf_test(verbose);
220			fail |= result("nbuf", ok);
221			tname_matched = true;
222		}
223
224		if (!testname || strcmp("processor", testname) == 0) {
225			ok = rumpns_npf_processor_test(verbose);
226			fail |= result("processor", ok);
227			tname_matched = true;
228		}
229
230		if (!testname || strcmp("table", testname) == 0) {
231			ok = rumpns_npf_table_test(verbose);
232			fail |= result("table", ok);
233			tname_matched = true;
234		}
235
236		if (!testname || strcmp("state", testname) == 0) {
237			ok = rumpns_npf_state_test(verbose);
238			fail |= result("state", ok);
239			tname_matched = true;
240		}
241	}
242
243	if (test && config) {
244		if (!testname || strcmp("rule", testname) == 0) {
245			ok = rumpns_npf_rule_test(verbose);
246			fail |= result("rule", ok);
247			tname_matched = true;
248		}
249
250		if (!testname || strcmp("nat", testname) == 0) {
251			ok = rumpns_npf_nat_test(verbose);
252			fail |= result("nat", ok);
253			tname_matched = true;
254		}
255	}
256
257	if (stream) {
258		process_stream(stream, NULL, idx);
259	}
260
261	rump_unschedule();
262
263	if (testname && !tname_matched)
264		errx(EXIT_FAILURE, "test \"%s\" unknown", testname);
265
266	return fail ? EXIT_FAILURE : EXIT_SUCCESS;
267}
268