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