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