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/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 @@ -451,6 +451,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) @@ -1208,11 +1218,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]; @@ -1235,7 +1245,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; @@ -1248,12 +1258,53 @@ 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]; } __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 @@ -2461,6 +2512,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 *); @@ -213,8 +212,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; @@ -285,6 +283,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; @@ -684,19 +683,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); } /* @@ -713,16 +743,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; @@ -734,7 +768,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 @@ -743,7 +799,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) @@ -760,12 +816,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 @@ -896,10 +960,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)); + return (tagname2tag(&V_pf_qids, qname, add_new)); +} + +static const char * +pf_qid2qname(uint16_t qid) +{ + return (tag2tagname(&V_pf_qids, qid)); } static void @@ -1148,7 +1218,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; @@ -1603,7 +1673,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); @@ -1617,7 +1687,7 @@ ASSIGN_OPT_SATU32(ulsc_m2); ASSIGN_OPT(flags); - + #undef ASSIGN_OPT #undef ASSIGN_OPT_SATU32 } else @@ -1725,7 +1795,7 @@ ASSIGN_OPT(ulsc_m2); ASSIGN_OPT(flags); - + #undef ASSIGN_OPT } else COPY(pq_u); @@ -1757,7 +1827,7 @@ ASSIGN(qid); break; } - default: + default: panic("%s: unhandled struct pfioc_altq version", __func__); break; } @@ -2188,11 +2258,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; @@ -3309,7 +3379,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; @@ -3860,11 +3930,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; @@ -4391,7 +4461,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); @@ -5714,6 +5784,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 */ @@ -5725,8 +5796,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)); @@ -5738,16 +5807,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); @@ -5763,22 +5847,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); @@ -6828,6 +6943,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, @@ -6977,7 +7093,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 @@ -6985,7 +7101,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,10 +939,14 @@ # 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 sysctl net.inet6.ip6.forwarding=1 jexec one arp -s 203.0.113.254 00:01:02:03:04:05 jexec one ifconfig pfsync0 \ syncdev ${epair_sync}a \ @@ -949,10 +955,14 @@ 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 sysctl net.inet6.ip6.forwarding=1 jexec two arp -s 203.0.113.254 00:01:02:03:04:05 jexec two ifconfig pfsync0 \ syncdev ${epair_sync}b \ @@ -960,9 +970,14 @@ 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 +1221,331 @@ 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 other1 other2 }" \ + "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_one || 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_init_test_cases() { atf_add_test_case "basic" @@ -1224,4 +1564,9 @@ 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" } 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