npf_ruleset.c revision 1.5 1 /* $NetBSD: npf_ruleset.c,v 1.5 2010/12/27 14:58:55 uebayasi Exp $ */
2
3 /*-
4 * Copyright (c) 2009-2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This material is based upon work partially supported by The
8 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * NPF ruleset module.
34 */
35
36 #ifdef _KERNEL
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.5 2010/12/27 14:58:55 uebayasi Exp $");
39
40 #include <sys/param.h>
41 #include <sys/kernel.h>
42
43 #include <sys/atomic.h>
44 #include <sys/kmem.h>
45 #include <sys/pool.h>
46 #include <sys/queue.h>
47 #include <sys/types.h>
48
49 #include <net/pfil.h>
50 #include <net/if.h>
51 #endif
52
53 #include "npf_ncode.h"
54 #include "npf_impl.h"
55
56 /* Ruleset structre (queue and default rule). */
57 struct npf_ruleset {
58 TAILQ_HEAD(, npf_rule) rs_queue;
59 npf_rule_t * rs_default;
60 };
61
62 /* Rule hook entry. */
63 struct npf_hook {
64 void (*hk_fn)(npf_cache_t *, nbuf_t *, void *);
65 void * hk_arg;
66 LIST_ENTRY(npf_hook) hk_entry;
67 };
68
69 /* Rule processing structure. */
70 struct npf_rproc {
71 /* Reference count. */
72 u_int rp_refcnt;
73 /* Normalization options. */
74 bool rp_rnd_ipid;
75 bool rp_no_df;
76 u_int rp_minttl;
77 u_int rp_maxmss;
78 /* Logging interface. */
79 u_int rp_log_ifid;
80 };
81
82 /* Rule structure. */
83 struct npf_rule {
84 TAILQ_ENTRY(npf_rule) r_entry;
85 /* Optional: sub-ruleset, NAT policy. */
86 npf_ruleset_t r_subset;
87 npf_natpolicy_t * r_natp;
88 /* Rule priority: (highest) 0, 1, 2 ... n (lowest). */
89 u_int r_priority;
90 /* N-code to process. */
91 void * r_ncode;
92 size_t r_nc_size;
93 /* Attributes of this rule. */
94 uint32_t r_attr;
95 /* Interface. */
96 u_int r_ifid;
97 /* Hit counter. */
98 u_long r_hitcount;
99 /* Rule processing data. */
100 npf_rproc_t * r_rproc;
101 /* List of hooks to process on match. */
102 kmutex_t r_hooks_lock;
103 LIST_HEAD(, npf_hook) r_hooks;
104 };
105
106 npf_ruleset_t *
107 npf_ruleset_create(void)
108 {
109 npf_ruleset_t *rlset;
110
111 rlset = kmem_zalloc(sizeof(npf_ruleset_t), KM_SLEEP);
112 TAILQ_INIT(&rlset->rs_queue);
113 return rlset;
114 }
115
116 void
117 npf_ruleset_destroy(npf_ruleset_t *rlset)
118 {
119 npf_rule_t *rl;
120
121 while ((rl = TAILQ_FIRST(&rlset->rs_queue)) != NULL) {
122 TAILQ_REMOVE(&rlset->rs_queue, rl, r_entry);
123 npf_rule_free(rl);
124 }
125 kmem_free(rlset, sizeof(npf_ruleset_t));
126 }
127
128 /*
129 * npf_ruleset_insert: insert the rule into the specified ruleset.
130 *
131 * Note: multiple rules at the same priority are allowed.
132 */
133 void
134 npf_ruleset_insert(npf_ruleset_t *rlset, npf_rule_t *rl)
135 {
136 npf_rule_t *it;
137
138 if (rl->r_attr & NPF_RULE_DEFAULT) {
139 rlset->rs_default = rl;
140 return;
141 }
142 TAILQ_FOREACH(it, &rlset->rs_queue, r_entry) {
143 /* Rule priority: (highest) 0, 1, 2, 4 ... n (lowest). */
144 if (it->r_priority > rl->r_priority)
145 break;
146 }
147 if (it == NULL) {
148 TAILQ_INSERT_TAIL(&rlset->rs_queue, rl, r_entry);
149 } else {
150 TAILQ_INSERT_BEFORE(it, rl, r_entry);
151 }
152 }
153
154 /*
155 * npf_ruleset_matchnat: find a matching NAT policy in the ruleset.
156 */
157 npf_rule_t *
158 npf_ruleset_matchnat(npf_ruleset_t *rlset, npf_natpolicy_t *mnp)
159 {
160 npf_rule_t *rl;
161
162 /* Find a matching NAT policy in the old ruleset. */
163 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
164 if (npf_nat_matchpolicy(rl->r_natp, mnp))
165 break;
166 }
167 return rl;
168 }
169
170 /*
171 * npf_ruleset_natreload: minimum reload of NAT policies by maching
172 * two (active and new) NAT rulesets.
173 *
174 * => Active ruleset should be exclusively locked.
175 */
176 void
177 npf_ruleset_natreload(npf_ruleset_t *nrlset, npf_ruleset_t *arlset)
178 {
179 npf_natpolicy_t *np, *anp;
180 npf_rule_t *rl, *arl;
181
182 KASSERT(npf_core_locked());
183
184 /* Scan a new NAT ruleset against NAT policies in old ruleset. */
185 TAILQ_FOREACH(rl, &nrlset->rs_queue, r_entry) {
186 np = rl->r_natp;
187 arl = npf_ruleset_matchnat(arlset, np);
188 if (arl == NULL) {
189 continue;
190 }
191 /* On match - we exchange NAT policies. */
192 anp = arl->r_natp;
193 rl->r_natp = anp;
194 arl->r_natp = np;
195 }
196 }
197
198 npf_rproc_t *
199 npf_rproc_create(prop_dictionary_t rpdict)
200 {
201 npf_rproc_t *rp;
202 prop_object_t obj;
203
204 rp = kmem_alloc(sizeof(npf_rproc_t), KM_SLEEP);
205 rp->rp_refcnt = 1;
206
207 /* Logging interface ID (integer). */
208 obj = prop_dictionary_get(rpdict, "log-interface");
209 rp->rp_log_ifid = prop_number_integer_value(obj);
210
211 /* Randomize IP ID (bool). */
212 obj = prop_dictionary_get(rpdict, "randomize-id");
213 rp->rp_rnd_ipid = prop_bool_true(obj);
214
215 /* IP_DF flag cleansing (bool). */
216 obj = prop_dictionary_get(rpdict, "no-df");
217 rp->rp_no_df = prop_bool_true(obj);
218
219 /* Minimum IP TTL (integer). */
220 obj = prop_dictionary_get(rpdict, "min-ttl");
221 rp->rp_minttl = prop_number_integer_value(obj);
222
223 /* Maximum TCP MSS (integer). */
224 obj = prop_dictionary_get(rpdict, "max-mss");
225 rp->rp_maxmss = prop_number_integer_value(obj);
226
227 return rp;
228 }
229
230 npf_rproc_t *
231 npf_rproc_return(npf_rule_t *rl)
232 {
233 npf_rproc_t *rp = rl->r_rproc;
234
235 if (rp) {
236 atomic_inc_uint(&rp->rp_refcnt);
237 }
238 return rp;
239 }
240
241 void
242 npf_rproc_release(npf_rproc_t *rp)
243 {
244
245 /* Destroy on last reference. */
246 if (atomic_dec_uint_nv(&rp->rp_refcnt) != 0) {
247 return;
248 }
249 kmem_free(rp, sizeof(npf_rproc_t));
250 }
251
252 void
253 npf_rproc_run(npf_cache_t *npc, nbuf_t *nbuf, npf_rproc_t *rp)
254 {
255
256 KASSERT(rp->rp_refcnt > 0);
257
258 /* Normalize the packet, if required. */
259 (void)npf_normalize(npc, nbuf,
260 rp->rp_rnd_ipid, rp->rp_no_df, rp->rp_minttl, rp->rp_maxmss);
261
262 /* Log packet, if required. */
263 if (rp->rp_log_ifid) {
264 npf_log_packet(npc, nbuf, rp->rp_log_ifid);
265 }
266
267 }
268
269 /*
270 * npf_rule_alloc: allocate a rule and copy ncode from user-space.
271 *
272 * => N-code should be validated by the caller.
273 */
274 npf_rule_t *
275 npf_rule_alloc(prop_dictionary_t rldict, void *nc, size_t nc_size)
276 {
277 npf_rule_t *rl;
278 prop_object_t obj;
279 #ifdef DIAGNOSTIC
280 int errat;
281 #endif
282
283 /* Allocate a rule structure. */
284 rl = kmem_alloc(sizeof(npf_rule_t), KM_SLEEP);
285 TAILQ_INIT(&rl->r_subset.rs_queue);
286 mutex_init(&rl->r_hooks_lock, MUTEX_DEFAULT, IPL_SOFTNET);
287 LIST_INIT(&rl->r_hooks);
288 rl->r_hitcount = 0;
289 rl->r_natp = NULL;
290
291 /* N-code. */
292 KASSERT(nc == NULL || npf_ncode_validate(nc, nc_size, &errat) == 0);
293 rl->r_ncode = nc;
294 rl->r_nc_size = nc_size;
295
296 /* Attributes (integer). */
297 obj = prop_dictionary_get(rldict, "attributes");
298 rl->r_attr = prop_number_integer_value(obj);
299
300 /* Priority (integer). */
301 obj = prop_dictionary_get(rldict, "priority");
302 rl->r_priority = prop_number_integer_value(obj);
303
304 /* Interface ID (integer). */
305 obj = prop_dictionary_get(rldict, "interface");
306 rl->r_ifid = prop_number_integer_value(obj);
307
308 /* Create rule processing structure, if any. */
309 if (rl->r_attr & (NPF_RULE_LOG | NPF_RULE_NORMALIZE)) {
310 rl->r_rproc = npf_rproc_create(rldict);
311 } else {
312 rl->r_rproc = NULL;
313 }
314 return rl;
315 }
316
317 /*
318 * npf_rule_free: free the specified rule.
319 */
320 void
321 npf_rule_free(npf_rule_t *rl)
322 {
323 npf_natpolicy_t *np = rl->r_natp;
324 npf_rproc_t *rp = rl->r_rproc;
325
326 if (np) {
327 /* Free NAT policy. */
328 npf_nat_freepolicy(np);
329 }
330 if (rp) {
331 /* Release/free rule processing structure. */
332 npf_rproc_release(rp);
333 }
334 if (rl->r_ncode) {
335 /* Free n-code. */
336 npf_ncode_free(rl->r_ncode, rl->r_nc_size);
337 }
338 mutex_destroy(&rl->r_hooks_lock);
339 kmem_free(rl, sizeof(npf_rule_t));
340 }
341
342 /*
343 * npf_rule_subset: return sub-ruleset, if any.
344 * npf_rule_getnat: get NAT policy assigned to the rule.
345 */
346
347 npf_ruleset_t *
348 npf_rule_subset(npf_rule_t *rl)
349 {
350 return &rl->r_subset;
351 }
352
353 npf_natpolicy_t *
354 npf_rule_getnat(const npf_rule_t *rl)
355 {
356 return rl->r_natp;
357 }
358
359 /*
360 * npf_rule_setnat: assign NAT policy to the rule and insert into the
361 * NAT policy list in the ruleset.
362 */
363 void
364 npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np)
365 {
366
367 KASSERT(rl->r_natp == NULL);
368 rl->r_natp = np;
369 }
370
371 /*
372 * npf_hook_register: register action hook in the rule.
373 */
374 npf_hook_t *
375 npf_hook_register(npf_rule_t *rl,
376 void (*fn)(npf_cache_t *, nbuf_t *, void *), void *arg)
377 {
378 npf_hook_t *hk;
379
380 hk = kmem_alloc(sizeof(npf_hook_t), KM_SLEEP);
381 if (hk != NULL) {
382 hk->hk_fn = fn;
383 hk->hk_arg = arg;
384 mutex_enter(&rl->r_hooks_lock);
385 LIST_INSERT_HEAD(&rl->r_hooks, hk, hk_entry);
386 mutex_exit(&rl->r_hooks_lock);
387 }
388 return hk;
389 }
390
391 /*
392 * npf_hook_unregister: unregister a specified hook.
393 *
394 * => Hook should have been registered in the rule.
395 */
396 void
397 npf_hook_unregister(npf_rule_t *rl, npf_hook_t *hk)
398 {
399
400 mutex_enter(&rl->r_hooks_lock);
401 LIST_REMOVE(hk, hk_entry);
402 mutex_exit(&rl->r_hooks_lock);
403 kmem_free(hk, sizeof(npf_hook_t));
404 }
405
406 /*
407 * npf_ruleset_match: inspect the packet against the given ruleset.
408 *
409 * Loop for each rule in the set and run n-code processor of each rule
410 * against the packet (nbuf chain).
411 */
412 npf_rule_t *
413 npf_ruleset_match(npf_ruleset_t *rlset, npf_cache_t *npc, nbuf_t *nbuf,
414 struct ifnet *ifp, const int di, const int layer)
415 {
416 npf_rule_t *final_rl = NULL, *rl;
417
418 KASSERT(((di & PFIL_IN) != 0) ^ ((di & PFIL_OUT) != 0));
419
420 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
421 KASSERT(!final_rl || rl->r_priority >= final_rl->r_priority);
422
423 /* Match the interface. */
424 if (rl->r_ifid && rl->r_ifid != ifp->if_index) {
425 continue;
426 }
427 /* Match the direction. */
428 if ((rl->r_attr & NPF_RULE_DIMASK) != NPF_RULE_DIMASK) {
429 const int di_mask =
430 (di & PFIL_IN) ? NPF_RULE_IN : NPF_RULE_OUT;
431
432 if ((rl->r_attr & di_mask) == 0)
433 continue;
434 }
435 /* Process the n-code, if any. */
436 const void *nc = rl->r_ncode;
437 if (nc && npf_ncode_process(npc, nc, nbuf, layer)) {
438 continue;
439 }
440 /* Set the matching rule and check for "final". */
441 final_rl = rl;
442 if (rl->r_attr & NPF_RULE_FINAL) {
443 break;
444 }
445 }
446 return final_rl;
447 }
448
449 /*
450 * npf_ruleset_inspect: inspection of the main ruleset for filtering.
451 * If sub-ruleset is found, inspect it.
452 *
453 * => If found, ruleset is kept read-locked.
454 * => Caller should protect the nbuf chain.
455 */
456 npf_rule_t *
457 npf_ruleset_inspect(npf_cache_t *npc, nbuf_t *nbuf,
458 struct ifnet *ifp, const int di, const int layer)
459 {
460 npf_ruleset_t *rlset;
461 npf_rule_t *rl;
462 bool defed;
463
464 defed = false;
465 npf_core_enter();
466 rlset = npf_core_ruleset();
467 reinspect:
468 rl = npf_ruleset_match(rlset, npc, nbuf, ifp, di, layer);
469
470 /* If no final rule, then - default. */
471 if (rl == NULL && !defed) {
472 npf_ruleset_t *mainrlset = npf_core_ruleset();
473 rl = mainrlset->rs_default;
474 defed = true;
475 }
476 /* Inspect the sub-ruleset, if any. */
477 if (rl && !TAILQ_EMPTY(&rl->r_subset.rs_queue)) {
478 rlset = &rl->r_subset;
479 goto reinspect;
480 }
481 if (rl == NULL) {
482 npf_core_exit();
483 }
484 return rl;
485 }
486
487 /*
488 * npf_rule_apply: apply the rule i.e. run hooks and return appropriate value.
489 *
490 * => Returns ENETUNREACH if "block" and 0 if "pass".
491 * => Releases the ruleset lock.
492 */
493 int
494 npf_rule_apply(npf_cache_t *npc, nbuf_t *nbuf, npf_rule_t *rl, int *retfl)
495 {
496 npf_hook_t *hk;
497 int error;
498
499 KASSERT(npf_core_locked());
500
501 /* Update the "hit" counter. */
502 if (rl->r_attr & NPF_RULE_COUNT) {
503 atomic_inc_ulong(&rl->r_hitcount);
504 }
505
506 /* If not passing - drop the packet. */
507 if ((rl->r_attr & NPF_RULE_PASS) == 0) {
508 error = ENETUNREACH;
509 goto done;
510 }
511 error = 0;
512
513 /* Passing. Run the hooks. */
514 LIST_FOREACH(hk, &rl->r_hooks, hk_entry) {
515 KASSERT(hk->hk_fn != NULL);
516 (*hk->hk_fn)(npc, nbuf, hk->hk_arg);
517 }
518 done:
519 *retfl = rl->r_attr;
520 npf_core_exit();
521 return error;
522 }
523
524 #if defined(DDB) || defined(_NPF_TESTING)
525
526 void
527 npf_rulenc_dump(npf_rule_t *rl)
528 {
529 uint32_t *op = rl->r_ncode;
530 size_t n = rl->r_nc_size;
531
532 while (n) {
533 printf("\t> |0x%02x|\n", (uint32_t)*op);
534 op++;
535 n -= sizeof(*op);
536 }
537 printf("-> %s\n", (rl->r_attr & NPF_RULE_PASS) ? "pass" : "block");
538 }
539
540 #endif
541