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