xref: /netbsd-src/usr.sbin/npf/npftest/libnpftest/npf_rule_test.c (revision 4603749a908bfb032852d6f3d5e4efbb73018398)
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);
208 
209 	slock = npf_config_read_enter(npf);
210 	rl = npf_ruleset_inspect(npc, npf_config_ruleset(npf), t->di, NPF_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