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