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
54struct 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)
67static 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};
76static 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
81NL_DECLARE_PARSER(state_parser, struct genlmsghdr, nlf_p_generic, nla_p_state);
82
83static void
84dump_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
96static bool
97dump_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
125static bool
126dump_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
142static int
143dump_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	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
152		goto enomem;
153
154	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
155	ghdr_new->cmd = PFNL_CMD_GETSTATES;
156	ghdr_new->version = 0;
157	ghdr_new->reserved = 0;
158
159	nlattr_add_u64(nw, PF_ST_VERSION, PF_STATE_VERSION);
160
161	key = s->key[PF_SK_WIRE];
162	if (!dump_state_key(nw, PF_ST_KEY_WIRE, key))
163		goto enomem;
164	key = s->key[PF_SK_STACK];
165	if (!dump_state_key(nw, PF_ST_KEY_STACK, key))
166		goto enomem;
167
168	af = s->key[PF_SK_WIRE]->af;
169	nlattr_add_u8(nw, PF_ST_PROTO, s->key[PF_SK_WIRE]->proto);
170	nlattr_add_u8(nw, PF_ST_AF, af);
171
172	nlattr_add_string(nw, PF_ST_IFNAME, s->kif->pfik_name);
173	nlattr_add_string(nw, PF_ST_ORIG_IFNAME, s->orig_kif->pfik_name);
174	dump_addr(nw, PF_ST_RT_ADDR, &s->rt_addr, af);
175	nlattr_add_u32(nw, PF_ST_CREATION, time_uptime - (s->creation / 1000));
176	uint32_t expire = pf_state_expires(s);
177	if (expire > time_uptime)
178		expire = expire - time_uptime;
179	nlattr_add_u32(nw, PF_ST_EXPIRE, expire);
180	nlattr_add_u8(nw, PF_ST_DIRECTION, s->direction);
181	nlattr_add_u8(nw, PF_ST_LOG, s->act.log);
182	nlattr_add_u8(nw, PF_ST_TIMEOUT, s->timeout);
183	nlattr_add_u16(nw, PF_ST_STATE_FLAGS, s->state_flags);
184	uint8_t sync_flags = 0;
185	if (s->src_node)
186		sync_flags |= PFSYNC_FLAG_SRCNODE;
187	if (s->nat_src_node)
188		sync_flags |= PFSYNC_FLAG_NATSRCNODE;
189	nlattr_add_u8(nw, PF_ST_SYNC_FLAGS, sync_flags);
190	nlattr_add_u64(nw, PF_ST_ID, s->id);
191	nlattr_add_u32(nw, PF_ST_CREATORID, htonl(s->creatorid));
192
193	nlattr_add_u32(nw, PF_ST_RULE, s->rule.ptr ? s->rule.ptr->nr : -1);
194	nlattr_add_u32(nw, PF_ST_ANCHOR, s->anchor.ptr ? s->anchor.ptr->nr : -1);
195	nlattr_add_u32(nw, PF_ST_NAT_RULE, s->nat_rule.ptr ? s->nat_rule.ptr->nr : -1);
196
197	nlattr_add_u64(nw, PF_ST_PACKETS0, s->packets[0]);
198	nlattr_add_u64(nw, PF_ST_PACKETS1, s->packets[1]);
199	nlattr_add_u64(nw, PF_ST_BYTES0, s->bytes[0]);
200	nlattr_add_u64(nw, PF_ST_BYTES1, s->bytes[1]);
201	nlattr_add_u32(nw, PF_ST_RTABLEID, s->act.rtableid);
202	nlattr_add_u8(nw, PF_ST_MIN_TTL, s->act.min_ttl);
203	nlattr_add_u16(nw, PF_ST_MAX_MSS, s->act.max_mss);
204	nlattr_add_u16(nw, PF_ST_DNPIPE, s->act.dnpipe);
205	nlattr_add_u16(nw, PF_ST_DNRPIPE, s->act.dnrpipe);
206	nlattr_add_u8(nw, PF_ST_RT, s->rt);
207	if (s->rt_kif != NULL)
208		nlattr_add_string(nw, PF_ST_RT_IFNAME, s->rt_kif->pfik_name);
209
210	if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src))
211		goto enomem;
212	if (!dump_state_peer(nw, PF_ST_PEER_DST, &s->dst))
213		goto enomem;
214
215	if (nlmsg_end(nw))
216		return (0);
217
218enomem:
219	error = ENOMEM;
220	nlmsg_abort(nw);
221	return (error);
222}
223
224static int
225handle_dumpstates(struct nlpcb *nlp, struct nl_parsed_state *attrs,
226    struct nlmsghdr *hdr, struct nl_pstate *npt)
227{
228	int error = 0;
229
230	hdr->nlmsg_flags |= NLM_F_MULTI;
231
232	for (int i = 0; i <= pf_hashmask; i++) {
233		struct pf_idhash *ih = &V_pf_idhash[i];
234		struct pf_kstate *s;
235
236		if (LIST_EMPTY(&ih->states))
237			continue;
238
239		PF_HASHROW_LOCK(ih);
240		LIST_FOREACH(s, &ih->states, entry) {
241			sa_family_t af = s->key[PF_SK_WIRE]->af;
242
243			if (s->timeout == PFTM_UNLINKED)
244				continue;
245
246			/* Filter */
247			if (attrs->creatorid != 0 && s->creatorid != attrs->creatorid)
248				continue;
249			if (attrs->ifname[0] != 0 &&
250			    strncmp(attrs->ifname, s->kif->pfik_name, IFNAMSIZ) != 0)
251				continue;
252			if (attrs->proto != 0 && s->key[PF_SK_WIRE]->proto != attrs->proto)
253				continue;
254			if (attrs->af != 0 && af != attrs->af)
255				continue;
256			if (pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[0],
257			    &attrs->mask, &attrs->addr, af) &&
258			    pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[1],
259			    &attrs->mask, &attrs->addr, af) &&
260			    pf_match_addr(1, &s->key[PF_SK_STACK]->addr[0],
261			    &attrs->mask, &attrs->addr, af) &&
262			    pf_match_addr(1, &s->key[PF_SK_STACK]->addr[1],
263			    &attrs->mask, &attrs->addr, af))
264				continue;
265
266			error = dump_state(nlp, hdr, s, npt);
267			if (error != 0)
268				break;
269		}
270		PF_HASHROW_UNLOCK(ih);
271	}
272
273	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
274		NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
275		return (ENOMEM);
276	}
277
278	return (error);
279}
280
281static int
282handle_getstate(struct nlpcb *nlp, struct nl_parsed_state *attrs,
283    struct nlmsghdr *hdr, struct nl_pstate *npt)
284{
285	struct pf_kstate *s = pf_find_state_byid(attrs->id, attrs->creatorid);
286	if (s == NULL)
287		return (ENOENT);
288	return (dump_state(nlp, hdr, s, npt));
289}
290
291static int
292dump_creatorid(struct nlpcb *nlp, const struct nlmsghdr *hdr, uint32_t creator,
293    struct nl_pstate *npt)
294{
295	struct nl_writer *nw = npt->nw;
296
297	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
298		goto enomem;
299
300	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
301	ghdr_new->cmd = PFNL_CMD_GETCREATORS;
302	ghdr_new->version = 0;
303	ghdr_new->reserved = 0;
304
305	nlattr_add_u32(nw, PF_ST_CREATORID, htonl(creator));
306
307	if (nlmsg_end(nw))
308		return (0);
309
310enomem:
311	nlmsg_abort(nw);
312	return (ENOMEM);
313}
314
315static int
316pf_handle_getstates(struct nlmsghdr *hdr, struct nl_pstate *npt)
317{
318	int error;
319
320	struct nl_parsed_state attrs = {};
321	error = nl_parse_nlmsg(hdr, &state_parser, npt, &attrs);
322	if (error != 0)
323		return (error);
324
325	if (attrs.id != 0)
326		error = handle_getstate(npt->nlp, &attrs, hdr, npt);
327	else
328		error = handle_dumpstates(npt->nlp, &attrs, hdr, npt);
329
330	return (error);
331}
332
333static int
334pf_handle_getcreators(struct nlmsghdr *hdr, struct nl_pstate *npt)
335{
336	uint32_t creators[16];
337	int error = 0;
338
339	bzero(creators, sizeof(creators));
340
341	for (int i = 0; i < pf_hashmask; i++) {
342		struct pf_idhash *ih = &V_pf_idhash[i];
343		struct pf_kstate *s;
344
345		if (LIST_EMPTY(&ih->states))
346			continue;
347
348		PF_HASHROW_LOCK(ih);
349		LIST_FOREACH(s, &ih->states, entry) {
350			int j;
351			if (s->timeout == PFTM_UNLINKED)
352				continue;
353
354			for (j = 0; j < nitems(creators); j++) {
355				if (creators[j] == s->creatorid)
356					break;
357				if (creators[j] == 0) {
358					creators[j] = s->creatorid;
359					break;
360				}
361			}
362			if (j == nitems(creators))
363				printf("Warning: too many creators!\n");
364		}
365		PF_HASHROW_UNLOCK(ih);
366	}
367
368	hdr->nlmsg_flags |= NLM_F_MULTI;
369	for (int i = 0; i < nitems(creators); i++) {
370		if (creators[i] == 0)
371			break;
372		error = dump_creatorid(npt->nlp, hdr, creators[i], npt);
373	}
374
375	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
376		NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
377		return (ENOMEM);
378	}
379
380	return (error);
381}
382
383static int
384pf_handle_start(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused)
385{
386	return (pf_start());
387}
388
389static int
390pf_handle_stop(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused)
391{
392	return (pf_stop());
393}
394
395#define _OUT(_field)	offsetof(struct pf_addr_wrap, _field)
396static const struct nlattr_parser nla_p_addr_wrap[] = {
397	{ .type = PF_AT_ADDR, .off = _OUT(v.a.addr), .cb = nlattr_get_in6_addr },
398	{ .type = PF_AT_MASK, .off = _OUT(v.a.mask), .cb = nlattr_get_in6_addr },
399	{ .type = PF_AT_IFNAME, .off = _OUT(v.ifname), .arg = (void *)IFNAMSIZ,.cb = nlattr_get_chara },
400	{ .type = PF_AT_TABLENAME, .off = _OUT(v.tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
401	{ .type = PF_AT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
402	{ .type = PF_AT_IFLAGS, .off = _OUT(iflags), .cb = nlattr_get_uint8 },
403};
404NL_DECLARE_ATTR_PARSER(addr_wrap_parser, nla_p_addr_wrap);
405#undef _OUT
406
407static bool
408nlattr_add_addr_wrap(struct nl_writer *nw, int attrtype, struct pf_addr_wrap *a)
409{
410	int off = nlattr_add_nested(nw, attrtype);
411	int num;
412
413	nlattr_add_in6_addr(nw, PF_AT_ADDR, &a->v.a.addr.v6);
414	nlattr_add_in6_addr(nw, PF_AT_MASK, &a->v.a.mask.v6);
415	nlattr_add_u8(nw, PF_AT_TYPE, a->type);
416	nlattr_add_u8(nw, PF_AT_IFLAGS, a->iflags);
417
418	if (a->type == PF_ADDR_DYNIFTL) {
419		nlattr_add_string(nw, PF_AT_IFNAME, a->v.ifname);
420		num = 0;
421		if (a->p.dyn != NULL)
422			num = a->p.dyn->pfid_acnt4 + a->p.dyn->pfid_acnt6;
423		nlattr_add_u32(nw, PF_AT_DYNCNT, num);
424	} else if (a->type == PF_ADDR_TABLE) {
425		struct pfr_ktable *kt;
426
427		nlattr_add_string(nw, PF_AT_TABLENAME, a->v.tblname);
428		num = -1;
429		kt = a->p.tbl;
430		if ((kt->pfrkt_flags & PFR_TFLAG_ACTIVE) &&
431		    kt->pfrkt_root != NULL)
432			kt = kt->pfrkt_root;
433		if (kt->pfrkt_flags & PFR_TFLAG_ACTIVE)
434			num = kt->pfrkt_cnt;
435		nlattr_add_u32(nw, PF_AT_TBLCNT, num);
436	}
437
438	nlattr_set_len(nw, off);
439
440	return (true);
441}
442
443#define _OUT(_field)	offsetof(struct pf_rule_addr, _field)
444static const struct nlattr_parser nla_p_ruleaddr[] = {
445	{ .type = PF_RAT_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = nlattr_get_nested },
446	{ .type = PF_RAT_SRC_PORT, .off = _OUT(port[0]), .cb = nlattr_get_uint16 },
447	{ .type = PF_RAT_DST_PORT, .off = _OUT(port[1]), .cb = nlattr_get_uint16 },
448	{ .type = PF_RAT_NEG, .off = _OUT(neg), .cb = nlattr_get_uint8 },
449	{ .type = PF_RAT_OP, .off = _OUT(port_op), .cb = nlattr_get_uint8 },
450};
451NL_DECLARE_ATTR_PARSER(rule_addr_parser, nla_p_ruleaddr);
452#undef _OUT
453
454static bool
455nlattr_add_rule_addr(struct nl_writer *nw, int attrtype, struct pf_rule_addr *r)
456{
457	int off = nlattr_add_nested(nw, attrtype);
458
459	nlattr_add_addr_wrap(nw, PF_RAT_ADDR, &r->addr);
460	nlattr_add_u16(nw, PF_RAT_SRC_PORT, r->port[0]);
461	nlattr_add_u16(nw, PF_RAT_DST_PORT, r->port[1]);
462	nlattr_add_u8(nw, PF_RAT_NEG, r->neg);
463	nlattr_add_u8(nw, PF_RAT_OP, r->port_op);
464
465	nlattr_set_len(nw, off);
466
467	return (true);
468}
469
470#define _OUT(_field)	offsetof(struct pf_mape_portset, _field)
471static const struct nlattr_parser nla_p_mape_portset[] = {
472	{ .type = PF_MET_OFFSET, .off = _OUT(offset), .cb = nlattr_get_uint8 },
473	{ .type = PF_MET_PSID_LEN, .off = _OUT(psidlen), .cb = nlattr_get_uint8 },
474	{. type = PF_MET_PSID, .off = _OUT(psid), .cb = nlattr_get_uint16 },
475};
476NL_DECLARE_ATTR_PARSER(mape_portset_parser, nla_p_mape_portset);
477#undef _OUT
478
479static bool
480nlattr_add_mape_portset(struct nl_writer *nw, int attrtype, const struct pf_mape_portset *m)
481{
482	int off = nlattr_add_nested(nw, attrtype);
483
484	nlattr_add_u8(nw, PF_MET_OFFSET, m->offset);
485	nlattr_add_u8(nw, PF_MET_PSID_LEN, m->psidlen);
486	nlattr_add_u16(nw, PF_MET_PSID, m->psid);
487
488	nlattr_set_len(nw, off);
489
490	return (true);
491}
492
493struct nl_parsed_labels
494{
495	char		labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
496	uint32_t	i;
497};
498
499static int
500nlattr_get_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt,
501    const void *arg, void *target)
502{
503	struct nl_parsed_labels *l = (struct nl_parsed_labels *)target;
504	int ret;
505
506	if (l->i >= PF_RULE_MAX_LABEL_COUNT)
507		return (E2BIG);
508
509	ret = nlattr_get_chara(nla, npt, (void *)PF_RULE_LABEL_SIZE,
510	    l->labels[l->i]);
511	if (ret == 0)
512		l->i++;
513
514	return (ret);
515}
516
517#define _OUT(_field)	offsetof(struct nl_parsed_labels, _field)
518static const struct nlattr_parser nla_p_labels[] = {
519	{ .type = PF_LT_LABEL, .off = 0, .cb = nlattr_get_pf_rule_labels },
520};
521NL_DECLARE_ATTR_PARSER(rule_labels_parser, nla_p_labels);
522#undef _OUT
523
524static int
525nlattr_get_nested_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
526{
527	struct nl_parsed_labels parsed_labels = { };
528	int error;
529
530	/* Assumes target points to the beginning of the structure */
531	error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &rule_labels_parser, npt, &parsed_labels);
532	if (error != 0)
533		return (error);
534
535	memcpy(target, parsed_labels.labels, sizeof(parsed_labels.labels));
536
537	return (0);
538}
539
540static bool
541nlattr_add_labels(struct nl_writer *nw, int attrtype, const struct pf_krule *r)
542{
543	int off = nlattr_add_nested(nw, attrtype);
544	int i = 0;
545
546	while (r->label[i][0] != 0
547	    && i < PF_RULE_MAX_LABEL_COUNT) {
548		nlattr_add_string(nw, PF_LT_LABEL, r->label[i]);
549		i++;
550	}
551
552	nlattr_set_len(nw, off);
553
554	return (true);
555}
556
557#define _OUT(_field)	offsetof(struct pf_kpool, _field)
558static const struct nlattr_parser nla_p_pool[] = {
559	{ .type = PF_PT_KEY, .off = _OUT(key), .arg = (void *)sizeof(struct pf_poolhashkey), .cb = nlattr_get_bytes },
560	{ .type = PF_PT_COUNTER, .off = _OUT(counter), .cb = nlattr_get_in6_addr },
561	{ .type = PF_PT_TBLIDX, .off = _OUT(tblidx), .cb = nlattr_get_uint32 },
562	{ .type = PF_PT_PROXY_SRC_PORT, .off = _OUT(proxy_port[0]), .cb = nlattr_get_uint16 },
563	{ .type = PF_PT_PROXY_DST_PORT, .off = _OUT(proxy_port[1]), .cb = nlattr_get_uint16 },
564	{ .type = PF_PT_OPTS, .off = _OUT(opts), .cb = nlattr_get_uint8 },
565	{ .type = PF_PT_MAPE, .off = _OUT(mape), .arg = &mape_portset_parser, .cb = nlattr_get_nested },
566};
567NL_DECLARE_ATTR_PARSER(pool_parser, nla_p_pool);
568#undef _OUT
569
570static bool
571nlattr_add_pool(struct nl_writer *nw, int attrtype, const struct pf_kpool *pool)
572{
573	int off = nlattr_add_nested(nw, attrtype);
574
575	nlattr_add(nw, PF_PT_KEY, sizeof(struct pf_poolhashkey), &pool->key);
576	nlattr_add_in6_addr(nw, PF_PT_COUNTER, (const struct in6_addr *)&pool->counter);
577	nlattr_add_u32(nw, PF_PT_TBLIDX, pool->tblidx);
578	nlattr_add_u16(nw, PF_PT_PROXY_SRC_PORT, pool->proxy_port[0]);
579	nlattr_add_u16(nw, PF_PT_PROXY_DST_PORT, pool->proxy_port[1]);
580	nlattr_add_u8(nw, PF_PT_OPTS, pool->opts);
581	nlattr_add_mape_portset(nw, PF_PT_MAPE, &pool->mape);
582
583	nlattr_set_len(nw, off);
584
585	return (true);
586}
587
588#define _OUT(_field)	offsetof(struct pf_rule_uid, _field)
589static const struct nlattr_parser nla_p_rule_uid[] = {
590	{ .type = PF_RUT_UID_LOW, .off = _OUT(uid[0]), .cb = nlattr_get_uint32 },
591	{ .type = PF_RUT_UID_HIGH, .off = _OUT(uid[1]), .cb = nlattr_get_uint32 },
592	{ .type = PF_RUT_OP, .off = _OUT(op), .cb = nlattr_get_uint8 },
593};
594NL_DECLARE_ATTR_PARSER(rule_uid_parser, nla_p_rule_uid);
595#undef _OUT
596
597static bool
598nlattr_add_rule_uid(struct nl_writer *nw, int attrtype, const struct pf_rule_uid *u)
599{
600	int off = nlattr_add_nested(nw, attrtype);
601
602	nlattr_add_u32(nw, PF_RUT_UID_LOW, u->uid[0]);
603	nlattr_add_u32(nw, PF_RUT_UID_HIGH, u->uid[1]);
604	nlattr_add_u8(nw, PF_RUT_OP, u->op);
605
606	nlattr_set_len(nw, off);
607
608	return (true);
609}
610
611struct nl_parsed_timeouts
612{
613	uint32_t	timeouts[PFTM_MAX];
614	uint32_t	i;
615};
616
617static int
618nlattr_get_pf_timeout(struct nlattr *nla, struct nl_pstate *npt,
619    const void *arg, void *target)
620{
621	struct nl_parsed_timeouts *t = (struct nl_parsed_timeouts *)target;
622	int ret;
623
624	if (t->i >= PFTM_MAX)
625		return (E2BIG);
626
627	ret = nlattr_get_uint32(nla, npt, NULL, &t->timeouts[t->i]);
628	if (ret == 0)
629		t->i++;
630
631	return (ret);
632}
633
634#define _OUT(_field)	offsetof(struct nl_parsed_timeout, _field)
635static const struct nlattr_parser nla_p_timeouts[] = {
636	{ .type = PF_TT_TIMEOUT, .off = 0, .cb = nlattr_get_pf_timeout },
637};
638NL_DECLARE_ATTR_PARSER(timeout_parser, nla_p_timeouts);
639#undef _OUT
640
641static int
642nlattr_get_nested_timeouts(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
643{
644	struct nl_parsed_timeouts parsed_timeouts = { };
645	int error;
646
647	/* Assumes target points to the beginning of the structure */
648	error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &timeout_parser, npt, &parsed_timeouts);
649	if (error != 0)
650		return (error);
651
652	memcpy(target, parsed_timeouts.timeouts, sizeof(parsed_timeouts.timeouts));
653
654	return (0);
655}
656
657static bool
658nlattr_add_timeout(struct nl_writer *nw, int attrtype, uint32_t *timeout)
659{
660	int off = nlattr_add_nested(nw, attrtype);
661
662	for (int i = 0; i < PFTM_MAX; i++)
663		nlattr_add_u32(nw, PF_RT_TIMEOUT, timeout[i]);
664
665	nlattr_set_len(nw, off);
666
667	return (true);
668}
669
670#define _OUT(_field)	offsetof(struct pf_krule, _field)
671static const struct nlattr_parser nla_p_rule[] = {
672	{ .type = PF_RT_SRC, .off = _OUT(src), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
673	{ .type = PF_RT_DST, .off = _OUT(dst), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
674	{ .type = PF_RT_RIDENTIFIER, .off = _OUT(ridentifier), .cb = nlattr_get_uint32 },
675	{ .type = PF_RT_LABELS, .off = _OUT(label), .arg = &rule_labels_parser,.cb = nlattr_get_nested_pf_rule_labels },
676	{ .type = PF_RT_IFNAME, .off = _OUT(ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
677	{ .type = PF_RT_QNAME, .off = _OUT(qname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
678	{ .type = PF_RT_PQNAME, .off = _OUT(pqname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
679	{ .type = PF_RT_TAGNAME, .off = _OUT(tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
680	{ .type = PF_RT_MATCH_TAGNAME, .off = _OUT(match_tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
681	{ .type = PF_RT_OVERLOAD_TBLNAME, .off = _OUT(overload_tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
682	{ .type = PF_RT_RPOOL, .off = _OUT(rpool), .arg = &pool_parser, .cb = nlattr_get_nested },
683	{ .type = PF_RT_OS_FINGERPRINT, .off = _OUT(os_fingerprint), .cb = nlattr_get_uint32 },
684	{ .type = PF_RT_RTABLEID, .off = _OUT(rtableid), .cb = nlattr_get_uint32 },
685	{ .type = PF_RT_TIMEOUT, .off = _OUT(timeout), .arg = &timeout_parser, .cb = nlattr_get_nested_timeouts },
686	{ .type = PF_RT_MAX_STATES, .off = _OUT(max_states), .cb = nlattr_get_uint32 },
687	{ .type = PF_RT_MAX_SRC_NODES, .off = _OUT(max_src_nodes), .cb = nlattr_get_uint32 },
688	{ .type = PF_RT_MAX_SRC_STATES, .off = _OUT(max_src_states), .cb = nlattr_get_uint32 },
689	{ .type = PF_RT_MAX_SRC_CONN_RATE_LIMIT, .off = _OUT(max_src_conn_rate.limit), .cb = nlattr_get_uint32 },
690	{ .type = PF_RT_MAX_SRC_CONN_RATE_SECS, .off = _OUT(max_src_conn_rate.seconds), .cb = nlattr_get_uint32 },
691	{ .type = PF_RT_DNPIPE, .off = _OUT(dnpipe), .cb = nlattr_get_uint16 },
692	{ .type = PF_RT_DNRPIPE, .off = _OUT(dnrpipe), .cb = nlattr_get_uint16 },
693	{ .type = PF_RT_DNFLAGS, .off = _OUT(free_flags), .cb = nlattr_get_uint32 },
694	{ .type = PF_RT_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
695	{ .type = PF_RT_PROB, .off = _OUT(prob), .cb = nlattr_get_uint32 },
696	{ .type = PF_RT_CUID, .off = _OUT(cuid), .cb = nlattr_get_uint32 },
697	{. type = PF_RT_CPID, .off = _OUT(cpid), .cb = nlattr_get_uint32 },
698	{ .type = PF_RT_RETURN_ICMP, .off = _OUT(return_icmp), .cb = nlattr_get_uint16 },
699	{ .type = PF_RT_RETURN_ICMP6, .off = _OUT(return_icmp6), .cb = nlattr_get_uint16 },
700	{ .type = PF_RT_MAX_MSS, .off = _OUT(max_mss), .cb = nlattr_get_uint16 },
701	{ .type = PF_RT_SCRUB_FLAGS, .off = _OUT(scrub_flags), .cb = nlattr_get_uint16 },
702	{ .type = PF_RT_UID, .off = _OUT(uid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
703	{ .type = PF_RT_GID, .off = _OUT(gid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
704	{ .type = PF_RT_RULE_FLAG, .off = _OUT(rule_flag), .cb = nlattr_get_uint32 },
705	{ .type = PF_RT_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 },
706	{ .type = PF_RT_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 },
707	{ .type = PF_RT_LOG, .off = _OUT(log), .cb = nlattr_get_uint8 },
708	{ .type = PF_RT_LOGIF, .off = _OUT(logif), .cb = nlattr_get_uint8 },
709	{ .type = PF_RT_QUICK, .off = _OUT(quick), .cb = nlattr_get_uint8 },
710	{ .type = PF_RT_IF_NOT, .off = _OUT(ifnot), .cb = nlattr_get_uint8 },
711	{ .type = PF_RT_MATCH_TAG_NOT, .off = _OUT(match_tag_not), .cb = nlattr_get_uint8 },
712	{ .type = PF_RT_NATPASS, .off = _OUT(natpass), .cb = nlattr_get_uint8 },
713	{ .type = PF_RT_KEEP_STATE, .off = _OUT(keep_state), .cb = nlattr_get_uint8 },
714	{ .type = PF_RT_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
715	{ .type = PF_RT_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 },
716	{ .type = PF_RT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
717	{ .type = PF_RT_CODE, .off = _OUT(code), .cb = nlattr_get_uint8 },
718	{ .type = PF_RT_FLAGS, .off = _OUT(flags), .cb = nlattr_get_uint8 },
719	{ .type = PF_RT_FLAGSET, .off = _OUT(flagset), .cb = nlattr_get_uint8 },
720	{ .type = PF_RT_MIN_TTL, .off = _OUT(min_ttl), .cb = nlattr_get_uint8 },
721	{ .type = PF_RT_ALLOW_OPTS, .off = _OUT(allow_opts), .cb = nlattr_get_uint8 },
722	{ .type = PF_RT_RT, .off = _OUT(rt), .cb = nlattr_get_uint8 },
723	{ .type = PF_RT_RETURN_TTL, .off = _OUT(return_ttl), .cb = nlattr_get_uint8 },
724	{ .type = PF_RT_TOS, .off = _OUT(tos), .cb = nlattr_get_uint8 },
725	{ .type = PF_RT_SET_TOS, .off = _OUT(set_tos), .cb = nlattr_get_uint8 },
726	{ .type = PF_RT_ANCHOR_RELATIVE, .off = _OUT(anchor_relative), .cb = nlattr_get_uint8 },
727	{ .type = PF_RT_ANCHOR_WILDCARD, .off = _OUT(anchor_wildcard), .cb = nlattr_get_uint8 },
728	{ .type = PF_RT_FLUSH, .off = _OUT(flush), .cb = nlattr_get_uint8 },
729	{ .type = PF_RT_PRIO, .off = _OUT(prio), .cb = nlattr_get_uint8 },
730	{ .type = PF_RT_SET_PRIO, .off = _OUT(set_prio[0]), .cb = nlattr_get_uint8 },
731	{ .type = PF_RT_SET_PRIO_REPLY, .off = _OUT(set_prio[1]), .cb = nlattr_get_uint8 },
732	{ .type = PF_RT_DIVERT_ADDRESS, .off = _OUT(divert.addr), .cb = nlattr_get_in6_addr },
733	{ .type = PF_RT_DIVERT_PORT, .off = _OUT(divert.port), .cb = nlattr_get_uint16 },
734};
735NL_DECLARE_ATTR_PARSER(rule_parser, nla_p_rule);
736#undef _OUT
737struct nl_parsed_addrule {
738	struct pf_krule	*rule;
739	uint32_t	 ticket;
740	uint32_t	 pool_ticket;
741	char		*anchor;
742	char		*anchor_call;
743};
744#define	_IN(_field)	offsetof(struct genlmsghdr, _field)
745#define	_OUT(_field)	offsetof(struct nl_parsed_addrule, _field)
746static const struct nlattr_parser nla_p_addrule[] = {
747	{ .type = PF_ART_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
748	{ .type = PF_ART_POOL_TICKET, .off = _OUT(pool_ticket), .cb = nlattr_get_uint32 },
749	{ .type = PF_ART_ANCHOR, .off = _OUT(anchor), .cb = nlattr_get_string },
750	{ .type = PF_ART_ANCHOR_CALL, .off = _OUT(anchor_call), .cb = nlattr_get_string },
751	{ .type = PF_ART_RULE, .off = _OUT(rule), .arg = &rule_parser, .cb = nlattr_get_nested_ptr }
752};
753static const struct nlfield_parser nlf_p_addrule[] = {
754};
755#undef _IN
756#undef _OUT
757NL_DECLARE_PARSER(addrule_parser, struct genlmsghdr, nlf_p_addrule, nla_p_addrule);
758
759static int
760pf_handle_addrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
761{
762	int error;
763	struct nl_parsed_addrule attrs = {};
764
765	attrs.rule = pf_krule_alloc();
766
767	error = nl_parse_nlmsg(hdr, &addrule_parser, npt, &attrs);
768	if (error != 0) {
769		pf_free_rule(attrs.rule);
770		return (error);
771	}
772
773	error = pf_ioctl_addrule(attrs.rule, attrs.ticket, attrs.pool_ticket,
774	    attrs.anchor, attrs.anchor_call, nlp_get_cred(npt->nlp)->cr_uid,
775	    hdr->nlmsg_pid);
776
777	return (error);
778}
779
780#define	_IN(_field)	offsetof(struct genlmsghdr, _field)
781#define	_OUT(_field)	offsetof(struct pfioc_rule, _field)
782static const struct nlattr_parser nla_p_getrules[] = {
783	{ .type = PF_GR_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
784	{ .type = PF_GR_ACTION, .off = _OUT(rule.action), .cb = nlattr_get_uint8 },
785};
786static const struct nlfield_parser nlf_p_getrules[] = {
787};
788#undef _IN
789#undef _OUT
790NL_DECLARE_PARSER(getrules_parser, struct genlmsghdr, nlf_p_getrules, nla_p_getrules);
791
792static int
793pf_handle_getrules(struct nlmsghdr *hdr, struct nl_pstate *npt)
794{
795	struct pfioc_rule attrs = {};
796	int error;
797	struct nl_writer *nw = npt->nw;
798	struct genlmsghdr *ghdr_new;
799
800	error = nl_parse_nlmsg(hdr, &getrules_parser, npt, &attrs);
801	if (error != 0)
802		return (error);
803
804	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
805		return (ENOMEM);
806
807	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
808	ghdr_new->cmd = PFNL_CMD_GETRULES;
809	ghdr_new->version = 0;
810	ghdr_new->reserved = 0;
811
812	error = pf_ioctl_getrules(&attrs);
813	if (error != 0)
814		goto out;
815
816	nlattr_add_u32(nw, PF_GR_NR, attrs.nr);
817	nlattr_add_u32(nw, PF_GR_TICKET, attrs.ticket);
818
819	if (!nlmsg_end(nw)) {
820		error = ENOMEM;
821		goto out;
822	}
823
824	return (0);
825
826out:
827	nlmsg_abort(nw);
828	return (error);
829}
830
831struct nl_parsed_get_rule {
832	char anchor[MAXPATHLEN];
833	uint8_t action;
834	uint32_t nr;
835	uint32_t ticket;
836	uint8_t clear;
837};
838#define	_IN(_field)	offsetof(struct genlmsghdr, _field)
839#define	_OUT(_field)	offsetof(struct nl_parsed_get_rule, _field)
840static const struct nlattr_parser nla_p_getrule[] = {
841	{ .type = PF_GR_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
842	{ .type = PF_GR_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 },
843	{ .type = PF_GR_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
844	{ .type = PF_GR_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
845	{ .type = PF_GR_CLEAR, .off = _OUT(clear), .cb = nlattr_get_uint8 },
846};
847static const struct nlfield_parser nlf_p_getrule[] = {
848};
849#undef _IN
850#undef _OUT
851NL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, nlf_p_getrule, nla_p_getrule);
852
853static int
854pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
855{
856	char				 anchor_call[MAXPATHLEN];
857	struct nl_parsed_get_rule	 attrs = {};
858	struct nl_writer		*nw = npt->nw;
859	struct genlmsghdr		*ghdr_new;
860	struct pf_kruleset		*ruleset;
861	struct pf_krule			*rule;
862	int				 rs_num;
863	int				 error;
864
865	error = nl_parse_nlmsg(hdr, &getrule_parser, npt, &attrs);
866	if (error != 0)
867		return (error);
868
869	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
870		return (ENOMEM);
871
872	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
873	ghdr_new->cmd = PFNL_CMD_GETRULE;
874	ghdr_new->version = 0;
875	ghdr_new->reserved = 0;
876
877	PF_RULES_WLOCK();
878	ruleset = pf_find_kruleset(attrs.anchor);
879	if (ruleset == NULL) {
880		PF_RULES_WUNLOCK();
881		error = ENOENT;
882		goto out;
883	}
884
885	rs_num = pf_get_ruleset_number(attrs.action);
886	if (rs_num >= PF_RULESET_MAX) {
887		PF_RULES_WUNLOCK();
888		error = EINVAL;
889		goto out;
890	}
891
892	if (attrs.ticket != ruleset->rules[rs_num].active.ticket) {
893		PF_RULES_WUNLOCK();
894		error = EBUSY;
895		goto out;
896	}
897
898	rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr);
899	while ((rule != NULL) && (rule->nr != attrs.nr))
900		rule = TAILQ_NEXT(rule, entries);
901	if (rule == NULL) {
902		PF_RULES_WUNLOCK();
903		error = EBUSY;
904		goto out;
905	}
906
907	nlattr_add_rule_addr(nw, PF_RT_SRC, &rule->src);
908	nlattr_add_rule_addr(nw, PF_RT_DST, &rule->dst);
909	nlattr_add_u32(nw, PF_RT_RIDENTIFIER, rule->ridentifier);
910	nlattr_add_labels(nw, PF_RT_LABELS, rule);
911	nlattr_add_string(nw, PF_RT_IFNAME, rule->ifname);
912	nlattr_add_string(nw, PF_RT_QNAME, rule->qname);
913	nlattr_add_string(nw, PF_RT_PQNAME, rule->pqname);
914	nlattr_add_string(nw, PF_RT_TAGNAME, rule->tagname);
915	nlattr_add_string(nw, PF_RT_MATCH_TAGNAME, rule->match_tagname);
916	nlattr_add_string(nw, PF_RT_OVERLOAD_TBLNAME, rule->overload_tblname);
917	nlattr_add_pool(nw, PF_RT_RPOOL, &rule->rpool);
918	nlattr_add_u32(nw, PF_RT_OS_FINGERPRINT, rule->os_fingerprint);
919	nlattr_add_u32(nw, PF_RT_RTABLEID, rule->rtableid);
920	nlattr_add_timeout(nw, PF_RT_TIMEOUT, rule->timeout);
921	nlattr_add_u32(nw, PF_RT_MAX_STATES, rule->max_states);
922	nlattr_add_u32(nw, PF_RT_MAX_SRC_NODES, rule->max_src_nodes);
923	nlattr_add_u32(nw, PF_RT_MAX_SRC_STATES, rule->max_src_states);
924	nlattr_add_u32(nw, PF_RT_MAX_SRC_CONN_RATE_LIMIT, rule->max_src_conn_rate.limit);
925	nlattr_add_u32(nw, PF_RT_MAX_SRC_CONN_RATE_SECS, rule->max_src_conn_rate.seconds);
926
927	nlattr_add_u16(nw, PF_RT_DNPIPE, rule->dnpipe);
928	nlattr_add_u16(nw, PF_RT_DNRPIPE, rule->dnrpipe);
929	nlattr_add_u32(nw, PF_RT_DNFLAGS, rule->free_flags);
930
931	nlattr_add_u32(nw, PF_RT_NR, rule->nr);
932	nlattr_add_u32(nw, PF_RT_PROB, rule->prob);
933	nlattr_add_u32(nw, PF_RT_CUID, rule->cuid);
934	nlattr_add_u32(nw, PF_RT_CPID, rule->cpid);
935
936	nlattr_add_u16(nw, PF_RT_RETURN_ICMP, rule->return_icmp);
937	nlattr_add_u16(nw, PF_RT_RETURN_ICMP6, rule->return_icmp6);
938	nlattr_add_u16(nw, PF_RT_RETURN_ICMP6, rule->return_icmp6);
939	nlattr_add_u16(nw, PF_RT_MAX_MSS, rule->max_mss);
940	nlattr_add_u16(nw, PF_RT_SCRUB_FLAGS, rule->scrub_flags);
941
942	nlattr_add_rule_uid(nw, PF_RT_UID, &rule->uid);
943	nlattr_add_rule_uid(nw, PF_RT_GID, (const struct pf_rule_uid *)&rule->gid);
944
945	nlattr_add_u32(nw, PF_RT_RULE_FLAG, rule->rule_flag);
946	nlattr_add_u8(nw, PF_RT_ACTION, rule->action);
947	nlattr_add_u8(nw, PF_RT_DIRECTION, rule->direction);
948	nlattr_add_u8(nw, PF_RT_LOG, rule->log);
949	nlattr_add_u8(nw, PF_RT_LOGIF, rule->logif);
950	nlattr_add_u8(nw, PF_RT_QUICK, rule->quick);
951	nlattr_add_u8(nw, PF_RT_IF_NOT, rule->ifnot);
952	nlattr_add_u8(nw, PF_RT_MATCH_TAG_NOT, rule->match_tag_not);
953	nlattr_add_u8(nw, PF_RT_NATPASS, rule->natpass);
954	nlattr_add_u8(nw, PF_RT_KEEP_STATE, rule->keep_state);
955
956	nlattr_add_u8(nw, PF_RT_AF, rule->af);
957	nlattr_add_u8(nw, PF_RT_PROTO, rule->proto);
958	nlattr_add_u8(nw, PF_RT_TYPE, rule->type);
959	nlattr_add_u8(nw, PF_RT_CODE, rule->code);
960	nlattr_add_u8(nw, PF_RT_FLAGS, rule->flags);
961	nlattr_add_u8(nw, PF_RT_FLAGSET, rule->flagset);
962	nlattr_add_u8(nw, PF_RT_MIN_TTL, rule->min_ttl);
963	nlattr_add_u8(nw, PF_RT_ALLOW_OPTS, rule->allow_opts);
964	nlattr_add_u8(nw, PF_RT_RT, rule->rt);
965	nlattr_add_u8(nw, PF_RT_RETURN_TTL, rule->return_ttl);
966	nlattr_add_u8(nw, PF_RT_TOS, rule->tos);
967	nlattr_add_u8(nw, PF_RT_SET_TOS, rule->set_tos);
968	nlattr_add_u8(nw, PF_RT_ANCHOR_RELATIVE, rule->anchor_relative);
969	nlattr_add_u8(nw, PF_RT_ANCHOR_WILDCARD, rule->anchor_wildcard);
970	nlattr_add_u8(nw, PF_RT_FLUSH, rule->flush);
971	nlattr_add_u8(nw, PF_RT_PRIO, rule->prio);
972	nlattr_add_u8(nw, PF_RT_SET_PRIO, rule->set_prio[0]);
973	nlattr_add_u8(nw, PF_RT_SET_PRIO_REPLY, rule->set_prio[1]);
974
975	nlattr_add_in6_addr(nw, PF_RT_DIVERT_ADDRESS, &rule->divert.addr.v6);
976	nlattr_add_u16(nw, PF_RT_DIVERT_PORT, rule->divert.port);
977
978	nlattr_add_u64(nw, PF_RT_PACKETS_IN, pf_counter_u64_fetch(&rule->packets[0]));
979	nlattr_add_u64(nw, PF_RT_PACKETS_OUT, pf_counter_u64_fetch(&rule->packets[1]));
980	nlattr_add_u64(nw, PF_RT_BYTES_IN, pf_counter_u64_fetch(&rule->bytes[0]));
981	nlattr_add_u64(nw, PF_RT_BYTES_OUT, pf_counter_u64_fetch(&rule->bytes[1]));
982	nlattr_add_u64(nw, PF_RT_EVALUATIONS, pf_counter_u64_fetch(&rule->evaluations));
983	nlattr_add_u64(nw, PF_RT_TIMESTAMP, pf_get_timestamp(rule));
984	nlattr_add_u64(nw, PF_RT_STATES_CUR, counter_u64_fetch(rule->states_cur));
985	nlattr_add_u64(nw, PF_RT_STATES_TOTAL, counter_u64_fetch(rule->states_tot));
986	nlattr_add_u64(nw, PF_RT_SRC_NODES, counter_u64_fetch(rule->src_nodes));
987
988	error = pf_kanchor_copyout(ruleset, rule, anchor_call, sizeof(anchor_call));
989	MPASS(error == 0);
990
991	nlattr_add_string(nw, PF_RT_ANCHOR_CALL, anchor_call);
992
993	if (attrs.clear)
994		pf_krule_clear_counters(rule);
995
996	PF_RULES_WUNLOCK();
997
998	if (!nlmsg_end(nw)) {
999		error = ENOMEM;
1000		goto out;
1001	}
1002
1003	return (0);
1004out:
1005	nlmsg_abort(nw);
1006	return (error);
1007}
1008
1009#define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1010#define	_OUT(_field)	offsetof(struct pf_kstate_kill, _field)
1011static const struct nlattr_parser nla_p_clear_states[] = {
1012	{ .type = PF_CS_CMP_ID, .off = _OUT(psk_pfcmp.id), .cb = nlattr_get_uint64 },
1013	{ .type = PF_CS_CMP_CREATORID, .off = _OUT(psk_pfcmp.creatorid), .cb = nlattr_get_uint32 },
1014	{ .type = PF_CS_CMP_DIR, .off = _OUT(psk_pfcmp.direction), .cb = nlattr_get_uint8 },
1015	{ .type = PF_CS_AF, .off = _OUT(psk_af), .cb = nlattr_get_uint8 },
1016	{ .type = PF_CS_PROTO, .off = _OUT(psk_proto), .cb = nlattr_get_uint8 },
1017	{ .type = PF_CS_SRC, .off = _OUT(psk_src), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
1018	{ .type = PF_CS_DST, .off = _OUT(psk_dst), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
1019	{ .type = PF_CS_RT_ADDR, .off = _OUT(psk_rt_addr), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
1020	{ .type = PF_CS_IFNAME, .off = _OUT(psk_ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
1021	{ .type = PF_CS_LABEL, .off = _OUT(psk_label), .arg = (void *)PF_RULE_LABEL_SIZE, .cb = nlattr_get_chara },
1022	{ .type = PF_CS_KILL_MATCH, .off = _OUT(psk_kill_match), .cb = nlattr_get_bool },
1023	{ .type = PF_CS_NAT, .off = _OUT(psk_nat), .cb = nlattr_get_bool },
1024};
1025static const struct nlfield_parser nlf_p_clear_states[] = {};
1026#undef _IN
1027#undef _OUT
1028NL_DECLARE_PARSER(clear_states_parser, struct genlmsghdr, nlf_p_clear_states, nla_p_clear_states);
1029
1030static int
1031pf_handle_killclear_states(struct nlmsghdr *hdr, struct nl_pstate *npt, int cmd)
1032{
1033	struct pf_kstate_kill		 kill = {};
1034	struct epoch_tracker		 et;
1035	struct nl_writer		*nw = npt->nw;
1036	struct genlmsghdr		*ghdr_new;
1037	int				 error;
1038	unsigned int			 killed = 0;
1039
1040	error = nl_parse_nlmsg(hdr, &clear_states_parser, npt, &kill);
1041	if (error != 0)
1042		return (error);
1043
1044	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1045		return (ENOMEM);
1046
1047	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1048	ghdr_new->cmd = cmd;
1049	ghdr_new->version = 0;
1050	ghdr_new->reserved = 0;
1051
1052	NET_EPOCH_ENTER(et);
1053	if (cmd == PFNL_CMD_KILLSTATES)
1054		pf_killstates(&kill, &killed);
1055	else
1056		killed = pf_clear_states(&kill);
1057	NET_EPOCH_EXIT(et);
1058
1059	nlattr_add_u32(nw, PF_CS_KILLED, killed);
1060
1061	if (! nlmsg_end(nw)) {
1062		error = ENOMEM;
1063		goto out;
1064	}
1065
1066	return (0);
1067
1068out:
1069	nlmsg_abort(nw);
1070	return (error);
1071}
1072
1073static int
1074pf_handle_clear_states(struct nlmsghdr *hdr, struct nl_pstate *npt)
1075{
1076	return (pf_handle_killclear_states(hdr, npt, PFNL_CMD_CLRSTATES));
1077}
1078
1079static int
1080pf_handle_kill_states(struct nlmsghdr *hdr, struct nl_pstate *npt)
1081{
1082	return (pf_handle_killclear_states(hdr, npt, PFNL_CMD_KILLSTATES));
1083}
1084
1085struct nl_parsed_set_statusif {
1086	char ifname[IFNAMSIZ];
1087};
1088#define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1089#define	_OUT(_field)	offsetof(struct nl_parsed_set_statusif, _field)
1090static const struct nlattr_parser nla_p_set_statusif[] = {
1091	{ .type = PF_SS_IFNAME, .off = _OUT(ifname), .arg = (const void *)IFNAMSIZ, .cb = nlattr_get_chara },
1092};
1093static const struct nlfield_parser nlf_p_set_statusif[] = {};
1094#undef _IN
1095#undef _OUT
1096NL_DECLARE_PARSER(set_statusif_parser, struct genlmsghdr, nlf_p_set_statusif, nla_p_set_statusif);
1097
1098static int
1099pf_handle_set_statusif(struct nlmsghdr *hdr, struct nl_pstate *npt)
1100{
1101	int error;
1102	struct nl_parsed_set_statusif attrs = {};
1103
1104	error = nl_parse_nlmsg(hdr, &set_statusif_parser, npt, &attrs);
1105	if (error != 0)
1106		return (error);
1107
1108	PF_RULES_WLOCK();
1109	strlcpy(V_pf_status.ifname, attrs.ifname, IFNAMSIZ);
1110	PF_RULES_WUNLOCK();
1111
1112	return (0);
1113}
1114
1115static bool
1116nlattr_add_counters(struct nl_writer *nw, int attr, size_t number, char **names,
1117    counter_u64_t *counters)
1118{
1119	for (int i = 0; i < number; i++) {
1120		int off = nlattr_add_nested(nw, attr);
1121		nlattr_add_u32(nw, PF_C_ID, i);
1122		nlattr_add_string(nw, PF_C_NAME, names[i]);
1123		nlattr_add_u64(nw, PF_C_COUNTER, counter_u64_fetch(counters[i]));
1124		nlattr_set_len(nw, off);
1125	}
1126
1127	return (true);
1128}
1129
1130static bool
1131nlattr_add_fcounters(struct nl_writer *nw, int attr, size_t number, char **names,
1132    struct pf_counter_u64 *counters)
1133{
1134	for (int i = 0; i < number; i++) {
1135		int off = nlattr_add_nested(nw, attr);
1136		nlattr_add_u32(nw, PF_C_ID, i);
1137		nlattr_add_string(nw, PF_C_NAME, names[i]);
1138		nlattr_add_u64(nw, PF_C_COUNTER, pf_counter_u64_fetch(&counters[i]));
1139		nlattr_set_len(nw, off);
1140	}
1141
1142	return (true);
1143}
1144
1145static bool
1146nlattr_add_u64_array(struct nl_writer *nw, int attr, size_t number, uint64_t *array)
1147{
1148	int off = nlattr_add_nested(nw, attr);
1149
1150	for (size_t i = 0; i < number; i++)
1151		nlattr_add_u64(nw, 0, array[i]);
1152
1153	nlattr_set_len(nw, off);
1154
1155	return (true);
1156}
1157
1158static int
1159pf_handle_get_status(struct nlmsghdr *hdr, struct nl_pstate *npt)
1160{
1161	struct pf_status s;
1162	struct nl_writer *nw = npt->nw;
1163	struct genlmsghdr *ghdr_new;
1164	char *pf_reasons[PFRES_MAX+1] = PFRES_NAMES;
1165	char *pf_lcounter[KLCNT_MAX+1] = KLCNT_NAMES;
1166	char *pf_fcounter[FCNT_MAX+1] = FCNT_NAMES;
1167	int error;
1168
1169	PF_RULES_RLOCK_TRACKER;
1170
1171	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1172		return (ENOMEM);
1173
1174	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1175	ghdr_new->cmd = PFNL_CMD_GET_STATUS;
1176	ghdr_new->version = 0;
1177	ghdr_new->reserved = 0;
1178
1179	PF_RULES_RLOCK();
1180
1181	nlattr_add_string(nw, PF_GS_IFNAME, V_pf_status.ifname);
1182	nlattr_add_bool(nw, PF_GS_RUNNING, V_pf_status.running);
1183	nlattr_add_u32(nw, PF_GS_SINCE, V_pf_status.since);
1184	nlattr_add_u32(nw, PF_GS_DEBUG, V_pf_status.debug);
1185	nlattr_add_u32(nw, PF_GS_HOSTID, ntohl(V_pf_status.hostid));
1186	nlattr_add_u32(nw, PF_GS_STATES, V_pf_status.states);
1187	nlattr_add_u32(nw, PF_GS_SRC_NODES, V_pf_status.src_nodes);
1188	nlattr_add_u32(nw, PF_GS_REASSEMBLE, V_pf_status.reass);
1189	nlattr_add_u32(nw, PF_GS_SYNCOOKIES_ACTIVE, V_pf_status.syncookies_active);
1190
1191	nlattr_add_counters(nw, PF_GS_COUNTERS, PFRES_MAX, pf_reasons,
1192	    V_pf_status.counters);
1193	nlattr_add_counters(nw, PF_GS_LCOUNTERS, KLCNT_MAX, pf_lcounter,
1194	    V_pf_status.lcounters);
1195	nlattr_add_fcounters(nw, PF_GS_FCOUNTERS, FCNT_MAX, pf_fcounter,
1196	    V_pf_status.fcounters);
1197	nlattr_add_counters(nw, PF_GS_SCOUNTERS, SCNT_MAX, pf_fcounter,
1198	    V_pf_status.scounters);
1199
1200	pfi_update_status(V_pf_status.ifname, &s);
1201	nlattr_add_u64_array(nw, PF_GS_BCOUNTERS, 2 * 2, (uint64_t *)s.bcounters);
1202	nlattr_add_u64_array(nw, PF_GS_PCOUNTERS, 2 * 2 * 2, (uint64_t *)s.pcounters);
1203
1204	nlattr_add(nw, PF_GS_CHKSUM, PF_MD5_DIGEST_LENGTH, V_pf_status.pf_chksum);
1205
1206	PF_RULES_RUNLOCK();
1207
1208	if (!nlmsg_end(nw)) {
1209		error = ENOMEM;
1210		goto out;
1211	}
1212
1213	return (0);
1214
1215out:
1216	nlmsg_abort(nw);
1217	return (error);
1218}
1219
1220static int
1221pf_handle_clear_status(struct nlmsghdr *hdr, struct nl_pstate *npt)
1222{
1223	pf_ioctl_clear_status();
1224
1225	return (0);
1226}
1227
1228struct pf_nl_natlook {
1229	sa_family_t af;
1230	uint8_t direction;
1231	uint8_t proto;
1232	struct pf_addr src;
1233	struct pf_addr dst;
1234	uint16_t sport;
1235	uint16_t dport;
1236};
1237
1238#define	_IN(_field)	offsetof(struct genlmsghdr, _field)
1239#define	_OUT(_field)	offsetof(struct pf_nl_natlook, _field)
1240static const struct nlattr_parser nla_p_natlook[] = {
1241	{ .type = PF_NL_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
1242	{ .type = PF_NL_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 },
1243	{ .type = PF_NL_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 },
1244	{ .type = PF_NL_SRC_ADDR, .off = _OUT(src), .cb = nlattr_get_in6_addr },
1245	{ .type = PF_NL_DST_ADDR, .off = _OUT(dst), .cb = nlattr_get_in6_addr },
1246	{ .type = PF_NL_SRC_PORT, .off = _OUT(sport), .cb = nlattr_get_uint16 },
1247	{ .type = PF_NL_DST_PORT, .off = _OUT(dport), .cb = nlattr_get_uint16 },
1248};
1249static const struct nlfield_parser nlf_p_natlook[] = {};
1250#undef _IN
1251#undef _OUT
1252NL_DECLARE_PARSER(natlook_parser, struct genlmsghdr, nlf_p_natlook, nla_p_natlook);
1253
1254static int
1255pf_handle_natlook(struct nlmsghdr *hdr, struct nl_pstate *npt)
1256{
1257	struct pf_nl_natlook	 attrs = {};
1258	struct pf_state_key_cmp	 key = {};
1259	struct nl_writer	*nw = npt->nw;
1260	struct pf_state_key	*sk;
1261	struct pf_kstate	*state;
1262	struct genlmsghdr	*ghdr_new;
1263	int			 error, m;
1264	int			 sidx, didx;
1265
1266	error = nl_parse_nlmsg(hdr, &natlook_parser, npt, &attrs);
1267	if (error != 0)
1268		return (error);
1269
1270	if (attrs.proto == 0 ||
1271	    PF_AZERO(&attrs.src, attrs.af) ||
1272	    PF_AZERO(&attrs.dst, attrs.af) ||
1273	    ((attrs.proto == IPPROTO_TCP || attrs.proto == IPPROTO_UDP) &&
1274	     (attrs.sport == 0 || attrs.dport == 0)))
1275		return (EINVAL);
1276
1277	/* NATLOOK src and dst are reversed, so reverse sidx/didx */
1278	sidx = (attrs.direction == PF_IN) ? 1 : 0;
1279	didx = (attrs.direction == PF_IN) ? 0 : 1;
1280
1281	key.af = attrs.af;
1282	key.proto = attrs.proto;
1283	PF_ACPY(&key.addr[sidx], &attrs.src, attrs.af);
1284	key.port[sidx] = attrs.sport;
1285	PF_ACPY(&key.addr[didx], &attrs.dst, attrs.af);
1286	key.port[didx] = attrs.dport;
1287
1288	state = pf_find_state_all(&key, attrs.direction, &m);
1289	if (state == NULL)
1290		return (ENOENT);
1291	if (m > 1) {
1292		PF_STATE_UNLOCK(state);
1293		return (E2BIG);
1294	}
1295
1296	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
1297		PF_STATE_UNLOCK(state);
1298		return (ENOMEM);
1299	}
1300
1301	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1302	ghdr_new->cmd = PFNL_CMD_NATLOOK;
1303	ghdr_new->version = 0;
1304	ghdr_new->reserved = 0;
1305
1306	sk = state->key[sidx];
1307
1308	nlattr_add_in6_addr(nw, PF_NL_SRC_ADDR, &sk->addr[sidx].v6);
1309	nlattr_add_in6_addr(nw, PF_NL_DST_ADDR, &sk->addr[didx].v6);
1310	nlattr_add_u16(nw, PF_NL_SRC_PORT, sk->port[sidx]);
1311	nlattr_add_u16(nw, PF_NL_DST_PORT, sk->port[didx]);
1312
1313	PF_STATE_UNLOCK(state);
1314
1315	if (!nlmsg_end(nw)) {
1316		nlmsg_abort(nw);
1317		return (ENOMEM);
1318	}
1319
1320	return (0);
1321}
1322
1323struct pf_nl_set_debug
1324{
1325	uint32_t level;
1326};
1327#define	_OUT(_field)	offsetof(struct pf_nl_set_debug, _field)
1328static const struct nlattr_parser nla_p_set_debug[] = {
1329	{ .type = PF_SD_LEVEL, .off = _OUT(level), .cb = nlattr_get_uint32 },
1330};
1331static const struct nlfield_parser nlf_p_set_debug[] = {};
1332#undef _OUT
1333NL_DECLARE_PARSER(set_debug_parser, struct genlmsghdr, nlf_p_set_debug, nla_p_set_debug);
1334
1335static int
1336pf_handle_set_debug(struct nlmsghdr *hdr, struct nl_pstate *npt)
1337{
1338	struct pf_nl_set_debug attrs = {};
1339	int error;
1340
1341	error = nl_parse_nlmsg(hdr, &set_debug_parser, npt, &attrs);
1342	if (error != 0)
1343		return (error);
1344
1345	PF_RULES_WLOCK();
1346	V_pf_status.debug = attrs.level;
1347	PF_RULES_WUNLOCK();
1348
1349	return (0);
1350}
1351
1352static const struct nlhdr_parser *all_parsers[] = {
1353	&state_parser,
1354	&addrule_parser,
1355	&getrules_parser,
1356	&clear_states_parser,
1357	&set_statusif_parser,
1358	&natlook_parser,
1359	&set_debug_parser,
1360};
1361
1362static int family_id;
1363
1364static const struct genl_cmd pf_cmds[] = {
1365	{
1366		.cmd_num = PFNL_CMD_GETSTATES,
1367		.cmd_name = "GETSTATES",
1368		.cmd_cb = pf_handle_getstates,
1369		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1370		.cmd_priv = PRIV_NETINET_PF,
1371	},
1372	{
1373		.cmd_num = PFNL_CMD_GETCREATORS,
1374		.cmd_name = "GETCREATORS",
1375		.cmd_cb = pf_handle_getcreators,
1376		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1377		.cmd_priv = PRIV_NETINET_PF,
1378	},
1379	{
1380		.cmd_num = PFNL_CMD_START,
1381		.cmd_name = "START",
1382		.cmd_cb = pf_handle_start,
1383		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1384		.cmd_priv = PRIV_NETINET_PF,
1385	},
1386	{
1387		.cmd_num = PFNL_CMD_STOP,
1388		.cmd_name = "STOP",
1389		.cmd_cb = pf_handle_stop,
1390		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1391		.cmd_priv = PRIV_NETINET_PF,
1392	},
1393	{
1394		.cmd_num = PFNL_CMD_ADDRULE,
1395		.cmd_name = "ADDRULE",
1396		.cmd_cb = pf_handle_addrule,
1397		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1398		.cmd_priv = PRIV_NETINET_PF,
1399	},
1400	{
1401		.cmd_num = PFNL_CMD_GETRULES,
1402		.cmd_name = "GETRULES",
1403		.cmd_cb = pf_handle_getrules,
1404		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1405		.cmd_priv = PRIV_NETINET_PF,
1406	},
1407	{
1408		.cmd_num = PFNL_CMD_GETRULE,
1409		.cmd_name = "GETRULE",
1410		.cmd_cb = pf_handle_getrule,
1411		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1412		.cmd_priv = PRIV_NETINET_PF,
1413	},
1414	{
1415		.cmd_num = PFNL_CMD_CLRSTATES,
1416		.cmd_name = "CLRSTATES",
1417		.cmd_cb = pf_handle_clear_states,
1418		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1419		.cmd_priv = PRIV_NETINET_PF,
1420	},
1421	{
1422		.cmd_num = PFNL_CMD_KILLSTATES,
1423		.cmd_name = "KILLSTATES",
1424		.cmd_cb = pf_handle_kill_states,
1425		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1426		.cmd_priv = PRIV_NETINET_PF,
1427	},
1428	{
1429		.cmd_num = PFNL_CMD_SET_STATUSIF,
1430		.cmd_name = "SETSTATUSIF",
1431		.cmd_cb = pf_handle_set_statusif,
1432		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1433		.cmd_priv = PRIV_NETINET_PF,
1434	},
1435	{
1436		.cmd_num = PFNL_CMD_GET_STATUS,
1437		.cmd_name = "GETSTATUS",
1438		.cmd_cb = pf_handle_get_status,
1439		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1440		.cmd_priv = PRIV_NETINET_PF,
1441	},
1442	{
1443		.cmd_num = PFNL_CMD_CLEAR_STATUS,
1444		.cmd_name = "CLEARSTATUS",
1445		.cmd_cb = pf_handle_clear_status,
1446		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1447		.cmd_priv = PRIV_NETINET_PF,
1448	},
1449	{
1450		.cmd_num = PFNL_CMD_NATLOOK,
1451		.cmd_name = "NATLOOK",
1452		.cmd_cb = pf_handle_natlook,
1453		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1454		.cmd_priv = PRIV_NETINET_PF,
1455	},
1456	{
1457		.cmd_num = PFNL_CMD_SET_DEBUG,
1458		.cmd_name = "SET_DEBUG",
1459		.cmd_cb = pf_handle_set_debug,
1460		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1461		.cmd_priv = PRIV_NETINET_PF,
1462	},
1463};
1464
1465void
1466pf_nl_register(void)
1467{
1468	NL_VERIFY_PARSERS(all_parsers);
1469
1470	family_id = genl_register_family(PFNL_FAMILY_NAME, 0, 2, PFNL_CMD_MAX);
1471	genl_register_cmds(PFNL_FAMILY_NAME, pf_cmds, NL_ARRAY_LEN(pf_cmds));
1472}
1473
1474void
1475pf_nl_unregister(void)
1476{
1477	genl_unregister_family(PFNL_FAMILY_NAME);
1478}
1479