npftest.c revision 1.13 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