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