npf_ruleset.c revision 1.7.10.1 1 1.7.10.1 mrg /* $NetBSD: npf_ruleset.c,v 1.7.10.1 2012/02/18 07:35:38 mrg Exp $ */
2 1.1 rmind
3 1.1 rmind /*-
4 1.7.10.1 mrg * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
5 1.1 rmind * All rights reserved.
6 1.1 rmind *
7 1.1 rmind * This material is based upon work partially supported by The
8 1.1 rmind * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
9 1.1 rmind *
10 1.1 rmind * Redistribution and use in source and binary forms, with or without
11 1.1 rmind * modification, are permitted provided that the following conditions
12 1.1 rmind * are met:
13 1.1 rmind * 1. Redistributions of source code must retain the above copyright
14 1.1 rmind * notice, this list of conditions and the following disclaimer.
15 1.1 rmind * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 rmind * notice, this list of conditions and the following disclaimer in the
17 1.1 rmind * documentation and/or other materials provided with the distribution.
18 1.1 rmind *
19 1.1 rmind * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 rmind * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 rmind * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 rmind * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 rmind * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 rmind * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 rmind * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 rmind * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 rmind * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 rmind * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 rmind * POSSIBILITY OF SUCH DAMAGE.
30 1.1 rmind */
31 1.1 rmind
32 1.1 rmind /*
33 1.1 rmind * NPF ruleset module.
34 1.1 rmind */
35 1.1 rmind
36 1.1 rmind #include <sys/cdefs.h>
37 1.7.10.1 mrg __KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.7.10.1 2012/02/18 07:35:38 mrg Exp $");
38 1.1 rmind
39 1.1 rmind #include <sys/param.h>
40 1.1 rmind #include <sys/kernel.h>
41 1.1 rmind
42 1.1 rmind #include <sys/kmem.h>
43 1.1 rmind #include <sys/queue.h>
44 1.1 rmind #include <sys/types.h>
45 1.1 rmind
46 1.3 rmind #include <net/pfil.h>
47 1.1 rmind #include <net/if.h>
48 1.1 rmind
49 1.1 rmind #include "npf_ncode.h"
50 1.1 rmind #include "npf_impl.h"
51 1.1 rmind
52 1.4 rmind /* Ruleset structre (queue and default rule). */
53 1.4 rmind struct npf_ruleset {
54 1.4 rmind TAILQ_HEAD(, npf_rule) rs_queue;
55 1.4 rmind npf_rule_t * rs_default;
56 1.4 rmind };
57 1.4 rmind
58 1.7 rmind #define NPF_RNAME_LEN 16
59 1.7 rmind
60 1.1 rmind /* Rule structure. */
61 1.1 rmind struct npf_rule {
62 1.7 rmind /* Rule name (optional) and list entry. */
63 1.7 rmind char r_name[NPF_RNAME_LEN];
64 1.4 rmind TAILQ_ENTRY(npf_rule) r_entry;
65 1.1 rmind /* Optional: sub-ruleset, NAT policy. */
66 1.4 rmind npf_ruleset_t r_subset;
67 1.4 rmind npf_natpolicy_t * r_natp;
68 1.1 rmind /* Rule priority: (highest) 0, 1, 2 ... n (lowest). */
69 1.7 rmind pri_t r_priority;
70 1.1 rmind /* N-code to process. */
71 1.4 rmind void * r_ncode;
72 1.4 rmind size_t r_nc_size;
73 1.1 rmind /* Attributes of this rule. */
74 1.4 rmind uint32_t r_attr;
75 1.1 rmind /* Interface. */
76 1.4 rmind u_int r_ifid;
77 1.6 rmind /* Rule procedure data. */
78 1.4 rmind npf_rproc_t * r_rproc;
79 1.1 rmind };
80 1.1 rmind
81 1.1 rmind npf_ruleset_t *
82 1.1 rmind npf_ruleset_create(void)
83 1.1 rmind {
84 1.1 rmind npf_ruleset_t *rlset;
85 1.1 rmind
86 1.1 rmind rlset = kmem_zalloc(sizeof(npf_ruleset_t), KM_SLEEP);
87 1.1 rmind TAILQ_INIT(&rlset->rs_queue);
88 1.1 rmind return rlset;
89 1.1 rmind }
90 1.1 rmind
91 1.1 rmind void
92 1.1 rmind npf_ruleset_destroy(npf_ruleset_t *rlset)
93 1.1 rmind {
94 1.1 rmind npf_rule_t *rl;
95 1.1 rmind
96 1.1 rmind while ((rl = TAILQ_FIRST(&rlset->rs_queue)) != NULL) {
97 1.1 rmind TAILQ_REMOVE(&rlset->rs_queue, rl, r_entry);
98 1.1 rmind npf_rule_free(rl);
99 1.1 rmind }
100 1.1 rmind kmem_free(rlset, sizeof(npf_ruleset_t));
101 1.1 rmind }
102 1.1 rmind
103 1.1 rmind /*
104 1.1 rmind * npf_ruleset_insert: insert the rule into the specified ruleset.
105 1.1 rmind *
106 1.1 rmind * Note: multiple rules at the same priority are allowed.
107 1.1 rmind */
108 1.1 rmind void
109 1.1 rmind npf_ruleset_insert(npf_ruleset_t *rlset, npf_rule_t *rl)
110 1.1 rmind {
111 1.1 rmind npf_rule_t *it;
112 1.1 rmind
113 1.1 rmind if (rl->r_attr & NPF_RULE_DEFAULT) {
114 1.1 rmind rlset->rs_default = rl;
115 1.1 rmind return;
116 1.1 rmind }
117 1.1 rmind TAILQ_FOREACH(it, &rlset->rs_queue, r_entry) {
118 1.1 rmind /* Rule priority: (highest) 0, 1, 2, 4 ... n (lowest). */
119 1.1 rmind if (it->r_priority > rl->r_priority)
120 1.1 rmind break;
121 1.1 rmind }
122 1.1 rmind if (it == NULL) {
123 1.1 rmind TAILQ_INSERT_TAIL(&rlset->rs_queue, rl, r_entry);
124 1.1 rmind } else {
125 1.1 rmind TAILQ_INSERT_BEFORE(it, rl, r_entry);
126 1.1 rmind }
127 1.1 rmind }
128 1.1 rmind
129 1.1 rmind /*
130 1.4 rmind * npf_ruleset_matchnat: find a matching NAT policy in the ruleset.
131 1.1 rmind */
132 1.4 rmind npf_rule_t *
133 1.4 rmind npf_ruleset_matchnat(npf_ruleset_t *rlset, npf_natpolicy_t *mnp)
134 1.1 rmind {
135 1.4 rmind npf_rule_t *rl;
136 1.1 rmind
137 1.4 rmind /* Find a matching NAT policy in the old ruleset. */
138 1.4 rmind TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
139 1.4 rmind if (npf_nat_matchpolicy(rl->r_natp, mnp))
140 1.4 rmind break;
141 1.4 rmind }
142 1.4 rmind return rl;
143 1.1 rmind }
144 1.1 rmind
145 1.6 rmind npf_rule_t *
146 1.6 rmind npf_ruleset_sharepm(npf_ruleset_t *rlset, npf_natpolicy_t *mnp)
147 1.6 rmind {
148 1.6 rmind npf_natpolicy_t *np;
149 1.6 rmind npf_rule_t *rl;
150 1.6 rmind
151 1.6 rmind /* Find a matching NAT policy in the old ruleset. */
152 1.6 rmind TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
153 1.6 rmind /*
154 1.6 rmind * NAT policy might not yet be set during the creation of
155 1.6 rmind * the ruleset (in such case, rule is for our policy), or
156 1.6 rmind * policies might be equal due to rule exchange on reload.
157 1.6 rmind */
158 1.6 rmind np = rl->r_natp;
159 1.6 rmind if (np == NULL || np == mnp)
160 1.6 rmind continue;
161 1.6 rmind if (npf_nat_sharepm(np, mnp))
162 1.6 rmind break;
163 1.6 rmind }
164 1.6 rmind return rl;
165 1.6 rmind }
166 1.6 rmind
167 1.1 rmind /*
168 1.4 rmind * npf_ruleset_natreload: minimum reload of NAT policies by maching
169 1.6 rmind * two (active and new) NAT rulesets.
170 1.4 rmind *
171 1.4 rmind * => Active ruleset should be exclusively locked.
172 1.1 rmind */
173 1.4 rmind void
174 1.4 rmind npf_ruleset_natreload(npf_ruleset_t *nrlset, npf_ruleset_t *arlset)
175 1.1 rmind {
176 1.4 rmind npf_natpolicy_t *np, *anp;
177 1.4 rmind npf_rule_t *rl, *arl;
178 1.4 rmind
179 1.4 rmind KASSERT(npf_core_locked());
180 1.1 rmind
181 1.4 rmind /* Scan a new NAT ruleset against NAT policies in old ruleset. */
182 1.4 rmind TAILQ_FOREACH(rl, &nrlset->rs_queue, r_entry) {
183 1.4 rmind np = rl->r_natp;
184 1.4 rmind arl = npf_ruleset_matchnat(arlset, np);
185 1.4 rmind if (arl == NULL) {
186 1.4 rmind continue;
187 1.4 rmind }
188 1.4 rmind /* On match - we exchange NAT policies. */
189 1.4 rmind anp = arl->r_natp;
190 1.4 rmind rl->r_natp = anp;
191 1.4 rmind arl->r_natp = np;
192 1.6 rmind /* Update other NAT policies to share portmap. */
193 1.6 rmind (void)npf_ruleset_sharepm(nrlset, anp);
194 1.1 rmind }
195 1.4 rmind }
196 1.4 rmind
197 1.1 rmind /*
198 1.6 rmind * npf_rule_alloc: allocate a rule and copy n-code from user-space.
199 1.4 rmind *
200 1.4 rmind * => N-code should be validated by the caller.
201 1.1 rmind */
202 1.4 rmind npf_rule_t *
203 1.6 rmind npf_rule_alloc(prop_dictionary_t rldict, npf_rproc_t *rp,
204 1.6 rmind void *nc, size_t nc_size)
205 1.1 rmind {
206 1.4 rmind npf_rule_t *rl;
207 1.7 rmind const char *rname;
208 1.7.10.1 mrg int errat __unused;
209 1.1 rmind
210 1.4 rmind /* Allocate a rule structure. */
211 1.4 rmind rl = kmem_alloc(sizeof(npf_rule_t), KM_SLEEP);
212 1.4 rmind TAILQ_INIT(&rl->r_subset.rs_queue);
213 1.4 rmind rl->r_natp = NULL;
214 1.4 rmind
215 1.4 rmind /* N-code. */
216 1.4 rmind KASSERT(nc == NULL || npf_ncode_validate(nc, nc_size, &errat) == 0);
217 1.4 rmind rl->r_ncode = nc;
218 1.4 rmind rl->r_nc_size = nc_size;
219 1.4 rmind
220 1.7 rmind /* Name (string, optional) */
221 1.7 rmind if (prop_dictionary_get_cstring_nocopy(rldict, "name", &rname)) {
222 1.7 rmind strlcpy(rl->r_name, rname, NPF_RNAME_LEN);
223 1.7 rmind } else {
224 1.7 rmind rl->r_name[0] = '\0';
225 1.7 rmind }
226 1.7 rmind
227 1.7 rmind /* Attributes, priority and interface ID. */
228 1.7 rmind prop_dictionary_get_uint32(rldict, "attributes", &rl->r_attr);
229 1.7 rmind prop_dictionary_get_int32(rldict, "priority", &rl->r_priority);
230 1.7 rmind prop_dictionary_get_uint32(rldict, "interface", &rl->r_ifid);
231 1.4 rmind
232 1.6 rmind /* Rule procedure. */
233 1.6 rmind if (rp) {
234 1.7.10.1 mrg npf_rproc_acquire(rp);
235 1.4 rmind }
236 1.6 rmind rl->r_rproc = rp;
237 1.6 rmind
238 1.4 rmind return rl;
239 1.1 rmind }
240 1.1 rmind
241 1.1 rmind /*
242 1.1 rmind * npf_rule_free: free the specified rule.
243 1.1 rmind */
244 1.1 rmind void
245 1.1 rmind npf_rule_free(npf_rule_t *rl)
246 1.1 rmind {
247 1.4 rmind npf_natpolicy_t *np = rl->r_natp;
248 1.4 rmind npf_rproc_t *rp = rl->r_rproc;
249 1.1 rmind
250 1.4 rmind if (np) {
251 1.4 rmind /* Free NAT policy. */
252 1.4 rmind npf_nat_freepolicy(np);
253 1.4 rmind }
254 1.4 rmind if (rp) {
255 1.6 rmind /* Release rule procedure. */
256 1.4 rmind npf_rproc_release(rp);
257 1.4 rmind }
258 1.1 rmind if (rl->r_ncode) {
259 1.4 rmind /* Free n-code. */
260 1.1 rmind npf_ncode_free(rl->r_ncode, rl->r_nc_size);
261 1.1 rmind }
262 1.4 rmind kmem_free(rl, sizeof(npf_rule_t));
263 1.1 rmind }
264 1.1 rmind
265 1.1 rmind /*
266 1.1 rmind * npf_rule_subset: return sub-ruleset, if any.
267 1.7.10.1 mrg * npf_rule_getrproc: acquire a reference and return rule procedure, if any.
268 1.1 rmind * npf_rule_getnat: get NAT policy assigned to the rule.
269 1.1 rmind */
270 1.1 rmind
271 1.1 rmind npf_ruleset_t *
272 1.1 rmind npf_rule_subset(npf_rule_t *rl)
273 1.1 rmind {
274 1.1 rmind return &rl->r_subset;
275 1.1 rmind }
276 1.1 rmind
277 1.7.10.1 mrg npf_rproc_t *
278 1.7.10.1 mrg npf_rule_getrproc(npf_rule_t *rl)
279 1.7.10.1 mrg {
280 1.7.10.1 mrg npf_rproc_t *rp = rl->r_rproc;
281 1.7.10.1 mrg
282 1.7.10.1 mrg KASSERT(npf_core_locked());
283 1.7.10.1 mrg if (rp) {
284 1.7.10.1 mrg npf_rproc_acquire(rp);
285 1.7.10.1 mrg }
286 1.7.10.1 mrg return rp;
287 1.7.10.1 mrg }
288 1.7.10.1 mrg
289 1.1 rmind npf_natpolicy_t *
290 1.1 rmind npf_rule_getnat(const npf_rule_t *rl)
291 1.1 rmind {
292 1.4 rmind return rl->r_natp;
293 1.1 rmind }
294 1.1 rmind
295 1.4 rmind /*
296 1.4 rmind * npf_rule_setnat: assign NAT policy to the rule and insert into the
297 1.4 rmind * NAT policy list in the ruleset.
298 1.4 rmind */
299 1.1 rmind void
300 1.1 rmind npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np)
301 1.1 rmind {
302 1.3 rmind
303 1.4 rmind KASSERT(rl->r_natp == NULL);
304 1.4 rmind rl->r_natp = np;
305 1.1 rmind }
306 1.1 rmind
307 1.7 rmind npf_rule_t *
308 1.7 rmind npf_ruleset_replace(const char *name, npf_ruleset_t *rlset)
309 1.7 rmind {
310 1.7 rmind npf_ruleset_t orlset;
311 1.7 rmind npf_rule_t *rl;
312 1.7 rmind
313 1.7 rmind npf_core_enter(); /* XXX */
314 1.7 rmind rlset = npf_core_ruleset();
315 1.7 rmind TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
316 1.7 rmind if (rl->r_name[0] == '\0')
317 1.7 rmind continue;
318 1.7 rmind if (strncmp(rl->r_name, name, NPF_RNAME_LEN))
319 1.7 rmind continue;
320 1.7 rmind memcpy(&orlset, &rl->r_subset, sizeof(npf_ruleset_t));
321 1.7 rmind break;
322 1.7 rmind }
323 1.7 rmind npf_core_exit();
324 1.7 rmind return rl;
325 1.7 rmind }
326 1.1 rmind
327 1.1 rmind /*
328 1.7 rmind * npf_ruleset_inspect: inspect the packet against the given ruleset.
329 1.1 rmind *
330 1.7 rmind * Loop through the rules in the set and run n-code processor of each rule
331 1.7 rmind * against the packet (nbuf chain). If sub-ruleset is found, inspect it.
332 1.7 rmind *
333 1.7.10.1 mrg * => Caller is responsible for nbuf chain protection.
334 1.1 rmind */
335 1.1 rmind npf_rule_t *
336 1.7 rmind npf_ruleset_inspect(npf_cache_t *npc, nbuf_t *nbuf, npf_ruleset_t *mainrlset,
337 1.6 rmind ifnet_t *ifp, const int di, const int layer)
338 1.1 rmind {
339 1.7 rmind const int di_mask = (di & PFIL_IN) ? NPF_RULE_IN : NPF_RULE_OUT;
340 1.7 rmind npf_ruleset_t *rlset = mainrlset;
341 1.1 rmind npf_rule_t *final_rl = NULL, *rl;
342 1.7 rmind bool defed = false;
343 1.1 rmind
344 1.7 rmind KASSERT(npf_core_locked());
345 1.1 rmind KASSERT(((di & PFIL_IN) != 0) ^ ((di & PFIL_OUT) != 0));
346 1.7 rmind again:
347 1.1 rmind TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
348 1.1 rmind KASSERT(!final_rl || rl->r_priority >= final_rl->r_priority);
349 1.1 rmind
350 1.1 rmind /* Match the interface. */
351 1.1 rmind if (rl->r_ifid && rl->r_ifid != ifp->if_index) {
352 1.1 rmind continue;
353 1.1 rmind }
354 1.1 rmind /* Match the direction. */
355 1.1 rmind if ((rl->r_attr & NPF_RULE_DIMASK) != NPF_RULE_DIMASK) {
356 1.1 rmind if ((rl->r_attr & di_mask) == 0)
357 1.1 rmind continue;
358 1.1 rmind }
359 1.1 rmind /* Process the n-code, if any. */
360 1.1 rmind const void *nc = rl->r_ncode;
361 1.1 rmind if (nc && npf_ncode_process(npc, nc, nbuf, layer)) {
362 1.1 rmind continue;
363 1.1 rmind }
364 1.1 rmind /* Set the matching rule and check for "final". */
365 1.1 rmind final_rl = rl;
366 1.1 rmind if (rl->r_attr & NPF_RULE_FINAL) {
367 1.2 rmind break;
368 1.1 rmind }
369 1.1 rmind }
370 1.2 rmind
371 1.2 rmind /* If no final rule, then - default. */
372 1.7 rmind if (final_rl == NULL && !defed) {
373 1.7 rmind final_rl = mainrlset->rs_default;
374 1.2 rmind defed = true;
375 1.2 rmind }
376 1.2 rmind /* Inspect the sub-ruleset, if any. */
377 1.7 rmind if (final_rl && !TAILQ_EMPTY(&final_rl->r_subset.rs_queue)) {
378 1.7 rmind rlset = &final_rl->r_subset;
379 1.7 rmind final_rl = NULL;
380 1.7 rmind goto again;
381 1.2 rmind }
382 1.7 rmind return final_rl;
383 1.1 rmind }
384 1.1 rmind
385 1.1 rmind /*
386 1.7.10.1 mrg * npf_rule_apply: apply the rule and return appropriate value.
387 1.1 rmind *
388 1.1 rmind * => Returns ENETUNREACH if "block" and 0 if "pass".
389 1.1 rmind * => Releases the ruleset lock.
390 1.1 rmind */
391 1.1 rmind int
392 1.4 rmind npf_rule_apply(npf_cache_t *npc, nbuf_t *nbuf, npf_rule_t *rl, int *retfl)
393 1.1 rmind {
394 1.4 rmind int error;
395 1.1 rmind
396 1.4 rmind KASSERT(npf_core_locked());
397 1.1 rmind
398 1.1 rmind /* If not passing - drop the packet. */
399 1.7.10.1 mrg error = (rl->r_attr & NPF_RULE_PASS) ? 0 : ENETUNREACH;
400 1.7.10.1 mrg
401 1.4 rmind *retfl = rl->r_attr;
402 1.4 rmind npf_core_exit();
403 1.7.10.1 mrg
404 1.4 rmind return error;
405 1.1 rmind }
406 1.1 rmind
407 1.1 rmind #if defined(DDB) || defined(_NPF_TESTING)
408 1.1 rmind
409 1.1 rmind void
410 1.1 rmind npf_rulenc_dump(npf_rule_t *rl)
411 1.1 rmind {
412 1.1 rmind uint32_t *op = rl->r_ncode;
413 1.1 rmind size_t n = rl->r_nc_size;
414 1.1 rmind
415 1.2 rmind while (n) {
416 1.1 rmind printf("\t> |0x%02x|\n", (uint32_t)*op);
417 1.1 rmind op++;
418 1.1 rmind n -= sizeof(*op);
419 1.2 rmind }
420 1.1 rmind printf("-> %s\n", (rl->r_attr & NPF_RULE_PASS) ? "pass" : "block");
421 1.1 rmind }
422 1.1 rmind
423 1.1 rmind #endif
424