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