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