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