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