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