npftest.c revision 1.9 1 /* $NetBSD: npftest.c,v 1.9 2013/09/19 01:04:45 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 "processor\tncode processing\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_dictionary_t ifdict;
79 prop_object_iterator_t it;
80 prop_array_t iflist;
81
82 iflist = prop_dictionary_get(dbg_dict, "interfaces");
83 it = prop_array_iterator(iflist);
84 while ((ifdict = prop_object_iterator_next(it)) != NULL) {
85 const char *ifname;
86 unsigned if_idx;
87
88 prop_dictionary_get_cstring_nocopy(ifdict, "name", &ifname);
89 prop_dictionary_get_uint32(ifdict, "idx", &if_idx);
90 (void)rumpns_npf_test_addif(ifname, if_idx, verbose);
91 }
92 prop_object_iterator_release(it);
93 }
94
95 static void
96 load_npf_config(const char *config)
97 {
98 prop_dictionary_t npf_dict, dbg_dict;
99 void *xml;
100 int error;
101
102 /* Read the configuration from the specified file. */
103 npf_dict = prop_dictionary_internalize_from_file(config);
104 if (!npf_dict) {
105 err(EXIT_FAILURE, "prop_dictionary_internalize_from_file");
106 }
107 xml = prop_dictionary_externalize(npf_dict);
108
109 /* Inspect the debug data. Create the interfaces, if any. */
110 dbg_dict = prop_dictionary_get(npf_dict, "debug");
111 if (dbg_dict) {
112 load_npf_config_ifs(dbg_dict);
113 }
114 prop_object_release(npf_dict);
115
116 /* Pass the XML configuration for NPF kernel component to load. */
117 error = rumpns_npf_test_load(xml);
118 if (error) {
119 errx(EXIT_FAILURE, "npf_test_load: %s\n", strerror(error));
120 }
121 free(xml);
122
123 if (verbose) {
124 printf("Loaded NPF config at '%s'\n", config);
125 }
126 }
127
128 /*
129 * Need to override for cprng_fast32(), since RUMP uses arc4random() for it.
130 */
131 uint32_t
132 arc4random(void)
133 {
134 return random();
135 }
136
137 int
138 main(int argc, char **argv)
139 {
140 bool benchmark, test, ok, fail, tname_matched;
141 char *config, *interface, *stream, *testname;
142 int idx = -1, ch;
143
144 benchmark = false;
145 test = false;
146
147 tname_matched = false;
148 testname = NULL;
149 config = NULL;
150 interface = NULL;
151 stream = NULL;
152
153 verbose = false;
154 quiet = false;
155
156 while ((ch = getopt(argc, argv, "bqvc:i:s:tT:L")) != -1) {
157 switch (ch) {
158 case 'b':
159 benchmark = true;
160 break;
161 case 'q':
162 quiet = true;
163 break;
164 case 'v':
165 verbose = true;
166 break;
167 case 'c':
168 config = optarg;
169 break;
170 case 'i':
171 interface = optarg;
172 break;
173 case 's':
174 stream = optarg;
175 break;
176 case 't':
177 test = true;
178 break;
179 case 'T':
180 test = true;
181 testname = optarg;
182 break;
183 case 'L':
184 describe_tests();
185 default:
186 usage();
187 }
188 }
189
190 /*
191 * Either benchmark or test. If stream analysis, then the interface
192 * is needed as well.
193 */
194 if (benchmark == test && (stream && !interface)) {
195 usage();
196 }
197
198 /* XXX rn_init */
199 extern int rumpns_max_keylen;
200 rumpns_max_keylen = 1;
201
202 rump_init();
203 rump_schedule();
204
205 rumpns_npf_test_init();
206
207 if (config) {
208 load_npf_config(config);
209 }
210 if (interface && (idx = rumpns_npf_test_getif(interface)) == 0) {
211 errx(EXIT_FAILURE, "failed to find the interface");
212 }
213
214 srandom(1);
215 fail = false;
216
217 if (test) {
218 if (!testname || strcmp("nbuf", testname) == 0) {
219 ok = rumpns_npf_nbuf_test(verbose);
220 fail |= result("nbuf", ok);
221 tname_matched = true;
222 }
223
224 if (!testname || strcmp("bpf", testname) == 0) {
225 ok = rumpns_npf_bpf_test(verbose);
226 fail |= result("bpf", ok);
227 tname_matched = true;
228 }
229
230 if (!testname || strcmp("processor", testname) == 0) {
231 ok = rumpns_npf_processor_test(verbose);
232 fail |= result("processor", ok);
233 tname_matched = true;
234 }
235
236 if (!testname || strcmp("table", testname) == 0) {
237 ok = rumpns_npf_table_test(verbose);
238 fail |= result("table", ok);
239 tname_matched = true;
240 }
241
242 if (!testname || strcmp("state", testname) == 0) {
243 ok = rumpns_npf_state_test(verbose);
244 fail |= result("state", ok);
245 tname_matched = true;
246 }
247 }
248
249 if (test && config) {
250 if (!testname || strcmp("rule", testname) == 0) {
251 ok = rumpns_npf_rule_test(verbose);
252 fail |= result("rule", ok);
253 tname_matched = true;
254 }
255
256 if (!testname || strcmp("nat", testname) == 0) {
257 ok = rumpns_npf_nat_test(verbose);
258 fail |= result("nat", ok);
259 tname_matched = true;
260 }
261 }
262
263 if (stream) {
264 process_stream(stream, NULL, idx);
265 }
266
267 rump_unschedule();
268
269 if (testname && !tname_matched)
270 errx(EXIT_FAILURE, "test \"%s\" unknown", testname);
271
272 return fail ? EXIT_FAILURE : EXIT_SUCCESS;
273 }
274