xref: /netbsd-src/usr.sbin/npf/npftest/libnpftest/npf_rule_test.c (revision 4603749a908bfb032852d6f3d5e4efbb73018398)
163f44833Srmind /*
23d9a792dSrmind  * NPF ruleset tests.
363f44833Srmind  *
463f44833Srmind  * Public Domain.
563f44833Srmind  */
663f44833Srmind 
7f75d79ebSchristos #ifdef _KERNEL
863f44833Srmind #include <sys/types.h>
9f75d79ebSchristos #endif
1063f44833Srmind 
1163f44833Srmind #include "npf_impl.h"
1263f44833Srmind #include "npf_test.h"
1363f44833Srmind 
1463f44833Srmind #define	RESULT_PASS	0
1563f44833Srmind #define	RESULT_BLOCK	ENETUNREACH
1663f44833Srmind 
1763f44833Srmind static const struct test_case {
18ccafaf8eSriastradh 	int		af;
1963f44833Srmind 	const char *	src;
2063f44833Srmind 	const char *	dst;
2163f44833Srmind 	const char *	ifname;
2263f44833Srmind 	int		di;
2363f44833Srmind 	int		stateful_ret;
2463f44833Srmind 	int		ret;
2563f44833Srmind } test_cases[] = {
2663f44833Srmind 
2763f44833Srmind 	/* Stateful pass. */
2863f44833Srmind 	{
29ccafaf8eSriastradh 		.af = AF_INET,
3063f44833Srmind 		.src = "10.1.1.1",		.dst = "10.1.1.2",
3163f44833Srmind 		.ifname = IFNAME_INT,		.di = PFIL_OUT,
3263f44833Srmind 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
3363f44833Srmind 	},
3463f44833Srmind 	{
35ccafaf8eSriastradh 		.af = AF_INET,
3663f44833Srmind 		.src = "10.1.1.2",		.dst = "10.1.1.1",
3763f44833Srmind 		.ifname = IFNAME_INT,		.di = PFIL_IN,
3863f44833Srmind 		.stateful_ret = RESULT_PASS,	.ret = RESULT_BLOCK
3963f44833Srmind 	},
4063f44833Srmind 
4163f44833Srmind 	/* Pass forwards stream only. */
4263f44833Srmind 	{
43ccafaf8eSriastradh 		.af = AF_INET,
4463f44833Srmind 		.src = "10.1.1.1",		.dst = "10.1.1.3",
4563f44833Srmind 		.ifname = IFNAME_INT,		.di = PFIL_OUT,
4663f44833Srmind 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
4763f44833Srmind 	},
4863f44833Srmind 	{
49ccafaf8eSriastradh 		.af = AF_INET,
5063f44833Srmind 		.src = "10.1.1.3",		.dst = "10.1.1.1",
5163f44833Srmind 		.ifname = IFNAME_INT,		.di = PFIL_IN,
5263f44833Srmind 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
5363f44833Srmind 	},
5463f44833Srmind 
551452d7e1Sriastradh 	/*
56*4603749aSriastradh 	 * Pass in from any of the { fe80::1, fe80:1000:0:0/95,
57*4603749aSriastradh 	 * fe80::2, fe80::2000:0:0/96, fe80::3, fe80::3000:0:0/97 }
58*4603749aSriastradh 	 * group.
591452d7e1Sriastradh 	 */
60*4603749aSriastradh 	{			/* fe80::1 */
611452d7e1Sriastradh 		.af = AF_INET6,
621452d7e1Sriastradh 		.src = "fe80::1", .dst = "fe80::adec:c91c:d116:7592",
63158a2254Sriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
641452d7e1Sriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
651452d7e1Sriastradh 	},
66*4603749aSriastradh 	{			/* fe80::1000:0:0/95 */
67*4603749aSriastradh 		.af = AF_INET6,
68*4603749aSriastradh 		.src = "fe80::1001:0:0", .dst = "fe80::adec:c91c:d116:7592",
69*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
70*4603749aSriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
71*4603749aSriastradh 	},
72*4603749aSriastradh 	{			/* fe80::1000:0:0/95, one bit off */
73*4603749aSriastradh 		.af = AF_INET6,
74*4603749aSriastradh 		.src = "fe80::1003:0:0", .dst = "fe80::adec:c91c:d116:7592",
75*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
76*4603749aSriastradh 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
77*4603749aSriastradh 	},
78*4603749aSriastradh 	{			/* fe80::2 */
791452d7e1Sriastradh 		.af = AF_INET6,
801452d7e1Sriastradh 		.src = "fe80::2", .dst = "fe80::adec:c91c:d116:7592",
81158a2254Sriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
821452d7e1Sriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
831452d7e1Sriastradh 	},
84*4603749aSriastradh 	{			/* fe80::2000:0:0/96 */
85*4603749aSriastradh 		.af = AF_INET6,
86*4603749aSriastradh 		.src = "fe80::2000:8000:0", .dst = "fe80::adec:c91c:d116:7592",
87*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
88*4603749aSriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
89*4603749aSriastradh 	},
90*4603749aSriastradh 	{			/* fe80::2000:0:0/96, one bit off */
91*4603749aSriastradh 		.af = AF_INET6,
92*4603749aSriastradh 		.src = "fe80::2001:8000:0", .dst = "fe80::adec:c91c:d116:7592",
93*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
94*4603749aSriastradh 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
95*4603749aSriastradh 	},
96*4603749aSriastradh 	{			/* fe80::3 */
971452d7e1Sriastradh 		.af = AF_INET6,
981452d7e1Sriastradh 		.src = "fe80::3", .dst = "fe80::adec:c91c:d116:7592",
99158a2254Sriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
100*4603749aSriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
101*4603749aSriastradh 	},
102*4603749aSriastradh 	{			/* fe80::3000:0:0/97 */
103*4603749aSriastradh 		.af = AF_INET6,
104*4603749aSriastradh 		.src = "fe80::3000:7fff:0", .dst = "fe80::adec:c91c:d116:7592",
105*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
106*4603749aSriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
107*4603749aSriastradh 	},
108*4603749aSriastradh 	{			/* fe80::3000:0:0/97, one bit off */
109*4603749aSriastradh 		.af = AF_INET6,
110*4603749aSriastradh 		.src = "fe80::3000:ffff:0", .dst = "fe80::adec:c91c:d116:7592",
111*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
112*4603749aSriastradh 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
113*4603749aSriastradh 	},
114*4603749aSriastradh 	{
115*4603749aSriastradh 		.af = AF_INET6,
116*4603749aSriastradh 		.src = "fe80::4", .dst = "fe80::adec:c91c:d116:7592",
117*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
118158a2254Sriastradh 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
119158a2254Sriastradh 	},
120158a2254Sriastradh 
121158a2254Sriastradh 	/*
122*4603749aSriastradh 	 * Pass in from anywhere _not_ in that group, as long as it is
123*4603749aSriastradh 	 * to that group.
124158a2254Sriastradh 	 */
125*4603749aSriastradh 	{			/* fe80::1 */
126158a2254Sriastradh 		.af = AF_INET6,
127158a2254Sriastradh 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::1",
128158a2254Sriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
129158a2254Sriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
130158a2254Sriastradh 	},
131*4603749aSriastradh 	{			/* fe80::1000:0:0/95 */
132*4603749aSriastradh 		.af = AF_INET6,
133*4603749aSriastradh 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::1001:0:0",
134*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
135*4603749aSriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
136*4603749aSriastradh 	},
137*4603749aSriastradh 	{			/* fe80::1000:0:0/95, one bit off */
138*4603749aSriastradh 		.af = AF_INET6,
139*4603749aSriastradh 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::1003:0:0",
140*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
141*4603749aSriastradh 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
142*4603749aSriastradh 	},
143*4603749aSriastradh 	{			/* fe80::2 */
144158a2254Sriastradh 		.af = AF_INET6,
145158a2254Sriastradh 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::2",
146158a2254Sriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
147158a2254Sriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
148158a2254Sriastradh 	},
149*4603749aSriastradh 	{			/* fe80::2000:0:0/96 */
150*4603749aSriastradh 		.af = AF_INET6,
151*4603749aSriastradh 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::2000:8000:0",
152*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
153*4603749aSriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
154*4603749aSriastradh 	},
155*4603749aSriastradh 	{			/* fe80::2000:0:0/96, one bit off */
156*4603749aSriastradh 		.af = AF_INET6,
157*4603749aSriastradh 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::2001:8000:0",
158*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
159*4603749aSriastradh 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
160*4603749aSriastradh 	},
161*4603749aSriastradh 	{			/* fe80::3 */
162158a2254Sriastradh 		.af = AF_INET6,
163158a2254Sriastradh 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::3",
164158a2254Sriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
165*4603749aSriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
166*4603749aSriastradh 	},
167*4603749aSriastradh 	{			/* fe80::3000:0:0/97 */
168*4603749aSriastradh 		.af = AF_INET6,
169*4603749aSriastradh 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::3000:7fff:0",
170*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
171*4603749aSriastradh 		.stateful_ret = RESULT_PASS,	.ret = RESULT_PASS
172*4603749aSriastradh 	},
173*4603749aSriastradh 	{			/* fe80::3000:0:0/97, one bit off */
174*4603749aSriastradh 		.af = AF_INET6,
175*4603749aSriastradh 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::3000:ffff:0",
176*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
177*4603749aSriastradh 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
178*4603749aSriastradh 	},
179*4603749aSriastradh 	{
180*4603749aSriastradh 		.af = AF_INET6,
181*4603749aSriastradh 		.src = "fe80::adec:c91c:d116:7592", .dst = "fe80::4",
182*4603749aSriastradh 		.ifname = IFNAME_INT,		.di = PFIL_IN,
1831452d7e1Sriastradh 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
1841452d7e1Sriastradh 	},
1851452d7e1Sriastradh 
18663f44833Srmind 	/* Block. */
187ccafaf8eSriastradh 	{
188ccafaf8eSriastradh 		.af = AF_INET,
189ccafaf8eSriastradh 		.src = "10.1.1.1",		.dst = "10.1.1.4",
19063f44833Srmind 		.ifname = IFNAME_INT,		.di = PFIL_OUT,
19163f44833Srmind 		.stateful_ret = RESULT_BLOCK,	.ret = RESULT_BLOCK
19263f44833Srmind 	},
19363f44833Srmind 
19463f44833Srmind };
19563f44833Srmind 
19663f44833Srmind static int
197dadc88e3Srmind run_raw_testcase(unsigned i)
19863f44833Srmind {
199dadc88e3Srmind 	const struct test_case *t = &test_cases[i];
200f75d79ebSchristos 	npf_t *npf = npf_getkernctx();
201dadc88e3Srmind 	npf_cache_t *npc;
202dadc88e3Srmind 	struct mbuf *m;
20363f44833Srmind 	npf_rule_t *rl;
204dadc88e3Srmind 	int slock, error;
20563f44833Srmind 
206ccafaf8eSriastradh 	m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, 9000, 9000);
207dadc88e3Srmind 	npc = get_cached_pkt(m, t->ifname);
208352f1606Srmind 
209298883feSrmind 	slock = npf_config_read_enter(npf);
210dadc88e3Srmind 	rl = npf_ruleset_inspect(npc, npf_config_ruleset(npf), t->di, NPF_LAYER_3);
21163f44833Srmind 	if (rl) {
212dadc88e3Srmind 		npf_match_info_t mi;
2132e57ffe4Schristos 		error = npf_rule_conclude(rl, &mi);
21463f44833Srmind 	} else {
21563f44833Srmind 		error = ENOENT;
21663f44833Srmind 	}
217298883feSrmind 	npf_config_read_exit(npf, slock);
218dadc88e3Srmind 
219dadc88e3Srmind 	put_cached_pkt(npc);
22063f44833Srmind 	return error;
22163f44833Srmind }
22263f44833Srmind 
2230e218254Srmind static int
224dadc88e3Srmind run_handler_testcase(unsigned i)
2250e218254Srmind {
2264e592132Srmind 	const struct test_case *t = &test_cases[i];
227f75d79ebSchristos 	ifnet_t *ifp = npf_test_getif(t->ifname);
228dadc88e3Srmind 	npf_t *npf = npf_getkernctx();
229dadc88e3Srmind 	struct mbuf *m;
2300e218254Srmind 	int error;
2310e218254Srmind 
232ccafaf8eSriastradh 	m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, 9000, 9000);
23304ad65d9Srmind 	error = npfk_packet_handler(npf, &m, ifp, t->di);
234dadc88e3Srmind 	if (m) {
2350e218254Srmind 		m_freem(m);
236dadc88e3Srmind 	}
2370e218254Srmind 	return error;
2380e218254Srmind }
2390e218254Srmind 
2400e218254Srmind static npf_rule_t *
2410e218254Srmind npf_blockall_rule(void)
2420e218254Srmind {
243f75d79ebSchristos 	npf_t *npf = npf_getkernctx();
24439013e66Srmind 	nvlist_t *rule = nvlist_create(0);
245dadc88e3Srmind 	npf_rule_t *rl;
2460e218254Srmind 
24739013e66Srmind 	nvlist_add_number(rule, "attr",
248e9a253f3Srmind 	    NPF_RULE_IN | NPF_RULE_OUT | NPF_RULE_DYNAMIC);
249dadc88e3Srmind 	rl = npf_rule_alloc(npf, rule);
250dadc88e3Srmind 	nvlist_destroy(rule);
251dadc88e3Srmind 	return rl;
2520e218254Srmind }
2530e218254Srmind 
254dadc88e3Srmind static bool
255dadc88e3Srmind test_static(bool verbose)
256dadc88e3Srmind {
257dadc88e3Srmind 	for (unsigned i = 0; i < __arraycount(test_cases); i++) {
258dadc88e3Srmind 		const struct test_case *t = &test_cases[i];
259dadc88e3Srmind 		int error, serror;
260dadc88e3Srmind 
261dadc88e3Srmind 		if (npf_test_getif(t->ifname) == NULL) {
262dadc88e3Srmind 			printf("Interface %s is not configured.\n", t->ifname);
263dadc88e3Srmind 			return false;
264dadc88e3Srmind 		}
265dadc88e3Srmind 
266dadc88e3Srmind 		error = run_raw_testcase(i);
267dadc88e3Srmind 		serror = run_handler_testcase(i);
268dadc88e3Srmind 
269dadc88e3Srmind 		if (verbose) {
270dadc88e3Srmind 			printf("rule test %d:\texpected %d (stateful) and %d\n"
271dadc88e3Srmind 			    "\t\t-> returned %d and %d\n",
272dadc88e3Srmind 			    i + 1, t->stateful_ret, t->ret, serror, error);
273dadc88e3Srmind 		}
274dadc88e3Srmind 		CHECK_TRUE(error == t->ret);
275dadc88e3Srmind 		CHECK_TRUE(serror == t->stateful_ret)
276dadc88e3Srmind 	}
277dadc88e3Srmind 	return true;
278dadc88e3Srmind }
279dadc88e3Srmind 
280dadc88e3Srmind static bool
281dadc88e3Srmind test_dynamic(void)
28263f44833Srmind {
283f75d79ebSchristos 	npf_t *npf = npf_getkernctx();
2840e218254Srmind 	npf_ruleset_t *rlset;
2850e218254Srmind 	npf_rule_t *rl;
28656910be7Srmind 	uint64_t id;
2870e218254Srmind 	int error;
288b8c27e4aSrmind 
2894e592132Srmind 	/*
2904e592132Srmind 	 * Test dynamic NPF rules.
2914e592132Srmind 	 */
2924e592132Srmind 
293dadc88e3Srmind 	error = run_raw_testcase(0);
2943d9a792dSrmind 	CHECK_TRUE(error == RESULT_PASS);
2950e218254Srmind 
296f75d79ebSchristos 	npf_config_enter(npf);
297f75d79ebSchristos 	rlset = npf_config_ruleset(npf);
2980e218254Srmind 
2990e218254Srmind 	rl = npf_blockall_rule();
3000e218254Srmind 	error = npf_ruleset_add(rlset, "test-rules", rl);
3013d9a792dSrmind 	CHECK_TRUE(error == 0);
3020e218254Srmind 
303dadc88e3Srmind 	error = run_raw_testcase(0);
3043d9a792dSrmind 	CHECK_TRUE(error == RESULT_BLOCK);
3050e218254Srmind 
30656910be7Srmind 	id = npf_rule_getid(rl);
30756910be7Srmind 	error = npf_ruleset_remove(rlset, "test-rules", id);
3083d9a792dSrmind 	CHECK_TRUE(error == 0);
3090e218254Srmind 
310f75d79ebSchristos 	npf_config_exit(npf);
3110e218254Srmind 
312dadc88e3Srmind 	error = run_raw_testcase(0);
3133d9a792dSrmind 	CHECK_TRUE(error == RESULT_PASS);
3140e218254Srmind 
3153d9a792dSrmind 	return true;
31663f44833Srmind }
317dadc88e3Srmind 
318dadc88e3Srmind bool
319dadc88e3Srmind npf_rule_test(bool verbose)
320dadc88e3Srmind {
321dadc88e3Srmind 	bool ok;
322dadc88e3Srmind 
323dadc88e3Srmind 	ok = test_static(verbose);
324dadc88e3Srmind 	CHECK_TRUE(ok);
325dadc88e3Srmind 
326dadc88e3Srmind 	ok = test_dynamic();
327dadc88e3Srmind 	CHECK_TRUE(ok);
328dadc88e3Srmind 
329dadc88e3Srmind 	return true;
330dadc88e3Srmind }
331