npf_rule_test.c revision 1.22 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 any of the { fe80::1, fe80::2 } group.
57 */
58 {
59 .af = AF_INET6,
60 .src = "fe80::1", .dst = "fe80::adec:c91c:d116:7592",
61 .ifname = IFNAME_INT, .di = PFIL_IN,
62 .stateful_ret = RESULT_PASS, .ret = RESULT_PASS
63 },
64 {
65 .af = AF_INET6,
66 .src = "fe80::2", .dst = "fe80::adec:c91c:d116:7592",
67 .ifname = IFNAME_INT, .di = PFIL_IN,
68 .stateful_ret = RESULT_PASS, .ret = RESULT_PASS
69 },
70 {
71 .af = AF_INET6,
72 .src = "fe80::3", .dst = "fe80::adec:c91c:d116:7592",
73 .ifname = IFNAME_INT, .di = PFIL_IN,
74 .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK
75 },
76
77 /*
78 * Pass in anything _not_ in the group { fe80::1, fe80::2 }, as
79 * long as it is to that group.
80 */
81 {
82 .af = AF_INET6,
83 .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::1",
84 .ifname = IFNAME_INT, .di = PFIL_IN,
85 .stateful_ret = RESULT_PASS, .ret = RESULT_PASS
86 },
87 {
88 .af = AF_INET6,
89 .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::2",
90 .ifname = IFNAME_INT, .di = PFIL_IN,
91 .stateful_ret = RESULT_PASS, .ret = RESULT_PASS
92 },
93 {
94 .af = AF_INET6,
95 .src = "fe80::adec:c91c:d116:7592", .dst = "fe80::3",
96 .ifname = IFNAME_INT, .di = PFIL_IN,
97 .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK
98 },
99
100 /* Block. */
101 {
102 .af = AF_INET,
103 .src = "10.1.1.1", .dst = "10.1.1.4",
104 .ifname = IFNAME_INT, .di = PFIL_OUT,
105 .stateful_ret = RESULT_BLOCK, .ret = RESULT_BLOCK
106 },
107
108 };
109
110 static int
111 run_raw_testcase(unsigned i)
112 {
113 const struct test_case *t = &test_cases[i];
114 npf_t *npf = npf_getkernctx();
115 npf_cache_t *npc;
116 struct mbuf *m;
117 npf_rule_t *rl;
118 int slock, error;
119
120 m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, 9000, 9000);
121 npc = get_cached_pkt(m, t->ifname);
122
123 slock = npf_config_read_enter(npf);
124 rl = npf_ruleset_inspect(npc, npf_config_ruleset(npf), t->di, NPF_LAYER_3);
125 if (rl) {
126 npf_match_info_t mi;
127 error = npf_rule_conclude(rl, &mi);
128 } else {
129 error = ENOENT;
130 }
131 npf_config_read_exit(npf, slock);
132
133 put_cached_pkt(npc);
134 return error;
135 }
136
137 static int
138 run_handler_testcase(unsigned i)
139 {
140 const struct test_case *t = &test_cases[i];
141 ifnet_t *ifp = npf_test_getif(t->ifname);
142 npf_t *npf = npf_getkernctx();
143 struct mbuf *m;
144 int error;
145
146 m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, 9000, 9000);
147 error = npfk_packet_handler(npf, &m, ifp, t->di);
148 if (m) {
149 m_freem(m);
150 }
151 return error;
152 }
153
154 static npf_rule_t *
155 npf_blockall_rule(void)
156 {
157 npf_t *npf = npf_getkernctx();
158 nvlist_t *rule = nvlist_create(0);
159 npf_rule_t *rl;
160
161 nvlist_add_number(rule, "attr",
162 NPF_RULE_IN | NPF_RULE_OUT | NPF_RULE_DYNAMIC);
163 rl = npf_rule_alloc(npf, rule);
164 nvlist_destroy(rule);
165 return rl;
166 }
167
168 static bool
169 test_static(bool verbose)
170 {
171 for (unsigned i = 0; i < __arraycount(test_cases); i++) {
172 const struct test_case *t = &test_cases[i];
173 int error, serror;
174
175 if (npf_test_getif(t->ifname) == NULL) {
176 printf("Interface %s is not configured.\n", t->ifname);
177 return false;
178 }
179
180 error = run_raw_testcase(i);
181 serror = run_handler_testcase(i);
182
183 if (verbose) {
184 printf("rule test %d:\texpected %d (stateful) and %d\n"
185 "\t\t-> returned %d and %d\n",
186 i + 1, t->stateful_ret, t->ret, serror, error);
187 }
188 CHECK_TRUE(error == t->ret);
189 CHECK_TRUE(serror == t->stateful_ret)
190 }
191 return true;
192 }
193
194 static bool
195 test_dynamic(void)
196 {
197 npf_t *npf = npf_getkernctx();
198 npf_ruleset_t *rlset;
199 npf_rule_t *rl;
200 uint64_t id;
201 int error;
202
203 /*
204 * Test dynamic NPF rules.
205 */
206
207 error = run_raw_testcase(0);
208 CHECK_TRUE(error == RESULT_PASS);
209
210 npf_config_enter(npf);
211 rlset = npf_config_ruleset(npf);
212
213 rl = npf_blockall_rule();
214 error = npf_ruleset_add(rlset, "test-rules", rl);
215 CHECK_TRUE(error == 0);
216
217 error = run_raw_testcase(0);
218 CHECK_TRUE(error == RESULT_BLOCK);
219
220 id = npf_rule_getid(rl);
221 error = npf_ruleset_remove(rlset, "test-rules", id);
222 CHECK_TRUE(error == 0);
223
224 npf_config_exit(npf);
225
226 error = run_raw_testcase(0);
227 CHECK_TRUE(error == RESULT_PASS);
228
229 return true;
230 }
231
232 bool
233 npf_rule_test(bool verbose)
234 {
235 bool ok;
236
237 ok = test_static(verbose);
238 CHECK_TRUE(ok);
239
240 ok = test_dynamic();
241 CHECK_TRUE(ok);
242
243 return true;
244 }
245