npf_ruleset.c revision 1.6.2.1 1 /* $NetBSD: npf_ruleset.c,v 1.6.2.1 2011/02/08 16:20:01 bouyer Exp $ */
2
3 /*-
4 * Copyright (c) 2009-2011 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 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: npf_ruleset.c,v 1.6.2.1 2011/02/08 16:20:01 bouyer Exp $");
38
39 #include <sys/param.h>
40 #include <sys/kernel.h>
41
42 #include <sys/atomic.h>
43 #include <sys/kmem.h>
44 #include <sys/pool.h>
45 #include <sys/queue.h>
46 #include <sys/types.h>
47
48 #include <net/pfil.h>
49 #include <net/if.h>
50
51 #include "npf_ncode.h"
52 #include "npf_impl.h"
53
54 /* Ruleset structre (queue and default rule). */
55 struct npf_ruleset {
56 TAILQ_HEAD(, npf_rule) rs_queue;
57 npf_rule_t * rs_default;
58 };
59
60 /* Rule hook entry. */
61 struct npf_hook {
62 void (*hk_fn)(npf_cache_t *, nbuf_t *, void *);
63 void * hk_arg;
64 LIST_ENTRY(npf_hook) hk_entry;
65 };
66
67 #define NPF_RNAME_LEN 16
68
69 /* Rule procedure structure. */
70 struct npf_rproc {
71 /* Name. */
72 char rp_name[NPF_RNAME_LEN];
73 /* Reference count. */
74 u_int rp_refcnt;
75 uint32_t rp_flags;
76 /* Normalization options. */
77 bool rp_rnd_ipid;
78 bool rp_no_df;
79 u_int rp_minttl;
80 u_int rp_maxmss;
81 /* Logging interface. */
82 u_int rp_log_ifid;
83 };
84
85 /* Rule structure. */
86 struct npf_rule {
87 /* Rule name (optional) and list entry. */
88 char r_name[NPF_RNAME_LEN];
89 TAILQ_ENTRY(npf_rule) r_entry;
90 /* Optional: sub-ruleset, NAT policy. */
91 npf_ruleset_t r_subset;
92 npf_natpolicy_t * r_natp;
93 /* Rule priority: (highest) 0, 1, 2 ... n (lowest). */
94 pri_t r_priority;
95 /* N-code to process. */
96 void * r_ncode;
97 size_t r_nc_size;
98 /* Attributes of this rule. */
99 uint32_t r_attr;
100 /* Interface. */
101 u_int r_ifid;
102 /* Rule procedure data. */
103 npf_rproc_t * r_rproc;
104 /* List of hooks to process on match. */
105 kmutex_t r_hooks_lock;
106 LIST_HEAD(, npf_hook) r_hooks;
107 };
108
109 npf_ruleset_t *
110 npf_ruleset_create(void)
111 {
112 npf_ruleset_t *rlset;
113
114 rlset = kmem_zalloc(sizeof(npf_ruleset_t), KM_SLEEP);
115 TAILQ_INIT(&rlset->rs_queue);
116 return rlset;
117 }
118
119 void
120 npf_ruleset_destroy(npf_ruleset_t *rlset)
121 {
122 npf_rule_t *rl;
123
124 while ((rl = TAILQ_FIRST(&rlset->rs_queue)) != NULL) {
125 TAILQ_REMOVE(&rlset->rs_queue, rl, r_entry);
126 npf_rule_free(rl);
127 }
128 kmem_free(rlset, sizeof(npf_ruleset_t));
129 }
130
131 /*
132 * npf_ruleset_insert: insert the rule into the specified ruleset.
133 *
134 * Note: multiple rules at the same priority are allowed.
135 */
136 void
137 npf_ruleset_insert(npf_ruleset_t *rlset, npf_rule_t *rl)
138 {
139 npf_rule_t *it;
140
141 if (rl->r_attr & NPF_RULE_DEFAULT) {
142 rlset->rs_default = rl;
143 return;
144 }
145 TAILQ_FOREACH(it, &rlset->rs_queue, r_entry) {
146 /* Rule priority: (highest) 0, 1, 2, 4 ... n (lowest). */
147 if (it->r_priority > rl->r_priority)
148 break;
149 }
150 if (it == NULL) {
151 TAILQ_INSERT_TAIL(&rlset->rs_queue, rl, r_entry);
152 } else {
153 TAILQ_INSERT_BEFORE(it, rl, r_entry);
154 }
155 }
156
157 /*
158 * npf_ruleset_matchnat: find a matching NAT policy in the ruleset.
159 */
160 npf_rule_t *
161 npf_ruleset_matchnat(npf_ruleset_t *rlset, npf_natpolicy_t *mnp)
162 {
163 npf_rule_t *rl;
164
165 /* Find a matching NAT policy in the old ruleset. */
166 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
167 if (npf_nat_matchpolicy(rl->r_natp, mnp))
168 break;
169 }
170 return rl;
171 }
172
173 npf_rule_t *
174 npf_ruleset_sharepm(npf_ruleset_t *rlset, npf_natpolicy_t *mnp)
175 {
176 npf_natpolicy_t *np;
177 npf_rule_t *rl;
178
179 /* Find a matching NAT policy in the old ruleset. */
180 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
181 /*
182 * NAT policy might not yet be set during the creation of
183 * the ruleset (in such case, rule is for our policy), or
184 * policies might be equal due to rule exchange on reload.
185 */
186 np = rl->r_natp;
187 if (np == NULL || np == mnp)
188 continue;
189 if (npf_nat_sharepm(np, mnp))
190 break;
191 }
192 return rl;
193 }
194
195 /*
196 * npf_ruleset_natreload: minimum reload of NAT policies by maching
197 * two (active and new) NAT rulesets.
198 *
199 * => Active ruleset should be exclusively locked.
200 */
201 void
202 npf_ruleset_natreload(npf_ruleset_t *nrlset, npf_ruleset_t *arlset)
203 {
204 npf_natpolicy_t *np, *anp;
205 npf_rule_t *rl, *arl;
206
207 KASSERT(npf_core_locked());
208
209 /* Scan a new NAT ruleset against NAT policies in old ruleset. */
210 TAILQ_FOREACH(rl, &nrlset->rs_queue, r_entry) {
211 np = rl->r_natp;
212 arl = npf_ruleset_matchnat(arlset, np);
213 if (arl == NULL) {
214 continue;
215 }
216 /* On match - we exchange NAT policies. */
217 anp = arl->r_natp;
218 rl->r_natp = anp;
219 arl->r_natp = np;
220 /* Update other NAT policies to share portmap. */
221 (void)npf_ruleset_sharepm(nrlset, anp);
222 }
223 }
224
225 npf_rproc_t *
226 npf_rproc_create(prop_dictionary_t rpdict)
227 {
228 npf_rproc_t *rp;
229 const char *rname;
230
231 rp = kmem_zalloc(sizeof(npf_rproc_t), KM_SLEEP);
232 rp->rp_refcnt = 1;
233
234 /* Name and flags. */
235 prop_dictionary_get_cstring_nocopy(rpdict, "name", &rname);
236 strlcpy(rp->rp_name, rname, NPF_RNAME_LEN);
237 prop_dictionary_get_uint32(rpdict, "flags", &rp->rp_flags);
238
239 /* Logging interface ID (integer). */
240 prop_dictionary_get_uint32(rpdict, "log-interface", &rp->rp_log_ifid);
241
242 /* IP ID randomization and IP_DF flag cleansing. */
243 prop_dictionary_get_bool(rpdict, "randomize-id", &rp->rp_rnd_ipid);
244 prop_dictionary_get_bool(rpdict, "no-df", &rp->rp_no_df);
245
246 /* Minimum IP TTL and maximum TCP MSS. */
247 prop_dictionary_get_uint32(rpdict, "min-ttl", &rp->rp_minttl);
248 prop_dictionary_get_uint32(rpdict, "max-mss", &rp->rp_maxmss);
249
250 return rp;
251 }
252
253 npf_rproc_t *
254 npf_rproc_return(npf_rule_t *rl)
255 {
256 npf_rproc_t *rp = rl->r_rproc;
257
258 KASSERT(npf_core_locked());
259 if (rp) {
260 atomic_inc_uint(&rp->rp_refcnt);
261 }
262 return rp;
263 }
264
265 void
266 npf_rproc_release(npf_rproc_t *rp)
267 {
268
269 /* Destroy on last reference. */
270 if (atomic_dec_uint_nv(&rp->rp_refcnt) != 0) {
271 return;
272 }
273 kmem_free(rp, sizeof(npf_rproc_t));
274 }
275
276 void
277 npf_rproc_run(npf_cache_t *npc, nbuf_t *nbuf, npf_rproc_t *rp, int error)
278 {
279 const uint32_t flags = rp->rp_flags;
280
281 KASSERT(rp->rp_refcnt > 0);
282
283 /* Normalize the packet, if required. */
284 if ((flags & NPF_RPROC_NORMALIZE) != 0 && !error) {
285 (void)npf_normalize(npc, nbuf,
286 rp->rp_rnd_ipid, rp->rp_no_df,
287 rp->rp_minttl, rp->rp_maxmss);
288 npf_stats_inc(NPF_STAT_RPROC_NORM);
289 }
290
291 /* Log packet, if required. */
292 if ((flags & NPF_RPROC_LOG) != 0) {
293 npf_log_packet(npc, nbuf, rp->rp_log_ifid);
294 npf_stats_inc(NPF_STAT_RPROC_LOG);
295 }
296 }
297
298 /*
299 * npf_rule_alloc: allocate a rule and copy n-code from user-space.
300 *
301 * => N-code should be validated by the caller.
302 */
303 npf_rule_t *
304 npf_rule_alloc(prop_dictionary_t rldict, npf_rproc_t *rp,
305 void *nc, size_t nc_size)
306 {
307 npf_rule_t *rl;
308 const char *rname;
309 int errat;
310
311 /* Allocate a rule structure. */
312 rl = kmem_alloc(sizeof(npf_rule_t), KM_SLEEP);
313 TAILQ_INIT(&rl->r_subset.rs_queue);
314 mutex_init(&rl->r_hooks_lock, MUTEX_DEFAULT, IPL_SOFTNET);
315 LIST_INIT(&rl->r_hooks);
316 rl->r_natp = NULL;
317
318 /* N-code. */
319 (void)errat;
320 KASSERT(nc == NULL || npf_ncode_validate(nc, nc_size, &errat) == 0);
321 rl->r_ncode = nc;
322 rl->r_nc_size = nc_size;
323
324 /* Name (string, optional) */
325 if (prop_dictionary_get_cstring_nocopy(rldict, "name", &rname)) {
326 strlcpy(rl->r_name, rname, NPF_RNAME_LEN);
327 } else {
328 rl->r_name[0] = '\0';
329 }
330
331 /* Attributes, priority and interface ID. */
332 prop_dictionary_get_uint32(rldict, "attributes", &rl->r_attr);
333 prop_dictionary_get_int32(rldict, "priority", &rl->r_priority);
334 prop_dictionary_get_uint32(rldict, "interface", &rl->r_ifid);
335
336 /* Rule procedure. */
337 if (rp) {
338 atomic_inc_uint(&rp->rp_refcnt);
339 }
340 rl->r_rproc = rp;
341
342 return rl;
343 }
344
345 /*
346 * npf_rule_free: free the specified rule.
347 */
348 void
349 npf_rule_free(npf_rule_t *rl)
350 {
351 npf_natpolicy_t *np = rl->r_natp;
352 npf_rproc_t *rp = rl->r_rproc;
353
354 if (np) {
355 /* Free NAT policy. */
356 npf_nat_freepolicy(np);
357 }
358 if (rp) {
359 /* Release rule procedure. */
360 npf_rproc_release(rp);
361 }
362 if (rl->r_ncode) {
363 /* Free n-code. */
364 npf_ncode_free(rl->r_ncode, rl->r_nc_size);
365 }
366 mutex_destroy(&rl->r_hooks_lock);
367 kmem_free(rl, sizeof(npf_rule_t));
368 }
369
370 /*
371 * npf_rule_subset: return sub-ruleset, if any.
372 * npf_rule_getnat: get NAT policy assigned to the rule.
373 */
374
375 npf_ruleset_t *
376 npf_rule_subset(npf_rule_t *rl)
377 {
378 return &rl->r_subset;
379 }
380
381 npf_natpolicy_t *
382 npf_rule_getnat(const npf_rule_t *rl)
383 {
384 return rl->r_natp;
385 }
386
387 /*
388 * npf_rule_setnat: assign NAT policy to the rule and insert into the
389 * NAT policy list in the ruleset.
390 */
391 void
392 npf_rule_setnat(npf_rule_t *rl, npf_natpolicy_t *np)
393 {
394
395 KASSERT(rl->r_natp == NULL);
396 rl->r_natp = np;
397 }
398
399 #if 0
400 /*
401 * npf_hook_register: register action hook in the rule.
402 */
403 npf_hook_t *
404 npf_hook_register(npf_rule_t *rl,
405 void (*fn)(npf_cache_t *, nbuf_t *, void *), void *arg)
406 {
407 npf_hook_t *hk;
408
409 hk = kmem_alloc(sizeof(npf_hook_t), KM_SLEEP);
410 if (hk != NULL) {
411 hk->hk_fn = fn;
412 hk->hk_arg = arg;
413 mutex_enter(&rl->r_hooks_lock);
414 LIST_INSERT_HEAD(&rl->r_hooks, hk, hk_entry);
415 mutex_exit(&rl->r_hooks_lock);
416 }
417 return hk;
418 }
419
420 /*
421 * npf_hook_unregister: unregister a specified hook.
422 *
423 * => Hook should have been registered in the rule.
424 */
425 void
426 npf_hook_unregister(npf_rule_t *rl, npf_hook_t *hk)
427 {
428
429 mutex_enter(&rl->r_hooks_lock);
430 LIST_REMOVE(hk, hk_entry);
431 mutex_exit(&rl->r_hooks_lock);
432 kmem_free(hk, sizeof(npf_hook_t));
433 }
434 #endif
435
436 npf_rule_t *
437 npf_ruleset_replace(const char *name, npf_ruleset_t *rlset)
438 {
439 npf_ruleset_t orlset;
440 npf_rule_t *rl;
441
442 npf_core_enter(); /* XXX */
443 rlset = npf_core_ruleset();
444 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
445 if (rl->r_name[0] == '\0')
446 continue;
447 if (strncmp(rl->r_name, name, NPF_RNAME_LEN))
448 continue;
449 memcpy(&orlset, &rl->r_subset, sizeof(npf_ruleset_t));
450 break;
451 }
452 npf_core_exit();
453 return rl;
454 }
455
456 /*
457 * npf_ruleset_inspect: inspect the packet against the given ruleset.
458 *
459 * Loop through the rules in the set and run n-code processor of each rule
460 * against the packet (nbuf chain). If sub-ruleset is found, inspect it.
461 *
462 * => If not found, core ruleset lock is released.
463 * => Caller should protect the nbuf chain.
464 */
465 npf_rule_t *
466 npf_ruleset_inspect(npf_cache_t *npc, nbuf_t *nbuf, npf_ruleset_t *mainrlset,
467 ifnet_t *ifp, const int di, const int layer)
468 {
469 const int di_mask = (di & PFIL_IN) ? NPF_RULE_IN : NPF_RULE_OUT;
470 npf_ruleset_t *rlset = mainrlset;
471 npf_rule_t *final_rl = NULL, *rl;
472 bool defed = false;
473
474 KASSERT(npf_core_locked());
475 KASSERT(((di & PFIL_IN) != 0) ^ ((di & PFIL_OUT) != 0));
476 again:
477 TAILQ_FOREACH(rl, &rlset->rs_queue, r_entry) {
478 KASSERT(!final_rl || rl->r_priority >= final_rl->r_priority);
479
480 /* Match the interface. */
481 if (rl->r_ifid && rl->r_ifid != ifp->if_index) {
482 continue;
483 }
484 /* Match the direction. */
485 if ((rl->r_attr & NPF_RULE_DIMASK) != NPF_RULE_DIMASK) {
486 if ((rl->r_attr & di_mask) == 0)
487 continue;
488 }
489 /* Process the n-code, if any. */
490 const void *nc = rl->r_ncode;
491 if (nc && npf_ncode_process(npc, nc, nbuf, layer)) {
492 continue;
493 }
494 /* Set the matching rule and check for "final". */
495 final_rl = rl;
496 if (rl->r_attr & NPF_RULE_FINAL) {
497 break;
498 }
499 }
500
501 /* If no final rule, then - default. */
502 if (final_rl == NULL && !defed) {
503 final_rl = mainrlset->rs_default;
504 defed = true;
505 }
506 /* Inspect the sub-ruleset, if any. */
507 if (final_rl && !TAILQ_EMPTY(&final_rl->r_subset.rs_queue)) {
508 rlset = &final_rl->r_subset;
509 final_rl = NULL;
510 goto again;
511 }
512 if (final_rl == NULL) {
513 npf_core_exit();
514 }
515 return final_rl;
516 }
517
518 /*
519 * npf_rule_apply: apply the rule i.e. run hooks and return appropriate value.
520 *
521 * => Returns ENETUNREACH if "block" and 0 if "pass".
522 * => Releases the ruleset lock.
523 */
524 int
525 npf_rule_apply(npf_cache_t *npc, nbuf_t *nbuf, npf_rule_t *rl, int *retfl)
526 {
527 npf_hook_t *hk;
528 int error;
529
530 KASSERT(npf_core_locked());
531
532 /* If not passing - drop the packet. */
533 if ((rl->r_attr & NPF_RULE_PASS) == 0) {
534 error = ENETUNREACH;
535 goto done;
536 }
537 error = 0;
538
539 /* Passing. Run the hooks. */
540 LIST_FOREACH(hk, &rl->r_hooks, hk_entry) {
541 KASSERT(hk->hk_fn != NULL);
542 (*hk->hk_fn)(npc, nbuf, hk->hk_arg);
543 }
544 done:
545 *retfl = rl->r_attr;
546 npf_core_exit();
547 return error;
548 }
549
550 #if defined(DDB) || defined(_NPF_TESTING)
551
552 void
553 npf_rulenc_dump(npf_rule_t *rl)
554 {
555 uint32_t *op = rl->r_ncode;
556 size_t n = rl->r_nc_size;
557
558 while (n) {
559 printf("\t> |0x%02x|\n", (uint32_t)*op);
560 op++;
561 n -= sizeof(*op);
562 }
563 printf("-> %s\n", (rl->r_attr & NPF_RULE_PASS) ? "pass" : "block");
564 }
565
566 #endif
567