git: 99475087d63b - main - pf: Add pfsync protocol for FreeBSD 15
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 23 Sep 2025 10:11:59 UTC
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 ***