xref: /netbsd-src/lib/libnpf/npf.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: npf.c,v 1.29 2014/05/19 18:47:19 jakllsch Exp $	*/
2 
3 /*-
4  * Copyright (c) 2010-2014 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 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.29 2014/05/19 18:47:19 jakllsch Exp $");
34 
35 #include <sys/types.h>
36 #include <netinet/in_systm.h>
37 #include <netinet/in.h>
38 #include <net/if.h>
39 #include <prop/proplib.h>
40 
41 #include <stdlib.h>
42 #include <string.h>
43 #include <assert.h>
44 #include <errno.h>
45 #include <err.h>
46 
47 #define	_NPF_PRIVATE
48 #include "npf.h"
49 
50 struct nl_rule {
51 	prop_dictionary_t	nrl_dict;
52 };
53 
54 struct nl_rproc {
55 	prop_dictionary_t	nrp_dict;
56 };
57 
58 struct nl_table {
59 	prop_dictionary_t	ntl_dict;
60 };
61 
62 struct nl_alg {
63 	prop_dictionary_t	nal_dict;
64 };
65 
66 struct nl_ext {
67 	const char *		nxt_name;
68 	prop_dictionary_t	nxt_dict;
69 };
70 
71 struct nl_config {
72 	/* Rules, translations, tables, procedures. */
73 	prop_dictionary_t	ncf_dict;
74 	prop_array_t		ncf_alg_list;
75 	prop_array_t		ncf_rules_list;
76 	prop_array_t		ncf_rproc_list;
77 	prop_array_t		ncf_table_list;
78 	prop_array_t		ncf_nat_list;
79 
80 	/* Iterators. */
81 	prop_object_iterator_t	ncf_rule_iter;
82 	unsigned		ncf_reduce[16];
83 	unsigned		ncf_nlevel;
84 	unsigned		ncf_counter;
85 	nl_rule_t		ncf_cur_rule;
86 
87 	prop_object_iterator_t	ncf_table_iter;
88 	nl_table_t		ncf_cur_table;
89 
90 	prop_object_iterator_t	ncf_rproc_iter;
91 	nl_rproc_t		ncf_cur_rproc;
92 
93 	/* Error report and debug information. */
94 	prop_dictionary_t	ncf_err;
95 	prop_dictionary_t	ncf_debug;
96 
97 	/* Custom file to externalise property-list. */
98 	const char *		ncf_plist;
99 	bool			ncf_flush;
100 };
101 
102 static prop_array_t	_npf_ruleset_transform(prop_array_t);
103 
104 /*
105  * CONFIGURATION INTERFACE.
106  */
107 
108 nl_config_t *
109 npf_config_create(void)
110 {
111 	nl_config_t *ncf;
112 
113 	ncf = calloc(1, sizeof(*ncf));
114 	if (ncf == NULL) {
115 		return NULL;
116 	}
117 	ncf->ncf_alg_list = prop_array_create();
118 	ncf->ncf_rules_list = prop_array_create();
119 	ncf->ncf_rproc_list = prop_array_create();
120 	ncf->ncf_table_list = prop_array_create();
121 	ncf->ncf_nat_list = prop_array_create();
122 
123 	ncf->ncf_plist = NULL;
124 	ncf->ncf_flush = false;
125 
126 	return ncf;
127 }
128 
129 int
130 npf_config_submit(nl_config_t *ncf, int fd)
131 {
132 	const char *plist = ncf->ncf_plist;
133 	prop_dictionary_t npf_dict;
134 	prop_array_t rlset;
135 	int error = 0;
136 
137 	npf_dict = prop_dictionary_create();
138 	if (npf_dict == NULL) {
139 		return ENOMEM;
140 	}
141 	prop_dictionary_set_uint32(npf_dict, "version", NPF_VERSION);
142 
143 	rlset = _npf_ruleset_transform(ncf->ncf_rules_list);
144 	if (rlset == NULL) {
145 		prop_object_release(npf_dict);
146 		return ENOMEM;
147 	}
148 	prop_object_release(ncf->ncf_rules_list);
149 	ncf->ncf_rules_list = rlset;
150 
151 	prop_dictionary_set(npf_dict, "rules", ncf->ncf_rules_list);
152 	prop_dictionary_set(npf_dict, "algs", ncf->ncf_alg_list);
153 	prop_dictionary_set(npf_dict, "rprocs", ncf->ncf_rproc_list);
154 	prop_dictionary_set(npf_dict, "tables", ncf->ncf_table_list);
155 	prop_dictionary_set(npf_dict, "translation", ncf->ncf_nat_list);
156 	prop_dictionary_set_bool(npf_dict, "flush", ncf->ncf_flush);
157 	if (ncf->ncf_debug) {
158 		prop_dictionary_set(npf_dict, "debug", ncf->ncf_debug);
159 	}
160 
161 	if (plist) {
162 		if (!prop_dictionary_externalize_to_file(npf_dict, plist)) {
163 			error = errno;
164 		}
165 		prop_object_release(npf_dict);
166 		return error;
167 	}
168 	if (fd) {
169 		error = prop_dictionary_sendrecv_ioctl(npf_dict, fd,
170 		    IOC_NPF_RELOAD, &ncf->ncf_err);
171 		if (error) {
172 			prop_object_release(npf_dict);
173 			assert(ncf->ncf_err == NULL);
174 			return error;
175 		}
176 		prop_dictionary_get_int32(ncf->ncf_err, "errno", &error);
177 	}
178 	prop_object_release(npf_dict);
179 	return error;
180 }
181 
182 nl_config_t *
183 npf_config_retrieve(int fd, bool *active, bool *loaded)
184 {
185 	prop_dictionary_t npf_dict;
186 	nl_config_t *ncf;
187 	int error;
188 
189 	error = prop_dictionary_recv_ioctl(fd, IOC_NPF_GETCONF, &npf_dict);
190 	if (error) {
191 		return NULL;
192 	}
193 	ncf = calloc(1, sizeof(*ncf));
194 	if (ncf == NULL) {
195 		prop_object_release(npf_dict);
196 		return NULL;
197 	}
198 	ncf->ncf_dict = npf_dict;
199 	ncf->ncf_alg_list = prop_dictionary_get(npf_dict, "algs");
200 	ncf->ncf_rules_list = prop_dictionary_get(npf_dict, "rules");
201 	ncf->ncf_rproc_list = prop_dictionary_get(npf_dict, "rprocs");
202 	ncf->ncf_table_list = prop_dictionary_get(npf_dict, "tables");
203 	ncf->ncf_nat_list = prop_dictionary_get(npf_dict, "translation");
204 
205 	prop_dictionary_get_bool(npf_dict, "active", active);
206 	*loaded = (ncf->ncf_rules_list != NULL);
207 	return ncf;
208 }
209 
210 int
211 npf_config_flush(int fd)
212 {
213 	nl_config_t *ncf;
214 	int error;
215 
216 	ncf = npf_config_create();
217 	if (ncf == NULL) {
218 		return ENOMEM;
219 	}
220 	ncf->ncf_flush = true;
221 	error = npf_config_submit(ncf, fd);
222 	npf_config_destroy(ncf);
223 	return error;
224 }
225 
226 void
227 _npf_config_error(nl_config_t *ncf, nl_error_t *ne)
228 {
229 	memset(ne, 0, sizeof(*ne));
230 	prop_dictionary_get_int32(ncf->ncf_err, "id", &ne->ne_id);
231 	prop_dictionary_get_cstring(ncf->ncf_err,
232 	    "source-file", &ne->ne_source_file);
233 	prop_dictionary_get_uint32(ncf->ncf_err,
234 	    "source-line", &ne->ne_source_line);
235 	prop_dictionary_get_int32(ncf->ncf_err,
236 	    "code-error", &ne->ne_ncode_error);
237 	prop_dictionary_get_int32(ncf->ncf_err,
238 	    "code-errat", &ne->ne_ncode_errat);
239 }
240 
241 void
242 npf_config_destroy(nl_config_t *ncf)
243 {
244 	if (!ncf->ncf_dict) {
245 		prop_object_release(ncf->ncf_alg_list);
246 		prop_object_release(ncf->ncf_rules_list);
247 		prop_object_release(ncf->ncf_rproc_list);
248 		prop_object_release(ncf->ncf_table_list);
249 		prop_object_release(ncf->ncf_nat_list);
250 	}
251 	if (ncf->ncf_err) {
252 		prop_object_release(ncf->ncf_err);
253 	}
254 	if (ncf->ncf_debug) {
255 		prop_object_release(ncf->ncf_debug);
256 	}
257 	free(ncf);
258 }
259 
260 void
261 _npf_config_setsubmit(nl_config_t *ncf, const char *plist_file)
262 {
263 	ncf->ncf_plist = plist_file;
264 }
265 
266 static bool
267 _npf_prop_array_lookup(prop_array_t array, const char *key, const char *name)
268 {
269 	prop_dictionary_t dict;
270 	prop_object_iterator_t it;
271 
272 	it = prop_array_iterator(array);
273 	while ((dict = prop_object_iterator_next(it)) != NULL) {
274 		const char *lname;
275 		prop_dictionary_get_cstring_nocopy(dict, key, &lname);
276 		if (strcmp(name, lname) == 0)
277 			break;
278 	}
279 	prop_object_iterator_release(it);
280 	return dict ? true : false;
281 }
282 
283 /*
284  * DYNAMIC RULESET INTERFACE.
285  */
286 
287 int
288 npf_ruleset_add(int fd, const char *rname, nl_rule_t *rl, uint64_t *id)
289 {
290 	prop_dictionary_t rldict = rl->nrl_dict;
291 	prop_dictionary_t ret;
292 	int error;
293 
294 	prop_dictionary_set_cstring(rldict, "ruleset-name", rname);
295 	prop_dictionary_set_uint32(rldict, "command", NPF_CMD_RULE_ADD);
296 	error = prop_dictionary_sendrecv_ioctl(rldict, fd, IOC_NPF_RULE, &ret);
297 	if (!error) {
298 		prop_dictionary_get_uint64(ret, "id", id);
299 	}
300 	return error;
301 }
302 
303 int
304 npf_ruleset_remove(int fd, const char *rname, uint64_t id)
305 {
306 	prop_dictionary_t rldict;
307 
308 	rldict = prop_dictionary_create();
309 	if (rldict == NULL) {
310 		return ENOMEM;
311 	}
312 	prop_dictionary_set_cstring(rldict, "ruleset-name", rname);
313 	prop_dictionary_set_uint32(rldict, "command", NPF_CMD_RULE_REMOVE);
314 	prop_dictionary_set_uint64(rldict, "id", id);
315 	return prop_dictionary_send_ioctl(rldict, fd, IOC_NPF_RULE);
316 }
317 
318 int
319 npf_ruleset_remkey(int fd, const char *rname, const void *key, size_t len)
320 {
321 	prop_dictionary_t rldict;
322 	prop_data_t keyobj;
323 
324 	rldict = prop_dictionary_create();
325 	if (rldict == NULL) {
326 		return ENOMEM;
327 	}
328 	prop_dictionary_set_cstring(rldict, "ruleset-name", rname);
329 	prop_dictionary_set_uint32(rldict, "command", NPF_CMD_RULE_REMKEY);
330 
331 	keyobj = prop_data_create_data(key, len);
332 	if (keyobj == NULL) {
333 		prop_object_release(rldict);
334 		return ENOMEM;
335 	}
336 	prop_dictionary_set(rldict, "key", keyobj);
337 	prop_object_release(keyobj);
338 
339 	return prop_dictionary_send_ioctl(rldict, fd, IOC_NPF_RULE);
340 }
341 
342 int
343 npf_ruleset_flush(int fd, const char *rname)
344 {
345 	prop_dictionary_t rldict;
346 
347 	rldict = prop_dictionary_create();
348 	if (rldict == NULL) {
349 		return ENOMEM;
350 	}
351 	prop_dictionary_set_cstring(rldict, "ruleset-name", rname);
352 	prop_dictionary_set_uint32(rldict, "command", NPF_CMD_RULE_FLUSH);
353 	return prop_dictionary_send_ioctl(rldict, fd, IOC_NPF_RULE);
354 }
355 
356 /*
357  * _npf_ruleset_transform: transform the ruleset representing nested
358  * rules with lists into an array.
359  */
360 
361 static void
362 _npf_ruleset_transform1(prop_array_t rlset, prop_array_t rules)
363 {
364 	prop_object_iterator_t it;
365 	prop_dictionary_t rldict;
366 	prop_array_t subrlset;
367 
368 	it = prop_array_iterator(rules);
369 	while ((rldict = prop_object_iterator_next(it)) != NULL) {
370 		unsigned idx;
371 
372 		/* Add rules to the array (reference is retained). */
373 		prop_array_add(rlset, rldict);
374 
375 		subrlset = prop_dictionary_get(rldict, "subrules");
376 		if (subrlset) {
377 			/* Process subrules recursively. */
378 			_npf_ruleset_transform1(rlset, subrlset);
379 			/* Add the skip-to position. */
380 			idx = prop_array_count(rlset);
381 			prop_dictionary_set_uint32(rldict, "skip-to", idx);
382 			prop_dictionary_remove(rldict, "subrules");
383 		}
384 	}
385 	prop_object_iterator_release(it);
386 }
387 
388 static prop_array_t
389 _npf_ruleset_transform(prop_array_t rlset)
390 {
391 	prop_array_t nrlset;
392 
393 	nrlset = prop_array_create();
394 	_npf_ruleset_transform1(nrlset, rlset);
395 	return nrlset;
396 }
397 
398 /*
399  * NPF EXTENSION INTERFACE.
400  */
401 
402 nl_ext_t *
403 npf_ext_construct(const char *name)
404 {
405 	nl_ext_t *ext;
406 
407 	ext = malloc(sizeof(*ext));
408 	if (ext == NULL) {
409 		return NULL;
410 	}
411 	ext->nxt_name = strdup(name);
412 	if (ext->nxt_name == NULL) {
413 		free(ext);
414 		return NULL;
415 	}
416 	ext->nxt_dict = prop_dictionary_create();
417 
418 	return ext;
419 }
420 
421 void
422 npf_ext_param_u32(nl_ext_t *ext, const char *key, uint32_t val)
423 {
424 	prop_dictionary_t extdict = ext->nxt_dict;
425 	prop_dictionary_set_uint32(extdict, key, val);
426 }
427 
428 void
429 npf_ext_param_bool(nl_ext_t *ext, const char *key, bool val)
430 {
431 	prop_dictionary_t extdict = ext->nxt_dict;
432 	prop_dictionary_set_bool(extdict, key, val);
433 }
434 
435 void
436 npf_ext_param_string(nl_ext_t *ext, const char *key, const char *val)
437 {
438 	prop_dictionary_t extdict = ext->nxt_dict;
439 	prop_dictionary_set_cstring(extdict, key, val);
440 }
441 
442 /*
443  * RULE INTERFACE.
444  */
445 
446 nl_rule_t *
447 npf_rule_create(const char *name, uint32_t attr, const char *ifname)
448 {
449 	prop_dictionary_t rldict;
450 	nl_rule_t *rl;
451 
452 	rl = malloc(sizeof(*rl));
453 	if (rl == NULL) {
454 		return NULL;
455 	}
456 	rldict = prop_dictionary_create();
457 	if (rldict == NULL) {
458 		free(rl);
459 		return NULL;
460 	}
461 	if (name) {
462 		prop_dictionary_set_cstring(rldict, "name", name);
463 	}
464 	prop_dictionary_set_uint32(rldict, "attributes", attr);
465 
466 	if (ifname) {
467 		prop_dictionary_set_cstring(rldict, "interface", ifname);
468 	}
469 	rl->nrl_dict = rldict;
470 	return rl;
471 }
472 
473 int
474 npf_rule_setcode(nl_rule_t *rl, int type, const void *code, size_t len)
475 {
476 	prop_dictionary_t rldict = rl->nrl_dict;
477 	prop_data_t cdata;
478 
479 	switch (type) {
480 	case NPF_CODE_NC:
481 	case NPF_CODE_BPF:
482 		break;
483 	default:
484 		return ENOTSUP;
485 	}
486 	prop_dictionary_set_uint32(rldict, "code-type", type);
487 	if ((cdata = prop_data_create_data(code, len)) == NULL) {
488 		return ENOMEM;
489 	}
490 	prop_dictionary_set(rldict, "code", cdata);
491 	prop_object_release(cdata);
492 	return 0;
493 }
494 
495 int
496 npf_rule_setkey(nl_rule_t *rl, const void *key, size_t len)
497 {
498 	prop_dictionary_t rldict = rl->nrl_dict;
499 	prop_data_t kdata;
500 
501 	if ((kdata = prop_data_create_data(key, len)) == NULL) {
502 		return ENOMEM;
503 	}
504 	prop_dictionary_set(rldict, "key", kdata);
505 	prop_object_release(kdata);
506 	return 0;
507 }
508 
509 int
510 npf_rule_setinfo(nl_rule_t *rl, const void *info, size_t len)
511 {
512 	prop_dictionary_t rldict = rl->nrl_dict;
513 	prop_data_t idata;
514 
515 	if ((idata = prop_data_create_data(info, len)) == NULL) {
516 		return ENOMEM;
517 	}
518 	prop_dictionary_set(rldict, "info", idata);
519 	prop_object_release(idata);
520 	return 0;
521 }
522 
523 int
524 npf_rule_setprio(nl_rule_t *rl, pri_t pri)
525 {
526 	prop_dictionary_t rldict = rl->nrl_dict;
527 
528 	prop_dictionary_set_int32(rldict, "priority", pri);
529 	return 0;
530 }
531 
532 int
533 npf_rule_setproc(nl_rule_t *rl, const char *name)
534 {
535 	prop_dictionary_t rldict = rl->nrl_dict;
536 
537 	prop_dictionary_set_cstring(rldict, "rproc", name);
538 	return 0;
539 }
540 
541 void *
542 npf_rule_export(nl_rule_t *rl, size_t *length)
543 {
544 	prop_dictionary_t rldict = rl->nrl_dict;
545 	void *xml;
546 
547 	if ((xml = prop_dictionary_externalize(rldict)) == NULL) {
548 		return NULL;
549 	}
550 	*length = strlen(xml);
551 	return xml;
552 }
553 
554 bool
555 npf_rule_exists_p(nl_config_t *ncf, const char *name)
556 {
557 	return _npf_prop_array_lookup(ncf->ncf_rules_list, "name", name);
558 }
559 
560 int
561 npf_rule_insert(nl_config_t *ncf, nl_rule_t *parent, nl_rule_t *rl)
562 {
563 	prop_dictionary_t rldict = rl->nrl_dict;
564 	prop_array_t rlset;
565 
566 	if (parent) {
567 		prop_dictionary_t pdict = parent->nrl_dict;
568 		rlset = prop_dictionary_get(pdict, "subrules");
569 		if (rlset == NULL) {
570 			rlset = prop_array_create();
571 			prop_dictionary_set(pdict, "subrules", rlset);
572 			prop_object_release(rlset);
573 		}
574 	} else {
575 		rlset = ncf->ncf_rules_list;
576 	}
577 	prop_array_add(rlset, rldict);
578 	return 0;
579 }
580 
581 static nl_rule_t *
582 _npf_rule_iterate1(nl_config_t *ncf, prop_array_t rlist, unsigned *level)
583 {
584 	prop_dictionary_t rldict;
585 	uint32_t skipto = 0;
586 
587 	if (!ncf->ncf_rule_iter) {
588 		/* Initialise the iterator. */
589 		ncf->ncf_rule_iter = prop_array_iterator(rlist);
590 		ncf->ncf_nlevel = 0;
591 		ncf->ncf_reduce[0] = 0;
592 		ncf->ncf_counter = 0;
593 	}
594 
595 	rldict = prop_object_iterator_next(ncf->ncf_rule_iter);
596 	if ((ncf->ncf_cur_rule.nrl_dict = rldict) == NULL) {
597 		prop_object_iterator_release(ncf->ncf_rule_iter);
598 		ncf->ncf_rule_iter = NULL;
599 		return NULL;
600 	}
601 	*level = ncf->ncf_nlevel;
602 
603 	prop_dictionary_get_uint32(rldict, "skip-to", &skipto);
604 	if (skipto) {
605 		ncf->ncf_nlevel++;
606 		ncf->ncf_reduce[ncf->ncf_nlevel] = skipto;
607 	}
608 	if (ncf->ncf_reduce[ncf->ncf_nlevel] == ++ncf->ncf_counter) {
609 		assert(ncf->ncf_nlevel > 0);
610 		ncf->ncf_nlevel--;
611 	}
612 	return &ncf->ncf_cur_rule;
613 }
614 
615 nl_rule_t *
616 npf_rule_iterate(nl_config_t *ncf, unsigned *level)
617 {
618 	return _npf_rule_iterate1(ncf, ncf->ncf_rules_list, level);
619 }
620 
621 const char *
622 npf_rule_getname(nl_rule_t *rl)
623 {
624 	prop_dictionary_t rldict = rl->nrl_dict;
625 	const char *rname = NULL;
626 
627 	prop_dictionary_get_cstring_nocopy(rldict, "name", &rname);
628 	return rname;
629 }
630 
631 uint32_t
632 npf_rule_getattr(nl_rule_t *rl)
633 {
634 	prop_dictionary_t rldict = rl->nrl_dict;
635 	uint32_t attr = 0;
636 
637 	prop_dictionary_get_uint32(rldict, "attributes", &attr);
638 	return attr;
639 }
640 
641 const char *
642 npf_rule_getinterface(nl_rule_t *rl)
643 {
644 	prop_dictionary_t rldict = rl->nrl_dict;
645 	const char *ifname = NULL;
646 
647 	prop_dictionary_get_cstring_nocopy(rldict, "interface", &ifname);
648 	return ifname;
649 }
650 
651 const void *
652 npf_rule_getinfo(nl_rule_t *rl, size_t *len)
653 {
654 	prop_dictionary_t rldict = rl->nrl_dict;
655 	prop_object_t obj = prop_dictionary_get(rldict, "info");
656 
657 	*len = prop_data_size(obj);
658 	return prop_data_data_nocopy(obj);
659 }
660 
661 const char *
662 npf_rule_getproc(nl_rule_t *rl)
663 {
664 	prop_dictionary_t rldict = rl->nrl_dict;
665 	const char *rpname = NULL;
666 
667 	prop_dictionary_get_cstring_nocopy(rldict, "rproc", &rpname);
668 	return rpname;
669 }
670 
671 int
672 _npf_ruleset_list(int fd, const char *rname, nl_config_t *ncf)
673 {
674 	prop_dictionary_t rldict, ret;
675 	int error;
676 
677 	rldict = prop_dictionary_create();
678 	if (rldict == NULL) {
679 		return ENOMEM;
680 	}
681 	prop_dictionary_set_cstring(rldict, "ruleset-name", rname);
682 	prop_dictionary_set_uint32(rldict, "command", NPF_CMD_RULE_LIST);
683 	error = prop_dictionary_sendrecv_ioctl(rldict, fd, IOC_NPF_RULE, &ret);
684 	if (!error) {
685 		prop_array_t rules;
686 
687 		rules = prop_dictionary_get(ret, "rules");
688 		if (rules == NULL) {
689 			return EINVAL;
690 		}
691 		prop_object_release(ncf->ncf_rules_list);
692 		ncf->ncf_rules_list = rules;
693 	}
694 	return error;
695 }
696 
697 void
698 npf_rule_destroy(nl_rule_t *rl)
699 {
700 
701 	prop_object_release(rl->nrl_dict);
702 	free(rl);
703 }
704 
705 /*
706  * RULE PROCEDURE INTERFACE.
707  */
708 
709 nl_rproc_t *
710 npf_rproc_create(const char *name)
711 {
712 	prop_dictionary_t rpdict;
713 	prop_array_t extcalls;
714 	nl_rproc_t *nrp;
715 
716 	nrp = malloc(sizeof(nl_rproc_t));
717 	if (nrp == NULL) {
718 		return NULL;
719 	}
720 	rpdict = prop_dictionary_create();
721 	if (rpdict == NULL) {
722 		free(nrp);
723 		return NULL;
724 	}
725 	prop_dictionary_set_cstring(rpdict, "name", name);
726 
727 	extcalls = prop_array_create();
728 	if (extcalls == NULL) {
729 		prop_object_release(rpdict);
730 		free(nrp);
731 		return NULL;
732 	}
733 	prop_dictionary_set(rpdict, "extcalls", extcalls);
734 	prop_object_release(extcalls);
735 
736 	nrp->nrp_dict = rpdict;
737 	return nrp;
738 }
739 
740 int
741 npf_rproc_extcall(nl_rproc_t *rp, nl_ext_t *ext)
742 {
743 	prop_dictionary_t rpdict = rp->nrp_dict;
744 	prop_dictionary_t extdict = ext->nxt_dict;
745 	prop_array_t extcalls;
746 
747 	extcalls = prop_dictionary_get(rpdict, "extcalls");
748 	if (_npf_prop_array_lookup(extcalls, "name", ext->nxt_name)) {
749 		return EEXIST;
750 	}
751 	prop_dictionary_set_cstring(extdict, "name", ext->nxt_name);
752 	prop_array_add(extcalls, extdict);
753 	return 0;
754 }
755 
756 bool
757 npf_rproc_exists_p(nl_config_t *ncf, const char *name)
758 {
759 	return _npf_prop_array_lookup(ncf->ncf_rproc_list, "name", name);
760 }
761 
762 int
763 npf_rproc_insert(nl_config_t *ncf, nl_rproc_t *rp)
764 {
765 	prop_dictionary_t rpdict = rp->nrp_dict;
766 	const char *name;
767 
768 	if (!prop_dictionary_get_cstring_nocopy(rpdict, "name", &name)) {
769 		return EINVAL;
770 	}
771 	if (npf_rproc_exists_p(ncf, name)) {
772 		return EEXIST;
773 	}
774 	prop_array_add(ncf->ncf_rproc_list, rpdict);
775 	return 0;
776 }
777 
778 nl_rproc_t *
779 npf_rproc_iterate(nl_config_t *ncf)
780 {
781 	prop_dictionary_t rpdict;
782 
783 	if (!ncf->ncf_rproc_iter) {
784 		/* Initialise the iterator. */
785 		ncf->ncf_rproc_iter = prop_array_iterator(ncf->ncf_rproc_list);
786 	}
787 	rpdict = prop_object_iterator_next(ncf->ncf_rproc_iter);
788 	if ((ncf->ncf_cur_rproc.nrp_dict = rpdict) == NULL) {
789 		prop_object_iterator_release(ncf->ncf_rproc_iter);
790 		ncf->ncf_rproc_iter = NULL;
791 		return NULL;
792 	}
793 	return &ncf->ncf_cur_rproc;
794 }
795 
796 const char *
797 npf_rproc_getname(nl_rproc_t *rp)
798 {
799 	prop_dictionary_t rpdict = rp->nrp_dict;
800 	const char *rpname = NULL;
801 
802 	prop_dictionary_get_cstring_nocopy(rpdict, "name", &rpname);
803 	return rpname;
804 }
805 
806 /*
807  * TRANSLATION INTERFACE.
808  */
809 
810 nl_nat_t *
811 npf_nat_create(int type, u_int flags, const char *ifname,
812     int af, npf_addr_t *addr, npf_netmask_t mask, in_port_t port)
813 {
814 	nl_rule_t *rl;
815 	prop_dictionary_t rldict;
816 	prop_data_t addrdat;
817 	uint32_t attr;
818 	size_t sz;
819 
820 	if (af == AF_INET) {
821 		sz = sizeof(struct in_addr);
822 	} else if (af == AF_INET6) {
823 		sz = sizeof(struct in6_addr);
824 	} else {
825 		return NULL;
826 	}
827 
828 	attr = NPF_RULE_PASS | NPF_RULE_FINAL |
829 	    (type == NPF_NATOUT ? NPF_RULE_OUT : NPF_RULE_IN);
830 
831 	/* Create a rule for NAT policy.  Next, will add translation data. */
832 	rl = npf_rule_create(NULL, attr, ifname);
833 	if (rl == NULL) {
834 		return NULL;
835 	}
836 	rldict = rl->nrl_dict;
837 
838 	/* Translation type and flags. */
839 	prop_dictionary_set_int32(rldict, "type", type);
840 	prop_dictionary_set_uint32(rldict, "flags", flags);
841 
842 	/* Translation IP and mask. */
843 	addrdat = prop_data_create_data(addr, sz);
844 	if (addrdat == NULL) {
845 		npf_rule_destroy(rl);
846 		return NULL;
847 	}
848 	prop_dictionary_set(rldict, "translation-ip", addrdat);
849 	prop_dictionary_set_uint32(rldict, "translation-mask", mask);
850 	prop_object_release(addrdat);
851 
852 	/* Translation port (for redirect case). */
853 	prop_dictionary_set_uint16(rldict, "translation-port", port);
854 
855 	return (nl_nat_t *)rl;
856 }
857 
858 int
859 npf_nat_insert(nl_config_t *ncf, nl_nat_t *nt, pri_t pri __unused)
860 {
861 	prop_dictionary_t rldict = nt->nrl_dict;
862 
863 	prop_dictionary_set_int32(rldict, "priority", NPF_PRI_LAST);
864 	prop_array_add(ncf->ncf_nat_list, rldict);
865 	return 0;
866 }
867 
868 nl_nat_t *
869 npf_nat_iterate(nl_config_t *ncf)
870 {
871 	u_int level;
872 	return _npf_rule_iterate1(ncf, ncf->ncf_nat_list, &level);
873 }
874 
875 int
876 npf_nat_setalgo(nl_nat_t *nt, u_int algo)
877 {
878 	prop_dictionary_t rldict = nt->nrl_dict;
879 	prop_dictionary_set_uint32(rldict, "translation-algo", algo);
880 	return 0;
881 }
882 
883 int
884 npf_nat_setnpt66(nl_nat_t *nt, uint16_t adj)
885 {
886 	prop_dictionary_t rldict = nt->nrl_dict;
887 	int error;
888 
889 	if ((error = npf_nat_setalgo(nt, NPF_ALGO_NPT66)) != 0) {
890 		return error;
891 	}
892 	prop_dictionary_set_uint16(rldict, "npt66-adjustment", adj);
893 	return 0;
894 }
895 
896 int
897 npf_nat_gettype(nl_nat_t *nt)
898 {
899 	prop_dictionary_t rldict = nt->nrl_dict;
900 	int type = 0;
901 
902 	prop_dictionary_get_int32(rldict, "type", &type);
903 	return type;
904 }
905 
906 u_int
907 npf_nat_getflags(nl_nat_t *nt)
908 {
909 	prop_dictionary_t rldict = nt->nrl_dict;
910 	unsigned flags = 0;
911 
912 	prop_dictionary_get_uint32(rldict, "flags", &flags);
913 	return flags;
914 }
915 
916 void
917 npf_nat_getmap(nl_nat_t *nt, npf_addr_t *addr, size_t *alen, in_port_t *port)
918 {
919 	prop_dictionary_t rldict = nt->nrl_dict;
920 	prop_object_t obj = prop_dictionary_get(rldict, "translation-ip");
921 
922 	*alen = prop_data_size(obj);
923 	memcpy(addr, prop_data_data_nocopy(obj), *alen);
924 
925 	*port = 0;
926 	prop_dictionary_get_uint16(rldict, "translation-port", port);
927 }
928 
929 /*
930  * TABLE INTERFACE.
931  */
932 
933 nl_table_t *
934 npf_table_create(const char *name, u_int id, int type)
935 {
936 	prop_dictionary_t tldict;
937 	prop_array_t tblents;
938 	nl_table_t *tl;
939 
940 	tl = malloc(sizeof(*tl));
941 	if (tl == NULL) {
942 		return NULL;
943 	}
944 	tldict = prop_dictionary_create();
945 	if (tldict == NULL) {
946 		free(tl);
947 		return NULL;
948 	}
949 	prop_dictionary_set_cstring(tldict, "name", name);
950 	prop_dictionary_set_uint32(tldict, "id", id);
951 	prop_dictionary_set_int32(tldict, "type", type);
952 
953 	tblents = prop_array_create();
954 	if (tblents == NULL) {
955 		prop_object_release(tldict);
956 		free(tl);
957 		return NULL;
958 	}
959 	prop_dictionary_set(tldict, "entries", tblents);
960 	prop_object_release(tblents);
961 
962 	tl->ntl_dict = tldict;
963 	return tl;
964 }
965 
966 int
967 npf_table_add_entry(nl_table_t *tl, int af, const npf_addr_t *addr,
968     const npf_netmask_t mask)
969 {
970 	prop_dictionary_t tldict = tl->ntl_dict, entdict;
971 	prop_array_t tblents;
972 	prop_data_t addrdata;
973 	unsigned alen;
974 
975 	/* Create the table entry. */
976 	entdict = prop_dictionary_create();
977 	if (entdict == NULL) {
978 		return ENOMEM;
979 	}
980 
981 	switch (af) {
982 	case AF_INET:
983 		alen = sizeof(struct in_addr);
984 		break;
985 	case AF_INET6:
986 		alen = sizeof(struct in6_addr);
987 		break;
988 	default:
989 		return EINVAL;
990 	}
991 
992 	addrdata = prop_data_create_data(addr, alen);
993 	prop_dictionary_set(entdict, "addr", addrdata);
994 	prop_dictionary_set_uint8(entdict, "mask", mask);
995 	prop_object_release(addrdata);
996 
997 	tblents = prop_dictionary_get(tldict, "entries");
998 	prop_array_add(tblents, entdict);
999 	prop_object_release(entdict);
1000 	return 0;
1001 }
1002 
1003 int
1004 npf_table_setdata(nl_table_t *tl, const void *blob, size_t len)
1005 {
1006 	prop_dictionary_t tldict = tl->ntl_dict;
1007 	prop_data_t bobj;
1008 
1009 	if ((bobj = prop_data_create_data(blob, len)) == NULL) {
1010 		return ENOMEM;
1011 	}
1012 	prop_dictionary_set(tldict, "data", bobj);
1013 	prop_object_release(bobj);
1014 	return 0;
1015 }
1016 
1017 static bool
1018 _npf_table_exists_p(nl_config_t *ncf, const char *name)
1019 {
1020 	prop_dictionary_t tldict;
1021 	prop_object_iterator_t it;
1022 
1023 	it = prop_array_iterator(ncf->ncf_table_list);
1024 	while ((tldict = prop_object_iterator_next(it)) != NULL) {
1025 		const char *tname = NULL;
1026 
1027 		if (prop_dictionary_get_cstring_nocopy(tldict, "name", &tname)
1028 		    && strcmp(tname, name) == 0)
1029 			break;
1030 	}
1031 	prop_object_iterator_release(it);
1032 	return tldict ? true : false;
1033 }
1034 
1035 int
1036 npf_table_insert(nl_config_t *ncf, nl_table_t *tl)
1037 {
1038 	prop_dictionary_t tldict = tl->ntl_dict;
1039 	const char *name = NULL;
1040 
1041 	if (!prop_dictionary_get_cstring_nocopy(tldict, "name", &name)) {
1042 		return EINVAL;
1043 	}
1044 	if (_npf_table_exists_p(ncf, name)) {
1045 		return EEXIST;
1046 	}
1047 	prop_array_add(ncf->ncf_table_list, tldict);
1048 	return 0;
1049 }
1050 
1051 nl_table_t *
1052 npf_table_iterate(nl_config_t *ncf)
1053 {
1054 	prop_dictionary_t tldict;
1055 
1056 	if (!ncf->ncf_table_iter) {
1057 		/* Initialise the iterator. */
1058 		ncf->ncf_table_iter = prop_array_iterator(ncf->ncf_table_list);
1059 	}
1060 	tldict = prop_object_iterator_next(ncf->ncf_table_iter);
1061 	if ((ncf->ncf_cur_table.ntl_dict = tldict) == NULL) {
1062 		prop_object_iterator_release(ncf->ncf_table_iter);
1063 		ncf->ncf_table_iter = NULL;
1064 		return NULL;
1065 	}
1066 	return &ncf->ncf_cur_table;
1067 }
1068 
1069 unsigned
1070 npf_table_getid(nl_table_t *tl)
1071 {
1072 	prop_dictionary_t tldict = tl->ntl_dict;
1073 	unsigned id = (unsigned)-1;
1074 
1075 	prop_dictionary_get_uint32(tldict, "id", &id);
1076 	return id;
1077 }
1078 
1079 const char *
1080 npf_table_getname(nl_table_t *tl)
1081 {
1082 	prop_dictionary_t tldict = tl->ntl_dict;
1083 	const char *tname = NULL;
1084 
1085 	prop_dictionary_get_cstring_nocopy(tldict, "name", &tname);
1086 	return tname;
1087 }
1088 
1089 int
1090 npf_table_gettype(nl_table_t *tl)
1091 {
1092 	prop_dictionary_t tldict = tl->ntl_dict;
1093 	int type = 0;
1094 
1095 	prop_dictionary_get_int32(tldict, "type", &type);
1096 	return type;
1097 }
1098 
1099 void
1100 npf_table_destroy(nl_table_t *tl)
1101 {
1102 	prop_object_release(tl->ntl_dict);
1103 	free(tl);
1104 }
1105 
1106 /*
1107  * ALG INTERFACE.
1108  */
1109 
1110 int
1111 _npf_alg_load(nl_config_t *ncf, const char *name)
1112 {
1113 	prop_dictionary_t al_dict;
1114 
1115 	if (_npf_prop_array_lookup(ncf->ncf_alg_list, "name", name))
1116 		return EEXIST;
1117 
1118 	al_dict = prop_dictionary_create();
1119 	prop_dictionary_set_cstring(al_dict, "name", name);
1120 	prop_array_add(ncf->ncf_alg_list, al_dict);
1121 	prop_object_release(al_dict);
1122 	return 0;
1123 }
1124 
1125 int
1126 _npf_alg_unload(nl_config_t *ncf, const char *name)
1127 {
1128 	if (!_npf_prop_array_lookup(ncf->ncf_alg_list, "name", name))
1129 		return ENOENT;
1130 
1131 	// Not yet: prop_array_add(ncf->ncf_alg_list, al_dict);
1132 	return ENOTSUP;
1133 }
1134 
1135 /*
1136  * MISC.
1137  */
1138 
1139 int
1140 npf_sessions_recv(int fd, const char *fpath)
1141 {
1142 	prop_dictionary_t sdict;
1143 	int error;
1144 
1145 	error = prop_dictionary_recv_ioctl(fd, IOC_NPF_SESSIONS_SAVE, &sdict);
1146 	if (error) {
1147 		return error;
1148 	}
1149 	if (!prop_dictionary_externalize_to_file(sdict, fpath)) {
1150 		error = errno;
1151 	}
1152 	prop_object_release(sdict);
1153 	return error;
1154 }
1155 
1156 int
1157 npf_sessions_send(int fd, const char *fpath)
1158 {
1159 	prop_dictionary_t sdict;
1160 	int error;
1161 
1162 	if (fpath) {
1163 		sdict = prop_dictionary_internalize_from_file(fpath);
1164 		if (sdict == NULL) {
1165 			return errno;
1166 		}
1167 	} else {
1168 		/* Empty: will flush the sessions. */
1169 		prop_array_t selist = prop_array_create();
1170 		sdict = prop_dictionary_create();
1171 		prop_dictionary_set(sdict, "session-list", selist);
1172 		prop_object_release(selist);
1173 	}
1174 	error = prop_dictionary_send_ioctl(sdict, fd, IOC_NPF_SESSIONS_LOAD);
1175 	prop_object_release(sdict);
1176 	return error;
1177 }
1178 
1179 static prop_dictionary_t
1180 _npf_debug_initonce(nl_config_t *ncf)
1181 {
1182 	if (!ncf->ncf_debug) {
1183 		prop_array_t iflist = prop_array_create();
1184 		ncf->ncf_debug = prop_dictionary_create();
1185 		prop_dictionary_set(ncf->ncf_debug, "interfaces", iflist);
1186 		prop_object_release(iflist);
1187 	}
1188 	return ncf->ncf_debug;
1189 }
1190 
1191 void
1192 _npf_debug_addif(nl_config_t *ncf, const char *ifname)
1193 {
1194 	prop_dictionary_t ifdict, dbg = _npf_debug_initonce(ncf);
1195 	prop_array_t iflist = prop_dictionary_get(dbg, "interfaces");
1196 	u_int if_idx = if_nametoindex(ifname);
1197 
1198 	if (_npf_prop_array_lookup(iflist, "name", ifname)) {
1199 		return;
1200 	}
1201 	ifdict = prop_dictionary_create();
1202 	prop_dictionary_set_cstring(ifdict, "name", ifname);
1203 	prop_dictionary_set_uint32(ifdict, "index", if_idx);
1204 	prop_array_add(iflist, ifdict);
1205 	prop_object_release(ifdict);
1206 }
1207