git: d9ab89993138 - main - pf: migrate DIOCGETLIMIT/DIOCSETLIMIT to netlink

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Fri, 07 Jun 2024 19:14:37 UTC
The branch main has been updated by kp:

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

commit d9ab8999313845e87c67532437a0441d9cd57e72
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2024-06-07 01:08:50 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2024-06-07 18:59:02 +0000

    pf: migrate DIOCGETLIMIT/DIOCSETLIMIT to netlink
    
    Event:          Kitchener-Waterloo Hackathon 202406
---
 lib/libpfctl/libpfctl.c                   | 88 +++++++++++++++++++++++++++++--
 lib/libpfctl/libpfctl.h                   |  2 +
 sbin/pfctl/parse.y                        |  2 +-
 sbin/pfctl/pfctl.c                        | 19 +++----
 sbin/pfctl/pfctl_parser.h                 |  2 +-
 sys/net/pfvar.h                           |  2 +
 sys/netpfil/pf/pf_ioctl.c                 | 56 +++++++++++++-------
 sys/netpfil/pf/pf_nl.c                    | 76 ++++++++++++++++++++++++++
 sys/netpfil/pf/pf_nl.h                    |  8 +++
 tests/sys/netpfil/pf/Makefile             |  1 +
 tests/sys/netpfil/pf/limits.sh            | 66 +++++++++++++++++++++++
 usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c | 14 +++--
 12 files changed, 291 insertions(+), 45 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index a31fe6f0aff4..ebe52e7a415f 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -2213,7 +2213,7 @@ pfctl_clear_eth_rules(int dev, const char *anchorname)
 }
 
 static int
-pfctl_get_limit(int dev, const int index, uint *limit)
+_pfctl_get_limit(int dev, const int index, uint *limit)
 {
 	struct pfioc_limit pl;
 
@@ -2237,7 +2237,7 @@ pfctl_set_syncookies(int dev, const struct pfctl_syncookies *s)
 	uint		 state_limit;
 	uint64_t	 lim, hi, lo;
 
-	ret = pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit);
+	ret = _pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit);
 	if (ret != 0)
 		return (ret);
 
@@ -2274,7 +2274,7 @@ pfctl_get_syncookies(int dev, struct pfctl_syncookies *s)
 	uint		 state_limit;
 	bool		 enabled, adaptive;
 
-	ret = pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit);
+	ret = _pfctl_get_limit(dev, PF_LIMIT_STATES, &state_limit);
 	if (ret != 0)
 		return (ret);
 
@@ -2606,3 +2606,85 @@ pfctl_get_timeout(struct pfctl_handle *h, uint32_t timeout, uint32_t *seconds)
 	return (e.error);
 }
 
+int
+pfctl_set_limit(struct pfctl_handle *h, const int index, const uint limit)
+{
+	struct snl_writer nw;
+	struct snl_errmsg_data e = {};
+	struct nlmsghdr *hdr;
+	uint32_t seq_id;
+	int family_id;
+
+	family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
+	if (family_id == 0)
+		return (ENOTSUP);
+
+	snl_init_writer(&h->ss, &nw);
+	hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_SET_LIMIT);
+
+	snl_add_msg_attr_u32(&nw, PF_LI_INDEX, index);
+	snl_add_msg_attr_u32(&nw, PF_LI_LIMIT, limit);
+
+	if ((hdr = snl_finalize_msg(&nw)) == NULL)
+		return (ENXIO);
+
+	seq_id = hdr->nlmsg_seq;
+
+	if (! snl_send_message(&h->ss, hdr))
+		return (ENXIO);
+
+	while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
+	}
+
+	return (e.error);
+}
+
+struct pfctl_nl_limit {
+	unsigned int limit;
+};
+#define	_OUT(_field)	offsetof(struct pfctl_nl_limit, _field)
+static struct snl_attr_parser ap_get_limit[] = {
+	{ .type = PF_LI_LIMIT, .off = _OUT(limit), .cb = snl_attr_get_uint32 },
+};
+static struct snl_field_parser fp_get_limit[] = {};
+#undef _OUT
+SNL_DECLARE_PARSER(get_limit_parser, struct genlmsghdr, fp_get_limit, ap_get_limit);
+
+int
+pfctl_get_limit(struct pfctl_handle *h, const int index, uint *limit)
+{
+	struct snl_writer nw;
+	struct pfctl_nl_limit li = {};
+	struct snl_errmsg_data e = {};
+	struct nlmsghdr *hdr;
+	uint32_t seq_id;
+	int family_id;
+
+	family_id = snl_get_genl_family(&h->ss, PFNL_FAMILY_NAME);
+	if (family_id == 0)
+		return (ENOTSUP);
+
+	snl_init_writer(&h->ss, &nw);
+	hdr = snl_create_genl_msg_request(&nw, family_id, PFNL_CMD_GET_LIMIT);
+	hdr->nlmsg_flags |= NLM_F_DUMP;
+
+	snl_add_msg_attr_u32(&nw, PF_LI_INDEX, index);
+
+	if ((hdr = snl_finalize_msg(&nw)) == NULL)
+		return (ENXIO);
+
+	seq_id = hdr->nlmsg_seq;
+
+	if (! snl_send_message(&h->ss, hdr))
+		return (ENXIO);
+
+	while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
+		if (! snl_parse_nlmsg(&h->ss, hdr, &get_limit_parser, &li))
+			continue;
+	}
+
+	if (limit != NULL)
+		*limit = li.limit;
+
+	return (e.error);
+}
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 6d59d66a924a..44c7fa47e88d 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -495,5 +495,7 @@ int	pfctl_natlook(struct pfctl_handle *h,
 int	pfctl_set_debug(struct pfctl_handle *h, uint32_t level);
 int	pfctl_set_timeout(struct pfctl_handle *h, uint32_t timeout, uint32_t seconds);
 int	pfctl_get_timeout(struct pfctl_handle *h, uint32_t timeout, uint32_t *seconds);
+int	pfctl_set_limit(struct pfctl_handle *h, const int index, const uint limit);
+int	pfctl_get_limit(struct pfctl_handle *h, const int index, uint *limit);
 
 #endif
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 2876eb6e89dc..d07a3fdc188e 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -5198,7 +5198,7 @@ limit_spec	: STRING NUMBER
 				yyerror("only positive values permitted");
 				YYERROR;
 			}
-			if (pfctl_set_limit(pf, $1, $2) != 0) {
+			if (pfctl_apply_limit(pf, $1, $2) != 0) {
 				yyerror("unable to set limit %s %u", $1, $2);
 				free($1);
 				YYERROR;
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index d97043fc5c66..ec718f6b0a99 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1679,21 +1679,19 @@ pfctl_show_timeouts(int dev, int opts)
 int
 pfctl_show_limits(int dev, int opts)
 {
-	struct pfioc_limit pl;
+	unsigned int limit;
 	int i;
 
 	if (opts & PF_OPT_SHOWALL)
 		pfctl_print_title("LIMITS:");
-	memset(&pl, 0, sizeof(pl));
 	for (i = 0; pf_limits[i].name; i++) {
-		pl.index = pf_limits[i].index;
-		if (ioctl(dev, DIOCGETLIMIT, &pl))
+		if (pfctl_get_limit(pfh, pf_limits[i].index, &limit))
 			err(1, "DIOCGETLIMIT");
 		printf("%-13s ", pf_limits[i].name);
-		if (pl.limit == UINT_MAX)
+		if (limit == UINT_MAX)
 			printf("unlimited\n");
 		else
-			printf("hard limit %8u\n", pl.limit);
+			printf("hard limit %8u\n", limit);
 	}
 	return (0);
 }
@@ -2425,7 +2423,7 @@ pfctl_load_options(struct pfctl *pf)
 }
 
 int
-pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)
+pfctl_apply_limit(struct pfctl *pf, const char *opt, unsigned int limit)
 {
 	int i;
 
@@ -2451,12 +2449,7 @@ pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)
 int
 pfctl_load_limit(struct pfctl *pf, unsigned int index, unsigned int limit)
 {
-	struct pfioc_limit pl;
-
-	memset(&pl, 0, sizeof(pl));
-	pl.index = index;
-	pl.limit = limit;
-	if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) {
+	if (pfctl_set_limit(pf->h, index, limit)) {
 		if (errno == EBUSY)
 			warnx("Current pool size exceeds requested hard limit");
 		else
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index 06ab5d052631..6de998b34e52 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -288,7 +288,7 @@ void	pfctl_clear_pool(struct pfctl_pool *);
 int	pfctl_apply_timeout(struct pfctl *, const char *, int, int);
 int	pfctl_set_reassembly(struct pfctl *, int, int);
 int	pfctl_set_optimization(struct pfctl *, const char *);
-int	pfctl_set_limit(struct pfctl *, const char *, unsigned int);
+int	pfctl_apply_limit(struct pfctl *, const char *, unsigned int);
 int	pfctl_set_logif(struct pfctl *, char *);
 int	pfctl_set_hostid(struct pfctl *, u_int32_t);
 int	pfctl_do_set_debug(struct pfctl *, char *);
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 16d3d90f4862..eb36c0aee4d1 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -2505,6 +2505,8 @@ int			 pf_ioctl_addrule(struct pf_krule *, uint32_t,
 void			 pf_ioctl_clear_status(void);
 int			 pf_ioctl_get_timeout(int, int *);
 int			 pf_ioctl_set_timeout(int, int, int *);
+int			 pf_ioctl_get_limit(int, unsigned int *);
+int			 pf_ioctl_set_limit(int, unsigned int, unsigned int *);
 
 void			 pf_krule_free(struct pf_krule *);
 void			 pf_krule_clear_counters(struct pf_krule *);
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index cef50c00283b..af3f914982ec 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2483,6 +2483,40 @@ pf_ioctl_get_timeout(int timeout, int *seconds)
 	return (0);
 }
 
+int
+pf_ioctl_set_limit(int index, unsigned int limit, unsigned int *old_limit)
+{
+
+	PF_RULES_WLOCK();
+	if (index < 0 || index >= PF_LIMIT_MAX ||
+	    V_pf_limits[index].zone == NULL) {
+		PF_RULES_WUNLOCK();
+		return (EINVAL);
+	}
+	uma_zone_set_max(V_pf_limits[index].zone, limit);
+	if (old_limit != NULL)
+		*old_limit = V_pf_limits[index].limit;
+	V_pf_limits[index].limit = limit;
+	PF_RULES_WUNLOCK();
+
+	return (0);
+}
+
+int
+pf_ioctl_get_limit(int index, unsigned int *limit)
+{
+	PF_RULES_RLOCK_TRACKER;
+
+	if (index < 0 || index >= PF_LIMIT_MAX)
+		return (EINVAL);
+
+	PF_RULES_RLOCK();
+	*limit = V_pf_limits[index].limit;
+	PF_RULES_RUNLOCK();
+
+	return (0);
+}
+
 static int
 pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td)
 {
@@ -3894,32 +3928,16 @@ DIOCGETSTATESV2_full:
 	case DIOCGETLIMIT: {
 		struct pfioc_limit	*pl = (struct pfioc_limit *)addr;
 
-		if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) {
-			error = EINVAL;
-			break;
-		}
-		PF_RULES_RLOCK();
-		pl->limit = V_pf_limits[pl->index].limit;
-		PF_RULES_RUNLOCK();
+		error = pf_ioctl_get_limit(pl->index, &pl->limit);
 		break;
 	}
 
 	case DIOCSETLIMIT: {
 		struct pfioc_limit	*pl = (struct pfioc_limit *)addr;
-		int			 old_limit;
+		unsigned int old_limit;
 
-		PF_RULES_WLOCK();
-		if (pl->index < 0 || pl->index >= PF_LIMIT_MAX ||
-		    V_pf_limits[pl->index].zone == NULL) {
-			PF_RULES_WUNLOCK();
-			error = EINVAL;
-			break;
-		}
-		uma_zone_set_max(V_pf_limits[pl->index].zone, pl->limit);
-		old_limit = V_pf_limits[pl->index].limit;
-		V_pf_limits[pl->index].limit = pl->limit;
+		error = pf_ioctl_set_limit(pl->index, pl->limit, &old_limit);
 		pl->limit = old_limit;
-		PF_RULES_WUNLOCK();
 		break;
 	}
 
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
index 026f8caab535..57b1a4a6b5b3 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -1410,6 +1410,67 @@ pf_handle_get_timeout(struct nlmsghdr *hdr, struct nl_pstate *npt)
 	return (0);
 }
 
+struct pf_nl_set_limit
+{
+	uint32_t index;
+	uint32_t limit;
+};
+#define	_OUT(_field)	offsetof(struct pf_nl_set_limit, _field)
+static const struct nlattr_parser nla_p_set_limit[] = {
+	{ .type = PF_LI_INDEX, .off = _OUT(index), .cb = nlattr_get_uint32 },
+	{ .type = PF_LI_LIMIT, .off = _OUT(limit), .cb = nlattr_get_uint32 },
+};
+static const struct nlfield_parser nlf_p_set_limit[] = {};
+#undef _OUT
+NL_DECLARE_PARSER(set_limit_parser, struct genlmsghdr, nlf_p_set_limit, nla_p_set_limit);
+
+static int
+pf_handle_set_limit(struct nlmsghdr *hdr, struct nl_pstate *npt)
+{
+	struct pf_nl_set_limit attrs = {};
+	int error;
+
+	error = nl_parse_nlmsg(hdr, &set_limit_parser, npt, &attrs);
+	if (error != 0)
+		return (error);
+
+	return (pf_ioctl_set_limit(attrs.index, attrs.limit, NULL));
+}
+
+static int
+pf_handle_get_limit(struct nlmsghdr *hdr, struct nl_pstate *npt)
+{
+	struct pf_nl_set_limit attrs = {};
+	struct nl_writer *nw = npt->nw;
+	struct genlmsghdr *ghdr_new;
+	int error;
+
+	error = nl_parse_nlmsg(hdr, &set_limit_parser, npt, &attrs);
+	if (error != 0)
+		return (error);
+
+	error = pf_ioctl_get_limit(attrs.index, &attrs.limit);
+	if (error != 0)
+		return (error);
+
+	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
+		return (ENOMEM);
+
+	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
+	ghdr_new->cmd = PFNL_CMD_GET_LIMIT;
+	ghdr_new->version = 0;
+	ghdr_new->reserved = 0;
+
+	nlattr_add_u32(nw, PF_LI_LIMIT, attrs.limit);
+
+	if (!nlmsg_end(nw)) {
+		nlmsg_abort(nw);
+		return (ENOMEM);
+	}
+
+	return (0);
+}
+
 static const struct nlhdr_parser *all_parsers[] = {
 	&state_parser,
 	&addrule_parser,
@@ -1419,6 +1480,7 @@ static const struct nlhdr_parser *all_parsers[] = {
 	&natlook_parser,
 	&set_debug_parser,
 	&set_timeout_parser,
+	&set_limit_parser,
 };
 
 static int family_id;
@@ -1536,6 +1598,20 @@ static const struct genl_cmd pf_cmds[] = {
 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
 		.cmd_priv = PRIV_NETINET_PF,
 	},
+	{
+		.cmd_num = PFNL_CMD_SET_LIMIT,
+		.cmd_name = "SET_LIMIT",
+		.cmd_cb = pf_handle_set_limit,
+		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
+		.cmd_priv = PRIV_NETINET_PF,
+	},
+	{
+		.cmd_num = PFNL_CMD_GET_LIMIT,
+		.cmd_name = "GET_LIMIT",
+		.cmd_cb = pf_handle_get_limit,
+		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
+		.cmd_priv = PRIV_NETINET_PF,
+	},
 };
 
 void
diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h
index 5f9d8166ca50..437cc6482dbb 100644
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -52,6 +52,8 @@ enum {
 	PFNL_CMD_SET_DEBUG = 14,
 	PFNL_CMD_SET_TIMEOUT = 15,
 	PFNL_CMD_GET_TIMEOUT = 16,
+	PFNL_CMD_SET_LIMIT = 17,
+	PFNL_CMD_GET_LIMIT = 18,
 	__PFNL_CMD_MAX,
 };
 #define PFNL_CMD_MAX (__PFNL_CMD_MAX -1)
@@ -342,6 +344,12 @@ enum pf_timeout_types_t {
 	PF_TO_SECONDS		= 2, /* u32 */
 };
 
+enum pf_limit_types_t {
+	PF_LI_UNSPEC,
+	PF_LI_INDEX		= 1, /* u32 */
+	PF_LI_LIMIT		= 2, /* u32 */
+};
+
 #ifdef _KERNEL
 
 void	pf_nl_register(void);
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
index 089db2f19766..2840dc92f2fa 100644
--- a/tests/sys/netpfil/pf/Makefile
+++ b/tests/sys/netpfil/pf/Makefile
@@ -17,6 +17,7 @@ ATF_TESTS_SH+=	altq \
 		fragmentation_no_reassembly \
 		get_state \
 		icmp \
+		limits \
 		loginterface \
 		killstate \
 		macro \
diff --git a/tests/sys/netpfil/pf/limits.sh b/tests/sys/netpfil/pf/limits.sh
new file mode 100644
index 000000000000..474684bef660
--- /dev/null
+++ b/tests/sys/netpfil/pf/limits.sh
@@ -0,0 +1,66 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Kristof Provost <kp@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "basic" "cleanup"
+basic_head()
+{
+	atf_set descr 'Test setting and retrieving limits'
+	atf_set require.user root
+}
+
+basic_body()
+{
+	pft_init
+
+	vnet_mkjail alcatraz
+
+	pft_set_rules alcatraz \
+		"set limit states 200" \
+		"set limit frags 100" \
+		"set limit src-nodes 50" \
+		"set limit table-entries 25"
+
+	atf_check -s exit:0 -o match:'states.*200' \
+	    jexec alcatraz pfctl -sm
+	atf_check -s exit:0 -o match:'frags.*100' \
+	    jexec alcatraz pfctl -sm
+	atf_check -s exit:0 -o match:'src-nodes.*50' \
+	    jexec alcatraz pfctl -sm
+	atf_check -s exit:0 -o match:'table-entries.*25' \
+	    jexec alcatraz pfctl -sm
+}
+
+basic_cleanup()
+{
+	pft_cleanup
+}
+
+atf_init_test_cases()
+{
+	atf_add_test_case "basic"
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c b/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c
index f02329901f69..9f7e6d4f31cb 100644
--- a/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c
+++ b/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c
@@ -318,36 +318,34 @@ pf_limits(struct snmp_context __unused *ctx, struct snmp_value *val,
 	u_int sub, u_int __unused vindex, enum snmp_op op)
 {
 	asn_subid_t		which = val->var.subs[sub - 1];
-	struct pfioc_limit	pl;
+	unsigned int		index, limit;
 
 	if (op == SNMP_OP_SET)
 		return (SNMP_ERR_NOT_WRITEABLE);
 
 	if (op == SNMP_OP_GET) {
-		bzero(&pl, sizeof(struct pfioc_limit));
-
 		switch (which) {
 			case LEAF_pfLimitsStates:
-				pl.index = PF_LIMIT_STATES;
+				index = PF_LIMIT_STATES;
 				break;
 			case LEAF_pfLimitsSrcNodes:
-				pl.index = PF_LIMIT_SRC_NODES;
+				index = PF_LIMIT_SRC_NODES;
 				break;
 			case LEAF_pfLimitsFrags:
-				pl.index = PF_LIMIT_FRAGS;
+				index = PF_LIMIT_FRAGS;
 				break;
 
 			default:
 				return (SNMP_ERR_NOSUCHNAME);
 		}
 
-		if (ioctl(pfctl_fd(pfh), DIOCGETLIMIT, &pl)) {
+		if (pfctl_get_limit(pfh, index, &limit)) {
 			syslog(LOG_ERR, "pf_limits(): ioctl(): %s",
 			    strerror(errno));
 			return (SNMP_ERR_GENERR);
 		}
 
-		val->v.uint32 = pl.limit;
+		val->v.uint32 = limit;
 
 		return (SNMP_ERR_NOERROR);
 	}