xref: /freebsd/sys/netpfil/pf/pf_nl.c (revision 024248c933c5741a21c17eda63092f330dd98337)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023 Alexander V. Chernikov <melifaro@FreeBSD.org>
5  * Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
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  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 #include <sys/cdefs.h>
30 #include "opt_inet.h"
31 #include "opt_inet6.h"
32 
33 #include <sys/param.h>
34 #include <sys/malloc.h>
35 #include <sys/mbuf.h>
36 #include <sys/priv.h>
37 #include <sys/socket.h>
38 #include <sys/ucred.h>
39 
40 #include <net/pfvar.h>
41 
42 #include <netlink/netlink.h>
43 #include <netlink/netlink_ctl.h>
44 #include <netlink/netlink_generic.h>
45 #include <netlink/netlink_message_writer.h>
46 
47 #include <netpfil/pf/pf_nl.h>
48 
49 #define	DEBUG_MOD_NAME	nl_pf
50 #define	DEBUG_MAX_LEVEL	LOG_DEBUG3
51 #include <netlink/netlink_debug.h>
52 _DECLARE_DEBUG(LOG_DEBUG);
53 
54 struct nl_parsed_state {
55 	uint8_t		version;
56 	uint32_t	id;
57 	uint32_t	creatorid;
58 	char		ifname[IFNAMSIZ];
59 	uint16_t	proto;
60 	sa_family_t	af;
61 	struct pf_addr	addr;
62 	struct pf_addr	mask;
63 };
64 
65 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
66 #define	_OUT(_field)	offsetof(struct nl_parsed_state, _field)
67 static const struct nlattr_parser nla_p_state[] = {
68 	{ .type = PF_ST_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
69 	{ .type = PF_ST_CREATORID, .off = _OUT(creatorid), .cb = nlattr_get_uint32 },
70 	{ .type = PF_ST_IFNAME, .arg = (const void *)IFNAMSIZ, .off = _OUT(ifname), .cb = nlattr_get_chara },
71 	{ .type = PF_ST_AF, .off = _OUT(proto), .cb = nlattr_get_uint8 },
72 	{ .type = PF_ST_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint16 },
73 	{ .type = PF_ST_FILTER_ADDR, .off = _OUT(addr), .cb = nlattr_get_in6_addr },
74 	{ .type = PF_ST_FILTER_MASK, .off = _OUT(mask), .cb = nlattr_get_in6_addr },
75 };
76 static const struct nlfield_parser nlf_p_generic[] = {
77 	{ .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 },
78 };
79 #undef _IN
80 #undef _OUT
81 NL_DECLARE_PARSER(state_parser, struct genlmsghdr, nlf_p_generic, nla_p_state);
82 
83 static void
84 dump_addr(struct nl_writer *nw, int attr, const struct pf_addr *addr, int af)
85 {
86 	switch (af) {
87 	case AF_INET:
88 		nlattr_add(nw, attr, 4, &addr->v4);
89 		break;
90 	case AF_INET6:
91 		nlattr_add(nw, attr, 16, &addr->v6);
92 		break;
93 	};
94 }
95 
96 static bool
97 dump_state_peer(struct nl_writer *nw, int attr, const struct pf_state_peer *peer)
98 {
99 	int off = nlattr_add_nested(nw, attr);
100 	if (off == 0)
101 		return (false);
102 
103 	nlattr_add_u32(nw, PF_STP_SEQLO, peer->seqlo);
104 	nlattr_add_u32(nw, PF_STP_SEQHI, peer->seqhi);
105 	nlattr_add_u32(nw, PF_STP_SEQDIFF, peer->seqdiff);
106 	nlattr_add_u16(nw, PF_STP_MAX_WIN, peer->max_win);
107 	nlattr_add_u16(nw, PF_STP_MSS, peer->mss);
108 	nlattr_add_u8(nw, PF_STP_STATE, peer->state);
109 	nlattr_add_u8(nw, PF_STP_WSCALE, peer->wscale);
110 
111 	if (peer->scrub != NULL) {
112 		struct pf_state_scrub *sc = peer->scrub;
113 		uint16_t pfss_flags = sc->pfss_flags & PFSS_TIMESTAMP;
114 
115 		nlattr_add_u16(nw, PF_STP_PFSS_FLAGS, pfss_flags);
116 		nlattr_add_u32(nw, PF_STP_PFSS_TS_MOD, sc->pfss_ts_mod);
117 		nlattr_add_u8(nw, PF_STP_PFSS_TTL, sc->pfss_ttl);
118 		nlattr_add_u8(nw, PF_STP_SCRUB_FLAG, PFSYNC_SCRUB_FLAG_VALID);
119 	}
120 	nlattr_set_len(nw, off);
121 
122 	return (true);
123 }
124 
125 static bool
126 dump_state_key(struct nl_writer *nw, int attr, const struct pf_state_key *key)
127 {
128 	int off = nlattr_add_nested(nw, attr);
129 	if (off == 0)
130 		return (false);
131 
132 	dump_addr(nw, PF_STK_ADDR0, &key->addr[0], key->af);
133 	dump_addr(nw, PF_STK_ADDR1, &key->addr[1], key->af);
134 	nlattr_add_u16(nw, PF_STK_PORT0, key->port[0]);
135 	nlattr_add_u16(nw, PF_STK_PORT1, key->port[1]);
136 
137 	nlattr_set_len(nw, off);
138 
139 	return (true);
140 }
141 
142 static int
143 dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, struct pf_kstate *s,
144     struct nl_pstate *npt)
145 {
146 	struct nl_writer *nw = npt->nw;
147 	int error = 0;
148 	int af;
149 	struct pf_state_key *key;
150 
151 	PF_STATE_LOCK_ASSERT(s);
152 
153 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
154 		goto enomem;
155 
156 	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
157 	ghdr_new->cmd = PFNL_CMD_GETSTATES;
158 	ghdr_new->version = 0;
159 	ghdr_new->reserved = 0;
160 
161 	nlattr_add_u64(nw, PF_ST_VERSION, PF_STATE_VERSION);
162 
163 	key = s->key[PF_SK_WIRE];
164 	if (!dump_state_key(nw, PF_ST_KEY_WIRE, key))
165 		goto enomem;
166 	key = s->key[PF_SK_STACK];
167 	if (!dump_state_key(nw, PF_ST_KEY_STACK, key))
168 		goto enomem;
169 
170 	af = s->key[PF_SK_WIRE]->af;
171 	nlattr_add_u8(nw, PF_ST_PROTO, s->key[PF_SK_WIRE]->proto);
172 	nlattr_add_u8(nw, PF_ST_AF, af);
173 
174 	nlattr_add_string(nw, PF_ST_IFNAME, s->kif->pfik_name);
175 	nlattr_add_string(nw, PF_ST_ORIG_IFNAME, s->orig_kif->pfik_name);
176 	dump_addr(nw, PF_ST_RT_ADDR, &s->rt_addr, af);
177 	nlattr_add_u32(nw, PF_ST_CREATION, time_uptime - (s->creation / 1000));
178 	uint32_t expire = pf_state_expires(s);
179 	if (expire > time_uptime)
180 		expire = expire - time_uptime;
181 	nlattr_add_u32(nw, PF_ST_EXPIRE, expire);
182 	nlattr_add_u8(nw, PF_ST_DIRECTION, s->direction);
183 	nlattr_add_u8(nw, PF_ST_LOG, s->act.log);
184 	nlattr_add_u8(nw, PF_ST_TIMEOUT, s->timeout);
185 	nlattr_add_u16(nw, PF_ST_STATE_FLAGS, s->state_flags);
186 	uint8_t sync_flags = 0;
187 	if (s->src_node)
188 		sync_flags |= PFSYNC_FLAG_SRCNODE;
189 	if (s->nat_src_node)
190 		sync_flags |= PFSYNC_FLAG_NATSRCNODE;
191 	nlattr_add_u8(nw, PF_ST_SYNC_FLAGS, sync_flags);
192 	nlattr_add_u64(nw, PF_ST_ID, s->id);
193 	nlattr_add_u32(nw, PF_ST_CREATORID, htonl(s->creatorid));
194 
195 	nlattr_add_u32(nw, PF_ST_RULE, s->rule.ptr ? s->rule.ptr->nr : -1);
196 	nlattr_add_u32(nw, PF_ST_ANCHOR, s->anchor.ptr ? s->anchor.ptr->nr : -1);
197 	nlattr_add_u32(nw, PF_ST_NAT_RULE, s->nat_rule.ptr ? s->nat_rule.ptr->nr : -1);
198 
199 	nlattr_add_u64(nw, PF_ST_PACKETS0, s->packets[0]);
200 	nlattr_add_u64(nw, PF_ST_PACKETS1, s->packets[1]);
201 	nlattr_add_u64(nw, PF_ST_BYTES0, s->bytes[0]);
202 	nlattr_add_u64(nw, PF_ST_BYTES1, s->bytes[1]);
203 	nlattr_add_u32(nw, PF_ST_RTABLEID, s->act.rtableid);
204 	nlattr_add_u8(nw, PF_ST_MIN_TTL, s->act.min_ttl);
205 	nlattr_add_u16(nw, PF_ST_MAX_MSS, s->act.max_mss);
206 	nlattr_add_u16(nw, PF_ST_DNPIPE, s->act.dnpipe);
207 	nlattr_add_u16(nw, PF_ST_DNRPIPE, s->act.dnrpipe);
208 	nlattr_add_u8(nw, PF_ST_RT, s->rt);
209 	if (s->rt_kif != NULL)
210 		nlattr_add_string(nw, PF_ST_RT_IFNAME, s->rt_kif->pfik_name);
211 
212 	if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src))
213 		goto enomem;
214 	if (!dump_state_peer(nw, PF_ST_PEER_DST, &s->dst))
215 		goto enomem;
216 
217 	if (nlmsg_end(nw))
218 		return (0);
219 
220 enomem:
221 	error = ENOMEM;
222 	nlmsg_abort(nw);
223 	return (error);
224 }
225 
226 static int
227 handle_dumpstates(struct nlpcb *nlp, struct nl_parsed_state *attrs,
228     struct nlmsghdr *hdr, struct nl_pstate *npt)
229 {
230 	int error = 0;
231 
232 	hdr->nlmsg_flags |= NLM_F_MULTI;
233 
234 	for (int i = 0; i <= V_pf_hashmask; i++) {
235 		struct pf_idhash *ih = &V_pf_idhash[i];
236 		struct pf_kstate *s;
237 
238 		if (LIST_EMPTY(&ih->states))
239 			continue;
240 
241 		PF_HASHROW_LOCK(ih);
242 		LIST_FOREACH(s, &ih->states, entry) {
243 			sa_family_t af = s->key[PF_SK_WIRE]->af;
244 
245 			if (s->timeout == PFTM_UNLINKED)
246 				continue;
247 
248 			/* Filter */
249 			if (attrs->creatorid != 0 && s->creatorid != attrs->creatorid)
250 				continue;
251 			if (attrs->ifname[0] != 0 &&
252 			    strncmp(attrs->ifname, s->kif->pfik_name, IFNAMSIZ) != 0)
253 				continue;
254 			if (attrs->proto != 0 && s->key[PF_SK_WIRE]->proto != attrs->proto)
255 				continue;
256 			if (attrs->af != 0 && af != attrs->af)
257 				continue;
258 			if (pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[0],
259 			    &attrs->mask, &attrs->addr, af) &&
260 			    pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[1],
261 			    &attrs->mask, &attrs->addr, af) &&
262 			    pf_match_addr(1, &s->key[PF_SK_STACK]->addr[0],
263 			    &attrs->mask, &attrs->addr, af) &&
264 			    pf_match_addr(1, &s->key[PF_SK_STACK]->addr[1],
265 			    &attrs->mask, &attrs->addr, af))
266 				continue;
267 
268 			error = dump_state(nlp, hdr, s, npt);
269 			if (error != 0)
270 				break;
271 		}
272 		PF_HASHROW_UNLOCK(ih);
273 	}
274 
275 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
276 		NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
277 		return (ENOMEM);
278 	}
279 
280 	return (error);
281 }
282 
283 static int
284 handle_getstate(struct nlpcb *nlp, struct nl_parsed_state *attrs,
285     struct nlmsghdr *hdr, struct nl_pstate *npt)
286 {
287 	struct pf_kstate *s;
288 	int ret;
289 
290 	s = pf_find_state_byid(attrs->id, attrs->creatorid);
291 	if (s == NULL)
292 		return (ENOENT);
293 	ret = dump_state(nlp, hdr, s, npt);
294 	PF_STATE_UNLOCK(s);
295 
296 	return (ret);
297 }
298 
299 static int
300 dump_creatorid(struct nlpcb *nlp, const struct nlmsghdr *hdr, uint32_t creator,
301     struct nl_pstate *npt)
302 {
303 	struct nl_writer *nw = npt->nw;
304 
305 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
306 		goto enomem;
307 
308 	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
309 	ghdr_new->cmd = PFNL_CMD_GETCREATORS;
310 	ghdr_new->version = 0;
311 	ghdr_new->reserved = 0;
312 
313 	nlattr_add_u32(nw, PF_ST_CREATORID, htonl(creator));
314 
315 	if (nlmsg_end(nw))
316 		return (0);
317 
318 enomem:
319 	nlmsg_abort(nw);
320 	return (ENOMEM);
321 }
322 
323 static int
324 pf_handle_getstates(struct nlmsghdr *hdr, struct nl_pstate *npt)
325 {
326 	int error;
327 
328 	struct nl_parsed_state attrs = {};
329 	error = nl_parse_nlmsg(hdr, &state_parser, npt, &attrs);
330 	if (error != 0)
331 		return (error);
332 
333 	if (attrs.id != 0)
334 		error = handle_getstate(npt->nlp, &attrs, hdr, npt);
335 	else
336 		error = handle_dumpstates(npt->nlp, &attrs, hdr, npt);
337 
338 	return (error);
339 }
340 
341 static int
342 pf_handle_getcreators(struct nlmsghdr *hdr, struct nl_pstate *npt)
343 {
344 	uint32_t creators[16];
345 	int error = 0;
346 
347 	bzero(creators, sizeof(creators));
348 
349 	for (int i = 0; i < V_pf_hashmask; i++) {
350 		struct pf_idhash *ih = &V_pf_idhash[i];
351 		struct pf_kstate *s;
352 
353 		if (LIST_EMPTY(&ih->states))
354 			continue;
355 
356 		PF_HASHROW_LOCK(ih);
357 		LIST_FOREACH(s, &ih->states, entry) {
358 			int j;
359 			if (s->timeout == PFTM_UNLINKED)
360 				continue;
361 
362 			for (j = 0; j < nitems(creators); j++) {
363 				if (creators[j] == s->creatorid)
364 					break;
365 				if (creators[j] == 0) {
366 					creators[j] = s->creatorid;
367 					break;
368 				}
369 			}
370 			if (j == nitems(creators))
371 				printf("Warning: too many creators!\n");
372 		}
373 		PF_HASHROW_UNLOCK(ih);
374 	}
375 
376 	hdr->nlmsg_flags |= NLM_F_MULTI;
377 	for (int i = 0; i < nitems(creators); i++) {
378 		if (creators[i] == 0)
379 			break;
380 		error = dump_creatorid(npt->nlp, hdr, creators[i], npt);
381 	}
382 
383 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
384 		NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
385 		return (ENOMEM);
386 	}
387 
388 	return (error);
389 }
390 
391 static int
392 pf_handle_start(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused)
393 {
394 	return (pf_start());
395 }
396 
397 static int
398 pf_handle_stop(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused)
399 {
400 	return (pf_stop());
401 }
402 
403 #define _OUT(_field)	offsetof(struct pf_addr_wrap, _field)
404 static const struct nlattr_parser nla_p_addr_wrap[] = {
405 	{ .type = PF_AT_ADDR, .off = _OUT(v.a.addr), .cb = nlattr_get_in6_addr },
406 	{ .type = PF_AT_MASK, .off = _OUT(v.a.mask), .cb = nlattr_get_in6_addr },
407 	{ .type = PF_AT_IFNAME, .off = _OUT(v.ifname), .arg = (void *)IFNAMSIZ,.cb = nlattr_get_chara },
408 	{ .type = PF_AT_TABLENAME, .off = _OUT(v.tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
409 	{ .type = PF_AT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
410 	{ .type = PF_AT_IFLAGS, .off = _OUT(iflags), .cb = nlattr_get_uint8 },
411 };
412 NL_DECLARE_ATTR_PARSER(addr_wrap_parser, nla_p_addr_wrap);
413 #undef _OUT
414 
415 static bool
416 nlattr_add_addr_wrap(struct nl_writer *nw, int attrtype, struct pf_addr_wrap *a)
417 {
418 	int off = nlattr_add_nested(nw, attrtype);
419 
420 	nlattr_add_in6_addr(nw, PF_AT_ADDR, &a->v.a.addr.v6);
421 	nlattr_add_in6_addr(nw, PF_AT_MASK, &a->v.a.mask.v6);
422 	nlattr_add_u8(nw, PF_AT_TYPE, a->type);
423 	nlattr_add_u8(nw, PF_AT_IFLAGS, a->iflags);
424 
425 	if (a->type == PF_ADDR_DYNIFTL) {
426 		nlattr_add_string(nw, PF_AT_IFNAME, a->v.ifname);
427 		nlattr_add_u32(nw, PF_AT_DYNCNT, a->p.dyncnt);
428 	} else if (a->type == PF_ADDR_TABLE) {
429 		nlattr_add_string(nw, PF_AT_TABLENAME, a->v.tblname);
430 		nlattr_add_u32(nw, PF_AT_TBLCNT, a->p.tblcnt);
431 	}
432 
433 	nlattr_set_len(nw, off);
434 
435 	return (true);
436 }
437 
438 #define _OUT(_field)	offsetof(struct pf_rule_addr, _field)
439 static const struct nlattr_parser nla_p_ruleaddr[] = {
440 	{ .type = PF_RAT_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = nlattr_get_nested },
441 	{ .type = PF_RAT_SRC_PORT, .off = _OUT(port[0]), .cb = nlattr_get_uint16 },
442 	{ .type = PF_RAT_DST_PORT, .off = _OUT(port[1]), .cb = nlattr_get_uint16 },
443 	{ .type = PF_RAT_NEG, .off = _OUT(neg), .cb = nlattr_get_uint8 },
444 	{ .type = PF_RAT_OP, .off = _OUT(port_op), .cb = nlattr_get_uint8 },
445 };
446 NL_DECLARE_ATTR_PARSER(rule_addr_parser, nla_p_ruleaddr);
447 #undef _OUT
448 
449 static bool
450 nlattr_add_rule_addr(struct nl_writer *nw, int attrtype, struct pf_rule_addr *r)
451 {
452 	struct pf_addr_wrap aw = {0};
453 	int off = nlattr_add_nested(nw, attrtype);
454 
455 	bcopy(&(r->addr), &aw, sizeof(struct pf_addr_wrap));
456 	pf_addr_copyout(&aw);
457 
458 	nlattr_add_addr_wrap(nw, PF_RAT_ADDR, &aw);
459 	nlattr_add_u16(nw, PF_RAT_SRC_PORT, r->port[0]);
460 	nlattr_add_u16(nw, PF_RAT_DST_PORT, r->port[1]);
461 	nlattr_add_u8(nw, PF_RAT_NEG, r->neg);
462 	nlattr_add_u8(nw, PF_RAT_OP, r->port_op);
463 
464 	nlattr_set_len(nw, off);
465 
466 	return (true);
467 }
468 
469 #define _OUT(_field)	offsetof(struct pf_mape_portset, _field)
470 static const struct nlattr_parser nla_p_mape_portset[] = {
471 	{ .type = PF_MET_OFFSET, .off = _OUT(offset), .cb = nlattr_get_uint8 },
472 	{ .type = PF_MET_PSID_LEN, .off = _OUT(psidlen), .cb = nlattr_get_uint8 },
473 	{. type = PF_MET_PSID, .off = _OUT(psid), .cb = nlattr_get_uint16 },
474 };
475 NL_DECLARE_ATTR_PARSER(mape_portset_parser, nla_p_mape_portset);
476 #undef _OUT
477 
478 static bool
479 nlattr_add_mape_portset(struct nl_writer *nw, int attrtype, const struct pf_mape_portset *m)
480 {
481 	int off = nlattr_add_nested(nw, attrtype);
482 
483 	nlattr_add_u8(nw, PF_MET_OFFSET, m->offset);
484 	nlattr_add_u8(nw, PF_MET_PSID_LEN, m->psidlen);
485 	nlattr_add_u16(nw, PF_MET_PSID, m->psid);
486 
487 	nlattr_set_len(nw, off);
488 
489 	return (true);
490 }
491 
492 struct nl_parsed_labels
493 {
494 	char		labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
495 	uint32_t	i;
496 };
497 
498 static int
499 nlattr_get_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt,
500     const void *arg, void *target)
501 {
502 	struct nl_parsed_labels *l = (struct nl_parsed_labels *)target;
503 	int ret;
504 
505 	if (l->i >= PF_RULE_MAX_LABEL_COUNT)
506 		return (E2BIG);
507 
508 	ret = nlattr_get_chara(nla, npt, (void *)PF_RULE_LABEL_SIZE,
509 	    l->labels[l->i]);
510 	if (ret == 0)
511 		l->i++;
512 
513 	return (ret);
514 }
515 
516 #define _OUT(_field)	offsetof(struct nl_parsed_labels, _field)
517 static const struct nlattr_parser nla_p_labels[] = {
518 	{ .type = PF_LT_LABEL, .off = 0, .cb = nlattr_get_pf_rule_labels },
519 };
520 NL_DECLARE_ATTR_PARSER(rule_labels_parser, nla_p_labels);
521 #undef _OUT
522 
523 static int
524 nlattr_get_nested_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
525 {
526 	struct nl_parsed_labels parsed_labels = { };
527 	int error;
528 
529 	/* Assumes target points to the beginning of the structure */
530 	error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &rule_labels_parser, npt, &parsed_labels);
531 	if (error != 0)
532 		return (error);
533 
534 	memcpy(target, parsed_labels.labels, sizeof(parsed_labels.labels));
535 
536 	return (0);
537 }
538 
539 static bool
540 nlattr_add_labels(struct nl_writer *nw, int attrtype, const struct pf_krule *r)
541 {
542 	int off = nlattr_add_nested(nw, attrtype);
543 	int i = 0;
544 
545 	while (r->label[i][0] != 0
546 	    && i < PF_RULE_MAX_LABEL_COUNT) {
547 		nlattr_add_string(nw, PF_LT_LABEL, r->label[i]);
548 		i++;
549 	}
550 
551 	nlattr_set_len(nw, off);
552 
553 	return (true);
554 }
555 
556 #define _OUT(_field)	offsetof(struct pf_kpool, _field)
557 static const struct nlattr_parser nla_p_pool[] = {
558 	{ .type = PF_PT_KEY, .off = _OUT(key), .arg = (void *)sizeof(struct pf_poolhashkey), .cb = nlattr_get_bytes },
559 	{ .type = PF_PT_COUNTER, .off = _OUT(counter), .cb = nlattr_get_in6_addr },
560 	{ .type = PF_PT_TBLIDX, .off = _OUT(tblidx), .cb = nlattr_get_uint32 },
561 	{ .type = PF_PT_PROXY_SRC_PORT, .off = _OUT(proxy_port[0]), .cb = nlattr_get_uint16 },
562 	{ .type = PF_PT_PROXY_DST_PORT, .off = _OUT(proxy_port[1]), .cb = nlattr_get_uint16 },
563 	{ .type = PF_PT_OPTS, .off = _OUT(opts), .cb = nlattr_get_uint8 },
564 	{ .type = PF_PT_MAPE, .off = _OUT(mape), .arg = &mape_portset_parser, .cb = nlattr_get_nested },
565 };
566 NL_DECLARE_ATTR_PARSER(pool_parser, nla_p_pool);
567 #undef _OUT
568 
569 static bool
570 nlattr_add_pool(struct nl_writer *nw, int attrtype, const struct pf_kpool *pool)
571 {
572 	int off = nlattr_add_nested(nw, attrtype);
573 
574 	nlattr_add(nw, PF_PT_KEY, sizeof(struct pf_poolhashkey), &pool->key);
575 	nlattr_add_in6_addr(nw, PF_PT_COUNTER, (const struct in6_addr *)&pool->counter);
576 	nlattr_add_u32(nw, PF_PT_TBLIDX, pool->tblidx);
577 	nlattr_add_u16(nw, PF_PT_PROXY_SRC_PORT, pool->proxy_port[0]);
578 	nlattr_add_u16(nw, PF_PT_PROXY_DST_PORT, pool->proxy_port[1]);
579 	nlattr_add_u8(nw, PF_PT_OPTS, pool->opts);
580 	nlattr_add_mape_portset(nw, PF_PT_MAPE, &pool->mape);
581 
582 	nlattr_set_len(nw, off);
583 
584 	return (true);
585 }
586 
587 #define _OUT(_field)	offsetof(struct pf_rule_uid, _field)
588 static const struct nlattr_parser nla_p_rule_uid[] = {
589 	{ .type = PF_RUT_UID_LOW, .off = _OUT(uid[0]), .cb = nlattr_get_uint32 },
590 	{ .type = PF_RUT_UID_HIGH, .off = _OUT(uid[1]), .cb = nlattr_get_uint32 },
591 	{ .type = PF_RUT_OP, .off = _OUT(op), .cb = nlattr_get_uint8 },
592 };
593 NL_DECLARE_ATTR_PARSER(rule_uid_parser, nla_p_rule_uid);
594 #undef _OUT
595 
596 static bool
597 nlattr_add_rule_uid(struct nl_writer *nw, int attrtype, const struct pf_rule_uid *u)
598 {
599 	int off = nlattr_add_nested(nw, attrtype);
600 
601 	nlattr_add_u32(nw, PF_RUT_UID_LOW, u->uid[0]);
602 	nlattr_add_u32(nw, PF_RUT_UID_HIGH, u->uid[1]);
603 	nlattr_add_u8(nw, PF_RUT_OP, u->op);
604 
605 	nlattr_set_len(nw, off);
606 
607 	return (true);
608 }
609 
610 struct nl_parsed_timeouts
611 {
612 	uint32_t	timeouts[PFTM_MAX];
613 	uint32_t	i;
614 };
615 
616 static int
617 nlattr_get_pf_timeout(struct nlattr *nla, struct nl_pstate *npt,
618     const void *arg, void *target)
619 {
620 	struct nl_parsed_timeouts *t = (struct nl_parsed_timeouts *)target;
621 	int ret;
622 
623 	if (t->i >= PFTM_MAX)
624 		return (E2BIG);
625 
626 	ret = nlattr_get_uint32(nla, npt, NULL, &t->timeouts[t->i]);
627 	if (ret == 0)
628 		t->i++;
629 
630 	return (ret);
631 }
632 
633 #define _OUT(_field)	offsetof(struct nl_parsed_timeout, _field)
634 static const struct nlattr_parser nla_p_timeouts[] = {
635 	{ .type = PF_TT_TIMEOUT, .off = 0, .cb = nlattr_get_pf_timeout },
636 };
637 NL_DECLARE_ATTR_PARSER(timeout_parser, nla_p_timeouts);
638 #undef _OUT
639 
640 static int
641 nlattr_get_nested_timeouts(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
642 {
643 	struct nl_parsed_timeouts parsed_timeouts = { };
644 	int error;
645 
646 	/* Assumes target points to the beginning of the structure */
647 	error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &timeout_parser, npt, &parsed_timeouts);
648 	if (error != 0)
649 		return (error);
650 
651 	memcpy(target, parsed_timeouts.timeouts, sizeof(parsed_timeouts.timeouts));
652 
653 	return (0);
654 }
655 
656 static bool
657 nlattr_add_timeout(struct nl_writer *nw, int attrtype, uint32_t *timeout)
658 {
659 	int off = nlattr_add_nested(nw, attrtype);
660 
661 	for (int i = 0; i < PFTM_MAX; i++)
662 		nlattr_add_u32(nw, PF_RT_TIMEOUT, timeout[i]);
663 
664 	nlattr_set_len(nw, off);
665 
666 	return (true);
667 }
668 
669 #define _OUT(_field)	offsetof(struct pf_krule, _field)
670 static const struct nlattr_parser nla_p_rule[] = {
671 	{ .type = PF_RT_SRC, .off = _OUT(src), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
672 	{ .type = PF_RT_DST, .off = _OUT(dst), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
673 	{ .type = PF_RT_RIDENTIFIER, .off = _OUT(ridentifier), .cb = nlattr_get_uint32 },
674 	{ .type = PF_RT_LABELS, .off = _OUT(label), .arg = &rule_labels_parser,.cb = nlattr_get_nested_pf_rule_labels },
675 	{ .type = PF_RT_IFNAME, .off = _OUT(ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
676 	{ .type = PF_RT_QNAME, .off = _OUT(qname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
677 	{ .type = PF_RT_PQNAME, .off = _OUT(pqname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
678 	{ .type = PF_RT_TAGNAME, .off = _OUT(tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
679 	{ .type = PF_RT_MATCH_TAGNAME, .off = _OUT(match_tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
680 	{ .type = PF_RT_OVERLOAD_TBLNAME, .off = _OUT(overload_tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
681 	{ .type = PF_RT_RPOOL, .off = _OUT(rpool), .arg = &pool_parser, .cb = nlattr_get_nested },
682 	{ .type = PF_RT_OS_FINGERPRINT, .off = _OUT(os_fingerprint), .cb = nlattr_get_uint32 },
683 	{ .type = PF_RT_RTABLEID, .off = _OUT(rtableid), .cb = nlattr_get_uint32 },
684 	{ .type = PF_RT_TIMEOUT, .off = _OUT(timeout), .arg = &timeout_parser, .cb = nlattr_get_nested_timeouts },
685 	{ .type = PF_RT_MAX_STATES, .off = _OUT(max_states), .cb = nlattr_get_uint32 },
686 	{ .type = PF_RT_MAX_SRC_NODES, .off = _OUT(max_src_nodes), .cb = nlattr_get_uint32 },
687 	{ .type = PF_RT_MAX_SRC_STATES, .off = _OUT(max_src_states), .cb = nlattr_get_uint32 },
688 	{ .type = PF_RT_MAX_SRC_CONN_RATE_LIMIT, .off = _OUT(max_src_conn_rate.limit), .cb = nlattr_get_uint32 },
689 	{ .type = PF_RT_MAX_SRC_CONN_RATE_SECS, .off = _OUT(max_src_conn_rate.seconds), .cb = nlattr_get_uint32 },
690 	{ .type = PF_RT_DNPIPE, .off = _OUT(dnpipe), .cb = nlattr_get_uint16 },
691 	{ .type = PF_RT_DNRPIPE, .off = _OUT(dnrpipe), .cb = nlattr_get_uint16 },
692 	{ .type = PF_RT_DNFLAGS, .off = _OUT(free_flags), .cb = nlattr_get_uint32 },
693 	{ .type = PF_RT_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
694 	{ .type = PF_RT_PROB, .off = _OUT(prob), .cb = nlattr_get_uint32 },
695 	{ .type = PF_RT_CUID, .off = _OUT(cuid), .cb = nlattr_get_uint32 },
696 	{. type = PF_RT_CPID, .off = _OUT(cpid), .cb = nlattr_get_uint32 },
697 	{ .type = PF_RT_RETURN_ICMP, .off = _OUT(return_icmp), .cb = nlattr_get_uint16 },
698 	{ .type = PF_RT_RETURN_ICMP6, .off = _OUT(return_icmp6), .cb = nlattr_get_uint16 },
699 	{ .type = PF_RT_MAX_MSS, .off = _OUT(max_mss), .cb = nlattr_get_uint16 },
700 	{ .type = PF_RT_SCRUB_FLAGS, .off = _OUT(scrub_flags), .cb = nlattr_get_uint16 },
701 	{ .type = PF_RT_UID, .off = _OUT(uid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
702 	{ .type = PF_RT_GID, .off = _OUT(gid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
703 	{ .type = PF_RT_RULE_FLAG, .off = _OUT(rule_flag), .cb = nlattr_get_uint32 },
704 	{ .type = PF_RT_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 },
705 	{ .type = PF_RT_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 },
706 	{ .type = PF_RT_LOG, .off = _OUT(log), .cb = nlattr_get_uint8 },
707 	{ .type = PF_RT_LOGIF, .off = _OUT(logif), .cb = nlattr_get_uint8 },
708 	{ .type = PF_RT_QUICK, .off = _OUT(quick), .cb = nlattr_get_uint8 },
709 	{ .type = PF_RT_IF_NOT, .off = _OUT(ifnot), .cb = nlattr_get_uint8 },
710 	{ .type = PF_RT_MATCH_TAG_NOT, .off = _OUT(match_tag_not), .cb = nlattr_get_uint8 },
711 	{ .type = PF_RT_NATPASS, .off = _OUT(natpass), .cb = nlattr_get_uint8 },
712 	{ .type = PF_RT_KEEP_STATE, .off = _OUT(keep_state), .cb = nlattr_get_uint8 },
713 	{ .type = PF_RT_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
714 	{ .type = PF_RT_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 },
715 	{ .type = PF_RT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
716 	{ .type = PF_RT_CODE, .off = _OUT(code), .cb = nlattr_get_uint8 },
717 	{ .type = PF_RT_FLAGS, .off = _OUT(flags), .cb = nlattr_get_uint8 },
718 	{ .type = PF_RT_FLAGSET, .off = _OUT(flagset), .cb = nlattr_get_uint8 },
719 	{ .type = PF_RT_MIN_TTL, .off = _OUT(min_ttl), .cb = nlattr_get_uint8 },
720 	{ .type = PF_RT_ALLOW_OPTS, .off = _OUT(allow_opts), .cb = nlattr_get_uint8 },
721 	{ .type = PF_RT_RT, .off = _OUT(rt), .cb = nlattr_get_uint8 },
722 	{ .type = PF_RT_RETURN_TTL, .off = _OUT(return_ttl), .cb = nlattr_get_uint8 },
723 	{ .type = PF_RT_TOS, .off = _OUT(tos), .cb = nlattr_get_uint8 },
724 	{ .type = PF_RT_SET_TOS, .off = _OUT(set_tos), .cb = nlattr_get_uint8 },
725 	{ .type = PF_RT_ANCHOR_RELATIVE, .off = _OUT(anchor_relative), .cb = nlattr_get_uint8 },
726 	{ .type = PF_RT_ANCHOR_WILDCARD, .off = _OUT(anchor_wildcard), .cb = nlattr_get_uint8 },
727 	{ .type = PF_RT_FLUSH, .off = _OUT(flush), .cb = nlattr_get_uint8 },
728 	{ .type = PF_RT_PRIO, .off = _OUT(prio), .cb = nlattr_get_uint8 },
729 	{ .type = PF_RT_SET_PRIO, .off = _OUT(set_prio[0]), .cb = nlattr_get_uint8 },
730 	{ .type = PF_RT_SET_PRIO_REPLY, .off = _OUT(set_prio[1]), .cb = nlattr_get_uint8 },
731 	{ .type = PF_RT_DIVERT_ADDRESS, .off = _OUT(divert.addr), .cb = nlattr_get_in6_addr },
732 	{ .type = PF_RT_DIVERT_PORT, .off = _OUT(divert.port), .cb = nlattr_get_uint16 },
733 };
734 NL_DECLARE_ATTR_PARSER(rule_parser, nla_p_rule);
735 #undef _OUT
736 struct nl_parsed_addrule {
737 	struct pf_krule	*rule;
738 	uint32_t	 ticket;
739 	uint32_t	 pool_ticket;
740 	char		*anchor;
741 	char		*anchor_call;
742 };
743 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
744 #define	_OUT(_field)	offsetof(struct nl_parsed_addrule, _field)
745 static const struct nlattr_parser nla_p_addrule[] = {
746 	{ .type = PF_ART_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
747 	{ .type = PF_ART_POOL_TICKET, .off = _OUT(pool_ticket), .cb = nlattr_get_uint32 },
748 	{ .type = PF_ART_ANCHOR, .off = _OUT(anchor), .cb = nlattr_get_string },
749 	{ .type = PF_ART_ANCHOR_CALL, .off = _OUT(anchor_call), .cb = nlattr_get_string },
750 	{ .type = PF_ART_RULE, .off = _OUT(rule), .arg = &rule_parser, .cb = nlattr_get_nested_ptr }
751 };
752 static const struct nlfield_parser nlf_p_addrule[] = {
753 };
754 #undef _IN
755 #undef _OUT
756 NL_DECLARE_PARSER(addrule_parser, struct genlmsghdr, nlf_p_addrule, nla_p_addrule);
757 
758 static int
759 pf_handle_addrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
760 {
761 	int error;
762 	struct nl_parsed_addrule attrs = {};
763 
764 	attrs.rule = pf_krule_alloc();
765 
766 	error = nl_parse_nlmsg(hdr, &addrule_parser, npt, &attrs);
767 	if (error != 0) {
768 		pf_free_rule(attrs.rule);
769 		return (error);
770 	}
771 
772 	error = pf_ioctl_addrule(attrs.rule, attrs.ticket, attrs.pool_ticket,
773 	    attrs.anchor, attrs.anchor_call, nlp_get_cred(npt->nlp)->cr_uid,
774 	    hdr->nlmsg_pid);
775 
776 	return (error);
777 }
778 
779 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
780 #define	_OUT(_field)	offsetof(struct pfioc_rule, _field)
781 static const struct nlattr_parser nla_p_getrules[] = {
782 	{ .type = PF_GR_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
783 	{ .type = PF_GR_ACTION, .off = _OUT(rule.action), .cb = nlattr_get_uint8 },
784 };
785 static const struct nlfield_parser nlf_p_getrules[] = {
786 };
787 #undef _IN
788 #undef _OUT
789 NL_DECLARE_PARSER(getrules_parser, struct genlmsghdr, nlf_p_getrules, nla_p_getrules);
790 
791 static int
792 pf_handle_getrules(struct nlmsghdr *hdr, struct nl_pstate *npt)
793 {
794 	struct pfioc_rule attrs = {};
795 	int error;
796 	struct nl_writer *nw = npt->nw;
797 	struct genlmsghdr *ghdr_new;
798 
799 	error = nl_parse_nlmsg(hdr, &getrules_parser, npt, &attrs);
800 	if (error != 0)
801 		return (error);
802 
803 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
804 		return (ENOMEM);
805 
806 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
807 	ghdr_new->cmd = PFNL_CMD_GETRULES;
808 	ghdr_new->version = 0;
809 	ghdr_new->reserved = 0;
810 
811 	error = pf_ioctl_getrules(&attrs);
812 	if (error != 0)
813 		goto out;
814 
815 	nlattr_add_u32(nw, PF_GR_NR, attrs.nr);
816 	nlattr_add_u32(nw, PF_GR_TICKET, attrs.ticket);
817 
818 	if (!nlmsg_end(nw)) {
819 		error = ENOMEM;
820 		goto out;
821 	}
822 
823 	return (0);
824 
825 out:
826 	nlmsg_abort(nw);
827 	return (error);
828 }
829 
830 struct nl_parsed_get_rule {
831 	char anchor[MAXPATHLEN];
832 	uint8_t action;
833 	uint32_t nr;
834 	uint32_t ticket;
835 	uint8_t clear;
836 };
837 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
838 #define	_OUT(_field)	offsetof(struct nl_parsed_get_rule, _field)
839 static const struct nlattr_parser nla_p_getrule[] = {
840 	{ .type = PF_GR_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
841 	{ .type = PF_GR_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 },
842 	{ .type = PF_GR_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
843 	{ .type = PF_GR_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
844 	{ .type = PF_GR_CLEAR, .off = _OUT(clear), .cb = nlattr_get_uint8 },
845 };
846 static const struct nlfield_parser nlf_p_getrule[] = {
847 };
848 #undef _IN
849 #undef _OUT
850 NL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, nlf_p_getrule, nla_p_getrule);
851 
852 static int
853 pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
854 {
855 	char				 anchor_call[MAXPATHLEN];
856 	struct nl_parsed_get_rule	 attrs = {};
857 	struct nl_writer		*nw = npt->nw;
858 	struct genlmsghdr		*ghdr_new;
859 	struct pf_kruleset		*ruleset;
860 	struct pf_krule			*rule;
861 	int				 rs_num;
862 	int				 error;
863 
864 	error = nl_parse_nlmsg(hdr, &getrule_parser, npt, &attrs);
865 	if (error != 0)
866 		return (error);
867 
868 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
869 		return (ENOMEM);
870 
871 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
872 	ghdr_new->cmd = PFNL_CMD_GETRULE;
873 	ghdr_new->version = 0;
874 	ghdr_new->reserved = 0;
875 
876 	PF_RULES_WLOCK();
877 	ruleset = pf_find_kruleset(attrs.anchor);
878 	if (ruleset == NULL) {
879 		PF_RULES_WUNLOCK();
880 		error = ENOENT;
881 		goto out;
882 	}
883 
884 	rs_num = pf_get_ruleset_number(attrs.action);
885 	if (rs_num >= PF_RULESET_MAX) {
886 		PF_RULES_WUNLOCK();
887 		error = EINVAL;
888 		goto out;
889 	}
890 
891 	if (attrs.ticket != ruleset->rules[rs_num].active.ticket) {
892 		PF_RULES_WUNLOCK();
893 		error = EBUSY;
894 		goto out;
895 	}
896 
897 	rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr);
898 	while ((rule != NULL) && (rule->nr != attrs.nr))
899 		rule = TAILQ_NEXT(rule, entries);
900 	if (rule == NULL) {
901 		PF_RULES_WUNLOCK();
902 		error = EBUSY;
903 		goto out;
904 	}
905 
906 	nlattr_add_rule_addr(nw, PF_RT_SRC, &rule->src);
907 	nlattr_add_rule_addr(nw, PF_RT_DST, &rule->dst);
908 	nlattr_add_u32(nw, PF_RT_RIDENTIFIER, rule->ridentifier);
909 	nlattr_add_labels(nw, PF_RT_LABELS, rule);
910 	nlattr_add_string(nw, PF_RT_IFNAME, rule->ifname);
911 	nlattr_add_string(nw, PF_RT_QNAME, rule->qname);
912 	nlattr_add_string(nw, PF_RT_PQNAME, rule->pqname);
913 	nlattr_add_string(nw, PF_RT_TAGNAME, rule->tagname);
914 	nlattr_add_string(nw, PF_RT_MATCH_TAGNAME, rule->match_tagname);
915 	nlattr_add_string(nw, PF_RT_OVERLOAD_TBLNAME, rule->overload_tblname);
916 	nlattr_add_pool(nw, PF_RT_RPOOL, &rule->rpool);
917 	nlattr_add_u32(nw, PF_RT_OS_FINGERPRINT, rule->os_fingerprint);
918 	nlattr_add_u32(nw, PF_RT_RTABLEID, rule->rtableid);
919 	nlattr_add_timeout(nw, PF_RT_TIMEOUT, rule->timeout);
920 	nlattr_add_u32(nw, PF_RT_MAX_STATES, rule->max_states);
921 	nlattr_add_u32(nw, PF_RT_MAX_SRC_NODES, rule->max_src_nodes);
922 	nlattr_add_u32(nw, PF_RT_MAX_SRC_STATES, rule->max_src_states);
923 	nlattr_add_u32(nw, PF_RT_MAX_SRC_CONN_RATE_LIMIT, rule->max_src_conn_rate.limit);
924 	nlattr_add_u32(nw, PF_RT_MAX_SRC_CONN_RATE_SECS, rule->max_src_conn_rate.seconds);
925 
926 	nlattr_add_u16(nw, PF_RT_DNPIPE, rule->dnpipe);
927 	nlattr_add_u16(nw, PF_RT_DNRPIPE, rule->dnrpipe);
928 	nlattr_add_u32(nw, PF_RT_DNFLAGS, rule->free_flags);
929 
930 	nlattr_add_u32(nw, PF_RT_NR, rule->nr);
931 	nlattr_add_u32(nw, PF_RT_PROB, rule->prob);
932 	nlattr_add_u32(nw, PF_RT_CUID, rule->cuid);
933 	nlattr_add_u32(nw, PF_RT_CPID, rule->cpid);
934 
935 	nlattr_add_u16(nw, PF_RT_RETURN_ICMP, rule->return_icmp);
936 	nlattr_add_u16(nw, PF_RT_RETURN_ICMP6, rule->return_icmp6);
937 	nlattr_add_u16(nw, PF_RT_RETURN_ICMP6, rule->return_icmp6);
938 	nlattr_add_u16(nw, PF_RT_MAX_MSS, rule->max_mss);
939 	nlattr_add_u16(nw, PF_RT_SCRUB_FLAGS, rule->scrub_flags);
940 
941 	nlattr_add_rule_uid(nw, PF_RT_UID, &rule->uid);
942 	nlattr_add_rule_uid(nw, PF_RT_GID, (const struct pf_rule_uid *)&rule->gid);
943 
944 	nlattr_add_u32(nw, PF_RT_RULE_FLAG, rule->rule_flag);
945 	nlattr_add_u8(nw, PF_RT_ACTION, rule->action);
946 	nlattr_add_u8(nw, PF_RT_DIRECTION, rule->direction);
947 	nlattr_add_u8(nw, PF_RT_LOG, rule->log);
948 	nlattr_add_u8(nw, PF_RT_LOGIF, rule->logif);
949 	nlattr_add_u8(nw, PF_RT_QUICK, rule->quick);
950 	nlattr_add_u8(nw, PF_RT_IF_NOT, rule->ifnot);
951 	nlattr_add_u8(nw, PF_RT_MATCH_TAG_NOT, rule->match_tag_not);
952 	nlattr_add_u8(nw, PF_RT_NATPASS, rule->natpass);
953 	nlattr_add_u8(nw, PF_RT_KEEP_STATE, rule->keep_state);
954 
955 	nlattr_add_u8(nw, PF_RT_AF, rule->af);
956 	nlattr_add_u8(nw, PF_RT_PROTO, rule->proto);
957 	nlattr_add_u8(nw, PF_RT_TYPE, rule->type);
958 	nlattr_add_u8(nw, PF_RT_CODE, rule->code);
959 	nlattr_add_u8(nw, PF_RT_FLAGS, rule->flags);
960 	nlattr_add_u8(nw, PF_RT_FLAGSET, rule->flagset);
961 	nlattr_add_u8(nw, PF_RT_MIN_TTL, rule->min_ttl);
962 	nlattr_add_u8(nw, PF_RT_ALLOW_OPTS, rule->allow_opts);
963 	nlattr_add_u8(nw, PF_RT_RT, rule->rt);
964 	nlattr_add_u8(nw, PF_RT_RETURN_TTL, rule->return_ttl);
965 	nlattr_add_u8(nw, PF_RT_TOS, rule->tos);
966 	nlattr_add_u8(nw, PF_RT_SET_TOS, rule->set_tos);
967 	nlattr_add_u8(nw, PF_RT_ANCHOR_RELATIVE, rule->anchor_relative);
968 	nlattr_add_u8(nw, PF_RT_ANCHOR_WILDCARD, rule->anchor_wildcard);
969 	nlattr_add_u8(nw, PF_RT_FLUSH, rule->flush);
970 	nlattr_add_u8(nw, PF_RT_PRIO, rule->prio);
971 	nlattr_add_u8(nw, PF_RT_SET_PRIO, rule->set_prio[0]);
972 	nlattr_add_u8(nw, PF_RT_SET_PRIO_REPLY, rule->set_prio[1]);
973 
974 	nlattr_add_in6_addr(nw, PF_RT_DIVERT_ADDRESS, &rule->divert.addr.v6);
975 	nlattr_add_u16(nw, PF_RT_DIVERT_PORT, rule->divert.port);
976 
977 	nlattr_add_u64(nw, PF_RT_PACKETS_IN, pf_counter_u64_fetch(&rule->packets[0]));
978 	nlattr_add_u64(nw, PF_RT_PACKETS_OUT, pf_counter_u64_fetch(&rule->packets[1]));
979 	nlattr_add_u64(nw, PF_RT_BYTES_IN, pf_counter_u64_fetch(&rule->bytes[0]));
980 	nlattr_add_u64(nw, PF_RT_BYTES_OUT, pf_counter_u64_fetch(&rule->bytes[1]));
981 	nlattr_add_u64(nw, PF_RT_EVALUATIONS, pf_counter_u64_fetch(&rule->evaluations));
982 	nlattr_add_u64(nw, PF_RT_TIMESTAMP, pf_get_timestamp(rule));
983 	nlattr_add_u64(nw, PF_RT_STATES_CUR, counter_u64_fetch(rule->states_cur));
984 	nlattr_add_u64(nw, PF_RT_STATES_TOTAL, counter_u64_fetch(rule->states_tot));
985 	nlattr_add_u64(nw, PF_RT_SRC_NODES, counter_u64_fetch(rule->src_nodes));
986 
987 	error = pf_kanchor_copyout(ruleset, rule, anchor_call, sizeof(anchor_call));
988 	MPASS(error == 0);
989 
990 	nlattr_add_string(nw, PF_RT_ANCHOR_CALL, anchor_call);
991 
992 	if (attrs.clear)
993 		pf_krule_clear_counters(rule);
994 
995 	PF_RULES_WUNLOCK();
996 
997 	if (!nlmsg_end(nw)) {
998 		error = ENOMEM;
999 		goto out;
1000 	}
1001 
1002 	return (0);
1003 out:
1004 	nlmsg_abort(nw);
1005 	return (error);
1006 }
1007 
1008 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1009 #define	_OUT(_field)	offsetof(struct pf_kstate_kill, _field)
1010 static const struct nlattr_parser nla_p_clear_states[] = {
1011 	{ .type = PF_CS_CMP_ID, .off = _OUT(psk_pfcmp.id), .cb = nlattr_get_uint64 },
1012 	{ .type = PF_CS_CMP_CREATORID, .off = _OUT(psk_pfcmp.creatorid), .cb = nlattr_get_uint32 },
1013 	{ .type = PF_CS_CMP_DIR, .off = _OUT(psk_pfcmp.direction), .cb = nlattr_get_uint8 },
1014 	{ .type = PF_CS_AF, .off = _OUT(psk_af), .cb = nlattr_get_uint8 },
1015 	{ .type = PF_CS_PROTO, .off = _OUT(psk_proto), .cb = nlattr_get_uint8 },
1016 	{ .type = PF_CS_SRC, .off = _OUT(psk_src), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
1017 	{ .type = PF_CS_DST, .off = _OUT(psk_dst), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
1018 	{ .type = PF_CS_RT_ADDR, .off = _OUT(psk_rt_addr), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
1019 	{ .type = PF_CS_IFNAME, .off = _OUT(psk_ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
1020 	{ .type = PF_CS_LABEL, .off = _OUT(psk_label), .arg = (void *)PF_RULE_LABEL_SIZE, .cb = nlattr_get_chara },
1021 	{ .type = PF_CS_KILL_MATCH, .off = _OUT(psk_kill_match), .cb = nlattr_get_bool },
1022 	{ .type = PF_CS_NAT, .off = _OUT(psk_nat), .cb = nlattr_get_bool },
1023 };
1024 static const struct nlfield_parser nlf_p_clear_states[] = {};
1025 #undef _IN
1026 #undef _OUT
1027 NL_DECLARE_PARSER(clear_states_parser, struct genlmsghdr, nlf_p_clear_states, nla_p_clear_states);
1028 
1029 static int
1030 pf_handle_killclear_states(struct nlmsghdr *hdr, struct nl_pstate *npt, int cmd)
1031 {
1032 	struct pf_kstate_kill		 kill = {};
1033 	struct epoch_tracker		 et;
1034 	struct nl_writer		*nw = npt->nw;
1035 	struct genlmsghdr		*ghdr_new;
1036 	int				 error;
1037 	unsigned int			 killed = 0;
1038 
1039 	error = nl_parse_nlmsg(hdr, &clear_states_parser, npt, &kill);
1040 	if (error != 0)
1041 		return (error);
1042 
1043 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1044 		return (ENOMEM);
1045 
1046 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1047 	ghdr_new->cmd = cmd;
1048 	ghdr_new->version = 0;
1049 	ghdr_new->reserved = 0;
1050 
1051 	NET_EPOCH_ENTER(et);
1052 	if (cmd == PFNL_CMD_KILLSTATES)
1053 		pf_killstates(&kill, &killed);
1054 	else
1055 		killed = pf_clear_states(&kill);
1056 	NET_EPOCH_EXIT(et);
1057 
1058 	nlattr_add_u32(nw, PF_CS_KILLED, killed);
1059 
1060 	if (! nlmsg_end(nw)) {
1061 		error = ENOMEM;
1062 		goto out;
1063 	}
1064 
1065 	return (0);
1066 
1067 out:
1068 	nlmsg_abort(nw);
1069 	return (error);
1070 }
1071 
1072 static int
1073 pf_handle_clear_states(struct nlmsghdr *hdr, struct nl_pstate *npt)
1074 {
1075 	return (pf_handle_killclear_states(hdr, npt, PFNL_CMD_CLRSTATES));
1076 }
1077 
1078 static int
1079 pf_handle_kill_states(struct nlmsghdr *hdr, struct nl_pstate *npt)
1080 {
1081 	return (pf_handle_killclear_states(hdr, npt, PFNL_CMD_KILLSTATES));
1082 }
1083 
1084 struct nl_parsed_set_statusif {
1085 	char ifname[IFNAMSIZ];
1086 };
1087 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1088 #define	_OUT(_field)	offsetof(struct nl_parsed_set_statusif, _field)
1089 static const struct nlattr_parser nla_p_set_statusif[] = {
1090 	{ .type = PF_SS_IFNAME, .off = _OUT(ifname), .arg = (const void *)IFNAMSIZ, .cb = nlattr_get_chara },
1091 };
1092 static const struct nlfield_parser nlf_p_set_statusif[] = {};
1093 #undef _IN
1094 #undef _OUT
1095 NL_DECLARE_PARSER(set_statusif_parser, struct genlmsghdr, nlf_p_set_statusif, nla_p_set_statusif);
1096 
1097 static int
1098 pf_handle_set_statusif(struct nlmsghdr *hdr, struct nl_pstate *npt)
1099 {
1100 	int error;
1101 	struct nl_parsed_set_statusif attrs = {};
1102 
1103 	error = nl_parse_nlmsg(hdr, &set_statusif_parser, npt, &attrs);
1104 	if (error != 0)
1105 		return (error);
1106 
1107 	PF_RULES_WLOCK();
1108 	strlcpy(V_pf_status.ifname, attrs.ifname, IFNAMSIZ);
1109 	PF_RULES_WUNLOCK();
1110 
1111 	return (0);
1112 }
1113 
1114 static bool
1115 nlattr_add_counters(struct nl_writer *nw, int attr, size_t number, char **names,
1116     counter_u64_t *counters)
1117 {
1118 	for (int i = 0; i < number; i++) {
1119 		int off = nlattr_add_nested(nw, attr);
1120 		nlattr_add_u32(nw, PF_C_ID, i);
1121 		nlattr_add_string(nw, PF_C_NAME, names[i]);
1122 		nlattr_add_u64(nw, PF_C_COUNTER, counter_u64_fetch(counters[i]));
1123 		nlattr_set_len(nw, off);
1124 	}
1125 
1126 	return (true);
1127 }
1128 
1129 static bool
1130 nlattr_add_fcounters(struct nl_writer *nw, int attr, size_t number, char **names,
1131     struct pf_counter_u64 *counters)
1132 {
1133 	for (int i = 0; i < number; i++) {
1134 		int off = nlattr_add_nested(nw, attr);
1135 		nlattr_add_u32(nw, PF_C_ID, i);
1136 		nlattr_add_string(nw, PF_C_NAME, names[i]);
1137 		nlattr_add_u64(nw, PF_C_COUNTER, pf_counter_u64_fetch(&counters[i]));
1138 		nlattr_set_len(nw, off);
1139 	}
1140 
1141 	return (true);
1142 }
1143 
1144 static bool
1145 nlattr_add_u64_array(struct nl_writer *nw, int attr, size_t number, uint64_t *array)
1146 {
1147 	int off = nlattr_add_nested(nw, attr);
1148 
1149 	for (size_t i = 0; i < number; i++)
1150 		nlattr_add_u64(nw, 0, array[i]);
1151 
1152 	nlattr_set_len(nw, off);
1153 
1154 	return (true);
1155 }
1156 
1157 static int
1158 pf_handle_get_status(struct nlmsghdr *hdr, struct nl_pstate *npt)
1159 {
1160 	struct pf_status s;
1161 	struct nl_writer *nw = npt->nw;
1162 	struct genlmsghdr *ghdr_new;
1163 	char *pf_reasons[PFRES_MAX+1] = PFRES_NAMES;
1164 	char *pf_lcounter[KLCNT_MAX+1] = KLCNT_NAMES;
1165 	char *pf_fcounter[FCNT_MAX+1] = FCNT_NAMES;
1166 	int error;
1167 
1168 	PF_RULES_RLOCK_TRACKER;
1169 
1170 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1171 		return (ENOMEM);
1172 
1173 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1174 	ghdr_new->cmd = PFNL_CMD_GET_STATUS;
1175 	ghdr_new->version = 0;
1176 	ghdr_new->reserved = 0;
1177 
1178 	PF_RULES_RLOCK();
1179 
1180 	nlattr_add_string(nw, PF_GS_IFNAME, V_pf_status.ifname);
1181 	nlattr_add_bool(nw, PF_GS_RUNNING, V_pf_status.running);
1182 	nlattr_add_u32(nw, PF_GS_SINCE, V_pf_status.since);
1183 	nlattr_add_u32(nw, PF_GS_DEBUG, V_pf_status.debug);
1184 	nlattr_add_u32(nw, PF_GS_HOSTID, ntohl(V_pf_status.hostid));
1185 	nlattr_add_u32(nw, PF_GS_STATES, V_pf_status.states);
1186 	nlattr_add_u32(nw, PF_GS_SRC_NODES, V_pf_status.src_nodes);
1187 	nlattr_add_u32(nw, PF_GS_REASSEMBLE, V_pf_status.reass);
1188 	nlattr_add_u32(nw, PF_GS_SYNCOOKIES_ACTIVE, V_pf_status.syncookies_active);
1189 
1190 	nlattr_add_counters(nw, PF_GS_COUNTERS, PFRES_MAX, pf_reasons,
1191 	    V_pf_status.counters);
1192 	nlattr_add_counters(nw, PF_GS_LCOUNTERS, KLCNT_MAX, pf_lcounter,
1193 	    V_pf_status.lcounters);
1194 	nlattr_add_fcounters(nw, PF_GS_FCOUNTERS, FCNT_MAX, pf_fcounter,
1195 	    V_pf_status.fcounters);
1196 	nlattr_add_counters(nw, PF_GS_SCOUNTERS, SCNT_MAX, pf_fcounter,
1197 	    V_pf_status.scounters);
1198 
1199 	pfi_update_status(V_pf_status.ifname, &s);
1200 	nlattr_add_u64_array(nw, PF_GS_BCOUNTERS, 2 * 2, (uint64_t *)s.bcounters);
1201 	nlattr_add_u64_array(nw, PF_GS_PCOUNTERS, 2 * 2 * 2, (uint64_t *)s.pcounters);
1202 
1203 	nlattr_add(nw, PF_GS_CHKSUM, PF_MD5_DIGEST_LENGTH, V_pf_status.pf_chksum);
1204 
1205 	PF_RULES_RUNLOCK();
1206 
1207 	if (!nlmsg_end(nw)) {
1208 		error = ENOMEM;
1209 		goto out;
1210 	}
1211 
1212 	return (0);
1213 
1214 out:
1215 	nlmsg_abort(nw);
1216 	return (error);
1217 }
1218 
1219 static int
1220 pf_handle_clear_status(struct nlmsghdr *hdr, struct nl_pstate *npt)
1221 {
1222 	pf_ioctl_clear_status();
1223 
1224 	return (0);
1225 }
1226 
1227 struct pf_nl_natlook {
1228 	sa_family_t af;
1229 	uint8_t direction;
1230 	uint8_t proto;
1231 	struct pf_addr src;
1232 	struct pf_addr dst;
1233 	uint16_t sport;
1234 	uint16_t dport;
1235 };
1236 
1237 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1238 #define	_OUT(_field)	offsetof(struct pf_nl_natlook, _field)
1239 static const struct nlattr_parser nla_p_natlook[] = {
1240 	{ .type = PF_NL_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
1241 	{ .type = PF_NL_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 },
1242 	{ .type = PF_NL_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 },
1243 	{ .type = PF_NL_SRC_ADDR, .off = _OUT(src), .cb = nlattr_get_in6_addr },
1244 	{ .type = PF_NL_DST_ADDR, .off = _OUT(dst), .cb = nlattr_get_in6_addr },
1245 	{ .type = PF_NL_SRC_PORT, .off = _OUT(sport), .cb = nlattr_get_uint16 },
1246 	{ .type = PF_NL_DST_PORT, .off = _OUT(dport), .cb = nlattr_get_uint16 },
1247 };
1248 static const struct nlfield_parser nlf_p_natlook[] = {};
1249 #undef _IN
1250 #undef _OUT
1251 NL_DECLARE_PARSER(natlook_parser, struct genlmsghdr, nlf_p_natlook, nla_p_natlook);
1252 
1253 static int
1254 pf_handle_natlook(struct nlmsghdr *hdr, struct nl_pstate *npt)
1255 {
1256 	struct pf_nl_natlook	 attrs = {};
1257 	struct pf_state_key_cmp	 key = {};
1258 	struct nl_writer	*nw = npt->nw;
1259 	struct pf_state_key	*sk;
1260 	struct pf_kstate	*state;
1261 	struct genlmsghdr	*ghdr_new;
1262 	int			 error, m;
1263 	int			 sidx, didx;
1264 
1265 	error = nl_parse_nlmsg(hdr, &natlook_parser, npt, &attrs);
1266 	if (error != 0)
1267 		return (error);
1268 
1269 	if (attrs.proto == 0 ||
1270 	    PF_AZERO(&attrs.src, attrs.af) ||
1271 	    PF_AZERO(&attrs.dst, attrs.af) ||
1272 	    ((attrs.proto == IPPROTO_TCP || attrs.proto == IPPROTO_UDP) &&
1273 	     (attrs.sport == 0 || attrs.dport == 0)))
1274 		return (EINVAL);
1275 
1276 	/* NATLOOK src and dst are reversed, so reverse sidx/didx */
1277 	sidx = (attrs.direction == PF_IN) ? 1 : 0;
1278 	didx = (attrs.direction == PF_IN) ? 0 : 1;
1279 
1280 	key.af = attrs.af;
1281 	key.proto = attrs.proto;
1282 	PF_ACPY(&key.addr[sidx], &attrs.src, attrs.af);
1283 	key.port[sidx] = attrs.sport;
1284 	PF_ACPY(&key.addr[didx], &attrs.dst, attrs.af);
1285 	key.port[didx] = attrs.dport;
1286 
1287 	state = pf_find_state_all(&key, attrs.direction, &m);
1288 	if (state == NULL)
1289 		return (ENOENT);
1290 	if (m > 1) {
1291 		PF_STATE_UNLOCK(state);
1292 		return (E2BIG);
1293 	}
1294 
1295 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
1296 		PF_STATE_UNLOCK(state);
1297 		return (ENOMEM);
1298 	}
1299 
1300 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1301 	ghdr_new->cmd = PFNL_CMD_NATLOOK;
1302 	ghdr_new->version = 0;
1303 	ghdr_new->reserved = 0;
1304 
1305 	sk = state->key[sidx];
1306 
1307 	nlattr_add_in6_addr(nw, PF_NL_SRC_ADDR, &sk->addr[sidx].v6);
1308 	nlattr_add_in6_addr(nw, PF_NL_DST_ADDR, &sk->addr[didx].v6);
1309 	nlattr_add_u16(nw, PF_NL_SRC_PORT, sk->port[sidx]);
1310 	nlattr_add_u16(nw, PF_NL_DST_PORT, sk->port[didx]);
1311 
1312 	PF_STATE_UNLOCK(state);
1313 
1314 	if (!nlmsg_end(nw)) {
1315 		nlmsg_abort(nw);
1316 		return (ENOMEM);
1317 	}
1318 
1319 	return (0);
1320 }
1321 
1322 struct pf_nl_set_debug
1323 {
1324 	uint32_t level;
1325 };
1326 #define	_OUT(_field)	offsetof(struct pf_nl_set_debug, _field)
1327 static const struct nlattr_parser nla_p_set_debug[] = {
1328 	{ .type = PF_SD_LEVEL, .off = _OUT(level), .cb = nlattr_get_uint32 },
1329 };
1330 static const struct nlfield_parser nlf_p_set_debug[] = {};
1331 #undef _OUT
1332 NL_DECLARE_PARSER(set_debug_parser, struct genlmsghdr, nlf_p_set_debug, nla_p_set_debug);
1333 
1334 static int
1335 pf_handle_set_debug(struct nlmsghdr *hdr, struct nl_pstate *npt)
1336 {
1337 	struct pf_nl_set_debug attrs = {};
1338 	int error;
1339 
1340 	error = nl_parse_nlmsg(hdr, &set_debug_parser, npt, &attrs);
1341 	if (error != 0)
1342 		return (error);
1343 
1344 	PF_RULES_WLOCK();
1345 	V_pf_status.debug = attrs.level;
1346 	PF_RULES_WUNLOCK();
1347 
1348 	return (0);
1349 }
1350 
1351 struct pf_nl_set_timeout
1352 {
1353 	uint32_t timeout;
1354 	uint32_t seconds;
1355 };
1356 #define	_OUT(_field)	offsetof(struct pf_nl_set_timeout, _field)
1357 static const struct nlattr_parser nla_p_set_timeout[] = {
1358 	{ .type = PF_TO_TIMEOUT, .off = _OUT(timeout), .cb = nlattr_get_uint32 },
1359 	{ .type = PF_TO_SECONDS, .off = _OUT(seconds), .cb = nlattr_get_uint32 },
1360 };
1361 static const struct nlfield_parser nlf_p_set_timeout[] = {};
1362 #undef _OUT
1363 NL_DECLARE_PARSER(set_timeout_parser, struct genlmsghdr, nlf_p_set_timeout, nla_p_set_timeout);
1364 
1365 static int
1366 pf_handle_set_timeout(struct nlmsghdr *hdr, struct nl_pstate *npt)
1367 {
1368 	struct pf_nl_set_timeout attrs = {};
1369 	int error;
1370 
1371 	error = nl_parse_nlmsg(hdr, &set_timeout_parser, npt, &attrs);
1372 	if (error != 0)
1373 		return (error);
1374 
1375 	return (pf_ioctl_set_timeout(attrs.timeout, attrs.seconds, NULL));
1376 }
1377 
1378 static int
1379 pf_handle_get_timeout(struct nlmsghdr *hdr, struct nl_pstate *npt)
1380 {
1381 	struct pf_nl_set_timeout attrs = {};
1382 	struct nl_writer *nw = npt->nw;
1383 	struct genlmsghdr *ghdr_new;
1384 	int error;
1385 
1386 	error = nl_parse_nlmsg(hdr, &set_timeout_parser, npt, &attrs);
1387 	if (error != 0)
1388 		return (error);
1389 
1390 	error = pf_ioctl_get_timeout(attrs.timeout, &attrs.seconds);
1391 	if (error != 0)
1392 		return (error);
1393 
1394 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1395 		return (ENOMEM);
1396 
1397 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1398 	ghdr_new->cmd = PFNL_CMD_GET_TIMEOUT;
1399 	ghdr_new->version = 0;
1400 	ghdr_new->reserved = 0;
1401 
1402 	nlattr_add_u32(nw, PF_TO_SECONDS, attrs.seconds);
1403 
1404 	if (!nlmsg_end(nw)) {
1405 		nlmsg_abort(nw);
1406 		return (ENOMEM);
1407 	}
1408 
1409 	return (0);
1410 }
1411 
1412 struct pf_nl_set_limit
1413 {
1414 	uint32_t index;
1415 	uint32_t limit;
1416 };
1417 #define	_OUT(_field)	offsetof(struct pf_nl_set_limit, _field)
1418 static const struct nlattr_parser nla_p_set_limit[] = {
1419 	{ .type = PF_LI_INDEX, .off = _OUT(index), .cb = nlattr_get_uint32 },
1420 	{ .type = PF_LI_LIMIT, .off = _OUT(limit), .cb = nlattr_get_uint32 },
1421 };
1422 static const struct nlfield_parser nlf_p_set_limit[] = {};
1423 #undef _OUT
1424 NL_DECLARE_PARSER(set_limit_parser, struct genlmsghdr, nlf_p_set_limit, nla_p_set_limit);
1425 
1426 static int
1427 pf_handle_set_limit(struct nlmsghdr *hdr, struct nl_pstate *npt)
1428 {
1429 	struct pf_nl_set_limit attrs = {};
1430 	int error;
1431 
1432 	error = nl_parse_nlmsg(hdr, &set_limit_parser, npt, &attrs);
1433 	if (error != 0)
1434 		return (error);
1435 
1436 	return (pf_ioctl_set_limit(attrs.index, attrs.limit, NULL));
1437 }
1438 
1439 static int
1440 pf_handle_get_limit(struct nlmsghdr *hdr, struct nl_pstate *npt)
1441 {
1442 	struct pf_nl_set_limit attrs = {};
1443 	struct nl_writer *nw = npt->nw;
1444 	struct genlmsghdr *ghdr_new;
1445 	int error;
1446 
1447 	error = nl_parse_nlmsg(hdr, &set_limit_parser, npt, &attrs);
1448 	if (error != 0)
1449 		return (error);
1450 
1451 	error = pf_ioctl_get_limit(attrs.index, &attrs.limit);
1452 	if (error != 0)
1453 		return (error);
1454 
1455 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1456 		return (ENOMEM);
1457 
1458 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1459 	ghdr_new->cmd = PFNL_CMD_GET_LIMIT;
1460 	ghdr_new->version = 0;
1461 	ghdr_new->reserved = 0;
1462 
1463 	nlattr_add_u32(nw, PF_LI_LIMIT, attrs.limit);
1464 
1465 	if (!nlmsg_end(nw)) {
1466 		nlmsg_abort(nw);
1467 		return (ENOMEM);
1468 	}
1469 
1470 	return (0);
1471 }
1472 
1473 static int
1474 pf_handle_begin_addrs(struct nlmsghdr *hdr, struct nl_pstate *npt)
1475 {
1476 	struct nl_writer *nw = npt->nw;
1477 	struct genlmsghdr *ghdr_new;
1478 	uint32_t ticket;
1479 	int error;
1480 
1481 	error = pf_ioctl_begin_addrs(&ticket);
1482 	if (error != 0)
1483 		return (error);
1484 
1485 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1486 		return (ENOMEM);
1487 
1488 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1489 	ghdr_new->cmd = PFNL_CMD_BEGIN_ADDRS;
1490 	ghdr_new->version = 0;
1491 	ghdr_new->reserved = 0;
1492 
1493 	nlattr_add_u32(nw, PF_BA_TICKET, ticket);
1494 
1495 	if (!nlmsg_end(nw)) {
1496 		nlmsg_abort(nw);
1497 		return (ENOMEM);
1498 	}
1499 
1500 	return (0);
1501 }
1502 
1503 static bool
1504 nlattr_add_pool_addr(struct nl_writer *nw, int attrtype, struct pf_pooladdr *a)
1505 {
1506 	int off;
1507 
1508 	off = nlattr_add_nested(nw, attrtype);
1509 
1510 	nlattr_add_addr_wrap(nw, PF_PA_ADDR, &a->addr);
1511 	nlattr_add_string(nw, PF_PA_IFNAME, a->ifname);
1512 
1513 	nlattr_set_len(nw, off);
1514 
1515 	return (true);
1516 }
1517 
1518 #define _OUT(_field)	offsetof(struct pf_pooladdr, _field)
1519 static const struct nlattr_parser nla_p_pool_addr[] = {
1520 	{ .type = PF_PA_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = nlattr_get_nested },
1521 	{ .type = PF_PA_IFNAME, .off = _OUT(ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
1522 };
1523 NL_DECLARE_ATTR_PARSER(pool_addr_parser, nla_p_pool_addr);
1524 #undef _OUT
1525 
1526 #define	_OUT(_field)	offsetof(struct pfioc_pooladdr, _field)
1527 static const struct nlattr_parser nla_p_add_addr[] = {
1528 	{ .type = PF_AA_ACTION, .off = _OUT(action), .cb = nlattr_get_uint32 },
1529 	{ .type = PF_AA_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
1530 	{ .type = PF_AA_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
1531 	{ .type = PF_AA_R_NUM, .off = _OUT(r_num), .cb = nlattr_get_uint32 },
1532 	{ .type = PF_AA_R_ACTION, .off = _OUT(r_action), .cb = nlattr_get_uint8 },
1533 	{ .type = PF_AA_R_LAST, .off = _OUT(r_last), .cb = nlattr_get_uint8 },
1534 	{ .type = PF_AA_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
1535 	{ .type = PF_AA_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
1536 	{ .type = PF_AA_ADDR, .off = _OUT(addr), .arg = &pool_addr_parser, .cb = nlattr_get_nested },
1537 };
1538 static const struct nlfield_parser nlf_p_add_addr[] = {};
1539 #undef _OUT
1540 NL_DECLARE_PARSER(add_addr_parser, struct genlmsghdr, nlf_p_add_addr, nla_p_add_addr);
1541 
1542 static int
1543 pf_handle_add_addr(struct nlmsghdr *hdr, struct nl_pstate *npt)
1544 {
1545 	struct pfioc_pooladdr attrs = { 0 };
1546 	int error;
1547 
1548 	error = nl_parse_nlmsg(hdr, &add_addr_parser, npt, &attrs);
1549 	if (error != 0)
1550 		return (error);
1551 
1552 	error = pf_ioctl_add_addr(&attrs);
1553 
1554 	return (error);
1555 }
1556 
1557 static int
1558 pf_handle_get_addrs(struct nlmsghdr *hdr, struct nl_pstate *npt)
1559 {
1560 	struct pfioc_pooladdr attrs = { 0 };
1561 	struct nl_writer *nw = npt->nw;
1562 	struct genlmsghdr *ghdr_new;
1563 	int error;
1564 
1565 	error = nl_parse_nlmsg(hdr, &add_addr_parser, npt, &attrs);
1566 	if (error != 0)
1567 		return (error);
1568 
1569 	error = pf_ioctl_get_addrs(&attrs);
1570 	if (error != 0)
1571 		return (error);
1572 
1573 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1574 		return (ENOMEM);
1575 
1576 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1577 	ghdr_new->cmd = PFNL_CMD_GET_ADDRS;
1578 	ghdr_new->version = 0;
1579 	ghdr_new->reserved = 0;
1580 
1581 	nlattr_add_u32(nw, PF_AA_NR, attrs.nr);
1582 
1583 	if (!nlmsg_end(nw)) {
1584 		nlmsg_abort(nw);
1585 		return (ENOMEM);
1586 	}
1587 
1588 	return (error);
1589 }
1590 
1591 static int
1592 pf_handle_get_addr(struct nlmsghdr *hdr, struct nl_pstate *npt)
1593 {
1594 	struct pfioc_pooladdr attrs = { 0 };
1595 	struct nl_writer *nw = npt->nw;
1596 	struct genlmsghdr *ghdr_new;
1597 	int error;
1598 
1599 	error = nl_parse_nlmsg(hdr, &add_addr_parser, npt, &attrs);
1600 	if (error != 0)
1601 		return (error);
1602 
1603 	error = pf_ioctl_get_addr(&attrs);
1604 	if (error != 0)
1605 		return (error);
1606 
1607 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1608 		return (ENOMEM);
1609 
1610 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1611 	ghdr_new->cmd = PFNL_CMD_GET_ADDRS;
1612 	ghdr_new->version = 0;
1613 	ghdr_new->reserved = 0;
1614 
1615 	nlattr_add_u32(nw, PF_AA_ACTION, attrs.action);
1616 	nlattr_add_u32(nw, PF_AA_TICKET, attrs.ticket);
1617 	nlattr_add_u32(nw, PF_AA_NR, attrs.nr);
1618 	nlattr_add_u32(nw, PF_AA_R_NUM, attrs.r_num);
1619 	nlattr_add_u8(nw, PF_AA_R_ACTION, attrs.r_action);
1620 	nlattr_add_u8(nw, PF_AA_R_LAST, attrs.r_last);
1621 	nlattr_add_u8(nw, PF_AA_AF, attrs.af);
1622 	nlattr_add_string(nw, PF_AA_ANCHOR, attrs.anchor);
1623 	nlattr_add_pool_addr(nw, PF_AA_ADDR, &attrs.addr);
1624 
1625 	if (!nlmsg_end(nw)) {
1626 		nlmsg_abort(nw);
1627 		return (ENOMEM);
1628 	}
1629 
1630 	return (0);
1631 }
1632 
1633 static const struct nlhdr_parser *all_parsers[] = {
1634 	&state_parser,
1635 	&addrule_parser,
1636 	&getrules_parser,
1637 	&clear_states_parser,
1638 	&set_statusif_parser,
1639 	&natlook_parser,
1640 	&set_debug_parser,
1641 	&set_timeout_parser,
1642 	&set_limit_parser,
1643 	&pool_addr_parser,
1644 	&add_addr_parser,
1645 };
1646 
1647 static int family_id;
1648 
1649 static const struct genl_cmd pf_cmds[] = {
1650 	{
1651 		.cmd_num = PFNL_CMD_GETSTATES,
1652 		.cmd_name = "GETSTATES",
1653 		.cmd_cb = pf_handle_getstates,
1654 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1655 		.cmd_priv = PRIV_NETINET_PF,
1656 	},
1657 	{
1658 		.cmd_num = PFNL_CMD_GETCREATORS,
1659 		.cmd_name = "GETCREATORS",
1660 		.cmd_cb = pf_handle_getcreators,
1661 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1662 		.cmd_priv = PRIV_NETINET_PF,
1663 	},
1664 	{
1665 		.cmd_num = PFNL_CMD_START,
1666 		.cmd_name = "START",
1667 		.cmd_cb = pf_handle_start,
1668 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1669 		.cmd_priv = PRIV_NETINET_PF,
1670 	},
1671 	{
1672 		.cmd_num = PFNL_CMD_STOP,
1673 		.cmd_name = "STOP",
1674 		.cmd_cb = pf_handle_stop,
1675 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1676 		.cmd_priv = PRIV_NETINET_PF,
1677 	},
1678 	{
1679 		.cmd_num = PFNL_CMD_ADDRULE,
1680 		.cmd_name = "ADDRULE",
1681 		.cmd_cb = pf_handle_addrule,
1682 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1683 		.cmd_priv = PRIV_NETINET_PF,
1684 	},
1685 	{
1686 		.cmd_num = PFNL_CMD_GETRULES,
1687 		.cmd_name = "GETRULES",
1688 		.cmd_cb = pf_handle_getrules,
1689 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1690 		.cmd_priv = PRIV_NETINET_PF,
1691 	},
1692 	{
1693 		.cmd_num = PFNL_CMD_GETRULE,
1694 		.cmd_name = "GETRULE",
1695 		.cmd_cb = pf_handle_getrule,
1696 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1697 		.cmd_priv = PRIV_NETINET_PF,
1698 	},
1699 	{
1700 		.cmd_num = PFNL_CMD_CLRSTATES,
1701 		.cmd_name = "CLRSTATES",
1702 		.cmd_cb = pf_handle_clear_states,
1703 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1704 		.cmd_priv = PRIV_NETINET_PF,
1705 	},
1706 	{
1707 		.cmd_num = PFNL_CMD_KILLSTATES,
1708 		.cmd_name = "KILLSTATES",
1709 		.cmd_cb = pf_handle_kill_states,
1710 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1711 		.cmd_priv = PRIV_NETINET_PF,
1712 	},
1713 	{
1714 		.cmd_num = PFNL_CMD_SET_STATUSIF,
1715 		.cmd_name = "SETSTATUSIF",
1716 		.cmd_cb = pf_handle_set_statusif,
1717 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1718 		.cmd_priv = PRIV_NETINET_PF,
1719 	},
1720 	{
1721 		.cmd_num = PFNL_CMD_GET_STATUS,
1722 		.cmd_name = "GETSTATUS",
1723 		.cmd_cb = pf_handle_get_status,
1724 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1725 		.cmd_priv = PRIV_NETINET_PF,
1726 	},
1727 	{
1728 		.cmd_num = PFNL_CMD_CLEAR_STATUS,
1729 		.cmd_name = "CLEARSTATUS",
1730 		.cmd_cb = pf_handle_clear_status,
1731 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1732 		.cmd_priv = PRIV_NETINET_PF,
1733 	},
1734 	{
1735 		.cmd_num = PFNL_CMD_NATLOOK,
1736 		.cmd_name = "NATLOOK",
1737 		.cmd_cb = pf_handle_natlook,
1738 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1739 		.cmd_priv = PRIV_NETINET_PF,
1740 	},
1741 	{
1742 		.cmd_num = PFNL_CMD_SET_DEBUG,
1743 		.cmd_name = "SET_DEBUG",
1744 		.cmd_cb = pf_handle_set_debug,
1745 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1746 		.cmd_priv = PRIV_NETINET_PF,
1747 	},
1748 	{
1749 		.cmd_num = PFNL_CMD_SET_TIMEOUT,
1750 		.cmd_name = "SET_TIMEOUT",
1751 		.cmd_cb = pf_handle_set_timeout,
1752 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1753 		.cmd_priv = PRIV_NETINET_PF,
1754 	},
1755 	{
1756 		.cmd_num = PFNL_CMD_GET_TIMEOUT,
1757 		.cmd_name = "GET_TIMEOUT",
1758 		.cmd_cb = pf_handle_get_timeout,
1759 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1760 		.cmd_priv = PRIV_NETINET_PF,
1761 	},
1762 	{
1763 		.cmd_num = PFNL_CMD_SET_LIMIT,
1764 		.cmd_name = "SET_LIMIT",
1765 		.cmd_cb = pf_handle_set_limit,
1766 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1767 		.cmd_priv = PRIV_NETINET_PF,
1768 	},
1769 	{
1770 		.cmd_num = PFNL_CMD_GET_LIMIT,
1771 		.cmd_name = "GET_LIMIT",
1772 		.cmd_cb = pf_handle_get_limit,
1773 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1774 		.cmd_priv = PRIV_NETINET_PF,
1775 	},
1776 	{
1777 		.cmd_num = PFNL_CMD_BEGIN_ADDRS,
1778 		.cmd_name = "BEGIN_ADDRS",
1779 		.cmd_cb = pf_handle_begin_addrs,
1780 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1781 		.cmd_priv = PRIV_NETINET_PF,
1782 	},
1783 	{
1784 		.cmd_num = PFNL_CMD_ADD_ADDR,
1785 		.cmd_name = "ADD_ADDR",
1786 		.cmd_cb = pf_handle_add_addr,
1787 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1788 		.cmd_priv = PRIV_NETINET_PF,
1789 	},
1790 	{
1791 		.cmd_num = PFNL_CMD_GET_ADDRS,
1792 		.cmd_name = "GET_ADDRS",
1793 		.cmd_cb = pf_handle_get_addrs,
1794 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1795 		.cmd_priv = PRIV_NETINET_PF,
1796 	},
1797 	{
1798 		.cmd_num = PFNL_CMD_GET_ADDR,
1799 		.cmd_name = "GET_ADDRS",
1800 		.cmd_cb = pf_handle_get_addr,
1801 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1802 		.cmd_priv = PRIV_NETINET_PF,
1803 	},
1804 };
1805 
1806 void
1807 pf_nl_register(void)
1808 {
1809 	NL_VERIFY_PARSERS(all_parsers);
1810 
1811 	family_id = genl_register_family(PFNL_FAMILY_NAME, 0, 2, PFNL_CMD_MAX);
1812 	genl_register_cmds(PFNL_FAMILY_NAME, pf_cmds, NL_ARRAY_LEN(pf_cmds));
1813 }
1814 
1815 void
1816 pf_nl_unregister(void)
1817 {
1818 	genl_unregister_family(PFNL_FAMILY_NAME);
1819 }
1820