Home | History | Annotate | Line # | Download | only in npftest
npftest.c revision 1.13
      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