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