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