xref: /netbsd-src/usr.sbin/npf/npftest/npftest.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: npftest.c,v 1.13 2013/11/08 00:38:26 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/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 
27 static bool verbose, quiet;
28 
29 __dead static void
30 usage(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
52 describe_tests(void)
53 {
54 	printf(	"nbuf\tbasic npf mbuf handling\n"
55 		"bpf\tBPF coprocessor\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 
63 static bool
64 result(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 
75 static void
76 load_npf_config_ifs(prop_dictionary_t dbg_dict)
77 {
78 	prop_array_t iflist = prop_dictionary_get(dbg_dict, "interfaces");
79 	prop_object_iterator_t it = prop_array_iterator(iflist);
80 	prop_dictionary_t ifdict;
81 
82 	while ((ifdict = prop_object_iterator_next(it)) != NULL) {
83 		const char *ifname = NULL;
84 
85 		prop_dictionary_get_cstring_nocopy(ifdict, "name", &ifname);
86 		(void)rumpns_npf_test_addif(ifname, true, verbose);
87 	}
88 	prop_object_iterator_release(it);
89 }
90 
91 static void
92 load_npf_config(const char *config)
93 {
94 	prop_dictionary_t npf_dict, dbg_dict;
95 	void *xml;
96 	int error;
97 
98 	/* Read the configuration from the specified file. */
99 	npf_dict = prop_dictionary_internalize_from_file(config);
100 	if (!npf_dict) {
101 		err(EXIT_FAILURE, "prop_dictionary_internalize_from_file");
102 	}
103 	xml = prop_dictionary_externalize(npf_dict);
104 
105 	/* Inspect the debug data.  Create the interfaces, if any. */
106 	dbg_dict = prop_dictionary_get(npf_dict, "debug");
107 	if (dbg_dict) {
108 		load_npf_config_ifs(dbg_dict);
109 	}
110 	prop_object_release(npf_dict);
111 
112 	/* Pass the XML configuration for NPF kernel component to load. */
113 	error = rumpns_npf_test_load(xml);
114 	if (error) {
115 		errx(EXIT_FAILURE, "npf_test_load: %s\n", strerror(error));
116 	}
117 	free(xml);
118 
119 	if (verbose) {
120 		printf("Loaded NPF config at '%s'\n", config);
121 	}
122 }
123 
124 /*
125  * Need to override for cprng_fast32(), since RUMP uses arc4random() for it.
126  */
127 uint32_t
128 arc4random(void)
129 {
130 	return random();
131 }
132 
133 int
134 main(int argc, char **argv)
135 {
136 	bool test, ok, fail, tname_matched;
137 	char *benchmark, *config, *interface, *stream, *testname;
138 	unsigned nthreads = 0;
139 	ifnet_t *ifp = NULL;
140 	int ch;
141 
142 	benchmark = NULL;
143 	test = false;
144 
145 	tname_matched = false;
146 	testname = NULL;
147 	config = NULL;
148 	interface = NULL;
149 	stream = NULL;
150 
151 	verbose = false;
152 	quiet = false;
153 
154 	while ((ch = getopt(argc, argv, "b:qvc:i:s:tT:Lp:")) != -1) {
155 		switch (ch) {
156 		case 'b':
157 			benchmark = optarg;
158 			break;
159 		case 'q':
160 			quiet = true;
161 			break;
162 		case 'v':
163 			verbose = true;
164 			break;
165 		case 'c':
166 			config = optarg;
167 			break;
168 		case 'i':
169 			interface = optarg;
170 			break;
171 		case 's':
172 			stream = optarg;
173 			break;
174 		case 't':
175 			test = true;
176 			break;
177 		case 'T':
178 			test = true;
179 			testname = optarg;
180 			break;
181 		case 'L':
182 			describe_tests();
183 			break;
184 		case 'p':
185 			/* Note: RUMP_NCPU must be high enough. */
186 			if ((nthreads = atoi(optarg)) > 0 &&
187 			    getenv("RUMP_NCPU") == NULL) {
188 				char *val;
189 				asprintf(&val, "%u", nthreads + 1);
190 				setenv("RUMP_NCPU", val, 1);
191 				free(val);
192 			}
193 			break;
194 		default:
195 			usage();
196 		}
197 	}
198 
199 	/*
200 	 * Either benchmark or test.  If stream analysis, then the
201 	 * interface should be specified.  If benchmark, then the
202 	 * config should be loaded.
203 	 */
204 	if ((benchmark != NULL) == test && (stream && !interface)) {
205 		usage();
206 	}
207 	if (benchmark && (!config || !nthreads)) {
208 		errx(EXIT_FAILURE, "missing config for the benchmark or "
209 		    "invalid thread count");
210 	}
211 
212 	/* XXX rn_init */
213 	extern int rumpns_max_keylen;
214 	rumpns_max_keylen = 1;
215 
216 	rump_init();
217 	rump_schedule();
218 
219 	rumpns_npf_test_init();
220 
221 	if (config) {
222 		load_npf_config(config);
223 	}
224 	if (interface && (ifp = rumpns_npf_test_getif(interface)) == 0) {
225 		errx(EXIT_FAILURE, "failed to find the interface");
226 	}
227 
228 	srandom(1);
229 	fail = false;
230 
231 	if (test) {
232 		if (!testname || strcmp("nbuf", testname) == 0) {
233 			ok = rumpns_npf_nbuf_test(verbose);
234 			fail |= result("nbuf", ok);
235 			tname_matched = true;
236 		}
237 
238 		if (!testname || strcmp("bpf", testname) == 0) {
239 			ok = rumpns_npf_bpf_test(verbose);
240 			fail |= result("bpf", ok);
241 			tname_matched = true;
242 		}
243 
244 		if (!testname || strcmp("table", testname) == 0) {
245 			ok = rumpns_npf_table_test(verbose);
246 			fail |= result("table", ok);
247 			tname_matched = true;
248 		}
249 
250 		if (!testname || strcmp("state", testname) == 0) {
251 			ok = rumpns_npf_state_test(verbose);
252 			fail |= result("state", ok);
253 			tname_matched = true;
254 		}
255 	}
256 
257 	if (test && config) {
258 		if (!testname || strcmp("rule", testname) == 0) {
259 			ok = rumpns_npf_rule_test(verbose);
260 			fail |= result("rule", ok);
261 			tname_matched = true;
262 		}
263 
264 		if (!testname || strcmp("nat", testname) == 0) {
265 			ok = rumpns_npf_nat_test(verbose);
266 			fail |= result("nat", ok);
267 			tname_matched = true;
268 		}
269 	}
270 
271 	if (stream) {
272 		process_stream(stream, NULL, ifp);
273 	}
274 
275 	if (benchmark) {
276 		if (strcmp("rule", benchmark) == 0) {
277 			rumpns_npf_test_conc(false, nthreads);
278 		}
279 		if (strcmp("state", benchmark) == 0) {
280 			rumpns_npf_test_conc(true, nthreads);
281 		}
282 	}
283 
284 	rump_unschedule();
285 
286 	if (testname && !tname_matched)
287 		errx(EXIT_FAILURE, "test \"%s\" unknown", testname);
288 
289 	return fail ? EXIT_FAILURE : EXIT_SUCCESS;
290 }
291