Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 23 Sep 2025 10:11:59 GMT
From:      Kajetan Staszkiewicz <ks@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 99475087d63b - main - pf: Add pfsync protocol for FreeBSD 15
Message-ID:  <202509231011.58NABxFd099561@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by ks:

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

commit 99475087d63b4602a0213645bc17d82c3946e2fd
Author:     Kajetan Staszkiewicz <ks@FreeBSD.org>
AuthorDate: 2025-08-22 10:44:20 +0000
Commit:     Kajetan Staszkiewicz <ks@FreeBSD.org>
CommitDate: 2025-09-23 10:06:33 +0000

    pf: Add pfsync protocol for FreeBSD 15
    
    A new version of pfsync packet is introduced: 1500. This version solves
    the issues with data alignment introduced in version 1400 and adds syncing
    of information needed to sync states created by rules with af-to (original
    interface, af and proto separate for wire and stack keys), of rt_af
    needed for prefer-ipv6-nexthop, and of tag names.
    
    Reviewed by:    kp
    Sponsored by:   InnoGames GmbH
    Differential Revision:  https://reviews.freebsd.org/D52176
---
 contrib/tcpdump/print-pfsync.c |  14 +-
 share/man/man4/pfsync.4        |   2 +
 sys/net/if_pfsync.h            |   7 +-
 sys/net/pfvar.h                |  62 +++++-
 sys/netpfil/pf/if_pfsync.c     | 219 +++++++++++++++----
 sys/netpfil/pf/pf_ioctl.c      | 204 ++++++++++++++----
 tests/sys/netpfil/pf/pfsync.sh | 466 ++++++++++++++++++++++++++++++++++++++++-
 usr.bin/netstat/if.c           |  12 +-
 8 files changed, 883 insertions(+), 103 deletions(-)

diff --git a/contrib/tcpdump/print-pfsync.c b/contrib/tcpdump/print-pfsync.c
index e4f11930816c..e7885c4a9e00 100644
--- a/contrib/tcpdump/print-pfsync.c
+++ b/contrib/tcpdump/print-pfsync.c
@@ -102,6 +102,7 @@ struct pfsync_actions {
 static void	pfsync_print_clr(netdissect_options *, const void *);
 static void	pfsync_print_state_1301(netdissect_options *, const void *);
 static void	pfsync_print_state_1400(netdissect_options *, const void *);
+static void	pfsync_print_state_1500(netdissect_options *, const void *);
 static void	pfsync_print_ins_ack(netdissect_options *, const void *);
 static void	pfsync_print_upd_c(netdissect_options *, const void *);
 static void	pfsync_print_upd_req(netdissect_options *, const void *);
@@ -131,6 +132,8 @@ struct pfsync_actions actions[] = {
 	{ "eof", 0,					NULL },
 	{ "insert", sizeof(struct pfsync_state_1400),	pfsync_print_state_1400 },
 	{ "update", sizeof(struct pfsync_state_1400),	pfsync_print_state_1400 },
+	{ "insert", sizeof(struct pfsync_state_1500),	pfsync_print_state_1500 },
+	{ "update", sizeof(struct pfsync_state_1500),	pfsync_print_state_1500 },
 };
 
 static void
@@ -228,12 +231,21 @@ pfsync_print_state_1301(netdissect_options *ndo, const void *bp)
 static void
 pfsync_print_state_1400(netdissect_options *ndo, const void *bp)
 {
-	struct pfsync_state_1301 *st = (struct pfsync_state_1301 *)bp;
+	struct pfsync_state_1400 *st = (struct pfsync_state_1400 *)bp;
 
 	fn_print_char(ndo, '\n');
 	print_state(ndo, (union pfsync_state_union *)st, PFSYNC_MSG_VERSION_1400);
 }
 
+static void
+pfsync_print_state_1500(netdissect_options *ndo, const void *bp)
+{
+	struct pfsync_state_1500 *st = (struct pfsync_state_1500 *)bp;
+
+	fn_print_char(ndo, '\n');
+	print_state(ndo, (union pfsync_state_union *)st, PFSYNC_MSG_VERSION_1500);
+}
+
 static void
 pfsync_print_ins_ack(netdissect_options *ndo, const void *bp)
 {
diff --git a/share/man/man4/pfsync.4 b/share/man/man4/pfsync.4
index cc9c350ea875..c12bad74831f 100644
--- a/share/man/man4/pfsync.4
+++ b/share/man/man4/pfsync.4
@@ -162,6 +162,8 @@ FreeBSD releases 13.2 and older.
 Compatibility with FreeBSD 13.1 has been verified.
 .It Cm 1400
 FreeBSD release 14.0.
+.It Cm 1500
+FreeBSD release 15.0.
 .El
 .Sh SYSCTL VARIABLES
 The following variables can be entered at the
diff --git a/sys/net/if_pfsync.h b/sys/net/if_pfsync.h
index e99df0b85ccf..7b3177e1137d 100644
--- a/sys/net/if_pfsync.h
+++ b/sys/net/if_pfsync.h
@@ -62,9 +62,10 @@ enum pfsync_msg_versions {
 	PFSYNC_MSG_VERSION_UNSPECIFIED = 0,
 	PFSYNC_MSG_VERSION_1301 = 1301,
 	PFSYNC_MSG_VERSION_1400 = 1400,
+	PFSYNC_MSG_VERSION_1500 = 1500,
 };
 
-#define PFSYNC_MSG_VERSION_DEFAULT PFSYNC_MSG_VERSION_1400
+#define PFSYNC_MSG_VERSION_DEFAULT PFSYNC_MSG_VERSION_1500
 
 #define	PFSYNC_ACT_CLR		0	/* clear all states */
 #define	PFSYNC_ACT_INS_1301	1	/* insert state */
@@ -81,7 +82,9 @@ enum pfsync_msg_versions {
 #define	PFSYNC_ACT_EOF		12	/* end of frame */
 #define PFSYNC_ACT_INS_1400	13	/* insert state */
 #define PFSYNC_ACT_UPD_1400	14	/* update state */
-#define	PFSYNC_ACT_MAX		15
+#define PFSYNC_ACT_INS_1500	15	/* insert state */
+#define PFSYNC_ACT_UPD_1500	16	/* update state */
+#define	PFSYNC_ACT_MAX		17
 
 /*
  * A pfsync frame is built from a header followed by several sections which
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 8b102f198de8..d7d69615151d 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -452,6 +452,16 @@ VNET_DECLARE(struct rmlock, pf_rules_lock);
 #define	PF_RULES_RASSERT()	rm_assert(&V_pf_rules_lock, RA_RLOCKED)
 #define	PF_RULES_WASSERT()	rm_assert(&V_pf_rules_lock, RA_WLOCKED)
 
+VNET_DECLARE(struct rmlock, pf_tags_lock);
+#define	V_pf_tags_lock		VNET(pf_tags_lock)
+
+#define	PF_TAGS_RLOCK_TRACKER	struct rm_priotracker _pf_tags_tracker
+#define	PF_TAGS_RLOCK()		rm_rlock(&V_pf_tags_lock, &_pf_tags_tracker)
+#define	PF_TAGS_RUNLOCK()	rm_runlock(&V_pf_tags_lock, &_pf_tags_tracker)
+#define	PF_TAGS_WLOCK()		rm_wlock(&V_pf_tags_lock)
+#define	PF_TAGS_WUNLOCK()	rm_wunlock(&V_pf_tags_lock)
+#define	PF_TAGS_WASSERT()	rm_assert(&V_pf_tags_lock, RA_WLOCKED)
+
 extern struct mtx_padalign pf_table_stats_lock;
 #define	PF_TABLE_STATS_LOCK()	mtx_lock(&pf_table_stats_lock)
 #define	PF_TABLE_STATS_UNLOCK()	mtx_unlock(&pf_table_stats_lock)
@@ -1209,11 +1219,11 @@ struct pfsync_state_1301 {
 	u_int8_t	 state_flags;
 	u_int8_t	 timeout;
 	u_int8_t	 sync_flags;
-	u_int8_t	 updates;
+	u_int8_t	 updates;	/* unused */
 } __packed;
 
 struct pfsync_state_1400 {
-	/* The beginning of the struct is compatible with previous versions */
+	/* The beginning of the struct is compatible with pfsync_state_1301 */
 	u_int64_t	 id;
 	char		 ifname[IFNAMSIZ];
 	struct pfsync_state_key	key[2];
@@ -1236,7 +1246,7 @@ struct pfsync_state_1400 {
 	u_int8_t	 __spare;
 	u_int8_t	 timeout;
 	u_int8_t	 sync_flags;
-	u_int8_t	 updates;
+	u_int8_t	 updates;	/* unused */
 	/* The rest is not */
 	u_int16_t	 qid;
 	u_int16_t	 pqid;
@@ -1249,12 +1259,54 @@ struct pfsync_state_1400 {
 	u_int8_t	 set_prio[2];
 	u_int8_t	 rt;
 	char		 rt_ifname[IFNAMSIZ];
+} __packed;
 
+struct pfsync_state_1500 {
+	/* The beginning of the struct is compatible with pfsync_state_1301 */
+	u_int64_t	 id;
+	char		 ifname[IFNAMSIZ];
+	struct pfsync_state_key	key[2];
+	struct pf_state_peer_export src;
+	struct pf_state_peer_export dst;
+	struct pf_addr	 rt_addr;
+	u_int32_t	 rule;
+	u_int32_t	 anchor;
+	u_int32_t	 nat_rule;
+	u_int32_t	 creation;
+	u_int32_t	 expire;
+	u_int32_t	 packets[2][2];
+	u_int32_t	 bytes[2][2];
+	u_int32_t	 creatorid;
+	/* The rest is not, use the opportunity to fix alignment */
+	char		 tagname[PF_TAG_NAME_SIZE];
+	char		 rt_ifname[IFNAMSIZ];
+	char		 orig_ifname[IFNAMSIZ];
+	int32_t		 rtableid;
+	u_int16_t	 state_flags;
+	u_int16_t	 qid;
+	u_int16_t	 pqid;
+	u_int16_t	 dnpipe;
+	u_int16_t	 dnrpipe;
+	u_int16_t	 max_mss;
+	sa_family_t	 wire_af;
+	sa_family_t	 stack_af;
+	sa_family_t	 rt_af;
+	u_int8_t	 wire_proto;
+	u_int8_t	 stack_proto;
+	u_int8_t	 log;
+	u_int8_t	 timeout;
+	u_int8_t	 direction;
+	u_int8_t	 rt;
+	u_int8_t	 min_ttl;
+	u_int8_t	 set_tos;
+	u_int8_t	 set_prio[2];
+	u_int8_t	 spare[3];	/* Improve struct alignment */
 } __packed;
 
 union pfsync_state_union {
 	struct pfsync_state_1301 pfs_1301;
 	struct pfsync_state_1400 pfs_1400;
+	struct pfsync_state_1500 pfs_1500;
 } __packed;
 
 #ifdef _KERNEL
@@ -2462,6 +2514,10 @@ int	pf_translate(struct pf_pdesc *, struct pf_addr *, u_int16_t,
 	    struct pf_addr *, u_int16_t, u_int16_t, int);
 int	pf_translate_af(struct pf_pdesc *);
 bool	pf_init_threshold(struct pf_kthreshold *, uint32_t, uint32_t);
+uint16_t	pf_tagname2tag(const char *);
+#ifdef ALTQ
+uint16_t	pf_qname2qid(const char *, bool);
+#endif /* ALTQ */
 
 void	pfr_initialize(void);
 void	pfr_cleanup(void);
diff --git a/sys/netpfil/pf/if_pfsync.c b/sys/netpfil/pf/if_pfsync.c
index 7b9405ee1f8d..66bc99df2afa 100644
--- a/sys/netpfil/pf/if_pfsync.c
+++ b/sys/netpfil/pf/if_pfsync.c
@@ -153,6 +153,8 @@ static int (*pfsync_acts[])(struct mbuf *, int, int, int, int) = {
 	pfsync_in_eof,			/* PFSYNC_ACT_EOF */
 	pfsync_in_ins,			/* PFSYNC_ACT_INS_1400 */
 	pfsync_in_upd,			/* PFSYNC_ACT_UPD_1400 */
+	pfsync_in_ins,			/* PFSYNC_ACT_INS_1500 */
+	pfsync_in_upd,			/* PFSYNC_ACT_UPD_1500 */
 };
 
 struct pfsync_q {
@@ -165,9 +167,11 @@ struct pfsync_q {
 enum pfsync_q_id {
 	PFSYNC_Q_INS_1301,
 	PFSYNC_Q_INS_1400,
+	PFSYNC_Q_INS_1500,
 	PFSYNC_Q_IACK,
 	PFSYNC_Q_UPD_1301,
 	PFSYNC_Q_UPD_1400,
+	PFSYNC_Q_UPD_1500,
 	PFSYNC_Q_UPD_C,
 	PFSYNC_Q_DEL_C,
 	PFSYNC_Q_COUNT,
@@ -176,6 +180,7 @@ enum pfsync_q_id {
 /* Functions for building messages for given queue */
 static void	pfsync_out_state_1301(struct pf_kstate *, void *);
 static void	pfsync_out_state_1400(struct pf_kstate *, void *);
+static void	pfsync_out_state_1500(struct pf_kstate *, void *);
 static void	pfsync_out_iack(struct pf_kstate *, void *);
 static void	pfsync_out_upd_c(struct pf_kstate *, void *);
 static void	pfsync_out_del_c(struct pf_kstate *, void *);
@@ -184,9 +189,11 @@ static void	pfsync_out_del_c(struct pf_kstate *, void *);
 static struct pfsync_q pfsync_qs[] = {
 	{ pfsync_out_state_1301, sizeof(struct pfsync_state_1301), PFSYNC_ACT_INS_1301 },
 	{ pfsync_out_state_1400, sizeof(struct pfsync_state_1400), PFSYNC_ACT_INS_1400 },
+	{ pfsync_out_state_1500, sizeof(struct pfsync_state_1500), PFSYNC_ACT_INS_1500 },
 	{ pfsync_out_iack,       sizeof(struct pfsync_ins_ack),    PFSYNC_ACT_INS_ACK },
 	{ pfsync_out_state_1301, sizeof(struct pfsync_state_1301), PFSYNC_ACT_UPD_1301 },
 	{ pfsync_out_state_1400, sizeof(struct pfsync_state_1400), PFSYNC_ACT_UPD_1400 },
+	{ pfsync_out_state_1500, sizeof(struct pfsync_state_1500), PFSYNC_ACT_UPD_1500 },
 	{ pfsync_out_upd_c,      sizeof(struct pfsync_upd_c),      PFSYNC_ACT_UPD_C },
 	{ pfsync_out_del_c,      sizeof(struct pfsync_del_c),      PFSYNC_ACT_DEL_C }
 };
@@ -195,9 +202,11 @@ static struct pfsync_q pfsync_qs[] = {
 static u_int8_t pfsync_qid_sstate[] = {
 	PFSYNC_S_INS,   /* PFSYNC_Q_INS_1301 */
 	PFSYNC_S_INS,   /* PFSYNC_Q_INS_1400 */
+	PFSYNC_S_INS,   /* PFSYNC_Q_INS_1500 */
 	PFSYNC_S_IACK,  /* PFSYNC_Q_IACK */
 	PFSYNC_S_UPD,   /* PFSYNC_Q_UPD_1301 */
 	PFSYNC_S_UPD,   /* PFSYNC_Q_UPD_1400 */
+	PFSYNC_S_UPD,   /* PFSYNC_Q_UPD_1500 */
 	PFSYNC_S_UPD_C, /* PFSYNC_Q_UPD_C */
 	PFSYNC_S_DEL_C, /* PFSYNC_Q_DEL_C */
 };
@@ -525,13 +534,15 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 	struct pf_kstate	*st = NULL;
 	struct pf_state_key	*skw = NULL, *sks = NULL;
 	struct pf_krule		*r = NULL;
-	struct pfi_kkif		*kif;
+	struct pfi_kkif		*kif, *orig_kif;
 	struct pfi_kkif		*rt_kif = NULL;
 	struct pf_kpooladdr	*rpool_first;
 	int			 error;
+	int			 n = 0;
 	sa_family_t		 rt_af = 0;
 	uint8_t			 rt = 0;
-	int			 n = 0;
+	sa_family_t		 wire_af, stack_af;
+	u_int8_t		 wire_proto, stack_proto;
 
 	PF_RULES_RASSERT();
 
@@ -542,7 +553,11 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 		return (EINVAL);
 	}
 
-	if ((kif = pfi_kkif_find(sp->pfs_1301.ifname)) == NULL) {
+	/*
+	 * Check interfaces early on. Do it before allocating memory etc.
+	 * Because there is a high chance there will be a lot more such states.
+	 */
+	if ((kif = orig_kif = pfi_kkif_find(sp->pfs_1301.ifname)) == NULL) {
 		if (V_pf_status.debug >= PF_DEBUG_MISC)
 			printf("%s: unknown interface: %s\n", __func__,
 			    sp->pfs_1301.ifname);
@@ -551,6 +566,30 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 		return (0);	/* skip this state */
 	}
 
+	/*
+	 * States created with floating interface policy can be synchronized to
+	 * hosts with different interfaces, because they are bound to V_pfi_all.
+	 * But s->orig_kif still points to a real interface. Don't abort
+	 * importing the state if orig_kif does not exists on the importing host
+	 * but the state is not interface-bound.
+	 */
+	if (msg_version == PFSYNC_MSG_VERSION_1500) {
+		orig_kif = pfi_kkif_find(sp->pfs_1500.orig_ifname);
+		if (orig_kif == NULL) {
+			if (kif == V_pfi_all) {
+				orig_kif = kif;
+			} else {
+				if (V_pf_status.debug >= PF_DEBUG_MISC)
+					printf("%s: unknown original interface:"
+					    " %s\n", __func__,
+					    sp->pfs_1500.orig_ifname);
+				if (flags & PFSYNC_SI_IOCTL)
+					return (EINVAL);
+				return (0);	/* skip this state */
+			}
+		}
+	}
+
 	/*
 	 * If the ruleset checksums match or the state is coming from the ioctl,
 	 * it's safe to associate the state with the rule of that number.
@@ -565,10 +604,6 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 	} else
 		r = &V_pf_default_rule;
 
-	/*
-	 * Check routing interface early on. Do it before allocating memory etc.
-	 * because there is a high chance there will be a lot more such states.
-	 */
 	switch (msg_version) {
 	case PFSYNC_MSG_VERSION_1301:
 		/*
@@ -619,10 +654,12 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 			    "because of different ruleset", __func__);
 			return ((flags & PFSYNC_SI_IOCTL) ? EINVAL : 0);
 		}
+		wire_af = stack_af = sp->pfs_1301.af;
+		wire_proto = stack_proto = sp->pfs_1301.proto;
 	break;
 	case PFSYNC_MSG_VERSION_1400:
 		/*
-		 * On FreeBSD 14 and above we're not taking any chances.
+		 * On FreeBSD 14 we're not taking any chances.
 		 * We use the information synced to us.
 		 */
 		if (sp->pfs_1400.rt) {
@@ -641,6 +678,29 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 			 */
 			rt_af = sp->pfs_1400.af;
 		}
+		wire_af = stack_af = sp->pfs_1400.af;
+		wire_proto = stack_proto = sp->pfs_1400.proto;
+	break;
+	case PFSYNC_MSG_VERSION_1500:
+		/*
+		 * On FreeBSD 15 and above we're not taking any chances.
+		 * We use the information synced to us.
+		 */
+		if (sp->pfs_1500.rt) {
+			rt_kif = pfi_kkif_find(sp->pfs_1500.rt_ifname);
+			if (rt_kif == NULL) {
+				DPFPRINTF(PF_DEBUG_MISC,
+				    "%s: unknown route interface: %s",
+				    __func__, sp->pfs_1500.rt_ifname);
+				return ((flags & PFSYNC_SI_IOCTL) ? EINVAL : 0);
+			}
+			rt = sp->pfs_1500.rt;
+			rt_af = sp->pfs_1500.rt_af;
+		}
+		wire_af = sp->pfs_1500.wire_af;
+		stack_af = sp->pfs_1500.stack_af;
+		wire_proto = sp->pfs_1500.wire_proto;
+		stack_proto = sp->pfs_1500.stack_proto;
 	break;
 	}
 
@@ -667,8 +727,9 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 	ks = &sp->pfs_1301.key[PF_SK_STACK];
 #endif
 
-	if (PF_ANEQ(&kw->addr[0], &ks->addr[0], sp->pfs_1301.af) ||
-	    PF_ANEQ(&kw->addr[1], &ks->addr[1], sp->pfs_1301.af) ||
+	if (wire_af != stack_af ||
+	    PF_ANEQ(&kw->addr[0], &ks->addr[0], wire_af) ||
+	    PF_ANEQ(&kw->addr[1], &ks->addr[1], wire_af) ||
 	    kw->port[0] != ks->port[0] ||
 	    kw->port[1] != ks->port[1]) {
 		sks = uma_zalloc(V_pf_state_key_z, M_NOWAIT);
@@ -687,36 +748,19 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 	skw->addr[1] = kw->addr[1];
 	skw->port[0] = kw->port[0];
 	skw->port[1] = kw->port[1];
-	skw->proto = sp->pfs_1301.proto;
-	skw->af = sp->pfs_1301.af;
+	skw->proto = wire_proto;
+	skw->af = wire_af;
 	if (sks != skw) {
 		sks->addr[0] = ks->addr[0];
 		sks->addr[1] = ks->addr[1];
 		sks->port[0] = ks->port[0];
 		sks->port[1] = ks->port[1];
-		sks->proto = sp->pfs_1301.proto;
-		sks->af = sp->pfs_1301.af;
+		sks->proto = stack_proto;
+		sks->af = stack_af;
 	}
 
 	/* copy to state */
-	bcopy(&sp->pfs_1301.rt_addr, &st->act.rt_addr, sizeof(st->act.rt_addr));
 	st->creation = (time_uptime - ntohl(sp->pfs_1301.creation)) * 1000;
-	st->expire = pf_get_uptime();
-	if (sp->pfs_1301.expire) {
-		uint32_t timeout;
-
-		timeout = r->timeout[sp->pfs_1301.timeout];
-		if (!timeout)
-			timeout = V_pf_default_rule.timeout[sp->pfs_1301.timeout];
-
-		/* sp->expire may have been adaptively scaled by export. */
-		st->expire -= (timeout - ntohl(sp->pfs_1301.expire)) * 1000;
-	}
-
-	st->direction = sp->pfs_1301.direction;
-	st->act.log = sp->pfs_1301.log;
-	st->timeout = sp->pfs_1301.timeout;
-
 	st->act.rt = rt;
 	st->act.rt_kif = rt_kif;
 	st->act.rt_af = rt_af;
@@ -724,6 +768,12 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 	switch (msg_version) {
 		case PFSYNC_MSG_VERSION_1301:
 			st->state_flags = sp->pfs_1301.state_flags;
+			st->direction = sp->pfs_1301.direction;
+			st->act.log = sp->pfs_1301.log;
+			st->timeout = sp->pfs_1301.timeout;
+			if (rt)
+				bcopy(&sp->pfs_1301.rt_addr, &st->act.rt_addr,
+				    sizeof(st->act.rt_addr));
 			/*
 			 * In FreeBSD 13 pfsync lacks many attributes. Copy them
 			 * from the rule if possible. If rule can't be matched
@@ -762,6 +812,9 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 			break;
 		case PFSYNC_MSG_VERSION_1400:
 			st->state_flags = ntohs(sp->pfs_1400.state_flags);
+			st->direction = sp->pfs_1400.direction;
+			st->act.log = sp->pfs_1400.log;
+			st->timeout = sp->pfs_1400.timeout;
 			st->act.qid = ntohs(sp->pfs_1400.qid);
 			st->act.pqid = ntohs(sp->pfs_1400.pqid);
 			st->act.dnpipe = ntohs(sp->pfs_1400.dnpipe);
@@ -772,12 +825,47 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 			st->act.max_mss = ntohs(sp->pfs_1400.max_mss);
 			st->act.set_prio[0] = sp->pfs_1400.set_prio[0];
 			st->act.set_prio[1] = sp->pfs_1400.set_prio[1];
+			if (rt)
+				bcopy(&sp->pfs_1400.rt_addr, &st->act.rt_addr,
+				    sizeof(st->act.rt_addr));
+			break;
+		case PFSYNC_MSG_VERSION_1500:
+			st->state_flags = ntohs(sp->pfs_1500.state_flags);
+			st->direction = sp->pfs_1500.direction;
+			st->act.log = sp->pfs_1500.log;
+			st->timeout = sp->pfs_1500.timeout;
+			st->act.qid = ntohs(sp->pfs_1500.qid);
+			st->act.pqid = ntohs(sp->pfs_1500.pqid);
+			st->act.dnpipe = ntohs(sp->pfs_1500.dnpipe);
+			st->act.dnrpipe = ntohs(sp->pfs_1500.dnrpipe);
+			st->act.rtableid = ntohl(sp->pfs_1500.rtableid);
+			st->act.min_ttl = sp->pfs_1500.min_ttl;
+			st->act.set_tos = sp->pfs_1500.set_tos;
+			st->act.max_mss = ntohs(sp->pfs_1500.max_mss);
+			st->act.set_prio[0] = sp->pfs_1500.set_prio[0];
+			st->act.set_prio[1] = sp->pfs_1500.set_prio[1];
+			if (rt)
+				bcopy(&sp->pfs_1500.rt_addr, &st->act.rt_addr,
+				    sizeof(st->act.rt_addr));
+			if (sp->pfs_1500.tagname[0] != 0)
+				st->tag = pf_tagname2tag(sp->pfs_1500.tagname);
 			break;
 		default:
 			panic("%s: Unsupported pfsync_msg_version %d",
 			    __func__, msg_version);
 	}
 
+	st->expire = pf_get_uptime();
+	if (sp->pfs_1301.expire) {
+		uint32_t timeout;
+		timeout = r->timeout[st->timeout];
+		if (!timeout)
+			timeout = V_pf_default_rule.timeout[st->timeout];
+
+		/* sp->expire may have been adaptively scaled by export. */
+		st->expire -= (timeout - ntohl(sp->pfs_1301.expire)) * 1000;
+	}
+
 	if (! (st->act.rtableid == -1 ||
 	    (st->act.rtableid >= 0 && st->act.rtableid < rt_numfibs)))
 		goto cleanup;
@@ -797,7 +885,7 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 	if (!(flags & PFSYNC_SI_IOCTL))
 		st->state_flags |= PFSTATE_NOSYNC;
 
-	if ((error = pf_state_insert(kif, kif, skw, sks, st)) != 0)
+	if ((error = pf_state_insert(kif, orig_kif, skw, sks, st)) != 0)
 		goto cleanup_state;
 
 	/* XXX when we have nat_rule/anchors, use STATE_INC_COUNTERS */
@@ -1089,23 +1177,29 @@ pfsync_in_ins(struct mbuf *m, int offset, int count, int flags, int action)
 	struct mbuf *mp;
 	union pfsync_state_union *sa, *sp;
 	int i, offp, total_len, msg_version, msg_len;
+	u_int8_t timeout, direction;
+	sa_family_t af;
 
 	switch (action) {
 		case PFSYNC_ACT_INS_1301:
 			msg_len = sizeof(struct pfsync_state_1301);
-			total_len = msg_len * count;
 			msg_version = PFSYNC_MSG_VERSION_1301;
 			break;
 		case PFSYNC_ACT_INS_1400:
 			msg_len = sizeof(struct pfsync_state_1400);
-			total_len = msg_len * count;
 			msg_version = PFSYNC_MSG_VERSION_1400;
 			break;
+		case PFSYNC_ACT_INS_1500:
+			msg_len = sizeof(struct pfsync_state_1500);
+			msg_version = PFSYNC_MSG_VERSION_1500;
+			break;
 		default:
 			V_pfsyncstats.pfsyncs_badver++;
 			return (-1);
 	}
 
+	total_len = msg_len * count;
+
 	mp = m_pulldown(m, offset, total_len, &offp);
 	if (mp == NULL) {
 		V_pfsyncstats.pfsyncs_badlen++;
@@ -1116,13 +1210,26 @@ pfsync_in_ins(struct mbuf *m, int offset, int count, int flags, int action)
 	for (i = 0; i < count; i++) {
 		sp = (union pfsync_state_union *)((char *)sa + msg_len * i);
 
+		switch (msg_version) {
+			case PFSYNC_MSG_VERSION_1301:
+			case PFSYNC_MSG_VERSION_1400:
+				af = sp->pfs_1301.af;
+				timeout = sp->pfs_1301.timeout;
+				direction = sp->pfs_1301.direction;
+			break;
+			case PFSYNC_MSG_VERSION_1500:
+				af = sp->pfs_1500.wire_af;
+				timeout = sp->pfs_1500.timeout;
+				direction = sp->pfs_1500.direction;
+			break;
+		}
+
 		/* Check for invalid values. */
-		if (sp->pfs_1301.timeout >= PFTM_MAX ||
+		if (timeout >= PFTM_MAX ||
 		    sp->pfs_1301.src.state > PF_TCPS_PROXY_DST ||
 		    sp->pfs_1301.dst.state > PF_TCPS_PROXY_DST ||
-		    sp->pfs_1301.direction > PF_OUT ||
-		    (sp->pfs_1301.af != AF_INET &&
-		    sp->pfs_1301.af != AF_INET6)) {
+		    direction > PF_OUT ||
+		    (af != AF_INET && af != AF_INET6)) {
 			if (V_pf_status.debug >= PF_DEBUG_MISC)
 				printf("%s: invalid value\n", __func__);
 			V_pfsyncstats.pfsyncs_badval++;
@@ -1215,23 +1322,28 @@ pfsync_in_upd(struct mbuf *m, int offset, int count, int flags, int action)
 	struct pf_kstate *st;
 	struct mbuf *mp;
 	int sync, offp, i, total_len, msg_len, msg_version;
+	u_int8_t timeout;
 
 	switch (action) {
 		case PFSYNC_ACT_UPD_1301:
 			msg_len = sizeof(struct pfsync_state_1301);
-			total_len = msg_len * count;
 			msg_version = PFSYNC_MSG_VERSION_1301;
 			break;
 		case PFSYNC_ACT_UPD_1400:
 			msg_len = sizeof(struct pfsync_state_1400);
-			total_len = msg_len * count;
 			msg_version = PFSYNC_MSG_VERSION_1400;
 			break;
+		case PFSYNC_ACT_UPD_1500:
+			msg_len = sizeof(struct pfsync_state_1500);
+			msg_version = PFSYNC_MSG_VERSION_1500;
+			break;
 		default:
 			V_pfsyncstats.pfsyncs_badact++;
 			return (-1);
 	}
 
+	total_len = msg_len * count;
+
 	mp = m_pulldown(m, offset, total_len, &offp);
 	if (mp == NULL) {
 		V_pfsyncstats.pfsyncs_badlen++;
@@ -1242,8 +1354,18 @@ pfsync_in_upd(struct mbuf *m, int offset, int count, int flags, int action)
 	for (i = 0; i < count; i++) {
 		sp = (union pfsync_state_union *)((char *)sa + msg_len * i);
 
+		switch (msg_version) {
+			case PFSYNC_MSG_VERSION_1301:
+			case PFSYNC_MSG_VERSION_1400:
+				timeout = sp->pfs_1301.timeout;
+			break;
+			case PFSYNC_MSG_VERSION_1500:
+				timeout = sp->pfs_1500.timeout;
+			break;
+		}
+
 		/* check for invalid values */
-		if (sp->pfs_1301.timeout >= PFTM_MAX ||
+		if (timeout >= PFTM_MAX ||
 		    sp->pfs_1301.src.state > PF_TCPS_PROXY_DST ||
 		    sp->pfs_1301.dst.state > PF_TCPS_PROXY_DST) {
 			if (V_pf_status.debug >= PF_DEBUG_MISC) {
@@ -1288,7 +1410,7 @@ pfsync_in_upd(struct mbuf *m, int offset, int count, int flags, int action)
 			pfsync_alloc_scrub_memory(&sp->pfs_1301.dst, &st->dst);
 			pf_state_peer_ntoh(&sp->pfs_1301.dst, &st->dst);
 			st->expire = pf_get_uptime();
-			st->timeout = sp->pfs_1301.timeout;
+			st->timeout = timeout;
 		}
 		st->pfsync_time = time_uptime;
 
@@ -1788,6 +1910,14 @@ pfsync_out_state_1400(struct pf_kstate *st, void *buf)
 	pfsync_state_export(sp, st, PFSYNC_MSG_VERSION_1400);
 }
 
+static void
+pfsync_out_state_1500(struct pf_kstate *st, void *buf)
+{
+	union pfsync_state_union *sp = buf;
+
+	pfsync_state_export(sp, st, PFSYNC_MSG_VERSION_1500);
+}
+
 static void
 pfsync_out_iack(struct pf_kstate *st, void *buf)
 {
@@ -2455,6 +2585,8 @@ pfsync_sstate_to_qid(u_int8_t sync_state)
 					return PFSYNC_Q_INS_1301;
 				case PFSYNC_MSG_VERSION_1400:
 					return PFSYNC_Q_INS_1400;
+				case PFSYNC_MSG_VERSION_1500:
+					return PFSYNC_Q_INS_1500;
 			}
 			break;
 		case PFSYNC_S_IACK:
@@ -2465,6 +2597,8 @@ pfsync_sstate_to_qid(u_int8_t sync_state)
 					return PFSYNC_Q_UPD_1301;
 				case PFSYNC_MSG_VERSION_1400:
 					return PFSYNC_Q_UPD_1400;
+				case PFSYNC_MSG_VERSION_1500:
+					return PFSYNC_Q_UPD_1500;
 			}
 			break;
 		case PFSYNC_S_UPD_C:
@@ -3021,6 +3155,7 @@ pfsync_kstatus_to_softc(struct pfsync_kstatus *status, struct pfsync_softc *sc)
 			break;
 		case PFSYNC_MSG_VERSION_1301:
 		case PFSYNC_MSG_VERSION_1400:
+		case PFSYNC_MSG_VERSION_1500:
 			sc->sc_version = status->version;
 			break;
 		default:
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index bd506c092da2..d58af6e5ec4d 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -116,7 +116,6 @@ static int		 pf_rollback_altq(u_int32_t);
 static int		 pf_commit_altq(u_int32_t);
 static int		 pf_enable_altq(struct pf_altq *);
 static int		 pf_disable_altq(struct pf_altq *);
-static uint16_t		 pf_qname2qid(const char *);
 static void		 pf_qid_unref(uint16_t);
 #endif /* ALTQ */
 static int		 pf_begin_rules(u_int32_t *, int, const char *);
@@ -214,8 +213,7 @@ static void		 pf_init_tagset(struct pf_tagset *, unsigned int *,
 static void		 pf_cleanup_tagset(struct pf_tagset *);
 static uint16_t		 tagname2hashindex(const struct pf_tagset *, const char *);
 static uint16_t		 tag2hashindex(const struct pf_tagset *, uint16_t);
-static u_int16_t	 tagname2tag(struct pf_tagset *, const char *);
-static u_int16_t	 pf_tagname2tag(const char *);
+static u_int16_t	 tagname2tag(struct pf_tagset *, const char *, bool);
 static void		 tag_unref(struct pf_tagset *, u_int16_t);
 
 struct cdev *pf_dev;
@@ -286,6 +284,7 @@ int pf_end_threads;
 struct proc *pf_purge_proc;
 
 VNET_DEFINE(struct rmlock, pf_rules_lock);
+VNET_DEFINE(struct rmlock, pf_tags_lock);
 VNET_DEFINE_STATIC(struct sx, pf_ioctl_lock);
 #define	V_pf_ioctl_lock		VNET(pf_ioctl_lock)
 struct sx			pf_end_lock;
@@ -687,19 +686,50 @@ tag2hashindex(const struct pf_tagset *ts, uint16_t tag)
 }
 
 static u_int16_t
-tagname2tag(struct pf_tagset *ts, const char *tagname)
+tagname2tag(struct pf_tagset *ts, const char *tagname, bool add_new)
 {
 	struct pf_tagname	*tag;
 	u_int32_t		 index;
 	u_int16_t		 new_tagid;
 
-	PF_RULES_WASSERT();
+	PF_TAGS_RLOCK_TRACKER;
+
+	PF_TAGS_RLOCK();
 
 	index = tagname2hashindex(ts, tagname);
 	TAILQ_FOREACH(tag, &ts->namehash[index], namehash_entries)
 		if (strcmp(tagname, tag->name) == 0) {
 			tag->ref++;
-			return (tag->tag);
+			new_tagid = tag->tag;
+			PF_TAGS_RUNLOCK();
+			return (new_tagid);
+		}
+
+	/*
+	 * When used for pfsync with queues we must not create new entries.
+	 * Pf tags can be created just fine by this function, but queues
+	 * require additional configuration. If they are missing on the target
+	 * system we just ignore them
+	 */
+	if (add_new == false) {
+		printf("%s: Not creating a new tag\n", __func__);
+		PF_TAGS_RUNLOCK();
+		return (0);
+	}
+
+	/*
+	 * If a new entry must be created do it under a write lock.
+	 * But first search again, somebody could have created the tag
+	 * between unlocking the read lock and locking the write lock.
+	 */
+	PF_TAGS_RUNLOCK();
+	PF_TAGS_WLOCK();
+	TAILQ_FOREACH(tag, &ts->namehash[index], namehash_entries)
+		if (strcmp(tagname, tag->name) == 0) {
+			tag->ref++;
+			new_tagid = tag->tag;
+			PF_TAGS_WUNLOCK();
+			return (new_tagid);
 		}
 
 	/*
@@ -716,16 +746,20 @@ tagname2tag(struct pf_tagset *ts, const char *tagname)
 	 * to rounding of the number of bits in the vector up to a multiple
 	 * of the vector word size at declaration/allocation time.
 	 */
-	if ((new_tagid == 0) || (new_tagid > TAGID_MAX))
+	if ((new_tagid == 0) || (new_tagid > TAGID_MAX)) {
+		PF_TAGS_WUNLOCK();
 		return (0);
+	}
 
 	/* Mark the tag as in use.  Bits are 0-based for BIT_CLR() */
 	BIT_CLR(TAGID_MAX, new_tagid - 1, &ts->avail);
 
 	/* allocate and fill new struct pf_tagname */
 	tag = uma_zalloc(V_pf_tag_z, M_NOWAIT);
-	if (tag == NULL)
+	if (tag == NULL) {
+		PF_TAGS_WUNLOCK();
 		return (0);
+	}
 	strlcpy(tag->name, tagname, sizeof(tag->name));
 	tag->tag = new_tagid;
 	tag->ref = 1;
@@ -737,7 +771,29 @@ tagname2tag(struct pf_tagset *ts, const char *tagname)
 	index = tag2hashindex(ts, new_tagid);
 	TAILQ_INSERT_TAIL(&ts->taghash[index], tag, taghash_entries);
 
-	return (tag->tag);
+	PF_TAGS_WUNLOCK();
+	return (new_tagid);
+}
+
+static char *
+tag2tagname(struct pf_tagset *ts, u_int16_t tag)
+{
+	struct pf_tagname	*t;
+	uint16_t		 index;
+
+	PF_TAGS_RLOCK_TRACKER;
+
+	PF_TAGS_RLOCK();
+
+	index = tag2hashindex(ts, tag);
+	TAILQ_FOREACH(t, &ts->taghash[index], taghash_entries)
+		if (tag == t->tag) {
+			PF_TAGS_RUNLOCK();
+			return (t->name);
+		}
+
+	PF_TAGS_RUNLOCK();
+	return (NULL);
 }
 
 static void
@@ -746,7 +802,7 @@ tag_unref(struct pf_tagset *ts, u_int16_t tag)
 	struct pf_tagname	*t;
 	uint16_t		 index;
 
-	PF_RULES_WASSERT();
+	PF_TAGS_WLOCK();
 
 	index = tag2hashindex(ts, tag);
 	TAILQ_FOREACH(t, &ts->taghash[index], taghash_entries)
@@ -763,12 +819,20 @@ tag_unref(struct pf_tagset *ts, u_int16_t tag)
 			}
 			break;
 		}
+
+	PF_TAGS_WUNLOCK();
 }
 
-static uint16_t
+uint16_t
 pf_tagname2tag(const char *tagname)
 {
-	return (tagname2tag(&V_pf_tags, tagname));
+	return (tagname2tag(&V_pf_tags, tagname, true));
+}
+
+static const char *
+pf_tag2tagname(uint16_t tag)
+{
+	return (tag2tagname(&V_pf_tags, tag));
 }
 
 static int
@@ -899,10 +963,16 @@ pf_commit_eth(uint32_t ticket, const char *anchor)
 }
 
 #ifdef ALTQ
-static uint16_t
-pf_qname2qid(const char *qname)
+uint16_t
+pf_qname2qid(const char *qname, bool add_new)
+{
+	return (tagname2tag(&V_pf_qids, qname, add_new));
+}
+
+static const char *
+pf_qid2qname(uint16_t qid)
 {
-	return (tagname2tag(&V_pf_qids, qname));
+	return (tag2tagname(&V_pf_qids, qid));
 }
 
 static void
@@ -1151,7 +1221,7 @@ pf_altq_ifnet_event(struct ifnet *ifp, int remove)
 		}
 		bcopy(a1, a2, sizeof(struct pf_altq));
 
-		if ((a2->qid = pf_qname2qid(a2->qname)) == 0) {
+		if ((a2->qid = pf_qname2qid(a2->qname, true)) == 0) {
 			error = EBUSY;
 			free(a2, M_PFALTQ);
 			break;
@@ -1606,7 +1676,7 @@ pf_export_kaltq(struct pf_altq *q, struct pfioc_altq_v1 *pa, size_t ioc_size)
 #define ASSIGN_OPT(x) exported_q->pq_u.hfsc_opts.x = q->pq_u.hfsc_opts.x
 #define ASSIGN_OPT_SATU32(x) exported_q->pq_u.hfsc_opts.x = \
 			    SATU32(q->pq_u.hfsc_opts.x)
-			
+
 			ASSIGN_OPT_SATU32(rtsc_m1);
 			ASSIGN_OPT(rtsc_d);
 			ASSIGN_OPT_SATU32(rtsc_m2);
@@ -1620,7 +1690,7 @@ pf_export_kaltq(struct pf_altq *q, struct pfioc_altq_v1 *pa, size_t ioc_size)
 			ASSIGN_OPT_SATU32(ulsc_m2);
 
 			ASSIGN_OPT(flags);
-			
+
 #undef ASSIGN_OPT
 #undef ASSIGN_OPT_SATU32
 		} else
@@ -1728,7 +1798,7 @@ pf_import_kaltq(struct pfioc_altq_v1 *pa, struct pf_altq *q, size_t ioc_size)
 			ASSIGN_OPT(ulsc_m2);
 
 			ASSIGN_OPT(flags);
-			
+
 #undef ASSIGN_OPT
 		} else
 			COPY(pq_u);
@@ -1760,7 +1830,7 @@ pf_import_kaltq(struct pfioc_altq_v1 *pa, struct pf_altq *q, size_t ioc_size)
 		ASSIGN(qid);
 		break;
 	}
-	default:	
+	default:
 		panic("%s: unhandled struct pfioc_altq version", __func__);
 		break;
 	}
@@ -2191,11 +2261,11 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
 #ifdef ALTQ
 	/* set queue IDs */
 	if (rule->qname[0] != 0) {
-		if ((rule->qid = pf_qname2qid(rule->qname)) == 0)
+		if ((rule->qid = pf_qname2qid(rule->qname, true)) == 0)
 			ERROUT(EBUSY);
 		else if (rule->pqname[0] != 0) {
 			if ((rule->pqid =
-			    pf_qname2qid(rule->pqname)) == 0)
+			    pf_qname2qid(rule->pqname, true)) == 0)
 				ERROUT(EBUSY);
 		} else
 			rule->pqid = rule->qid;
@@ -3314,7 +3384,7 @@ DIOCGETETHRULE_error:
 #ifdef ALTQ
 		/* set queue IDs */
 		if (rule->qname[0] != 0) {
-			if ((rule->qid = pf_qname2qid(rule->qname)) == 0)
+			if ((rule->qid = pf_qname2qid(rule->qname, true)) == 0)
 				error = EBUSY;
 			else
 				rule->qid = rule->qid;
@@ -3865,11 +3935,11 @@ DIOCGETRULENV_error:
 			/* set queue IDs */
 			if (newrule->qname[0] != 0) {
 				if ((newrule->qid =
-				    pf_qname2qid(newrule->qname)) == 0)
+				    pf_qname2qid(newrule->qname, true)) == 0)
 					error = EBUSY;
 				else if (newrule->pqname[0] != 0) {
 					if ((newrule->pqid =
-					    pf_qname2qid(newrule->pqname)) == 0)
+					    pf_qname2qid(newrule->pqname, true)) == 0)
 						error = EBUSY;
 				} else
 					newrule->pqid = newrule->qid;
@@ -4400,7 +4470,7 @@ DIOCGETSTATESV2_full:
 		 * copy the necessary fields
 		 */
 		if (altq->qname[0] != 0) {
-			if ((altq->qid = pf_qname2qid(altq->qname)) == 0) {
+			if ((altq->qid = pf_qname2qid(altq->qname, true)) == 0) {
 				PF_RULES_WUNLOCK();
 				error = EBUSY;
 				free(altq, M_PFALTQ);
@@ -5723,6 +5793,7 @@ fail:
 void
 pfsync_state_export(union pfsync_state_union *sp, struct pf_kstate *st, int msg_version)
 {
+	const char	*tagname;
 	bzero(sp, sizeof(union pfsync_state_union));
 
 	/* copy from state key */
*** 680 LINES SKIPPED ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202509231011.58NABxFd099561>