git: 30bad751e8bf - main - pf: convert DIOCGETTIMEOUT/DIOCSETTIMEOUT to netlink

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Thu, 06 Jun 2024 20:40:36 UTC
The branch main has been updated by kp:

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

commit 30bad751e8bf7bea01a1756ec9112490875c4a92
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2024-06-05 03:58:56 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2024-06-06 18:46:18 +0000

    pf: convert DIOCGETTIMEOUT/DIOCSETTIMEOUT to netlink
---
 lib/libpfctl/libpfctl.c   | 84 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/libpfctl/libpfctl.h   |  2 ++
 sbin/pfctl/parse.y        |  4 +--
 sbin/pfctl/pfctl.c        | 19 ++++-------
 sbin/pfctl/pfctl_parser.h |  2 +-
 sys/net/pfvar.h           |  2 ++
 sys/netpfil/pf/pf_ioctl.c | 65 +++++++++++++++++++++++-------------
 sys/netpfil/pf/pf_nl.c    | 76 ++++++++++++++++++++++++++++++++++++++++++
 sys/netpfil/pf/pf_nl.h    |  8 +++++
 9 files changed, 224 insertions(+), 38 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 42339af5642b..a31fe6f0aff4 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -2522,3 +2522,87 @@ pfctl_set_debug(struct pfctl_handle *h, uint32_t level)
 
 	return (e.error);
 }
+
+int
+pfctl_set_timeout(struct pfctl_handle *h, uint32_t timeout, uint32_t seconds)
+{
+	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_TIMEOUT);
+
+	snl_add_msg_attr_u32(&nw, PF_TO_TIMEOUT, timeout);
+	snl_add_msg_attr_u32(&nw, PF_TO_SECONDS, seconds);
+
+	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_timeout {
+	uint32_t seconds;
+};
+#define	_OUT(_field)	offsetof(struct pfctl_nl_timeout, _field)
+static struct snl_attr_parser ap_get_timeout[] = {
+	{ .type = PF_TO_SECONDS, .off = _OUT(seconds), .cb = snl_attr_get_uint32 },
+};
+static struct snl_field_parser fp_get_timeout[] = {};
+#undef _OUT
+SNL_DECLARE_PARSER(get_timeout_parser, struct genlmsghdr, fp_get_timeout, ap_get_timeout);
+
+int
+pfctl_get_timeout(struct pfctl_handle *h, uint32_t timeout, uint32_t *seconds)
+{
+	struct snl_writer nw;
+	struct pfctl_nl_timeout to = {};
+	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_TIMEOUT);
+	hdr->nlmsg_flags |= NLM_F_DUMP;
+
+	snl_add_msg_attr_u32(&nw, PF_TO_TIMEOUT, timeout);
+
+	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_timeout_parser, &to))
+			continue;
+	}
+
+	if (seconds != NULL)
+		*seconds = to.seconds;
+
+	return (e.error);
+}
+
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index ae1457e9304b..6d59d66a924a 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -493,5 +493,7 @@ struct pfctl_natlook {
 int	pfctl_natlook(struct pfctl_handle *h,
 	    const struct pfctl_natlook_key *k, struct pfctl_natlook *r);
 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);
 
 #endif
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 19e029c881d1..2876eb6e89dc 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -5165,7 +5165,7 @@ timeout_spec	: STRING NUMBER
 				yyerror("only positive values permitted");
 				YYERROR;
 			}
-			if (pfctl_set_timeout(pf, $1, $2, 0) != 0) {
+			if (pfctl_apply_timeout(pf, $1, $2, 0) != 0) {
 				yyerror("unknown timeout %s", $1);
 				free($1);
 				YYERROR;
@@ -5179,7 +5179,7 @@ timeout_spec	: STRING NUMBER
 				yyerror("only positive values permitted");
 				YYERROR;
 			}
-			if (pfctl_set_timeout(pf, "interval", $2, 0) != 0)
+			if (pfctl_apply_timeout(pf, "interval", $2, 0) != 0)
 				YYERROR;
 		}
 		;
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 8776ec7f82dc..d97043fc5c66 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1656,17 +1656,15 @@ pfctl_show_running(int dev)
 int
 pfctl_show_timeouts(int dev, int opts)
 {
-	struct pfioc_tm pt;
+	uint32_t seconds;
 	int i;
 
 	if (opts & PF_OPT_SHOWALL)
 		pfctl_print_title("TIMEOUTS:");
-	memset(&pt, 0, sizeof(pt));
 	for (i = 0; pf_timeouts[i].name; i++) {
-		pt.timeout = pf_timeouts[i].timeout;
-		if (ioctl(dev, DIOCGETTIMEOUT, &pt))
+		if (pfctl_get_timeout(pfh, pf_timeouts[i].timeout, &seconds))
 			err(1, "DIOCGETTIMEOUT");
-		printf("%-20s %10d", pf_timeouts[i].name, pt.seconds);
+		printf("%-20s %10d", pf_timeouts[i].name, seconds);
 		if (pf_timeouts[i].timeout >= PFTM_ADAPTIVE_START &&
 		    pf_timeouts[i].timeout <= PFTM_ADAPTIVE_END)
 			printf(" states");
@@ -2469,7 +2467,7 @@ pfctl_load_limit(struct pfctl *pf, unsigned int index, unsigned int limit)
 }
 
 int
-pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
+pfctl_apply_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
 {
 	int i;
 
@@ -2499,12 +2497,7 @@ pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
 int
 pfctl_load_timeout(struct pfctl *pf, unsigned int timeout, unsigned int seconds)
 {
-	struct pfioc_tm pt;
-
-	memset(&pt, 0, sizeof(pt));
-	pt.timeout = timeout;
-	pt.seconds = seconds;
-	if (ioctl(pf->dev, DIOCSETTIMEOUT, &pt)) {
+	if (pfctl_set_timeout(pf->h, timeout, seconds)) {
 		warnx("DIOCSETTIMEOUT");
 		return (1);
 	}
@@ -2553,7 +2546,7 @@ pfctl_set_optimization(struct pfctl *pf, const char *opt)
 	}
 
 	for (i = 0; hint[i].name; i++)
-		if ((r = pfctl_set_timeout(pf, hint[i].name,
+		if ((r = pfctl_apply_timeout(pf, hint[i].name,
 		    hint[i].timeout, 1)))
 			return (r);
 
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index 58532ad37e12..06ab5d052631 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -285,7 +285,7 @@ int	pfctl_add_pool(struct pfctl *, struct pfctl_pool *, sa_family_t);
 void	pfctl_move_pool(struct pfctl_pool *, struct pfctl_pool *);
 void	pfctl_clear_pool(struct pfctl_pool *);
 
-int	pfctl_set_timeout(struct pfctl *, const char *, int, int);
+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);
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 0ea4741f8937..16d3d90f4862 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -2503,6 +2503,8 @@ int			 pf_ioctl_addrule(struct pf_krule *, uint32_t,
 			    uint32_t, const char *, const char *, uid_t uid,
 			    pid_t);
 void			 pf_ioctl_clear_status(void);
+int			 pf_ioctl_get_timeout(int, int *);
+int			 pf_ioctl_set_timeout(int, int, 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 99cb3bd85d57..cef50c00283b 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2443,6 +2443,46 @@ pf_ioctl_clear_status(void)
 	PF_RULES_WUNLOCK();
 }
 
+int
+pf_ioctl_set_timeout(int timeout, int seconds, int *prev_seconds)
+{
+	uint32_t old;
+
+	if (timeout < 0 || timeout >= PFTM_MAX ||
+	    seconds < 0)
+		return (EINVAL);
+
+	PF_RULES_WLOCK();
+	old = V_pf_default_rule.timeout[timeout];
+	if (timeout == PFTM_INTERVAL && seconds == 0)
+		seconds = 1;
+	V_pf_default_rule.timeout[timeout] = seconds;
+	if (timeout == PFTM_INTERVAL && seconds < old)
+		wakeup(pf_purge_thread);
+
+	if (prev_seconds != NULL)
+		*prev_seconds = old;
+
+	PF_RULES_WUNLOCK();
+
+	return (0);
+}
+
+int
+pf_ioctl_get_timeout(int timeout, int *seconds)
+{
+	PF_RULES_RLOCK_TRACKER;
+
+	if (timeout < 0 || timeout >= PFTM_MAX)
+		return (EINVAL);
+
+	PF_RULES_RLOCK();
+	*seconds = V_pf_default_rule.timeout[timeout];
+	PF_RULES_RUNLOCK();
+
+	return (0);
+}
+
 static int
 pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td)
 {
@@ -3838,35 +3878,16 @@ DIOCGETSTATESV2_full:
 
 	case DIOCSETTIMEOUT: {
 		struct pfioc_tm	*pt = (struct pfioc_tm *)addr;
-		int		 old;
 
-		if (pt->timeout < 0 || pt->timeout >= PFTM_MAX ||
-		    pt->seconds < 0) {
-			error = EINVAL;
-			break;
-		}
-		PF_RULES_WLOCK();
-		old = V_pf_default_rule.timeout[pt->timeout];
-		if (pt->timeout == PFTM_INTERVAL && pt->seconds == 0)
-			pt->seconds = 1;
-		V_pf_default_rule.timeout[pt->timeout] = pt->seconds;
-		if (pt->timeout == PFTM_INTERVAL && pt->seconds < old)
-			wakeup(pf_purge_thread);
-		pt->seconds = old;
-		PF_RULES_WUNLOCK();
+		error = pf_ioctl_set_timeout(pt->timeout, pt->seconds,
+		    &pt->seconds);
 		break;
 	}
 
 	case DIOCGETTIMEOUT: {
 		struct pfioc_tm	*pt = (struct pfioc_tm *)addr;
 
-		if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) {
-			error = EINVAL;
-			break;
-		}
-		PF_RULES_RLOCK();
-		pt->seconds = V_pf_default_rule.timeout[pt->timeout];
-		PF_RULES_RUNLOCK();
+		error = pf_ioctl_get_timeout(pt->timeout, &pt->seconds);
 		break;
 	}
 
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
index cf5146c716c6..026f8caab535 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -1349,6 +1349,67 @@ pf_handle_set_debug(struct nlmsghdr *hdr, struct nl_pstate *npt)
 	return (0);
 }
 
+struct pf_nl_set_timeout
+{
+	uint32_t timeout;
+	uint32_t seconds;
+};
+#define	_OUT(_field)	offsetof(struct pf_nl_set_timeout, _field)
+static const struct nlattr_parser nla_p_set_timeout[] = {
+	{ .type = PF_TO_TIMEOUT, .off = _OUT(timeout), .cb = nlattr_get_uint32 },
+	{ .type = PF_TO_SECONDS, .off = _OUT(seconds), .cb = nlattr_get_uint32 },
+};
+static const struct nlfield_parser nlf_p_set_timeout[] = {};
+#undef _OUT
+NL_DECLARE_PARSER(set_timeout_parser, struct genlmsghdr, nlf_p_set_timeout, nla_p_set_timeout);
+
+static int
+pf_handle_set_timeout(struct nlmsghdr *hdr, struct nl_pstate *npt)
+{
+	struct pf_nl_set_timeout attrs = {};
+	int error;
+
+	error = nl_parse_nlmsg(hdr, &set_timeout_parser, npt, &attrs);
+	if (error != 0)
+		return (error);
+
+	return (pf_ioctl_set_timeout(attrs.timeout, attrs.seconds, NULL));
+}
+
+static int
+pf_handle_get_timeout(struct nlmsghdr *hdr, struct nl_pstate *npt)
+{
+	struct pf_nl_set_timeout attrs = {};
+	struct nl_writer *nw = npt->nw;
+	struct genlmsghdr *ghdr_new;
+	int error;
+
+	error = nl_parse_nlmsg(hdr, &set_timeout_parser, npt, &attrs);
+	if (error != 0)
+		return (error);
+
+	error = pf_ioctl_get_timeout(attrs.timeout, &attrs.seconds);
+	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_TIMEOUT;
+	ghdr_new->version = 0;
+	ghdr_new->reserved = 0;
+
+	nlattr_add_u32(nw, PF_TO_SECONDS, attrs.seconds);
+
+	if (!nlmsg_end(nw)) {
+		nlmsg_abort(nw);
+		return (ENOMEM);
+	}
+
+	return (0);
+}
+
 static const struct nlhdr_parser *all_parsers[] = {
 	&state_parser,
 	&addrule_parser,
@@ -1357,6 +1418,7 @@ static const struct nlhdr_parser *all_parsers[] = {
 	&set_statusif_parser,
 	&natlook_parser,
 	&set_debug_parser,
+	&set_timeout_parser,
 };
 
 static int family_id;
@@ -1460,6 +1522,20 @@ static const struct genl_cmd pf_cmds[] = {
 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
 		.cmd_priv = PRIV_NETINET_PF,
 	},
+	{
+		.cmd_num = PFNL_CMD_SET_TIMEOUT,
+		.cmd_name = "SET_TIMEOUT",
+		.cmd_cb = pf_handle_set_timeout,
+		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
+		.cmd_priv = PRIV_NETINET_PF,
+	},
+	{
+		.cmd_num = PFNL_CMD_GET_TIMEOUT,
+		.cmd_name = "GET_TIMEOUT",
+		.cmd_cb = pf_handle_get_timeout,
+		.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 ab199e308a38..5f9d8166ca50 100644
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -50,6 +50,8 @@ enum {
 	PFNL_CMD_CLEAR_STATUS = 12,
 	PFNL_CMD_NATLOOK = 13,
 	PFNL_CMD_SET_DEBUG = 14,
+	PFNL_CMD_SET_TIMEOUT = 15,
+	PFNL_CMD_GET_TIMEOUT = 16,
 	__PFNL_CMD_MAX,
 };
 #define PFNL_CMD_MAX (__PFNL_CMD_MAX -1)
@@ -334,6 +336,12 @@ enum pf_set_debug_types_t {
 	PF_SD_LEVEL		= 1, /* u32 */
 };
 
+enum pf_timeout_types_t {
+	PF_TO_UNSPEC,
+	PF_TO_TIMEOUT		= 1, /* u32 */
+	PF_TO_SECONDS		= 2, /* u32 */
+};
+
 #ifdef _KERNEL
 
 void	pf_nl_register(void);