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