xref: /freebsd/sys/netpfil/pf/pf_nl.c (revision 84a80eae6987bace1552882a7bb94e62fa1dc3ad)
12cef6288SAlexander V. Chernikov /*-
22cef6288SAlexander V. Chernikov  * SPDX-License-Identifier: BSD-2-Clause
32cef6288SAlexander V. Chernikov  *
42cef6288SAlexander V. Chernikov  * Copyright (c) 2023 Alexander V. Chernikov <melifaro@FreeBSD.org>
52cef6288SAlexander V. Chernikov  * Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
62cef6288SAlexander V. Chernikov  *
72cef6288SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
82cef6288SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
92cef6288SAlexander V. Chernikov  * are met:
102cef6288SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
112cef6288SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
122cef6288SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
132cef6288SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
142cef6288SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
152cef6288SAlexander V. Chernikov  *
162cef6288SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
172cef6288SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
182cef6288SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
192cef6288SAlexander V. Chernikov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
202cef6288SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
212cef6288SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
222cef6288SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
232cef6288SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
242cef6288SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252cef6288SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262cef6288SAlexander V. Chernikov  * SUCH DAMAGE.
272cef6288SAlexander V. Chernikov  *
282cef6288SAlexander V. Chernikov  */
2971d3c704SKristof Provost #include <sys/cdefs.h>
3071d3c704SKristof Provost #include "opt_inet.h"
3171d3c704SKristof Provost #include "opt_inet6.h"
322cef6288SAlexander V. Chernikov 
332cef6288SAlexander V. Chernikov #include <sys/param.h>
342cef6288SAlexander V. Chernikov #include <sys/malloc.h>
352cef6288SAlexander V. Chernikov #include <sys/mbuf.h>
36e774c1efSKristof Provost #include <sys/priv.h>
372cef6288SAlexander V. Chernikov #include <sys/socket.h>
387b2ab18fSKonstantin Belousov #include <sys/ucred.h>
392cef6288SAlexander V. Chernikov 
402cef6288SAlexander V. Chernikov #include <net/pfvar.h>
412cef6288SAlexander V. Chernikov 
422cef6288SAlexander V. Chernikov #include <netlink/netlink.h>
432cef6288SAlexander V. Chernikov #include <netlink/netlink_ctl.h>
442cef6288SAlexander V. Chernikov #include <netlink/netlink_generic.h>
452cef6288SAlexander V. Chernikov #include <netlink/netlink_message_writer.h>
462cef6288SAlexander V. Chernikov 
472cef6288SAlexander V. Chernikov #include <netpfil/pf/pf_nl.h>
482cef6288SAlexander V. Chernikov 
492cef6288SAlexander V. Chernikov #define	DEBUG_MOD_NAME	nl_pf
502cef6288SAlexander V. Chernikov #define	DEBUG_MAX_LEVEL	LOG_DEBUG3
512cef6288SAlexander V. Chernikov #include <netlink/netlink_debug.h>
522cef6288SAlexander V. Chernikov _DECLARE_DEBUG(LOG_DEBUG);
532cef6288SAlexander V. Chernikov 
542cef6288SAlexander V. Chernikov struct nl_parsed_state {
552cef6288SAlexander V. Chernikov 	uint8_t		version;
562cef6288SAlexander V. Chernikov 	uint32_t	id;
572cef6288SAlexander V. Chernikov 	uint32_t	creatorid;
58044eef6aSKristof Provost 	char		ifname[IFNAMSIZ];
59044eef6aSKristof Provost 	uint16_t	proto;
60044eef6aSKristof Provost 	sa_family_t	af;
61044eef6aSKristof Provost 	struct pf_addr	addr;
62044eef6aSKristof Provost 	struct pf_addr	mask;
632cef6288SAlexander V. Chernikov };
642cef6288SAlexander V. Chernikov 
652cef6288SAlexander V. Chernikov #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
662cef6288SAlexander V. Chernikov #define	_OUT(_field)	offsetof(struct nl_parsed_state, _field)
672cef6288SAlexander V. Chernikov static const struct nlattr_parser nla_p_state[] = {
682cef6288SAlexander V. Chernikov 	{ .type = PF_ST_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
692cef6288SAlexander V. Chernikov 	{ .type = PF_ST_CREATORID, .off = _OUT(creatorid), .cb = nlattr_get_uint32 },
70044eef6aSKristof Provost 	{ .type = PF_ST_IFNAME, .arg = (const void *)IFNAMSIZ, .off = _OUT(ifname), .cb = nlattr_get_chara },
71e8ff5e56SKristof Provost 	{ .type = PF_ST_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
72044eef6aSKristof Provost 	{ .type = PF_ST_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint16 },
73044eef6aSKristof Provost 	{ .type = PF_ST_FILTER_ADDR, .off = _OUT(addr), .cb = nlattr_get_in6_addr },
74044eef6aSKristof Provost 	{ .type = PF_ST_FILTER_MASK, .off = _OUT(mask), .cb = nlattr_get_in6_addr },
752cef6288SAlexander V. Chernikov };
762cef6288SAlexander V. Chernikov static const struct nlfield_parser nlf_p_generic[] = {
772cef6288SAlexander V. Chernikov 	{ .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 },
782cef6288SAlexander V. Chernikov };
792cef6288SAlexander V. Chernikov #undef _IN
802cef6288SAlexander V. Chernikov #undef _OUT
812cef6288SAlexander V. Chernikov NL_DECLARE_PARSER(state_parser, struct genlmsghdr, nlf_p_generic, nla_p_state);
822cef6288SAlexander V. Chernikov 
832cef6288SAlexander V. Chernikov static void
842cef6288SAlexander V. Chernikov dump_addr(struct nl_writer *nw, int attr, const struct pf_addr *addr, int af)
852cef6288SAlexander V. Chernikov {
862cef6288SAlexander V. Chernikov 	switch (af) {
872cef6288SAlexander V. Chernikov 	case AF_INET:
882cef6288SAlexander V. Chernikov 		nlattr_add(nw, attr, 4, &addr->v4);
892cef6288SAlexander V. Chernikov 		break;
902cef6288SAlexander V. Chernikov 	case AF_INET6:
912cef6288SAlexander V. Chernikov 		nlattr_add(nw, attr, 16, &addr->v6);
922cef6288SAlexander V. Chernikov 		break;
932cef6288SAlexander V. Chernikov 	};
942cef6288SAlexander V. Chernikov }
952cef6288SAlexander V. Chernikov 
962cef6288SAlexander V. Chernikov static bool
972cef6288SAlexander V. Chernikov dump_state_peer(struct nl_writer *nw, int attr, const struct pf_state_peer *peer)
982cef6288SAlexander V. Chernikov {
992cef6288SAlexander V. Chernikov 	int off = nlattr_add_nested(nw, attr);
1002cef6288SAlexander V. Chernikov 	if (off == 0)
1012cef6288SAlexander V. Chernikov 		return (false);
1022cef6288SAlexander V. Chernikov 
1032cef6288SAlexander V. Chernikov 	nlattr_add_u32(nw, PF_STP_SEQLO, peer->seqlo);
1042cef6288SAlexander V. Chernikov 	nlattr_add_u32(nw, PF_STP_SEQHI, peer->seqhi);
1052cef6288SAlexander V. Chernikov 	nlattr_add_u32(nw, PF_STP_SEQDIFF, peer->seqdiff);
1062cef6288SAlexander V. Chernikov 	nlattr_add_u16(nw, PF_STP_MAX_WIN, peer->max_win);
1072cef6288SAlexander V. Chernikov 	nlattr_add_u16(nw, PF_STP_MSS, peer->mss);
1082cef6288SAlexander V. Chernikov 	nlattr_add_u8(nw, PF_STP_STATE, peer->state);
1092cef6288SAlexander V. Chernikov 	nlattr_add_u8(nw, PF_STP_WSCALE, peer->wscale);
1102cef6288SAlexander V. Chernikov 
1112cef6288SAlexander V. Chernikov 	if (peer->scrub != NULL) {
1122cef6288SAlexander V. Chernikov 		struct pf_state_scrub *sc = peer->scrub;
1132cef6288SAlexander V. Chernikov 		uint16_t pfss_flags = sc->pfss_flags & PFSS_TIMESTAMP;
1142cef6288SAlexander V. Chernikov 
1152cef6288SAlexander V. Chernikov 		nlattr_add_u16(nw, PF_STP_PFSS_FLAGS, pfss_flags);
1162cef6288SAlexander V. Chernikov 		nlattr_add_u32(nw, PF_STP_PFSS_TS_MOD, sc->pfss_ts_mod);
1172cef6288SAlexander V. Chernikov 		nlattr_add_u8(nw, PF_STP_PFSS_TTL, sc->pfss_ttl);
1182cef6288SAlexander V. Chernikov 		nlattr_add_u8(nw, PF_STP_SCRUB_FLAG, PFSYNC_SCRUB_FLAG_VALID);
1192cef6288SAlexander V. Chernikov 	}
1202cef6288SAlexander V. Chernikov 	nlattr_set_len(nw, off);
1212cef6288SAlexander V. Chernikov 
1222cef6288SAlexander V. Chernikov 	return (true);
1232cef6288SAlexander V. Chernikov }
1242cef6288SAlexander V. Chernikov 
1252cef6288SAlexander V. Chernikov static bool
1262cef6288SAlexander V. Chernikov dump_state_key(struct nl_writer *nw, int attr, const struct pf_state_key *key)
1272cef6288SAlexander V. Chernikov {
1282cef6288SAlexander V. Chernikov 	int off = nlattr_add_nested(nw, attr);
1292cef6288SAlexander V. Chernikov 	if (off == 0)
1302cef6288SAlexander V. Chernikov 		return (false);
1312cef6288SAlexander V. Chernikov 
1322cef6288SAlexander V. Chernikov 	dump_addr(nw, PF_STK_ADDR0, &key->addr[0], key->af);
1332cef6288SAlexander V. Chernikov 	dump_addr(nw, PF_STK_ADDR1, &key->addr[1], key->af);
1342cef6288SAlexander V. Chernikov 	nlattr_add_u16(nw, PF_STK_PORT0, key->port[0]);
1352cef6288SAlexander V. Chernikov 	nlattr_add_u16(nw, PF_STK_PORT1, key->port[1]);
136ebe11b46SKristof Provost 	nlattr_add_u8(nw, PF_STK_AF, key->af);
137ebe11b46SKristof Provost 	nlattr_add_u16(nw, PF_STK_PROTO, key->proto);
1382cef6288SAlexander V. Chernikov 
1392cef6288SAlexander V. Chernikov 	nlattr_set_len(nw, off);
1402cef6288SAlexander V. Chernikov 
1412cef6288SAlexander V. Chernikov 	return (true);
1422cef6288SAlexander V. Chernikov }
1432cef6288SAlexander V. Chernikov 
1442cef6288SAlexander V. Chernikov static int
1452cef6288SAlexander V. Chernikov dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, struct pf_kstate *s,
1462cef6288SAlexander V. Chernikov     struct nl_pstate *npt)
1472cef6288SAlexander V. Chernikov {
1482cef6288SAlexander V. Chernikov 	struct nl_writer *nw = npt->nw;
1492cef6288SAlexander V. Chernikov 	int error = 0;
1502cef6288SAlexander V. Chernikov 	int af;
1512cef6288SAlexander V. Chernikov 	struct pf_state_key *key;
1522cef6288SAlexander V. Chernikov 
153b1e0f0ffSKristof Provost 	PF_STATE_LOCK_ASSERT(s);
154b1e0f0ffSKristof Provost 
1552cef6288SAlexander V. Chernikov 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1562cef6288SAlexander V. Chernikov 		goto enomem;
1572cef6288SAlexander V. Chernikov 
1582cef6288SAlexander V. Chernikov 	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1592cef6288SAlexander V. Chernikov 	ghdr_new->cmd = PFNL_CMD_GETSTATES;
1602cef6288SAlexander V. Chernikov 	ghdr_new->version = 0;
1612cef6288SAlexander V. Chernikov 	ghdr_new->reserved = 0;
1622cef6288SAlexander V. Chernikov 
1632cef6288SAlexander V. Chernikov 	nlattr_add_u64(nw, PF_ST_VERSION, PF_STATE_VERSION);
1642cef6288SAlexander V. Chernikov 
1652cef6288SAlexander V. Chernikov 	key = s->key[PF_SK_WIRE];
1662cef6288SAlexander V. Chernikov 	if (!dump_state_key(nw, PF_ST_KEY_WIRE, key))
1672cef6288SAlexander V. Chernikov 		goto enomem;
1682cef6288SAlexander V. Chernikov 	key = s->key[PF_SK_STACK];
1692cef6288SAlexander V. Chernikov 	if (!dump_state_key(nw, PF_ST_KEY_STACK, key))
1702cef6288SAlexander V. Chernikov 		goto enomem;
1712cef6288SAlexander V. Chernikov 
1722cef6288SAlexander V. Chernikov 	af = s->key[PF_SK_WIRE]->af;
1732cef6288SAlexander V. Chernikov 	nlattr_add_u8(nw, PF_ST_PROTO, s->key[PF_SK_WIRE]->proto);
1742cef6288SAlexander V. Chernikov 	nlattr_add_u8(nw, PF_ST_AF, af);
1752cef6288SAlexander V. Chernikov 
1762cef6288SAlexander V. Chernikov 	nlattr_add_string(nw, PF_ST_IFNAME, s->kif->pfik_name);
1772cef6288SAlexander V. Chernikov 	nlattr_add_string(nw, PF_ST_ORIG_IFNAME, s->orig_kif->pfik_name);
178c49c9da2SKajetan Staszkiewicz 	dump_addr(nw, PF_ST_RT_ADDR, &s->act.rt_addr, af);
17904932601SKristof Provost 	nlattr_add_u32(nw, PF_ST_CREATION, time_uptime - (s->creation / 1000));
1802cef6288SAlexander V. Chernikov 	uint32_t expire = pf_state_expires(s);
1812cef6288SAlexander V. Chernikov 	if (expire > time_uptime)
1822cef6288SAlexander V. Chernikov 		expire = expire - time_uptime;
1832cef6288SAlexander V. Chernikov 	nlattr_add_u32(nw, PF_ST_EXPIRE, expire);
1842cef6288SAlexander V. Chernikov 	nlattr_add_u8(nw, PF_ST_DIRECTION, s->direction);
1852cef6288SAlexander V. Chernikov 	nlattr_add_u8(nw, PF_ST_LOG, s->act.log);
1862cef6288SAlexander V. Chernikov 	nlattr_add_u8(nw, PF_ST_TIMEOUT, s->timeout);
1872cef6288SAlexander V. Chernikov 	nlattr_add_u16(nw, PF_ST_STATE_FLAGS, s->state_flags);
1882cef6288SAlexander V. Chernikov 	uint8_t sync_flags = 0;
1892cef6288SAlexander V. Chernikov 	if (s->src_node)
1902cef6288SAlexander V. Chernikov 		sync_flags |= PFSYNC_FLAG_SRCNODE;
1912cef6288SAlexander V. Chernikov 	if (s->nat_src_node)
1922cef6288SAlexander V. Chernikov 		sync_flags |= PFSYNC_FLAG_NATSRCNODE;
1932cef6288SAlexander V. Chernikov 	nlattr_add_u8(nw, PF_ST_SYNC_FLAGS, sync_flags);
1942cef6288SAlexander V. Chernikov 	nlattr_add_u64(nw, PF_ST_ID, s->id);
1952cef6288SAlexander V. Chernikov 	nlattr_add_u32(nw, PF_ST_CREATORID, htonl(s->creatorid));
1962cef6288SAlexander V. Chernikov 
197e5c64b26SKajetan Staszkiewicz 	nlattr_add_u32(nw, PF_ST_RULE, s->rule ? s->rule->nr : -1);
198e5c64b26SKajetan Staszkiewicz 	nlattr_add_u32(nw, PF_ST_ANCHOR, s->anchor ? s->anchor->nr : -1);
199e5c64b26SKajetan Staszkiewicz 	nlattr_add_u32(nw, PF_ST_NAT_RULE, s->nat_rule ? s->nat_rule->nr : -1);
2002cef6288SAlexander V. Chernikov 
2012cef6288SAlexander V. Chernikov 	nlattr_add_u64(nw, PF_ST_PACKETS0, s->packets[0]);
2022cef6288SAlexander V. Chernikov 	nlattr_add_u64(nw, PF_ST_PACKETS1, s->packets[1]);
2032cef6288SAlexander V. Chernikov 	nlattr_add_u64(nw, PF_ST_BYTES0, s->bytes[0]);
2042cef6288SAlexander V. Chernikov 	nlattr_add_u64(nw, PF_ST_BYTES1, s->bytes[1]);
205881bf881SKristof Provost 	nlattr_add_u32(nw, PF_ST_RTABLEID, s->act.rtableid);
206881bf881SKristof Provost 	nlattr_add_u8(nw, PF_ST_MIN_TTL, s->act.min_ttl);
207881bf881SKristof Provost 	nlattr_add_u16(nw, PF_ST_MAX_MSS, s->act.max_mss);
208881bf881SKristof Provost 	nlattr_add_u16(nw, PF_ST_DNPIPE, s->act.dnpipe);
209881bf881SKristof Provost 	nlattr_add_u16(nw, PF_ST_DNRPIPE, s->act.dnrpipe);
210c49c9da2SKajetan Staszkiewicz 	nlattr_add_u8(nw, PF_ST_RT, s->act.rt);
211c49c9da2SKajetan Staszkiewicz 	if (s->act.rt_kif != NULL)
212c49c9da2SKajetan Staszkiewicz 		nlattr_add_string(nw, PF_ST_RT_IFNAME, s->act.rt_kif->pfik_name);
2132cef6288SAlexander V. Chernikov 
2142cef6288SAlexander V. Chernikov 	if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src))
2152cef6288SAlexander V. Chernikov 		goto enomem;
2162cef6288SAlexander V. Chernikov 	if (!dump_state_peer(nw, PF_ST_PEER_DST, &s->dst))
2172cef6288SAlexander V. Chernikov 		goto enomem;
2182cef6288SAlexander V. Chernikov 
2192cef6288SAlexander V. Chernikov 	if (nlmsg_end(nw))
2202cef6288SAlexander V. Chernikov 		return (0);
2212cef6288SAlexander V. Chernikov 
2222cef6288SAlexander V. Chernikov enomem:
2232cef6288SAlexander V. Chernikov 	error = ENOMEM;
2242cef6288SAlexander V. Chernikov 	nlmsg_abort(nw);
2252cef6288SAlexander V. Chernikov 	return (error);
2262cef6288SAlexander V. Chernikov }
2272cef6288SAlexander V. Chernikov 
2282cef6288SAlexander V. Chernikov static int
2292cef6288SAlexander V. Chernikov handle_dumpstates(struct nlpcb *nlp, struct nl_parsed_state *attrs,
2302cef6288SAlexander V. Chernikov     struct nlmsghdr *hdr, struct nl_pstate *npt)
2312cef6288SAlexander V. Chernikov {
2322cef6288SAlexander V. Chernikov 	int error = 0;
2332cef6288SAlexander V. Chernikov 
2342cef6288SAlexander V. Chernikov 	hdr->nlmsg_flags |= NLM_F_MULTI;
2352cef6288SAlexander V. Chernikov 
236271f1469SKristof Provost 	for (int i = 0; i <= V_pf_hashmask; i++) {
2372cef6288SAlexander V. Chernikov 		struct pf_idhash *ih = &V_pf_idhash[i];
2382cef6288SAlexander V. Chernikov 		struct pf_kstate *s;
2392cef6288SAlexander V. Chernikov 
2402cef6288SAlexander V. Chernikov 		if (LIST_EMPTY(&ih->states))
2412cef6288SAlexander V. Chernikov 			continue;
2422cef6288SAlexander V. Chernikov 
2432cef6288SAlexander V. Chernikov 		PF_HASHROW_LOCK(ih);
2442cef6288SAlexander V. Chernikov 		LIST_FOREACH(s, &ih->states, entry) {
245044eef6aSKristof Provost 			sa_family_t af = s->key[PF_SK_WIRE]->af;
246044eef6aSKristof Provost 
247044eef6aSKristof Provost 			if (s->timeout == PFTM_UNLINKED)
248044eef6aSKristof Provost 				continue;
249044eef6aSKristof Provost 
250044eef6aSKristof Provost 			/* Filter */
251044eef6aSKristof Provost 			if (attrs->creatorid != 0 && s->creatorid != attrs->creatorid)
252044eef6aSKristof Provost 				continue;
253044eef6aSKristof Provost 			if (attrs->ifname[0] != 0 &&
254044eef6aSKristof Provost 			    strncmp(attrs->ifname, s->kif->pfik_name, IFNAMSIZ) != 0)
255044eef6aSKristof Provost 				continue;
256044eef6aSKristof Provost 			if (attrs->proto != 0 && s->key[PF_SK_WIRE]->proto != attrs->proto)
257044eef6aSKristof Provost 				continue;
258044eef6aSKristof Provost 			if (attrs->af != 0 && af != attrs->af)
259044eef6aSKristof Provost 				continue;
260044eef6aSKristof Provost 			if (pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[0],
261044eef6aSKristof Provost 			    &attrs->mask, &attrs->addr, af) &&
262044eef6aSKristof Provost 			    pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[1],
263044eef6aSKristof Provost 			    &attrs->mask, &attrs->addr, af) &&
264044eef6aSKristof Provost 			    pf_match_addr(1, &s->key[PF_SK_STACK]->addr[0],
265044eef6aSKristof Provost 			    &attrs->mask, &attrs->addr, af) &&
266044eef6aSKristof Provost 			    pf_match_addr(1, &s->key[PF_SK_STACK]->addr[1],
267044eef6aSKristof Provost 			    &attrs->mask, &attrs->addr, af))
268044eef6aSKristof Provost 				continue;
269044eef6aSKristof Provost 
2702cef6288SAlexander V. Chernikov 			error = dump_state(nlp, hdr, s, npt);
2712cef6288SAlexander V. Chernikov 			if (error != 0)
2722cef6288SAlexander V. Chernikov 				break;
2732cef6288SAlexander V. Chernikov 		}
2742cef6288SAlexander V. Chernikov 		PF_HASHROW_UNLOCK(ih);
2752cef6288SAlexander V. Chernikov 	}
2762cef6288SAlexander V. Chernikov 
2772cef6288SAlexander V. Chernikov 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
2782cef6288SAlexander V. Chernikov 		NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
2792cef6288SAlexander V. Chernikov 		return (ENOMEM);
2802cef6288SAlexander V. Chernikov 	}
2812cef6288SAlexander V. Chernikov 
2822cef6288SAlexander V. Chernikov 	return (error);
2832cef6288SAlexander V. Chernikov }
2842cef6288SAlexander V. Chernikov 
2852cef6288SAlexander V. Chernikov static int
2862cef6288SAlexander V. Chernikov handle_getstate(struct nlpcb *nlp, struct nl_parsed_state *attrs,
2872cef6288SAlexander V. Chernikov     struct nlmsghdr *hdr, struct nl_pstate *npt)
2882cef6288SAlexander V. Chernikov {
289b1e0f0ffSKristof Provost 	struct pf_kstate *s;
290b1e0f0ffSKristof Provost 	int ret;
291b1e0f0ffSKristof Provost 
292b1e0f0ffSKristof Provost 	s = pf_find_state_byid(attrs->id, attrs->creatorid);
2932cef6288SAlexander V. Chernikov 	if (s == NULL)
2942cef6288SAlexander V. Chernikov 		return (ENOENT);
295b1e0f0ffSKristof Provost 	ret = dump_state(nlp, hdr, s, npt);
296b1e0f0ffSKristof Provost 	PF_STATE_UNLOCK(s);
297b1e0f0ffSKristof Provost 
298b1e0f0ffSKristof Provost 	return (ret);
2992cef6288SAlexander V. Chernikov }
3002cef6288SAlexander V. Chernikov 
3012cef6288SAlexander V. Chernikov static int
302a7191e5dSKristof Provost dump_creatorid(struct nlpcb *nlp, const struct nlmsghdr *hdr, uint32_t creator,
303a7191e5dSKristof Provost     struct nl_pstate *npt)
304a7191e5dSKristof Provost {
305a7191e5dSKristof Provost 	struct nl_writer *nw = npt->nw;
306a7191e5dSKristof Provost 
307a7191e5dSKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
308a7191e5dSKristof Provost 		goto enomem;
309a7191e5dSKristof Provost 
310a7191e5dSKristof Provost 	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
311a7191e5dSKristof Provost 	ghdr_new->cmd = PFNL_CMD_GETCREATORS;
312a7191e5dSKristof Provost 	ghdr_new->version = 0;
313a7191e5dSKristof Provost 	ghdr_new->reserved = 0;
314a7191e5dSKristof Provost 
315a7191e5dSKristof Provost 	nlattr_add_u32(nw, PF_ST_CREATORID, htonl(creator));
316a7191e5dSKristof Provost 
317a7191e5dSKristof Provost 	if (nlmsg_end(nw))
318a7191e5dSKristof Provost 		return (0);
319a7191e5dSKristof Provost 
320a7191e5dSKristof Provost enomem:
321a7191e5dSKristof Provost 	nlmsg_abort(nw);
322a7191e5dSKristof Provost 	return (ENOMEM);
323a7191e5dSKristof Provost }
324a7191e5dSKristof Provost 
325a7191e5dSKristof Provost static int
3262cef6288SAlexander V. Chernikov pf_handle_getstates(struct nlmsghdr *hdr, struct nl_pstate *npt)
3272cef6288SAlexander V. Chernikov {
3282cef6288SAlexander V. Chernikov 	int error;
3292cef6288SAlexander V. Chernikov 
3302cef6288SAlexander V. Chernikov 	struct nl_parsed_state attrs = {};
3312cef6288SAlexander V. Chernikov 	error = nl_parse_nlmsg(hdr, &state_parser, npt, &attrs);
3322cef6288SAlexander V. Chernikov 	if (error != 0)
3332cef6288SAlexander V. Chernikov 		return (error);
3342cef6288SAlexander V. Chernikov 
3352cef6288SAlexander V. Chernikov 	if (attrs.id != 0)
3362cef6288SAlexander V. Chernikov 		error = handle_getstate(npt->nlp, &attrs, hdr, npt);
3372cef6288SAlexander V. Chernikov 	else
3382cef6288SAlexander V. Chernikov 		error = handle_dumpstates(npt->nlp, &attrs, hdr, npt);
3392cef6288SAlexander V. Chernikov 
3402cef6288SAlexander V. Chernikov 	return (error);
3412cef6288SAlexander V. Chernikov }
3422cef6288SAlexander V. Chernikov 
343a7191e5dSKristof Provost static int
344a7191e5dSKristof Provost pf_handle_getcreators(struct nlmsghdr *hdr, struct nl_pstate *npt)
345a7191e5dSKristof Provost {
346a7191e5dSKristof Provost 	uint32_t creators[16];
347a7191e5dSKristof Provost 	int error = 0;
348a7191e5dSKristof Provost 
349a7191e5dSKristof Provost 	bzero(creators, sizeof(creators));
350a7191e5dSKristof Provost 
351271f1469SKristof Provost 	for (int i = 0; i < V_pf_hashmask; i++) {
352a7191e5dSKristof Provost 		struct pf_idhash *ih = &V_pf_idhash[i];
353a7191e5dSKristof Provost 		struct pf_kstate *s;
354a7191e5dSKristof Provost 
355a7191e5dSKristof Provost 		if (LIST_EMPTY(&ih->states))
356a7191e5dSKristof Provost 			continue;
357a7191e5dSKristof Provost 
358a7191e5dSKristof Provost 		PF_HASHROW_LOCK(ih);
359a7191e5dSKristof Provost 		LIST_FOREACH(s, &ih->states, entry) {
360a7191e5dSKristof Provost 			int j;
361a7191e5dSKristof Provost 			if (s->timeout == PFTM_UNLINKED)
362a7191e5dSKristof Provost 				continue;
363a7191e5dSKristof Provost 
364a7191e5dSKristof Provost 			for (j = 0; j < nitems(creators); j++) {
365a7191e5dSKristof Provost 				if (creators[j] == s->creatorid)
366a7191e5dSKristof Provost 					break;
367a7191e5dSKristof Provost 				if (creators[j] == 0) {
368a7191e5dSKristof Provost 					creators[j] = s->creatorid;
369a7191e5dSKristof Provost 					break;
370a7191e5dSKristof Provost 				}
371a7191e5dSKristof Provost 			}
372a7191e5dSKristof Provost 			if (j == nitems(creators))
373a7191e5dSKristof Provost 				printf("Warning: too many creators!\n");
374a7191e5dSKristof Provost 		}
375a7191e5dSKristof Provost 		PF_HASHROW_UNLOCK(ih);
376a7191e5dSKristof Provost 	}
377a7191e5dSKristof Provost 
378a7191e5dSKristof Provost 	hdr->nlmsg_flags |= NLM_F_MULTI;
379a7191e5dSKristof Provost 	for (int i = 0; i < nitems(creators); i++) {
380a7191e5dSKristof Provost 		if (creators[i] == 0)
381a7191e5dSKristof Provost 			break;
382a7191e5dSKristof Provost 		error = dump_creatorid(npt->nlp, hdr, creators[i], npt);
383a7191e5dSKristof Provost 	}
384a7191e5dSKristof Provost 
385a7191e5dSKristof Provost 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
386a7191e5dSKristof Provost 		NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
387a7191e5dSKristof Provost 		return (ENOMEM);
388a7191e5dSKristof Provost 	}
389a7191e5dSKristof Provost 
390a7191e5dSKristof Provost 	return (error);
391a7191e5dSKristof Provost }
392a7191e5dSKristof Provost 
39381647eb6SKristof Provost static int
39481647eb6SKristof Provost pf_handle_start(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused)
39581647eb6SKristof Provost {
39681647eb6SKristof Provost 	return (pf_start());
39781647eb6SKristof Provost }
39881647eb6SKristof Provost 
39981647eb6SKristof Provost static int
40081647eb6SKristof Provost pf_handle_stop(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused)
40181647eb6SKristof Provost {
40281647eb6SKristof Provost 	return (pf_stop());
40381647eb6SKristof Provost }
40481647eb6SKristof Provost 
405ffbf2595SKristof Provost #define _OUT(_field)	offsetof(struct pf_addr_wrap, _field)
406ffbf2595SKristof Provost static const struct nlattr_parser nla_p_addr_wrap[] = {
407ffbf2595SKristof Provost 	{ .type = PF_AT_ADDR, .off = _OUT(v.a.addr), .cb = nlattr_get_in6_addr },
408ffbf2595SKristof Provost 	{ .type = PF_AT_MASK, .off = _OUT(v.a.mask), .cb = nlattr_get_in6_addr },
409ffbf2595SKristof Provost 	{ .type = PF_AT_IFNAME, .off = _OUT(v.ifname), .arg = (void *)IFNAMSIZ,.cb = nlattr_get_chara },
410ffbf2595SKristof Provost 	{ .type = PF_AT_TABLENAME, .off = _OUT(v.tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
411ffbf2595SKristof Provost 	{ .type = PF_AT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
412ffbf2595SKristof Provost 	{ .type = PF_AT_IFLAGS, .off = _OUT(iflags), .cb = nlattr_get_uint8 },
413ffbf2595SKristof Provost };
414ffbf2595SKristof Provost NL_DECLARE_ATTR_PARSER(addr_wrap_parser, nla_p_addr_wrap);
415ffbf2595SKristof Provost #undef _OUT
416ffbf2595SKristof Provost 
417777a4702SKristof Provost static bool
418777a4702SKristof Provost nlattr_add_addr_wrap(struct nl_writer *nw, int attrtype, struct pf_addr_wrap *a)
419777a4702SKristof Provost {
420777a4702SKristof Provost 	int off = nlattr_add_nested(nw, attrtype);
421777a4702SKristof Provost 
422777a4702SKristof Provost 	nlattr_add_in6_addr(nw, PF_AT_ADDR, &a->v.a.addr.v6);
423777a4702SKristof Provost 	nlattr_add_in6_addr(nw, PF_AT_MASK, &a->v.a.mask.v6);
424777a4702SKristof Provost 	nlattr_add_u8(nw, PF_AT_TYPE, a->type);
425777a4702SKristof Provost 	nlattr_add_u8(nw, PF_AT_IFLAGS, a->iflags);
426777a4702SKristof Provost 
427777a4702SKristof Provost 	if (a->type == PF_ADDR_DYNIFTL) {
428777a4702SKristof Provost 		nlattr_add_string(nw, PF_AT_IFNAME, a->v.ifname);
4291fc0dac5SKajetan Staszkiewicz 		nlattr_add_u32(nw, PF_AT_DYNCNT, a->p.dyncnt);
430777a4702SKristof Provost 	} else if (a->type == PF_ADDR_TABLE) {
431777a4702SKristof Provost 		nlattr_add_string(nw, PF_AT_TABLENAME, a->v.tblname);
4321fc0dac5SKajetan Staszkiewicz 		nlattr_add_u32(nw, PF_AT_TBLCNT, a->p.tblcnt);
433777a4702SKristof Provost 	}
434777a4702SKristof Provost 
435777a4702SKristof Provost 	nlattr_set_len(nw, off);
436777a4702SKristof Provost 
437777a4702SKristof Provost 	return (true);
438777a4702SKristof Provost }
439777a4702SKristof Provost 
440ffbf2595SKristof Provost #define _OUT(_field)	offsetof(struct pf_rule_addr, _field)
441ffbf2595SKristof Provost static const struct nlattr_parser nla_p_ruleaddr[] = {
442ffbf2595SKristof Provost 	{ .type = PF_RAT_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = nlattr_get_nested },
443ffbf2595SKristof Provost 	{ .type = PF_RAT_SRC_PORT, .off = _OUT(port[0]), .cb = nlattr_get_uint16 },
444ffbf2595SKristof Provost 	{ .type = PF_RAT_DST_PORT, .off = _OUT(port[1]), .cb = nlattr_get_uint16 },
445ffbf2595SKristof Provost 	{ .type = PF_RAT_NEG, .off = _OUT(neg), .cb = nlattr_get_uint8 },
446ffbf2595SKristof Provost 	{ .type = PF_RAT_OP, .off = _OUT(port_op), .cb = nlattr_get_uint8 },
447ffbf2595SKristof Provost };
448ffbf2595SKristof Provost NL_DECLARE_ATTR_PARSER(rule_addr_parser, nla_p_ruleaddr);
449ffbf2595SKristof Provost #undef _OUT
450ffbf2595SKristof Provost 
451777a4702SKristof Provost static bool
452777a4702SKristof Provost nlattr_add_rule_addr(struct nl_writer *nw, int attrtype, struct pf_rule_addr *r)
453777a4702SKristof Provost {
4541fc0dac5SKajetan Staszkiewicz 	struct pf_addr_wrap aw = {0};
455777a4702SKristof Provost 	int off = nlattr_add_nested(nw, attrtype);
456777a4702SKristof Provost 
4571fc0dac5SKajetan Staszkiewicz 	bcopy(&(r->addr), &aw, sizeof(struct pf_addr_wrap));
4581fc0dac5SKajetan Staszkiewicz 	pf_addr_copyout(&aw);
4591fc0dac5SKajetan Staszkiewicz 
4601fc0dac5SKajetan Staszkiewicz 	nlattr_add_addr_wrap(nw, PF_RAT_ADDR, &aw);
461777a4702SKristof Provost 	nlattr_add_u16(nw, PF_RAT_SRC_PORT, r->port[0]);
462777a4702SKristof Provost 	nlattr_add_u16(nw, PF_RAT_DST_PORT, r->port[1]);
463777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RAT_NEG, r->neg);
464777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RAT_OP, r->port_op);
465777a4702SKristof Provost 
466777a4702SKristof Provost 	nlattr_set_len(nw, off);
467777a4702SKristof Provost 
468777a4702SKristof Provost 	return (true);
469777a4702SKristof Provost }
470777a4702SKristof Provost 
471ffbf2595SKristof Provost #define _OUT(_field)	offsetof(struct pf_mape_portset, _field)
472ffbf2595SKristof Provost static const struct nlattr_parser nla_p_mape_portset[] = {
473ffbf2595SKristof Provost 	{ .type = PF_MET_OFFSET, .off = _OUT(offset), .cb = nlattr_get_uint8 },
474ffbf2595SKristof Provost 	{ .type = PF_MET_PSID_LEN, .off = _OUT(psidlen), .cb = nlattr_get_uint8 },
475ffbf2595SKristof Provost 	{. type = PF_MET_PSID, .off = _OUT(psid), .cb = nlattr_get_uint16 },
476ffbf2595SKristof Provost };
477ffbf2595SKristof Provost NL_DECLARE_ATTR_PARSER(mape_portset_parser, nla_p_mape_portset);
478ffbf2595SKristof Provost #undef _OUT
479ffbf2595SKristof Provost 
480777a4702SKristof Provost static bool
481777a4702SKristof Provost nlattr_add_mape_portset(struct nl_writer *nw, int attrtype, const struct pf_mape_portset *m)
482777a4702SKristof Provost {
483777a4702SKristof Provost 	int off = nlattr_add_nested(nw, attrtype);
484777a4702SKristof Provost 
485777a4702SKristof Provost 	nlattr_add_u8(nw, PF_MET_OFFSET, m->offset);
486777a4702SKristof Provost 	nlattr_add_u8(nw, PF_MET_PSID_LEN, m->psidlen);
487777a4702SKristof Provost 	nlattr_add_u16(nw, PF_MET_PSID, m->psid);
488777a4702SKristof Provost 
489777a4702SKristof Provost 	nlattr_set_len(nw, off);
490777a4702SKristof Provost 
491777a4702SKristof Provost 	return (true);
492777a4702SKristof Provost }
493777a4702SKristof Provost 
494ffbf2595SKristof Provost struct nl_parsed_labels
495ffbf2595SKristof Provost {
496ffbf2595SKristof Provost 	char		labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
497ffbf2595SKristof Provost 	uint32_t	i;
498ffbf2595SKristof Provost };
499ffbf2595SKristof Provost 
500ffbf2595SKristof Provost static int
501ffbf2595SKristof Provost nlattr_get_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt,
502ffbf2595SKristof Provost     const void *arg, void *target)
503ffbf2595SKristof Provost {
504ffbf2595SKristof Provost 	struct nl_parsed_labels *l = (struct nl_parsed_labels *)target;
505ffbf2595SKristof Provost 	int ret;
506ffbf2595SKristof Provost 
507ffbf2595SKristof Provost 	if (l->i >= PF_RULE_MAX_LABEL_COUNT)
508ffbf2595SKristof Provost 		return (E2BIG);
509ffbf2595SKristof Provost 
510ffbf2595SKristof Provost 	ret = nlattr_get_chara(nla, npt, (void *)PF_RULE_LABEL_SIZE,
511ffbf2595SKristof Provost 	    l->labels[l->i]);
512ffbf2595SKristof Provost 	if (ret == 0)
513ffbf2595SKristof Provost 		l->i++;
514ffbf2595SKristof Provost 
515ffbf2595SKristof Provost 	return (ret);
516ffbf2595SKristof Provost }
517ffbf2595SKristof Provost 
518ffbf2595SKristof Provost #define _OUT(_field)	offsetof(struct nl_parsed_labels, _field)
519ffbf2595SKristof Provost static const struct nlattr_parser nla_p_labels[] = {
520ffbf2595SKristof Provost 	{ .type = PF_LT_LABEL, .off = 0, .cb = nlattr_get_pf_rule_labels },
521ffbf2595SKristof Provost };
522ffbf2595SKristof Provost NL_DECLARE_ATTR_PARSER(rule_labels_parser, nla_p_labels);
523ffbf2595SKristof Provost #undef _OUT
524ffbf2595SKristof Provost 
525ffbf2595SKristof Provost static int
526ffbf2595SKristof Provost nlattr_get_nested_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
527ffbf2595SKristof Provost {
528ffbf2595SKristof Provost 	struct nl_parsed_labels parsed_labels = { };
529ffbf2595SKristof Provost 	int error;
530ffbf2595SKristof Provost 
531ffbf2595SKristof Provost 	/* Assumes target points to the beginning of the structure */
532ffbf2595SKristof Provost 	error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &rule_labels_parser, npt, &parsed_labels);
533ffbf2595SKristof Provost 	if (error != 0)
534ffbf2595SKristof Provost 		return (error);
535ffbf2595SKristof Provost 
53684ff9e91SKristof Provost 	memcpy(target, parsed_labels.labels, sizeof(parsed_labels.labels));
537ffbf2595SKristof Provost 
538ffbf2595SKristof Provost 	return (0);
539ffbf2595SKristof Provost }
540ffbf2595SKristof Provost 
541777a4702SKristof Provost static bool
542777a4702SKristof Provost nlattr_add_labels(struct nl_writer *nw, int attrtype, const struct pf_krule *r)
543777a4702SKristof Provost {
544777a4702SKristof Provost 	int off = nlattr_add_nested(nw, attrtype);
545777a4702SKristof Provost 	int i = 0;
546777a4702SKristof Provost 
547777a4702SKristof Provost 	while (r->label[i][0] != 0
548777a4702SKristof Provost 	    && i < PF_RULE_MAX_LABEL_COUNT) {
549777a4702SKristof Provost 		nlattr_add_string(nw, PF_LT_LABEL, r->label[i]);
550777a4702SKristof Provost 		i++;
551777a4702SKristof Provost 	}
552777a4702SKristof Provost 
553777a4702SKristof Provost 	nlattr_set_len(nw, off);
554777a4702SKristof Provost 
555777a4702SKristof Provost 	return (true);
556777a4702SKristof Provost }
557777a4702SKristof Provost 
558ffbf2595SKristof Provost #define _OUT(_field)	offsetof(struct pf_kpool, _field)
559ffbf2595SKristof Provost static const struct nlattr_parser nla_p_pool[] = {
560ffbf2595SKristof Provost 	{ .type = PF_PT_KEY, .off = _OUT(key), .arg = (void *)sizeof(struct pf_poolhashkey), .cb = nlattr_get_bytes },
561ffbf2595SKristof Provost 	{ .type = PF_PT_COUNTER, .off = _OUT(counter), .cb = nlattr_get_in6_addr },
562ffbf2595SKristof Provost 	{ .type = PF_PT_TBLIDX, .off = _OUT(tblidx), .cb = nlattr_get_uint32 },
563ffbf2595SKristof Provost 	{ .type = PF_PT_PROXY_SRC_PORT, .off = _OUT(proxy_port[0]), .cb = nlattr_get_uint16 },
564ffbf2595SKristof Provost 	{ .type = PF_PT_PROXY_DST_PORT, .off = _OUT(proxy_port[1]), .cb = nlattr_get_uint16 },
565ffbf2595SKristof Provost 	{ .type = PF_PT_OPTS, .off = _OUT(opts), .cb = nlattr_get_uint8 },
566ffbf2595SKristof Provost 	{ .type = PF_PT_MAPE, .off = _OUT(mape), .arg = &mape_portset_parser, .cb = nlattr_get_nested },
567ffbf2595SKristof Provost };
568ffbf2595SKristof Provost NL_DECLARE_ATTR_PARSER(pool_parser, nla_p_pool);
569ffbf2595SKristof Provost #undef _OUT
570ffbf2595SKristof Provost 
571777a4702SKristof Provost static bool
572777a4702SKristof Provost nlattr_add_pool(struct nl_writer *nw, int attrtype, const struct pf_kpool *pool)
573777a4702SKristof Provost {
574777a4702SKristof Provost 	int off = nlattr_add_nested(nw, attrtype);
575777a4702SKristof Provost 
576777a4702SKristof Provost 	nlattr_add(nw, PF_PT_KEY, sizeof(struct pf_poolhashkey), &pool->key);
577777a4702SKristof Provost 	nlattr_add_in6_addr(nw, PF_PT_COUNTER, (const struct in6_addr *)&pool->counter);
578777a4702SKristof Provost 	nlattr_add_u32(nw, PF_PT_TBLIDX, pool->tblidx);
579777a4702SKristof Provost 	nlattr_add_u16(nw, PF_PT_PROXY_SRC_PORT, pool->proxy_port[0]);
580777a4702SKristof Provost 	nlattr_add_u16(nw, PF_PT_PROXY_DST_PORT, pool->proxy_port[1]);
581777a4702SKristof Provost 	nlattr_add_u8(nw, PF_PT_OPTS, pool->opts);
582777a4702SKristof Provost 	nlattr_add_mape_portset(nw, PF_PT_MAPE, &pool->mape);
583777a4702SKristof Provost 
584777a4702SKristof Provost 	nlattr_set_len(nw, off);
585777a4702SKristof Provost 
586777a4702SKristof Provost 	return (true);
587777a4702SKristof Provost }
588777a4702SKristof Provost 
589ffbf2595SKristof Provost #define _OUT(_field)	offsetof(struct pf_rule_uid, _field)
590ffbf2595SKristof Provost static const struct nlattr_parser nla_p_rule_uid[] = {
591ffbf2595SKristof Provost 	{ .type = PF_RUT_UID_LOW, .off = _OUT(uid[0]), .cb = nlattr_get_uint32 },
592ffbf2595SKristof Provost 	{ .type = PF_RUT_UID_HIGH, .off = _OUT(uid[1]), .cb = nlattr_get_uint32 },
593ffbf2595SKristof Provost 	{ .type = PF_RUT_OP, .off = _OUT(op), .cb = nlattr_get_uint8 },
594ffbf2595SKristof Provost };
595ffbf2595SKristof Provost NL_DECLARE_ATTR_PARSER(rule_uid_parser, nla_p_rule_uid);
596ffbf2595SKristof Provost #undef _OUT
597ffbf2595SKristof Provost 
598777a4702SKristof Provost static bool
599777a4702SKristof Provost nlattr_add_rule_uid(struct nl_writer *nw, int attrtype, const struct pf_rule_uid *u)
600777a4702SKristof Provost {
601777a4702SKristof Provost 	int off = nlattr_add_nested(nw, attrtype);
602777a4702SKristof Provost 
603777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RUT_UID_LOW, u->uid[0]);
604777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RUT_UID_HIGH, u->uid[1]);
605777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RUT_OP, u->op);
606777a4702SKristof Provost 
607777a4702SKristof Provost 	nlattr_set_len(nw, off);
608777a4702SKristof Provost 
609777a4702SKristof Provost 	return (true);
610777a4702SKristof Provost }
611777a4702SKristof Provost 
612ffbf2595SKristof Provost struct nl_parsed_timeouts
613ffbf2595SKristof Provost {
614ffbf2595SKristof Provost 	uint32_t	timeouts[PFTM_MAX];
615ffbf2595SKristof Provost 	uint32_t	i;
616ffbf2595SKristof Provost };
617ffbf2595SKristof Provost 
618ffbf2595SKristof Provost static int
619ffbf2595SKristof Provost nlattr_get_pf_timeout(struct nlattr *nla, struct nl_pstate *npt,
620ffbf2595SKristof Provost     const void *arg, void *target)
621ffbf2595SKristof Provost {
622ffbf2595SKristof Provost 	struct nl_parsed_timeouts *t = (struct nl_parsed_timeouts *)target;
623ffbf2595SKristof Provost 	int ret;
624ffbf2595SKristof Provost 
625ffbf2595SKristof Provost 	if (t->i >= PFTM_MAX)
626ffbf2595SKristof Provost 		return (E2BIG);
627ffbf2595SKristof Provost 
628ffbf2595SKristof Provost 	ret = nlattr_get_uint32(nla, npt, NULL, &t->timeouts[t->i]);
629ffbf2595SKristof Provost 	if (ret == 0)
630ffbf2595SKristof Provost 		t->i++;
631ffbf2595SKristof Provost 
632ffbf2595SKristof Provost 	return (ret);
633ffbf2595SKristof Provost }
634ffbf2595SKristof Provost 
635ffbf2595SKristof Provost #define _OUT(_field)	offsetof(struct nl_parsed_timeout, _field)
636ffbf2595SKristof Provost static const struct nlattr_parser nla_p_timeouts[] = {
637ffbf2595SKristof Provost 	{ .type = PF_TT_TIMEOUT, .off = 0, .cb = nlattr_get_pf_timeout },
638ffbf2595SKristof Provost };
639ffbf2595SKristof Provost NL_DECLARE_ATTR_PARSER(timeout_parser, nla_p_timeouts);
640ffbf2595SKristof Provost #undef _OUT
641ffbf2595SKristof Provost 
642ffbf2595SKristof Provost static int
643ffbf2595SKristof Provost nlattr_get_nested_timeouts(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
644ffbf2595SKristof Provost {
645ffbf2595SKristof Provost 	struct nl_parsed_timeouts parsed_timeouts = { };
646ffbf2595SKristof Provost 	int error;
647ffbf2595SKristof Provost 
648ffbf2595SKristof Provost 	/* Assumes target points to the beginning of the structure */
649ffbf2595SKristof Provost 	error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &timeout_parser, npt, &parsed_timeouts);
650ffbf2595SKristof Provost 	if (error != 0)
651ffbf2595SKristof Provost 		return (error);
652ffbf2595SKristof Provost 
653ffbf2595SKristof Provost 	memcpy(target, parsed_timeouts.timeouts, sizeof(parsed_timeouts.timeouts));
654ffbf2595SKristof Provost 
655ffbf2595SKristof Provost 	return (0);
656ffbf2595SKristof Provost }
657ffbf2595SKristof Provost 
658777a4702SKristof Provost static bool
659777a4702SKristof Provost nlattr_add_timeout(struct nl_writer *nw, int attrtype, uint32_t *timeout)
660777a4702SKristof Provost {
661777a4702SKristof Provost 	int off = nlattr_add_nested(nw, attrtype);
662777a4702SKristof Provost 
663777a4702SKristof Provost 	for (int i = 0; i < PFTM_MAX; i++)
664777a4702SKristof Provost 		nlattr_add_u32(nw, PF_RT_TIMEOUT, timeout[i]);
665777a4702SKristof Provost 
666777a4702SKristof Provost 	nlattr_set_len(nw, off);
667777a4702SKristof Provost 
668777a4702SKristof Provost 	return (true);
669777a4702SKristof Provost }
670777a4702SKristof Provost 
671ffbf2595SKristof Provost #define _OUT(_field)	offsetof(struct pf_krule, _field)
672ffbf2595SKristof Provost static const struct nlattr_parser nla_p_rule[] = {
673ffbf2595SKristof Provost 	{ .type = PF_RT_SRC, .off = _OUT(src), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
674ffbf2595SKristof Provost 	{ .type = PF_RT_DST, .off = _OUT(dst), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
675ffbf2595SKristof Provost 	{ .type = PF_RT_RIDENTIFIER, .off = _OUT(ridentifier), .cb = nlattr_get_uint32 },
676ffbf2595SKristof Provost 	{ .type = PF_RT_LABELS, .off = _OUT(label), .arg = &rule_labels_parser,.cb = nlattr_get_nested_pf_rule_labels },
677ffbf2595SKristof Provost 	{ .type = PF_RT_IFNAME, .off = _OUT(ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
678ffbf2595SKristof Provost 	{ .type = PF_RT_QNAME, .off = _OUT(qname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
679ffbf2595SKristof Provost 	{ .type = PF_RT_PQNAME, .off = _OUT(pqname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
680ffbf2595SKristof Provost 	{ .type = PF_RT_TAGNAME, .off = _OUT(tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
681ffbf2595SKristof Provost 	{ .type = PF_RT_MATCH_TAGNAME, .off = _OUT(match_tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
682ffbf2595SKristof Provost 	{ .type = PF_RT_OVERLOAD_TBLNAME, .off = _OUT(overload_tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
683e11dacbfSKristof Provost 	{ .type = PF_RT_RPOOL_RDR, .off = _OUT(rdr), .arg = &pool_parser, .cb = nlattr_get_nested },
684ffbf2595SKristof Provost 	{ .type = PF_RT_OS_FINGERPRINT, .off = _OUT(os_fingerprint), .cb = nlattr_get_uint32 },
685ffbf2595SKristof Provost 	{ .type = PF_RT_RTABLEID, .off = _OUT(rtableid), .cb = nlattr_get_uint32 },
686ffbf2595SKristof Provost 	{ .type = PF_RT_TIMEOUT, .off = _OUT(timeout), .arg = &timeout_parser, .cb = nlattr_get_nested_timeouts },
687ffbf2595SKristof Provost 	{ .type = PF_RT_MAX_STATES, .off = _OUT(max_states), .cb = nlattr_get_uint32 },
688ffbf2595SKristof Provost 	{ .type = PF_RT_MAX_SRC_NODES, .off = _OUT(max_src_nodes), .cb = nlattr_get_uint32 },
689ffbf2595SKristof Provost 	{ .type = PF_RT_MAX_SRC_STATES, .off = _OUT(max_src_states), .cb = nlattr_get_uint32 },
690ffbf2595SKristof Provost 	{ .type = PF_RT_MAX_SRC_CONN_RATE_LIMIT, .off = _OUT(max_src_conn_rate.limit), .cb = nlattr_get_uint32 },
691ffbf2595SKristof Provost 	{ .type = PF_RT_MAX_SRC_CONN_RATE_SECS, .off = _OUT(max_src_conn_rate.seconds), .cb = nlattr_get_uint32 },
692ffbf2595SKristof Provost 	{ .type = PF_RT_DNPIPE, .off = _OUT(dnpipe), .cb = nlattr_get_uint16 },
693ffbf2595SKristof Provost 	{ .type = PF_RT_DNRPIPE, .off = _OUT(dnrpipe), .cb = nlattr_get_uint16 },
694ffbf2595SKristof Provost 	{ .type = PF_RT_DNFLAGS, .off = _OUT(free_flags), .cb = nlattr_get_uint32 },
695ffbf2595SKristof Provost 	{ .type = PF_RT_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
696ffbf2595SKristof Provost 	{ .type = PF_RT_PROB, .off = _OUT(prob), .cb = nlattr_get_uint32 },
697ffbf2595SKristof Provost 	{ .type = PF_RT_CUID, .off = _OUT(cuid), .cb = nlattr_get_uint32 },
698ffbf2595SKristof Provost 	{. type = PF_RT_CPID, .off = _OUT(cpid), .cb = nlattr_get_uint32 },
699ffbf2595SKristof Provost 	{ .type = PF_RT_RETURN_ICMP, .off = _OUT(return_icmp), .cb = nlattr_get_uint16 },
700ffbf2595SKristof Provost 	{ .type = PF_RT_RETURN_ICMP6, .off = _OUT(return_icmp6), .cb = nlattr_get_uint16 },
701ffbf2595SKristof Provost 	{ .type = PF_RT_MAX_MSS, .off = _OUT(max_mss), .cb = nlattr_get_uint16 },
702ffbf2595SKristof Provost 	{ .type = PF_RT_SCRUB_FLAGS, .off = _OUT(scrub_flags), .cb = nlattr_get_uint16 },
703ffbf2595SKristof Provost 	{ .type = PF_RT_UID, .off = _OUT(uid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
704ffbf2595SKristof Provost 	{ .type = PF_RT_GID, .off = _OUT(gid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
705ffbf2595SKristof Provost 	{ .type = PF_RT_RULE_FLAG, .off = _OUT(rule_flag), .cb = nlattr_get_uint32 },
706ffbf2595SKristof Provost 	{ .type = PF_RT_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 },
707ffbf2595SKristof Provost 	{ .type = PF_RT_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 },
708ffbf2595SKristof Provost 	{ .type = PF_RT_LOG, .off = _OUT(log), .cb = nlattr_get_uint8 },
709ffbf2595SKristof Provost 	{ .type = PF_RT_LOGIF, .off = _OUT(logif), .cb = nlattr_get_uint8 },
710ffbf2595SKristof Provost 	{ .type = PF_RT_QUICK, .off = _OUT(quick), .cb = nlattr_get_uint8 },
711ffbf2595SKristof Provost 	{ .type = PF_RT_IF_NOT, .off = _OUT(ifnot), .cb = nlattr_get_uint8 },
712ffbf2595SKristof Provost 	{ .type = PF_RT_MATCH_TAG_NOT, .off = _OUT(match_tag_not), .cb = nlattr_get_uint8 },
713ffbf2595SKristof Provost 	{ .type = PF_RT_NATPASS, .off = _OUT(natpass), .cb = nlattr_get_uint8 },
714ffbf2595SKristof Provost 	{ .type = PF_RT_KEEP_STATE, .off = _OUT(keep_state), .cb = nlattr_get_uint8 },
715ffbf2595SKristof Provost 	{ .type = PF_RT_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
716ffbf2595SKristof Provost 	{ .type = PF_RT_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 },
717ffbf2595SKristof Provost 	{ .type = PF_RT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
718ffbf2595SKristof Provost 	{ .type = PF_RT_CODE, .off = _OUT(code), .cb = nlattr_get_uint8 },
719ffbf2595SKristof Provost 	{ .type = PF_RT_FLAGS, .off = _OUT(flags), .cb = nlattr_get_uint8 },
720ffbf2595SKristof Provost 	{ .type = PF_RT_FLAGSET, .off = _OUT(flagset), .cb = nlattr_get_uint8 },
721ffbf2595SKristof Provost 	{ .type = PF_RT_MIN_TTL, .off = _OUT(min_ttl), .cb = nlattr_get_uint8 },
722ffbf2595SKristof Provost 	{ .type = PF_RT_ALLOW_OPTS, .off = _OUT(allow_opts), .cb = nlattr_get_uint8 },
723ffbf2595SKristof Provost 	{ .type = PF_RT_RT, .off = _OUT(rt), .cb = nlattr_get_uint8 },
724ffbf2595SKristof Provost 	{ .type = PF_RT_RETURN_TTL, .off = _OUT(return_ttl), .cb = nlattr_get_uint8 },
725ffbf2595SKristof Provost 	{ .type = PF_RT_TOS, .off = _OUT(tos), .cb = nlattr_get_uint8 },
726ffbf2595SKristof Provost 	{ .type = PF_RT_SET_TOS, .off = _OUT(set_tos), .cb = nlattr_get_uint8 },
727ffbf2595SKristof Provost 	{ .type = PF_RT_ANCHOR_RELATIVE, .off = _OUT(anchor_relative), .cb = nlattr_get_uint8 },
728ffbf2595SKristof Provost 	{ .type = PF_RT_ANCHOR_WILDCARD, .off = _OUT(anchor_wildcard), .cb = nlattr_get_uint8 },
729ffbf2595SKristof Provost 	{ .type = PF_RT_FLUSH, .off = _OUT(flush), .cb = nlattr_get_uint8 },
730ffbf2595SKristof Provost 	{ .type = PF_RT_PRIO, .off = _OUT(prio), .cb = nlattr_get_uint8 },
731ffbf2595SKristof Provost 	{ .type = PF_RT_SET_PRIO, .off = _OUT(set_prio[0]), .cb = nlattr_get_uint8 },
732ffbf2595SKristof Provost 	{ .type = PF_RT_SET_PRIO_REPLY, .off = _OUT(set_prio[1]), .cb = nlattr_get_uint8 },
733ffbf2595SKristof Provost 	{ .type = PF_RT_DIVERT_ADDRESS, .off = _OUT(divert.addr), .cb = nlattr_get_in6_addr },
734ffbf2595SKristof Provost 	{ .type = PF_RT_DIVERT_PORT, .off = _OUT(divert.port), .cb = nlattr_get_uint16 },
7352339ead6SKristof Provost 	{ .type = PF_RT_RCV_IFNAME, .off = _OUT(rcv_ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
7367fe42038SKajetan Staszkiewicz 	{ .type = PF_RT_MAX_SRC_CONN, .off = _OUT(max_src_conn), .cb = nlattr_get_uint32 },
737e11dacbfSKristof Provost 	{ .type = PF_RT_RPOOL_NAT, .off = _OUT(nat), .arg = &pool_parser, .cb = nlattr_get_nested },
738fcdb520cSKristof Provost 	{ .type = PF_RT_NAF, .off = _OUT(naf), .cb = nlattr_get_uint8 },
7390972294eSKristof Provost 	{ .type = PF_RT_RPOOL_RT, .off = _OUT(route), .arg = &pool_parser, .cb = nlattr_get_nested },
740ffbf2595SKristof Provost };
741ffbf2595SKristof Provost NL_DECLARE_ATTR_PARSER(rule_parser, nla_p_rule);
742ffbf2595SKristof Provost #undef _OUT
743ffbf2595SKristof Provost struct nl_parsed_addrule {
744ffbf2595SKristof Provost 	struct pf_krule	*rule;
745ffbf2595SKristof Provost 	uint32_t	 ticket;
746ffbf2595SKristof Provost 	uint32_t	 pool_ticket;
747ffbf2595SKristof Provost 	char		*anchor;
748ffbf2595SKristof Provost 	char		*anchor_call;
749ffbf2595SKristof Provost };
750ffbf2595SKristof Provost #define	_OUT(_field)	offsetof(struct nl_parsed_addrule, _field)
751ffbf2595SKristof Provost static const struct nlattr_parser nla_p_addrule[] = {
752ffbf2595SKristof Provost 	{ .type = PF_ART_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
753ffbf2595SKristof Provost 	{ .type = PF_ART_POOL_TICKET, .off = _OUT(pool_ticket), .cb = nlattr_get_uint32 },
754ffbf2595SKristof Provost 	{ .type = PF_ART_ANCHOR, .off = _OUT(anchor), .cb = nlattr_get_string },
755ffbf2595SKristof Provost 	{ .type = PF_ART_ANCHOR_CALL, .off = _OUT(anchor_call), .cb = nlattr_get_string },
756ffbf2595SKristof Provost 	{ .type = PF_ART_RULE, .off = _OUT(rule), .arg = &rule_parser, .cb = nlattr_get_nested_ptr }
757ffbf2595SKristof Provost };
758ffbf2595SKristof Provost #undef _OUT
759e9255dafSGleb Smirnoff NL_DECLARE_PARSER(addrule_parser, struct genlmsghdr, nlf_p_empty, nla_p_addrule);
760ffbf2595SKristof Provost 
761ffbf2595SKristof Provost static int
762ffbf2595SKristof Provost pf_handle_addrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
763ffbf2595SKristof Provost {
764ffbf2595SKristof Provost 	int error;
765ffbf2595SKristof Provost 	struct nl_parsed_addrule attrs = {};
766ffbf2595SKristof Provost 
767ffbf2595SKristof Provost 	attrs.rule = pf_krule_alloc();
768ffbf2595SKristof Provost 
769ffbf2595SKristof Provost 	error = nl_parse_nlmsg(hdr, &addrule_parser, npt, &attrs);
770e249f5daSKristof Provost 	if (error != 0) {
771e249f5daSKristof Provost 		pf_free_rule(attrs.rule);
772ffbf2595SKristof Provost 		return (error);
773e249f5daSKristof Provost 	}
774ffbf2595SKristof Provost 
775ffbf2595SKristof Provost 	error = pf_ioctl_addrule(attrs.rule, attrs.ticket, attrs.pool_ticket,
776ffbf2595SKristof Provost 	    attrs.anchor, attrs.anchor_call, nlp_get_cred(npt->nlp)->cr_uid,
777ffbf2595SKristof Provost 	    hdr->nlmsg_pid);
778ffbf2595SKristof Provost 
779ffbf2595SKristof Provost 	return (error);
780ffbf2595SKristof Provost }
781ffbf2595SKristof Provost 
78244f323ecSKristof Provost #define	_OUT(_field)	offsetof(struct pfioc_rule, _field)
78344f323ecSKristof Provost static const struct nlattr_parser nla_p_getrules[] = {
78444f323ecSKristof Provost 	{ .type = PF_GR_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
78544f323ecSKristof Provost 	{ .type = PF_GR_ACTION, .off = _OUT(rule.action), .cb = nlattr_get_uint8 },
78644f323ecSKristof Provost };
787777a4702SKristof Provost #undef _OUT
788e9255dafSGleb Smirnoff NL_DECLARE_PARSER(getrules_parser, struct genlmsghdr, nlf_p_empty, nla_p_getrules);
78944f323ecSKristof Provost 
79044f323ecSKristof Provost static int
79144f323ecSKristof Provost pf_handle_getrules(struct nlmsghdr *hdr, struct nl_pstate *npt)
79244f323ecSKristof Provost {
79344f323ecSKristof Provost 	struct pfioc_rule attrs = {};
79444f323ecSKristof Provost 	int error;
79544f323ecSKristof Provost 	struct nl_writer *nw = npt->nw;
79644f323ecSKristof Provost 	struct genlmsghdr *ghdr_new;
79744f323ecSKristof Provost 
79844f323ecSKristof Provost 	error = nl_parse_nlmsg(hdr, &getrules_parser, npt, &attrs);
79944f323ecSKristof Provost 	if (error != 0)
80044f323ecSKristof Provost 		return (error);
80144f323ecSKristof Provost 
80244f323ecSKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
80344f323ecSKristof Provost 		return (ENOMEM);
80444f323ecSKristof Provost 
80544f323ecSKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
80644f323ecSKristof Provost 	ghdr_new->cmd = PFNL_CMD_GETRULES;
80744f323ecSKristof Provost 	ghdr_new->version = 0;
80844f323ecSKristof Provost 	ghdr_new->reserved = 0;
80944f323ecSKristof Provost 
81044f323ecSKristof Provost 	error = pf_ioctl_getrules(&attrs);
81144f323ecSKristof Provost 	if (error != 0)
81244f323ecSKristof Provost 		goto out;
81344f323ecSKristof Provost 
81444f323ecSKristof Provost 	nlattr_add_u32(nw, PF_GR_NR, attrs.nr);
81544f323ecSKristof Provost 	nlattr_add_u32(nw, PF_GR_TICKET, attrs.ticket);
81644f323ecSKristof Provost 
81744f323ecSKristof Provost 	if (!nlmsg_end(nw)) {
81844f323ecSKristof Provost 		error = ENOMEM;
81944f323ecSKristof Provost 		goto out;
82044f323ecSKristof Provost 	}
82144f323ecSKristof Provost 
82244f323ecSKristof Provost 	return (0);
82344f323ecSKristof Provost 
82444f323ecSKristof Provost out:
82544f323ecSKristof Provost 	nlmsg_abort(nw);
82644f323ecSKristof Provost 	return (error);
82744f323ecSKristof Provost }
82844f323ecSKristof Provost 
829777a4702SKristof Provost struct nl_parsed_get_rule {
830777a4702SKristof Provost 	char anchor[MAXPATHLEN];
831777a4702SKristof Provost 	uint8_t action;
832777a4702SKristof Provost 	uint32_t nr;
833777a4702SKristof Provost 	uint32_t ticket;
834777a4702SKristof Provost 	uint8_t clear;
835777a4702SKristof Provost };
836777a4702SKristof Provost #define	_OUT(_field)	offsetof(struct nl_parsed_get_rule, _field)
837777a4702SKristof Provost static const struct nlattr_parser nla_p_getrule[] = {
838777a4702SKristof Provost 	{ .type = PF_GR_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
839777a4702SKristof Provost 	{ .type = PF_GR_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 },
840777a4702SKristof Provost 	{ .type = PF_GR_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
841777a4702SKristof Provost 	{ .type = PF_GR_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
842777a4702SKristof Provost 	{ .type = PF_GR_CLEAR, .off = _OUT(clear), .cb = nlattr_get_uint8 },
843777a4702SKristof Provost };
844706d465dSKristof Provost #undef _OUT
845e9255dafSGleb Smirnoff NL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, nlf_p_empty, nla_p_getrule);
846777a4702SKristof Provost 
847777a4702SKristof Provost static int
848777a4702SKristof Provost pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
849777a4702SKristof Provost {
850777a4702SKristof Provost 	char				 anchor_call[MAXPATHLEN];
851777a4702SKristof Provost 	struct nl_parsed_get_rule	 attrs = {};
852777a4702SKristof Provost 	struct nl_writer		*nw = npt->nw;
853777a4702SKristof Provost 	struct genlmsghdr		*ghdr_new;
854777a4702SKristof Provost 	struct pf_kruleset		*ruleset;
855777a4702SKristof Provost 	struct pf_krule			*rule;
856777a4702SKristof Provost 	int				 rs_num;
857777a4702SKristof Provost 	int				 error;
858777a4702SKristof Provost 
859777a4702SKristof Provost 	error = nl_parse_nlmsg(hdr, &getrule_parser, npt, &attrs);
860777a4702SKristof Provost 	if (error != 0)
861777a4702SKristof Provost 		return (error);
862777a4702SKristof Provost 
863777a4702SKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
864777a4702SKristof Provost 		return (ENOMEM);
865777a4702SKristof Provost 
866777a4702SKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
867777a4702SKristof Provost 	ghdr_new->cmd = PFNL_CMD_GETRULE;
868777a4702SKristof Provost 	ghdr_new->version = 0;
869777a4702SKristof Provost 	ghdr_new->reserved = 0;
870777a4702SKristof Provost 
871777a4702SKristof Provost 	PF_RULES_WLOCK();
872777a4702SKristof Provost 	ruleset = pf_find_kruleset(attrs.anchor);
873777a4702SKristof Provost 	if (ruleset == NULL) {
874777a4702SKristof Provost 		PF_RULES_WUNLOCK();
875777a4702SKristof Provost 		error = ENOENT;
876777a4702SKristof Provost 		goto out;
877777a4702SKristof Provost 	}
878777a4702SKristof Provost 
879777a4702SKristof Provost 	rs_num = pf_get_ruleset_number(attrs.action);
880777a4702SKristof Provost 	if (rs_num >= PF_RULESET_MAX) {
881777a4702SKristof Provost 		PF_RULES_WUNLOCK();
882777a4702SKristof Provost 		error = EINVAL;
883777a4702SKristof Provost 		goto out;
884777a4702SKristof Provost 	}
885777a4702SKristof Provost 
886777a4702SKristof Provost 	if (attrs.ticket != ruleset->rules[rs_num].active.ticket) {
887777a4702SKristof Provost 		PF_RULES_WUNLOCK();
888777a4702SKristof Provost 		error = EBUSY;
889777a4702SKristof Provost 		goto out;
890777a4702SKristof Provost 	}
891777a4702SKristof Provost 
892777a4702SKristof Provost 	rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr);
893777a4702SKristof Provost 	while ((rule != NULL) && (rule->nr != attrs.nr))
894777a4702SKristof Provost 		rule = TAILQ_NEXT(rule, entries);
895777a4702SKristof Provost 	if (rule == NULL) {
896777a4702SKristof Provost 		PF_RULES_WUNLOCK();
897777a4702SKristof Provost 		error = EBUSY;
898777a4702SKristof Provost 		goto out;
899777a4702SKristof Provost 	}
900777a4702SKristof Provost 
901777a4702SKristof Provost 	nlattr_add_rule_addr(nw, PF_RT_SRC, &rule->src);
902777a4702SKristof Provost 	nlattr_add_rule_addr(nw, PF_RT_DST, &rule->dst);
903777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_RIDENTIFIER, rule->ridentifier);
904777a4702SKristof Provost 	nlattr_add_labels(nw, PF_RT_LABELS, rule);
905777a4702SKristof Provost 	nlattr_add_string(nw, PF_RT_IFNAME, rule->ifname);
906777a4702SKristof Provost 	nlattr_add_string(nw, PF_RT_QNAME, rule->qname);
907777a4702SKristof Provost 	nlattr_add_string(nw, PF_RT_PQNAME, rule->pqname);
908777a4702SKristof Provost 	nlattr_add_string(nw, PF_RT_TAGNAME, rule->tagname);
909777a4702SKristof Provost 	nlattr_add_string(nw, PF_RT_MATCH_TAGNAME, rule->match_tagname);
910777a4702SKristof Provost 	nlattr_add_string(nw, PF_RT_OVERLOAD_TBLNAME, rule->overload_tblname);
911e11dacbfSKristof Provost 	nlattr_add_pool(nw, PF_RT_RPOOL_RDR, &rule->rdr);
912e11dacbfSKristof Provost 	nlattr_add_pool(nw, PF_RT_RPOOL_NAT, &rule->nat);
9130972294eSKristof Provost 	nlattr_add_pool(nw, PF_RT_RPOOL_RT, &rule->route);
914777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_OS_FINGERPRINT, rule->os_fingerprint);
915777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_RTABLEID, rule->rtableid);
916777a4702SKristof Provost 	nlattr_add_timeout(nw, PF_RT_TIMEOUT, rule->timeout);
917777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_MAX_STATES, rule->max_states);
918777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_MAX_SRC_NODES, rule->max_src_nodes);
919777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_MAX_SRC_STATES, rule->max_src_states);
9207fe42038SKajetan Staszkiewicz 	nlattr_add_u32(nw, PF_RT_MAX_SRC_CONN, rule->max_src_conn);
921777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_MAX_SRC_CONN_RATE_LIMIT, rule->max_src_conn_rate.limit);
922777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_MAX_SRC_CONN_RATE_SECS, rule->max_src_conn_rate.seconds);
923777a4702SKristof Provost 
924777a4702SKristof Provost 	nlattr_add_u16(nw, PF_RT_DNPIPE, rule->dnpipe);
925777a4702SKristof Provost 	nlattr_add_u16(nw, PF_RT_DNRPIPE, rule->dnrpipe);
926777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_DNFLAGS, rule->free_flags);
927777a4702SKristof Provost 
928777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_NR, rule->nr);
929777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_PROB, rule->prob);
930777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_CUID, rule->cuid);
931777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_CPID, rule->cpid);
932777a4702SKristof Provost 
933777a4702SKristof Provost 	nlattr_add_u16(nw, PF_RT_RETURN_ICMP, rule->return_icmp);
934777a4702SKristof Provost 	nlattr_add_u16(nw, PF_RT_RETURN_ICMP6, rule->return_icmp6);
935777a4702SKristof Provost 	nlattr_add_u16(nw, PF_RT_RETURN_ICMP6, rule->return_icmp6);
936777a4702SKristof Provost 	nlattr_add_u16(nw, PF_RT_MAX_MSS, rule->max_mss);
937777a4702SKristof Provost 	nlattr_add_u16(nw, PF_RT_SCRUB_FLAGS, rule->scrub_flags);
938777a4702SKristof Provost 
939777a4702SKristof Provost 	nlattr_add_rule_uid(nw, PF_RT_UID, &rule->uid);
940777a4702SKristof Provost 	nlattr_add_rule_uid(nw, PF_RT_GID, (const struct pf_rule_uid *)&rule->gid);
941777a4702SKristof Provost 
9422339ead6SKristof Provost 	nlattr_add_string(nw, PF_RT_RCV_IFNAME, rule->rcv_ifname);
9432339ead6SKristof Provost 
944777a4702SKristof Provost 	nlattr_add_u32(nw, PF_RT_RULE_FLAG, rule->rule_flag);
945777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_ACTION, rule->action);
946777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_DIRECTION, rule->direction);
947777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_LOG, rule->log);
948777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_LOGIF, rule->logif);
949777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_QUICK, rule->quick);
950777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_IF_NOT, rule->ifnot);
951777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_MATCH_TAG_NOT, rule->match_tag_not);
952777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_NATPASS, rule->natpass);
953777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_KEEP_STATE, rule->keep_state);
954777a4702SKristof Provost 
955777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_AF, rule->af);
956fcdb520cSKristof Provost 	nlattr_add_u8(nw, PF_RT_NAF, rule->naf);
957777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_PROTO, rule->proto);
958777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_TYPE, rule->type);
959777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_CODE, rule->code);
960777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_FLAGS, rule->flags);
961777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_FLAGSET, rule->flagset);
962777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_MIN_TTL, rule->min_ttl);
963777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_ALLOW_OPTS, rule->allow_opts);
964777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_RT, rule->rt);
965777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_RETURN_TTL, rule->return_ttl);
966777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_TOS, rule->tos);
967777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_SET_TOS, rule->set_tos);
968777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_ANCHOR_RELATIVE, rule->anchor_relative);
969777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_ANCHOR_WILDCARD, rule->anchor_wildcard);
970777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_FLUSH, rule->flush);
971777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_PRIO, rule->prio);
972777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_SET_PRIO, rule->set_prio[0]);
973777a4702SKristof Provost 	nlattr_add_u8(nw, PF_RT_SET_PRIO_REPLY, rule->set_prio[1]);
974777a4702SKristof Provost 
975777a4702SKristof Provost 	nlattr_add_in6_addr(nw, PF_RT_DIVERT_ADDRESS, &rule->divert.addr.v6);
976777a4702SKristof Provost 	nlattr_add_u16(nw, PF_RT_DIVERT_PORT, rule->divert.port);
977777a4702SKristof Provost 
978777a4702SKristof Provost 	nlattr_add_u64(nw, PF_RT_PACKETS_IN, pf_counter_u64_fetch(&rule->packets[0]));
979777a4702SKristof Provost 	nlattr_add_u64(nw, PF_RT_PACKETS_OUT, pf_counter_u64_fetch(&rule->packets[1]));
980777a4702SKristof Provost 	nlattr_add_u64(nw, PF_RT_BYTES_IN, pf_counter_u64_fetch(&rule->bytes[0]));
981777a4702SKristof Provost 	nlattr_add_u64(nw, PF_RT_BYTES_OUT, pf_counter_u64_fetch(&rule->bytes[1]));
982777a4702SKristof Provost 	nlattr_add_u64(nw, PF_RT_EVALUATIONS, pf_counter_u64_fetch(&rule->evaluations));
983777a4702SKristof Provost 	nlattr_add_u64(nw, PF_RT_TIMESTAMP, pf_get_timestamp(rule));
984777a4702SKristof Provost 	nlattr_add_u64(nw, PF_RT_STATES_CUR, counter_u64_fetch(rule->states_cur));
985777a4702SKristof Provost 	nlattr_add_u64(nw, PF_RT_STATES_TOTAL, counter_u64_fetch(rule->states_tot));
986777a4702SKristof Provost 	nlattr_add_u64(nw, PF_RT_SRC_NODES, counter_u64_fetch(rule->src_nodes));
987777a4702SKristof Provost 
9886ee3e376SKristof Provost 	error = pf_kanchor_copyout(ruleset, rule, anchor_call, sizeof(anchor_call));
989777a4702SKristof Provost 	MPASS(error == 0);
990777a4702SKristof Provost 
991777a4702SKristof Provost 	nlattr_add_string(nw, PF_RT_ANCHOR_CALL, anchor_call);
992777a4702SKristof Provost 
993777a4702SKristof Provost 	if (attrs.clear)
994777a4702SKristof Provost 		pf_krule_clear_counters(rule);
995777a4702SKristof Provost 
996777a4702SKristof Provost 	PF_RULES_WUNLOCK();
997777a4702SKristof Provost 
998777a4702SKristof Provost 	if (!nlmsg_end(nw)) {
999777a4702SKristof Provost 		error = ENOMEM;
1000777a4702SKristof Provost 		goto out;
1001777a4702SKristof Provost 	}
1002777a4702SKristof Provost 
1003777a4702SKristof Provost 	return (0);
1004777a4702SKristof Provost out:
1005777a4702SKristof Provost 	nlmsg_abort(nw);
1006777a4702SKristof Provost 	return (error);
1007777a4702SKristof Provost }
1008777a4702SKristof Provost 
1009706d465dSKristof Provost #define	_OUT(_field)	offsetof(struct pf_kstate_kill, _field)
1010706d465dSKristof Provost static const struct nlattr_parser nla_p_clear_states[] = {
1011706d465dSKristof Provost 	{ .type = PF_CS_CMP_ID, .off = _OUT(psk_pfcmp.id), .cb = nlattr_get_uint64 },
1012706d465dSKristof Provost 	{ .type = PF_CS_CMP_CREATORID, .off = _OUT(psk_pfcmp.creatorid), .cb = nlattr_get_uint32 },
1013706d465dSKristof Provost 	{ .type = PF_CS_CMP_DIR, .off = _OUT(psk_pfcmp.direction), .cb = nlattr_get_uint8 },
1014706d465dSKristof Provost 	{ .type = PF_CS_AF, .off = _OUT(psk_af), .cb = nlattr_get_uint8 },
1015706d465dSKristof Provost 	{ .type = PF_CS_PROTO, .off = _OUT(psk_proto), .cb = nlattr_get_uint8 },
1016706d465dSKristof Provost 	{ .type = PF_CS_SRC, .off = _OUT(psk_src), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
1017706d465dSKristof Provost 	{ .type = PF_CS_DST, .off = _OUT(psk_dst), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
1018706d465dSKristof Provost 	{ .type = PF_CS_RT_ADDR, .off = _OUT(psk_rt_addr), .arg = &rule_addr_parser, .cb = nlattr_get_nested },
1019706d465dSKristof Provost 	{ .type = PF_CS_IFNAME, .off = _OUT(psk_ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
1020706d465dSKristof Provost 	{ .type = PF_CS_LABEL, .off = _OUT(psk_label), .arg = (void *)PF_RULE_LABEL_SIZE, .cb = nlattr_get_chara },
1021706d465dSKristof Provost 	{ .type = PF_CS_KILL_MATCH, .off = _OUT(psk_kill_match), .cb = nlattr_get_bool },
1022706d465dSKristof Provost 	{ .type = PF_CS_NAT, .off = _OUT(psk_nat), .cb = nlattr_get_bool },
1023706d465dSKristof Provost };
1024706d465dSKristof Provost #undef _OUT
1025e9255dafSGleb Smirnoff NL_DECLARE_PARSER(clear_states_parser, struct genlmsghdr, nlf_p_empty, nla_p_clear_states);
1026706d465dSKristof Provost 
1027706d465dSKristof Provost static int
1028706d465dSKristof Provost pf_handle_killclear_states(struct nlmsghdr *hdr, struct nl_pstate *npt, int cmd)
1029706d465dSKristof Provost {
1030706d465dSKristof Provost 	struct pf_kstate_kill		 kill = {};
1031706d465dSKristof Provost 	struct epoch_tracker		 et;
1032706d465dSKristof Provost 	struct nl_writer		*nw = npt->nw;
1033706d465dSKristof Provost 	struct genlmsghdr		*ghdr_new;
1034706d465dSKristof Provost 	int				 error;
1035706d465dSKristof Provost 	unsigned int			 killed = 0;
1036706d465dSKristof Provost 
1037706d465dSKristof Provost 	error = nl_parse_nlmsg(hdr, &clear_states_parser, npt, &kill);
1038706d465dSKristof Provost 	if (error != 0)
1039706d465dSKristof Provost 		return (error);
1040706d465dSKristof Provost 
1041706d465dSKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1042706d465dSKristof Provost 		return (ENOMEM);
1043706d465dSKristof Provost 
1044706d465dSKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1045706d465dSKristof Provost 	ghdr_new->cmd = cmd;
1046706d465dSKristof Provost 	ghdr_new->version = 0;
1047706d465dSKristof Provost 	ghdr_new->reserved = 0;
1048706d465dSKristof Provost 
1049706d465dSKristof Provost 	NET_EPOCH_ENTER(et);
1050706d465dSKristof Provost 	if (cmd == PFNL_CMD_KILLSTATES)
1051706d465dSKristof Provost 		pf_killstates(&kill, &killed);
1052706d465dSKristof Provost 	else
1053706d465dSKristof Provost 		killed = pf_clear_states(&kill);
1054706d465dSKristof Provost 	NET_EPOCH_EXIT(et);
1055706d465dSKristof Provost 
1056706d465dSKristof Provost 	nlattr_add_u32(nw, PF_CS_KILLED, killed);
1057706d465dSKristof Provost 
1058706d465dSKristof Provost 	if (! nlmsg_end(nw)) {
1059706d465dSKristof Provost 		error = ENOMEM;
1060706d465dSKristof Provost 		goto out;
1061706d465dSKristof Provost 	}
1062706d465dSKristof Provost 
1063706d465dSKristof Provost 	return (0);
1064706d465dSKristof Provost 
1065706d465dSKristof Provost out:
1066706d465dSKristof Provost 	nlmsg_abort(nw);
1067706d465dSKristof Provost 	return (error);
1068706d465dSKristof Provost }
1069706d465dSKristof Provost 
1070706d465dSKristof Provost static int
1071706d465dSKristof Provost pf_handle_clear_states(struct nlmsghdr *hdr, struct nl_pstate *npt)
1072706d465dSKristof Provost {
1073706d465dSKristof Provost 	return (pf_handle_killclear_states(hdr, npt, PFNL_CMD_CLRSTATES));
1074706d465dSKristof Provost }
1075706d465dSKristof Provost 
1076706d465dSKristof Provost static int
1077706d465dSKristof Provost pf_handle_kill_states(struct nlmsghdr *hdr, struct nl_pstate *npt)
1078706d465dSKristof Provost {
1079706d465dSKristof Provost 	return (pf_handle_killclear_states(hdr, npt, PFNL_CMD_KILLSTATES));
1080706d465dSKristof Provost }
1081706d465dSKristof Provost 
1082470a2b33SKristof Provost struct nl_parsed_set_statusif {
1083470a2b33SKristof Provost 	char ifname[IFNAMSIZ];
1084470a2b33SKristof Provost };
1085470a2b33SKristof Provost #define	_OUT(_field)	offsetof(struct nl_parsed_set_statusif, _field)
1086470a2b33SKristof Provost static const struct nlattr_parser nla_p_set_statusif[] = {
1087470a2b33SKristof Provost 	{ .type = PF_SS_IFNAME, .off = _OUT(ifname), .arg = (const void *)IFNAMSIZ, .cb = nlattr_get_chara },
1088470a2b33SKristof Provost };
1089470a2b33SKristof Provost #undef _OUT
1090e9255dafSGleb Smirnoff NL_DECLARE_PARSER(set_statusif_parser, struct genlmsghdr, nlf_p_empty, nla_p_set_statusif);
1091470a2b33SKristof Provost 
1092470a2b33SKristof Provost static int
1093470a2b33SKristof Provost pf_handle_set_statusif(struct nlmsghdr *hdr, struct nl_pstate *npt)
1094470a2b33SKristof Provost {
1095470a2b33SKristof Provost 	int error;
1096470a2b33SKristof Provost 	struct nl_parsed_set_statusif attrs = {};
1097470a2b33SKristof Provost 
1098470a2b33SKristof Provost 	error = nl_parse_nlmsg(hdr, &set_statusif_parser, npt, &attrs);
1099470a2b33SKristof Provost 	if (error != 0)
1100470a2b33SKristof Provost 		return (error);
1101470a2b33SKristof Provost 
1102470a2b33SKristof Provost 	PF_RULES_WLOCK();
1103470a2b33SKristof Provost 	strlcpy(V_pf_status.ifname, attrs.ifname, IFNAMSIZ);
1104470a2b33SKristof Provost 	PF_RULES_WUNLOCK();
1105470a2b33SKristof Provost 
1106470a2b33SKristof Provost 	return (0);
1107470a2b33SKristof Provost }
1108470a2b33SKristof Provost 
11095824df8dSKristof Provost static bool
11105824df8dSKristof Provost nlattr_add_counters(struct nl_writer *nw, int attr, size_t number, char **names,
11115824df8dSKristof Provost     counter_u64_t *counters)
11125824df8dSKristof Provost {
11135824df8dSKristof Provost 	for (int i = 0; i < number; i++) {
11145824df8dSKristof Provost 		int off = nlattr_add_nested(nw, attr);
11155824df8dSKristof Provost 		nlattr_add_u32(nw, PF_C_ID, i);
11165824df8dSKristof Provost 		nlattr_add_string(nw, PF_C_NAME, names[i]);
11175824df8dSKristof Provost 		nlattr_add_u64(nw, PF_C_COUNTER, counter_u64_fetch(counters[i]));
11185824df8dSKristof Provost 		nlattr_set_len(nw, off);
11195824df8dSKristof Provost 	}
11205824df8dSKristof Provost 
11215824df8dSKristof Provost 	return (true);
11225824df8dSKristof Provost }
11235824df8dSKristof Provost 
11245824df8dSKristof Provost static bool
11255824df8dSKristof Provost nlattr_add_fcounters(struct nl_writer *nw, int attr, size_t number, char **names,
11265824df8dSKristof Provost     struct pf_counter_u64 *counters)
11275824df8dSKristof Provost {
11285824df8dSKristof Provost 	for (int i = 0; i < number; i++) {
11295824df8dSKristof Provost 		int off = nlattr_add_nested(nw, attr);
11305824df8dSKristof Provost 		nlattr_add_u32(nw, PF_C_ID, i);
11315824df8dSKristof Provost 		nlattr_add_string(nw, PF_C_NAME, names[i]);
11325824df8dSKristof Provost 		nlattr_add_u64(nw, PF_C_COUNTER, pf_counter_u64_fetch(&counters[i]));
11335824df8dSKristof Provost 		nlattr_set_len(nw, off);
11345824df8dSKristof Provost 	}
11355824df8dSKristof Provost 
11365824df8dSKristof Provost 	return (true);
11375824df8dSKristof Provost }
11385824df8dSKristof Provost 
11395824df8dSKristof Provost static bool
11405824df8dSKristof Provost nlattr_add_u64_array(struct nl_writer *nw, int attr, size_t number, uint64_t *array)
11415824df8dSKristof Provost {
11425824df8dSKristof Provost 	int off = nlattr_add_nested(nw, attr);
11435824df8dSKristof Provost 
11445824df8dSKristof Provost 	for (size_t i = 0; i < number; i++)
11455824df8dSKristof Provost 		nlattr_add_u64(nw, 0, array[i]);
11465824df8dSKristof Provost 
11475824df8dSKristof Provost 	nlattr_set_len(nw, off);
11485824df8dSKristof Provost 
11495824df8dSKristof Provost 	return (true);
11505824df8dSKristof Provost }
11515824df8dSKristof Provost 
11525824df8dSKristof Provost static int
11535824df8dSKristof Provost pf_handle_get_status(struct nlmsghdr *hdr, struct nl_pstate *npt)
11545824df8dSKristof Provost {
11555824df8dSKristof Provost 	struct pf_status s;
11565824df8dSKristof Provost 	struct nl_writer *nw = npt->nw;
11575824df8dSKristof Provost 	struct genlmsghdr *ghdr_new;
11585824df8dSKristof Provost 	char *pf_reasons[PFRES_MAX+1] = PFRES_NAMES;
11595824df8dSKristof Provost 	char *pf_lcounter[KLCNT_MAX+1] = KLCNT_NAMES;
11605824df8dSKristof Provost 	char *pf_fcounter[FCNT_MAX+1] = FCNT_NAMES;
11615824df8dSKristof Provost 	int error;
11625824df8dSKristof Provost 
11635824df8dSKristof Provost 	PF_RULES_RLOCK_TRACKER;
11645824df8dSKristof Provost 
11655824df8dSKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
11665824df8dSKristof Provost 		return (ENOMEM);
11675824df8dSKristof Provost 
11685824df8dSKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
11695824df8dSKristof Provost 	ghdr_new->cmd = PFNL_CMD_GET_STATUS;
11705824df8dSKristof Provost 	ghdr_new->version = 0;
11715824df8dSKristof Provost 	ghdr_new->reserved = 0;
11725824df8dSKristof Provost 
11735824df8dSKristof Provost 	PF_RULES_RLOCK();
11745824df8dSKristof Provost 
11755824df8dSKristof Provost 	nlattr_add_string(nw, PF_GS_IFNAME, V_pf_status.ifname);
11765824df8dSKristof Provost 	nlattr_add_bool(nw, PF_GS_RUNNING, V_pf_status.running);
11775824df8dSKristof Provost 	nlattr_add_u32(nw, PF_GS_SINCE, V_pf_status.since);
11785824df8dSKristof Provost 	nlattr_add_u32(nw, PF_GS_DEBUG, V_pf_status.debug);
11795824df8dSKristof Provost 	nlattr_add_u32(nw, PF_GS_HOSTID, ntohl(V_pf_status.hostid));
11805824df8dSKristof Provost 	nlattr_add_u32(nw, PF_GS_STATES, V_pf_status.states);
11815824df8dSKristof Provost 	nlattr_add_u32(nw, PF_GS_SRC_NODES, V_pf_status.src_nodes);
11825824df8dSKristof Provost 	nlattr_add_u32(nw, PF_GS_REASSEMBLE, V_pf_status.reass);
11835824df8dSKristof Provost 	nlattr_add_u32(nw, PF_GS_SYNCOOKIES_ACTIVE, V_pf_status.syncookies_active);
11845824df8dSKristof Provost 
11855824df8dSKristof Provost 	nlattr_add_counters(nw, PF_GS_COUNTERS, PFRES_MAX, pf_reasons,
11865824df8dSKristof Provost 	    V_pf_status.counters);
11875824df8dSKristof Provost 	nlattr_add_counters(nw, PF_GS_LCOUNTERS, KLCNT_MAX, pf_lcounter,
11885824df8dSKristof Provost 	    V_pf_status.lcounters);
11895824df8dSKristof Provost 	nlattr_add_fcounters(nw, PF_GS_FCOUNTERS, FCNT_MAX, pf_fcounter,
11905824df8dSKristof Provost 	    V_pf_status.fcounters);
11915824df8dSKristof Provost 	nlattr_add_counters(nw, PF_GS_SCOUNTERS, SCNT_MAX, pf_fcounter,
11925824df8dSKristof Provost 	    V_pf_status.scounters);
11935824df8dSKristof Provost 
11945824df8dSKristof Provost 	pfi_update_status(V_pf_status.ifname, &s);
11955824df8dSKristof Provost 	nlattr_add_u64_array(nw, PF_GS_BCOUNTERS, 2 * 2, (uint64_t *)s.bcounters);
11965824df8dSKristof Provost 	nlattr_add_u64_array(nw, PF_GS_PCOUNTERS, 2 * 2 * 2, (uint64_t *)s.pcounters);
11975824df8dSKristof Provost 
11985824df8dSKristof Provost 	nlattr_add(nw, PF_GS_CHKSUM, PF_MD5_DIGEST_LENGTH, V_pf_status.pf_chksum);
11995824df8dSKristof Provost 
12005824df8dSKristof Provost 	PF_RULES_RUNLOCK();
12015824df8dSKristof Provost 
12025824df8dSKristof Provost 	if (!nlmsg_end(nw)) {
12035824df8dSKristof Provost 		error = ENOMEM;
12045824df8dSKristof Provost 		goto out;
12055824df8dSKristof Provost 	}
12065824df8dSKristof Provost 
12075824df8dSKristof Provost 	return (0);
12085824df8dSKristof Provost 
12095824df8dSKristof Provost out:
12105824df8dSKristof Provost 	nlmsg_abort(nw);
12115824df8dSKristof Provost 	return (error);
12125824df8dSKristof Provost }
12135824df8dSKristof Provost 
12149dbbe68bSKristof Provost static int
12159dbbe68bSKristof Provost pf_handle_clear_status(struct nlmsghdr *hdr, struct nl_pstate *npt)
12169dbbe68bSKristof Provost {
12179dbbe68bSKristof Provost 	pf_ioctl_clear_status();
12189dbbe68bSKristof Provost 
12199dbbe68bSKristof Provost 	return (0);
12209dbbe68bSKristof Provost }
12219dbbe68bSKristof Provost 
122271d3c704SKristof Provost struct pf_nl_natlook {
122371d3c704SKristof Provost 	sa_family_t af;
122471d3c704SKristof Provost 	uint8_t direction;
122571d3c704SKristof Provost 	uint8_t proto;
122671d3c704SKristof Provost 	struct pf_addr src;
122771d3c704SKristof Provost 	struct pf_addr dst;
122871d3c704SKristof Provost 	uint16_t sport;
122971d3c704SKristof Provost 	uint16_t dport;
123071d3c704SKristof Provost };
123171d3c704SKristof Provost 
123271d3c704SKristof Provost #define	_OUT(_field)	offsetof(struct pf_nl_natlook, _field)
123371d3c704SKristof Provost static const struct nlattr_parser nla_p_natlook[] = {
123471d3c704SKristof Provost 	{ .type = PF_NL_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
123571d3c704SKristof Provost 	{ .type = PF_NL_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 },
123671d3c704SKristof Provost 	{ .type = PF_NL_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 },
123771d3c704SKristof Provost 	{ .type = PF_NL_SRC_ADDR, .off = _OUT(src), .cb = nlattr_get_in6_addr },
123871d3c704SKristof Provost 	{ .type = PF_NL_DST_ADDR, .off = _OUT(dst), .cb = nlattr_get_in6_addr },
123971d3c704SKristof Provost 	{ .type = PF_NL_SRC_PORT, .off = _OUT(sport), .cb = nlattr_get_uint16 },
124071d3c704SKristof Provost 	{ .type = PF_NL_DST_PORT, .off = _OUT(dport), .cb = nlattr_get_uint16 },
124171d3c704SKristof Provost };
124271d3c704SKristof Provost #undef _OUT
1243e9255dafSGleb Smirnoff NL_DECLARE_PARSER(natlook_parser, struct genlmsghdr, nlf_p_empty, nla_p_natlook);
124471d3c704SKristof Provost 
124571d3c704SKristof Provost static int
124671d3c704SKristof Provost pf_handle_natlook(struct nlmsghdr *hdr, struct nl_pstate *npt)
124771d3c704SKristof Provost {
124871d3c704SKristof Provost 	struct pf_nl_natlook	 attrs = {};
124971d3c704SKristof Provost 	struct pf_state_key_cmp	 key = {};
125071d3c704SKristof Provost 	struct nl_writer	*nw = npt->nw;
125171d3c704SKristof Provost 	struct pf_state_key	*sk;
125271d3c704SKristof Provost 	struct pf_kstate	*state;
125371d3c704SKristof Provost 	struct genlmsghdr	*ghdr_new;
12544ee6a830SMark Johnston 	int			 error, m = 0;
125571d3c704SKristof Provost 	int			 sidx, didx;
125671d3c704SKristof Provost 
125771d3c704SKristof Provost 	error = nl_parse_nlmsg(hdr, &natlook_parser, npt, &attrs);
125871d3c704SKristof Provost 	if (error != 0)
125971d3c704SKristof Provost 		return (error);
126071d3c704SKristof Provost 
126171d3c704SKristof Provost 	if (attrs.proto == 0 ||
126271d3c704SKristof Provost 	    PF_AZERO(&attrs.src, attrs.af) ||
126371d3c704SKristof Provost 	    PF_AZERO(&attrs.dst, attrs.af) ||
126471d3c704SKristof Provost 	    ((attrs.proto == IPPROTO_TCP || attrs.proto == IPPROTO_UDP) &&
126571d3c704SKristof Provost 	     (attrs.sport == 0 || attrs.dport == 0)))
126671d3c704SKristof Provost 		return (EINVAL);
126771d3c704SKristof Provost 
126871d3c704SKristof Provost 	/* NATLOOK src and dst are reversed, so reverse sidx/didx */
126971d3c704SKristof Provost 	sidx = (attrs.direction == PF_IN) ? 1 : 0;
127071d3c704SKristof Provost 	didx = (attrs.direction == PF_IN) ? 0 : 1;
127171d3c704SKristof Provost 
127271d3c704SKristof Provost 	key.af = attrs.af;
127371d3c704SKristof Provost 	key.proto = attrs.proto;
127471d3c704SKristof Provost 	PF_ACPY(&key.addr[sidx], &attrs.src, attrs.af);
127571d3c704SKristof Provost 	key.port[sidx] = attrs.sport;
127671d3c704SKristof Provost 	PF_ACPY(&key.addr[didx], &attrs.dst, attrs.af);
127771d3c704SKristof Provost 	key.port[didx] = attrs.dport;
127871d3c704SKristof Provost 
127971d3c704SKristof Provost 	state = pf_find_state_all(&key, attrs.direction, &m);
128071d3c704SKristof Provost 	if (state == NULL)
128171d3c704SKristof Provost 		return (ENOENT);
128271d3c704SKristof Provost 	if (m > 1) {
128371d3c704SKristof Provost 		PF_STATE_UNLOCK(state);
128471d3c704SKristof Provost 		return (E2BIG);
128571d3c704SKristof Provost 	}
128671d3c704SKristof Provost 
128771d3c704SKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
128871d3c704SKristof Provost 		PF_STATE_UNLOCK(state);
128971d3c704SKristof Provost 		return (ENOMEM);
129071d3c704SKristof Provost 	}
129171d3c704SKristof Provost 
129271d3c704SKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
129371d3c704SKristof Provost 	ghdr_new->cmd = PFNL_CMD_NATLOOK;
129471d3c704SKristof Provost 	ghdr_new->version = 0;
129571d3c704SKristof Provost 	ghdr_new->reserved = 0;
129671d3c704SKristof Provost 
129771d3c704SKristof Provost 	sk = state->key[sidx];
129871d3c704SKristof Provost 
129971d3c704SKristof Provost 	nlattr_add_in6_addr(nw, PF_NL_SRC_ADDR, &sk->addr[sidx].v6);
130071d3c704SKristof Provost 	nlattr_add_in6_addr(nw, PF_NL_DST_ADDR, &sk->addr[didx].v6);
130171d3c704SKristof Provost 	nlattr_add_u16(nw, PF_NL_SRC_PORT, sk->port[sidx]);
130271d3c704SKristof Provost 	nlattr_add_u16(nw, PF_NL_DST_PORT, sk->port[didx]);
130371d3c704SKristof Provost 
130471d3c704SKristof Provost 	PF_STATE_UNLOCK(state);
130571d3c704SKristof Provost 
130671d3c704SKristof Provost 	if (!nlmsg_end(nw)) {
130771d3c704SKristof Provost 		nlmsg_abort(nw);
130871d3c704SKristof Provost 		return (ENOMEM);
130971d3c704SKristof Provost 	}
131071d3c704SKristof Provost 
131171d3c704SKristof Provost 	return (0);
131271d3c704SKristof Provost }
131371d3c704SKristof Provost 
1314c36c90a2SKristof Provost struct pf_nl_set_debug
1315c36c90a2SKristof Provost {
1316c36c90a2SKristof Provost 	uint32_t level;
1317c36c90a2SKristof Provost };
1318c36c90a2SKristof Provost #define	_OUT(_field)	offsetof(struct pf_nl_set_debug, _field)
1319c36c90a2SKristof Provost static const struct nlattr_parser nla_p_set_debug[] = {
1320c36c90a2SKristof Provost 	{ .type = PF_SD_LEVEL, .off = _OUT(level), .cb = nlattr_get_uint32 },
1321c36c90a2SKristof Provost };
1322c36c90a2SKristof Provost #undef _OUT
1323e9255dafSGleb Smirnoff NL_DECLARE_PARSER(set_debug_parser, struct genlmsghdr, nlf_p_empty, nla_p_set_debug);
1324c36c90a2SKristof Provost 
1325c36c90a2SKristof Provost static int
1326c36c90a2SKristof Provost pf_handle_set_debug(struct nlmsghdr *hdr, struct nl_pstate *npt)
1327c36c90a2SKristof Provost {
1328c36c90a2SKristof Provost 	struct pf_nl_set_debug attrs = {};
1329c36c90a2SKristof Provost 	int error;
1330c36c90a2SKristof Provost 
1331c36c90a2SKristof Provost 	error = nl_parse_nlmsg(hdr, &set_debug_parser, npt, &attrs);
1332c36c90a2SKristof Provost 	if (error != 0)
1333c36c90a2SKristof Provost 		return (error);
1334c36c90a2SKristof Provost 
1335c36c90a2SKristof Provost 	PF_RULES_WLOCK();
1336c36c90a2SKristof Provost 	V_pf_status.debug = attrs.level;
1337c36c90a2SKristof Provost 	PF_RULES_WUNLOCK();
1338c36c90a2SKristof Provost 
1339c36c90a2SKristof Provost 	return (0);
1340c36c90a2SKristof Provost }
1341c36c90a2SKristof Provost 
134230bad751SKristof Provost struct pf_nl_set_timeout
134330bad751SKristof Provost {
134430bad751SKristof Provost 	uint32_t timeout;
134530bad751SKristof Provost 	uint32_t seconds;
134630bad751SKristof Provost };
134730bad751SKristof Provost #define	_OUT(_field)	offsetof(struct pf_nl_set_timeout, _field)
134830bad751SKristof Provost static const struct nlattr_parser nla_p_set_timeout[] = {
134930bad751SKristof Provost 	{ .type = PF_TO_TIMEOUT, .off = _OUT(timeout), .cb = nlattr_get_uint32 },
135030bad751SKristof Provost 	{ .type = PF_TO_SECONDS, .off = _OUT(seconds), .cb = nlattr_get_uint32 },
135130bad751SKristof Provost };
135230bad751SKristof Provost #undef _OUT
1353e9255dafSGleb Smirnoff NL_DECLARE_PARSER(set_timeout_parser, struct genlmsghdr, nlf_p_empty, nla_p_set_timeout);
135430bad751SKristof Provost 
135530bad751SKristof Provost static int
135630bad751SKristof Provost pf_handle_set_timeout(struct nlmsghdr *hdr, struct nl_pstate *npt)
135730bad751SKristof Provost {
135830bad751SKristof Provost 	struct pf_nl_set_timeout attrs = {};
135930bad751SKristof Provost 	int error;
136030bad751SKristof Provost 
136130bad751SKristof Provost 	error = nl_parse_nlmsg(hdr, &set_timeout_parser, npt, &attrs);
136230bad751SKristof Provost 	if (error != 0)
136330bad751SKristof Provost 		return (error);
136430bad751SKristof Provost 
136530bad751SKristof Provost 	return (pf_ioctl_set_timeout(attrs.timeout, attrs.seconds, NULL));
136630bad751SKristof Provost }
136730bad751SKristof Provost 
136830bad751SKristof Provost static int
136930bad751SKristof Provost pf_handle_get_timeout(struct nlmsghdr *hdr, struct nl_pstate *npt)
137030bad751SKristof Provost {
137130bad751SKristof Provost 	struct pf_nl_set_timeout attrs = {};
137230bad751SKristof Provost 	struct nl_writer *nw = npt->nw;
137330bad751SKristof Provost 	struct genlmsghdr *ghdr_new;
137430bad751SKristof Provost 	int error;
137530bad751SKristof Provost 
137630bad751SKristof Provost 	error = nl_parse_nlmsg(hdr, &set_timeout_parser, npt, &attrs);
137730bad751SKristof Provost 	if (error != 0)
137830bad751SKristof Provost 		return (error);
137930bad751SKristof Provost 
138030bad751SKristof Provost 	error = pf_ioctl_get_timeout(attrs.timeout, &attrs.seconds);
138130bad751SKristof Provost 	if (error != 0)
138230bad751SKristof Provost 		return (error);
138330bad751SKristof Provost 
138430bad751SKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
138530bad751SKristof Provost 		return (ENOMEM);
138630bad751SKristof Provost 
138730bad751SKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
138830bad751SKristof Provost 	ghdr_new->cmd = PFNL_CMD_GET_TIMEOUT;
138930bad751SKristof Provost 	ghdr_new->version = 0;
139030bad751SKristof Provost 	ghdr_new->reserved = 0;
139130bad751SKristof Provost 
139230bad751SKristof Provost 	nlattr_add_u32(nw, PF_TO_SECONDS, attrs.seconds);
139330bad751SKristof Provost 
139430bad751SKristof Provost 	if (!nlmsg_end(nw)) {
139530bad751SKristof Provost 		nlmsg_abort(nw);
139630bad751SKristof Provost 		return (ENOMEM);
139730bad751SKristof Provost 	}
139830bad751SKristof Provost 
139930bad751SKristof Provost 	return (0);
140030bad751SKristof Provost }
140130bad751SKristof Provost 
1402d9ab8999SKristof Provost struct pf_nl_set_limit
1403d9ab8999SKristof Provost {
1404d9ab8999SKristof Provost 	uint32_t index;
1405d9ab8999SKristof Provost 	uint32_t limit;
1406d9ab8999SKristof Provost };
1407d9ab8999SKristof Provost #define	_OUT(_field)	offsetof(struct pf_nl_set_limit, _field)
1408d9ab8999SKristof Provost static const struct nlattr_parser nla_p_set_limit[] = {
1409d9ab8999SKristof Provost 	{ .type = PF_LI_INDEX, .off = _OUT(index), .cb = nlattr_get_uint32 },
1410d9ab8999SKristof Provost 	{ .type = PF_LI_LIMIT, .off = _OUT(limit), .cb = nlattr_get_uint32 },
1411d9ab8999SKristof Provost };
1412d9ab8999SKristof Provost #undef _OUT
1413e9255dafSGleb Smirnoff NL_DECLARE_PARSER(set_limit_parser, struct genlmsghdr, nlf_p_empty, nla_p_set_limit);
1414d9ab8999SKristof Provost 
1415d9ab8999SKristof Provost static int
1416d9ab8999SKristof Provost pf_handle_set_limit(struct nlmsghdr *hdr, struct nl_pstate *npt)
1417d9ab8999SKristof Provost {
1418d9ab8999SKristof Provost 	struct pf_nl_set_limit attrs = {};
1419d9ab8999SKristof Provost 	int error;
1420d9ab8999SKristof Provost 
1421d9ab8999SKristof Provost 	error = nl_parse_nlmsg(hdr, &set_limit_parser, npt, &attrs);
1422d9ab8999SKristof Provost 	if (error != 0)
1423d9ab8999SKristof Provost 		return (error);
1424d9ab8999SKristof Provost 
1425d9ab8999SKristof Provost 	return (pf_ioctl_set_limit(attrs.index, attrs.limit, NULL));
1426d9ab8999SKristof Provost }
1427d9ab8999SKristof Provost 
1428d9ab8999SKristof Provost static int
1429d9ab8999SKristof Provost pf_handle_get_limit(struct nlmsghdr *hdr, struct nl_pstate *npt)
1430d9ab8999SKristof Provost {
1431d9ab8999SKristof Provost 	struct pf_nl_set_limit attrs = {};
1432d9ab8999SKristof Provost 	struct nl_writer *nw = npt->nw;
1433d9ab8999SKristof Provost 	struct genlmsghdr *ghdr_new;
1434d9ab8999SKristof Provost 	int error;
1435d9ab8999SKristof Provost 
1436d9ab8999SKristof Provost 	error = nl_parse_nlmsg(hdr, &set_limit_parser, npt, &attrs);
1437d9ab8999SKristof Provost 	if (error != 0)
1438d9ab8999SKristof Provost 		return (error);
1439d9ab8999SKristof Provost 
1440d9ab8999SKristof Provost 	error = pf_ioctl_get_limit(attrs.index, &attrs.limit);
1441d9ab8999SKristof Provost 	if (error != 0)
1442d9ab8999SKristof Provost 		return (error);
1443d9ab8999SKristof Provost 
1444d9ab8999SKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1445d9ab8999SKristof Provost 		return (ENOMEM);
1446d9ab8999SKristof Provost 
1447d9ab8999SKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1448d9ab8999SKristof Provost 	ghdr_new->cmd = PFNL_CMD_GET_LIMIT;
1449d9ab8999SKristof Provost 	ghdr_new->version = 0;
1450d9ab8999SKristof Provost 	ghdr_new->reserved = 0;
1451d9ab8999SKristof Provost 
1452d9ab8999SKristof Provost 	nlattr_add_u32(nw, PF_LI_LIMIT, attrs.limit);
1453d9ab8999SKristof Provost 
1454d9ab8999SKristof Provost 	if (!nlmsg_end(nw)) {
1455d9ab8999SKristof Provost 		nlmsg_abort(nw);
1456d9ab8999SKristof Provost 		return (ENOMEM);
1457d9ab8999SKristof Provost 	}
1458d9ab8999SKristof Provost 
1459d9ab8999SKristof Provost 	return (0);
1460d9ab8999SKristof Provost }
1461d9ab8999SKristof Provost 
1462ba2a9207SKristof Provost static int
1463ba2a9207SKristof Provost pf_handle_begin_addrs(struct nlmsghdr *hdr, struct nl_pstate *npt)
1464ba2a9207SKristof Provost {
1465ba2a9207SKristof Provost 	struct nl_writer *nw = npt->nw;
1466ba2a9207SKristof Provost 	struct genlmsghdr *ghdr_new;
1467ba2a9207SKristof Provost 	uint32_t ticket;
1468ba2a9207SKristof Provost 	int error;
1469ba2a9207SKristof Provost 
1470ba2a9207SKristof Provost 	error = pf_ioctl_begin_addrs(&ticket);
1471ba2a9207SKristof Provost 	if (error != 0)
1472ba2a9207SKristof Provost 		return (error);
1473ba2a9207SKristof Provost 
1474ba2a9207SKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1475ba2a9207SKristof Provost 		return (ENOMEM);
1476ba2a9207SKristof Provost 
1477ba2a9207SKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1478ba2a9207SKristof Provost 	ghdr_new->cmd = PFNL_CMD_BEGIN_ADDRS;
1479ba2a9207SKristof Provost 	ghdr_new->version = 0;
1480ba2a9207SKristof Provost 	ghdr_new->reserved = 0;
1481ba2a9207SKristof Provost 
1482ba2a9207SKristof Provost 	nlattr_add_u32(nw, PF_BA_TICKET, ticket);
1483ba2a9207SKristof Provost 
1484ba2a9207SKristof Provost 	if (!nlmsg_end(nw)) {
1485ba2a9207SKristof Provost 		nlmsg_abort(nw);
1486ba2a9207SKristof Provost 		return (ENOMEM);
1487ba2a9207SKristof Provost 	}
1488ba2a9207SKristof Provost 
1489ba2a9207SKristof Provost 	return (0);
1490ba2a9207SKristof Provost }
1491ba2a9207SKristof Provost 
1492644b7b5aSKristof Provost static bool
1493644b7b5aSKristof Provost nlattr_add_pool_addr(struct nl_writer *nw, int attrtype, struct pf_pooladdr *a)
1494644b7b5aSKristof Provost {
1495644b7b5aSKristof Provost 	int off;
1496644b7b5aSKristof Provost 
1497644b7b5aSKristof Provost 	off = nlattr_add_nested(nw, attrtype);
1498644b7b5aSKristof Provost 
1499644b7b5aSKristof Provost 	nlattr_add_addr_wrap(nw, PF_PA_ADDR, &a->addr);
1500644b7b5aSKristof Provost 	nlattr_add_string(nw, PF_PA_IFNAME, a->ifname);
1501644b7b5aSKristof Provost 
1502644b7b5aSKristof Provost 	nlattr_set_len(nw, off);
1503644b7b5aSKristof Provost 
1504644b7b5aSKristof Provost 	return (true);
1505644b7b5aSKristof Provost }
1506644b7b5aSKristof Provost 
1507d909f06bSKristof Provost #define _OUT(_field)	offsetof(struct pf_pooladdr, _field)
1508d909f06bSKristof Provost static const struct nlattr_parser nla_p_pool_addr[] = {
1509d909f06bSKristof Provost 	{ .type = PF_PA_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = nlattr_get_nested },
1510d909f06bSKristof Provost 	{ .type = PF_PA_IFNAME, .off = _OUT(ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
1511d909f06bSKristof Provost };
1512d909f06bSKristof Provost NL_DECLARE_ATTR_PARSER(pool_addr_parser, nla_p_pool_addr);
1513d909f06bSKristof Provost #undef _OUT
1514d909f06bSKristof Provost 
1515e11dacbfSKristof Provost #define	_OUT(_field)	offsetof(struct pf_nl_pooladdr, _field)
1516d909f06bSKristof Provost static const struct nlattr_parser nla_p_add_addr[] = {
1517d909f06bSKristof Provost 	{ .type = PF_AA_ACTION, .off = _OUT(action), .cb = nlattr_get_uint32 },
1518d909f06bSKristof Provost 	{ .type = PF_AA_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
1519d909f06bSKristof Provost 	{ .type = PF_AA_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
1520d909f06bSKristof Provost 	{ .type = PF_AA_R_NUM, .off = _OUT(r_num), .cb = nlattr_get_uint32 },
1521d909f06bSKristof Provost 	{ .type = PF_AA_R_ACTION, .off = _OUT(r_action), .cb = nlattr_get_uint8 },
1522d909f06bSKristof Provost 	{ .type = PF_AA_R_LAST, .off = _OUT(r_last), .cb = nlattr_get_uint8 },
1523d909f06bSKristof Provost 	{ .type = PF_AA_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
1524d909f06bSKristof Provost 	{ .type = PF_AA_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
1525d909f06bSKristof Provost 	{ .type = PF_AA_ADDR, .off = _OUT(addr), .arg = &pool_addr_parser, .cb = nlattr_get_nested },
1526e11dacbfSKristof Provost 	{ .type = PF_AA_WHICH, .off = _OUT(which), .cb = nlattr_get_uint32 },
1527d909f06bSKristof Provost };
1528d909f06bSKristof Provost #undef _OUT
1529e9255dafSGleb Smirnoff NL_DECLARE_PARSER(add_addr_parser, struct genlmsghdr, nlf_p_empty, nla_p_add_addr);
1530d909f06bSKristof Provost 
1531d909f06bSKristof Provost static int
1532d909f06bSKristof Provost pf_handle_add_addr(struct nlmsghdr *hdr, struct nl_pstate *npt)
1533d909f06bSKristof Provost {
1534e11dacbfSKristof Provost 	struct pf_nl_pooladdr attrs = { 0 };
1535d909f06bSKristof Provost 	int error;
1536d909f06bSKristof Provost 
1537d909f06bSKristof Provost 	error = nl_parse_nlmsg(hdr, &add_addr_parser, npt, &attrs);
1538d909f06bSKristof Provost 	if (error != 0)
1539d909f06bSKristof Provost 		return (error);
1540d909f06bSKristof Provost 
1541e11dacbfSKristof Provost 	if (attrs.which == 0)
1542e11dacbfSKristof Provost 		attrs.which = PF_RDR;
1543e11dacbfSKristof Provost 
1544d909f06bSKristof Provost 	error = pf_ioctl_add_addr(&attrs);
1545d909f06bSKristof Provost 
1546d909f06bSKristof Provost 	return (error);
1547d909f06bSKristof Provost }
1548d909f06bSKristof Provost 
1549644b7b5aSKristof Provost static int
1550644b7b5aSKristof Provost pf_handle_get_addrs(struct nlmsghdr *hdr, struct nl_pstate *npt)
1551644b7b5aSKristof Provost {
1552e11dacbfSKristof Provost 	struct pf_nl_pooladdr attrs = { 0 };
1553644b7b5aSKristof Provost 	struct nl_writer *nw = npt->nw;
1554644b7b5aSKristof Provost 	struct genlmsghdr *ghdr_new;
1555644b7b5aSKristof Provost 	int error;
1556644b7b5aSKristof Provost 
1557644b7b5aSKristof Provost 	error = nl_parse_nlmsg(hdr, &add_addr_parser, npt, &attrs);
1558644b7b5aSKristof Provost 	if (error != 0)
1559644b7b5aSKristof Provost 		return (error);
1560644b7b5aSKristof Provost 
1561e11dacbfSKristof Provost 	if (attrs.which == 0)
1562e11dacbfSKristof Provost 		attrs.which = PF_RDR;
1563e11dacbfSKristof Provost 
1564644b7b5aSKristof Provost 	error = pf_ioctl_get_addrs(&attrs);
1565644b7b5aSKristof Provost 	if (error != 0)
1566644b7b5aSKristof Provost 		return (error);
1567644b7b5aSKristof Provost 
1568644b7b5aSKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1569644b7b5aSKristof Provost 		return (ENOMEM);
1570644b7b5aSKristof Provost 
1571644b7b5aSKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1572644b7b5aSKristof Provost 	ghdr_new->cmd = PFNL_CMD_GET_ADDRS;
1573644b7b5aSKristof Provost 	ghdr_new->version = 0;
1574644b7b5aSKristof Provost 	ghdr_new->reserved = 0;
1575644b7b5aSKristof Provost 
1576644b7b5aSKristof Provost 	nlattr_add_u32(nw, PF_AA_NR, attrs.nr);
1577644b7b5aSKristof Provost 
1578644b7b5aSKristof Provost 	if (!nlmsg_end(nw)) {
1579644b7b5aSKristof Provost 		nlmsg_abort(nw);
1580644b7b5aSKristof Provost 		return (ENOMEM);
1581644b7b5aSKristof Provost 	}
1582644b7b5aSKristof Provost 
1583644b7b5aSKristof Provost 	return (error);
1584644b7b5aSKristof Provost }
1585644b7b5aSKristof Provost 
15869ae91f59SKristof Provost static int
15879ae91f59SKristof Provost pf_handle_get_addr(struct nlmsghdr *hdr, struct nl_pstate *npt)
15889ae91f59SKristof Provost {
1589e11dacbfSKristof Provost 	struct pf_nl_pooladdr attrs = { 0 };
15909ae91f59SKristof Provost 	struct nl_writer *nw = npt->nw;
15919ae91f59SKristof Provost 	struct genlmsghdr *ghdr_new;
15929ae91f59SKristof Provost 	int error;
15939ae91f59SKristof Provost 
15949ae91f59SKristof Provost 	error = nl_parse_nlmsg(hdr, &add_addr_parser, npt, &attrs);
15959ae91f59SKristof Provost 	if (error != 0)
15969ae91f59SKristof Provost 		return (error);
15979ae91f59SKristof Provost 
1598e11dacbfSKristof Provost 	if (attrs.which == 0)
1599e11dacbfSKristof Provost 		attrs.which = PF_RDR;
1600e11dacbfSKristof Provost 
16019ae91f59SKristof Provost 	error = pf_ioctl_get_addr(&attrs);
16029ae91f59SKristof Provost 	if (error != 0)
16039ae91f59SKristof Provost 		return (error);
16049ae91f59SKristof Provost 
16059ae91f59SKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
16069ae91f59SKristof Provost 		return (ENOMEM);
16079ae91f59SKristof Provost 
16089ae91f59SKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
16093f6a34caSKristof Provost 	ghdr_new->cmd = PFNL_CMD_GET_ADDR;
16109ae91f59SKristof Provost 	ghdr_new->version = 0;
16119ae91f59SKristof Provost 	ghdr_new->reserved = 0;
16129ae91f59SKristof Provost 
16139ae91f59SKristof Provost 	nlattr_add_u32(nw, PF_AA_ACTION, attrs.action);
16149ae91f59SKristof Provost 	nlattr_add_u32(nw, PF_AA_TICKET, attrs.ticket);
16159ae91f59SKristof Provost 	nlattr_add_u32(nw, PF_AA_NR, attrs.nr);
16169ae91f59SKristof Provost 	nlattr_add_u32(nw, PF_AA_R_NUM, attrs.r_num);
16179ae91f59SKristof Provost 	nlattr_add_u8(nw, PF_AA_R_ACTION, attrs.r_action);
16189ae91f59SKristof Provost 	nlattr_add_u8(nw, PF_AA_R_LAST, attrs.r_last);
16199ae91f59SKristof Provost 	nlattr_add_u8(nw, PF_AA_AF, attrs.af);
16209ae91f59SKristof Provost 	nlattr_add_string(nw, PF_AA_ANCHOR, attrs.anchor);
16219ae91f59SKristof Provost 	nlattr_add_pool_addr(nw, PF_AA_ADDR, &attrs.addr);
16229ae91f59SKristof Provost 
16239ae91f59SKristof Provost 	if (!nlmsg_end(nw)) {
16249ae91f59SKristof Provost 		nlmsg_abort(nw);
16259ae91f59SKristof Provost 		return (ENOMEM);
16269ae91f59SKristof Provost 	}
16279ae91f59SKristof Provost 
16289ae91f59SKristof Provost 	return (0);
16299ae91f59SKristof Provost }
16309ae91f59SKristof Provost 
163125e0f8f9SKristof Provost #define _OUT(_field)	offsetof(struct pfioc_ruleset, _field)
163225e0f8f9SKristof Provost static const struct nlattr_parser nla_p_ruleset[] = {
163325e0f8f9SKristof Provost 	{ .type = PF_RS_PATH, .off = _OUT(path), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
163448f5bf8bSKristof Provost 	{ .type = PF_RS_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
163525e0f8f9SKristof Provost };
1636e9255dafSGleb Smirnoff NL_DECLARE_PARSER(ruleset_parser, struct genlmsghdr, nlf_p_empty, nla_p_ruleset);
163725e0f8f9SKristof Provost #undef _OUT
163825e0f8f9SKristof Provost 
163925e0f8f9SKristof Provost static int
164025e0f8f9SKristof Provost pf_handle_get_rulesets(struct nlmsghdr *hdr, struct nl_pstate *npt)
164125e0f8f9SKristof Provost {
164225e0f8f9SKristof Provost 	struct pfioc_ruleset attrs = { 0 };
164325e0f8f9SKristof Provost 	struct nl_writer *nw = npt->nw;
164425e0f8f9SKristof Provost 	struct genlmsghdr *ghdr_new;
164525e0f8f9SKristof Provost 	int error;
164625e0f8f9SKristof Provost 
164725e0f8f9SKristof Provost 	error = nl_parse_nlmsg(hdr, &ruleset_parser, npt, &attrs);
164825e0f8f9SKristof Provost 	if (error != 0)
164925e0f8f9SKristof Provost 		return (error);
165025e0f8f9SKristof Provost 
165125e0f8f9SKristof Provost 	error = pf_ioctl_get_rulesets(&attrs);
165225e0f8f9SKristof Provost 	if (error != 0)
165325e0f8f9SKristof Provost 		return (error);
165425e0f8f9SKristof Provost 
165525e0f8f9SKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
165625e0f8f9SKristof Provost 		return (ENOMEM);
165725e0f8f9SKristof Provost 
165825e0f8f9SKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
165925e0f8f9SKristof Provost 	ghdr_new->cmd = PFNL_CMD_GET_RULESETS;
166025e0f8f9SKristof Provost 	ghdr_new->version = 0;
166125e0f8f9SKristof Provost 	ghdr_new->reserved = 0;
166225e0f8f9SKristof Provost 
166325e0f8f9SKristof Provost 	nlattr_add_u32(nw, PF_RS_NR, attrs.nr);
166425e0f8f9SKristof Provost 
166525e0f8f9SKristof Provost 	if (!nlmsg_end(nw)) {
166625e0f8f9SKristof Provost 		nlmsg_abort(nw);
166725e0f8f9SKristof Provost 		return (ENOMEM);
166825e0f8f9SKristof Provost 	}
166925e0f8f9SKristof Provost 
167025e0f8f9SKristof Provost 	return (0);
167125e0f8f9SKristof Provost }
167225e0f8f9SKristof Provost 
167348f5bf8bSKristof Provost static int
167448f5bf8bSKristof Provost pf_handle_get_ruleset(struct nlmsghdr *hdr, struct nl_pstate *npt)
167548f5bf8bSKristof Provost {
167648f5bf8bSKristof Provost 	struct pfioc_ruleset attrs = { 0 };
167748f5bf8bSKristof Provost 	struct nl_writer *nw = npt->nw;
167848f5bf8bSKristof Provost 	struct genlmsghdr *ghdr_new;
167948f5bf8bSKristof Provost 	int error;
168048f5bf8bSKristof Provost 
168148f5bf8bSKristof Provost 	error = nl_parse_nlmsg(hdr, &ruleset_parser, npt, &attrs);
168248f5bf8bSKristof Provost 	if (error)
168348f5bf8bSKristof Provost 		return (error);
168448f5bf8bSKristof Provost 
168548f5bf8bSKristof Provost 	error = pf_ioctl_get_ruleset(&attrs);
168648f5bf8bSKristof Provost 	if (error != 0)
168748f5bf8bSKristof Provost 		return (error);
168848f5bf8bSKristof Provost 
168948f5bf8bSKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
169048f5bf8bSKristof Provost 		return (ENOMEM);
169148f5bf8bSKristof Provost 
169248f5bf8bSKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
169348f5bf8bSKristof Provost 	ghdr_new->cmd = PFNL_CMD_GET_RULESET;
169448f5bf8bSKristof Provost 	ghdr_new->version = 0;
169548f5bf8bSKristof Provost 	ghdr_new->reserved = 0;
169648f5bf8bSKristof Provost 
169748f5bf8bSKristof Provost 	nlattr_add_string(nw, PF_RS_NAME, attrs.name);
169848f5bf8bSKristof Provost 
169948f5bf8bSKristof Provost 	if (!nlmsg_end(nw)) {
170048f5bf8bSKristof Provost 		nlmsg_abort(nw);
170148f5bf8bSKristof Provost 		return (ENOMEM);
170248f5bf8bSKristof Provost 	}
170348f5bf8bSKristof Provost 
170448f5bf8bSKristof Provost 	return (0);
170548f5bf8bSKristof Provost }
170648f5bf8bSKristof Provost 
17079c125336SKristof Provost static bool
17087d929a44SKajetan Staszkiewicz nlattr_add_pf_threshold(struct nl_writer *nw, int attrtype,
17097d929a44SKajetan Staszkiewicz     struct pf_threshold *t, int secs)
17109c125336SKristof Provost {
17119c125336SKristof Provost 	int	 off = nlattr_add_nested(nw, attrtype);
17127d929a44SKajetan Staszkiewicz 	int	 diff, conn_rate_count;
17137d929a44SKajetan Staszkiewicz 
17147d929a44SKajetan Staszkiewicz 	/* Adjust the connection rate estimate. */
17157d929a44SKajetan Staszkiewicz 	conn_rate_count = t->count;
17167d929a44SKajetan Staszkiewicz 	diff = secs - t->last;
17177d929a44SKajetan Staszkiewicz 	if (diff >= t->seconds)
17187d929a44SKajetan Staszkiewicz 		conn_rate_count = 0;
17197d929a44SKajetan Staszkiewicz 	else
17207d929a44SKajetan Staszkiewicz 		conn_rate_count -= t->count * diff / t->seconds;
17219c125336SKristof Provost 
17229c125336SKristof Provost 	nlattr_add_u32(nw, PF_TH_LIMIT, t->limit);
17239c125336SKristof Provost 	nlattr_add_u32(nw, PF_TH_SECONDS, t->seconds);
17247d929a44SKajetan Staszkiewicz 	nlattr_add_u32(nw, PF_TH_COUNT, conn_rate_count);
17259c125336SKristof Provost 	nlattr_add_u32(nw, PF_TH_LAST, t->last);
17269c125336SKristof Provost 
17279c125336SKristof Provost 	nlattr_set_len(nw, off);
17289c125336SKristof Provost 
17299c125336SKristof Provost 	return (true);
17309c125336SKristof Provost }
17319c125336SKristof Provost 
17329c125336SKristof Provost static int
17339c125336SKristof Provost pf_handle_get_srcnodes(struct nlmsghdr *hdr, struct nl_pstate *npt)
17349c125336SKristof Provost {
17359c125336SKristof Provost 	struct nl_writer	*nw = npt->nw;
17369c125336SKristof Provost 	struct genlmsghdr	*ghdr_new;
17379c125336SKristof Provost 	struct pf_ksrc_node	*n;
17389c125336SKristof Provost 	struct pf_srchash	*sh;
17399c125336SKristof Provost 	int			 i;
17407d929a44SKajetan Staszkiewicz 	int			 secs;
17419c125336SKristof Provost 
17429c125336SKristof Provost 	hdr->nlmsg_flags |= NLM_F_MULTI;
17439c125336SKristof Provost 
17449c125336SKristof Provost 	for (i = 0, sh = V_pf_srchash; i <= V_pf_srchashmask;
17459c125336SKristof Provost 	    i++, sh++) {
17469c125336SKristof Provost 		/* Avoid locking empty rows. */
17479c125336SKristof Provost 		if (LIST_EMPTY(&sh->nodes))
17489c125336SKristof Provost 			continue;
17499c125336SKristof Provost 
17509c125336SKristof Provost 		PF_HASHROW_LOCK(sh);
17517d929a44SKajetan Staszkiewicz 		secs = time_uptime;
17527d929a44SKajetan Staszkiewicz 
17539c125336SKristof Provost 		LIST_FOREACH(n, &sh->nodes, entry) {
17549c125336SKristof Provost 			if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) {
17559c125336SKristof Provost 				nlmsg_abort(nw);
17569c125336SKristof Provost 				return (ENOMEM);
17579c125336SKristof Provost 			}
17589c125336SKristof Provost 
17599c125336SKristof Provost 			ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
17609c125336SKristof Provost 			ghdr_new->cmd = PFNL_CMD_GET_SRCNODES;
17619c125336SKristof Provost 			ghdr_new->version = 0;
17629c125336SKristof Provost 			ghdr_new->reserved = 0;
17639c125336SKristof Provost 
17649c125336SKristof Provost 			nlattr_add_in6_addr(nw, PF_SN_ADDR, &n->addr.v6);
17659c125336SKristof Provost 			nlattr_add_in6_addr(nw, PF_SN_RADDR, &n->raddr.v6);
17669c125336SKristof Provost 			nlattr_add_u32(nw, PF_SN_RULE_NR, n->rule->nr);
17679c125336SKristof Provost 			nlattr_add_u64(nw, PF_SN_BYTES_IN, counter_u64_fetch(n->bytes[0]));
17689c125336SKristof Provost 			nlattr_add_u64(nw, PF_SN_BYTES_OUT, counter_u64_fetch(n->bytes[1]));
17699c125336SKristof Provost 			nlattr_add_u64(nw, PF_SN_PACKETS_IN, counter_u64_fetch(n->packets[0]));
17709c125336SKristof Provost 			nlattr_add_u64(nw, PF_SN_PACKETS_OUT, counter_u64_fetch(n->packets[1]));
17719c125336SKristof Provost 			nlattr_add_u32(nw, PF_SN_STATES, n->states);
17729c125336SKristof Provost 			nlattr_add_u32(nw, PF_SN_CONNECTIONS, n->conn);
17739c125336SKristof Provost 			nlattr_add_u8(nw, PF_SN_AF, n->af);
1774aa69fdf1SKristof Provost 			nlattr_add_u8(nw, PF_SN_NAF, n->naf);
17759c125336SKristof Provost 			nlattr_add_u8(nw, PF_SN_RULE_TYPE, n->ruletype);
17767d929a44SKajetan Staszkiewicz 
17777d929a44SKajetan Staszkiewicz 			nlattr_add_u64(nw, PF_SN_CREATION, secs - n->creation);
17787d929a44SKajetan Staszkiewicz 			if (n->expire > secs)
17797d929a44SKajetan Staszkiewicz 				nlattr_add_u64(nw, PF_SN_EXPIRE, n->expire - secs);
17807d929a44SKajetan Staszkiewicz 			else
17817d929a44SKajetan Staszkiewicz 				nlattr_add_u64(nw, PF_SN_EXPIRE, 0);
17827d929a44SKajetan Staszkiewicz 
17837d929a44SKajetan Staszkiewicz 			nlattr_add_pf_threshold(nw, PF_SN_CONNECTION_RATE,
17847d929a44SKajetan Staszkiewicz 			    &n->conn_rate, secs);
17859c125336SKristof Provost 
17869c125336SKristof Provost 			if (!nlmsg_end(nw)) {
17879c125336SKristof Provost 				PF_HASHROW_UNLOCK(sh);
17889c125336SKristof Provost 				nlmsg_abort(nw);
17899c125336SKristof Provost 				return (ENOMEM);
17909c125336SKristof Provost 			}
17919c125336SKristof Provost 		}
17929c125336SKristof Provost 		PF_HASHROW_UNLOCK(sh);
17939c125336SKristof Provost 	}
17949c125336SKristof Provost 
17959c125336SKristof Provost 	return (0);
17969c125336SKristof Provost }
17979c125336SKristof Provost 
1798441d4894SKristof Provost #define _OUT(_field)	offsetof(struct pfioc_table, _field)
1799441d4894SKristof Provost static const struct nlattr_parser nla_p_table[] = {
1800441d4894SKristof Provost 	{ .type = PF_T_ANCHOR, .off = _OUT(pfrio_table.pfrt_anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
1801441d4894SKristof Provost 	{ .type = PF_T_NAME, .off = _OUT(pfrio_table.pfrt_name), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
1802441d4894SKristof Provost 	{ .type = PF_T_TABLE_FLAGS, .off = _OUT(pfrio_table.pfrt_flags), .cb = nlattr_get_uint32 },
1803441d4894SKristof Provost 	{ .type = PF_T_FLAGS, .off = _OUT(pfrio_flags), .cb = nlattr_get_uint32 },
1804441d4894SKristof Provost };
1805441d4894SKristof Provost static const struct nlfield_parser nlf_p_table[] = {};
1806441d4894SKristof Provost NL_DECLARE_PARSER(table_parser, struct genlmsghdr, nlf_p_table, nla_p_table);
1807441d4894SKristof Provost #undef _OUT
1808441d4894SKristof Provost static int
1809441d4894SKristof Provost pf_handle_clear_tables(struct nlmsghdr *hdr, struct nl_pstate *npt)
1810441d4894SKristof Provost {
1811441d4894SKristof Provost 	struct pfioc_table attrs = { 0 };
1812441d4894SKristof Provost 	struct nl_writer *nw = npt->nw;
1813441d4894SKristof Provost 	struct genlmsghdr *ghdr_new;
1814441d4894SKristof Provost 	int ndel = 0;
1815441d4894SKristof Provost 	int error;
1816441d4894SKristof Provost 
1817441d4894SKristof Provost 	error = nl_parse_nlmsg(hdr, &table_parser, npt, &attrs);
1818441d4894SKristof Provost 	if (error != 0)
1819441d4894SKristof Provost 		return (error);
1820441d4894SKristof Provost 
1821441d4894SKristof Provost 	PF_RULES_WLOCK();
1822441d4894SKristof Provost 	error = pfr_clr_tables(&attrs.pfrio_table, &ndel, attrs.pfrio_flags | PFR_FLAG_USERIOCTL);
1823441d4894SKristof Provost 	PF_RULES_WUNLOCK();
1824441d4894SKristof Provost 	if (error != 0)
1825441d4894SKristof Provost 		return (error);
1826441d4894SKristof Provost 
1827441d4894SKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1828441d4894SKristof Provost 		return (ENOMEM);
1829441d4894SKristof Provost 
1830441d4894SKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1831441d4894SKristof Provost 	ghdr_new->cmd = PFNL_CMD_CLEAR_TABLES;
1832441d4894SKristof Provost 	ghdr_new->version = 0;
1833441d4894SKristof Provost 	ghdr_new->reserved = 0;
1834441d4894SKristof Provost 
1835441d4894SKristof Provost 	nlattr_add_u32(nw, PF_T_NBR_DELETED, ndel);
1836441d4894SKristof Provost 
1837441d4894SKristof Provost 	if (!nlmsg_end(nw)) {
1838441d4894SKristof Provost 		nlmsg_abort(nw);
1839441d4894SKristof Provost 		return (ENOMEM);
1840441d4894SKristof Provost 	}
1841441d4894SKristof Provost 
1842441d4894SKristof Provost 	return (0);
1843441d4894SKristof Provost }
1844441d4894SKristof Provost 
1845*84a80eaeSKristof Provost static int
1846*84a80eaeSKristof Provost pf_handle_add_table(struct nlmsghdr *hdr, struct nl_pstate *npt)
1847*84a80eaeSKristof Provost {
1848*84a80eaeSKristof Provost 	struct pfioc_table attrs = { 0 };
1849*84a80eaeSKristof Provost 	struct nl_writer *nw = npt->nw;
1850*84a80eaeSKristof Provost 	struct genlmsghdr *ghdr_new;
1851*84a80eaeSKristof Provost 	int error;
1852*84a80eaeSKristof Provost 
1853*84a80eaeSKristof Provost 	error = nl_parse_nlmsg(hdr, &table_parser, npt, &attrs);
1854*84a80eaeSKristof Provost 	if (error != 0)
1855*84a80eaeSKristof Provost 		return (error);
1856*84a80eaeSKristof Provost 
1857*84a80eaeSKristof Provost 	PF_RULES_WLOCK();
1858*84a80eaeSKristof Provost 	error = pfr_add_tables(&attrs.pfrio_table, 1, &attrs.pfrio_nadd,
1859*84a80eaeSKristof Provost 	    attrs.pfrio_flags | PFR_FLAG_USERIOCTL);
1860*84a80eaeSKristof Provost 	PF_RULES_WUNLOCK();
1861*84a80eaeSKristof Provost 	if (error != 0)
1862*84a80eaeSKristof Provost 		return (error);
1863*84a80eaeSKristof Provost 
1864*84a80eaeSKristof Provost 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
1865*84a80eaeSKristof Provost 		return (ENOMEM);
1866*84a80eaeSKristof Provost 
1867*84a80eaeSKristof Provost 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
1868*84a80eaeSKristof Provost 	ghdr_new->cmd = PFNL_CMD_ADD_TABLE;
1869*84a80eaeSKristof Provost 	ghdr_new->version = 0;
1870*84a80eaeSKristof Provost 	ghdr_new->reserved = 0;
1871*84a80eaeSKristof Provost 
1872*84a80eaeSKristof Provost 	nlattr_add_u32(nw, PF_T_NBR_ADDED, attrs.pfrio_nadd);
1873*84a80eaeSKristof Provost 
1874*84a80eaeSKristof Provost 	if (!nlmsg_end(nw)) {
1875*84a80eaeSKristof Provost 		nlmsg_abort(nw);
1876*84a80eaeSKristof Provost 		return (ENOMEM);
1877*84a80eaeSKristof Provost 	}
1878*84a80eaeSKristof Provost 
1879*84a80eaeSKristof Provost 	return (0);
1880*84a80eaeSKristof Provost }
1881*84a80eaeSKristof Provost 
188244f323ecSKristof Provost static const struct nlhdr_parser *all_parsers[] = {
188344f323ecSKristof Provost 	&state_parser,
188444f323ecSKristof Provost 	&addrule_parser,
1885706d465dSKristof Provost 	&getrules_parser,
1886706d465dSKristof Provost 	&clear_states_parser,
1887470a2b33SKristof Provost 	&set_statusif_parser,
188871d3c704SKristof Provost 	&natlook_parser,
1889c36c90a2SKristof Provost 	&set_debug_parser,
189030bad751SKristof Provost 	&set_timeout_parser,
1891d9ab8999SKristof Provost 	&set_limit_parser,
1892d909f06bSKristof Provost 	&pool_addr_parser,
1893d909f06bSKristof Provost 	&add_addr_parser,
189425e0f8f9SKristof Provost 	&ruleset_parser,
1895441d4894SKristof Provost 	&table_parser,
189644f323ecSKristof Provost };
18972cef6288SAlexander V. Chernikov 
1898ee507b70SGleb Smirnoff static uint16_t family_id;
18992cef6288SAlexander V. Chernikov 
19002cef6288SAlexander V. Chernikov static const struct genl_cmd pf_cmds[] = {
19012cef6288SAlexander V. Chernikov 	{
19022cef6288SAlexander V. Chernikov 		.cmd_num = PFNL_CMD_GETSTATES,
19032cef6288SAlexander V. Chernikov 		.cmd_name = "GETSTATES",
19042cef6288SAlexander V. Chernikov 		.cmd_cb = pf_handle_getstates,
19052cef6288SAlexander V. Chernikov 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1906e774c1efSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
19072cef6288SAlexander V. Chernikov 	},
1908a7191e5dSKristof Provost 	{
1909a7191e5dSKristof Provost 		.cmd_num = PFNL_CMD_GETCREATORS,
1910a7191e5dSKristof Provost 		.cmd_name = "GETCREATORS",
1911a7191e5dSKristof Provost 		.cmd_cb = pf_handle_getcreators,
1912a7191e5dSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1913e774c1efSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1914a7191e5dSKristof Provost 	},
191581647eb6SKristof Provost 	{
191681647eb6SKristof Provost 		.cmd_num = PFNL_CMD_START,
191781647eb6SKristof Provost 		.cmd_name = "START",
191881647eb6SKristof Provost 		.cmd_cb = pf_handle_start,
191981647eb6SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1920e774c1efSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
192181647eb6SKristof Provost 	},
192281647eb6SKristof Provost 	{
192381647eb6SKristof Provost 		.cmd_num = PFNL_CMD_STOP,
192481647eb6SKristof Provost 		.cmd_name = "STOP",
192581647eb6SKristof Provost 		.cmd_cb = pf_handle_stop,
192681647eb6SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1927e774c1efSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
192881647eb6SKristof Provost 	},
1929ffbf2595SKristof Provost 	{
1930ffbf2595SKristof Provost 		.cmd_num = PFNL_CMD_ADDRULE,
1931ffbf2595SKristof Provost 		.cmd_name = "ADDRULE",
1932ffbf2595SKristof Provost 		.cmd_cb = pf_handle_addrule,
1933ffbf2595SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1934e774c1efSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1935ffbf2595SKristof Provost 	},
193644f323ecSKristof Provost 	{
193744f323ecSKristof Provost 		.cmd_num = PFNL_CMD_GETRULES,
193844f323ecSKristof Provost 		.cmd_name = "GETRULES",
193944f323ecSKristof Provost 		.cmd_cb = pf_handle_getrules,
194044f323ecSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1941e774c1efSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
194244f323ecSKristof Provost 	},
1943777a4702SKristof Provost 	{
1944777a4702SKristof Provost 		.cmd_num = PFNL_CMD_GETRULE,
1945777a4702SKristof Provost 		.cmd_name = "GETRULE",
1946777a4702SKristof Provost 		.cmd_cb = pf_handle_getrule,
1947777a4702SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1948777a4702SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1949777a4702SKristof Provost 	},
1950706d465dSKristof Provost 	{
1951706d465dSKristof Provost 		.cmd_num = PFNL_CMD_CLRSTATES,
1952706d465dSKristof Provost 		.cmd_name = "CLRSTATES",
1953706d465dSKristof Provost 		.cmd_cb = pf_handle_clear_states,
1954706d465dSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1955706d465dSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1956706d465dSKristof Provost 	},
1957706d465dSKristof Provost 	{
1958706d465dSKristof Provost 		.cmd_num = PFNL_CMD_KILLSTATES,
1959706d465dSKristof Provost 		.cmd_name = "KILLSTATES",
1960706d465dSKristof Provost 		.cmd_cb = pf_handle_kill_states,
1961706d465dSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
1962706d465dSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1963706d465dSKristof Provost 	},
1964470a2b33SKristof Provost 	{
1965470a2b33SKristof Provost 		.cmd_num = PFNL_CMD_SET_STATUSIF,
1966470a2b33SKristof Provost 		.cmd_name = "SETSTATUSIF",
1967470a2b33SKristof Provost 		.cmd_cb = pf_handle_set_statusif,
1968470a2b33SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1969470a2b33SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
19705824df8dSKristof Provost 	},
19715824df8dSKristof Provost 	{
19725824df8dSKristof Provost 		.cmd_num = PFNL_CMD_GET_STATUS,
19735824df8dSKristof Provost 		.cmd_name = "GETSTATUS",
19745824df8dSKristof Provost 		.cmd_cb = pf_handle_get_status,
19755824df8dSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
19765824df8dSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
19775824df8dSKristof Provost 	},
19789dbbe68bSKristof Provost 	{
19799dbbe68bSKristof Provost 		.cmd_num = PFNL_CMD_CLEAR_STATUS,
19809dbbe68bSKristof Provost 		.cmd_name = "CLEARSTATUS",
19819dbbe68bSKristof Provost 		.cmd_cb = pf_handle_clear_status,
19829dbbe68bSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
19839dbbe68bSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
19849dbbe68bSKristof Provost 	},
198571d3c704SKristof Provost 	{
198671d3c704SKristof Provost 		.cmd_num = PFNL_CMD_NATLOOK,
198771d3c704SKristof Provost 		.cmd_name = "NATLOOK",
198871d3c704SKristof Provost 		.cmd_cb = pf_handle_natlook,
198971d3c704SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
199071d3c704SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
199171d3c704SKristof Provost 	},
1992c36c90a2SKristof Provost 	{
1993c36c90a2SKristof Provost 		.cmd_num = PFNL_CMD_SET_DEBUG,
1994c36c90a2SKristof Provost 		.cmd_name = "SET_DEBUG",
1995c36c90a2SKristof Provost 		.cmd_cb = pf_handle_set_debug,
1996c36c90a2SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
1997c36c90a2SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
1998c36c90a2SKristof Provost 	},
199930bad751SKristof Provost 	{
200030bad751SKristof Provost 		.cmd_num = PFNL_CMD_SET_TIMEOUT,
200130bad751SKristof Provost 		.cmd_name = "SET_TIMEOUT",
200230bad751SKristof Provost 		.cmd_cb = pf_handle_set_timeout,
200330bad751SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
200430bad751SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
200530bad751SKristof Provost 	},
200630bad751SKristof Provost 	{
200730bad751SKristof Provost 		.cmd_num = PFNL_CMD_GET_TIMEOUT,
200830bad751SKristof Provost 		.cmd_name = "GET_TIMEOUT",
200930bad751SKristof Provost 		.cmd_cb = pf_handle_get_timeout,
201030bad751SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
201130bad751SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
201230bad751SKristof Provost 	},
2013d9ab8999SKristof Provost 	{
2014d9ab8999SKristof Provost 		.cmd_num = PFNL_CMD_SET_LIMIT,
2015d9ab8999SKristof Provost 		.cmd_name = "SET_LIMIT",
2016d9ab8999SKristof Provost 		.cmd_cb = pf_handle_set_limit,
2017d9ab8999SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
2018d9ab8999SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
2019d9ab8999SKristof Provost 	},
2020d9ab8999SKristof Provost 	{
2021d9ab8999SKristof Provost 		.cmd_num = PFNL_CMD_GET_LIMIT,
2022d9ab8999SKristof Provost 		.cmd_name = "GET_LIMIT",
2023d9ab8999SKristof Provost 		.cmd_cb = pf_handle_get_limit,
2024d9ab8999SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
2025d9ab8999SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
2026d9ab8999SKristof Provost 	},
2027ba2a9207SKristof Provost 	{
2028ba2a9207SKristof Provost 		.cmd_num = PFNL_CMD_BEGIN_ADDRS,
2029ba2a9207SKristof Provost 		.cmd_name = "BEGIN_ADDRS",
2030ba2a9207SKristof Provost 		.cmd_cb = pf_handle_begin_addrs,
2031ba2a9207SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
2032ba2a9207SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
2033ba2a9207SKristof Provost 	},
2034d909f06bSKristof Provost 	{
2035d909f06bSKristof Provost 		.cmd_num = PFNL_CMD_ADD_ADDR,
2036d909f06bSKristof Provost 		.cmd_name = "ADD_ADDR",
2037d909f06bSKristof Provost 		.cmd_cb = pf_handle_add_addr,
2038d909f06bSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
2039d909f06bSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
2040d909f06bSKristof Provost 	},
2041644b7b5aSKristof Provost 	{
2042644b7b5aSKristof Provost 		.cmd_num = PFNL_CMD_GET_ADDRS,
2043644b7b5aSKristof Provost 		.cmd_name = "GET_ADDRS",
2044644b7b5aSKristof Provost 		.cmd_cb = pf_handle_get_addrs,
2045644b7b5aSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
2046644b7b5aSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
2047644b7b5aSKristof Provost 	},
20489ae91f59SKristof Provost 	{
20499ae91f59SKristof Provost 		.cmd_num = PFNL_CMD_GET_ADDR,
20509ae91f59SKristof Provost 		.cmd_name = "GET_ADDRS",
20519ae91f59SKristof Provost 		.cmd_cb = pf_handle_get_addr,
20529ae91f59SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
20539ae91f59SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
20549ae91f59SKristof Provost 	},
205525e0f8f9SKristof Provost 	{
205625e0f8f9SKristof Provost 		.cmd_num = PFNL_CMD_GET_RULESETS,
205725e0f8f9SKristof Provost 		.cmd_name = "GET_RULESETS",
205825e0f8f9SKristof Provost 		.cmd_cb = pf_handle_get_rulesets,
205925e0f8f9SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
206025e0f8f9SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
206125e0f8f9SKristof Provost 	},
206248f5bf8bSKristof Provost 	{
206348f5bf8bSKristof Provost 		.cmd_num = PFNL_CMD_GET_RULESET,
206448f5bf8bSKristof Provost 		.cmd_name = "GET_RULESET",
206548f5bf8bSKristof Provost 		.cmd_cb = pf_handle_get_ruleset,
206648f5bf8bSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
206748f5bf8bSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
206848f5bf8bSKristof Provost 	},
20699c125336SKristof Provost 	{
20709c125336SKristof Provost 		.cmd_num = PFNL_CMD_GET_SRCNODES,
20719c125336SKristof Provost 		.cmd_name = "GET_SRCNODES",
20729c125336SKristof Provost 		.cmd_cb = pf_handle_get_srcnodes,
20739c125336SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
20749c125336SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
20759c125336SKristof Provost 	},
2076441d4894SKristof Provost 	{
2077441d4894SKristof Provost 		.cmd_num = PFNL_CMD_CLEAR_TABLES,
2078441d4894SKristof Provost 		.cmd_name = "CLEAR_TABLES",
2079441d4894SKristof Provost 		.cmd_cb = pf_handle_clear_tables,
2080441d4894SKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
2081441d4894SKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
2082441d4894SKristof Provost 	},
2083*84a80eaeSKristof Provost 	{
2084*84a80eaeSKristof Provost 		.cmd_num = PFNL_CMD_ADD_TABLE,
2085*84a80eaeSKristof Provost 		.cmd_name = "ADD_TABLE",
2086*84a80eaeSKristof Provost 		.cmd_cb = pf_handle_add_table,
2087*84a80eaeSKristof Provost 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
2088*84a80eaeSKristof Provost 		.cmd_priv = PRIV_NETINET_PF,
2089*84a80eaeSKristof Provost 	},
20902cef6288SAlexander V. Chernikov };
20912cef6288SAlexander V. Chernikov 
20922cef6288SAlexander V. Chernikov void
20932cef6288SAlexander V. Chernikov pf_nl_register(void)
20942cef6288SAlexander V. Chernikov {
20952cef6288SAlexander V. Chernikov 	NL_VERIFY_PARSERS(all_parsers);
2096ffbf2595SKristof Provost 
20972cef6288SAlexander V. Chernikov 	family_id = genl_register_family(PFNL_FAMILY_NAME, 0, 2, PFNL_CMD_MAX);
2098ee507b70SGleb Smirnoff 	genl_register_cmds(family_id, pf_cmds, nitems(pf_cmds));
20992cef6288SAlexander V. Chernikov }
21002cef6288SAlexander V. Chernikov 
21012cef6288SAlexander V. Chernikov void
21022cef6288SAlexander V. Chernikov pf_nl_unregister(void)
21032cef6288SAlexander V. Chernikov {
2104ee507b70SGleb Smirnoff 	genl_unregister_family(family_id);
21052cef6288SAlexander V. Chernikov }
2106