Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F143080426
D52176.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
49 KB
Referenced Files
None
Subscribers
None
D52176.id.diff
View Options
diff --git a/contrib/tcpdump/print-pfsync.c b/contrib/tcpdump/print-pfsync.c
--- a/contrib/tcpdump/print-pfsync.c
+++ b/contrib/tcpdump/print-pfsync.c
@@ -102,6 +102,7 @@
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 @@
{ "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 @@
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
--- a/share/man/man4/pfsync.4
+++ b/share/man/man4/pfsync.4
@@ -162,6 +162,8 @@
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
--- a/sys/net/if_pfsync.h
+++ b/sys/net/if_pfsync.h
@@ -62,9 +62,10 @@
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 @@
#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
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -452,6 +452,16 @@
#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 @@
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 @@
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 @@
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 @@
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
--- a/sys/netpfil/pf/if_pfsync.c
+++ b/sys/netpfil/pf/if_pfsync.c
@@ -153,6 +153,8 @@
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 @@
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 @@
/* 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 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 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 @@
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 @@
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 @@
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 @@
} 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 @@
"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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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_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_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 @@
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 @@
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 @@
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
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -116,7 +116,6 @@
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_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 @@
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 @@
}
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 @@
* 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 @@
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 @@
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 @@
}
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 @@
}
#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 @@
}
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 @@
#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 @@
ASSIGN_OPT_SATU32(ulsc_m2);
ASSIGN_OPT(flags);
-
+
#undef ASSIGN_OPT
#undef ASSIGN_OPT_SATU32
} else
@@ -1728,7 +1798,7 @@
ASSIGN_OPT(ulsc_m2);
ASSIGN_OPT(flags);
-
+
#undef ASSIGN_OPT
} else
COPY(pq_u);
@@ -1760,7 +1830,7 @@
ASSIGN(qid);
break;
}
- default:
+ default:
panic("%s: unhandled struct pfioc_altq version", __func__);
break;
}
@@ -2191,11 +2261,11 @@
#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 @@
#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 @@
/* 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 @@
* 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 @@
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 */
@@ -5734,8 +5805,6 @@
sp->pfs_1301.key[PF_SK_STACK].addr[1] = st->key[PF_SK_STACK]->addr[1];
sp->pfs_1301.key[PF_SK_STACK].port[0] = st->key[PF_SK_STACK]->port[0];
sp->pfs_1301.key[PF_SK_STACK].port[1] = st->key[PF_SK_STACK]->port[1];
- sp->pfs_1301.proto = st->key[PF_SK_WIRE]->proto;
- sp->pfs_1301.af = st->key[PF_SK_WIRE]->af;
/* copy from state */
strlcpy(sp->pfs_1301.ifname, st->kif->pfik_name, sizeof(sp->pfs_1301.ifname));
@@ -5747,16 +5816,31 @@
else
sp->pfs_1301.expire = htonl(sp->pfs_1301.expire - time_uptime);
- sp->pfs_1301.direction = st->direction;
- sp->pfs_1301.log = st->act.log;
- sp->pfs_1301.timeout = st->timeout;
-
switch (msg_version) {
case PFSYNC_MSG_VERSION_1301:
sp->pfs_1301.state_flags = st->state_flags;
+ sp->pfs_1301.direction = st->direction;
+ sp->pfs_1301.log = st->act.log;
+ sp->pfs_1301.timeout = st->timeout;
+ sp->pfs_1301.proto = st->key[PF_SK_WIRE]->proto;
+ sp->pfs_1301.af = st->key[PF_SK_WIRE]->af;
+ /*
+ * XXX Why do we bother pfsyncing source node information if source
+ * nodes are not synced? Showing users that there is source tracking
+ * when there is none seems useless.
+ */
+ if (st->sns[PF_SN_LIMIT] != NULL)
+ sp->pfs_1301.sync_flags |= PFSYNC_FLAG_SRCNODE;
+ if (st->sns[PF_SN_NAT] != NULL || st->sns[PF_SN_ROUTE])
+ sp->pfs_1301.sync_flags |= PFSYNC_FLAG_NATSRCNODE;
break;
case PFSYNC_MSG_VERSION_1400:
sp->pfs_1400.state_flags = htons(st->state_flags);
+ sp->pfs_1400.direction = st->direction;
+ sp->pfs_1400.log = st->act.log;
+ sp->pfs_1400.timeout = st->timeout;
+ sp->pfs_1400.proto = st->key[PF_SK_WIRE]->proto;
+ sp->pfs_1400.af = st->key[PF_SK_WIRE]->af;
sp->pfs_1400.qid = htons(st->act.qid);
sp->pfs_1400.pqid = htons(st->act.pqid);
sp->pfs_1400.dnpipe = htons(st->act.dnpipe);
@@ -5772,22 +5856,53 @@
strlcpy(sp->pfs_1400.rt_ifname,
st->act.rt_kif->pfik_name,
sizeof(sp->pfs_1400.rt_ifname));
+ /*
+ * XXX Why do we bother pfsyncing source node information if source
+ * nodes are not synced? Showing users that there is source tracking
+ * when there is none seems useless.
+ */
+ if (st->sns[PF_SN_LIMIT] != NULL)
+ sp->pfs_1400.sync_flags |= PFSYNC_FLAG_SRCNODE;
+ if (st->sns[PF_SN_NAT] != NULL || st->sns[PF_SN_ROUTE])
+ sp->pfs_1400.sync_flags |= PFSYNC_FLAG_NATSRCNODE;
+ break;
+ case PFSYNC_MSG_VERSION_1500:
+ sp->pfs_1500.state_flags = htons(st->state_flags);
+ sp->pfs_1500.direction = st->direction;
+ sp->pfs_1500.log = st->act.log;
+ sp->pfs_1500.timeout = st->timeout;
+ sp->pfs_1500.wire_proto = st->key[PF_SK_WIRE]->proto;
+ sp->pfs_1500.wire_af = st->key[PF_SK_WIRE]->af;
+ sp->pfs_1500.stack_proto = st->key[PF_SK_STACK]->proto;
+ sp->pfs_1500.stack_af = st->key[PF_SK_STACK]->af;
+ sp->pfs_1500.qid = htons(st->act.qid);
+ sp->pfs_1500.pqid = htons(st->act.pqid);
+ sp->pfs_1500.dnpipe = htons(st->act.dnpipe);
+ sp->pfs_1500.dnrpipe = htons(st->act.dnrpipe);
+ sp->pfs_1500.rtableid = htonl(st->act.rtableid);
+ sp->pfs_1500.min_ttl = st->act.min_ttl;
+ sp->pfs_1500.set_tos = st->act.set_tos;
+ sp->pfs_1500.max_mss = htons(st->act.max_mss);
+ sp->pfs_1500.set_prio[0] = st->act.set_prio[0];
+ sp->pfs_1500.set_prio[1] = st->act.set_prio[1];
+ sp->pfs_1500.rt = st->act.rt;
+ sp->pfs_1500.rt_af = st->act.rt_af;
+ if (st->act.rt_kif)
+ strlcpy(sp->pfs_1500.rt_ifname,
+ st->act.rt_kif->pfik_name,
+ sizeof(sp->pfs_1500.rt_ifname));
+ strlcpy(sp->pfs_1500.orig_ifname,
+ st->orig_kif->pfik_name,
+ sizeof(sp->pfs_1500.orig_ifname));
+ if ((tagname = pf_tag2tagname(st->tag)) != NULL)
+ strlcpy(sp->pfs_1500.tagname, tagname,
+ sizeof(sp->pfs_1500.tagname));
break;
default:
panic("%s: Unsupported pfsync_msg_version %d",
__func__, msg_version);
}
- /*
- * XXX Why do we bother pfsyncing source node information if source
- * nodes are not synced? Showing users that there is source tracking
- * when there is none seems useless.
- */
- if (st->sns[PF_SN_LIMIT] != NULL)
- sp->pfs_1301.sync_flags |= PFSYNC_FLAG_SRCNODE;
- if (st->sns[PF_SN_NAT] != NULL || st->sns[PF_SN_ROUTE])
- sp->pfs_1301.sync_flags |= PFSYNC_FLAG_NATSRCNODE;
-
sp->pfs_1301.id = st->id;
sp->pfs_1301.creatorid = st->creatorid;
pf_state_peer_hton(&st->src, &sp->pfs_1301.src);
@@ -6842,6 +6957,7 @@
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
rm_init_flags(&V_pf_rules_lock, "pf rulesets", RM_RECURSE);
+ rm_init_flags(&V_pf_tags_lock, "pf tags and queues", RM_RECURSE);
sx_init(&V_pf_ioctl_lock, "pf ioctl");
pf_init_tagset(&V_pf_tags, &pf_rule_tag_hashsize,
@@ -6993,7 +7109,7 @@
pf_load_vnet();
}
-VNET_SYSINIT(vnet_pf_init, SI_SUB_PROTO_FIREWALL, SI_ORDER_THIRD,
+VNET_SYSINIT(vnet_pf_init, SI_SUB_PROTO_FIREWALL, SI_ORDER_THIRD,
vnet_pf_init, NULL);
static void
@@ -7001,7 +7117,7 @@
{
pf_unload_vnet();
-}
+}
SYSUNINIT(pf_unload, SI_SUB_PROTO_FIREWALL, SI_ORDER_SECOND, pf_unload, NULL);
VNET_SYSUNINIT(vnet_pf_uninit, SI_SUB_PROTO_FIREWALL, SI_ORDER_THIRD,
vnet_pf_uninit, NULL);
diff --git a/tests/sys/netpfil/pf/pfsync.sh b/tests/sys/netpfil/pf/pfsync.sh
--- a/tests/sys/netpfil/pf/pfsync.sh
+++ b/tests/sys/netpfil/pf/pfsync.sh
@@ -921,6 +921,8 @@
route_to_common_head()
{
+ # TODO: Extend setup_router_server_nat64 to create a 2nd router
+
pfsync_version=$1
shift
@@ -937,11 +939,16 @@
# pfsync interface
jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up
- jexec one ifconfig ${epair_one}a 198.51.100.1/24 up
+ jexec one ifconfig ${epair_one}a 198.51.100.1/28 up
+ jexec one ifconfig ${epair_one}a inet6 2001:db8:4211::1/64 no_dad
+ jexec one ifconfig ${epair_one}a name inif
jexec one ifconfig ${epair_out_one}a 203.0.113.1/24 up
+ jexec one ifconfig ${epair_out_one}a inet6 2001:db8:4200::1/64 no_dad
jexec one ifconfig ${epair_out_one}a name outif
jexec one sysctl net.inet.ip.forwarding=1
- jexec one arp -s 203.0.113.254 00:01:02:03:04:05
+ jexec one sysctl net.inet6.ip6.forwarding=1
+ jexec one arp -s 203.0.113.254 00:01:02:00:00:04
+ jexec one ndp -s 2001:db8:4200::fe 00:01:02:00:00:06
jexec one ifconfig pfsync0 \
syncdev ${epair_sync}a \
maxupd 1 \
@@ -949,20 +956,30 @@
up
jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up
- jexec two ifconfig ${epair_two}a 198.51.100.2/24 up
- jexec two ifconfig ${epair_out_two}a 203.0.113.2/24 up
+ jexec two ifconfig ${epair_two}a 198.51.100.17/28 up
+ jexec two ifconfig ${epair_two}a inet6 2001:db8:4212::1/64 no_dad
+ jexec two ifconfig ${epair_two}a name inif
+ jexec two ifconfig ${epair_out_two}a 203.0.113.1/24 up
+ jexec two ifconfig ${epair_out_two}a inet6 2001:db8:4200::2/64 no_dad
jexec two ifconfig ${epair_out_two}a name outif
jexec two sysctl net.inet.ip.forwarding=1
- jexec two arp -s 203.0.113.254 00:01:02:03:04:05
+ jexec two sysctl net.inet6.ip6.forwarding=1
+ jexec two arp -s 203.0.113.254 00:01:02:00:00:04
+ jexec two ndp -s 2001:db8:4200::fe 00:01:02:00:00:06
jexec two ifconfig pfsync0 \
syncdev ${epair_sync}b \
maxupd 1 \
version $pfsync_version \
up
- ifconfig ${epair_one}b 198.51.100.254/24 up
- ifconfig ${epair_two}b 198.51.100.253/24 up
+ ifconfig ${epair_one}b 198.51.100.2/28 up
+ ifconfig ${epair_one}b inet6 2001:db8:4211::2/64 no_dad
+ ifconfig ${epair_two}b 198.51.100.18/28 up
+ ifconfig ${epair_two}b inet6 2001:db8:4212::2/64 no_dad
+ # Target is behind router "one"
route add -net 203.0.113.0/24 198.51.100.1
+ route add -inet6 -net 64:ff9b::/96 2001:db8:4211::1
+
ifconfig ${epair_two}b up
ifconfig ${epair_out_one}b up
ifconfig ${epair_out_two}b up
@@ -1206,6 +1223,435 @@
pfsynct_cleanup
}
+atf_test_case "af_to_in_floating" "cleanup"
+af_to_in_floating_head()
+{
+ atf_set descr 'Test syncing of states created by inbound af-to rules with floating states'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+af_to_in_floating_body()
+{
+ route_to_common_head 1500
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set state-policy floating" \
+ "set skip on ${epair_sync}a" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif to 64:ff9b::/96 af-to inet from (outif) keep state"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)"
+
+ # ptf_ping can't deal with nat64, this test will fail but generate states
+ atf_check -s exit:1 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}b \
+ --fromaddr 2001:db8:4201::fe \
+ --to 64:ff9b::203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ # Allow time for sync
+ sleep 2
+
+ states_one=$(mktemp)
+ states_two=$(mktemp)
+ jexec one pfctl -qvvss | normalize_pfctl_s > $states_one
+ jexec two pfctl -qvvss | normalize_pfctl_s > $states_two
+
+ # Sanity check
+ grep -qE 'all ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* rule 3 .* origif: inif' $states_one ||
+ atf_fail "State missing on router one"
+
+ grep -qE 'all ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* origif: inif' $states_two ||
+ atf_fail "State missing on router two"
+}
+
+af_to_in_floating_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "af_to_in_if_bound" "cleanup"
+af_to_in_if_bound_head()
+{
+ atf_set descr 'Test syncing of states created by inbound af-to rules with if-bound states'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+af_to_in_if_bound_body()
+{
+ route_to_common_head 1500
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set state-policy if-bound" \
+ "set skip on ${epair_sync}a" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif to 64:ff9b::/96 af-to inet from (outif) keep state"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)"
+
+ # ptf_ping can't deal with nat64, this test will fail but generate states
+ atf_check -s exit:1 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}b \
+ --fromaddr 2001:db8:4201::fe \
+ --to 64:ff9b::203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ # Allow time for sync
+ sleep 2
+
+ states_one=$(mktemp)
+ states_two=$(mktemp)
+ jexec one pfctl -qvvss | normalize_pfctl_s > $states_one
+ jexec two pfctl -qvvss | normalize_pfctl_s > $states_two
+
+ # Sanity check
+ grep -qE 'outif ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* rule 3 .* origif: inif' $states_one ||
+ atf_fail "State missing on router one"
+
+ grep -qE 'outif ipv6-icmp 203.0.113.1 \(2001:db8:4201::fe\) -> 203.0.113.254:8 \(64:ff9b::cb00:71fe) .* origif: inif' $states_two ||
+ atf_fail "State missing on router two"
+}
+
+af_to_in_if_bound_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "af_to_out_if_bound" "cleanup"
+af_to_out_if_bound_head()
+{
+ atf_set descr 'Test syncing of states created by outbound af-to rules with if-bound states'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+af_to_out_if_bound_body()
+{
+ route_to_common_head 1500
+
+ jexec one route add -inet6 -net 64:ff9b::/96 -iface outif
+ jexec one sysctl net.inet6.ip6.forwarding=1
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set state-policy if-bound" \
+ "set skip on ${epair_sync}a" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif to 64:ff9b::/96 keep state" \
+ "pass out on outif to 64:ff9b::/96 af-to inet from (outif) keep state"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set skip on ${epair_sync}b" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)"
+
+ # ptf_ping can't deal with nat64, this test will fail but generate states
+ atf_check -s exit:1 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --sendif ${epair_one}b \
+ --fromaddr 2001:db8:4201::fe \
+ --to 64:ff9b::203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ # Allow time for sync
+ sleep 2
+
+ states_one=$(mktemp)
+ states_two=$(mktemp)
+ jexec one pfctl -qvvss | normalize_pfctl_s > $states_one
+ jexec two pfctl -qvvss | normalize_pfctl_s > $states_two
+
+ # Sanity check
+ # st->orig_kif is the same as st->kif, so st->orig_kif is not printed.
+ for state_regexp in \
+ "inif ipv6-icmp 64:ff9b::cb00:71fe\[128\] <- 2001:db8:4201::fe .* rule 3 .* creatorid: [0-9a-f]+" \
+ "outif icmp 203.0.113.1 \(64:ff9b::cb00:71fe\[8\]\) -> 203.0.113.254:8 \(2001:db8:4201::fe\) .* rule 4 .* creatorid: [0-9a-f]+" \
+ ; do
+ grep -qE "${state_regexp}" $states_one || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ for state_regexp in \
+ "inif ipv6-icmp 64:ff9b::cb00:71fe\[128\] <- 2001:db8:4201::fe .* creatorid: [0-9a-f]+" \
+ "outif icmp 203.0.113.1 \(64:ff9b::cb00:71fe\[8\]\) -> 203.0.113.254:8 \(2001:db8:4201::fe\) .* creatorid: [0-9a-f]+" \
+ ; do
+ grep -qE "${state_regexp}" $states_two || atf_fail "State not found for '${state_regexp}'"
+ done
+}
+
+af_to_out_if_bound_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "tag" "cleanup"
+tag_head()
+{
+ atf_set descr 'Test if the pf tag is synced'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+tag_body()
+{
+ route_to_common_head 1500
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif inet proto udp tag sometag keep state" \
+ "pass out on outif tagged sometag keep state (no-sync)"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set debug loud" \
+ "set skip on ${epair_sync}b" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "block tagged othertag" \
+ "pass out on outif tagged sometag keep state (no-sync)"
+
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_one}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ # Allow time for sync
+ sleep 2
+
+ # Force the next request to go through the 2nd router
+ route change -net 203.0.113.0/24 198.51.100.17
+
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_two}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.254 \
+ --recvif ${epair_out_two}b
+}
+
+tag_cleanup()
+{
+ pfsynct_cleanup
+}
+
+atf_test_case "altq_queues" "cleanup"
+altq_queues_head()
+{
+ atf_set descr 'Test if the altq queues are synced'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+altq_queues_body()
+{
+ route_to_common_head 1500
+ altq_init
+ is_altq_supported hfsc
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "altq on outif bandwidth 30000b hfsc queue { default other1 other2 }" \
+ "queue default hfsc(linkshare 10000b default)" \
+ "queue other1 hfsc(linkshare 10000b)" \
+ "queue other2 hfsc(linkshare 10000b)" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif inet proto udp queue other1 keep state" \
+ "pass out on outif inet proto udp keep state"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set debug loud" \
+ "set skip on ${epair_sync}b" \
+ "altq on outif bandwidth 30000b hfsc queue { default other2 other1 }" \
+ "queue default hfsc(linkshare 10000b default)" \
+ "queue other2 hfsc(linkshare 10000b)" \
+ "queue other1 hfsc(linkshare 10000b)" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass out on outif inet proto udp keep state"
+
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_one}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.254 \
+ --recvif ${epair_out_one}b
+
+ queues_one=$(mktemp)
+ jexec one pfctl -qvsq | normalize_pfctl_s > $queues_one
+ echo " === queues one === "
+ cat $queues_one
+ grep -qE 'queue other1 on outif .* pkts: 1 ' $queues_one || atf_fail 'Packets not sent through queue "other1"'
+
+ # Allow time for sync
+ sleep 2
+
+ # Force the next request to go through the 2nd router
+ route change -net 203.0.113.0/24 198.51.100.17
+
+ # Send a packet through router "two". It lacks the inbound rule
+ # but the inbound state should have been pfsynced from router "one"
+ # including altq queuing information. However the queues are created
+ # on router "two" in different order and we only sync queue index,
+ # so the packet ends up in a different queue. One must have identical
+ # queue set on both routers!
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_two}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.254 \
+ --recvif ${epair_out_two}b
+
+ queues_two=$(mktemp)
+ jexec two pfctl -qvsq | normalize_pfctl_s > $queues_two
+ echo " === queues two === "
+ cat $queues_two
+ grep -qE 'queue other2 on outif .* pkts: 1 ' $queues_two || atf_fail 'Packets not sent through queue "other2"'
+}
+
+altq_queues_cleanup()
+{
+ # Interface detaching seems badly broken in altq. If interfaces are
+ # destroyed when shutting down the vnet and then pf is unloaded, it will
+ # cause a kernel crash. Work around the issue by first flushing the
+ # pf rulesets
+ jexec one pfctl -F all
+ jexec two pfctl -F all
+ pfsynct_cleanup
+}
+
+atf_test_case "rt_af" "cleanup"
+rt_af_head()
+{
+ atf_set descr 'Test if the rt_af is synced'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+rt_af_body()
+{
+ route_to_common_head 1500
+
+ jexec one pfctl -e
+ pft_set_rules one \
+ "set skip on ${epair_sync}a" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+ "pass in on inif \
+ route-to (outif 203.0.113.254) prefer-ipv6-nexthop \
+ inet proto udp \
+ to 203.0.113.241 \
+ keep state" \
+ "pass in on inif \
+ route-to (outif 2001:db8:4200::fe) prefer-ipv6-nexthop \
+ inet proto udp \
+ to 203.0.113.242 \
+ keep state" \
+ "pass in on inif \
+ route-to (outif 2001:db8:4200::fe) prefer-ipv6-nexthop \
+ inet6 proto udp \
+ to 2001:db8:4200::f3 \
+ keep state" \
+ "pass out on outif inet proto udp keep state (no-sync)" \
+ "pass out on outif inet6 proto udp keep state (no-sync)"
+
+ jexec two pfctl -e
+ pft_set_rules two \
+ "set debug loud" \
+ "set skip on ${epair_sync}b" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv } keep state (no-sync)" \
+
+ # IPv4 packet over IPv4 gateway
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_one}b \
+ --fromaddr 198.51.100.254 \
+ --to 203.0.113.241 \
+ --recvif ${epair_out_one}b
+
+ # FIXME: Routing IPv4 packets over IPv6 gateways with gateway added
+ # with `ndp -s` causes the static NDP entry to become expired.
+ # Pfsync tests don't use "servers" which can reply to ARP and NDP,
+ # but such static entry for gateway and only check if a stateless
+ # ICMP or UDP packet is forward through.
+ #
+ # IPv4 packert over IPv6 gateway
+ #atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ # ${common_dir}/pft_ping.py \
+ # --ping-type=udp \
+ # --sendif ${epair_one}b \
+ # --fromaddr 198.51.100.254 \
+ # --to 203.0.113.242 \
+ # --recvif ${epair_out_one}b
+
+ # IPv6 packet over IPv6 gateway
+ atf_check -s exit:0 env PYTHONPATH=${common_dir} \
+ ${common_dir}/pft_ping.py \
+ --ping-type=udp \
+ --sendif ${epair_one}b \
+ --fromaddr 2001:db8:4211::fe \
+ --to 2001:db8:4200::f3 \
+ --recvif ${epair_out_one}b
+
+ sleep 5 # Wait for pfsync
+
+ states_one=$(mktemp)
+ states_two=$(mktemp)
+ jexec one pfctl -qvvss | normalize_pfctl_s > $states_one
+ jexec two pfctl -qvvss | normalize_pfctl_s > $states_two
+
+ echo " === states one === "
+ cat $states_one
+ echo " === states two === "
+ cat $states_two
+
+ for state_regexp in \
+ "all udp 203.0.113.241:9 <- 198.51.100.254 .* route-to: 203.0.113.254@outif origif: inif" \
+ "all udp 2001:db8:4200::f3\[9\] <- 2001:db8:4211::fe .* route-to: 2001:db8:4200::fe@outif origif: inif" \
+ ; do
+ grep -qE "${state_regexp}" $states_two || atf_fail "State not found for '${state_regexp}' on router two"
+ done
+}
+
+rt_af_cleanup()
+{
+ jexec one pfctl -qvvsr
+ jexec one pfctl -qvvss
+ jexec one arp -an
+ jexec one ndp -an
+ pfsynct_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "basic"
@@ -1224,4 +1670,10 @@
atf_add_test_case "route_to_1301_bad_rpool"
atf_add_test_case "route_to_1400_bad_ruleset"
atf_add_test_case "route_to_1400_bad_ifname"
+ atf_add_test_case "af_to_in_floating"
+ atf_add_test_case "af_to_in_if_bound"
+ atf_add_test_case "af_to_out_if_bound"
+ atf_add_test_case "tag"
+ atf_add_test_case "altq_queues"
+ atf_add_test_case "rt_af"
}
diff --git a/usr.bin/netstat/if.c b/usr.bin/netstat/if.c
--- a/usr.bin/netstat/if.c
+++ b/usr.bin/netstat/if.c
@@ -84,8 +84,10 @@
/* PFSYNC_ACT_BUS */ "bulk update mark",
/* PFSYNC_ACT_TDB */ "TDB replay counter update",
/* PFSYNC_ACT_EOF */ "end of frame mark",
- /* PFSYNC_ACT_INS_1400 */ "state insert",
- /* PFSYNC_ACT_UPD_1400 */ "state update",
+ /* PFSYNC_ACT_INS_1400 */ "14.0 state insert",
+ /* PFSYNC_ACT_UPD_1400 */ "14.0 state update",
+ /* PFSYNC_ACT_INS_1500 */ "state insert",
+ /* PFSYNC_ACT_UPD_1500 */ "state update",
};
static const char* pfsyncacts_name[] = {
@@ -102,8 +104,10 @@
/* PFSYNC_ACT_BUS */ "bulk-update-mark",
/* PFSYNC_ACT_TDB */ "TDB-replay-counter-update",
/* PFSYNC_ACT_EOF */ "end-of-frame-mark",
- /* PFSYNC_ACT_INS_1400 */ "state-insert",
- /* PFSYNC_ACT_UPD_1400 */ "state-update",
+ /* PFSYNC_ACT_INS_1400 */ "state-insert-1400",
+ /* PFSYNC_ACT_UPD_1400 */ "state-update-1400",
+ /* PFSYNC_ACT_INS_1500 */ "state-insert",
+ /* PFSYNC_ACT_UPD_1500 */ "state-update",
};
static void
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Jan 26, 6:16 PM (20 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28042133
Default Alt Text
D52176.id.diff (49 KB)
Attached To
Mode
D52176: pf: Add pfsync protocol for FreeBSD 15
Attached
Detach File
Event Timeline
Log In to Comment