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