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