npftest.c revision 1.6.2.2 1 1.6.2.2 tls /* $NetBSD: npftest.c,v 1.6.2.2 2014/08/20 00:05:11 tls 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.6.2.2 tls #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.6.2.2 tls #include <cdbw.h>
27 1.6.2.2 tls
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.6.2.1 tls __dead static void
33 1.1 rmind usage(void)
34 1.1 rmind {
35 1.6.2.1 tls printf("usage:\n"
36 1.6.2.1 tls " %s [ -q | -v ] [ -c <config> ] "
37 1.6.2.1 tls "[ -i <interface> ] < -b | -t | -s file >\n"
38 1.6.2.1 tls " %s -T <testname> -c <config>\n"
39 1.6.2.1 tls " %s -L\n"
40 1.6.2.1 tls "where:\n"
41 1.2 rmind "\t-b: benchmark\n"
42 1.2 rmind "\t-t: regression test\n"
43 1.6.2.1 tls "\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.6.2.1 tls "\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.6.2.1 tls getprogname(), getprogname(), getprogname());
51 1.2 rmind exit(EXIT_FAILURE);
52 1.1 rmind }
53 1.1 rmind
54 1.6.2.1 tls __dead static void
55 1.6.2.1 tls describe_tests(void)
56 1.6.2.1 tls {
57 1.6.2.1 tls printf( "nbuf\tbasic npf mbuf handling\n"
58 1.6.2.2 tls "bpf\tBPF coprocessor\n"
59 1.6.2.1 tls "table\ttable handling\n"
60 1.6.2.1 tls "state\tstate handling and processing\n"
61 1.6.2.1 tls "rule\trule processing\n"
62 1.6.2.1 tls "nat\tNAT rule processing\n");
63 1.6.2.1 tls exit(EXIT_SUCCESS);
64 1.6.2.1 tls }
65 1.6.2.1 tls
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.6.2.2 tls prop_array_t iflist = prop_dictionary_get(dbg_dict, "interfaces");
82 1.6.2.2 tls 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.6.2.2 tls const char *ifname = NULL;
87 1.4 rmind
88 1.4 rmind prop_dictionary_get_cstring_nocopy(ifdict, "name", &ifname);
89 1.6.2.2 tls (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.6.2.2 tls static void *
128 1.6.2.2 tls generate_test_cdb(size_t *size)
129 1.4 rmind {
130 1.6.2.2 tls in_addr_t addr;
131 1.6.2.2 tls struct cdbw *cdbw;
132 1.6.2.2 tls struct stat sb;
133 1.6.2.2 tls char sfn[32];
134 1.6.2.2 tls int alen, fd;
135 1.6.2.2 tls void *cdb;
136 1.6.2.2 tls
137 1.6.2.2 tls if ((cdbw = cdbw_open()) == NULL) {
138 1.6.2.2 tls err(EXIT_FAILURE, "cdbw_open");
139 1.6.2.2 tls }
140 1.6.2.2 tls strlcpy(sfn, "/tmp/npftest_cdb.XXXXXX", sizeof(sfn));
141 1.6.2.2 tls if ((fd = mkstemp(sfn)) == -1) {
142 1.6.2.2 tls err(EXIT_FAILURE, "mkstemp");
143 1.6.2.2 tls }
144 1.6.2.2 tls unlink(sfn);
145 1.6.2.2 tls
146 1.6.2.2 tls addr = inet_addr("192.168.1.1"), alen = sizeof(struct in_addr);
147 1.6.2.2 tls if (cdbw_put(cdbw, &addr, alen, &addr, alen) == -1)
148 1.6.2.2 tls err(EXIT_FAILURE, "cdbw_put");
149 1.6.2.2 tls
150 1.6.2.2 tls addr = inet_addr("10.0.0.2"), alen = sizeof(struct in_addr);
151 1.6.2.2 tls if (cdbw_put(cdbw, &addr, alen, &addr, alen) == -1)
152 1.6.2.2 tls err(EXIT_FAILURE, "cdbw_put");
153 1.6.2.2 tls
154 1.6.2.2 tls if (cdbw_output(cdbw, fd, "npf-table-cdb", NULL) == -1) {
155 1.6.2.2 tls err(EXIT_FAILURE, "cdbw_output");
156 1.6.2.2 tls }
157 1.6.2.2 tls cdbw_close(cdbw);
158 1.6.2.2 tls
159 1.6.2.2 tls if (fstat(fd, &sb) == -1) {
160 1.6.2.2 tls err(EXIT_FAILURE, "fstat");
161 1.6.2.2 tls }
162 1.6.2.2 tls if ((cdb = mmap(NULL, sb.st_size, PROT_READ,
163 1.6.2.2 tls MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
164 1.6.2.2 tls err(EXIT_FAILURE, "mmap");
165 1.6.2.2 tls }
166 1.6.2.2 tls close(fd);
167 1.6.2.2 tls
168 1.6.2.2 tls *size = sb.st_size;
169 1.6.2.2 tls return cdb;
170 1.4 rmind }
171 1.4 rmind
172 1.1 rmind int
173 1.1 rmind main(int argc, char **argv)
174 1.1 rmind {
175 1.6.2.2 tls bool test, ok, fail, tname_matched;
176 1.6.2.2 tls char *benchmark, *config, *interface, *stream, *testname;
177 1.6.2.2 tls unsigned nthreads = 0;
178 1.6.2.2 tls ifnet_t *ifp = NULL;
179 1.6.2.2 tls int ch;
180 1.1 rmind
181 1.6.2.2 tls benchmark = NULL;
182 1.2 rmind test = false;
183 1.2 rmind
184 1.6.2.1 tls tname_matched = false;
185 1.6.2.1 tls 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.6.2.2 tls 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.6.2.2 tls 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.6.2.1 tls case 'T':
217 1.6.2.1 tls test = true;
218 1.6.2.1 tls testname = optarg;
219 1.6.2.1 tls break;
220 1.6.2.1 tls case 'L':
221 1.6.2.1 tls describe_tests();
222 1.6.2.2 tls break;
223 1.6.2.2 tls case 'p':
224 1.6.2.2 tls /* Note: RUMP_NCPU must be high enough. */
225 1.6.2.2 tls if ((nthreads = atoi(optarg)) > 0 &&
226 1.6.2.2 tls getenv("RUMP_NCPU") == NULL) {
227 1.6.2.2 tls char *val;
228 1.6.2.2 tls asprintf(&val, "%u", nthreads + 1);
229 1.6.2.2 tls setenv("RUMP_NCPU", val, 1);
230 1.6.2.2 tls free(val);
231 1.6.2.2 tls }
232 1.6.2.2 tls 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.6.2.2 tls * Either benchmark or test. If stream analysis, then the
240 1.6.2.2 tls * interface should be specified. If benchmark, then the
241 1.6.2.2 tls * config should be loaded.
242 1.4 rmind */
243 1.6.2.2 tls if ((benchmark != NULL) == test && (stream && !interface)) {
244 1.2 rmind usage();
245 1.2 rmind }
246 1.6.2.2 tls if (benchmark && (!config || !nthreads)) {
247 1.6.2.2 tls errx(EXIT_FAILURE, "missing config for the benchmark or "
248 1.6.2.2 tls "invalid thread count");
249 1.6.2.2 tls }
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.6.2.2 tls rumpns_npf_test_init(inet_pton, inet_ntop, 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.6.2.2 tls 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.6.2.1 tls if (!testname || strcmp("nbuf", testname) == 0) {
272 1.6.2.1 tls ok = rumpns_npf_nbuf_test(verbose);
273 1.6.2.1 tls fail |= result("nbuf", ok);
274 1.6.2.1 tls tname_matched = true;
275 1.6.2.1 tls }
276 1.1 rmind
277 1.6.2.2 tls if (!testname || strcmp("bpf", testname) == 0) {
278 1.6.2.2 tls ok = rumpns_npf_bpf_test(verbose);
279 1.6.2.2 tls fail |= result("bpf", ok);
280 1.6.2.1 tls tname_matched = true;
281 1.6.2.1 tls }
282 1.1 rmind
283 1.6.2.1 tls if (!testname || strcmp("table", testname) == 0) {
284 1.6.2.2 tls void *cdb;
285 1.6.2.2 tls size_t len;
286 1.6.2.2 tls
287 1.6.2.2 tls cdb = generate_test_cdb(&len);
288 1.6.2.2 tls ok = rumpns_npf_table_test(verbose, cdb, len);
289 1.6.2.1 tls fail |= result("table", ok);
290 1.6.2.1 tls tname_matched = true;
291 1.6.2.2 tls munmap(cdb, len);
292 1.6.2.1 tls }
293 1.3 rmind
294 1.6.2.1 tls if (!testname || strcmp("state", testname) == 0) {
295 1.6.2.1 tls ok = rumpns_npf_state_test(verbose);
296 1.6.2.1 tls fail |= result("state", ok);
297 1.6.2.1 tls tname_matched = true;
298 1.6.2.1 tls }
299 1.2 rmind }
300 1.2 rmind
301 1.4 rmind if (test && config) {
302 1.6.2.1 tls if (!testname || strcmp("rule", testname) == 0) {
303 1.6.2.1 tls ok = rumpns_npf_rule_test(verbose);
304 1.6.2.1 tls fail |= result("rule", ok);
305 1.6.2.1 tls tname_matched = true;
306 1.6.2.1 tls }
307 1.4 rmind
308 1.6.2.1 tls if (!testname || strcmp("nat", testname) == 0) {
309 1.6.2.1 tls ok = rumpns_npf_nat_test(verbose);
310 1.6.2.1 tls fail |= result("nat", ok);
311 1.6.2.1 tls tname_matched = true;
312 1.6.2.1 tls }
313 1.4 rmind }
314 1.4 rmind
315 1.2 rmind if (stream) {
316 1.6.2.2 tls process_stream(stream, NULL, ifp);
317 1.6.2.2 tls }
318 1.6.2.2 tls
319 1.6.2.2 tls if (benchmark) {
320 1.6.2.2 tls if (strcmp("rule", benchmark) == 0) {
321 1.6.2.2 tls rumpns_npf_test_conc(false, nthreads);
322 1.6.2.2 tls }
323 1.6.2.2 tls if (strcmp("state", benchmark) == 0) {
324 1.6.2.2 tls rumpns_npf_test_conc(true, nthreads);
325 1.6.2.2 tls }
326 1.2 rmind }
327 1.1 rmind
328 1.1 rmind rump_unschedule();
329 1.1 rmind
330 1.6.2.1 tls if (testname && !tname_matched)
331 1.6.2.1 tls errx(EXIT_FAILURE, "test \"%s\" unknown", testname);
332 1.6.2.1 tls
333 1.6 rmind return fail ? EXIT_FAILURE : EXIT_SUCCESS;
334 1.1 rmind }
335