xref: /netbsd-src/sys/net/npf/npf_ruleset.c (revision daf6c4152fcddc27c445489775ed1f66ab4ea9a9)
1 /*	$NetBSD: npf_ruleset.c,v 1.7 2011/02/02 02:20:25 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.7 2011/02/02 02:20:25 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 #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