i think these code chunks are around the wrong way. pfsync may want to defer the transmission of a packet. it does this so it can try and get a state over to a peer firewall before a host may send a reply to the peer, which would get dropped cos there's no matching state.
i think the once rule processing should happen before that. the state is created from the rule, whether the packet the state is for goes out immediately or not shouldn't matter. ok? Index: pf.c =================================================================== RCS file: /cvs/src/sys/net/pf.c,v retrieving revision 1.1104 diff -u -p -U8 -r1.1104 pf.c --- pf.c 27 Jan 2021 23:53:35 -0000 1.1104 +++ pf.c 28 Jan 2021 01:43:22 -0000 @@ -3932,45 +3932,45 @@ pf_test_rule(struct pf_pdesc *pd, struct } } /* copy back packet headers if needed */ if (rewrite && pd->hdrlen) { m_copyback(pd->m, pd->off, pd->hdrlen, &pd->hdr, M_NOWAIT); } -#if NPFSYNC > 0 - if (*sm != NULL && !ISSET((*sm)->state_flags, PFSTATE_NOSYNC) && - pd->dir == PF_OUT && pfsync_up()) { - /* - * We want the state created, but we dont - * want to send this in case a partner - * firewall has to know about it to allow - * replies through it. - */ - if (pfsync_defer(*sm, pd->m)) - return (PF_DEFER); - } -#endif /* NPFSYNC > 0 */ - if (r->rule_flag & PFRULE_ONCE) { u_int32_t rule_flag; /* * Use atomic_cas() to determine a clear winner, which will * insert an expired rule to gcl. */ rule_flag = r->rule_flag; if (((rule_flag & PFRULE_EXPIRED) == 0) && atomic_cas_uint(&r->rule_flag, rule_flag, rule_flag | PFRULE_EXPIRED) == rule_flag) { r->exptime = gettime(); SLIST_INSERT_HEAD(&pf_rule_gcl, r, gcle); } } + +#if NPFSYNC > 0 + if (*sm != NULL && !ISSET((*sm)->state_flags, PFSTATE_NOSYNC) && + pd->dir == PF_OUT && pfsync_up()) { + /* + * We want the state created, but we dont + * want to send this in case a partner + * firewall has to know about it to allow + * replies through it. + */ + if (pfsync_defer(*sm, pd->m)) + return (PF_DEFER); + } +#endif /* NPFSYNC > 0 */ return (action); cleanup: while ((ctx.ri = SLIST_FIRST(&ctx.rules))) { SLIST_REMOVE_HEAD(&ctx.rules, entry); pool_put(&pf_rule_item_pl, ctx.ri); }