Home | History | Annotate | Line # | Download | only in libnpftest
      1 /*
      2  * NPF ruleset tests.
      3  *
      4  * Public Domain.
      5  */
      6 
      7 #ifdef _KERNEL
      8 #include <sys/types.h>
      9 #endif
     10 
     11 #include "npf_impl.h"
     12 #include "npf_test.h"
     13 
     14 #define	RESULT_PASS	0
     15 #define	RESULT_BLOCK	ENETUNREACH
     16 
     17 static const struct test_case {
     18 	int		af;
     19 	const char *	src;
     20 	const char *	dst;
     21 	const char *	ifname;
     22 	int		di;
     23 	int		stateful_ret;
     24 	int		ret;
     25 } test_cases[] = {
     26 
     27 	/* Stateful pass. */
     28 	{
     29 		.af = AF_INET,
     30 		.src = "10.1.1.1",		.dst = "10.1.1.2",
     31 		.ifname = IFNAME_INT,		.di = PFIL_OUT,
     32 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
     33 	},
     34 	{
     35 		.af = AF_INET,
     36 		.src = "10.1.1.2",		.dst = "10.1.1.1",
     37 		.ifname = IFNAME_INT,		.di = PFIL_IN,
     38 		.stateful_ret = RESULT_PASS,	.ret = RESULT_BLOCK
     39 	},
     40 
     41 	/* Pass forwards stream only. */
     42 	{
     43 		.af = AF_INET,
     44 		.src = "10.1.1.1",		.dst = "10.1.1.3",
     45 		.ifname = IFNAME_INT,		.di = PFIL_OUT,
     46 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
     47 	},
     48 	{
     49 		.af = AF_INET,
     50 		.src = "10.1.1.3",		.dst = "10.1.1.1",
     51 		.ifname = IFNAME_INT,		.di = PFIL_IN,
     52 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
     53 	},
     54 		/* PR bin/59511 */
     55 	{
     56 		.af = AF_INET,
     57 		.src = "192.168.100.5",		.dst = "10.1.1.5",
     58 		.ifname = IFNAME_INT,		.di = PFIL_IN,
     59 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
     60 	},
     61 	{
     62 		.af = AF_INET,
     63 		.src = "192.168.100.8",		.dst = "10.1.1.5",
     64 		.ifname = IFNAME_INT,		.di = PFIL_IN,
     65 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
     66 	},
     67 	{
     68 		.af = AF_INET,
     69 		.src = "192.168.64.3",		.dst = "10.1.1.5",
     70 		.ifname = IFNAME_INT,		.di = PFIL_IN,
     71 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
     72 	},
     73 	{
     74 		.af = AF_INET,
     75 		.src = "192.168.64.9",		.dst = "10.1.1.5",
     76 		.ifname = IFNAME_INT,		.di = PFIL_IN,
     77 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
     78 	},
     79 
     80 	/*
     81 	 * Pass in from any of the { fe80::1, fe80:1000:0:0/95,
     82 	 * fe80::2, fe80::2000:0:0/96, fe80::3, fe80::3000:0:0/97 }
     83 	 * group.
     84 	 */
     85 	{			/* fe80::1 */
     86 		.af = AF_INET6,
     87 		.src = "fe80::1", .dst = "fe80::adec:c91c:d116:7592",
     88 		.ifname = IFNAME_INT,		.di = PFIL_IN,
     89 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
     90 	},
     91 	{			/* fe80::1000:0:0/95 */
     92 		.af = AF_INET6,
     93 		.src = "fe80::1001:0:0", .dst = "fe80::adec:c91c:d116:7592",
     94 		.ifname = IFNAME_INT,		.di = PFIL_IN,
     95 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
     96 	},
     97 	{			/* fe80::1000:0:0/95, one bit off */
     98 		.af = AF_INET6,
     99 		.src = "fe80::1003:0:0", .dst = "fe80::adec:c91c:d116:7592",
    100 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    101 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
    102 	},
    103 	{			/* fe80::2 */
    104 		.af = AF_INET6,
    105 		.src = "fe80::2", .dst = "fe80::adec:c91c:d116:7592",
    106 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    107 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
    108 	},
    109 	{			/* fe80::2000:0:0/96 */
    110 		.af = AF_INET6,
    111 		.src = "fe80::2000:8000:0", .dst = "fe80::adec:c91c:d116:7592",
    112 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    113 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
    114 	},
    115 	{			/* fe80::2000:0:0/96, one bit off */
    116 		.af = AF_INET6,
    117 		.src = "fe80::2001:8000:0", .dst = "fe80::adec:c91c:d116:7592",
    118 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    119 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
    120 	},
    121 	{			/* fe80::3 */
    122 		.af = AF_INET6,
    123 		.src = "fe80::3", .dst = "fe80::adec:c91c:d116:7592",
    124 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    125 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
    126 	},
    127 	{			/* fe80::3000:0:0/97 */
    128 		.af = AF_INET6,
    129 		.src = "fe80::3000:7fff:0", .dst = "fe80::adec:c91c:d116:7592",
    130 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    131 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
    132 	},
    133 	{			/* fe80::3000:0:0/97, one bit off */
    134 		.af = AF_INET6,
    135 		.src = "fe80::3000:ffff:0", .dst = "fe80::adec:c91c:d116:7592",
    136 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    137 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
    138 	},
    139 	{
    140 		.af = AF_INET6,
    141 		.src = "fe80::4", .dst = "fe80::adec:c91c:d116:7592",
    142 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    143 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
    144 	},
    145 
    146 	/*
    147 	 * Pass in from anywhere _not_ in that group, as long as it is
    148 	 * to that group.
    149 	 */
    150 	{			/* fe80::1 */
    151 		.af = AF_INET6,
    152 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::1",
    153 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    154 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
    155 	},
    156 	{			/* fe80::1000:0:0/95 */
    157 		.af = AF_INET6,
    158 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::1001:0:0",
    159 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    160 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
    161 	},
    162 	{			/* fe80::1000:0:0/95, one bit off */
    163 		.af = AF_INET6,
    164 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::1003:0:0",
    165 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    166 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
    167 	},
    168 	{			/* fe80::2 */
    169 		.af = AF_INET6,
    170 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::2",
    171 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    172 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
    173 	},
    174 	{			/* fe80::2000:0:0/96 */
    175 		.af = AF_INET6,
    176 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::2000:8000:0",
    177 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    178 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
    179 	},
    180 	{			/* fe80::2000:0:0/96, one bit off */
    181 		.af = AF_INET6,
    182 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::2001:8000:0",
    183 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    184 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
    185 	},
    186 	{			/* fe80::3 */
    187 		.af = AF_INET6,
    188 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::3",
    189 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    190 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
    191 	},
    192 	{			/* fe80::3000:0:0/97 */
    193 		.af = AF_INET6,
    194 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::3000:7fff:0",
    195 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    196 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
    197 	},
    198 	{			/* fe80::3000:0:0/97, one bit off */
    199 		.af = AF_INET6,
    200 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::3000:ffff:0",
    201 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    202 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
    203 	},
    204 	{
    205 		.af = AF_INET6,
    206 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::4",
    207 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    208 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
    209 	},
    210 
    211 	/* Block. */
    212 	{
    213 		.af = AF_INET,
    214 		.src = "10.1.1.1",		.dst = "10.1.1.4",
    215 		.ifname = IFNAME_INT,		.di = PFIL_OUT,
    216 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
    217 	},
    218 	{	.src = "10.1.1.3",		.dst = "10.1.1.1",
    219 		.ifname = IFNAME_INT,		.di = PFIL_IN,
    220 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
    221 	},
    222 
    223 };
    224 
    225 static int
    226 run_raw_testcase(unsigned i)
    227 {
    228 	const struct test_case *t = &test_cases[i];
    229 	npf_t *npf = npf_getkernctx();
    230 	npf_cache_t *npc;
    231 	struct mbuf *m;
    232 	npf_rule_t *rl;
    233 	int slock, error;
    234 
    235 	m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, 9000, 9000);
    236 	npc = get_cached_pkt(m, t->ifname, NPF_RULE_LAYER_3);
    237 
    238 	slock = npf_config_read_enter(npf);
    239 	rl = npf_ruleset_inspect(npc, npf_config_ruleset(npf), t->di, NPF_RULE_LAYER_3);
    240 	if (rl) {
    241 		npf_match_info_t mi;
    242 		error = npf_rule_conclude(rl, &mi);
    243 	} else {
    244 		error = ENOENT;
    245 	}
    246 	npf_config_read_exit(npf, slock);
    247 
    248 	put_cached_pkt(npc);
    249 	return error;
    250 }
    251 
    252 static int
    253 run_handler_testcase(unsigned i)
    254 {
    255 	const struct test_case *t = &test_cases[i];
    256 	ifnet_t *ifp = npf_test_getif(t->ifname);
    257 	npf_t *npf = npf_getkernctx();
    258 	struct mbuf *m;
    259 	int error;
    260 
    261 	m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, 9000, 9000);
    262 	error = npfk_packet_handler(npf, &m, ifp, t->di);
    263 	if (m) {
    264 		m_freem(m);
    265 	}
    266 	return error;
    267 }
    268 
    269 static npf_rule_t *
    270 npf_blockall_rule(void)
    271 {
    272 	npf_t *npf = npf_getkernctx();
    273 	nvlist_t *rule = nvlist_create(0);
    274 	npf_rule_t *rl;
    275 
    276 	nvlist_add_number(rule, "attr",
    277 	    NPF_RULE_IN | NPF_RULE_OUT | NPF_RULE_DYNAMIC);
    278 	rl = npf_rule_alloc(npf, rule);
    279 	nvlist_destroy(rule);
    280 	return rl;
    281 }
    282 
    283 static bool
    284 test_static(bool verbose)
    285 {
    286 	for (unsigned i = 0; i < __arraycount(test_cases); i++) {
    287 		const struct test_case *t = &test_cases[i];
    288 		int error, serror;
    289 
    290 		if (npf_test_getif(t->ifname) == NULL) {
    291 			printf("Interface %s is not configured.\n", t->ifname);
    292 			return false;
    293 		}
    294 
    295 		error = run_raw_testcase(i);
    296 		serror = run_handler_testcase(i);
    297 
    298 		if (verbose) {
    299 			printf("rule test %d:\texpected %d (stateful) and %d\n"
    300 			    "\t\t-> returned %d and %d\n",
    301 			    i + 1, t->stateful_ret, t->ret, serror, error);
    302 		}
    303 		CHECK_TRUE(error == t->ret);
    304 		CHECK_TRUE(serror == t->stateful_ret)
    305 	}
    306 	return true;
    307 }
    308 
    309 static bool
    310 test_dynamic(void)
    311 {
    312 	npf_t *npf = npf_getkernctx();
    313 	npf_ruleset_t *rlset;
    314 	npf_rule_t *rl;
    315 	uint64_t id;
    316 	int error;
    317 
    318 	/*
    319 	 * Test dynamic NPF rules.
    320 	 */
    321 
    322 	error = run_raw_testcase(0);
    323 	CHECK_TRUE(error == RESULT_PASS);
    324 
    325 	npf_config_enter(npf);
    326 	rlset = npf_config_ruleset(npf);
    327 
    328 	rl = npf_blockall_rule();
    329 	error = npf_ruleset_add(rlset, "test-rules", rl);
    330 	CHECK_TRUE(error == 0);
    331 
    332 	error = run_raw_testcase(0);
    333 	CHECK_TRUE(error == RESULT_BLOCK);
    334 
    335 	id = npf_rule_getid(rl);
    336 	error = npf_ruleset_remove(rlset, "test-rules", id);
    337 	CHECK_TRUE(error == 0);
    338 
    339 	npf_config_exit(npf);
    340 
    341 	error = run_raw_testcase(0);
    342 	CHECK_TRUE(error == RESULT_PASS);
    343 
    344 	return true;
    345 }
    346 
    347 bool
    348 npf_rule_test(bool verbose)
    349 {
    350 	bool ok;
    351 
    352 	ok = test_static(verbose);
    353 	CHECK_TRUE(ok);
    354 
    355 	ok = test_dynamic();
    356 	CHECK_TRUE(ok);
    357 
    358 	return true;
    359 }
    360