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