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