xref: /freebsd/sys/netpfil/pf/pf_nv.c (revision ccfd87fe2ac0e2e6aeb1911a7d7cce6712a8564f)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include "opt_inet.h"
32 #include "opt_inet6.h"
33 
34 #include <sys/param.h>
35 #include <sys/errno.h>
36 #include <sys/limits.h>
37 #include <sys/queue.h>
38 #include <sys/systm.h>
39 
40 #include <netpfil/pf/pf_nv.h>
41 
42 #define	PF_NV_IMPL_UINT(fnname, type, max)					\
43 	int									\
44 	pf_nv ## fnname ## _opt(const nvlist_t *nvl, const char *name,		\
45 	    type *val, type dflt)						\
46 	{									\
47 		uint64_t raw;							\
48 		if (! nvlist_exists_number(nvl, name)) {			\
49 			*val = dflt;						\
50 			return (0);						\
51 		}								\
52 		raw = nvlist_get_number(nvl, name);				\
53 		if (raw > max)							\
54 			return (ERANGE);					\
55 		*val = (type)raw;						\
56 		return (0);							\
57 	}									\
58 	int									\
59 	pf_nv ## fnname(const nvlist_t *nvl, const char *name, type *val)	\
60 	{									\
61 		uint64_t raw;							\
62 		if (! nvlist_exists_number(nvl, name))				\
63 			return (EINVAL);					\
64 		raw = nvlist_get_number(nvl, name);				\
65 		if (raw > max)							\
66 			return (ERANGE);					\
67 		*val = (type)raw;						\
68 		return (0);							\
69 	}									\
70 	int									\
71 	pf_nv ## fnname ## _array(const nvlist_t *nvl, const char *name,	\
72 	    type *array, size_t maxelems, size_t *nelems)			\
73 	{									\
74 		const uint64_t *n;						\
75 		size_t nitems;							\
76 		bzero(array, sizeof(type) * maxelems);				\
77 		if (! nvlist_exists_number_array(nvl, name))			\
78 			return (EINVAL);					\
79 		n = nvlist_get_number_array(nvl, name, &nitems);		\
80 		if (nitems > maxelems)						\
81 			return (E2BIG);						\
82 		if (nelems != NULL)						\
83 			*nelems = nitems;					\
84 		for (size_t i = 0; i < nitems; i++) {				\
85 			if (n[i] > max)						\
86 				return (ERANGE);				\
87 			array[i] = (type)n[i];					\
88 		}								\
89 		return (0);							\
90 	}									\
91 	void									\
92 	pf_ ## fnname ## _array_nv(nvlist_t *nvl, const char *name,		\
93 	    const type *numbers, size_t count)					\
94 	{									\
95 		uint64_t tmp;							\
96 		for (size_t i = 0; i < count; i++) {				\
97 			tmp = numbers[i];					\
98 			nvlist_append_number_array(nvl, name, tmp);		\
99 		}								\
100 	}
101 
102 int
103 pf_nvbool(const nvlist_t *nvl, const char *name, bool *val)
104 {
105 	if (! nvlist_exists_bool(nvl, name))
106 		return (EINVAL);
107 
108 	*val = nvlist_get_bool(nvl, name);
109 
110 	return (0);
111 }
112 
113 int
114 pf_nvbinary(const nvlist_t *nvl, const char *name, void *data,
115     size_t expected_size)
116 {
117 	const uint8_t *nvdata;
118 	size_t len;
119 
120 	bzero(data, expected_size);
121 
122 	if (! nvlist_exists_binary(nvl, name))
123 		return (EINVAL);
124 
125 	nvdata = (const uint8_t *)nvlist_get_binary(nvl, name, &len);
126 	if (len > expected_size)
127 		return (EINVAL);
128 
129 	memcpy(data, nvdata, len);
130 
131 	return (0);
132 }
133 
134 PF_NV_IMPL_UINT(uint8, uint8_t, UINT8_MAX);
135 PF_NV_IMPL_UINT(uint16, uint16_t, UINT16_MAX);
136 PF_NV_IMPL_UINT(uint32, uint32_t, UINT32_MAX);
137 PF_NV_IMPL_UINT(uint64, uint64_t, UINT64_MAX);
138 
139 int
140 pf_nvint(const nvlist_t *nvl, const char *name, int *val)
141 {
142 	int64_t raw;
143 
144 	if (! nvlist_exists_number(nvl, name))
145 		return (EINVAL);
146 
147 	raw = nvlist_get_number(nvl, name);
148 	if (raw > INT_MAX || raw < INT_MIN)
149 		return (ERANGE);
150 
151 	*val = (int)raw;
152 
153 	return (0);
154 }
155 
156 int
157 pf_nvstring(const nvlist_t *nvl, const char *name, char *str, size_t maxlen)
158 {
159 	int ret;
160 
161 	if (! nvlist_exists_string(nvl, name))
162 		return (EINVAL);
163 
164 	ret = strlcpy(str, nvlist_get_string(nvl, name), maxlen);
165 	if (ret >= maxlen)
166 		return (EINVAL);
167 
168 	return (0);
169 }
170 
171 static int
172 pf_nvaddr_to_addr(const nvlist_t *nvl, struct pf_addr *paddr)
173 {
174 	return (pf_nvbinary(nvl, "addr", paddr, sizeof(*paddr)));
175 }
176 
177 static nvlist_t *
178 pf_addr_to_nvaddr(const struct pf_addr *paddr)
179 {
180 	nvlist_t *nvl;
181 
182 	nvl = nvlist_create(0);
183 	if (nvl == NULL)
184 		return (NULL);
185 
186 	nvlist_add_binary(nvl, "addr", paddr, sizeof(*paddr));
187 
188 	return (nvl);
189 }
190 
191 static int
192 pf_nvmape_to_mape(const nvlist_t *nvl, struct pf_mape_portset *mape)
193 {
194 	int error = 0;
195 
196 	bzero(mape, sizeof(*mape));
197 	PFNV_CHK(pf_nvuint8(nvl, "offset", &mape->offset));
198 	PFNV_CHK(pf_nvuint8(nvl, "psidlen", &mape->psidlen));
199 	PFNV_CHK(pf_nvuint16(nvl, "psid", &mape->psid));
200 
201 errout:
202 	return (error);
203 }
204 
205 static nvlist_t *
206 pf_mape_to_nvmape(const struct pf_mape_portset *mape)
207 {
208 	nvlist_t *nvl;
209 
210 	nvl = nvlist_create(0);
211 	if (nvl == NULL)
212 		return (NULL);
213 
214 	nvlist_add_number(nvl, "offset", mape->offset);
215 	nvlist_add_number(nvl, "psidlen", mape->psidlen);
216 	nvlist_add_number(nvl, "psid", mape->psid);
217 
218 	return (nvl);
219 }
220 
221 static int
222 pf_nvpool_to_pool(const nvlist_t *nvl, struct pf_kpool *kpool)
223 {
224 	int error = 0;
225 
226 	PFNV_CHK(pf_nvbinary(nvl, "key", &kpool->key, sizeof(kpool->key)));
227 
228 	if (nvlist_exists_nvlist(nvl, "counter")) {
229 		PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "counter"),
230 		    &kpool->counter));
231 	}
232 
233 	PFNV_CHK(pf_nvint(nvl, "tblidx", &kpool->tblidx));
234 	PFNV_CHK(pf_nvuint16_array(nvl, "proxy_port", kpool->proxy_port, 2,
235 	    NULL));
236 	PFNV_CHK(pf_nvuint8(nvl, "opts", &kpool->opts));
237 
238 	if (nvlist_exists_nvlist(nvl, "mape")) {
239 		PFNV_CHK(pf_nvmape_to_mape(nvlist_get_nvlist(nvl, "mape"),
240 		    &kpool->mape));
241 	}
242 
243 errout:
244 	return (error);
245 }
246 
247 static nvlist_t *
248 pf_pool_to_nvpool(const struct pf_kpool *pool)
249 {
250 	nvlist_t *nvl;
251 	nvlist_t *tmp;
252 
253 	nvl = nvlist_create(0);
254 	if (nvl == NULL)
255 		return (NULL);
256 
257 	nvlist_add_binary(nvl, "key", &pool->key, sizeof(pool->key));
258 	tmp = pf_addr_to_nvaddr(&pool->counter);
259 	if (tmp == NULL)
260 		goto error;
261 	nvlist_add_nvlist(nvl, "counter", tmp);
262 	nvlist_destroy(tmp);
263 
264 	nvlist_add_number(nvl, "tblidx", pool->tblidx);
265 	pf_uint16_array_nv(nvl, "proxy_port", pool->proxy_port, 2);
266 	nvlist_add_number(nvl, "opts", pool->opts);
267 
268 	tmp = pf_mape_to_nvmape(&pool->mape);
269 	if (tmp == NULL)
270 		goto error;
271 	nvlist_add_nvlist(nvl, "mape", tmp);
272 	nvlist_destroy(tmp);
273 
274 	return (nvl);
275 
276 error:
277 	nvlist_destroy(nvl);
278 	return (NULL);
279 }
280 
281 static int
282 pf_nvaddr_wrap_to_addr_wrap(const nvlist_t *nvl, struct pf_addr_wrap *addr)
283 {
284 	int error = 0;
285 
286 	bzero(addr, sizeof(*addr));
287 
288 	PFNV_CHK(pf_nvuint8(nvl, "type", &addr->type));
289 	PFNV_CHK(pf_nvuint8(nvl, "iflags", &addr->iflags));
290 	if (addr->type == PF_ADDR_DYNIFTL)
291 		PFNV_CHK(pf_nvstring(nvl, "ifname", addr->v.ifname,
292 		    sizeof(addr->v.ifname)));
293 	if (addr->type == PF_ADDR_TABLE)
294 		PFNV_CHK(pf_nvstring(nvl, "tblname", addr->v.tblname,
295 		    sizeof(addr->v.tblname)));
296 
297 	if (! nvlist_exists_nvlist(nvl, "addr"))
298 		return (EINVAL);
299 	PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"),
300 	    &addr->v.a.addr));
301 
302 	if (! nvlist_exists_nvlist(nvl, "mask"))
303 		return (EINVAL);
304 	PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "mask"),
305 	    &addr->v.a.mask));
306 
307 	switch (addr->type) {
308 	case PF_ADDR_DYNIFTL:
309 	case PF_ADDR_TABLE:
310 	case PF_ADDR_RANGE:
311 	case PF_ADDR_ADDRMASK:
312 	case PF_ADDR_NOROUTE:
313 	case PF_ADDR_URPFFAILED:
314 		break;
315 	default:
316 		return (EINVAL);
317 	}
318 
319 errout:
320 	return (error);
321 }
322 
323 static nvlist_t *
324 pf_addr_wrap_to_nvaddr_wrap(const struct pf_addr_wrap *addr)
325 {
326 	nvlist_t *nvl;
327 	nvlist_t *tmp;
328 	uint64_t num;
329 	struct pfr_ktable *kt;
330 
331 	nvl = nvlist_create(0);
332 	if (nvl == NULL)
333 		return (NULL);
334 
335 	nvlist_add_number(nvl, "type", addr->type);
336 	nvlist_add_number(nvl, "iflags", addr->iflags);
337 	if (addr->type == PF_ADDR_DYNIFTL) {
338 		nvlist_add_string(nvl, "ifname", addr->v.ifname);
339 		num = 0;
340 		if (addr->p.dyn != NULL)
341 			num = addr->p.dyn->pfid_acnt4 +
342 			    addr->p.dyn->pfid_acnt6;
343 		nvlist_add_number(nvl, "dyncnt", num);
344 	}
345 	if (addr->type == PF_ADDR_TABLE) {
346 		nvlist_add_string(nvl, "tblname", addr->v.tblname);
347 		num = -1;
348 		kt = addr->p.tbl;
349 		if ((kt->pfrkt_flags & PFR_TFLAG_ACTIVE) &&
350 		    kt->pfrkt_root != NULL)
351 			kt = kt->pfrkt_root;
352 		if (kt->pfrkt_flags & PFR_TFLAG_ACTIVE)
353 			num = kt->pfrkt_cnt;
354 		nvlist_add_number(nvl, "tblcnt", num);
355 	}
356 
357 	tmp = pf_addr_to_nvaddr(&addr->v.a.addr);
358 	if (tmp == NULL)
359 		goto error;
360 	nvlist_add_nvlist(nvl, "addr", tmp);
361 	nvlist_destroy(tmp);
362 	tmp = pf_addr_to_nvaddr(&addr->v.a.mask);
363 	if (tmp == NULL)
364 		goto error;
365 	nvlist_add_nvlist(nvl, "mask", tmp);
366 	nvlist_destroy(tmp);
367 
368 	return (nvl);
369 
370 error:
371 	nvlist_destroy(nvl);
372 	return (NULL);
373 }
374 
375 static int
376 pf_validate_op(uint8_t op)
377 {
378 	switch (op) {
379 	case PF_OP_NONE:
380 	case PF_OP_IRG:
381 	case PF_OP_EQ:
382 	case PF_OP_NE:
383 	case PF_OP_LT:
384 	case PF_OP_LE:
385 	case PF_OP_GT:
386 	case PF_OP_GE:
387 	case PF_OP_XRG:
388 	case PF_OP_RRG:
389 		break;
390 	default:
391 		return (EINVAL);
392 	}
393 
394 	return (0);
395 }
396 
397 static int
398 pf_nvrule_addr_to_rule_addr(const nvlist_t *nvl, struct pf_rule_addr *addr)
399 {
400 	int error = 0;
401 
402 	if (! nvlist_exists_nvlist(nvl, "addr"))
403 		return (EINVAL);
404 
405 	PFNV_CHK(pf_nvaddr_wrap_to_addr_wrap(nvlist_get_nvlist(nvl, "addr"),
406 	    &addr->addr));
407 	PFNV_CHK(pf_nvuint16_array(nvl, "port", addr->port, 2, NULL));
408 	PFNV_CHK(pf_nvuint8(nvl, "neg", &addr->neg));
409 	PFNV_CHK(pf_nvuint8(nvl, "port_op", &addr->port_op));
410 
411 	PFNV_CHK(pf_validate_op(addr->port_op));
412 
413 errout:
414 	return (error);
415 }
416 
417 static nvlist_t *
418 pf_rule_addr_to_nvrule_addr(const struct pf_rule_addr *addr)
419 {
420 	nvlist_t *nvl;
421 	nvlist_t *tmp;
422 
423 	nvl = nvlist_create(0);
424 	if (nvl == NULL)
425 		return (NULL);
426 
427 	tmp = pf_addr_wrap_to_nvaddr_wrap(&addr->addr);
428 	if (tmp == NULL)
429 		goto error;
430 	nvlist_add_nvlist(nvl, "addr", tmp);
431 	nvlist_destroy(tmp);
432 	pf_uint16_array_nv(nvl, "port", addr->port, 2);
433 	nvlist_add_number(nvl, "neg", addr->neg);
434 	nvlist_add_number(nvl, "port_op", addr->port_op);
435 
436 	return (nvl);
437 
438 error:
439 	nvlist_destroy(nvl);
440 	return (NULL);
441 }
442 
443 static int
444 pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid)
445 {
446 	int error = 0;
447 
448 	bzero(uid, sizeof(*uid));
449 
450 	PFNV_CHK(pf_nvuint32_array(nvl, "uid", uid->uid, 2, NULL));
451 	PFNV_CHK(pf_nvuint8(nvl, "op", &uid->op));
452 
453 	PFNV_CHK(pf_validate_op(uid->op));
454 
455 errout:
456 	return (error);
457 }
458 
459 static nvlist_t *
460 pf_rule_uid_to_nvrule_uid(const struct pf_rule_uid *uid)
461 {
462 	nvlist_t *nvl;
463 
464 	nvl = nvlist_create(0);
465 	if (nvl == NULL)
466 		return (NULL);
467 
468 	pf_uint32_array_nv(nvl, "uid", uid->uid, 2);
469 	nvlist_add_number(nvl, "op", uid->op);
470 
471 	return (nvl);
472 }
473 
474 static int
475 pf_nvrule_gid_to_rule_gid(const nvlist_t *nvl, struct pf_rule_gid *gid)
476 {
477 	/* Cheat a little. These stucts are the same, other than the name of
478 	 * the first field. */
479 	return (pf_nvrule_uid_to_rule_uid(nvl, (struct pf_rule_uid *)gid));
480 }
481 
482 int
483 pf_check_rule_addr(const struct pf_rule_addr *addr)
484 {
485 
486 	switch (addr->addr.type) {
487 	case PF_ADDR_ADDRMASK:
488 	case PF_ADDR_NOROUTE:
489 	case PF_ADDR_DYNIFTL:
490 	case PF_ADDR_TABLE:
491 	case PF_ADDR_URPFFAILED:
492 	case PF_ADDR_RANGE:
493 		break;
494 	default:
495 		return (EINVAL);
496 	}
497 
498 	if (addr->addr.p.dyn != NULL) {
499 		return (EINVAL);
500 	}
501 
502 	return (0);
503 }
504 
505 
506 int
507 pf_nvrule_to_krule(const nvlist_t *nvl, struct pf_krule *rule)
508 {
509 	int error = 0;
510 
511 #define	ERROUT(x)	ERROUT_FUNCTION(errout, x)
512 
513 	PFNV_CHK(pf_nvuint32(nvl, "nr", &rule->nr));
514 
515 	if (! nvlist_exists_nvlist(nvl, "src"))
516 		ERROUT(EINVAL);
517 
518 	error = pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "src"),
519 	    &rule->src);
520 	if (error != 0)
521 		ERROUT(error);
522 
523 	if (! nvlist_exists_nvlist(nvl, "dst"))
524 		ERROUT(EINVAL);
525 
526 	PFNV_CHK(pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "dst"),
527 	    &rule->dst));
528 
529 	if (nvlist_exists_string(nvl, "label")) {
530 		PFNV_CHK(pf_nvstring(nvl, "label", rule->label[0],
531 		    sizeof(rule->label[0])));
532 	} else if (nvlist_exists_string_array(nvl, "labels")) {
533 		const char *const *strs;
534 		size_t items;
535 		int ret;
536 
537 		strs = nvlist_get_string_array(nvl, "labels", &items);
538 		if (items > PF_RULE_MAX_LABEL_COUNT)
539 			ERROUT(E2BIG);
540 
541 		for (size_t i = 0; i < items; i++) {
542 			ret = strlcpy(rule->label[i], strs[i],
543 			    sizeof(rule->label[0]));
544 			if (ret >= sizeof(rule->label[0]))
545 				ERROUT(E2BIG);
546 		}
547 	}
548 
549 	PFNV_CHK(pf_nvuint32_opt(nvl, "ridentifier", &rule->ridentifier, 0));
550 	PFNV_CHK(pf_nvstring(nvl, "ifname", rule->ifname,
551 	    sizeof(rule->ifname)));
552 	PFNV_CHK(pf_nvstring(nvl, "qname", rule->qname, sizeof(rule->qname)));
553 	PFNV_CHK(pf_nvstring(nvl, "pqname", rule->pqname,
554 	    sizeof(rule->pqname)));
555 	PFNV_CHK(pf_nvstring(nvl, "tagname", rule->tagname,
556 	    sizeof(rule->tagname)));
557 	PFNV_CHK(pf_nvuint16_opt(nvl, "dnpipe", &rule->dnpipe, 0));
558 	PFNV_CHK(pf_nvuint16_opt(nvl, "dnrpipe", &rule->dnrpipe, 0));
559 	PFNV_CHK(pf_nvuint32_opt(nvl, "dnflags", &rule->free_flags, 0));
560 	PFNV_CHK(pf_nvstring(nvl, "match_tagname", rule->match_tagname,
561 	    sizeof(rule->match_tagname)));
562 	PFNV_CHK(pf_nvstring(nvl, "overload_tblname", rule->overload_tblname,
563 	    sizeof(rule->overload_tblname)));
564 
565 	if (! nvlist_exists_nvlist(nvl, "rpool"))
566 		ERROUT(EINVAL);
567 	PFNV_CHK(pf_nvpool_to_pool(nvlist_get_nvlist(nvl, "rpool"),
568 	    &rule->rpool));
569 
570 	PFNV_CHK(pf_nvuint32(nvl, "os_fingerprint", &rule->os_fingerprint));
571 
572 	PFNV_CHK(pf_nvint(nvl, "rtableid", &rule->rtableid));
573 	PFNV_CHK(pf_nvuint32_array(nvl, "timeout", rule->timeout, PFTM_MAX, NULL));
574 	PFNV_CHK(pf_nvuint32(nvl, "max_states", &rule->max_states));
575 	PFNV_CHK(pf_nvuint32(nvl, "max_src_nodes", &rule->max_src_nodes));
576 	PFNV_CHK(pf_nvuint32(nvl, "max_src_states", &rule->max_src_states));
577 	PFNV_CHK(pf_nvuint32(nvl, "max_src_conn", &rule->max_src_conn));
578 	PFNV_CHK(pf_nvuint32(nvl, "max_src_conn_rate.limit",
579 	    &rule->max_src_conn_rate.limit));
580 	PFNV_CHK(pf_nvuint32(nvl, "max_src_conn_rate.seconds",
581 	    &rule->max_src_conn_rate.seconds));
582 	PFNV_CHK(pf_nvuint32(nvl, "prob", &rule->prob));
583 	PFNV_CHK(pf_nvuint32(nvl, "cuid", &rule->cuid));
584 	PFNV_CHK(pf_nvuint32(nvl, "cpid", &rule->cpid));
585 
586 	PFNV_CHK(pf_nvuint16(nvl, "return_icmp", &rule->return_icmp));
587 	PFNV_CHK(pf_nvuint16(nvl, "return_icmp6", &rule->return_icmp6));
588 
589 	PFNV_CHK(pf_nvuint16(nvl, "max_mss", &rule->max_mss));
590 	PFNV_CHK(pf_nvuint16(nvl, "scrub_flags", &rule->scrub_flags));
591 
592 	if (! nvlist_exists_nvlist(nvl, "uid"))
593 		ERROUT(EINVAL);
594 	PFNV_CHK(pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "uid"),
595 	    &rule->uid));
596 
597 	if (! nvlist_exists_nvlist(nvl, "gid"))
598 		ERROUT(EINVAL);
599 	PFNV_CHK(pf_nvrule_gid_to_rule_gid(nvlist_get_nvlist(nvl, "gid"),
600 	    &rule->gid));
601 
602 	PFNV_CHK(pf_nvuint32(nvl, "rule_flag", &rule->rule_flag));
603 	PFNV_CHK(pf_nvuint8(nvl, "action", &rule->action));
604 	PFNV_CHK(pf_nvuint8(nvl, "direction", &rule->direction));
605 	PFNV_CHK(pf_nvuint8(nvl, "log", &rule->log));
606 	PFNV_CHK(pf_nvuint8(nvl, "logif", &rule->logif));
607 	PFNV_CHK(pf_nvuint8(nvl, "quick", &rule->quick));
608 	PFNV_CHK(pf_nvuint8(nvl, "ifnot", &rule->ifnot));
609 	PFNV_CHK(pf_nvuint8(nvl, "match_tag_not", &rule->match_tag_not));
610 	PFNV_CHK(pf_nvuint8(nvl, "natpass", &rule->natpass));
611 
612 	PFNV_CHK(pf_nvuint8(nvl, "keep_state", &rule->keep_state));
613 	PFNV_CHK(pf_nvuint8(nvl, "af", &rule->af));
614 	PFNV_CHK(pf_nvuint8(nvl, "proto", &rule->proto));
615 	PFNV_CHK(pf_nvuint8(nvl, "type", &rule->type));
616 	PFNV_CHK(pf_nvuint8(nvl, "code", &rule->code));
617 	PFNV_CHK(pf_nvuint8(nvl, "flags", &rule->flags));
618 	PFNV_CHK(pf_nvuint8(nvl, "flagset", &rule->flagset));
619 	PFNV_CHK(pf_nvuint8(nvl, "min_ttl", &rule->min_ttl));
620 	PFNV_CHK(pf_nvuint8(nvl, "allow_opts", &rule->allow_opts));
621 	PFNV_CHK(pf_nvuint8(nvl, "rt", &rule->rt));
622 	PFNV_CHK(pf_nvuint8(nvl, "return_ttl", &rule->return_ttl));
623 	PFNV_CHK(pf_nvuint8(nvl, "tos", &rule->tos));
624 	PFNV_CHK(pf_nvuint8(nvl, "set_tos", &rule->set_tos));
625 
626 	PFNV_CHK(pf_nvuint8(nvl, "flush", &rule->flush));
627 	PFNV_CHK(pf_nvuint8(nvl, "prio", &rule->prio));
628 
629 	PFNV_CHK(pf_nvuint8_array(nvl, "set_prio", rule->set_prio, 2, NULL));
630 
631 	if (nvlist_exists_nvlist(nvl, "divert")) {
632 		const nvlist_t *nvldivert = nvlist_get_nvlist(nvl, "divert");
633 
634 		if (! nvlist_exists_nvlist(nvldivert, "addr"))
635 			ERROUT(EINVAL);
636 		PFNV_CHK(pf_nvaddr_to_addr(nvlist_get_nvlist(nvldivert, "addr"),
637 		    &rule->divert.addr));
638 		PFNV_CHK(pf_nvuint16(nvldivert, "port", &rule->divert.port));
639 	}
640 
641 	/* Validation */
642 #ifndef INET
643 	if (rule->af == AF_INET)
644 		ERROUT(EAFNOSUPPORT);
645 #endif /* INET */
646 #ifndef INET6
647 	if (rule->af == AF_INET6)
648 		ERROUT(EAFNOSUPPORT);
649 #endif /* INET6 */
650 
651 	PFNV_CHK(pf_check_rule_addr(&rule->src));
652 	PFNV_CHK(pf_check_rule_addr(&rule->dst));
653 
654 	return (0);
655 
656 #undef ERROUT
657 errout:
658 	return (error);
659 }
660 
661 static nvlist_t *
662 pf_divert_to_nvdivert(const struct pf_krule *rule)
663 {
664 	nvlist_t *nvl;
665 	nvlist_t *tmp;
666 
667 	nvl = nvlist_create(0);
668 	if (nvl == NULL)
669 		return (NULL);
670 
671 	tmp = pf_addr_to_nvaddr(&rule->divert.addr);
672 	if (tmp == NULL)
673 		goto error;
674 	nvlist_add_nvlist(nvl, "addr", tmp);
675 	nvlist_destroy(tmp);
676 	nvlist_add_number(nvl, "port", rule->divert.port);
677 
678 	return (nvl);
679 
680 error:
681 	nvlist_destroy(nvl);
682 	return (NULL);
683 }
684 
685 nvlist_t *
686 pf_krule_to_nvrule(struct pf_krule *rule)
687 {
688 	nvlist_t *nvl, *tmp;
689 
690 	nvl = nvlist_create(0);
691 	if (nvl == NULL)
692 		return (nvl);
693 
694 	nvlist_add_number(nvl, "nr", rule->nr);
695 	tmp = pf_rule_addr_to_nvrule_addr(&rule->src);
696 	if (tmp == NULL)
697 		goto error;
698 	nvlist_add_nvlist(nvl, "src", tmp);
699 	nvlist_destroy(tmp);
700 	tmp = pf_rule_addr_to_nvrule_addr(&rule->dst);
701 	if (tmp == NULL)
702 		goto error;
703 	nvlist_add_nvlist(nvl, "dst", tmp);
704 	nvlist_destroy(tmp);
705 
706 	for (int i = 0; i < PF_SKIP_COUNT; i++) {
707 		nvlist_append_number_array(nvl, "skip",
708 		    rule->skip[i].ptr ? rule->skip[i].ptr->nr : -1);
709 	}
710 
711 	for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) {
712 		nvlist_append_string_array(nvl, "labels", rule->label[i]);
713 	}
714 	nvlist_add_string(nvl, "label", rule->label[0]);
715 	nvlist_add_number(nvl, "ridentifier", rule->ridentifier);
716 	nvlist_add_string(nvl, "ifname", rule->ifname);
717 	nvlist_add_string(nvl, "qname", rule->qname);
718 	nvlist_add_string(nvl, "pqname", rule->pqname);
719 	nvlist_add_number(nvl, "dnpipe", rule->dnpipe);
720 	nvlist_add_number(nvl, "dnrpipe", rule->dnrpipe);
721 	nvlist_add_number(nvl, "dnflags", rule->free_flags);
722 	nvlist_add_string(nvl, "tagname", rule->tagname);
723 	nvlist_add_string(nvl, "match_tagname", rule->match_tagname);
724 	nvlist_add_string(nvl, "overload_tblname", rule->overload_tblname);
725 
726 	tmp = pf_pool_to_nvpool(&rule->rpool);
727 	if (tmp == NULL)
728 		goto error;
729 	nvlist_add_nvlist(nvl, "rpool", tmp);
730 	nvlist_destroy(tmp);
731 
732 	nvlist_add_number(nvl, "evaluations",
733 	    pf_counter_u64_fetch(&rule->evaluations));
734 	for (int i = 0; i < 2; i++) {
735 		nvlist_append_number_array(nvl, "packets",
736 		    pf_counter_u64_fetch(&rule->packets[i]));
737 		nvlist_append_number_array(nvl, "bytes",
738 		    pf_counter_u64_fetch(&rule->bytes[i]));
739 	}
740 	nvlist_add_number(nvl, "timestamp", pf_get_timestamp(rule));
741 
742 	nvlist_add_number(nvl, "os_fingerprint", rule->os_fingerprint);
743 
744 	nvlist_add_number(nvl, "rtableid", rule->rtableid);
745 	pf_uint32_array_nv(nvl, "timeout", rule->timeout, PFTM_MAX);
746 	nvlist_add_number(nvl, "max_states", rule->max_states);
747 	nvlist_add_number(nvl, "max_src_nodes", rule->max_src_nodes);
748 	nvlist_add_number(nvl, "max_src_states", rule->max_src_states);
749 	nvlist_add_number(nvl, "max_src_conn", rule->max_src_conn);
750 	nvlist_add_number(nvl, "max_src_conn_rate.limit",
751 	    rule->max_src_conn_rate.limit);
752 	nvlist_add_number(nvl, "max_src_conn_rate.seconds",
753 	    rule->max_src_conn_rate.seconds);
754 	nvlist_add_number(nvl, "qid", rule->qid);
755 	nvlist_add_number(nvl, "pqid", rule->pqid);
756 	nvlist_add_number(nvl, "prob", rule->prob);
757 	nvlist_add_number(nvl, "cuid", rule->cuid);
758 	nvlist_add_number(nvl, "cpid", rule->cpid);
759 
760 	nvlist_add_number(nvl, "states_cur",
761 	    counter_u64_fetch(rule->states_cur));
762 	nvlist_add_number(nvl, "states_tot",
763 	    counter_u64_fetch(rule->states_tot));
764 	nvlist_add_number(nvl, "src_nodes",
765 	    counter_u64_fetch(rule->src_nodes));
766 
767 	nvlist_add_number(nvl, "return_icmp", rule->return_icmp);
768 	nvlist_add_number(nvl, "return_icmp6", rule->return_icmp6);
769 
770 	nvlist_add_number(nvl, "max_mss", rule->max_mss);
771 	nvlist_add_number(nvl, "scrub_flags", rule->scrub_flags);
772 
773 	tmp = pf_rule_uid_to_nvrule_uid(&rule->uid);
774 	if (tmp == NULL)
775 		goto error;
776 	nvlist_add_nvlist(nvl, "uid", tmp);
777 	nvlist_destroy(tmp);
778 	tmp = pf_rule_uid_to_nvrule_uid((const struct pf_rule_uid *)&rule->gid);
779 	if (tmp == NULL)
780 		goto error;
781 	nvlist_add_nvlist(nvl, "gid", tmp);
782 	nvlist_destroy(tmp);
783 
784 	nvlist_add_number(nvl, "rule_flag", rule->rule_flag);
785 	nvlist_add_number(nvl, "action", rule->action);
786 	nvlist_add_number(nvl, "direction", rule->direction);
787 	nvlist_add_number(nvl, "log", rule->log);
788 	nvlist_add_number(nvl, "logif", rule->logif);
789 	nvlist_add_number(nvl, "quick", rule->quick);
790 	nvlist_add_number(nvl, "ifnot", rule->ifnot);
791 	nvlist_add_number(nvl, "match_tag_not", rule->match_tag_not);
792 	nvlist_add_number(nvl, "natpass", rule->natpass);
793 
794 	nvlist_add_number(nvl, "keep_state", rule->keep_state);
795 	nvlist_add_number(nvl, "af", rule->af);
796 	nvlist_add_number(nvl, "proto", rule->proto);
797 	nvlist_add_number(nvl, "type", rule->type);
798 	nvlist_add_number(nvl, "code", rule->code);
799 	nvlist_add_number(nvl, "flags", rule->flags);
800 	nvlist_add_number(nvl, "flagset", rule->flagset);
801 	nvlist_add_number(nvl, "min_ttl", rule->min_ttl);
802 	nvlist_add_number(nvl, "allow_opts", rule->allow_opts);
803 	nvlist_add_number(nvl, "rt", rule->rt);
804 	nvlist_add_number(nvl, "return_ttl", rule->return_ttl);
805 	nvlist_add_number(nvl, "tos", rule->tos);
806 	nvlist_add_number(nvl, "set_tos", rule->set_tos);
807 	nvlist_add_number(nvl, "anchor_relative", rule->anchor_relative);
808 	nvlist_add_number(nvl, "anchor_wildcard", rule->anchor_wildcard);
809 
810 	nvlist_add_number(nvl, "flush", rule->flush);
811 	nvlist_add_number(nvl, "prio", rule->prio);
812 
813 	pf_uint8_array_nv(nvl, "set_prio", rule->set_prio, 2);
814 
815 	tmp = pf_divert_to_nvdivert(rule);
816 	if (tmp == NULL)
817 		goto error;
818 	nvlist_add_nvlist(nvl, "divert", tmp);
819 	nvlist_destroy(tmp);
820 
821 	return (nvl);
822 
823 error:
824 	nvlist_destroy(nvl);
825 	return (NULL);
826 }
827 
828 static int
829 pf_nvstate_cmp_to_state_cmp(const nvlist_t *nvl, struct pf_state_cmp *cmp)
830 {
831 	int error = 0;
832 
833 	bzero(cmp, sizeof(*cmp));
834 
835 	PFNV_CHK(pf_nvuint64(nvl, "id", &cmp->id));
836 	PFNV_CHK(pf_nvuint32(nvl, "creatorid", &cmp->creatorid));
837 	PFNV_CHK(pf_nvuint8(nvl, "direction", &cmp->direction));
838 
839 errout:
840 	return (error);
841 }
842 
843 int
844 pf_nvstate_kill_to_kstate_kill(const nvlist_t *nvl,
845     struct pf_kstate_kill *kill)
846 {
847 	int error = 0;
848 
849 	bzero(kill, sizeof(*kill));
850 
851 	if (! nvlist_exists_nvlist(nvl, "cmp"))
852 		return (EINVAL);
853 
854 	PFNV_CHK(pf_nvstate_cmp_to_state_cmp(nvlist_get_nvlist(nvl, "cmp"),
855 	    &kill->psk_pfcmp));
856 	PFNV_CHK(pf_nvuint8(nvl, "af", &kill->psk_af));
857 	PFNV_CHK(pf_nvint(nvl, "proto", &kill->psk_proto));
858 
859 	if (! nvlist_exists_nvlist(nvl, "src"))
860 		return (EINVAL);
861 	PFNV_CHK(pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "src"),
862 	    &kill->psk_src));
863 	if (! nvlist_exists_nvlist(nvl, "dst"))
864 		return (EINVAL);
865 	PFNV_CHK(pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "dst"),
866 	    &kill->psk_dst));
867 	if (nvlist_exists_nvlist(nvl, "rt_addr")) {
868 		PFNV_CHK(pf_nvrule_addr_to_rule_addr(
869 		    nvlist_get_nvlist(nvl, "rt_addr"), &kill->psk_rt_addr));
870 	}
871 
872 	PFNV_CHK(pf_nvstring(nvl, "ifname", kill->psk_ifname,
873 	    sizeof(kill->psk_ifname)));
874 	PFNV_CHK(pf_nvstring(nvl, "label", kill->psk_label,
875 	    sizeof(kill->psk_label)));
876 	PFNV_CHK(pf_nvbool(nvl, "kill_match", &kill->psk_kill_match));
877 
878 errout:
879 	return (error);
880 }
881 
882 static nvlist_t *
883 pf_state_key_to_nvstate_key(const struct pf_state_key *key)
884 {
885 	nvlist_t	*nvl, *tmp;
886 
887 	nvl = nvlist_create(0);
888 	if (nvl == NULL)
889 		return (NULL);
890 
891 	for (int i = 0; i < 2; i++) {
892 		tmp = pf_addr_to_nvaddr(&key->addr[i]);
893 		if (tmp == NULL)
894 			goto errout;
895 		nvlist_append_nvlist_array(nvl, "addr", tmp);
896 		nvlist_destroy(tmp);
897 		nvlist_append_number_array(nvl, "port", key->port[i]);
898 	}
899 	nvlist_add_number(nvl, "af", key->af);
900 	nvlist_add_number(nvl, "proto", key->proto);
901 
902 	return (nvl);
903 
904 errout:
905 	nvlist_destroy(nvl);
906 	return (NULL);
907 }
908 
909 static nvlist_t *
910 pf_state_peer_to_nvstate_peer(const struct pf_state_peer *peer)
911 {
912 	nvlist_t *nvl;
913 
914 	nvl = nvlist_create(0);
915 	if (nvl == NULL)
916 		return (NULL);
917 
918 	nvlist_add_number(nvl, "seqlo", peer->seqlo);
919 	nvlist_add_number(nvl, "seqhi", peer->seqhi);
920 	nvlist_add_number(nvl, "seqdiff", peer->seqdiff);
921 	nvlist_add_number(nvl, "state", peer->state);
922 	nvlist_add_number(nvl, "wscale", peer->wscale);
923 
924 	return (nvl);
925 }
926 
927 nvlist_t *
928 pf_state_to_nvstate(const struct pf_kstate *s)
929 {
930 	nvlist_t	*nvl, *tmp;
931 	uint32_t	 expire, flags = 0;
932 
933 	nvl = nvlist_create(0);
934 	if (nvl == NULL)
935 		return (NULL);
936 
937 	nvlist_add_number(nvl, "id", s->id);
938 	nvlist_add_string(nvl, "ifname", s->kif->pfik_name);
939 	nvlist_add_string(nvl, "orig_ifname", s->orig_kif->pfik_name);
940 
941 	tmp = pf_state_key_to_nvstate_key(s->key[PF_SK_STACK]);
942 	if (tmp == NULL)
943 		goto errout;
944 	nvlist_add_nvlist(nvl, "stack_key", tmp);
945 	nvlist_destroy(tmp);
946 
947 	tmp = pf_state_key_to_nvstate_key(s->key[PF_SK_WIRE]);
948 	if (tmp == NULL)
949 		goto errout;
950 	nvlist_add_nvlist(nvl, "wire_key", tmp);
951 	nvlist_destroy(tmp);
952 
953 	tmp = pf_state_peer_to_nvstate_peer(&s->src);
954 	if (tmp == NULL)
955 		goto errout;
956 	nvlist_add_nvlist(nvl, "src", tmp);
957 	nvlist_destroy(tmp);
958 
959 	tmp = pf_state_peer_to_nvstate_peer(&s->dst);
960 	if (tmp == NULL)
961 		goto errout;
962 	nvlist_add_nvlist(nvl, "dst", tmp);
963 	nvlist_destroy(tmp);
964 
965 	tmp = pf_addr_to_nvaddr(&s->rt_addr);
966 	if (tmp == NULL)
967 		goto errout;
968 	nvlist_add_nvlist(nvl, "rt_addr", tmp);
969 	nvlist_destroy(tmp);
970 
971 	nvlist_add_number(nvl, "rule", s->rule.ptr ? s->rule.ptr->nr : -1);
972 	nvlist_add_number(nvl, "anchor",
973 	    s->anchor.ptr ? s->anchor.ptr->nr : -1);
974 	nvlist_add_number(nvl, "nat_rule",
975 	    s->nat_rule.ptr ? s->nat_rule.ptr->nr : -1);
976 	nvlist_add_number(nvl, "creation", s->creation);
977 
978 	expire = pf_state_expires(s);
979 	if (expire <= time_uptime)
980 		expire = 0;
981 	else
982 		expire = expire - time_uptime;
983 	nvlist_add_number(nvl, "expire", expire);
984 
985 	for (int i = 0; i < 2; i++) {
986 		nvlist_append_number_array(nvl, "packets",
987 		    s->packets[i]);
988 		nvlist_append_number_array(nvl, "bytes",
989 		    s->bytes[i]);
990 	}
991 
992 	nvlist_add_number(nvl, "creatorid", s->creatorid);
993 	nvlist_add_number(nvl, "direction", s->direction);
994 	nvlist_add_number(nvl, "state_flags", s->state_flags);
995 	if (s->src_node)
996 		flags |= PFSYNC_FLAG_SRCNODE;
997 	if (s->nat_src_node)
998 		flags |= PFSYNC_FLAG_NATSRCNODE;
999 	nvlist_add_number(nvl, "sync_flags", flags);
1000 
1001 	return (nvl);
1002 
1003 errout:
1004 	nvlist_destroy(nvl);
1005 	return (NULL);
1006 }
1007 
1008 static int
1009 pf_nveth_rule_addr_to_keth_rule_addr(const nvlist_t *nvl,
1010     struct pf_keth_rule_addr *krule)
1011 {
1012 	static const u_int8_t EMPTY_MAC[ETHER_ADDR_LEN] = { 0 };
1013 	int error = 0;
1014 
1015 	PFNV_CHK(pf_nvbinary(nvl, "addr", &krule->addr, sizeof(krule->addr)));
1016 	PFNV_CHK(pf_nvbool(nvl, "neg", &krule->neg));
1017 	if (nvlist_exists_binary(nvl, "mask"))
1018 		PFNV_CHK(pf_nvbinary(nvl, "mask", &krule->mask,
1019 		    sizeof(krule->mask)));
1020 
1021 	/* To make checks for 'is this address set?' easier. */
1022 	if (memcmp(krule->addr, EMPTY_MAC, ETHER_ADDR_LEN) != 0)
1023 		krule->isset = 1;
1024 
1025 errout:
1026 	return (error);
1027 }
1028 
1029 static nvlist_t*
1030 pf_keth_rule_addr_to_nveth_rule_addr(const struct pf_keth_rule_addr *krule)
1031 {
1032 	nvlist_t *nvl;
1033 
1034 	nvl = nvlist_create(0);
1035 	if (nvl == NULL)
1036 		return (NULL);
1037 
1038 	nvlist_add_binary(nvl, "addr", &krule->addr, sizeof(krule->addr));
1039 	nvlist_add_binary(nvl, "mask", &krule->mask, sizeof(krule->mask));
1040 	nvlist_add_bool(nvl, "neg", krule->neg);
1041 
1042 	return (nvl);
1043 }
1044 
1045 nvlist_t*
1046 pf_keth_rule_to_nveth_rule(const struct pf_keth_rule *krule)
1047 {
1048 	nvlist_t *nvl, *addr;
1049 
1050 	nvl = nvlist_create(0);
1051 	if (nvl == NULL)
1052 		return (NULL);
1053 
1054 	for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) {
1055 		nvlist_append_string_array(nvl, "labels", krule->label[i]);
1056 	}
1057 	nvlist_add_number(nvl, "ridentifier", krule->ridentifier);
1058 
1059 	nvlist_add_number(nvl, "nr", krule->nr);
1060 	nvlist_add_bool(nvl, "quick", krule->quick);
1061 	nvlist_add_string(nvl, "ifname", krule->ifname);
1062 	nvlist_add_bool(nvl, "ifnot", krule->ifnot);
1063 	nvlist_add_number(nvl, "direction", krule->direction);
1064 	nvlist_add_number(nvl, "proto", krule->proto);
1065 	nvlist_add_string(nvl, "match_tagname", krule->match_tagname);
1066 	nvlist_add_number(nvl, "match_tag", krule->match_tag);
1067 	nvlist_add_bool(nvl, "match_tag_not", krule->match_tag_not);
1068 
1069 	addr = pf_keth_rule_addr_to_nveth_rule_addr(&krule->src);
1070 	if (addr == NULL) {
1071 		nvlist_destroy(nvl);
1072 		return (NULL);
1073 	}
1074 	nvlist_add_nvlist(nvl, "src", addr);
1075 	nvlist_destroy(addr);
1076 
1077 	addr = pf_keth_rule_addr_to_nveth_rule_addr(&krule->dst);
1078 	if (addr == NULL) {
1079 		nvlist_destroy(nvl);
1080 		return (NULL);
1081 	}
1082 	nvlist_add_nvlist(nvl, "dst", addr);
1083 	nvlist_destroy(addr);
1084 
1085 	addr = pf_rule_addr_to_nvrule_addr(&krule->ipsrc);
1086 	if (addr == NULL) {
1087 		nvlist_destroy(nvl);
1088 		return (NULL);
1089 	}
1090 	nvlist_add_nvlist(nvl, "ipsrc", addr);
1091 	nvlist_destroy(addr);
1092 
1093 	addr = pf_rule_addr_to_nvrule_addr(&krule->ipdst);
1094 	if (addr == NULL) {
1095 		nvlist_destroy(nvl);
1096 		return (NULL);
1097 	}
1098 	nvlist_add_nvlist(nvl, "ipdst", addr);
1099 	nvlist_destroy(addr);
1100 
1101 	nvlist_add_number(nvl, "evaluations",
1102 	    counter_u64_fetch(krule->evaluations));
1103 	nvlist_add_number(nvl, "packets-in",
1104 	    counter_u64_fetch(krule->packets[0]));
1105 	nvlist_add_number(nvl, "packets-out",
1106 	    counter_u64_fetch(krule->packets[1]));
1107 	nvlist_add_number(nvl, "bytes-in",
1108 	    counter_u64_fetch(krule->bytes[0]));
1109 	nvlist_add_number(nvl, "bytes-out",
1110 	    counter_u64_fetch(krule->bytes[1]));
1111 
1112 	nvlist_add_number(nvl, "timestamp", pf_get_timestamp(krule));
1113 	nvlist_add_string(nvl, "qname", krule->qname);
1114 	nvlist_add_string(nvl, "tagname", krule->tagname);
1115 
1116 	nvlist_add_number(nvl, "dnpipe", krule->dnpipe);
1117 	nvlist_add_number(nvl, "dnflags", krule->dnflags);
1118 
1119 	nvlist_add_number(nvl, "anchor_relative", krule->anchor_relative);
1120 	nvlist_add_number(nvl, "anchor_wildcard", krule->anchor_wildcard);
1121 
1122 	nvlist_add_string(nvl, "bridge_to", krule->bridge_to_name);
1123 	nvlist_add_number(nvl, "action", krule->action);
1124 
1125 	return (nvl);
1126 }
1127 
1128 int
1129 pf_nveth_rule_to_keth_rule(const nvlist_t *nvl,
1130     struct pf_keth_rule *krule)
1131 {
1132 	int error = 0;
1133 
1134 #define ERROUT(x)	ERROUT_FUNCTION(errout, x)
1135 
1136 	bzero(krule, sizeof(*krule));
1137 
1138 	if (nvlist_exists_string_array(nvl, "labels")) {
1139 		const char *const *strs;
1140 		size_t items;
1141 		int ret;
1142 
1143 		strs = nvlist_get_string_array(nvl, "labels", &items);
1144 		if (items > PF_RULE_MAX_LABEL_COUNT)
1145 			ERROUT(E2BIG);
1146 
1147 		for (size_t i = 0; i < items; i++) {
1148 			ret = strlcpy(krule->label[i], strs[i],
1149 			    sizeof(krule->label[0]));
1150 			if (ret >= sizeof(krule->label[0]))
1151 				ERROUT(E2BIG);
1152 		}
1153 	}
1154 
1155 	PFNV_CHK(pf_nvuint32_opt(nvl, "ridentifier", &krule->ridentifier, 0));
1156 
1157 	PFNV_CHK(pf_nvuint32(nvl, "nr", &krule->nr));
1158 	PFNV_CHK(pf_nvbool(nvl, "quick", &krule->quick));
1159 	PFNV_CHK(pf_nvstring(nvl, "ifname", krule->ifname,
1160 	    sizeof(krule->ifname)));
1161 	PFNV_CHK(pf_nvbool(nvl, "ifnot", &krule->ifnot));
1162 	PFNV_CHK(pf_nvuint8(nvl, "direction", &krule->direction));
1163 	PFNV_CHK(pf_nvuint16(nvl, "proto", &krule->proto));
1164 
1165 	if (nvlist_exists_nvlist(nvl, "src")) {
1166 		error = pf_nveth_rule_addr_to_keth_rule_addr(
1167 		    nvlist_get_nvlist(nvl, "src"), &krule->src);
1168 		if (error)
1169 			return (error);
1170 	}
1171 	if (nvlist_exists_nvlist(nvl, "dst")) {
1172 		error = pf_nveth_rule_addr_to_keth_rule_addr(
1173 		    nvlist_get_nvlist(nvl, "dst"), &krule->dst);
1174 		if (error)
1175 			return (error);
1176 	}
1177 
1178 	if (nvlist_exists_nvlist(nvl, "ipsrc")) {
1179 		error = pf_nvrule_addr_to_rule_addr(
1180 		    nvlist_get_nvlist(nvl, "ipsrc"), &krule->ipsrc);
1181 		if (error != 0)
1182 			return (error);
1183 
1184 		if (krule->ipsrc.addr.type != PF_ADDR_ADDRMASK &&
1185 		    krule->ipsrc.addr.type != PF_ADDR_TABLE)
1186 			return (EINVAL);
1187 	}
1188 
1189 	if (nvlist_exists_nvlist(nvl, "ipdst")) {
1190 		error = pf_nvrule_addr_to_rule_addr(
1191 		    nvlist_get_nvlist(nvl, "ipdst"), &krule->ipdst);
1192 		if (error != 0)
1193 			return (error);
1194 
1195 		if (krule->ipdst.addr.type != PF_ADDR_ADDRMASK &&
1196 		    krule->ipdst.addr.type != PF_ADDR_TABLE)
1197 			return (EINVAL);
1198 	}
1199 
1200 	if (nvlist_exists_string(nvl, "match_tagname")) {
1201 		PFNV_CHK(pf_nvstring(nvl, "match_tagname", krule->match_tagname,
1202 		    sizeof(krule->match_tagname)));
1203 		PFNV_CHK(pf_nvbool(nvl, "match_tag_not", &krule->match_tag_not));
1204 	}
1205 
1206 	PFNV_CHK(pf_nvstring(nvl, "qname", krule->qname, sizeof(krule->qname)));
1207 	PFNV_CHK(pf_nvstring(nvl, "tagname", krule->tagname,
1208 	    sizeof(krule->tagname)));
1209 
1210 	PFNV_CHK(pf_nvuint16_opt(nvl, "dnpipe", &krule->dnpipe, 0));
1211 	PFNV_CHK(pf_nvuint32_opt(nvl, "dnflags", &krule->dnflags, 0));
1212 	PFNV_CHK(pf_nvstring(nvl, "bridge_to", krule->bridge_to_name,
1213 	    sizeof(krule->bridge_to_name)));
1214 
1215 	PFNV_CHK(pf_nvuint8(nvl, "action", &krule->action));
1216 
1217 	if (krule->action != PF_PASS && krule->action != PF_DROP &&
1218 	    krule->action != PF_MATCH)
1219 		return (EBADMSG);
1220 
1221 #undef ERROUT
1222 errout:
1223 	return (error);
1224 }
1225