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