git: ffbf25951e7b - main - pf: convert rule addition to netlink

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Mon, 23 Oct 2023 15:10:33 UTC
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=ffbf25951e7b7f867989b621b2c07e9dad9441fb

commit ffbf25951e7b7f867989b621b2c07e9dad9441fb
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2023-10-14 13:10:03 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-10-23 14:24:51 +0000

    pf: convert rule addition to netlink
    
    The nvlist-based version will be removed in FreeBSD 16.
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D42279
---
 lib/libpfctl/libpfctl.c   | 385 ++++++++++++++++++++++++++--------------------
 sys/net/pfvar.h           |   4 +
 sys/netpfil/pf/pf_ioctl.c |  14 +-
 sys/netpfil/pf/pf_nl.c    | 268 +++++++++++++++++++++++++++++++-
 sys/netpfil/pf/pf_nl.h    | 132 ++++++++++++++++
 5 files changed, 626 insertions(+), 177 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 571fabae4359..3865dc85aea1 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -438,40 +438,6 @@ pf_nvrule_addr_to_rule_addr(const nvlist_t *nvl, struct pf_rule_addr *addr)
 	addr->port_op = nvlist_get_number(nvl, "port_op");
 }
 
-static void
-pfctl_nv_add_mape(nvlist_t *nvparent, const char *name,
-    const struct pf_mape_portset *mape)
-{
-	nvlist_t *nvl = nvlist_create(0);
-
-	nvlist_add_number(nvl, "offset", mape->offset);
-	nvlist_add_number(nvl, "psidlen", mape->psidlen);
-	nvlist_add_number(nvl, "psid", mape->psid);
-	nvlist_add_nvlist(nvparent, name, nvl);
-	nvlist_destroy(nvl);
-}
-
-static void
-pfctl_nv_add_pool(nvlist_t *nvparent, const char *name,
-    const struct pfctl_pool *pool)
-{
-	uint64_t ports[2];
-	nvlist_t *nvl = nvlist_create(0);
-
-	nvlist_add_binary(nvl, "key", &pool->key, sizeof(pool->key));
-	pfctl_nv_add_addr(nvl, "counter", &pool->counter);
-	nvlist_add_number(nvl, "tblidx", pool->tblidx);
-
-	ports[0] = pool->proxy_port[0];
-	ports[1] = pool->proxy_port[1];
-	nvlist_add_number_array(nvl, "proxy_port", ports, 2);
-	nvlist_add_number(nvl, "opts", pool->opts);
-	pfctl_nv_add_mape(nvl, "mape", &pool->mape);
-
-	nvlist_add_nvlist(nvparent, name, nvl);
-	nvlist_destroy(nvl);
-}
-
 static void
 pf_nvmape_to_mape(const nvlist_t *nvl, struct pf_mape_portset *mape)
 {
@@ -500,22 +466,6 @@ pf_nvpool_to_pool(const nvlist_t *nvl, struct pfctl_pool *pool)
 		pf_nvmape_to_mape(nvlist_get_nvlist(nvl, "mape"), &pool->mape);
 }
 
-static void
-pfctl_nv_add_uid(nvlist_t *nvparent, const char *name,
-    const struct pf_rule_uid *uid)
-{
-	uint64_t uids[2];
-	nvlist_t *nvl = nvlist_create(0);
-
-	uids[0] = uid->uid[0];
-	uids[1] = uid->uid[1];
-	nvlist_add_number_array(nvl, "uid", uids, 2);
-	nvlist_add_number(nvl, "op", uid->op);
-
-	nvlist_add_nvlist(nvparent, name, nvl);
-	nvlist_destroy(nvl);
-}
-
 static void
 pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid)
 {
@@ -523,19 +473,6 @@ pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid)
 	uid->op = nvlist_get_number(nvl, "op");
 }
 
-static void
-pfctl_nv_add_divert(nvlist_t *nvparent, const char *name,
-    const struct pfctl_rule *r)
-{
-	nvlist_t *nvl = nvlist_create(0);
-
-	pfctl_nv_add_addr(nvl, "addr", &r->divert.addr);
-	nvlist_add_number(nvl, "port", r->divert.port);
-
-	nvlist_add_nvlist(nvparent, name, nvl);
-	nvlist_destroy(nvl);
-}
-
 static void
 pf_nvdivert_to_divert(const nvlist_t *nvl, struct pfctl_rule *rule)
 {
@@ -926,127 +863,235 @@ pfctl_add_eth_rule(int dev, const struct pfctl_eth_rule *r, const char *anchor,
 	return (error);
 }
 
-int
-pfctl_add_rule(int dev, const struct pfctl_rule *r, const char *anchor,
-    const char *anchor_call, uint32_t ticket, uint32_t pool_ticket)
+static void
+snl_add_msg_attr_addr_wrap(struct snl_writer *nw, uint32_t type, const struct pf_addr_wrap *addr)
 {
-	struct pfioc_nv nv;
-	uint64_t timeouts[PFTM_MAX];
-	uint64_t set_prio[2];
-	nvlist_t *nvl, *nvlr;
-	size_t labelcount;
-	int ret;
+	int off;
 
-	nvl = nvlist_create(0);
-	nvlr = nvlist_create(0);
+	off = snl_add_msg_attr_nested(nw, type);
 
-	nvlist_add_number(nvl, "ticket", ticket);
-	nvlist_add_number(nvl, "pool_ticket", pool_ticket);
-	nvlist_add_string(nvl, "anchor", anchor);
-	nvlist_add_string(nvl, "anchor_call", anchor_call);
+	snl_add_msg_attr_ip6(nw, PF_AT_ADDR, &addr->v.a.addr.v6);
+	snl_add_msg_attr_ip6(nw, PF_AT_MASK, &addr->v.a.mask.v6);
 
-	nvlist_add_number(nvlr, "nr", r->nr);
-	pfctl_nv_add_rule_addr(nvlr, "src", &r->src);
-	pfctl_nv_add_rule_addr(nvlr, "dst", &r->dst);
+	if (addr->type == PF_ADDR_DYNIFTL)
+		snl_add_msg_attr_string(nw, PF_AT_IFNAME, addr->v.ifname);
+	if (addr->type == PF_ADDR_TABLE)
+		snl_add_msg_attr_string(nw, PF_AT_TABLENAME, addr->v.tblname);
+	snl_add_msg_attr_u8(nw, PF_AT_TYPE, addr->type);
+	snl_add_msg_attr_u8(nw, PF_AT_IFLAGS, addr->iflags);
 
-	labelcount = 0;
-	while (r->label[labelcount][0] != 0 &&
-	    labelcount < PF_RULE_MAX_LABEL_COUNT) {
-		nvlist_append_string_array(nvlr, "labels",
-		    r->label[labelcount]);
-		labelcount++;
+	snl_end_attr_nested(nw, off);
+}
+
+static void
+snl_add_msg_attr_rule_addr(struct snl_writer *nw, uint32_t type, const struct pf_rule_addr *addr)
+{
+	int off;
+
+	off = snl_add_msg_attr_nested(nw, type);
+
+	snl_add_msg_attr_addr_wrap(nw, PF_RAT_ADDR, &addr->addr);
+	snl_add_msg_attr_u16(nw, PF_RAT_SRC_PORT, addr->port[0]);
+	snl_add_msg_attr_u16(nw, PF_RAT_DST_PORT, addr->port[1]);
+	snl_add_msg_attr_u8(nw, PF_RAT_NEG, addr->neg);
+	snl_add_msg_attr_u8(nw, PF_RAT_OP, addr->port_op);
+
+	snl_end_attr_nested(nw, off);
+}
+
+static void
+snl_add_msg_attr_rule_labels(struct snl_writer *nw, uint32_t type, const char labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE])
+{
+	int off, i = 0;
+
+	off = snl_add_msg_attr_nested(nw, type);
+
+	while (labels[i][0] != 0 &&
+	    i < PF_RULE_MAX_LABEL_COUNT) {
+		snl_add_msg_attr_string(nw, PF_LT_LABEL, labels[i]);
+		i++;
 	}
-	nvlist_add_number(nvlr, "ridentifier", r->ridentifier);
 
-	nvlist_add_string(nvlr, "ifname", r->ifname);
-	nvlist_add_string(nvlr, "qname", r->qname);
-	nvlist_add_string(nvlr, "pqname", r->pqname);
-	nvlist_add_string(nvlr, "tagname", r->tagname);
-	nvlist_add_string(nvlr, "match_tagname", r->match_tagname);
-	nvlist_add_string(nvlr, "overload_tblname", r->overload_tblname);
+	snl_end_attr_nested(nw, off);
+}
+
+static void
+snl_add_msg_attr_mape(struct snl_writer *nw, uint32_t type, const struct pf_mape_portset *me)
+{
+	int off;
+
+	off = snl_add_msg_attr_nested(nw, type);
+
+	snl_add_msg_attr_u8(nw, PF_MET_OFFSET, me->offset);
+	snl_add_msg_attr_u8(nw, PF_MET_PSID_LEN, me->psidlen);
+	snl_add_msg_attr_u16(nw, PF_MET_PSID, me->psid);
+
+	snl_end_attr_nested(nw, off);
+}
+
+static void
+snl_add_msg_attr_rpool(struct snl_writer *nw, uint32_t type, const struct pfctl_pool *pool)
+{
+	int off;
+
+	off = snl_add_msg_attr_nested(nw, type);
 
-	pfctl_nv_add_pool(nvlr, "rpool", &r->rpool);
+	snl_add_msg_attr(nw, PF_PT_KEY, sizeof(pool->key), &pool->key);
+	snl_add_msg_attr_ip6(nw, PF_PT_COUNTER, &pool->counter.v6);
+	snl_add_msg_attr_u32(nw, PF_PT_TBLIDX, pool->tblidx);
+	snl_add_msg_attr_u16(nw, PF_PT_PROXY_SRC_PORT, pool->proxy_port[0]);
+	snl_add_msg_attr_u16(nw, PF_PT_PROXY_DST_PORT, pool->proxy_port[1]);
+	snl_add_msg_attr_u8(nw, PF_PT_OPTS, pool->opts);
+	snl_add_msg_attr_mape(nw, PF_PT_MAPE, &pool->mape);
 
-	nvlist_add_number(nvlr, "os_fingerprint", r->os_fingerprint);
+	snl_end_attr_nested(nw, off);
+}
+
+static void
+snl_add_msg_attr_timeouts(struct snl_writer *nw, uint32_t type, const uint32_t *timeouts)
+{
+	int off;
+
+	off = snl_add_msg_attr_nested(nw, type);
 
-	nvlist_add_number(nvlr, "rtableid", r->rtableid);
 	for (int i = 0; i < PFTM_MAX; i++)
-		timeouts[i] = r->timeout[i];
-	nvlist_add_number_array(nvlr, "timeout", timeouts, PFTM_MAX);
-	nvlist_add_number(nvlr, "max_states", r->max_states);
-	nvlist_add_number(nvlr, "max_src_nodes", r->max_src_nodes);
-	nvlist_add_number(nvlr, "max_src_states", r->max_src_states);
-	nvlist_add_number(nvlr, "max_src_conn", r->max_src_conn);
-	nvlist_add_number(nvlr, "max_src_conn_rate.limit",
-	    r->max_src_conn_rate.limit);
-	nvlist_add_number(nvlr, "max_src_conn_rate.seconds",
-	    r->max_src_conn_rate.seconds);
-	nvlist_add_number(nvlr, "dnpipe", r->dnpipe);
-	nvlist_add_number(nvlr, "dnrpipe", r->dnrpipe);
-	nvlist_add_number(nvlr, "dnflags", r->free_flags);
-	nvlist_add_number(nvlr, "prob", r->prob);
-	nvlist_add_number(nvlr, "cuid", r->cuid);
-	nvlist_add_number(nvlr, "cpid", r->cpid);
-
-	nvlist_add_number(nvlr, "return_icmp", r->return_icmp);
-	nvlist_add_number(nvlr, "return_icmp6", r->return_icmp6);
-
-	nvlist_add_number(nvlr, "max_mss", r->max_mss);
-	nvlist_add_number(nvlr, "scrub_flags", r->scrub_flags);
-
-	pfctl_nv_add_uid(nvlr, "uid", &r->uid);
-	pfctl_nv_add_uid(nvlr, "gid", (const struct pf_rule_uid *)&r->gid);
-
-	nvlist_add_number(nvlr, "rule_flag", r->rule_flag);
-	nvlist_add_number(nvlr, "action", r->action);
-	nvlist_add_number(nvlr, "direction", r->direction);
-	nvlist_add_number(nvlr, "log", r->log);
-	nvlist_add_number(nvlr, "logif", r->logif);
-	nvlist_add_number(nvlr, "quick", r->quick);
-	nvlist_add_number(nvlr, "ifnot", r->ifnot);
-	nvlist_add_number(nvlr, "match_tag_not", r->match_tag_not);
-	nvlist_add_number(nvlr, "natpass", r->natpass);
-
-	nvlist_add_number(nvlr, "keep_state", r->keep_state);
-	nvlist_add_number(nvlr, "af", r->af);
-	nvlist_add_number(nvlr, "proto", r->proto);
-	nvlist_add_number(nvlr, "type", r->type);
-	nvlist_add_number(nvlr, "code", r->code);
-	nvlist_add_number(nvlr, "flags", r->flags);
-	nvlist_add_number(nvlr, "flagset", r->flagset);
-	nvlist_add_number(nvlr, "min_ttl", r->min_ttl);
-	nvlist_add_number(nvlr, "allow_opts", r->allow_opts);
-	nvlist_add_number(nvlr, "rt", r->rt);
-	nvlist_add_number(nvlr, "return_ttl", r->return_ttl);
-	nvlist_add_number(nvlr, "tos", r->tos);
-	nvlist_add_number(nvlr, "set_tos", r->set_tos);
-	nvlist_add_number(nvlr, "anchor_relative", r->anchor_relative);
-	nvlist_add_number(nvlr, "anchor_wildcard", r->anchor_wildcard);
-
-	nvlist_add_number(nvlr, "flush", r->flush);
-
-	nvlist_add_number(nvlr, "prio", r->prio);
-	set_prio[0] = r->set_prio[0];
-	set_prio[1] = r->set_prio[1];
-	nvlist_add_number_array(nvlr, "set_prio", set_prio, 2);
-
-	pfctl_nv_add_divert(nvlr, "divert", r);
-
-	nvlist_add_nvlist(nvl, "rule", nvlr);
-	nvlist_destroy(nvlr);
-
-	/* Now do the call. */
-	nv.data = nvlist_pack(nvl, &nv.len);
-	nv.size = nv.len;
+		snl_add_msg_attr_u32(nw, PF_TT_TIMEOUT, timeouts[i]);
 
-	ret = ioctl(dev, DIOCADDRULENV, &nv);
-	if (ret == -1)
-		ret = errno;
+	snl_end_attr_nested(nw, off);
+}
 
-	free(nv.data);
-	nvlist_destroy(nvl);
+static void
+snl_add_msg_attr_uid(struct snl_writer *nw, uint32_t type, const struct pf_rule_uid *uid)
+{
+	int off;
 
-	return (ret);
+	off = snl_add_msg_attr_nested(nw, type);
+
+	snl_add_msg_attr_u32(nw, PF_RUT_UID_LOW, uid->uid[0]);
+	snl_add_msg_attr_u32(nw, PF_RUT_UID_HIGH, uid->uid[1]);
+	snl_add_msg_attr_u8(nw, PF_RUT_OP, uid->op);
+
+	snl_end_attr_nested(nw, off);
+}
+
+static void
+snl_add_msg_attr_pf_rule(struct snl_writer *nw, uint32_t type, const struct pfctl_rule *r)
+{
+	int off;
+
+	off = snl_add_msg_attr_nested(nw, type);
+
+	snl_add_msg_attr_rule_addr(nw, PF_RT_SRC, &r->src);
+	snl_add_msg_attr_rule_addr(nw, PF_RT_DST, &r->dst);
+	snl_add_msg_attr_rule_labels(nw, PF_RT_LABELS, r->label);
+	snl_add_msg_attr_u32(nw, PF_RT_RIDENTIFIER, r->ridentifier);
+	snl_add_msg_attr_string(nw, PF_RT_IFNAME, r->ifname);
+	snl_add_msg_attr_string(nw, PF_RT_QNAME, r->qname);
+	snl_add_msg_attr_string(nw, PF_RT_PQNAME, r->pqname);
+	snl_add_msg_attr_string(nw, PF_RT_TAGNAME, r->tagname);
+	snl_add_msg_attr_string(nw, PF_RT_MATCH_TAGNAME, r->match_tagname);
+	snl_add_msg_attr_string(nw, PF_RT_OVERLOAD_TBLNAME, r->overload_tblname);
+	snl_add_msg_attr_rpool(nw, PF_RT_RPOOL, &r->rpool);
+	snl_add_msg_attr_u32(nw, PF_RT_OS_FINGERPRINT, r->os_fingerprint);
+	snl_add_msg_attr_u32(nw, PF_RT_RTABLEID, r->rtableid);
+	snl_add_msg_attr_timeouts(nw, PF_RT_TIMEOUT, r->timeout);
+	snl_add_msg_attr_u32(nw, PF_RT_MAX_STATES, r->max_states);
+	snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_NODES, r->max_src_nodes);
+	snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_STATES, r->max_src_states);
+	snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_CONN_RATE_LIMIT, r->max_src_conn_rate.limit);
+	snl_add_msg_attr_u32(nw, PF_RT_MAX_SRC_CONN_RATE_SECS, r->max_src_conn_rate.seconds);
+
+	snl_add_msg_attr_u16(nw, PF_RT_DNPIPE, r->dnpipe);
+	snl_add_msg_attr_u16(nw, PF_RT_DNRPIPE, r->dnrpipe);
+	snl_add_msg_attr_u32(nw, PF_RT_DNFLAGS, r->free_flags);
+
+	snl_add_msg_attr_u32(nw, PF_RT_NR, r->nr);
+	snl_add_msg_attr_u32(nw, PF_RT_PROB, r->prob);
+	snl_add_msg_attr_u32(nw, PF_RT_CUID, r->cuid);
+	snl_add_msg_attr_u32(nw, PF_RT_CPID, r->cpid);
+
+	snl_add_msg_attr_u16(nw, PF_RT_RETURN_ICMP, r->return_icmp);
+	snl_add_msg_attr_u16(nw, PF_RT_RETURN_ICMP6, r->return_icmp6);
+	snl_add_msg_attr_u16(nw, PF_RT_MAX_MSS, r->max_mss);
+	snl_add_msg_attr_u16(nw, PF_RT_SCRUB_FLAGS, r->scrub_flags);
+
+	snl_add_msg_attr_uid(nw, PF_RT_UID, &r->uid);
+	snl_add_msg_attr_uid(nw, PF_RT_GID, (const struct pf_rule_uid *)&r->gid);
+
+	snl_add_msg_attr_u32(nw, PF_RT_RULE_FLAG, r->rule_flag);
+	snl_add_msg_attr_u8(nw, PF_RT_ACTION, r->action);
+	snl_add_msg_attr_u8(nw, PF_RT_DIRECTION, r->direction);
+	snl_add_msg_attr_u8(nw, PF_RT_LOG, r->log);
+	snl_add_msg_attr_u8(nw, PF_RT_LOGIF, r->logif);
+	snl_add_msg_attr_u8(nw, PF_RT_QUICK, r->quick);
+	snl_add_msg_attr_u8(nw, PF_RT_IF_NOT, r->ifnot);
+	snl_add_msg_attr_u8(nw, PF_RT_MATCH_TAG_NOT, r->match_tag_not);
+	snl_add_msg_attr_u8(nw, PF_RT_NATPASS, r->natpass);
+	snl_add_msg_attr_u8(nw, PF_RT_KEEP_STATE, r->keep_state);
+	snl_add_msg_attr_u8(nw, PF_RT_AF, r->af);
+	snl_add_msg_attr_u8(nw, PF_RT_PROTO, r->proto);
+	snl_add_msg_attr_u8(nw, PF_RT_TYPE, r->type);
+	snl_add_msg_attr_u8(nw, PF_RT_CODE, r->code);
+	snl_add_msg_attr_u8(nw, PF_RT_FLAGS, r->flags);
+	snl_add_msg_attr_u8(nw, PF_RT_FLAGSET, r->flagset);
+	snl_add_msg_attr_u8(nw, PF_RT_MIN_TTL, r->min_ttl);
+	snl_add_msg_attr_u8(nw, PF_RT_ALLOW_OPTS, r->allow_opts);
+	snl_add_msg_attr_u8(nw, PF_RT_RT, r->rt);
+	snl_add_msg_attr_u8(nw, PF_RT_RETURN_TTL, r->return_ttl);
+	snl_add_msg_attr_u8(nw, PF_RT_TOS, r->tos);
+	snl_add_msg_attr_u8(nw, PF_RT_SET_TOS, r->set_tos);
+
+	snl_add_msg_attr_u8(nw, PF_RT_ANCHOR_RELATIVE, r->anchor_relative);
+	snl_add_msg_attr_u8(nw, PF_RT_ANCHOR_WILDCARD, r->anchor_wildcard);
+	snl_add_msg_attr_u8(nw, PF_RT_FLUSH, r->flush);
+	snl_add_msg_attr_u8(nw, PF_RT_PRIO, r->prio);
+	snl_add_msg_attr_u8(nw, PF_RT_SET_PRIO, r->set_prio[0]);
+	snl_add_msg_attr_u8(nw, PF_RT_SET_PRIO_REPLY, r->set_prio[1]);
+
+	snl_add_msg_attr_ip6(nw, PF_RT_DIVERT_ADDRESS, &r->divert.addr.v6);
+	snl_add_msg_attr_u16(nw, PF_RT_DIVERT_PORT, r->divert.port);
+
+	snl_end_attr_nested(nw, off);
+}
+
+int
+pfctl_add_rule(int dev __unused, const struct pfctl_rule *r, const char *anchor,
+    const char *anchor_call, uint32_t ticket, uint32_t pool_ticket)
+{
+	struct snl_writer nw;
+	struct snl_state ss = {};
+	struct snl_errmsg_data e = {};
+	struct nlmsghdr *hdr;
+	uint32_t seq_id;
+	int family_id;
+
+	snl_init(&ss, NETLINK_GENERIC);
+	family_id = snl_get_genl_family(&ss, PFNL_FAMILY_NAME);
+
+	snl_init_writer(&ss, &nw);
+	hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_ADDRULE);
+	hdr->nlmsg_flags |= NLM_F_DUMP;
+	snl_add_msg_attr_u32(&nw, PF_ART_TICKET, ticket);
+	snl_add_msg_attr_u32(&nw, PF_ART_POOL_TICKET, pool_ticket);
+	snl_add_msg_attr_string(&nw, PF_ART_ANCHOR, anchor);
+	snl_add_msg_attr_string(&nw, PF_ART_ANCHOR_CALL, anchor_call);
+
+	snl_add_msg_attr_pf_rule(&nw, PF_ART_RULE, r);
+
+	if ((hdr = snl_finalize_msg(&nw)) == NULL)
+		return (ENXIO);
+
+	seq_id = hdr->nlmsg_seq;
+
+	if (! snl_send_message(&ss, hdr)) {
+		printf("Send failed\n");
+		return (ENXIO);
+	}
+
+	while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) {
+	}
+
+	return (e.error);
 }
 
 int
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index ec8f8293945b..b2aa1c450c50 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -2465,6 +2465,10 @@ int			 pf_keth_anchor_nvcopyout(
 struct pf_keth_ruleset	*pf_find_or_create_keth_ruleset(const char *);
 void			 pf_keth_anchor_remove(struct pf_keth_rule *);
 
+int			 pf_ioctl_addrule(struct pf_krule *, uint32_t,
+			    uint32_t, const char *, const char *, uid_t uid,
+			    pid_t);
+
 void			 pf_krule_free(struct pf_krule *);
 #endif
 
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 38c09303a543..2eae03a908ec 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2016,10 +2016,10 @@ pf_rule_to_krule(const struct pf_rule *rule, struct pf_krule *krule)
 	return (0);
 }
 
-static int
+int
 pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
     uint32_t pool_ticket, const char *anchor, const char *anchor_call,
-    struct thread *td)
+    uid_t uid, pid_t pid)
 {
 	struct pf_kruleset	*ruleset;
 	struct pf_krule		*tail;
@@ -2045,8 +2045,8 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
 	rule->states_cur = counter_u64_alloc(M_WAITOK);
 	rule->states_tot = counter_u64_alloc(M_WAITOK);
 	rule->src_nodes = counter_u64_alloc(M_WAITOK);
-	rule->cuid = td->td_ucred->cr_ruid;
-	rule->cpid = td->td_proc ? td->td_proc->p_pid : 0;
+	rule->cuid = uid;
+	rule->cpid = pid;
 	TAILQ_INIT(&rule->rpool.list);
 
 	PF_CONFIG_LOCK();
@@ -3076,7 +3076,8 @@ DIOCGETETHRULESET_error:
 
 		/* Frees rule on error */
 		error = pf_ioctl_addrule(rule, ticket, pool_ticket, anchor,
-		    anchor_call, td);
+		    anchor_call, td->td_ucred->cr_ruid,
+		    td->td_proc ? td->td_proc->p_pid : 0);
 
 		nvlist_destroy(nvl);
 		free(nvlpacked, M_NVLIST);
@@ -3104,7 +3105,8 @@ DIOCADDRULENV_error:
 
 		/* Frees rule on error */
 		error = pf_ioctl_addrule(rule, pr->ticket, pr->pool_ticket,
-		    pr->anchor, pr->anchor_call, td);
+		    pr->anchor, pr->anchor_call, td->td_ucred->cr_ruid,
+		    td->td_proc ? td->td_proc->p_pid : 0);
 		break;
 	}
 
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
index e079edcc166d..d5d294134b1f 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -348,7 +348,265 @@ pf_handle_stop(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused)
 	return (pf_stop());
 }
 
-static const struct nlhdr_parser *all_parsers[] = { &state_parser };
+#define _OUT(_field)	offsetof(struct pf_addr_wrap, _field)
+static const struct nlattr_parser nla_p_addr_wrap[] = {
+	{ .type = PF_AT_ADDR, .off = _OUT(v.a.addr), .cb = nlattr_get_in6_addr },
+	{ .type = PF_AT_MASK, .off = _OUT(v.a.mask), .cb = nlattr_get_in6_addr },
+	{ .type = PF_AT_IFNAME, .off = _OUT(v.ifname), .arg = (void *)IFNAMSIZ,.cb = nlattr_get_chara },
+	{ .type = PF_AT_TABLENAME, .off = _OUT(v.tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
+	{ .type = PF_AT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
+	{ .type = PF_AT_IFLAGS, .off = _OUT(iflags), .cb = nlattr_get_uint8 },
+};
+NL_DECLARE_ATTR_PARSER(addr_wrap_parser, nla_p_addr_wrap);
+#undef _OUT
+
+#define _OUT(_field)	offsetof(struct pf_rule_addr, _field)
+static const struct nlattr_parser nla_p_ruleaddr[] = {
+	{ .type = PF_RAT_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = nlattr_get_nested },
+	{ .type = PF_RAT_SRC_PORT, .off = _OUT(port[0]), .cb = nlattr_get_uint16 },
+	{ .type = PF_RAT_DST_PORT, .off = _OUT(port[1]), .cb = nlattr_get_uint16 },
+	{ .type = PF_RAT_NEG, .off = _OUT(neg), .cb = nlattr_get_uint8 },
+	{ .type = PF_RAT_OP, .off = _OUT(port_op), .cb = nlattr_get_uint8 },
+};
+NL_DECLARE_ATTR_PARSER(rule_addr_parser, nla_p_ruleaddr);
+#undef _OUT
+
+#define _OUT(_field)	offsetof(struct pf_mape_portset, _field)
+static const struct nlattr_parser nla_p_mape_portset[] = {
+	{ .type = PF_MET_OFFSET, .off = _OUT(offset), .cb = nlattr_get_uint8 },
+	{ .type = PF_MET_PSID_LEN, .off = _OUT(psidlen), .cb = nlattr_get_uint8 },
+	{. type = PF_MET_PSID, .off = _OUT(psid), .cb = nlattr_get_uint16 },
+};
+NL_DECLARE_ATTR_PARSER(mape_portset_parser, nla_p_mape_portset);
+#undef _OUT
+
+struct nl_parsed_labels
+{
+	char		labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
+	uint32_t	i;
+};
+
+static int
+nlattr_get_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt,
+    const void *arg, void *target)
+{
+	struct nl_parsed_labels *l = (struct nl_parsed_labels *)target;
+	int ret;
+
+	if (l->i >= PF_RULE_MAX_LABEL_COUNT)
+		return (E2BIG);
+
+	ret = nlattr_get_chara(nla, npt, (void *)PF_RULE_LABEL_SIZE,
+	    l->labels[l->i]);
+	if (ret == 0)
+		l->i++;
+
+	return (ret);
+}
+
+#define _OUT(_field)	offsetof(struct nl_parsed_labels, _field)
+static const struct nlattr_parser nla_p_labels[] = {
+	{ .type = PF_LT_LABEL, .off = 0, .cb = nlattr_get_pf_rule_labels },
+};
+NL_DECLARE_ATTR_PARSER(rule_labels_parser, nla_p_labels);
+#undef _OUT
+
+static int
+nlattr_get_nested_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
+{
+	struct nl_parsed_labels parsed_labels = { };
+	int error;
+
+	/* Assumes target points to the beginning of the structure */
+	error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &rule_labels_parser, npt, &parsed_labels);
+	if (error != 0)
+		return (error);
+
+	memcpy(target, parsed_labels.labels, sizeof(parsed_labels));
+
+	return (0);
+}
+
+#define _OUT(_field)	offsetof(struct pf_kpool, _field)
+static const struct nlattr_parser nla_p_pool[] = {
+	{ .type = PF_PT_KEY, .off = _OUT(key), .arg = (void *)sizeof(struct pf_poolhashkey), .cb = nlattr_get_bytes },
+	{ .type = PF_PT_COUNTER, .off = _OUT(counter), .cb = nlattr_get_in6_addr },
+	{ .type = PF_PT_TBLIDX, .off = _OUT(tblidx), .cb = nlattr_get_uint32 },
+	{ .type = PF_PT_PROXY_SRC_PORT, .off = _OUT(proxy_port[0]), .cb = nlattr_get_uint16 },
+	{ .type = PF_PT_PROXY_DST_PORT, .off = _OUT(proxy_port[1]), .cb = nlattr_get_uint16 },
+	{ .type = PF_PT_OPTS, .off = _OUT(opts), .cb = nlattr_get_uint8 },
+	{ .type = PF_PT_MAPE, .off = _OUT(mape), .arg = &mape_portset_parser, .cb = nlattr_get_nested },
+};
+NL_DECLARE_ATTR_PARSER(pool_parser, nla_p_pool);
+#undef _OUT
+
+#define _OUT(_field)	offsetof(struct pf_rule_uid, _field)
+static const struct nlattr_parser nla_p_rule_uid[] = {
+	{ .type = PF_RUT_UID_LOW, .off = _OUT(uid[0]), .cb = nlattr_get_uint32 },
+	{ .type = PF_RUT_UID_HIGH, .off = _OUT(uid[1]), .cb = nlattr_get_uint32 },
+	{ .type = PF_RUT_OP, .off = _OUT(op), .cb = nlattr_get_uint8 },
+};
+NL_DECLARE_ATTR_PARSER(rule_uid_parser, nla_p_rule_uid);
+#undef _OUT
+
+struct nl_parsed_timeouts
+{
+	uint32_t	timeouts[PFTM_MAX];
+	uint32_t	i;
+};
+
+static int
+nlattr_get_pf_timeout(struct nlattr *nla, struct nl_pstate *npt,
+    const void *arg, void *target)
+{
+	struct nl_parsed_timeouts *t = (struct nl_parsed_timeouts *)target;
+	int ret;
+
+	if (t->i >= PFTM_MAX)
+		return (E2BIG);
+
+	ret = nlattr_get_uint32(nla, npt, NULL, &t->timeouts[t->i]);
+	if (ret == 0)
+		t->i++;
+
+	return (ret);
+}
+
+#define _OUT(_field)	offsetof(struct nl_parsed_timeout, _field)
+static const struct nlattr_parser nla_p_timeouts[] = {
+	{ .type = PF_TT_TIMEOUT, .off = 0, .cb = nlattr_get_pf_timeout },
+};
+NL_DECLARE_ATTR_PARSER(timeout_parser, nla_p_timeouts);
+#undef _OUT
+
+static int
+nlattr_get_nested_timeouts(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
+{
+	struct nl_parsed_timeouts parsed_timeouts = { };
+	int error;
+
+	/* Assumes target points to the beginning of the structure */
+	error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &timeout_parser, npt, &parsed_timeouts);
+	if (error != 0)
+		return (error);
+
+	memcpy(target, parsed_timeouts.timeouts, sizeof(parsed_timeouts.timeouts));
+
+	return (0);
+}
+
+#define _OUT(_field)	offsetof(struct pf_krule, _field)
+static const struct nlattr_parser nla_p_rule[] = {
+	{ .type = PF_RT_SRC, .off = _OUT(src), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
+	{ .type = PF_RT_DST, .off = _OUT(dst), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
+	{ .type = PF_RT_RIDENTIFIER, .off = _OUT(ridentifier), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_LABELS, .off = _OUT(label), .arg = &rule_labels_parser,.cb = nlattr_get_nested_pf_rule_labels },
+	{ .type = PF_RT_IFNAME, .off = _OUT(ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
+	{ .type = PF_RT_QNAME, .off = _OUT(qname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
+	{ .type = PF_RT_PQNAME, .off = _OUT(pqname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
+	{ .type = PF_RT_TAGNAME, .off = _OUT(tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
+	{ .type = PF_RT_MATCH_TAGNAME, .off = _OUT(match_tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
+	{ .type = PF_RT_OVERLOAD_TBLNAME, .off = _OUT(overload_tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
+	{ .type = PF_RT_RPOOL, .off = _OUT(rpool), .arg = &pool_parser, .cb = nlattr_get_nested },
+	{ .type = PF_RT_OS_FINGERPRINT, .off = _OUT(os_fingerprint), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_RTABLEID, .off = _OUT(rtableid), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_TIMEOUT, .off = _OUT(timeout), .arg = &timeout_parser, .cb = nlattr_get_nested_timeouts },
+	{ .type = PF_RT_MAX_STATES, .off = _OUT(max_states), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_MAX_SRC_NODES, .off = _OUT(max_src_nodes), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_MAX_SRC_STATES, .off = _OUT(max_src_states), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_MAX_SRC_CONN_RATE_LIMIT, .off = _OUT(max_src_conn_rate.limit), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_MAX_SRC_CONN_RATE_SECS, .off = _OUT(max_src_conn_rate.seconds), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_DNPIPE, .off = _OUT(dnpipe), .cb = nlattr_get_uint16 },
+	{ .type = PF_RT_DNRPIPE, .off = _OUT(dnrpipe), .cb = nlattr_get_uint16 },
+	{ .type = PF_RT_DNFLAGS, .off = _OUT(free_flags), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_PROB, .off = _OUT(prob), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_CUID, .off = _OUT(cuid), .cb = nlattr_get_uint32 },
+	{. type = PF_RT_CPID, .off = _OUT(cpid), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_RETURN_ICMP, .off = _OUT(return_icmp), .cb = nlattr_get_uint16 },
+	{ .type = PF_RT_RETURN_ICMP6, .off = _OUT(return_icmp6), .cb = nlattr_get_uint16 },
+	{ .type = PF_RT_MAX_MSS, .off = _OUT(max_mss), .cb = nlattr_get_uint16 },
+	{ .type = PF_RT_SCRUB_FLAGS, .off = _OUT(scrub_flags), .cb = nlattr_get_uint16 },
+	{ .type = PF_RT_UID, .off = _OUT(uid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
+	{ .type = PF_RT_GID, .off = _OUT(gid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
+	{ .type = PF_RT_RULE_FLAG, .off = _OUT(rule_flag), .cb = nlattr_get_uint32 },
+	{ .type = PF_RT_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_LOG, .off = _OUT(log), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_LOGIF, .off = _OUT(logif), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_QUICK, .off = _OUT(quick), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_IF_NOT, .off = _OUT(ifnot), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_MATCH_TAG_NOT, .off = _OUT(match_tag_not), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_NATPASS, .off = _OUT(natpass), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_KEEP_STATE, .off = _OUT(keep_state), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_CODE, .off = _OUT(code), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_FLAGS, .off = _OUT(flags), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_FLAGSET, .off = _OUT(flagset), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_MIN_TTL, .off = _OUT(min_ttl), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_ALLOW_OPTS, .off = _OUT(allow_opts), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_RT, .off = _OUT(rt), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_RETURN_TTL, .off = _OUT(return_ttl), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_TOS, .off = _OUT(tos), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_SET_TOS, .off = _OUT(set_tos), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_ANCHOR_RELATIVE, .off = _OUT(anchor_relative), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_ANCHOR_WILDCARD, .off = _OUT(anchor_wildcard), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_FLUSH, .off = _OUT(flush), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_PRIO, .off = _OUT(prio), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_SET_PRIO, .off = _OUT(set_prio[0]), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_SET_PRIO_REPLY, .off = _OUT(set_prio[1]), .cb = nlattr_get_uint8 },
+	{ .type = PF_RT_DIVERT_ADDRESS, .off = _OUT(divert.addr), .cb = nlattr_get_in6_addr },
+	{ .type = PF_RT_DIVERT_PORT, .off = _OUT(divert.port), .cb = nlattr_get_uint16 },
+};
+NL_DECLARE_ATTR_PARSER(rule_parser, nla_p_rule);
+#undef _OUT
+struct nl_parsed_addrule {
+	struct pf_krule	*rule;
+	uint32_t	 ticket;
+	uint32_t	 pool_ticket;
+	char		*anchor;
+	char		*anchor_call;
+};
+#define	_IN(_field)	offsetof(struct genlmsghdr, _field)
+#define	_OUT(_field)	offsetof(struct nl_parsed_addrule, _field)
+static const struct nlattr_parser nla_p_addrule[] = {
+	{ .type = PF_ART_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
+	{ .type = PF_ART_POOL_TICKET, .off = _OUT(pool_ticket), .cb = nlattr_get_uint32 },
+	{ .type = PF_ART_ANCHOR, .off = _OUT(anchor), .cb = nlattr_get_string },
+	{ .type = PF_ART_ANCHOR_CALL, .off = _OUT(anchor_call), .cb = nlattr_get_string },
+	{ .type = PF_ART_RULE, .off = _OUT(rule), .arg = &rule_parser, .cb = nlattr_get_nested_ptr }
+};
+static const struct nlfield_parser nlf_p_addrule[] = {
+};
+#undef _IN
+#undef _OUT
+NL_DECLARE_PARSER(addrule_parser, struct genlmsghdr, nlf_p_addrule, nla_p_addrule);
+
+static int
+pf_handle_addrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
+{
+	int error;
+	struct nl_parsed_addrule attrs = {};
+
+	attrs.rule = pf_krule_alloc();
+
+	error = nl_parse_nlmsg(hdr, &addrule_parser, npt, &attrs);
+	if (error != 0)
+		return (error);
+
+	error = pf_ioctl_addrule(attrs.rule, attrs.ticket, attrs.pool_ticket,
+	    attrs.anchor, attrs.anchor_call, nlp_get_cred(npt->nlp)->cr_uid,
+	    hdr->nlmsg_pid);
+
+	if (error != 0)
+		pf_krule_free(attrs.rule);
+
+	return (error);
+}
+
+static const struct nlhdr_parser *all_parsers[] = { &state_parser, &addrule_parser };
 
 static int family_id;
 
@@ -377,12 +635,20 @@ static const struct genl_cmd pf_cmds[] = {
 		.cmd_cb = pf_handle_stop,
 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
 	},
+	{
+		.cmd_num = PFNL_CMD_ADDRULE,
+		.cmd_name = "ADDRULE",
+		.cmd_cb = pf_handle_addrule,
+		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
+	},
+
 };
 
 void
 pf_nl_register(void)
 {
 	NL_VERIFY_PARSERS(all_parsers);
+
 	family_id = genl_register_family(PFNL_FAMILY_NAME, 0, 2, PFNL_CMD_MAX);
 	genl_register_cmds(PFNL_FAMILY_NAME, pf_cmds, NL_ARRAY_LEN(pf_cmds));
 }
diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h
index 3c8c6d3b8ed4..d936044c0d7d 100644
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -40,6 +40,7 @@ enum {
 	PFNL_CMD_GETCREATORS = 2,
 	PFNL_CMD_START = 3,
 	PFNL_CMD_STOP = 4,
+	PFNL_CMD_ADDRULE = 5,
 	__PFNL_CMD_MAX,
 };
 #define PFNL_CMD_MAX (__PFNL_CMD_MAX -1)
@@ -98,6 +99,137 @@ enum pfstate_type_t {
 	PF_ST_VERSION		= 28, /* u64 */
 };
 
+enum pf_addr_type_t {
+	PF_AT_UNSPEC,
+	PF_AT_ADDR		= 1, /* in6_addr */
+	PF_AT_MASK		= 2, /* in6_addr */
+	PF_AT_IFNAME		= 3, /* string */
+	PF_AT_TABLENAME		= 4, /* string */
+	PF_AT_TYPE		= 5, /* u8 */
+	PF_AT_IFLAGS		= 6, /* u8 */
+};
+
+enum pfrule_addr_type_t {
+	PF_RAT_UNSPEC,
+	PF_RAT_ADDR		= 1, /* nested, pf_addr_type_t */
+	PF_RAT_SRC_PORT		= 2, /* u16 */
+	PF_RAT_DST_PORT		= 3, /* u16 */
+	PF_RAT_NEG		= 4, /* u8 */
+	PF_RAT_OP		= 5, /* u8 */
+};
+
+enum pf_labels_type_t {
+	PF_LT_UNSPEC,
+	PF_LT_LABEL		= 1, /* string */
+};
+
+enum pf_mape_portset_type_t
+{
+	PF_MET_UNSPEC,
+	PF_MET_OFFSET		= 1, /* u8 */
+	PF_MET_PSID_LEN		= 2, /* u8 */
+	PF_MET_PSID		= 3, /* u16 */
+};
+
+enum pf_rpool_type_t
+{
+	PF_PT_UNSPEC,
+	PF_PT_KEY		= 1, /* bytes, sizeof(struct pf_poolhashkey) */
+	PF_PT_COUNTER		= 2, /* in6_addr */
+	PF_PT_TBLIDX		= 3, /* u32 */
+	PF_PT_PROXY_SRC_PORT	= 4, /* u16 */
+	PF_PT_PROXY_DST_PORT	= 5, /* u16 */
+	PF_PT_OPTS		= 6, /* u8 */
+	PF_PT_MAPE		= 7, /* nested, pf_mape_portset_type_t */
+};
+
+enum pf_timeout_type_t {
+	PF_TT_UNSPEC,
+	PF_TT_TIMEOUT		= 1, /* u32 */
+};
+
+enum pf_rule_uid_type_t {
+	PF_RUT_UNSPEC,
+	PF_RUT_UID_LOW		= 1, /* u32 */
+	PF_RUT_UID_HIGH		= 2, /* u32 */
+	PF_RUT_OP		= 3, /* u8 */
+};
+
+enum pf_rule_type_t {
+	PF_RT_UNSPEC,
+	PF_RT_SRC		= 1, /* nested, pf_rule_addr_type_t */
+	PF_RT_DST		= 2, /* nested, pf_rule_addr_type_t */
+	PF_RT_RIDENTIFIER	= 3, /* u32 */
+	PF_RT_LABELS		= 4, /* nested, pf_labels_type_t */
+	PF_RT_IFNAME		= 5, /* string */
+	PF_RT_QNAME		= 6, /* string */
+	PF_RT_PQNAME		= 7, /* string */
+	PF_RT_TAGNAME		= 8, /* string */
+	PF_RT_MATCH_TAGNAME	= 9, /* string */
+	PF_RT_OVERLOAD_TBLNAME	= 10, /* string */
+	PF_RT_RPOOL		= 11, /* nested, pf_rpool_type_t */
+	PF_RT_OS_FINGERPRINT	= 12, /* u32 */
+	PF_RT_RTABLEID		= 13, /* u32 */
+	PF_RT_TIMEOUT		= 14, /* nested, pf_timeout_type_t */
+	PF_RT_MAX_STATES	= 15, /* u32 */
+	PF_RT_MAX_SRC_NODES	= 16, /* u32 */
+	PF_RT_MAX_SRC_STATES	= 17, /* u32 */
+	PF_RT_MAX_SRC_CONN_RATE_LIMIT	= 18, /* u32 */
+	PF_RT_MAX_SRC_CONN_RATE_SECS	= 19, /* u32 */
+	PF_RT_DNPIPE		= 20, /* u16 */
+	PF_RT_DNRPIPE		= 21, /* u16 */
+	PF_RT_DNFLAGS		= 22, /* u32 */
+	PF_RT_NR		= 23, /* u32 */
+	PF_RT_PROB		= 24, /* u32 */
+	PF_RT_CUID		= 25, /* u32 */
+	PF_RT_CPID		= 26, /* u32 */
+	PF_RT_RETURN_ICMP	= 27, /* u16 */
+	PF_RT_RETURN_ICMP6	= 28, /* u16 */
+	PF_RT_MAX_MSS		= 29, /* u16 */
+	PF_RT_SCRUB_FLAGS	= 30, /* u16 */
+	PF_RT_UID		= 31, /* nested, pf_rule_uid_type_t */
+	PF_RT_GID		= 32, /* nested, pf_rule_uid_type_t */
+	PF_RT_RULE_FLAG		= 33, /* u32 */
+	PF_RT_ACTION		= 34, /* u8 */
+	PF_RT_DIRECTION		= 35, /* u8 */
+	PF_RT_LOG		= 36, /* u8 */
+	PF_RT_LOGIF		= 37, /* u8 */
+	PF_RT_QUICK		= 38, /* u8 */
+	PF_RT_IF_NOT		= 39, /* u8 */
+	PF_RT_MATCH_TAG_NOT	= 40, /* u8 */
+	PF_RT_NATPASS		= 41, /* u8 */
+	PF_RT_KEEP_STATE	= 42, /* u8 */
+	PF_RT_AF		= 43, /* u8 */
+	PF_RT_PROTO		= 44, /* u8 */
+	PF_RT_TYPE		= 45, /* u8 */
+	PF_RT_CODE		= 46, /* u8 */
+	PF_RT_FLAGS		= 47, /* u8 */
+	PF_RT_FLAGSET		= 48, /* u8 */
+	PF_RT_MIN_TTL		= 49, /* u8 */
+	PF_RT_ALLOW_OPTS	= 50, /* u8 */
+	PF_RT_RT		= 51, /* u8 */
+	PF_RT_RETURN_TTL	= 52, /* u8 */
+	PF_RT_TOS		= 53, /* u8 */
+	PF_RT_SET_TOS		= 54, /* u8 */
+	PF_RT_ANCHOR_RELATIVE	= 55, /* u8 */
+	PF_RT_ANCHOR_WILDCARD	= 56, /* u8 */
+	PF_RT_FLUSH		= 57, /* u8 */
+	PF_RT_PRIO		= 58, /* u8 */
+	PF_RT_SET_PRIO		= 59, /* u8 */
+	PF_RT_SET_PRIO_REPLY	= 60, /* u8 */
+	PF_RT_DIVERT_ADDRESS	= 61, /* in6_addr */
+	PF_RT_DIVERT_PORT	= 62, /* u16 */
+};
+
+enum pf_addrule_type_t {
+	PF_ART_UNSPEC,
+	PF_ART_TICKET		= 1, /* u32 */
+	PF_ART_POOL_TICKET	= 2, /* u32 */
+	PF_ART_ANCHOR		= 3, /* string */
+	PF_ART_ANCHOR_CALL	= 4, /* string */
+	PF_ART_RULE		= 5, /* nested, pfrule_type_t */
+};
+
 #ifdef _KERNEL
 
 void	pf_nl_register(void);