npf_ctl.c revision 1.16 1 /* $NetBSD: npf_ctl.c,v 1.16 2012/07/15 00:23:00 rmind Exp $ */
2
3 /*-
4 * Copyright (c) 2009-2012 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 device control.
34 *
35 * Implementation of (re)loading, construction of tables and rules.
36 * NPF proplib(9) dictionary consumer.
37 */
38
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.16 2012/07/15 00:23:00 rmind Exp $");
41
42 #include <sys/param.h>
43 #include <sys/conf.h>
44
45 #include <prop/proplib.h>
46
47 #include "npf_ncode.h"
48 #include "npf_impl.h"
49
50 #if defined(DEBUG) || defined(DIAGNOSTIC)
51 #define NPF_ERR_DEBUG(e) \
52 prop_dictionary_set_cstring_nocopy((e), "source-file", __FILE__); \
53 prop_dictionary_set_uint32((e), "source-line", __LINE__);
54 #else
55 #define NPF_ERR_DEBUG(e)
56 #endif
57
58 /*
59 * npfctl_switch: enable or disable packet inspection.
60 */
61 int
62 npfctl_switch(void *data)
63 {
64 const bool onoff = *(int *)data ? true : false;
65 int error;
66
67 if (onoff) {
68 /* Enable: add pfil hooks. */
69 error = npf_pfil_register();
70 } else {
71 /* Disable: remove pfil hooks. */
72 npf_pfil_unregister();
73 error = 0;
74 }
75 return error;
76 }
77
78 static int __noinline
79 npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables,
80 prop_dictionary_t errdict)
81 {
82 prop_object_iterator_t it;
83 prop_dictionary_t tbldict;
84 int error = 0;
85
86 /* Tables - array. */
87 if (prop_object_type(tables) != PROP_TYPE_ARRAY) {
88 NPF_ERR_DEBUG(errdict);
89 return EINVAL;
90 }
91
92 it = prop_array_iterator(tables);
93 while ((tbldict = prop_object_iterator_next(it)) != NULL) {
94 prop_dictionary_t ent;
95 prop_object_iterator_t eit;
96 prop_array_t entries;
97 npf_table_t *t;
98 u_int tid;
99 int type;
100
101 /* Table - dictionary. */
102 if (prop_object_type(tbldict) != PROP_TYPE_DICTIONARY) {
103 NPF_ERR_DEBUG(errdict);
104 error = EINVAL;
105 break;
106 }
107
108 /* Table ID and type. */
109 prop_dictionary_get_uint32(tbldict, "id", &tid);
110 prop_dictionary_get_int32(tbldict, "type", &type);
111
112 /* Validate them, check for duplicate IDs. */
113 error = npf_table_check(tblset, tid, type);
114 if (error)
115 break;
116
117 /* Create and insert the table. */
118 t = npf_table_create(tid, type, 1024); /* XXX */
119 if (t == NULL) {
120 NPF_ERR_DEBUG(errdict);
121 error = ENOMEM;
122 break;
123 }
124 error = npf_tableset_insert(tblset, t);
125 KASSERT(error == 0);
126
127 /* Entries. */
128 entries = prop_dictionary_get(tbldict, "entries");
129 if (prop_object_type(entries) != PROP_TYPE_ARRAY) {
130 NPF_ERR_DEBUG(errdict);
131 error = EINVAL;
132 break;
133 }
134 eit = prop_array_iterator(entries);
135 while ((ent = prop_object_iterator_next(eit)) != NULL) {
136 const npf_addr_t *addr;
137 npf_netmask_t mask;
138 int alen;
139
140 /* Get address and mask. Add a table entry. */
141 prop_object_t obj = prop_dictionary_get(ent, "addr");
142 addr = (const npf_addr_t *)prop_data_data_nocopy(obj);
143 prop_dictionary_get_uint8(ent, "mask", &mask);
144 alen = prop_data_size(obj);
145
146 error = npf_table_insert(tblset, tid, alen, addr, mask);
147 if (error)
148 break;
149 }
150 prop_object_iterator_release(eit);
151 if (error)
152 break;
153 }
154 prop_object_iterator_release(it);
155 /*
156 * Note: in a case of error, caller will free the tableset.
157 */
158 return error;
159 }
160
161 static npf_rproc_t *
162 npf_mk_rproc(prop_array_t rprocs, const char *rpname)
163 {
164 prop_object_iterator_t it;
165 prop_dictionary_t rpdict;
166 npf_rproc_t *rp;
167
168 it = prop_array_iterator(rprocs);
169 while ((rpdict = prop_object_iterator_next(it)) != NULL) {
170 const char *iname;
171 prop_dictionary_get_cstring_nocopy(rpdict, "name", &iname);
172 KASSERT(iname != NULL);
173 if (strcmp(rpname, iname) == 0)
174 break;
175 }
176 prop_object_iterator_release(it);
177 if (rpdict == NULL) {
178 return NULL;
179 }
180 CTASSERT(sizeof(uintptr_t) <= sizeof(uint64_t));
181 if (!prop_dictionary_get_uint64(rpdict, "rproc-ptr", (uint64_t *)&rp)) {
182 rp = npf_rproc_create(rpdict);
183 prop_dictionary_set_uint64(rpdict, "rproc-ptr",
184 (uint64_t)(uintptr_t)rp);
185 }
186 return rp;
187 }
188
189 static int __noinline
190 npf_mk_ncode(prop_object_t obj, void **code, size_t *csize,
191 prop_dictionary_t errdict)
192 {
193 const void *ncptr;
194 int nc_err, errat;
195 size_t nc_size;
196 void *nc;
197
198 /*
199 * Allocate, copy and validate n-code. XXX: Inefficient.
200 */
201 ncptr = prop_data_data_nocopy(obj);
202 nc_size = prop_data_size(obj);
203 if (ncptr == NULL || nc_size > NPF_NCODE_LIMIT) {
204 NPF_ERR_DEBUG(errdict);
205 return ERANGE;
206 }
207 nc = npf_ncode_alloc(nc_size);
208 if (nc == NULL) {
209 NPF_ERR_DEBUG(errdict);
210 return ENOMEM;
211 }
212 memcpy(nc, ncptr, nc_size);
213 nc_err = npf_ncode_validate(nc, nc_size, &errat);
214 if (nc_err) {
215 npf_ncode_free(nc, nc_size);
216 prop_dictionary_set_int32(errdict, "ncode-error", nc_err);
217 prop_dictionary_set_int32(errdict, "ncode-errat", errat);
218 return EINVAL;
219 }
220 *code = nc;
221 *csize = nc_size;
222 return 0;
223 }
224
225 static int __noinline
226 npf_mk_singlerule(prop_dictionary_t rldict, prop_array_t rps, npf_rule_t **rl,
227 prop_dictionary_t errdict)
228 {
229 const char *rnm;
230 npf_rproc_t *rp;
231 prop_object_t obj;
232 size_t nc_size;
233 void *nc;
234 int p, error;
235
236 /* Rule - dictionary. */
237 if (prop_object_type(rldict) != PROP_TYPE_DICTIONARY) {
238 NPF_ERR_DEBUG(errdict);
239 return EINVAL;
240 }
241
242 error = 0;
243 obj = prop_dictionary_get(rldict, "ncode");
244 if (obj) {
245 /* N-code (binary data). */
246 error = npf_mk_ncode(obj, &nc, &nc_size, errdict);
247 if (error) {
248 goto err;
249 }
250 } else {
251 /* No n-code. */
252 nc = NULL;
253 nc_size = 0;
254 }
255
256 /* Check for rule procedure. */
257 if (rps && prop_dictionary_get_cstring_nocopy(rldict, "rproc", &rnm)) {
258 rp = npf_mk_rproc(rps, rnm);
259 if (rp == NULL) {
260 if (nc) {
261 npf_ncode_free(nc, nc_size); /* XXX */
262 }
263 NPF_ERR_DEBUG(errdict);
264 error = EINVAL;
265 goto err;
266 }
267 } else {
268 rp = NULL;
269 }
270
271 /* Finally, allocate and return the rule. */
272 *rl = npf_rule_alloc(rldict, rp, nc, nc_size);
273 KASSERT(*rl != NULL);
274 return 0;
275 err:
276 prop_dictionary_get_int32(rldict, "priority", &p); /* XXX */
277 prop_dictionary_set_int32(errdict, "id", p);
278 return error;
279 }
280
281 static int __noinline
282 npf_mk_subrules(npf_ruleset_t *rlset, prop_array_t rules, prop_array_t rprocs,
283 prop_dictionary_t errdict)
284 {
285 prop_object_iterator_t it;
286 prop_dictionary_t rldict;
287 int error = 0;
288
289 if (prop_object_type(rules) != PROP_TYPE_ARRAY) {
290 NPF_ERR_DEBUG(errdict);
291 return EINVAL;
292 }
293 it = prop_array_iterator(rules);
294 while ((rldict = prop_object_iterator_next(it)) != NULL) {
295 npf_rule_t *rl;
296 error = npf_mk_singlerule(rldict, rprocs, &rl, errdict);
297 if (error) {
298 break;
299 }
300 npf_ruleset_insert(rlset, rl);
301 }
302 prop_object_iterator_release(it);
303 return error;
304 }
305
306 static int __noinline
307 npf_mk_rules(npf_ruleset_t *rlset, prop_array_t rules, prop_array_t rprocs,
308 prop_dictionary_t errdict)
309 {
310 prop_object_iterator_t it;
311 prop_dictionary_t rldict, rpdict;
312 int error;
313
314 /* Rule procedures and the ruleset - arrays. */
315 if (prop_object_type(rprocs) != PROP_TYPE_ARRAY ||
316 prop_object_type(rules) != PROP_TYPE_ARRAY) {
317 NPF_ERR_DEBUG(errdict);
318 return EINVAL;
319 }
320
321 it = prop_array_iterator(rprocs);
322 while ((rpdict = prop_object_iterator_next(it)) != NULL) {
323 if (prop_dictionary_get(rpdict, "rproc-ptr")) {
324 prop_object_iterator_release(it);
325 NPF_ERR_DEBUG(errdict);
326 return EINVAL;
327 }
328 }
329 prop_object_iterator_release(it);
330
331 error = 0;
332 it = prop_array_iterator(rules);
333 while ((rldict = prop_object_iterator_next(it)) != NULL) {
334 prop_array_t subrules;
335 npf_ruleset_t *rlsetsub;
336 npf_rule_t *rl;
337
338 /* Generate a single rule. */
339 error = npf_mk_singlerule(rldict, rprocs, &rl, errdict);
340 if (error) {
341 break;
342 }
343 npf_ruleset_insert(rlset, rl);
344
345 /* Check for sub-rules and generate, if any. */
346 subrules = prop_dictionary_get(rldict, "subrules");
347 if (subrules == NULL) {
348 /* No subrules, next.. */
349 continue;
350 }
351 rlsetsub = npf_rule_subset(rl);
352 error = npf_mk_subrules(rlsetsub, subrules, rprocs, errdict);
353 if (error)
354 break;
355 }
356 prop_object_iterator_release(it);
357 /*
358 * Note: in a case of error, caller will free the ruleset.
359 */
360 return error;
361 }
362
363 static int __noinline
364 npf_mk_natlist(npf_ruleset_t *nset, prop_array_t natlist,
365 prop_dictionary_t errdict)
366 {
367 prop_object_iterator_t it;
368 prop_dictionary_t natdict;
369 int error;
370
371 /* NAT policies - array. */
372 if (prop_object_type(natlist) != PROP_TYPE_ARRAY) {
373 NPF_ERR_DEBUG(errdict);
374 return EINVAL;
375 }
376
377 error = 0;
378 it = prop_array_iterator(natlist);
379 while ((natdict = prop_object_iterator_next(it)) != NULL) {
380 npf_natpolicy_t *np;
381 npf_rule_t *rl;
382
383 /* NAT policy - dictionary. */
384 if (prop_object_type(natdict) != PROP_TYPE_DICTIONARY) {
385 NPF_ERR_DEBUG(errdict);
386 error = EINVAL;
387 break;
388 }
389
390 /*
391 * NAT policies are standard rules, plus additional
392 * information for translation. Make a rule.
393 */
394 error = npf_mk_singlerule(natdict, NULL, &rl, errdict);
395 if (error) {
396 break;
397 }
398 npf_ruleset_insert(nset, rl);
399
400 /* If rule is named, it is a group with NAT policies. */
401 if (prop_dictionary_get(natdict, "name") &&
402 prop_dictionary_get(natdict, "subrules")) {
403 continue;
404 }
405
406 /* Allocate a new NAT policy and assign to the rule. */
407 np = npf_nat_newpolicy(natdict, nset);
408 if (np == NULL) {
409 NPF_ERR_DEBUG(errdict);
410 error = ENOMEM;
411 break;
412 }
413 npf_rule_setnat(rl, np);
414 }
415 prop_object_iterator_release(it);
416 /*
417 * Note: in a case of error, caller will free entire NAT ruleset
418 * with assigned NAT policies.
419 */
420 return error;
421 }
422
423 /*
424 * npfctl_reload: store passed data i.e. update settings, create passed
425 * tables, rules and atomically activate all them.
426 */
427 int
428 npfctl_reload(u_long cmd, void *data)
429 {
430 struct plistref *pref = data;
431 prop_dictionary_t npf_dict, errdict;
432 prop_array_t natlist, tables, rprocs, rules;
433 npf_tableset_t *tblset = NULL;
434 npf_ruleset_t *rlset = NULL;
435 npf_ruleset_t *nset = NULL;
436 bool flush;
437 int error;
438
439 /* Retrieve the dictionary. */
440 #ifndef _NPF_TESTING
441 error = prop_dictionary_copyin_ioctl(pref, cmd, &npf_dict);
442 if (error)
443 return error;
444 #else
445 npf_dict = (prop_dictionary_t)pref;
446 #endif
447
448 /* Dictionary for error reporting. */
449 errdict = prop_dictionary_create();
450
451 /* NAT policies. */
452 nset = npf_ruleset_create();
453 natlist = prop_dictionary_get(npf_dict, "translation");
454 error = npf_mk_natlist(nset, natlist, errdict);
455 if (error) {
456 goto fail;
457 }
458
459 /* Tables. */
460 tblset = npf_tableset_create();
461 tables = prop_dictionary_get(npf_dict, "tables");
462 error = npf_mk_tables(tblset, tables, errdict);
463 if (error) {
464 goto fail;
465 }
466
467 /* Rules and rule procedures. */
468 rlset = npf_ruleset_create();
469 rprocs = prop_dictionary_get(npf_dict, "rprocs");
470 rules = prop_dictionary_get(npf_dict, "rules");
471 error = npf_mk_rules(rlset, rules, rprocs, errdict);
472 if (error) {
473 goto fail;
474 }
475
476 flush = false;
477 prop_dictionary_get_bool(npf_dict, "flush", &flush);
478
479 /*
480 * Finally - reload ruleset, tableset and NAT policies.
481 * Operation will be performed as a single transaction.
482 */
483 npf_reload(npf_dict, rlset, tblset, nset, flush);
484
485 /* Turn on/off session tracking accordingly. */
486 npf_session_tracking(!flush);
487
488 /* Done. Since data is consumed now, we shall not destroy it. */
489 tblset = NULL;
490 rlset = NULL;
491 nset = NULL;
492 fail:
493 /*
494 * Note: destroy rulesets first, to drop references to the tableset.
495 */
496 KASSERT(error == 0 || (nset || rlset || tblset));
497 if (nset) {
498 npf_ruleset_destroy(nset);
499 }
500 if (rlset) {
501 npf_ruleset_destroy(rlset);
502 }
503 if (tblset) {
504 npf_tableset_destroy(tblset);
505 }
506 if (error) {
507 prop_object_release(npf_dict);
508 }
509
510 /* Error report. */
511 prop_dictionary_set_int32(errdict, "errno", error);
512 #ifndef _NPF_TESTING
513 prop_dictionary_copyout_ioctl(pref, cmd, errdict);
514 #endif
515 prop_object_release(errdict);
516 return 0;
517 }
518
519 int
520 npfctl_getconf(u_long cmd, void *data)
521 {
522 struct plistref *pref = data;
523 prop_dictionary_t npf_dict;
524 int error;
525
526 npf_core_enter();
527 npf_dict = npf_core_dict();
528 prop_dictionary_set_bool(npf_dict, "active", npf_pfil_registered_p());
529 error = prop_dictionary_copyout_ioctl(pref, cmd, npf_dict);
530 npf_core_exit();
531
532 return error;
533 }
534
535 /*
536 * npfctl_update_rule: reload a specific rule identified by the name.
537 */
538 int
539 npfctl_update_rule(u_long cmd, void *data)
540 {
541 struct plistref *pref = data;
542 prop_dictionary_t dict, errdict;
543 prop_array_t subrules;
544 prop_object_t obj;
545 npf_ruleset_t *rlset;
546 const char *name;
547 int error;
548
549 /* Retrieve and construct the rule. */
550 error = prop_dictionary_copyin_ioctl(pref, cmd, &dict);
551 if (error) {
552 return error;
553 }
554
555 /* Dictionary for error reporting. */
556 errdict = prop_dictionary_create();
557
558 /* Create the ruleset and construct sub-rules. */
559 rlset = npf_ruleset_create();
560 subrules = prop_dictionary_get(dict, "subrules");
561 error = npf_mk_subrules(rlset, subrules, NULL, errdict);
562 if (error) {
563 goto out;
564 }
565
566 /* Lookup the rule by name, and replace its subset (sub-rules). */
567 obj = prop_dictionary_get(dict, "name");
568 name = prop_string_cstring_nocopy(obj);
569 if (npf_ruleset_replace(name, rlset) == NULL) {
570 /* Not found. */
571 error = ENOENT;
572 out: /* Error path. */
573 npf_ruleset_destroy(rlset);
574 }
575 prop_object_release(dict);
576
577 /* Error report. */
578 prop_dictionary_set_int32(errdict, "errno", error);
579 prop_dictionary_copyout_ioctl(pref, cmd, errdict);
580 prop_object_release(errdict);
581 return error;
582 }
583
584 /*
585 * npfctl_sessions_save: construct a list of sessions and export for saving.
586 */
587 int
588 npfctl_sessions_save(u_long cmd, void *data)
589 {
590 struct plistref *pref = data;
591 prop_dictionary_t sesdict;
592 prop_array_t selist, nplist;
593 int error;
594
595 /* Create a dictionary and two lists. */
596 sesdict = prop_dictionary_create();
597 selist = prop_array_create();
598 nplist = prop_array_create();
599
600 /* Save the sessions. */
601 error = npf_session_save(selist, nplist);
602 if (error) {
603 goto fail;
604 }
605
606 /* Set the session list, NAT policy list and export the dictionary. */
607 prop_dictionary_set(sesdict, "session-list", selist);
608 prop_dictionary_set(sesdict, "nat-policy-list", nplist);
609 error = prop_dictionary_copyout_ioctl(pref, cmd, sesdict);
610 fail:
611 prop_object_release(sesdict);
612 return error;
613 }
614
615 /*
616 * npfctl_sessions_load: import a list of sessions, reconstruct them and load.
617 */
618 int
619 npfctl_sessions_load(u_long cmd, void *data)
620 {
621 const struct plistref *pref = data;
622 npf_sehash_t *sehasht = NULL;
623 prop_dictionary_t sesdict, sedict;
624 prop_object_iterator_t it;
625 prop_array_t selist;
626 int error;
627
628 /* Retrieve the dictionary containing session and NAT policy lists. */
629 error = prop_dictionary_copyin_ioctl(pref, cmd, &sesdict);
630 if (error)
631 return error;
632
633 /*
634 * Note: session objects contain the references to the NAT policy
635 * entries. Therefore, no need to directly access it.
636 */
637 selist = prop_dictionary_get(sesdict, "session-list");
638 if (prop_object_type(selist) != PROP_TYPE_ARRAY) {
639 error = EINVAL;
640 goto fail;
641 }
642
643 /* Create a session hash table. */
644 sehasht = sess_htable_create();
645 if (sehasht == NULL) {
646 error = ENOMEM;
647 goto fail;
648 }
649
650 /*
651 * Iterate through and construct each session.
652 */
653 error = 0;
654 it = prop_array_iterator(selist);
655 npf_core_enter();
656 while ((sedict = prop_object_iterator_next(it)) != NULL) {
657 /* Session - dictionary. */
658 if (prop_object_type(sedict) != PROP_TYPE_DICTIONARY) {
659 error = EINVAL;
660 goto fail;
661 }
662 /* Construct and insert real session structure. */
663 error = npf_session_restore(sehasht, sedict);
664 if (error) {
665 goto fail;
666 }
667 }
668 npf_core_exit();
669 sess_htable_reload(sehasht);
670 fail:
671 prop_object_release(selist);
672 if (error && sehasht) {
673 /* Destroy session table. */
674 sess_htable_destroy(sehasht);
675 }
676 return error;
677 }
678
679 /*
680 * npfctl_table: add, remove or query entries in the specified table.
681 *
682 * For maximum performance, interface is avoiding proplib(3)'s overhead.
683 */
684 int
685 npfctl_table(void *data)
686 {
687 npf_ioctl_table_t *nct = data;
688 npf_tableset_t *tblset;
689 int error;
690
691 npf_core_enter(); /* XXXSMP */
692 tblset = npf_core_tableset();
693 switch (nct->nct_action) {
694 case NPF_IOCTL_TBLENT_ADD:
695 error = npf_table_insert(tblset, nct->nct_tid,
696 nct->nct_alen, &nct->nct_addr, nct->nct_mask);
697 break;
698 case NPF_IOCTL_TBLENT_REM:
699 error = npf_table_remove(tblset, nct->nct_tid,
700 nct->nct_alen, &nct->nct_addr, nct->nct_mask);
701 break;
702 default:
703 error = npf_table_lookup(tblset, nct->nct_tid,
704 nct->nct_alen, &nct->nct_addr);
705 }
706 npf_core_exit(); /* XXXSMP */
707 return error;
708 }
709