1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 *    - Redistributions of source code must retain the above copyright
12 *      notice, this list of conditions and the following disclaimer.
13 *    - Redistributions in binary form must reproduce the above
14 *      copyright notice, this list of conditions and the following
15 *      disclaimer in the documentation and/or other materials provided
16 *      with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 * 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
34#include <sys/ioctl.h>
35#include <sys/nv.h>
36#include <sys/queue.h>
37#include <sys/types.h>
38
39#include <net/if.h>
40#include <net/pfvar.h>
41#include <netinet/in.h>
42
43#include <netpfil/pf/pf_nl.h>
44#include <netlink/netlink.h>
45#include <netlink/netlink_generic.h>
46#include <netlink/netlink_snl.h>
47#include <netlink/netlink_snl_generic.h>
48#include <netlink/netlink_snl_route.h>
49
50#include <assert.h>
51#include <err.h>
52#include <errno.h>
53#include <fcntl.h>
54#include <stdlib.h>
55#include <string.h>
56
57#include "libpfctl.h"
58
59struct pfctl_handle {
60	int fd;
61	struct snl_state ss;
62};
63
64const char* PFCTL_SYNCOOKIES_MODE_NAMES[] = {
65	"never",
66	"always",
67	"adaptive"
68};
69
70static int	_pfctl_clear_states(int , const struct pfctl_kill *,
71		    unsigned int *, uint64_t);
72
73struct pfctl_handle *
74pfctl_open(const char *pf_device)
75{
76	struct pfctl_handle *h;
77
78	h = calloc(1, sizeof(struct pfctl_handle));
79	h->fd = -1;
80
81	h->fd = open(pf_device, O_RDWR);
82	if (h->fd < 0)
83		goto error;
84
85	if (!snl_init(&h->ss, NETLINK_GENERIC))
86		goto error;
87
88	return (h);
89error:
90	close(h->fd);
91	snl_free(&h->ss);
92	free(h);
93
94	return (NULL);
95}
96
97void
98pfctl_close(struct pfctl_handle *h)
99{
100	close(h->fd);
101	snl_free(&h->ss);
102	free(h);
103}
104
105int
106pfctl_fd(struct pfctl_handle *h)
107{
108	return (h->fd);
109}
110
111static int
112pfctl_do_ioctl(int dev, uint cmd, size_t size, nvlist_t **nvl)
113{
114	struct pfioc_nv nv;
115	void *data;
116	size_t nvlen;
117	int ret;
118
119	data = nvlist_pack(*nvl, &nvlen);
120	if (nvlen > size)
121		size = nvlen;
122
123retry:
124	nv.data = malloc(size);
125	if (nv.data == NULL) {
126		ret = ENOMEM;
127		goto out;
128	}
129
130	memcpy(nv.data, data, nvlen);
131
132	nv.len = nvlen;
133	nv.size = size;
134
135	ret = ioctl(dev, cmd, &nv);
136	if (ret == -1 && errno == ENOSPC) {
137		size *= 2;
138		free(nv.data);
139		goto retry;
140	}
141
142	nvlist_destroy(*nvl);
143	*nvl = NULL;
144
145	if (ret == 0) {
146		*nvl = nvlist_unpack(nv.data, nv.len, 0);
147		if (*nvl == NULL) {
148			ret = EIO;
149			goto out;
150		}
151	} else {
152		ret = errno;
153	}
154
155out:
156	free(data);
157	free(nv.data);
158
159	return (ret);
160}
161
162static void
163pf_nvuint_8_array(const nvlist_t *nvl, const char *name, size_t maxelems,
164    uint8_t *numbers, size_t *nelems)
165{
166	const uint64_t *tmp;
167	size_t elems;
168
169	tmp = nvlist_get_number_array(nvl, name, &elems);
170	assert(elems <= maxelems);
171
172	for (size_t i = 0; i < elems; i++)
173		numbers[i] = tmp[i];
174
175	if (nelems)
176		*nelems = elems;
177}
178
179static void
180pf_nvuint_16_array(const nvlist_t *nvl, const char *name, size_t maxelems,
181    uint16_t *numbers, size_t *nelems)
182{
183	const uint64_t *tmp;
184	size_t elems;
185
186	tmp = nvlist_get_number_array(nvl, name, &elems);
187	assert(elems <= maxelems);
188
189	for (size_t i = 0; i < elems; i++)
190		numbers[i] = tmp[i];
191
192	if (nelems)
193		*nelems = elems;
194}
195
196static void
197pf_nvuint_32_array(const nvlist_t *nvl, const char *name, size_t maxelems,
198    uint32_t *numbers, size_t *nelems)
199{
200	const uint64_t *tmp;
201	size_t elems;
202
203	tmp = nvlist_get_number_array(nvl, name, &elems);
204
205	for (size_t i = 0; i < elems && i < maxelems; i++)
206		numbers[i] = tmp[i];
207
208	if (nelems)
209		*nelems = elems;
210}
211
212static void
213pf_nvuint_64_array(const nvlist_t *nvl, const char *name, size_t maxelems,
214    uint64_t *numbers, size_t *nelems)
215{
216	const uint64_t *tmp;
217	size_t elems;
218
219	tmp = nvlist_get_number_array(nvl, name, &elems);
220	assert(elems <= maxelems);
221
222	for (size_t i = 0; i < elems; i++)
223		numbers[i] = tmp[i];
224
225	if (nelems)
226		*nelems = elems;
227}
228
229int
230pfctl_startstop(struct pfctl_handle *h, int start)
231{
232	struct snl_errmsg_data e = {};
233	struct snl_writer nw;
234	struct nlmsghdr *hdr;
235	uint32_t seq_id;
236	int family_id;
237
238	family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
239	if (family_id == 0)
240		return (ENOTSUP);
241
242	snl_init_writer(&h->ss, &nw);
243	hdr = snl_create_genl_msg_request(&nw, family_id,
244	    start ? PFNL_CMD_START : PFNL_CMD_STOP);
245
246	hdr = snl_finalize_msg(&nw);
247	if (hdr == NULL)
248		return (ENOMEM);
249	seq_id = hdr->nlmsg_seq;
250
251	snl_send_message(&h->ss, hdr);
252
253	while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
254	}
255
256	return (e.error);
257}
258
259static void
260_pfctl_get_status_counters(const nvlist_t *nvl,
261    struct pfctl_status_counters *counters)
262{
263	const uint64_t		*ids, *counts;
264	const char *const	*names;
265	size_t id_len, counter_len, names_len;
266
267	ids = nvlist_get_number_array(nvl, "ids", &id_len);
268	counts = nvlist_get_number_array(nvl, "counters", &counter_len);
269	names = nvlist_get_string_array(nvl, "names", &names_len);
270	assert(id_len == counter_len);
271	assert(counter_len == names_len);
272
273	TAILQ_INIT(counters);
274
275	for (size_t i = 0; i < id_len; i++) {
276		struct pfctl_status_counter *c;
277
278		c = malloc(sizeof(*c));
279		if (c == NULL)
280			continue;
281
282		c->id = ids[i];
283		c->counter = counts[i];
284		c->name = strdup(names[i]);
285
286		TAILQ_INSERT_TAIL(counters, c, entry);
287	}
288}
289
290#define	_OUT(_field)	offsetof(struct pfctl_status_counter, _field)
291static const struct snl_attr_parser ap_counter[] = {
292	{ .type = PF_C_COUNTER, .off = _OUT(counter), .cb = snl_attr_get_uint64 },
293	{ .type = PF_C_NAME, .off = _OUT(name), .cb = snl_attr_get_string },
294	{ .type = PF_C_ID, .off = _OUT(id), .cb = snl_attr_get_uint32 },
295};
296SNL_DECLARE_ATTR_PARSER(counter_parser, ap_counter);
297#undef _OUT
298
299static bool
300snl_attr_get_counters(struct snl_state *ss, struct nlattr *nla,
301    const void *arg __unused, void *target)
302{
303	struct pfctl_status_counter counter = {};
304	struct pfctl_status_counter *c;
305	bool error;
306
307	error = snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), &counter_parser, &counter);
308	if (! error)
309		return (error);
310
311	c = malloc(sizeof(*c));
312	if (c == NULL)
313		return (false);
314
315	c->id = counter.id;
316	c->counter = counter.counter;
317	c->name = strdup(counter.name);
318
319	TAILQ_INSERT_TAIL((struct pfctl_status_counters *)target, c, entry);
320
321	return (error);
322}
323
324struct snl_uint64_array {
325	uint64_t *array;
326	size_t count;
327	size_t max;
328};
329static bool
330snl_attr_get_uint64_element(struct snl_state *ss, struct nlattr *nla,
331    const void *arg, void *target)
332{
333	bool error;
334	uint64_t value;
335	struct snl_uint64_array *t = (struct snl_uint64_array *)target;
336
337	if (t->count >= t->max)
338		return (false);
339
340	error = snl_attr_get_uint64(ss, nla, arg, &value);
341	if (! error)
342		return (error);
343
344	t->array[t->count++] = value;
345
346	return (true);
347}
348
349static const struct snl_attr_parser ap_array[] = {
350	{ .cb = snl_attr_get_uint64_element },
351};
352SNL_DECLARE_ATTR_PARSER(array_parser, ap_array);
353static bool
354snl_attr_get_uint64_array(struct snl_state *ss, struct nlattr *nla,
355    const void *arg, void *target)
356{
357	struct snl_uint64_array a = {
358		.array = target,
359		.count = 0,
360		.max = (size_t)arg,
361	};
362	bool error;
363
364	error = snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), &array_parser, &a);
365	if (! error)
366		return (error);
367
368	return (true);
369}
370
371#define	_OUT(_field)	offsetof(struct pfctl_status, _field)
372static const struct snl_attr_parser ap_getstatus[] = {
373	{ .type = PF_GS_IFNAME, .off = _OUT(ifname), .arg_u32 = IFNAMSIZ, .cb = snl_attr_copy_string },
374	{ .type = PF_GS_RUNNING, .off = _OUT(running), .cb = snl_attr_get_bool },
375	{ .type = PF_GS_SINCE, .off = _OUT(since), .cb = snl_attr_get_uint32 },
376	{ .type = PF_GS_DEBUG, .off = _OUT(debug), .cb = snl_attr_get_uint32 },
377	{ .type = PF_GS_HOSTID, .off = _OUT(hostid), .cb = snl_attr_get_uint32 },
378	{ .type = PF_GS_STATES, .off = _OUT(states), .cb = snl_attr_get_uint32 },
379	{ .type = PF_GS_SRC_NODES, .off = _OUT(src_nodes), .cb = snl_attr_get_uint32 },
380	{ .type = PF_GS_REASSEMBLE, .off = _OUT(reass), .cb = snl_attr_get_uint32 },
381	{ .type = PF_GS_SYNCOOKIES_ACTIVE, .off = _OUT(syncookies_active), .cb = snl_attr_get_uint32 },
382	{ .type = PF_GS_COUNTERS, .off = _OUT(counters), .cb = snl_attr_get_counters },
383	{ .type = PF_GS_LCOUNTERS, .off = _OUT(lcounters), .cb = snl_attr_get_counters },
384	{ .type = PF_GS_FCOUNTERS, .off = _OUT(fcounters), .cb = snl_attr_get_counters },
385	{ .type = PF_GS_SCOUNTERS, .off = _OUT(scounters), .cb = snl_attr_get_counters },
386	{ .type = PF_GS_CHKSUM, .off = _OUT(pf_chksum), .arg_u32 = PF_MD5_DIGEST_LENGTH, .cb = snl_attr_get_bytes },
387	{ .type = PF_GS_BCOUNTERS, .off = _OUT(bcounters), .arg_u32 = 2 * 2, .cb = snl_attr_get_uint64_array },
388	{ .type = PF_GS_PCOUNTERS, .off = _OUT(pcounters), .arg_u32 = 2 * 2 * 2, .cb = snl_attr_get_uint64_array },
389};
390static struct snl_field_parser fp_getstatus[] = {};
391SNL_DECLARE_PARSER(getstatus_parser, struct genlmsghdr, fp_getstatus, ap_getstatus);
392#undef _OUT
393
394struct pfctl_status *
395pfctl_get_status_h(struct pfctl_handle *h __unused)
396{
397	struct pfctl_status	*status;
398	struct snl_errmsg_data e = {};
399	struct nlmsghdr *hdr;
400	struct snl_writer nw;
401	uint32_t seq_id;
402	int family_id;
403
404	family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
405	if (family_id == 0)
406		return (NULL);
407
408	snl_init_writer(&h->ss, &nw);
409	hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GET_STATUS);
410	hdr->nlmsg_flags |= NLM_F_DUMP;
411
412	hdr = snl_finalize_msg(&nw);
413	if (hdr == NULL) {
414		return (NULL);
415	}
416
417	seq_id = hdr->nlmsg_seq;
418	if (! snl_send_message(&h->ss, hdr))
419		return (NULL);
420
421	status = calloc(1, sizeof(*status));
422	if (status == NULL)
423		return (NULL);
424	TAILQ_INIT(&status->counters);
425	TAILQ_INIT(&status->lcounters);
426	TAILQ_INIT(&status->fcounters);
427	TAILQ_INIT(&status->scounters);
428
429	while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
430		if (! snl_parse_nlmsg(&h->ss, hdr, &getstatus_parser, status))
431			continue;
432	}
433
434	return (status);
435}
436
437struct pfctl_status *
438pfctl_get_status(int dev)
439{
440	struct pfctl_status	*status;
441	nvlist_t	*nvl;
442	size_t		 len;
443	const void	*chksum;
444
445	status = calloc(1, sizeof(*status));
446	if (status == NULL)
447		return (NULL);
448
449	nvl = nvlist_create(0);
450
451	if (pfctl_do_ioctl(dev, DIOCGETSTATUSNV, 4096, &nvl)) {
452		nvlist_destroy(nvl);
453		free(status);
454		return (NULL);
455	}
456
457	status->running = nvlist_get_bool(nvl, "running");
458	status->since = nvlist_get_number(nvl, "since");
459	status->debug = nvlist_get_number(nvl, "debug");
460	status->hostid = ntohl(nvlist_get_number(nvl, "hostid"));
461	status->states = nvlist_get_number(nvl, "states");
462	status->src_nodes = nvlist_get_number(nvl, "src_nodes");
463	status->syncookies_active = nvlist_get_bool(nvl, "syncookies_active");
464	status->reass = nvlist_get_number(nvl, "reass");
465
466	strlcpy(status->ifname, nvlist_get_string(nvl, "ifname"),
467	    IFNAMSIZ);
468	chksum = nvlist_get_binary(nvl, "chksum", &len);
469	assert(len == PF_MD5_DIGEST_LENGTH);
470	memcpy(status->pf_chksum, chksum, len);
471
472	_pfctl_get_status_counters(nvlist_get_nvlist(nvl, "counters"),
473	    &status->counters);
474	_pfctl_get_status_counters(nvlist_get_nvlist(nvl, "lcounters"),
475	    &status->lcounters);
476	_pfctl_get_status_counters(nvlist_get_nvlist(nvl, "fcounters"),
477	    &status->fcounters);
478	_pfctl_get_status_counters(nvlist_get_nvlist(nvl, "scounters"),
479	    &status->scounters);
480
481	pf_nvuint_64_array(nvl, "pcounters", 2 * 2 * 2,
482	    (uint64_t *)status->pcounters, NULL);
483	pf_nvuint_64_array(nvl, "bcounters", 2 * 2,
484	    (uint64_t *)status->bcounters, NULL);
485
486	nvlist_destroy(nvl);
487
488	return (status);
489}
490
491static uint64_t
492_pfctl_status_counter(struct pfctl_status_counters *counters, uint64_t id)
493{
494	struct pfctl_status_counter *c;
495
496	TAILQ_FOREACH(c, counters, entry) {
497		if (c->id == id)
498			return (c->counter);
499	}
500
501	return (0);
502}
503
504uint64_t
505pfctl_status_counter(struct pfctl_status *status, int id)
506{
507	return (_pfctl_status_counter(&status->counters, id));
508}
509
510uint64_t
511pfctl_status_lcounter(struct pfctl_status *status, int id)
512{
513	return (_pfctl_status_counter(&status->lcounters, id));
514}
515
516uint64_t
517pfctl_status_fcounter(struct pfctl_status *status, int id)
518{
519	return (_pfctl_status_counter(&status->fcounters, id));
520}
521
522uint64_t
523pfctl_status_scounter(struct pfctl_status *status, int id)
524{
525	return (_pfctl_status_counter(&status->scounters, id));
526}
527
528void
529pfctl_free_status(struct pfctl_status *status)
530{
531	struct pfctl_status_counter *c, *tmp;
532
533	if (status == NULL)
534		return;
535
536	TAILQ_FOREACH_SAFE(c, &status->counters, entry, tmp) {
537		free(c->name);
538		free(c);
539	}
540	TAILQ_FOREACH_SAFE(c, &status->lcounters, entry, tmp) {
541		free(c->name);
542		free(c);
543	}
544	TAILQ_FOREACH_SAFE(c, &status->fcounters, entry, tmp) {
545		free(c->name);
546		free(c);
547	}
548	TAILQ_FOREACH_SAFE(c, &status->scounters, entry, tmp) {
549		free(c->name);
550		free(c);
551	}
552
553	free(status);
554}
555
556static void
557pfctl_nv_add_addr(nvlist_t *nvparent, const char *name,
558    const struct pf_addr *addr)
559{
560	nvlist_t *nvl = nvlist_create(0);
561
562	nvlist_add_binary(nvl, "addr", addr, sizeof(*addr));
563
564	nvlist_add_nvlist(nvparent, name, nvl);
565	nvlist_destroy(nvl);
566}
567
568static void
569pf_nvaddr_to_addr(const nvlist_t *nvl, struct pf_addr *addr)
570{
571	size_t len;
572	const void *data;
573
574	data = nvlist_get_binary(nvl, "addr", &len);
575	assert(len == sizeof(struct pf_addr));
576	memcpy(addr, data, len);
577}
578
579static void
580pfctl_nv_add_addr_wrap(nvlist_t *nvparent, const char *name,
581    const struct pf_addr_wrap *addr)
582{
583	nvlist_t *nvl = nvlist_create(0);
584
585	nvlist_add_number(nvl, "type", addr->type);
586	nvlist_add_number(nvl, "iflags", addr->iflags);
587	if (addr->type == PF_ADDR_DYNIFTL)
588		nvlist_add_string(nvl, "ifname", addr->v.ifname);
589	if (addr->type == PF_ADDR_TABLE)
590		nvlist_add_string(nvl, "tblname", addr->v.tblname);
591	pfctl_nv_add_addr(nvl, "addr", &addr->v.a.addr);
592	pfctl_nv_add_addr(nvl, "mask", &addr->v.a.mask);
593
594	nvlist_add_nvlist(nvparent, name, nvl);
595	nvlist_destroy(nvl);
596}
597
598static void
599pf_nvaddr_wrap_to_addr_wrap(const nvlist_t *nvl, struct pf_addr_wrap *addr)
600{
601	bzero(addr, sizeof(*addr));
602
603	addr->type = nvlist_get_number(nvl, "type");
604	addr->iflags = nvlist_get_number(nvl, "iflags");
605	if (addr->type == PF_ADDR_DYNIFTL) {
606		strlcpy(addr->v.ifname, nvlist_get_string(nvl, "ifname"),
607		    IFNAMSIZ);
608		addr->p.dyncnt = nvlist_get_number(nvl, "dyncnt");
609	}
610	if (addr->type == PF_ADDR_TABLE) {
611		strlcpy(addr->v.tblname, nvlist_get_string(nvl, "tblname"),
612		    PF_TABLE_NAME_SIZE);
613		addr->p.tblcnt = nvlist_get_number(nvl, "tblcnt");
614	}
615
616	pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"), &addr->v.a.addr);
617	pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "mask"), &addr->v.a.mask);
618}
619
620static void
621pfctl_nv_add_rule_addr(nvlist_t *nvparent, const char *name,
622    const struct pf_rule_addr *addr)
623{
624	uint64_t ports[2];
625	nvlist_t *nvl = nvlist_create(0);
626
627	pfctl_nv_add_addr_wrap(nvl, "addr", &addr->addr);
628	ports[0] = addr->port[0];
629	ports[1] = addr->port[1];
630	nvlist_add_number_array(nvl, "port", ports, 2);
631	nvlist_add_number(nvl, "neg", addr->neg);
632	nvlist_add_number(nvl, "port_op", addr->port_op);
633
634	nvlist_add_nvlist(nvparent, name, nvl);
635	nvlist_destroy(nvl);
636}
637
638static void
639pf_nvrule_addr_to_rule_addr(const nvlist_t *nvl, struct pf_rule_addr *addr)
640{
641	pf_nvaddr_wrap_to_addr_wrap(nvlist_get_nvlist(nvl, "addr"), &addr->addr);
642
643	pf_nvuint_16_array(nvl, "port", 2, addr->port, NULL);
644	addr->neg = nvlist_get_number(nvl, "neg");
645	addr->port_op = nvlist_get_number(nvl, "port_op");
646}
647
648static void
649pf_nvmape_to_mape(const nvlist_t *nvl, struct pf_mape_portset *mape)
650{
651	mape->offset = nvlist_get_number(nvl, "offset");
652	mape->psidlen = nvlist_get_number(nvl, "psidlen");
653	mape->psid = nvlist_get_number(nvl, "psid");
654}
655
656static void
657pf_nvpool_to_pool(const nvlist_t *nvl, struct pfctl_pool *pool)
658{
659	size_t len;
660	const void *data;
661
662	data = nvlist_get_binary(nvl, "key", &len);
663	assert(len == sizeof(pool->key));
664	memcpy(&pool->key, data, len);
665
666	pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "counter"), &pool->counter);
667
668	pool->tblidx = nvlist_get_number(nvl, "tblidx");
669	pf_nvuint_16_array(nvl, "proxy_port", 2, pool->proxy_port, NULL);
670	pool->opts = nvlist_get_number(nvl, "opts");
671
672	if (nvlist_exists_nvlist(nvl, "mape"))
673		pf_nvmape_to_mape(nvlist_get_nvlist(nvl, "mape"), &pool->mape);
674}
675
676static void
677pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid)
678{
679	pf_nvuint_32_array(nvl, "uid", 2, uid->uid, NULL);
680	uid->op = nvlist_get_number(nvl, "op");
681}
682
683static void
684pf_nvdivert_to_divert(const nvlist_t *nvl, struct pfctl_rule *rule)
685{
686	pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"), &rule->divert.addr);
687	rule->divert.port = nvlist_get_number(nvl, "port");
688}
689
690static void
691pf_nvrule_to_rule(const nvlist_t *nvl, struct pfctl_rule *rule)
692{
693	const uint64_t *skip;
694	const char *const *labels;
695	size_t skipcount, labelcount;
696
697	rule->nr = nvlist_get_number(nvl, "nr");
698
699	pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "src"), &rule->src);
700	pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "dst"), &rule->dst);
701
702	skip = nvlist_get_number_array(nvl, "skip", &skipcount);
703	assert(skip);
704	assert(skipcount == PF_SKIP_COUNT);
705	for (int i = 0; i < PF_SKIP_COUNT; i++)
706		rule->skip[i].nr = skip[i];
707
708	labels = nvlist_get_string_array(nvl, "labels", &labelcount);
709	assert(labelcount <= PF_RULE_MAX_LABEL_COUNT);
710	for (size_t i = 0; i < labelcount; i++)
711		strlcpy(rule->label[i], labels[i], PF_RULE_LABEL_SIZE);
712	rule->ridentifier = nvlist_get_number(nvl, "ridentifier");
713	strlcpy(rule->ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ);
714	strlcpy(rule->qname, nvlist_get_string(nvl, "qname"), PF_QNAME_SIZE);
715	strlcpy(rule->pqname, nvlist_get_string(nvl, "pqname"), PF_QNAME_SIZE);
716	strlcpy(rule->tagname, nvlist_get_string(nvl, "tagname"),
717	    PF_TAG_NAME_SIZE);
718	strlcpy(rule->match_tagname, nvlist_get_string(nvl, "match_tagname"),
719	    PF_TAG_NAME_SIZE);
720
721	strlcpy(rule->overload_tblname, nvlist_get_string(nvl, "overload_tblname"),
722	    PF_TABLE_NAME_SIZE);
723
724	pf_nvpool_to_pool(nvlist_get_nvlist(nvl, "rpool"), &rule->rpool);
725
726	rule->evaluations = nvlist_get_number(nvl, "evaluations");
727	pf_nvuint_64_array(nvl, "packets", 2, rule->packets, NULL);
728	pf_nvuint_64_array(nvl, "bytes", 2, rule->bytes, NULL);
729
730	if (nvlist_exists_number(nvl, "timestamp")) {
731		rule->last_active_timestamp = nvlist_get_number(nvl, "timestamp");
732	}
733
734	rule->os_fingerprint = nvlist_get_number(nvl, "os_fingerprint");
735
736	rule->rtableid = nvlist_get_number(nvl, "rtableid");
737	pf_nvuint_32_array(nvl, "timeout", PFTM_MAX, rule->timeout, NULL);
738	rule->max_states = nvlist_get_number(nvl, "max_states");
739	rule->max_src_nodes = nvlist_get_number(nvl, "max_src_nodes");
740	rule->max_src_states = nvlist_get_number(nvl, "max_src_states");
741	rule->max_src_conn = nvlist_get_number(nvl, "max_src_conn");
742	rule->max_src_conn_rate.limit =
743	    nvlist_get_number(nvl, "max_src_conn_rate.limit");
744	rule->max_src_conn_rate.seconds =
745	    nvlist_get_number(nvl, "max_src_conn_rate.seconds");
746	rule->qid = nvlist_get_number(nvl, "qid");
747	rule->pqid = nvlist_get_number(nvl, "pqid");
748	rule->dnpipe = nvlist_get_number(nvl, "dnpipe");
749	rule->dnrpipe = nvlist_get_number(nvl, "dnrpipe");
750	rule->free_flags = nvlist_get_number(nvl, "dnflags");
751	rule->prob = nvlist_get_number(nvl, "prob");
752	rule->cuid = nvlist_get_number(nvl, "cuid");
753	rule->cpid = nvlist_get_number(nvl, "cpid");
754
755	rule->return_icmp = nvlist_get_number(nvl, "return_icmp");
756	rule->return_icmp6 = nvlist_get_number(nvl, "return_icmp6");
757	rule->max_mss = nvlist_get_number(nvl, "max_mss");
758	rule->scrub_flags = nvlist_get_number(nvl, "scrub_flags");
759
760	pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "uid"), &rule->uid);
761	pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "gid"),
762	    (struct pf_rule_uid *)&rule->gid);
763
764	rule->rule_flag = nvlist_get_number(nvl, "rule_flag");
765	rule->action = nvlist_get_number(nvl, "action");
766	rule->direction = nvlist_get_number(nvl, "direction");
767	rule->log = nvlist_get_number(nvl, "log");
768	rule->logif = nvlist_get_number(nvl, "logif");
769	rule->quick = nvlist_get_number(nvl, "quick");
770	rule->ifnot = nvlist_get_number(nvl, "ifnot");
771	rule->match_tag_not = nvlist_get_number(nvl, "match_tag_not");
772	rule->natpass = nvlist_get_number(nvl, "natpass");
773
774	rule->keep_state = nvlist_get_number(nvl, "keep_state");
775	rule->af = nvlist_get_number(nvl, "af");
776	rule->proto = nvlist_get_number(nvl, "proto");
777	rule->type = nvlist_get_number(nvl, "type");
778	rule->code = nvlist_get_number(nvl, "code");
779	rule->flags = nvlist_get_number(nvl, "flags");
780	rule->flagset = nvlist_get_number(nvl, "flagset");
781	rule->min_ttl = nvlist_get_number(nvl, "min_ttl");
782	rule->allow_opts = nvlist_get_number(nvl, "allow_opts");
783	rule->rt = nvlist_get_number(nvl, "rt");
784	rule->return_ttl  = nvlist_get_number(nvl, "return_ttl");
785	rule->tos = nvlist_get_number(nvl, "tos");
786	rule->set_tos = nvlist_get_number(nvl, "set_tos");
787	rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative");
788	rule->anchor_wildcard = nvlist_get_number(nvl, "anchor_wildcard");
789
790	rule->flush = nvlist_get_number(nvl, "flush");
791	rule->prio = nvlist_get_number(nvl, "prio");
792	pf_nvuint_8_array(nvl, "set_prio", 2, rule->set_prio, NULL);
793
794	pf_nvdivert_to_divert(nvlist_get_nvlist(nvl, "divert"), rule);
795
796	rule->states_cur = nvlist_get_number(nvl, "states_cur");
797	rule->states_tot = nvlist_get_number(nvl, "states_tot");
798	rule->src_nodes = nvlist_get_number(nvl, "src_nodes");
799}
800
801static void
802pfctl_nveth_addr_to_eth_addr(const nvlist_t *nvl, struct pfctl_eth_addr *addr)
803{
804	static const u_int8_t EMPTY_MAC[ETHER_ADDR_LEN] = { 0 };
805	size_t len;
806	const void *data;
807
808	data = nvlist_get_binary(nvl, "addr", &len);
809	assert(len == sizeof(addr->addr));
810	memcpy(addr->addr, data, sizeof(addr->addr));
811
812	data = nvlist_get_binary(nvl, "mask", &len);
813	assert(len == sizeof(addr->mask));
814	memcpy(addr->mask, data, sizeof(addr->mask));
815
816	addr->neg = nvlist_get_bool(nvl, "neg");
817
818	/* To make checks for 'is this address set?' easier. */
819	addr->isset = memcmp(addr->addr, EMPTY_MAC, ETHER_ADDR_LEN) != 0;
820}
821
822static nvlist_t *
823pfctl_eth_addr_to_nveth_addr(const struct pfctl_eth_addr *addr)
824{
825	nvlist_t *nvl;
826
827	nvl = nvlist_create(0);
828	if (nvl == NULL)
829		return (NULL);
830
831	nvlist_add_bool(nvl, "neg", addr->neg);
832	nvlist_add_binary(nvl, "addr", &addr->addr, ETHER_ADDR_LEN);
833	nvlist_add_binary(nvl, "mask", &addr->mask, ETHER_ADDR_LEN);
834
835	return (nvl);
836}
837
838static void
839pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
840{
841	const char *const *labels;
842	size_t labelcount, i;
843
844	rule->nr = nvlist_get_number(nvl, "nr");
845	rule->quick = nvlist_get_bool(nvl, "quick");
846	strlcpy(rule->ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ);
847	rule->ifnot = nvlist_get_bool(nvl, "ifnot");
848	rule->direction = nvlist_get_number(nvl, "direction");
849	rule->proto = nvlist_get_number(nvl, "proto");
850	strlcpy(rule->match_tagname, nvlist_get_string(nvl, "match_tagname"),
851	    PF_TAG_NAME_SIZE);
852	rule->match_tag = nvlist_get_number(nvl, "match_tag");
853	rule->match_tag_not = nvlist_get_bool(nvl, "match_tag_not");
854
855	labels = nvlist_get_string_array(nvl, "labels", &labelcount);
856	assert(labelcount <= PF_RULE_MAX_LABEL_COUNT);
857	for (i = 0; i < labelcount; i++)
858		strlcpy(rule->label[i], labels[i], PF_RULE_LABEL_SIZE);
859	rule->ridentifier = nvlist_get_number(nvl, "ridentifier");
860
861	pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "src"),
862	    &rule->src);
863	pfctl_nveth_addr_to_eth_addr(nvlist_get_nvlist(nvl, "dst"),
864	    &rule->dst);
865
866	pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "ipsrc"),
867	    &rule->ipsrc);
868	pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "ipdst"),
869	    &rule->ipdst);
870
871	rule->evaluations = nvlist_get_number(nvl, "evaluations");
872	rule->packets[0] = nvlist_get_number(nvl, "packets-in");
873	rule->packets[1] = nvlist_get_number(nvl, "packets-out");
874	rule->bytes[0] = nvlist_get_number(nvl, "bytes-in");
875	rule->bytes[1] = nvlist_get_number(nvl, "bytes-out");
876
877	if (nvlist_exists_number(nvl, "timestamp")) {
878		rule->last_active_timestamp = nvlist_get_number(nvl, "timestamp");
879	}
880
881	strlcpy(rule->qname, nvlist_get_string(nvl, "qname"), PF_QNAME_SIZE);
882	strlcpy(rule->tagname, nvlist_get_string(nvl, "tagname"),
883	    PF_TAG_NAME_SIZE);
884
885	rule->dnpipe = nvlist_get_number(nvl, "dnpipe");
886	rule->dnflags = nvlist_get_number(nvl, "dnflags");
887
888	rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative");
889	rule->anchor_wildcard = nvlist_get_number(nvl, "anchor_wildcard");
890
891	strlcpy(rule->bridge_to, nvlist_get_string(nvl, "bridge_to"),
892	    IFNAMSIZ);
893
894	rule->action = nvlist_get_number(nvl, "action");
895}
896
897int
898pfctl_get_eth_rulesets_info(int dev, struct pfctl_eth_rulesets_info *ri,
899    const char *path)
900{
901	nvlist_t *nvl;
902	int ret;
903
904	bzero(ri, sizeof(*ri));
905
906	nvl = nvlist_create(0);
907	nvlist_add_string(nvl, "path", path);
908
909	if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULESETS, 256, &nvl)) != 0)
910		goto out;
911
912	ri->nr = nvlist_get_number(nvl, "nr");
913
914out:
915	nvlist_destroy(nvl);
916	return (ret);
917}
918
919int
920pfctl_get_eth_ruleset(int dev, const char *path, int nr,
921    struct pfctl_eth_ruleset_info *ri)
922{
923	nvlist_t *nvl;
924	int ret;
925
926	bzero(ri, sizeof(*ri));
927
928	nvl = nvlist_create(0);
929	nvlist_add_string(nvl, "path", path);
930	nvlist_add_number(nvl, "nr", nr);
931
932	if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULESET, 1024, &nvl)) != 0)
933		goto out;
934
935	ri->nr = nvlist_get_number(nvl, "nr");
936	strlcpy(ri->path, nvlist_get_string(nvl, "path"), MAXPATHLEN);
937	strlcpy(ri->name, nvlist_get_string(nvl, "name"),
938	    PF_ANCHOR_NAME_SIZE);
939
940out:
941	nvlist_destroy(nvl);
942	return (ret);
943}
944
945int
946pfctl_get_eth_rules_info(int dev, struct pfctl_eth_rules_info *rules,
947    const char *path)
948{
949	nvlist_t *nvl;
950	int ret;
951
952	bzero(rules, sizeof(*rules));
953
954	nvl = nvlist_create(0);
955	nvlist_add_string(nvl, "anchor", path);
956
957	if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULES, 1024, &nvl)) != 0)
958		goto out;
959
960	rules->nr = nvlist_get_number(nvl, "nr");
961	rules->ticket = nvlist_get_number(nvl, "ticket");
962
963out:
964	nvlist_destroy(nvl);
965	return (ret);
966}
967
968int
969pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
970    const char *path, struct pfctl_eth_rule *rule, bool clear,
971    char *anchor_call)
972{
973	nvlist_t *nvl;
974	int ret;
975
976	nvl = nvlist_create(0);
977
978	nvlist_add_string(nvl, "anchor", path);
979	nvlist_add_number(nvl, "ticket", ticket);
980	nvlist_add_number(nvl, "nr", nr);
981	nvlist_add_bool(nvl, "clear", clear);
982
983	if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULE, 4096, &nvl)) != 0)
984		goto out;
985
986	pfctl_nveth_rule_to_eth_rule(nvl, rule);
987
988	if (anchor_call)
989		strlcpy(anchor_call, nvlist_get_string(nvl, "anchor_call"),
990		    MAXPATHLEN);
991
992out:
993	nvlist_destroy(nvl);
994	return (ret);
995}
996
997int
998pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor,
999    const char *anchor_call, uint32_t ticket)
1000{
1001	struct pfioc_nv nv;
1002	nvlist_t *nvl, *addr;
1003	void *packed;
1004	int error = 0;
1005	size_t labelcount, size;
1006
1007	nvl = nvlist_create(0);
1008
1009	nvlist_add_number(nvl, "ticket", ticket);
1010	nvlist_add_string(nvl, "anchor", anchor);
1011	nvlist_add_string(nvl, "anchor_call", anchor_call);
1012
1013	nvlist_add_number(nvl, "nr", r->nr);
1014	nvlist_add_bool(nvl, "quick", r->quick);
1015	nvlist_add_string(nvl, "ifname", r->ifname);
1016	nvlist_add_bool(nvl, "ifnot", r->ifnot);
1017	nvlist_add_number(nvl, "direction", r->direction);
1018	nvlist_add_number(nvl, "proto", r->proto);
1019	nvlist_add_string(nvl, "match_tagname", r->match_tagname);
1020	nvlist_add_bool(nvl, "match_tag_not", r->match_tag_not);
1021
1022	addr = pfctl_eth_addr_to_nveth_addr(&r->src);
1023	if (addr == NULL) {
1024		nvlist_destroy(nvl);
1025		return (ENOMEM);
1026	}
1027	nvlist_add_nvlist(nvl, "src", addr);
1028	nvlist_destroy(addr);
1029
1030	addr = pfctl_eth_addr_to_nveth_addr(&r->dst);
1031	if (addr == NULL) {
1032		nvlist_destroy(nvl);
1033		return (ENOMEM);
1034	}
1035	nvlist_add_nvlist(nvl, "dst", addr);
1036	nvlist_destroy(addr);
1037
1038	pfctl_nv_add_rule_addr(nvl, "ipsrc", &r->ipsrc);
1039	pfctl_nv_add_rule_addr(nvl, "ipdst", &r->ipdst);
1040
1041	labelcount = 0;
1042	while (labelcount < PF_RULE_MAX_LABEL_COUNT &&
1043	    r->label[labelcount][0] != 0) {
1044		nvlist_append_string_array(nvl, "labels",
1045		    r->label[labelcount]);
1046		labelcount++;
1047	}
1048	nvlist_add_number(nvl, "ridentifier", r->ridentifier);
1049
1050	nvlist_add_string(nvl, "qname", r->qname);
1051	nvlist_add_string(nvl, "tagname", r->tagname);
1052	nvlist_add_number(nvl, "dnpipe", r->dnpipe);
1053	nvlist_add_number(nvl, "dnflags", r->dnflags);
1054
1055	nvlist_add_string(nvl, "bridge_to", r->bridge_to);
1056
1057	nvlist_add_number(nvl, "action", r->action);
1058
1059	packed = nvlist_pack(nvl, &size);
1060	if (packed == NULL) {
1061		nvlist_destroy(nvl);
1062		return (ENOMEM);
1063	}
1064
1065	nv.len = size;
1066	nv.size = size;
1067	nv.data = packed;
1068
1069	if (ioctl(dev, DIOCADDETHRULE, &nv) != 0)
1070		error = errno;
1071
1072	free(packed);
1073	nvlist_destroy(nvl);
1074
1075	return (error);
1076}
1077
1078static void
1079snl_add_msg_attr_addr_wrap(struct snl_writer *nw, uint32_t type, const struct pf_addr_wrap *addr)
1080{
1081	int off;
1082
1083	off = snl_add_msg_attr_nested(nw, type);
1084
1085	snl_add_msg_attr_ip6(nw, PF_AT_ADDR, &addr->v.a.addr.v6);
1086	snl_add_msg_attr_ip6(nw, PF_AT_MASK, &addr->v.a.mask.v6);
1087
1088	if (addr->type == PF_ADDR_DYNIFTL)
1089		snl_add_msg_attr_string(nw, PF_AT_IFNAME, addr->v.ifname);
1090	if (addr->type == PF_ADDR_TABLE)
1091		snl_add_msg_attr_string(nw, PF_AT_TABLENAME, addr->v.tblname);
1092	snl_add_msg_attr_u8(nw, PF_AT_TYPE, addr->type);
1093	snl_add_msg_attr_u8(nw, PF_AT_IFLAGS, addr->iflags);
1094
1095	snl_end_attr_nested(nw, off);
1096}
1097
1098static void
1099snl_add_msg_attr_rule_addr(struct snl_writer *nw, uint32_t type, const struct pf_rule_addr *addr)
1100{
1101	int off;
1102
1103	off = snl_add_msg_attr_nested(nw, type);
1104
1105	snl_add_msg_attr_addr_wrap(nw, PF_RAT_ADDR, &addr->addr);
1106	snl_add_msg_attr_u16(nw, PF_RAT_SRC_PORT, addr->port[0]);
1107	snl_add_msg_attr_u16(nw, PF_RAT_DST_PORT, addr->port[1]);
1108	snl_add_msg_attr_u8(nw, PF_RAT_NEG, addr->neg);
1109	snl_add_msg_attr_u8(nw, PF_RAT_OP, addr->port_op);
1110
1111	snl_end_attr_nested(nw, off);
1112}
1113
1114static void
1115snl_add_msg_attr_rule_labels(struct snl_writer *nw, uint32_t type, const char labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE])
1116{
1117	int off, i = 0;
1118
1119	off = snl_add_msg_attr_nested(nw, type);
1120
1121	while (i < PF_RULE_MAX_LABEL_COUNT &&
1122	    labels[i][0] != 0) {
1123		snl_add_msg_attr_string(nw, PF_LT_LABEL, labels[i]);
1124		i++;
1125	}
1126
1127	snl_end_attr_nested(nw, off);
1128}
1129
1130static void
1131snl_add_msg_attr_mape(struct snl_writer *nw, uint32_t type, const struct pf_mape_portset *me)
1132{
1133	int off;
1134
1135	off = snl_add_msg_attr_nested(nw, type);
1136
1137	snl_add_msg_attr_u8(nw, PF_MET_OFFSET, me->offset);
1138	snl_add_msg_attr_u8(nw, PF_MET_PSID_LEN, me->psidlen);
1139	snl_add_msg_attr_u16(nw, PF_MET_PSID, me->psid);
1140
1141	snl_end_attr_nested(nw, off);
1142}
1143
1144static void
1145snl_add_msg_attr_rpool(struct snl_writer *nw, uint32_t type, const struct pfctl_pool *pool)
1146{
1147	int off;
1148
1149	off = snl_add_msg_attr_nested(nw, type);
1150
1151	snl_add_msg_attr(nw, PF_PT_KEY, sizeof(pool->key), &pool->key);
1152	snl_add_msg_attr_ip6(nw, PF_PT_COUNTER, &pool->counter.v6);
1153	snl_add_msg_attr_u32(nw, PF_PT_TBLIDX, pool->tblidx);
1154	snl_add_msg_attr_u16(nw, PF_PT_PROXY_SRC_PORT, pool->proxy_port[0]);
1155	snl_add_msg_attr_u16(nw, PF_PT_PROXY_DST_PORT, pool->proxy_port[1]);
1156	snl_add_msg_attr_u8(nw, PF_PT_OPTS, pool->opts);
1157	snl_add_msg_attr_mape(nw, PF_PT_MAPE, &pool->mape);
1158
1159	snl_end_attr_nested(nw, off);
1160}
1161
1162static void
1163snl_add_msg_attr_timeouts(struct snl_writer *nw, uint32_t type, const uint32_t *timeouts)
1164{
1165	int off;
1166
1167	off = snl_add_msg_attr_nested(nw, type);
1168
1169	for (int i = 0; i < PFTM_MAX; i++)
1170		snl_add_msg_attr_u32(nw, PF_TT_TIMEOUT, timeouts[i]);
1171
1172	snl_end_attr_nested(nw, off);
1173}
1174
1175static void
1176snl_add_msg_attr_uid(struct snl_writer *nw, uint32_t type, const struct pf_rule_uid *uid)
1177{
1178	int off;
1179
1180	off = snl_add_msg_attr_nested(nw, type);
1181
1182	snl_add_msg_attr_u32(nw, PF_RUT_UID_LOW, uid->uid[0]);
1183	snl_add_msg_attr_u32(nw, PF_RUT_UID_HIGH, uid->uid[1]);
1184	snl_add_msg_attr_u8(nw, PF_RUT_OP, uid->op);
1185
1186	snl_end_attr_nested(nw, off);
1187}
1188
1189static void
1190snl_add_msg_attr_pf_rule(struct snl_writer *nw, uint32_t type, const struct pfctl_rule *r)
1191{
1192	int off;
1193
1194	off = snl_add_msg_attr_nested(nw, type);
1195
1196	snl_add_msg_attr_rule_addr(nw, PF_RT_SRC, &r->src);
1197	snl_add_msg_attr_rule_addr(nw, PF_RT_DST, &r->dst);
1198	snl_add_msg_attr_rule_labels(nw, PF_RT_LABELS, r->label);
1199	snl_add_msg_attr_u32(nw, PF_RT_RIDENTIFIER, r->ridentifier);
1200	snl_add_msg_attr_string(nw, PF_RT_IFNAME, r->ifname);
1201	snl_add_msg_attr_string(nw, PF_RT_QNAME, r->qname);
1202	snl_add_msg_attr_string(nw, PF_RT_PQNAME, r->pqname);
1203	snl_add_msg_attr_string(nw, PF_RT_TAGNAME, r->tagname);
1204	snl_add_msg_attr_string(nw, PF_RT_MATCH_TAGNAME, r->match_tagname);
1205	snl_add_msg_attr_string(nw, PF_RT_OVERLOAD_TBLNAME, r->overload_tblname);
1206	snl_add_msg_attr_rpool(nw, PF_RT_RPOOL, &r->rpool);
1207	snl_add_msg_attr_u32(nw, PF_RT_OS_FINGERPRINT, r->os_fingerprint);
1208	snl_add_msg_attr_u32(nw, PF_RT_RTABLEID, r->rtableid);
1209	snl_add_msg_attr_timeouts(nw, PF_RT_TIMEOUT, r->timeout);
1210	snl_add_msg_attr_u32(nw, PF_RT_MAX_STATES, r->max_states);
1211	snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_NODES, r->max_src_nodes);
1212	snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_STATES, r->max_src_states);
1213	snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_CONN_RATE_LIMIT, r->max_src_conn_rate.limit);
1214	snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_CONN_RATE_SECS, r->max_src_conn_rate.seconds);
1215
1216	snl_add_msg_attr_u16(nw, PF_RT_DNPIPE, r->dnpipe);
1217	snl_add_msg_attr_u16(nw, PF_RT_DNRPIPE, r->dnrpipe);
1218	snl_add_msg_attr_u32(nw, PF_RT_DNFLAGS, r->free_flags);
1219
1220	snl_add_msg_attr_u32(nw, PF_RT_NR, r->nr);
1221	snl_add_msg_attr_u32(nw, PF_RT_PROB, r->prob);
1222	snl_add_msg_attr_u32(nw, PF_RT_CUID, r->cuid);
1223	snl_add_msg_attr_u32(nw, PF_RT_CPID, r->cpid);
1224
1225	snl_add_msg_attr_u16(nw, PF_RT_RETURN_ICMP, r->return_icmp);
1226	snl_add_msg_attr_u16(nw, PF_RT_RETURN_ICMP6, r->return_icmp6);
1227	snl_add_msg_attr_u16(nw, PF_RT_MAX_MSS, r->max_mss);
1228	snl_add_msg_attr_u16(nw, PF_RT_SCRUB_FLAGS, r->scrub_flags);
1229
1230	snl_add_msg_attr_uid(nw, PF_RT_UID, &r->uid);
1231	snl_add_msg_attr_uid(nw, PF_RT_GID, (const struct pf_rule_uid *)&r->gid);
1232
1233	snl_add_msg_attr_u32(nw, PF_RT_RULE_FLAG, r->rule_flag);
1234	snl_add_msg_attr_u8(nw, PF_RT_ACTION, r->action);
1235	snl_add_msg_attr_u8(nw, PF_RT_DIRECTION, r->direction);
1236	snl_add_msg_attr_u8(nw, PF_RT_LOG, r->log);
1237	snl_add_msg_attr_u8(nw, PF_RT_LOGIF, r->logif);
1238	snl_add_msg_attr_u8(nw, PF_RT_QUICK, r->quick);
1239	snl_add_msg_attr_u8(nw, PF_RT_IF_NOT, r->ifnot);
1240	snl_add_msg_attr_u8(nw, PF_RT_MATCH_TAG_NOT, r->match_tag_not);
1241	snl_add_msg_attr_u8(nw, PF_RT_NATPASS, r->natpass);
1242	snl_add_msg_attr_u8(nw, PF_RT_KEEP_STATE, r->keep_state);
1243	snl_add_msg_attr_u8(nw, PF_RT_AF, r->af);
1244	snl_add_msg_attr_u8(nw, PF_RT_PROTO, r->proto);
1245	snl_add_msg_attr_u8(nw, PF_RT_TYPE, r->type);
1246	snl_add_msg_attr_u8(nw, PF_RT_CODE, r->code);
1247	snl_add_msg_attr_u8(nw, PF_RT_FLAGS, r->flags);
1248	snl_add_msg_attr_u8(nw, PF_RT_FLAGSET, r->flagset);
1249	snl_add_msg_attr_u8(nw, PF_RT_MIN_TTL, r->min_ttl);
1250	snl_add_msg_attr_u8(nw, PF_RT_ALLOW_OPTS, r->allow_opts);
1251	snl_add_msg_attr_u8(nw, PF_RT_RT, r->rt);
1252	snl_add_msg_attr_u8(nw, PF_RT_RETURN_TTL, r->return_ttl);
1253	snl_add_msg_attr_u8(nw, PF_RT_TOS, r->tos);
1254	snl_add_msg_attr_u8(nw, PF_RT_SET_TOS, r->set_tos);
1255
1256	snl_add_msg_attr_u8(nw, PF_RT_ANCHOR_RELATIVE, r->anchor_relative);
1257	snl_add_msg_attr_u8(nw, PF_RT_ANCHOR_WILDCARD, r->anchor_wildcard);
1258	snl_add_msg_attr_u8(nw, PF_RT_FLUSH, r->flush);
1259	snl_add_msg_attr_u8(nw, PF_RT_PRIO, r->prio);
1260	snl_add_msg_attr_u8(nw, PF_RT_SET_PRIO, r->set_prio[0]);
1261	snl_add_msg_attr_u8(nw, PF_RT_SET_PRIO_REPLY, r->set_prio[1]);
1262
1263	snl_add_msg_attr_ip6(nw, PF_RT_DIVERT_ADDRESS, &r->divert.addr.v6);
1264	snl_add_msg_attr_u16(nw, PF_RT_DIVERT_PORT, r->divert.port);
1265
1266	snl_end_attr_nested(nw, off);
1267}
1268
1269int
1270pfctl_add_rule(int dev __unused, const struct pfctl_rule *r, const char *anchor,
1271    const char *anchor_call, uint32_t ticket, uint32_t pool_ticket)
1272{
1273	struct pfctl_handle *h;
1274	int ret;
1275
1276	h = pfctl_open(PF_DEVICE);
1277	if (h == NULL)
1278		return (ENODEV);
1279
1280	ret = pfctl_add_rule_h(h, r, anchor, anchor_call, ticket, pool_ticket);
1281
1282	pfctl_close(h);
1283
1284	return (ret);
1285}
1286
1287int
1288pfctl_add_rule_h(struct pfctl_handle *h, const struct pfctl_rule *r,
1289	    const char *anchor, const char *anchor_call, uint32_t ticket,
1290	    uint32_t pool_ticket)
1291{
1292	struct snl_writer nw;
1293	struct snl_errmsg_data e = {};
1294	struct nlmsghdr *hdr;
1295	uint32_t seq_id;
1296	int family_id;
1297
1298	family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
1299	if (family_id == 0)
1300		return (ENOTSUP);
1301
1302	snl_init_writer(&h->ss, &nw);
1303	hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_ADDRULE);
1304	hdr->nlmsg_flags |= NLM_F_DUMP;
1305	snl_add_msg_attr_u32(&nw, PF_ART_TICKET, ticket);
1306	snl_add_msg_attr_u32(&nw, PF_ART_POOL_TICKET, pool_ticket);
1307	snl_add_msg_attr_string(&nw, PF_ART_ANCHOR, anchor);
1308	snl_add_msg_attr_string(&nw, PF_ART_ANCHOR_CALL, anchor_call);
1309
1310	snl_add_msg_attr_pf_rule(&nw, PF_ART_RULE, r);
1311
1312	if ((hdr = snl_finalize_msg(&nw)) == NULL)
1313		return (ENXIO);
1314
1315	seq_id = hdr->nlmsg_seq;
1316
1317	if (! snl_send_message(&h->ss, hdr))
1318		return (ENXIO);
1319
1320	while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
1321	}
1322
1323	return (e.error);
1324}
1325
1326#define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1327#define	_OUT(_field)	offsetof(struct pfctl_rules_info, _field)
1328static struct snl_attr_parser ap_getrules[] = {
1329	{ .type = PF_GR_NR, .off = _OUT(nr), .cb = snl_attr_get_uint32 },
1330	{ .type = PF_GR_TICKET, .off = _OUT(ticket), .cb = snl_attr_get_uint32 },
1331};
1332static struct snl_field_parser fp_getrules[] = {
1333};
1334#undef _IN
1335#undef _OUT
1336SNL_DECLARE_PARSER(getrules_parser, struct genlmsghdr, fp_getrules, ap_getrules);
1337
1338int
1339pfctl_get_rules_info_h(struct pfctl_handle *h, struct pfctl_rules_info *rules, uint32_t ruleset,
1340    const char *path)
1341{
1342	struct snl_errmsg_data e = {};
1343	struct nlmsghdr *hdr;
1344	struct snl_writer nw;
1345	uint32_t seq_id;
1346	int family_id;
1347
1348	family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
1349	if (family_id == 0)
1350		return (ENOTSUP);
1351
1352	snl_init_writer(&h->ss, &nw);
1353	hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETRULES);
1354	hdr->nlmsg_flags |= NLM_F_DUMP;
1355
1356	snl_add_msg_attr_string(&nw, PF_GR_ANCHOR, path);
1357	snl_add_msg_attr_u8(&nw, PF_GR_ACTION, ruleset);
1358
1359	hdr = snl_finalize_msg(&nw);
1360	if (hdr == NULL)
1361		return (ENOMEM);
1362
1363	seq_id = hdr->nlmsg_seq;
1364	if (! snl_send_message(&h->ss, hdr))
1365		return (ENXIO);
1366
1367	while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
1368		if (! snl_parse_nlmsg(&h->ss, hdr, &getrules_parser, rules))
1369			continue;
1370	}
1371
1372	return (e.error);
1373}
1374
1375int
1376pfctl_get_rules_info(int dev __unused, struct pfctl_rules_info *rules, uint32_t ruleset,
1377    const char *path)
1378{
1379	struct pfctl_handle *h;
1380	int error;
1381
1382	h = pfctl_open(PF_DEVICE);
1383	if (h == NULL)
1384		return (ENOTSUP);
1385	error = pfctl_get_rules_info_h(h, rules, ruleset, path);
1386	pfctl_close(h);
1387
1388	return (error);
1389}
1390
1391int
1392pfctl_get_rule_h(struct pfctl_handle *h, uint32_t nr, uint32_t ticket, const char *anchor,
1393    uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call)
1394{
1395	return (pfctl_get_clear_rule_h(h, nr, ticket, anchor, ruleset, rule,
1396	    anchor_call, false));
1397}
1398
1399int
1400pfctl_get_rule(int dev, uint32_t nr, uint32_t ticket, const char *anchor,
1401    uint32_t ruleset, struct pfctl_rule *rule, char *anchor_call)
1402{
1403	return (pfctl_get_clear_rule(dev, nr, ticket, anchor, ruleset, rule,
1404	    anchor_call, false));
1405}
1406
1407#define _OUT(_field)	offsetof(struct pf_addr_wrap, _field)
1408static const struct snl_attr_parser ap_addr_wrap[] = {
1409	{ .type = PF_AT_ADDR, .off = _OUT(v.a.addr), .cb = snl_attr_get_in6_addr },
1410	{ .type = PF_AT_MASK, .off = _OUT(v.a.mask), .cb = snl_attr_get_in6_addr },
1411	{ .type = PF_AT_IFNAME, .off = _OUT(v.ifname), .arg = (void *)IFNAMSIZ,.cb = snl_attr_copy_string },
1412	{ .type = PF_AT_TABLENAME, .off = _OUT(v.tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = snl_attr_copy_string },
1413	{ .type = PF_AT_TYPE, .off = _OUT(type), .cb = snl_attr_get_uint8 },
1414	{ .type = PF_AT_IFLAGS, .off = _OUT(iflags), .cb = snl_attr_get_uint8 },
1415	{ .type = PF_AT_TBLCNT, .off = _OUT(p.tblcnt), .cb = snl_attr_get_uint32 },
1416	{ .type = PF_AT_DYNCNT, .off = _OUT(p.dyncnt), .cb = snl_attr_get_uint32 },
1417};
1418SNL_DECLARE_ATTR_PARSER(addr_wrap_parser, ap_addr_wrap);
1419#undef _OUT
1420
1421#define _OUT(_field)	offsetof(struct pf_rule_addr, _field)
1422static struct snl_attr_parser ap_rule_addr[] = {
1423	{ .type = PF_RAT_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = snl_attr_get_nested },
1424	{ .type = PF_RAT_SRC_PORT, .off = _OUT(port[0]), .cb = snl_attr_get_uint16 },
1425	{ .type = PF_RAT_DST_PORT, .off = _OUT(port[1]), .cb = snl_attr_get_uint16 },
1426	{ .type = PF_RAT_NEG, .off = _OUT(neg), .cb = snl_attr_get_uint8 },
1427	{ .type = PF_RAT_OP, .off = _OUT(port_op), .cb = snl_attr_get_uint8 },
1428};
1429#undef _OUT
1430SNL_DECLARE_ATTR_PARSER(rule_addr_parser, ap_rule_addr);
1431
1432struct snl_parsed_labels
1433{
1434	char		labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
1435	uint32_t	i;
1436};
1437
1438static bool
1439snl_attr_get_pf_rule_labels(struct snl_state *ss, struct nlattr *nla,
1440    const void *arg __unused, void *target)
1441{
1442	struct snl_parsed_labels *l = (struct snl_parsed_labels *)target;
1443	bool ret;
1444
1445	if (l->i >= PF_RULE_MAX_LABEL_COUNT)
1446		return (E2BIG);
1447
1448	ret = snl_attr_copy_string(ss, nla, (void *)PF_RULE_LABEL_SIZE,
1449	    l->labels[l->i]);
1450	if (ret)
1451		l->i++;
1452
1453	return (ret);
1454}
1455
1456#define _OUT(_field)	offsetof(struct nl_parsed_labels, _field)
1457static const struct snl_attr_parser ap_labels[] = {
1458	{ .type = PF_LT_LABEL, .off = 0, .cb = snl_attr_get_pf_rule_labels },
1459};
1460SNL_DECLARE_ATTR_PARSER(rule_labels_parser, ap_labels);
1461#undef _OUT
1462
1463static bool
1464snl_attr_get_nested_pf_rule_labels(struct snl_state *ss, struct nlattr *nla,
1465    const void *arg __unused, void *target)
1466{
1467	struct snl_parsed_labels parsed_labels = { };
1468	bool error;
1469
1470	/* Assumes target points to the beginning of the structure */
1471	error = snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), &rule_labels_parser, &parsed_labels);
1472	if (! error)
1473		return (error);
1474
1475	memcpy(target, parsed_labels.labels, sizeof(parsed_labels.labels));
1476
1477	return (true);
1478}
1479
1480#define _OUT(_field)	offsetof(struct pf_mape_portset, _field)
1481static const struct snl_attr_parser ap_mape_portset[] = {
1482	{ .type = PF_MET_OFFSET, .off = _OUT(offset), .cb = snl_attr_get_uint8 },
1483	{ .type = PF_MET_PSID_LEN, .off = _OUT(psidlen), .cb = snl_attr_get_uint8 },
1484	{. type = PF_MET_PSID, .off = _OUT(psid), .cb = snl_attr_get_uint16 },
1485};
1486SNL_DECLARE_ATTR_PARSER(mape_portset_parser, ap_mape_portset);
1487#undef _OUT
1488
1489#define _OUT(_field)	offsetof(struct pfctl_pool, _field)
1490static const struct snl_attr_parser ap_pool[] = {
1491	{ .type = PF_PT_KEY, .off = _OUT(key), .arg = (void *)sizeof(struct pf_poolhashkey), .cb = snl_attr_get_bytes },
1492	{ .type = PF_PT_COUNTER, .off = _OUT(counter), .cb = snl_attr_get_in6_addr },
1493	{ .type = PF_PT_TBLIDX, .off = _OUT(tblidx), .cb = snl_attr_get_uint32 },
1494	{ .type = PF_PT_PROXY_SRC_PORT, .off = _OUT(proxy_port[0]), .cb = snl_attr_get_uint16 },
1495	{ .type = PF_PT_PROXY_DST_PORT, .off = _OUT(proxy_port[1]), .cb = snl_attr_get_uint16 },
1496	{ .type = PF_PT_OPTS, .off = _OUT(opts), .cb = snl_attr_get_uint8 },
1497	{ .type = PF_PT_MAPE, .off = _OUT(mape), .arg = &mape_portset_parser, .cb = snl_attr_get_nested },
1498};
1499SNL_DECLARE_ATTR_PARSER(pool_parser, ap_pool);
1500#undef _OUT
1501
1502struct nl_parsed_timeouts
1503{
1504	uint32_t	timeouts[PFTM_MAX];
1505	uint32_t	i;
1506};
1507
1508static bool
1509snl_attr_get_pf_timeout(struct snl_state *ss, struct nlattr *nla,
1510    const void *arg __unused, void *target)
1511{
1512	struct nl_parsed_timeouts *t = (struct nl_parsed_timeouts *)target;
1513	bool ret;
1514
1515	if (t->i >= PFTM_MAX)
1516		return (E2BIG);
1517
1518	ret = snl_attr_get_uint32(ss, nla, NULL, &t->timeouts[t->i]);
1519	if (ret)
1520		t->i++;
1521
1522	return (ret);
1523}
1524
1525#define _OUT(_field)	offsetof(struct nl_parsed_timeout, _field)
1526static const struct snl_attr_parser ap_timeouts[] = {
1527	{ .type = PF_TT_TIMEOUT, .off = 0, .cb = snl_attr_get_pf_timeout },
1528};
1529SNL_DECLARE_ATTR_PARSER(timeout_parser, ap_timeouts);
1530#undef _OUT
1531
1532static bool
1533snl_attr_get_nested_timeouts(struct snl_state *ss, struct nlattr *nla,
1534    const void *arg __unused, void *target)
1535{
1536	struct nl_parsed_timeouts parsed_timeouts = { };
1537	bool error;
1538
1539	/* Assumes target points to the beginning of the structure */
1540	error = snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), &timeout_parser, &parsed_timeouts);
1541	if (! error)
1542		return (error);
1543
1544	memcpy(target, parsed_timeouts.timeouts, sizeof(parsed_timeouts.timeouts));
1545
1546	return (true);
1547}
1548
1549#define _OUT(_field)	offsetof(struct pf_rule_uid, _field)
1550static const struct snl_attr_parser ap_rule_uid[] = {
1551	{ .type = PF_RUT_UID_LOW, .off = _OUT(uid[0]), .cb = snl_attr_get_uint32 },
1552	{ .type = PF_RUT_UID_HIGH, .off = _OUT(uid[1]), .cb = snl_attr_get_uint32 },
1553	{ .type = PF_RUT_OP, .off = _OUT(op), .cb = snl_attr_get_uint8 },
1554};
1555SNL_DECLARE_ATTR_PARSER(rule_uid_parser, ap_rule_uid);
1556#undef _OUT
1557
1558struct pfctl_nl_get_rule {
1559	struct pfctl_rule r;
1560	char anchor_call[MAXPATHLEN];
1561};
1562#define	_OUT(_field)	offsetof(struct pfctl_nl_get_rule, _field)
1563static struct snl_attr_parser ap_getrule[] = {
1564	{ .type = PF_RT_SRC, .off = _OUT(r.src), .arg = &rule_addr_parser,.cb = snl_attr_get_nested },
1565	{ .type = PF_RT_DST, .off = _OUT(r.dst), .arg = &rule_addr_parser,.cb = snl_attr_get_nested },
1566	{ .type = PF_RT_RIDENTIFIER, .off = _OUT(r.ridentifier), .cb = snl_attr_get_uint32 },
1567	{ .type = PF_RT_LABELS, .off = _OUT(r.label), .arg = &rule_labels_parser,.cb = snl_attr_get_nested_pf_rule_labels },
1568	{ .type = PF_RT_IFNAME, .off = _OUT(r.ifname), .arg = (void *)IFNAMSIZ, .cb = snl_attr_copy_string },
1569	{ .type = PF_RT_QNAME, .off = _OUT(r.qname), .arg = (void *)PF_QNAME_SIZE, .cb = snl_attr_copy_string },
1570	{ .type = PF_RT_PQNAME, .off = _OUT(r.pqname), .arg = (void *)PF_QNAME_SIZE, .cb = snl_attr_copy_string },
1571	{ .type = PF_RT_TAGNAME, .off = _OUT(r.tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = snl_attr_copy_string },
1572	{ .type = PF_RT_MATCH_TAGNAME, .off = _OUT(r.match_tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = snl_attr_copy_string },
1573	{ .type = PF_RT_OVERLOAD_TBLNAME, .off = _OUT(r.overload_tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = snl_attr_copy_string },
1574	{ .type = PF_RT_RPOOL, .off = _OUT(r.rpool), .arg = &pool_parser, .cb = snl_attr_get_nested },
1575	{ .type = PF_RT_OS_FINGERPRINT, .off = _OUT(r.os_fingerprint), .cb = snl_attr_get_uint32 },
1576	{ .type = PF_RT_RTABLEID, .off = _OUT(r.rtableid), .cb = snl_attr_get_uint32 },
1577	{ .type = PF_RT_TIMEOUT, .off = _OUT(r.timeout), .arg = &timeout_parser, .cb = snl_attr_get_nested_timeouts },
1578	{ .type = PF_RT_MAX_STATES, .off = _OUT(r.max_states), .cb = snl_attr_get_uint32 },
1579	{ .type = PF_RT_MAX_SRC_NODES, .off = _OUT(r.max_src_nodes), .cb = snl_attr_get_uint32 },
1580	{ .type = PF_RT_MAX_SRC_STATES, .off = _OUT(r.max_src_states), .cb = snl_attr_get_uint32 },
1581	{ .type = PF_RT_MAX_SRC_CONN_RATE_LIMIT, .off = _OUT(r.max_src_conn_rate.limit), .cb = snl_attr_get_uint32 },
1582	{ .type = PF_RT_MAX_SRC_CONN_RATE_SECS, .off = _OUT(r.max_src_conn_rate.seconds), .cb = snl_attr_get_uint32 },
1583	{ .type = PF_RT_DNPIPE, .off = _OUT(r.dnpipe), .cb = snl_attr_get_uint16 },
1584	{ .type = PF_RT_DNRPIPE, .off = _OUT(r.dnrpipe), .cb = snl_attr_get_uint16 },
1585	{ .type = PF_RT_DNFLAGS, .off = _OUT(r.free_flags), .cb = snl_attr_get_uint32 },
1586	{ .type = PF_RT_NR, .off = _OUT(r.nr), .cb = snl_attr_get_uint32 },
1587	{ .type = PF_RT_PROB, .off = _OUT(r.prob), .cb = snl_attr_get_uint32 },
1588	{ .type = PF_RT_CUID, .off = _OUT(r.cuid), .cb = snl_attr_get_uint32 },
1589	{. type = PF_RT_CPID, .off = _OUT(r.cpid), .cb = snl_attr_get_uint32 },
1590	{ .type = PF_RT_RETURN_ICMP, .off = _OUT(r.return_icmp), .cb = snl_attr_get_uint16 },
1591	{ .type = PF_RT_RETURN_ICMP6, .off = _OUT(r.return_icmp6), .cb = snl_attr_get_uint16 },
1592	{ .type = PF_RT_MAX_MSS, .off = _OUT(r.max_mss), .cb = snl_attr_get_uint16 },
1593	{ .type = PF_RT_SCRUB_FLAGS, .off = _OUT(r.scrub_flags), .cb = snl_attr_get_uint16 },
1594	{ .type = PF_RT_UID, .off = _OUT(r.uid), .arg = &rule_uid_parser, .cb = snl_attr_get_nested },
1595	{ .type = PF_RT_GID, .off = _OUT(r.gid), .arg = &rule_uid_parser, .cb = snl_attr_get_nested },
1596	{ .type = PF_RT_RULE_FLAG, .off = _OUT(r.rule_flag), .cb = snl_attr_get_uint32 },
1597	{ .type = PF_RT_ACTION, .off = _OUT(r.action), .cb = snl_attr_get_uint8 },
1598	{ .type = PF_RT_DIRECTION, .off = _OUT(r.direction), .cb = snl_attr_get_uint8 },
1599	{ .type = PF_RT_LOG, .off = _OUT(r.log), .cb = snl_attr_get_uint8 },
1600	{ .type = PF_RT_LOGIF, .off = _OUT(r.logif), .cb = snl_attr_get_uint8 },
1601	{ .type = PF_RT_QUICK, .off = _OUT(r.quick), .cb = snl_attr_get_uint8 },
1602	{ .type = PF_RT_IF_NOT, .off = _OUT(r.ifnot), .cb = snl_attr_get_uint8 },
1603	{ .type = PF_RT_MATCH_TAG_NOT, .off = _OUT(r.match_tag_not), .cb = snl_attr_get_uint8 },
1604	{ .type = PF_RT_NATPASS, .off = _OUT(r.natpass), .cb = snl_attr_get_uint8 },
1605	{ .type = PF_RT_KEEP_STATE, .off = _OUT(r.keep_state), .cb = snl_attr_get_uint8 },
1606	{ .type = PF_RT_AF, .off = _OUT(r.af), .cb = snl_attr_get_uint8 },
1607	{ .type = PF_RT_PROTO, .off = _OUT(r.proto), .cb = snl_attr_get_uint8 },
1608	{ .type = PF_RT_TYPE, .off = _OUT(r.type), .cb = snl_attr_get_uint8 },
1609	{ .type = PF_RT_CODE, .off = _OUT(r.code), .cb = snl_attr_get_uint8 },
1610	{ .type = PF_RT_FLAGS, .off = _OUT(r.flags), .cb = snl_attr_get_uint8 },
1611	{ .type = PF_RT_FLAGSET, .off = _OUT(r.flagset), .cb = snl_attr_get_uint8 },
1612	{ .type = PF_RT_MIN_TTL, .off = _OUT(r.min_ttl), .cb = snl_attr_get_uint8 },
1613	{ .type = PF_RT_ALLOW_OPTS, .off = _OUT(r.allow_opts), .cb = snl_attr_get_uint8 },
1614	{ .type = PF_RT_RT, .off = _OUT(r.rt), .cb = snl_attr_get_uint8 },
1615	{ .type = PF_RT_RETURN_TTL, .off = _OUT(r.return_ttl), .cb = snl_attr_get_uint8 },
1616	{ .type = PF_RT_TOS, .off = _OUT(r.tos), .cb = snl_attr_get_uint8 },
1617	{ .type = PF_RT_SET_TOS, .off = _OUT(r.set_tos), .cb = snl_attr_get_uint8 },
1618	{ .type = PF_RT_ANCHOR_RELATIVE, .off = _OUT(r.anchor_relative), .cb = snl_attr_get_uint8 },
1619	{ .type = PF_RT_ANCHOR_WILDCARD, .off = _OUT(r.anchor_wildcard), .cb = snl_attr_get_uint8 },
1620	{ .type = PF_RT_FLUSH, .off = _OUT(r.flush), .cb = snl_attr_get_uint8 },
1621	{ .type = PF_RT_PRIO, .off = _OUT(r.prio), .cb = snl_attr_get_uint8 },
1622	{ .type = PF_RT_SET_PRIO, .off = _OUT(r.set_prio[0]), .cb = snl_attr_get_uint8 },
1623	{ .type = PF_RT_SET_PRIO_REPLY, .off = _OUT(r.set_prio[1]), .cb = snl_attr_get_uint8 },
1624	{ .type = PF_RT_DIVERT_ADDRESS, .off = _OUT(r.divert.addr), .cb = snl_attr_get_in6_addr },
1625	{ .type = PF_RT_DIVERT_PORT, .off = _OUT(r.divert.port), .cb = snl_attr_get_uint16 },
1626	{ .type = PF_RT_PACKETS_IN, .off = _OUT(r.packets[0]), .cb = snl_attr_get_uint64 },
1627	{ .type = PF_RT_PACKETS_OUT, .off = _OUT(r.packets[1]), .cb = snl_attr_get_uint64 },
1628	{ .type = PF_RT_BYTES_IN, .off = _OUT(r.bytes[0]), .cb = snl_attr_get_uint64 },
1629	{ .type = PF_RT_BYTES_OUT, .off = _OUT(r.bytes[1]), .cb = snl_attr_get_uint64 },
1630	{ .type = PF_RT_EVALUATIONS, .off = _OUT(r.evaluations), .cb = snl_attr_get_uint64 },
1631	{ .type = PF_RT_TIMESTAMP, .off = _OUT(r.last_active_timestamp), .cb = snl_attr_get_uint64 },
1632	{ .type = PF_RT_STATES_CUR, .off = _OUT(r.states_cur), .cb = snl_attr_get_uint64 },
1633	{ .type = PF_RT_STATES_TOTAL, .off = _OUT(r.states_tot), .cb = snl_attr_get_uint64 },
1634	{ .type = PF_RT_SRC_NODES, .off = _OUT(r.src_nodes), .cb = snl_attr_get_uint64 },
1635	{ .type = PF_RT_ANCHOR_CALL, .off = _OUT(anchor_call), .arg = (void*)MAXPATHLEN, .cb = snl_attr_copy_string },
1636};
1637static struct snl_field_parser fp_getrule[] = {};
1638#undef _OUT
1639SNL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, fp_getrule, ap_getrule);
1640
1641int
1642pfctl_get_clear_rule_h(struct pfctl_handle *h, uint32_t nr, uint32_t ticket,
1643    const char *anchor, uint32_t ruleset, struct pfctl_rule *rule,
1644    char *anchor_call, bool clear)
1645{
1646	struct pfctl_nl_get_rule attrs = {};
1647	struct snl_errmsg_data e = {};
1648	struct nlmsghdr *hdr;
1649	struct snl_writer nw;
1650	uint32_t seq_id;
1651	int family_id;
1652
1653	family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
1654	if (family_id == 0)
1655		return (ENOTSUP);
1656
1657	snl_init_writer(&h->ss, &nw);
1658	hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETRULE);
1659	hdr->nlmsg_flags |= NLM_F_DUMP;
1660
1661	snl_add_msg_attr_string(&nw, PF_GR_ANCHOR, anchor);
1662	snl_add_msg_attr_u8(&nw, PF_GR_ACTION, ruleset);
1663	snl_add_msg_attr_u32(&nw, PF_GR_NR, nr);
1664	snl_add_msg_attr_u32(&nw, PF_GR_TICKET, ticket);
1665	snl_add_msg_attr_u8(&nw, PF_GR_CLEAR, clear);
1666
1667	hdr = snl_finalize_msg(&nw);
1668	if (hdr == NULL)
1669		return (ENOMEM);
1670
1671	seq_id = hdr->nlmsg_seq;
1672	if (! snl_send_message(&h->ss, hdr))
1673		return (ENXIO);
1674
1675	while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
1676		if (! snl_parse_nlmsg(&h->ss, hdr, &getrule_parser, &attrs))
1677			continue;
1678	}
1679
1680	memcpy(rule, &attrs.r, sizeof(attrs.r));
1681	strlcpy(anchor_call, attrs.anchor_call, MAXPATHLEN);
1682
1683	return (e.error);
1684}
1685
1686int
1687pfctl_get_clear_rule(int dev, uint32_t nr, uint32_t ticket,
1688	    const char *anchor, uint32_t ruleset, struct pfctl_rule *rule,
1689	    char *anchor_call, bool clear)
1690{
1691	nvlist_t *nvl;
1692	int ret;
1693
1694	nvl = nvlist_create(0);
1695	if (nvl == 0)
1696		return (ENOMEM);
1697
1698	nvlist_add_number(nvl, "nr", nr);
1699	nvlist_add_number(nvl, "ticket", ticket);
1700	nvlist_add_string(nvl, "anchor", anchor);
1701	nvlist_add_number(nvl, "ruleset", ruleset);
1702
1703	if (clear)
1704		nvlist_add_bool(nvl, "clear_counter", true);
1705
1706	if ((ret = pfctl_do_ioctl(dev, DIOCGETRULENV, 8192, &nvl)) != 0)
1707		goto out;
1708
1709	pf_nvrule_to_rule(nvlist_get_nvlist(nvl, "rule"), rule);
1710
1711	if (anchor_call)
1712		strlcpy(anchor_call, nvlist_get_string(nvl, "anchor_call"),
1713		    MAXPATHLEN);
1714
1715out:
1716	nvlist_destroy(nvl);
1717	return (ret);
1718}
1719
1720int
1721pfctl_set_keepcounters(int dev, bool keep)
1722{
1723	struct pfioc_nv	 nv;
1724	nvlist_t	*nvl;
1725	int		 ret;
1726
1727	nvl = nvlist_create(0);
1728
1729	nvlist_add_bool(nvl, "keep_counters", keep);
1730
1731	nv.data = nvlist_pack(nvl, &nv.len);
1732	nv.size = nv.len;
1733
1734	nvlist_destroy(nvl);
1735
1736	ret = ioctl(dev, DIOCKEEPCOUNTERS, &nv);
1737
1738	free(nv.data);
1739	return (ret);
1740}
1741
1742struct pfctl_creator {
1743	uint32_t id;
1744};
1745#define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1746#define	_OUT(_field)	offsetof(struct pfctl_creator, _field)
1747static struct snl_attr_parser ap_creators[] = {
1748	{ .type = PF_ST_CREATORID, .off = _OUT(id), .cb = snl_attr_get_uint32 },
1749};
1750static struct snl_field_parser fp_creators[] = {
1751};
1752#undef _IN
1753#undef _OUT
1754SNL_DECLARE_PARSER(creator_parser, struct genlmsghdr, fp_creators, ap_creators);
1755
1756static int
1757pfctl_get_creators_nl(struct snl_state *ss, uint32_t *creators, size_t *len)
1758{
1759
1760	int family_id = snl_get_genl_family(ss, PFNL_FAMILY_NAME);
1761	size_t i = 0;
1762
1763	struct nlmsghdr *hdr;
1764	struct snl_writer nw;
1765
1766	if (family_id == 0)
1767		return (ENOTSUP);
1768
1769	snl_init_writer(ss, &nw);
1770	hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETCREATORS);
1771	hdr->nlmsg_flags |= NLM_F_DUMP;
1772	hdr = snl_finalize_msg(&nw);
1773	if (hdr == NULL)
1774		return (ENOMEM);
1775	uint32_t seq_id = hdr->nlmsg_seq;
1776
1777	snl_send_message(ss, hdr);
1778
1779	struct snl_errmsg_data e = {};
1780	while ((hdr = snl_read_reply_multi(ss, seq_id, &e)) != NULL) {
1781		struct pfctl_creator c;
1782		bzero(&c, sizeof(c));
1783
1784		if (!snl_parse_nlmsg(ss, hdr, &creator_parser, &c))
1785			continue;
1786
1787		creators[i] = c.id;
1788		i++;
1789		if (i > *len)
1790			return (E2BIG);
1791	}
1792
1793	*len = i;
1794
1795	return (0);
1796}
1797
1798int
1799pfctl_get_creatorids(struct pfctl_handle *h, uint32_t *creators, size_t *len)
1800{
1801	int error;
1802
1803	error = pfctl_get_creators_nl(&h->ss, creators, len);
1804
1805	return (error);
1806}
1807
1808static inline bool
1809snl_attr_get_pfaddr(struct snl_state *ss __unused, struct nlattr *nla,
1810    const void *arg __unused, void *target)
1811{
1812	memcpy(target, NLA_DATA(nla), NLA_DATA_LEN(nla));
1813	return (true);
1814}
1815
1816static inline bool
1817snl_attr_store_ifname(struct snl_state *ss __unused, struct nlattr *nla,
1818    const void *arg __unused, void *target)
1819{
1820	size_t maxlen = NLA_DATA_LEN(nla);
1821
1822	if (strnlen((char *)NLA_DATA(nla), maxlen) < maxlen) {
1823		strlcpy(target, (char *)NLA_DATA(nla), maxlen);
1824		return (true);
1825	}
1826	return (false);
1827}
1828
1829#define	_OUT(_field)	offsetof(struct pfctl_state_peer, _field)
1830static const struct snl_attr_parser nla_p_speer[] = {
1831	{ .type = PF_STP_SEQLO, .off = _OUT(seqlo), .cb = snl_attr_get_uint32 },
1832	{ .type = PF_STP_SEQHI, .off = _OUT(seqhi), .cb = snl_attr_get_uint32 },
1833	{ .type = PF_STP_SEQDIFF, .off = _OUT(seqdiff), .cb = snl_attr_get_uint32 },
1834	{ .type = PF_STP_STATE, .off = _OUT(state), .cb = snl_attr_get_uint8 },
1835	{ .type = PF_STP_WSCALE, .off = _OUT(wscale), .cb = snl_attr_get_uint8 },
1836};
1837SNL_DECLARE_ATTR_PARSER(speer_parser, nla_p_speer);
1838#undef _OUT
1839
1840#define	_OUT(_field)	offsetof(struct pf_state_key_export, _field)
1841static const struct snl_attr_parser nla_p_skey[] = {
1842	{ .type = PF_STK_ADDR0, .off = _OUT(addr[0]), .cb = snl_attr_get_pfaddr },
1843	{ .type = PF_STK_ADDR1, .off = _OUT(addr[1]), .cb = snl_attr_get_pfaddr },
1844	{ .type = PF_STK_PORT0, .off = _OUT(port[0]), .cb = snl_attr_get_uint16 },
1845	{ .type = PF_STK_PORT1, .off = _OUT(port[1]), .cb = snl_attr_get_uint16 },
1846};
1847SNL_DECLARE_ATTR_PARSER(skey_parser, nla_p_skey);
1848#undef _OUT
1849
1850#define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1851#define	_OUT(_field)	offsetof(struct pfctl_state, _field)
1852static struct snl_attr_parser ap_state[] = {
1853	{ .type = PF_ST_ID, .off = _OUT(id), .cb = snl_attr_get_uint64 },
1854	{ .type = PF_ST_CREATORID, .off = _OUT(creatorid), .cb = snl_attr_get_uint32 },
1855	{ .type = PF_ST_IFNAME, .off = _OUT(ifname), .cb = snl_attr_store_ifname },
1856	{ .type = PF_ST_ORIG_IFNAME, .off = _OUT(orig_ifname), .cb = snl_attr_store_ifname },
1857	{ .type = PF_ST_KEY_WIRE, .off = _OUT(key[0]), .arg = &skey_parser, .cb = snl_attr_get_nested },
1858	{ .type = PF_ST_KEY_STACK, .off = _OUT(key[1]), .arg = &skey_parser, .cb = snl_attr_get_nested },
1859	{ .type = PF_ST_PEER_SRC, .off = _OUT(src), .arg = &speer_parser, .cb = snl_attr_get_nested },
1860	{ .type = PF_ST_PEER_DST, .off = _OUT(dst), .arg = &speer_parser, .cb = snl_attr_get_nested },
1861	{ .type = PF_ST_RT_ADDR, .off = _OUT(rt_addr), .cb = snl_attr_get_pfaddr },
1862	{ .type = PF_ST_RULE, .off = _OUT(rule), .cb = snl_attr_get_uint32 },
1863	{ .type = PF_ST_ANCHOR, .off = _OUT(anchor), .cb = snl_attr_get_uint32 },
1864	{ .type = PF_ST_NAT_RULE, .off = _OUT(nat_rule), .cb = snl_attr_get_uint32 },
1865	{ .type = PF_ST_CREATION, .off = _OUT(creation), .cb = snl_attr_get_uint32 },
1866	{ .type = PF_ST_EXPIRE, .off = _OUT(expire), .cb = snl_attr_get_uint32 },
1867	{ .type = PF_ST_PACKETS0, .off = _OUT(packets[0]), .cb = snl_attr_get_uint64 },
1868	{ .type = PF_ST_PACKETS1, .off = _OUT(packets[1]), .cb = snl_attr_get_uint64 },
1869	{ .type = PF_ST_BYTES0, .off = _OUT(bytes[0]), .cb = snl_attr_get_uint64 },
1870	{ .type = PF_ST_BYTES1, .off = _OUT(bytes[1]), .cb = snl_attr_get_uint64 },
1871	{ .type = PF_ST_AF, .off = _OUT(key[0].af), .cb = snl_attr_get_uint8 },
1872	{ .type = PF_ST_PROTO, .off = _OUT(key[0].proto), .cb = snl_attr_get_uint8 },
1873	{ .type = PF_ST_DIRECTION, .off = _OUT(direction), .cb = snl_attr_get_uint8 },
1874	{ .type = PF_ST_LOG, .off = _OUT(log), .cb = snl_attr_get_uint8 },
1875	{ .type = PF_ST_STATE_FLAGS, .off = _OUT(state_flags), .cb = snl_attr_get_uint16 },
1876	{ .type = PF_ST_SYNC_FLAGS, .off = _OUT(sync_flags), .cb = snl_attr_get_uint8 },
1877	{ .type = PF_ST_RTABLEID, .off = _OUT(rtableid), .cb = snl_attr_get_int32 },
1878	{ .type = PF_ST_MIN_TTL, .off = _OUT(min_ttl), .cb = snl_attr_get_uint8 },
1879	{ .type = PF_ST_MAX_MSS, .off = _OUT(max_mss), .cb = snl_attr_get_uint16 },
1880	{ .type = PF_ST_DNPIPE, .off = _OUT(dnpipe), .cb = snl_attr_get_uint16 },
1881	{ .type = PF_ST_DNRPIPE, .off = _OUT(dnrpipe), .cb = snl_attr_get_uint16 },
1882	{ .type = PF_ST_RT, .off = _OUT(rt), .cb = snl_attr_get_uint8 },
1883	{ .type = PF_ST_RT_IFNAME, .off = _OUT(rt_ifname), .cb = snl_attr_store_ifname },
1884};
1885static struct snl_field_parser fp_state[] = {
1886};
1887#undef _IN
1888#undef _OUT
1889SNL_DECLARE_PARSER(state_parser, struct genlmsghdr, fp_state, ap_state);
1890
1891static const struct snl_hdr_parser *all_parsers[] = {
1892	&state_parser, &skey_parser, &speer_parser,
1893	&creator_parser, &getrules_parser
1894};
1895
1896static int
1897pfctl_get_states_nl(struct pfctl_state_filter *filter, struct snl_state *ss, pfctl_get_state_fn f, void *arg)
1898{
1899	SNL_VERIFY_PARSERS(all_parsers);
1900	int family_id = snl_get_genl_family(ss, PFNL_FAMILY_NAME);
1901	int ret;
1902
1903	struct nlmsghdr *hdr;
1904	struct snl_writer nw;
1905
1906	if (family_id == 0)
1907		return (ENOTSUP);
1908
1909	snl_init_writer(ss, &nw);
1910	hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETSTATES);
1911	hdr->nlmsg_flags |= NLM_F_DUMP;
1912	snl_add_msg_attr_string(&nw, PF_ST_IFNAME, filter->ifname);
1913	snl_add_msg_attr_u16(&nw, PF_ST_PROTO, filter->proto);
1914	snl_add_msg_attr_u8(&nw, PF_ST_AF, filter->af);
1915	snl_add_msg_attr_ip6(&nw, PF_ST_FILTER_ADDR, &filter->addr.v6);
1916	snl_add_msg_attr_ip6(&nw, PF_ST_FILTER_MASK, &filter->mask.v6);
1917
1918	hdr = snl_finalize_msg(&nw);
1919	if (hdr == NULL)
1920		return (ENOMEM);
1921
1922	uint32_t seq_id = hdr->nlmsg_seq;
1923
1924	snl_send_message(ss, hdr);
1925
1926	struct snl_errmsg_data e = {};
1927	while ((hdr = snl_read_reply_multi(ss, seq_id, &e)) != NULL) {
1928		struct pfctl_state s;
1929		bzero(&s, sizeof(s));
1930		if (!snl_parse_nlmsg(ss, hdr, &state_parser, &s))
1931			continue;
1932
1933		s.key[1].af = s.key[0].af;
1934		s.key[1].proto = s.key[0].proto;
1935
1936		ret = f(&s, arg);
1937		if (ret != 0)
1938			return (ret);
1939	}
1940
1941	return (0);
1942}
1943
1944int
1945pfctl_get_states_iter(pfctl_get_state_fn f, void *arg)
1946{
1947	struct pfctl_state_filter filter = {};
1948	return (pfctl_get_filtered_states_iter(&filter, f, arg));
1949}
1950
1951int
1952pfctl_get_filtered_states_iter(struct pfctl_state_filter *filter, pfctl_get_state_fn f, void *arg)
1953{
1954	struct snl_state ss = {};
1955	int error;
1956
1957	snl_init(&ss, NETLINK_GENERIC);
1958	error = pfctl_get_states_nl(filter, &ss, f, arg);
1959	snl_free(&ss);
1960
1961	return (error);
1962}
1963
1964static int
1965pfctl_append_states(struct pfctl_state *s, void *arg)
1966{
1967	struct pfctl_state *new;
1968	struct pfctl_states *states = (struct pfctl_states *)arg;
1969
1970	new = malloc(sizeof(*s));
1971	if (new == NULL)
1972		return (ENOMEM);
1973
1974	memcpy(new, s, sizeof(*s));
1975
1976	TAILQ_INSERT_TAIL(&states->states, new, entry);
1977
1978	return (0);
1979}
1980
1981int
1982pfctl_get_states(int dev __unused, struct pfctl_states *states)
1983{
1984	int ret;
1985
1986	bzero(states, sizeof(*states));
1987	TAILQ_INIT(&states->states);
1988
1989	ret = pfctl_get_states_iter(pfctl_append_states, states);
1990	if (ret != 0) {
1991		pfctl_free_states(states);
1992		return (ret);
1993	}
1994
1995	return (0);
1996}
1997
1998void
1999pfctl_free_states(struct pfctl_states *states)
2000{
2001	struct pfctl_state *s, *tmp;
2002
2003	TAILQ_FOREACH_SAFE(s, &states->states, entry, tmp) {
2004		free(s);
2005	}
2006
2007	bzero(states, sizeof(*states));
2008}
2009
2010struct pfctl_nl_clear_states {
2011	uint32_t killed;
2012};
2013#define	_OUT(_field)	offsetof(struct pfctl_nl_clear_states, _field)
2014static struct snl_attr_parser ap_clear_states[] = {
2015	{ .type = PF_CS_KILLED, .off = _OUT(killed), .cb = snl_attr_get_uint32 },
2016};
2017static struct snl_field_parser fp_clear_states[] = {};
2018#undef _OUT
2019SNL_DECLARE_PARSER(clear_states_parser, struct genlmsghdr, fp_clear_states, ap_clear_states);
2020
2021static int
2022_pfctl_clear_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill,
2023    unsigned int *killed, int cmd)
2024{
2025	struct snl_writer nw;
2026	struct snl_errmsg_data e = {};
2027	struct pfctl_nl_clear_states attrs = {};
2028	struct nlmsghdr *hdr;
2029	uint32_t seq_id;
2030	int family_id;
2031
2032	family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
2033	if (family_id == 0)
2034		return (ENOTSUP);
2035
2036	snl_init_writer(&h->ss, &nw);
2037	hdr = snl_create_genl_msg_request(&nw, family_id, cmd);
2038	hdr->nlmsg_flags |= NLM_F_DUMP;
2039
2040	snl_add_msg_attr_u64(&nw, PF_CS_CMP_ID, kill->cmp.id);
2041	snl_add_msg_attr_u32(&nw, PF_CS_CMP_CREATORID, htonl(kill->cmp.creatorid));
2042	snl_add_msg_attr_u8(&nw, PF_CS_CMP_DIR, kill->cmp.direction);
2043	snl_add_msg_attr_u8(&nw, PF_CS_AF, kill->af);
2044	snl_add_msg_attr_u8(&nw, PF_CS_PROTO, kill->proto);
2045	snl_add_msg_attr_rule_addr(&nw, PF_CS_SRC, &kill->src);
2046	snl_add_msg_attr_rule_addr(&nw, PF_CS_DST, &kill->dst);
2047	snl_add_msg_attr_rule_addr(&nw, PF_CS_RT_ADDR, &kill->rt_addr);
2048	snl_add_msg_attr_string(&nw, PF_CS_IFNAME, kill->ifname);
2049	snl_add_msg_attr_string(&nw, PF_CS_LABEL, kill->label);
2050	snl_add_msg_attr_bool(&nw, PF_CS_KILL_MATCH, kill->kill_match);
2051	snl_add_msg_attr_bool(&nw, PF_CS_NAT, kill->nat);
2052
2053	if ((hdr = snl_finalize_msg(&nw)) == NULL)
2054		return (ENXIO);
2055
2056	seq_id = hdr->nlmsg_seq;
2057
2058	if (! snl_send_message(&h->ss, hdr))
2059		return (ENXIO);
2060
2061	while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
2062		if (! snl_parse_nlmsg(&h->ss, hdr, &clear_states_parser, &attrs))
2063			continue;
2064	}
2065
2066	if (killed)
2067		*killed = attrs.killed;
2068
2069	return (e.error);
2070}
2071
2072int
2073pfctl_clear_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill,
2074    unsigned int *killed)
2075{
2076	return(_pfctl_clear_states_h(h, kill, killed, PFNL_CMD_CLRSTATES));
2077}
2078
2079int
2080pfctl_kill_states_h(struct pfctl_handle *h, const struct pfctl_kill *kill,
2081    unsigned int *killed)
2082{
2083	return(_pfctl_clear_states_h(h, kill, killed, PFNL_CMD_KILLSTATES));
2084}
2085
2086static int
2087_pfctl_clear_states(int dev __unused, const struct pfctl_kill *kill,
2088    unsigned int *killed, uint64_t cmd)
2089{
2090	struct pfctl_handle *h;
2091	int ret;
2092
2093	h = pfctl_open(PF_DEVICE);
2094	if (h == NULL)
2095		return (ENODEV);
2096
2097	ret = _pfctl_clear_states_h(h, kill, killed, cmd);
2098	pfctl_close(h);
2099
2100	return (ret);
2101}
2102
2103int
2104pfctl_clear_states(int dev __unused, const struct pfctl_kill *kill,
2105    unsigned int *killed)
2106{
2107	return (_pfctl_clear_states(dev, kill, killed, PFNL_CMD_CLRSTATES));
2108}
2109
2110int
2111pfctl_kill_states(int dev __unused, const struct pfctl_kill *kill, unsigned int *killed)
2112{
2113	return (_pfctl_clear_states(dev, kill, killed, PFNL_CMD_KILLSTATES));
2114}
2115
2116int
2117pfctl_clear_rules(int dev, const char *anchorname)
2118{
2119	struct pfioc_trans trans;
2120	struct pfioc_trans_e transe[2];
2121	int ret;
2122
2123	bzero(&trans, sizeof(trans));
2124	bzero(&transe, sizeof(transe));
2125
2126	transe[0].rs_num = PF_RULESET_SCRUB;
2127	if (strlcpy(transe[0].anchor, anchorname, sizeof(transe[0].anchor))
2128	    >= sizeof(transe[0].anchor))
2129		return (E2BIG);
2130
2131	transe[1].rs_num = PF_RULESET_FILTER;
2132	if (strlcpy(transe[1].anchor, anchorname, sizeof(transe[1].anchor))
2133	    >= sizeof(transe[1].anchor))
2134		return (E2BIG);
2135
2136	trans.size = 2;
2137	trans.esize = sizeof(transe[0]);
2138	trans.array = transe;
2139
2140	ret = ioctl(dev, DIOCXBEGIN, &trans);
2141	if (ret != 0)
2142		return (ret);
2143	return ioctl(dev, DIOCXCOMMIT, &trans);
2144}
2145
2146int
2147pfctl_clear_nat(int dev, const char *anchorname)
2148{
2149	struct pfioc_trans trans;
2150	struct pfioc_trans_e transe[3];
2151	int ret;
2152
2153	bzero(&trans, sizeof(trans));
2154	bzero(&transe, sizeof(transe));
2155
2156	transe[0].rs_num = PF_RULESET_NAT;
2157	if (strlcpy(transe[0].anchor, anchorname, sizeof(transe[0].anchor))
2158	    >= sizeof(transe[0].anchor))
2159		return (E2BIG);
2160
2161	transe[1].rs_num = PF_RULESET_BINAT;
2162	if (strlcpy(transe[1].anchor, anchorname, sizeof(transe[1].anchor))
2163	    >= sizeof(transe[0].anchor))
2164		return (E2BIG);
2165
2166	transe[2].rs_num = PF_RULESET_RDR;
2167	if (strlcpy(transe[2].anchor, anchorname, sizeof(transe[2].anchor))
2168	    >= sizeof(transe[2].anchor))
2169		return (E2BIG);
2170
2171	trans.size = 3;
2172	trans.esize = sizeof(transe[0]);
2173	trans.array = transe;
2174
2175	ret = ioctl(dev, DIOCXBEGIN, &trans);
2176	if (ret != 0)
2177		return (ret);
2178	return ioctl(dev, DIOCXCOMMIT, &trans);
2179}
2180int
2181pfctl_clear_eth_rules(int dev, const char *anchorname)
2182{
2183	struct pfioc_trans trans;
2184	struct pfioc_trans_e transe;
2185	int ret;
2186
2187	bzero(&trans, sizeof(trans));
2188	bzero(&transe, sizeof(transe));
2189
2190	transe.rs_num = PF_RULESET_ETH;
2191	if (strlcpy(transe.anchor, anchorname, sizeof(transe.anchor))
2192	    >= sizeof(transe.anchor))
2193		return (E2BIG);
2194
2195	trans.size = 1;
2196	trans.esize = sizeof(transe);
2197	trans.array = &transe;
2198
2199	ret = ioctl(dev, DIOCXBEGIN, &trans);
2200	if (ret != 0)
2201		return (ret);
2202	return ioctl(dev, DIOCXCOMMIT, &trans);
2203}
2204
2205static int
2206pfctl_get_limit(int dev, const int index, uint *limit)
2207{
2208	struct pfioc_limit pl;
2209
2210	bzero(&pl, sizeof(pl));
2211	pl.index = index;
2212
2213	if (ioctl(dev, DIOCGETLIMIT, &pl) == -1)
2214		return (errno);
2215
2216	*limit = pl.limit;
2217
2218	return (0);
2219}
2220
2221int
2222pfctl_set_syncookies(int dev, const struct pfctl_syncookies *s)
2223{
2224	struct pfioc_nv	 nv;
2225	nvlist_t	*nvl;
2226	int		 ret;
2227	uint		 state_limit;
2228	uint64_t	 lim, hi, lo;
2229
2230	ret = pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit);
2231	if (ret != 0)
2232		return (ret);
2233
2234	lim = state_limit;
2235	hi = lim * s->highwater / 100;
2236	lo = lim * s->lowwater / 100;
2237
2238	if (lo == hi)
2239		hi++;
2240
2241	nvl = nvlist_create(0);
2242
2243	nvlist_add_bool(nvl, "enabled", s->mode != PFCTL_SYNCOOKIES_NEVER);
2244	nvlist_add_bool(nvl, "adaptive", s->mode == PFCTL_SYNCOOKIES_ADAPTIVE);
2245	nvlist_add_number(nvl, "highwater", hi);
2246	nvlist_add_number(nvl, "lowwater", lo);
2247
2248	nv.data = nvlist_pack(nvl, &nv.len);
2249	nv.size = nv.len;
2250	nvlist_destroy(nvl);
2251	nvl = NULL;
2252
2253	ret = ioctl(dev, DIOCSETSYNCOOKIES, &nv);
2254
2255	free(nv.data);
2256	return (ret);
2257}
2258
2259int
2260pfctl_get_syncookies(int dev, struct pfctl_syncookies *s)
2261{
2262	nvlist_t	*nvl;
2263	int		 ret;
2264	uint		 state_limit;
2265	bool		 enabled, adaptive;
2266
2267	ret = pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit);
2268	if (ret != 0)
2269		return (ret);
2270
2271	bzero(s, sizeof(*s));
2272
2273	nvl = nvlist_create(0);
2274
2275	if ((ret = pfctl_do_ioctl(dev, DIOCGETSYNCOOKIES, 256, &nvl)) != 0) {
2276		ret = errno;
2277		goto out;
2278	}
2279
2280	enabled = nvlist_get_bool(nvl, "enabled");
2281	adaptive = nvlist_get_bool(nvl, "adaptive");
2282
2283	if (enabled) {
2284		if (adaptive)
2285			s->mode = PFCTL_SYNCOOKIES_ADAPTIVE;
2286		else
2287			s->mode = PFCTL_SYNCOOKIES_ALWAYS;
2288	} else {
2289		s->mode = PFCTL_SYNCOOKIES_NEVER;
2290	}
2291
2292	s->highwater = nvlist_get_number(nvl, "highwater") * 100 / state_limit;
2293	s->lowwater = nvlist_get_number(nvl, "lowwater") * 100 / state_limit;
2294	s->halfopen_states = nvlist_get_number(nvl, "halfopen_states");
2295
2296out:
2297	nvlist_destroy(nvl);
2298	return (ret);
2299}
2300
2301int
2302pfctl_table_add_addrs(int dev, struct pfr_table *tbl, struct pfr_addr
2303    *addr, int size, int *nadd, int flags)
2304{
2305	struct pfioc_table io;
2306
2307	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
2308		return (EINVAL);
2309	}
2310	bzero(&io, sizeof io);
2311	io.pfrio_flags = flags;
2312	io.pfrio_table = *tbl;
2313	io.pfrio_buffer = addr;
2314	io.pfrio_esize = sizeof(*addr);
2315	io.pfrio_size = size;
2316
2317	if (ioctl(dev, DIOCRADDADDRS, &io))
2318		return (errno);
2319	if (nadd != NULL)
2320		*nadd = io.pfrio_nadd;
2321	return (0);
2322}
2323
2324int
2325pfctl_table_del_addrs(int dev, struct pfr_table *tbl, struct pfr_addr
2326    *addr, int size, int *ndel, int flags)
2327{
2328	struct pfioc_table io;
2329
2330	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
2331		return (EINVAL);
2332	}
2333	bzero(&io, sizeof io);
2334	io.pfrio_flags = flags;
2335	io.pfrio_table = *tbl;
2336	io.pfrio_buffer = addr;
2337	io.pfrio_esize = sizeof(*addr);
2338	io.pfrio_size = size;
2339
2340	if (ioctl(dev, DIOCRDELADDRS, &io))
2341		return (errno);
2342	if (ndel != NULL)
2343		*ndel = io.pfrio_ndel;
2344	return (0);
2345}
2346
2347int
2348pfctl_table_set_addrs(int dev, struct pfr_table *tbl, struct pfr_addr
2349    *addr, int size, int *size2, int *nadd, int *ndel, int *nchange, int flags)
2350{
2351	struct pfioc_table io;
2352
2353	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
2354		return (EINVAL);
2355	}
2356	bzero(&io, sizeof io);
2357	io.pfrio_flags = flags;
2358	io.pfrio_table = *tbl;
2359	io.pfrio_buffer = addr;
2360	io.pfrio_esize = sizeof(*addr);
2361	io.pfrio_size = size;
2362	io.pfrio_size2 = (size2 != NULL) ? *size2 : 0;
2363	if (ioctl(dev, DIOCRSETADDRS, &io))
2364		return (-1);
2365	if (nadd != NULL)
2366		*nadd = io.pfrio_nadd;
2367	if (ndel != NULL)
2368		*ndel = io.pfrio_ndel;
2369	if (nchange != NULL)
2370		*nchange = io.pfrio_nchange;
2371	if (size2 != NULL)
2372		*size2 = io.pfrio_size2;
2373	return (0);
2374}
2375
2376int pfctl_table_get_addrs(int dev, struct pfr_table *tbl, struct pfr_addr *addr,
2377    int *size, int flags)
2378{
2379	struct pfioc_table io;
2380
2381	if (tbl == NULL || size == NULL || *size < 0 ||
2382	    (*size && addr == NULL)) {
2383		return (EINVAL);
2384	}
2385	bzero(&io, sizeof io);
2386	io.pfrio_flags = flags;
2387	io.pfrio_table = *tbl;
2388	io.pfrio_buffer = addr;
2389	io.pfrio_esize = sizeof(*addr);
2390	io.pfrio_size = *size;
2391	if (ioctl(dev, DIOCRGETADDRS, &io))
2392		return (-1);
2393	*size = io.pfrio_size;
2394	return (0);
2395}
2396
2397int
2398pfctl_set_statusif(struct pfctl_handle *h, const char *ifname)
2399{
2400	struct snl_writer nw;
2401	struct snl_errmsg_data e = {};
2402	struct nlmsghdr *hdr;
2403	uint32_t seq_id;
2404	int family_id;
2405
2406	family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
2407	if (family_id == 0)
2408		return (ENOTSUP);
2409
2410	snl_init_writer(&h->ss, &nw);
2411	hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_SET_STATUSIF);
2412
2413	snl_add_msg_attr_string(&nw, PF_SS_IFNAME, ifname);
2414
2415	if ((hdr = snl_finalize_msg(&nw)) == NULL)
2416		return (ENXIO);
2417
2418	seq_id = hdr->nlmsg_seq;
2419
2420	if (! snl_send_message(&h->ss, hdr))
2421		return (ENXIO);
2422
2423	while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
2424	}
2425
2426	return (e.error);
2427}
2428