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