git: 044eef6ab9cf - main - pf: support basic filters for state listing

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

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

commit 044eef6ab9cf1730d21c5f5dffebb0cd35fb5bbf
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2023-10-16 14:47:22 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-10-23 14:24:52 +0000

    pf: support basic filters for state listing
    
    Allow users(pace) to specify a protocol, interface, address family and/
    or address and mask, allowing the state listing to be pre-filtered in
    the kernel.
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D42280
---
 lib/libpfctl/libpfctl.c | 18 ++++++++++++++++--
 lib/libpfctl/libpfctl.h |  9 +++++++++
 sbin/pfctl/pfctl.c      |  9 +++++----
 sys/netpfil/pf/pf_nl.c  | 43 ++++++++++++++++++++++++++++++++++++++-----
 sys/netpfil/pf/pf_nl.h  |  2 ++
 5 files changed, 70 insertions(+), 11 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 3865dc85aea1..25bb77d9c021 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -1342,7 +1342,7 @@ static const struct snl_hdr_parser *all_parsers[] = {
 };
 
 static int
-pfctl_get_states_nl(struct snl_state *ss, pfctl_get_state_fn f, void *arg)
+pfctl_get_states_nl(struct pfctl_state_filter *filter, struct snl_state *ss, pfctl_get_state_fn f, void *arg)
 {
 	SNL_VERIFY_PARSERS(all_parsers);
 	int family_id = snl_get_genl_family(ss, PFNL_FAMILY_NAME);
@@ -1354,7 +1354,14 @@ pfctl_get_states_nl(struct snl_state *ss, pfctl_get_state_fn f, void *arg)
 	snl_init_writer(ss, &nw);
 	hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GETSTATES);
 	hdr->nlmsg_flags |= NLM_F_DUMP;
+	snl_add_msg_attr_string(&nw, PF_ST_IFNAME, filter->ifname);
+	snl_add_msg_attr_u16(&nw, PF_ST_PROTO, filter->proto);
+	snl_add_msg_attr_u8(&nw, PF_ST_AF, filter->af);
+	snl_add_msg_attr_ip6(&nw, PF_ST_FILTER_ADDR, &filter->addr.v6);
+	snl_add_msg_attr_ip6(&nw, PF_ST_FILTER_MASK, &filter->mask.v6);
+
 	hdr = snl_finalize_msg(&nw);
+
 	uint32_t seq_id = hdr->nlmsg_seq;
 
 	snl_send_message(ss, hdr);
@@ -1379,12 +1386,19 @@ pfctl_get_states_nl(struct snl_state *ss, pfctl_get_state_fn f, void *arg)
 
 int
 pfctl_get_states_iter(pfctl_get_state_fn f, void *arg)
+{
+	struct pfctl_state_filter filter = {};
+	return (pfctl_get_filtered_states_iter(&filter, f, arg));
+}
+
+int
+pfctl_get_filtered_states_iter(struct pfctl_state_filter *filter, pfctl_get_state_fn f, void *arg)
 {
 	struct snl_state ss = {};
 	int error;
 
 	snl_init(&ss, NETLINK_GENERIC);
-	error = pfctl_get_states_nl(&ss, f, arg);
+	error = pfctl_get_states_nl(filter, &ss, f, arg);
 	snl_free(&ss);
 
 	return (error);
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 06cd25e82c08..ad6fde89771c 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -415,8 +415,17 @@ int	pfctl_add_rule(int dev, const struct pfctl_rule *r,
 	    uint32_t pool_ticket);
 int	pfctl_set_keepcounters(int dev, bool keep);
 int	pfctl_get_creatorids(uint32_t *creators, size_t *len);
+
+struct pfctl_state_filter {
+	char			ifname[IFNAMSIZ];
+	uint16_t		proto;
+	sa_family_t		af;
+	struct pf_addr		addr;
+	struct pf_addr		mask;
+};
 typedef int (*pfctl_get_state_fn)(struct pfctl_state *, void *);
 int pfctl_get_states_iter(pfctl_get_state_fn f, void *arg);
+int pfctl_get_filtered_states_iter(struct pfctl_state_filter *filter, pfctl_get_state_fn f, void *arg);
 int	pfctl_get_states(int dev, struct pfctl_states *states);
 void	pfctl_free_states(struct pfctl_states *states);
 int	pfctl_clear_states(int dev, const struct pfctl_kill *kill,
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 56b1d28c6fd6..c3f3d82ff767 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1529,9 +1529,6 @@ pfctl_show_state(struct pfctl_state *s, void *arg)
 {
 	struct pfctl_show_state_arg *a = (struct pfctl_show_state_arg *)arg;
 
-	if (a->iface != NULL && strcmp(s->ifname, a->iface))
-		return (0);
-
 	if (a->dotitle) {
 		pfctl_print_title("STATES:");
 		a->dotitle = 0;
@@ -1545,12 +1542,16 @@ int
 pfctl_show_states(int dev, const char *iface, int opts)
 {
 	struct pfctl_show_state_arg arg;
+	struct pfctl_state_filter filter = {};
+
+	if (iface != NULL)
+		strncpy(filter.ifname, iface, IFNAMSIZ);
 
 	arg.opts = opts;
 	arg.dotitle = opts & PF_OPT_SHOWALL;
 	arg.iface = iface;
 
-	if (pfctl_get_states_iter(pfctl_show_state, &arg))
+	if (pfctl_get_filtered_states_iter(&filter, pfctl_show_state, &arg))
 		return (-1);
 
 	return (0);
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
index d5d294134b1f..53ff5d031ed6 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -52,6 +52,11 @@ struct nl_parsed_state {
 	uint8_t		version;
 	uint32_t	id;
 	uint32_t	creatorid;
+	char		ifname[IFNAMSIZ];
+	uint16_t	proto;
+	sa_family_t	af;
+	struct pf_addr	addr;
+	struct pf_addr	mask;
 };
 
 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
@@ -59,6 +64,11 @@ struct nl_parsed_state {
 static const struct nlattr_parser nla_p_state[] = {
 	{ .type = PF_ST_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
 	{ .type = PF_ST_CREATORID, .off = _OUT(creatorid), .cb = nlattr_get_uint32 },
+	{ .type = PF_ST_IFNAME, .arg = (const void *)IFNAMSIZ, .off = _OUT(ifname), .cb = nlattr_get_chara },
+	{ .type = PF_ST_AF, .off = _OUT(proto), .cb = nlattr_get_uint8 },
+	{ .type = PF_ST_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint16 },
+	{ .type = PF_ST_FILTER_ADDR, .off = _OUT(addr), .cb = nlattr_get_in6_addr },
+	{ .type = PF_ST_FILTER_MASK, .off = _OUT(mask), .cb = nlattr_get_in6_addr },
 };
 static const struct nlfield_parser nlf_p_generic[] = {
 	{ .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 },
@@ -217,11 +227,34 @@ handle_dumpstates(struct nlpcb *nlp, struct nl_parsed_state *attrs,
 
 		PF_HASHROW_LOCK(ih);
 		LIST_FOREACH(s, &ih->states, entry) {
-			if (s->timeout != PFTM_UNLINKED) {
-				error = dump_state(nlp, hdr, s, npt);
-				if (error != 0)
-					break;
-			}
+			sa_family_t af = s->key[PF_SK_WIRE]->af;
+
+			if (s->timeout == PFTM_UNLINKED)
+				continue;
+
+			/* Filter */
+			if (attrs->creatorid != 0 && s->creatorid != attrs->creatorid)
+				continue;
+			if (attrs->ifname[0] != 0 &&
+			    strncmp(attrs->ifname, s->kif->pfik_name, IFNAMSIZ) != 0)
+				continue;
+			if (attrs->proto != 0 && s->key[PF_SK_WIRE]->proto != attrs->proto)
+				continue;
+			if (attrs->af != 0 && af != attrs->af)
+				continue;
+			if (pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[0],
+			    &attrs->mask, &attrs->addr, af) &&
+			    pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[1],
+			    &attrs->mask, &attrs->addr, af) &&
+			    pf_match_addr(1, &s->key[PF_SK_STACK]->addr[0],
+			    &attrs->mask, &attrs->addr, af) &&
+			    pf_match_addr(1, &s->key[PF_SK_STACK]->addr[1],
+			    &attrs->mask, &attrs->addr, af))
+				continue;
+
+			error = dump_state(nlp, hdr, s, npt);
+			if (error != 0)
+				break;
 		}
 		PF_HASHROW_UNLOCK(ih);
 	}
diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h
index d936044c0d7d..8265ae1d1bfa 100644
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -97,6 +97,8 @@ enum pfstate_type_t {
 	PF_ST_SYNC_FLAGS	= 26, /* u8 */
 	PF_ST_UPDATES		= 27, /* u8 */
 	PF_ST_VERSION		= 28, /* u64 */
+	PF_ST_FILTER_ADDR	= 29, /* in6_addr */
+	PF_ST_FILTER_MASK	= 30, /* in6_addr */
 };
 
 enum pf_addr_type_t {