The branch main has been updated by kp:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=af922319e8136a818bc6c38440d98a574c5df7a9

commit af922319e8136a818bc6c38440d98a574c5df7a9
Author:     Kristof Provost <k...@freebsd.org>
AuthorDate: 2025-08-27 10:02:51 +0000
Commit:     Kristof Provost <k...@freebsd.org>
CommitDate: 2025-09-25 12:41:07 +0000

    pf: support one shot rules
    
    Add support for one shot rules that remove themselves from an active
    ruleset after match.
    This is an extremely handy technique for firewall proxies.
    
    ok henning, mcbride
    
    Note that the FreeBSD implementation differs significantly from the OpenBSD
    version due to locking differences. We do not remove the rule, but mark it 
as
    having fired previously so we can skip it.
    
    Obtained from:  OpenBSD, mikeb <mi...@openbsd.org>, c981122504
    Obtained from:  OpenBSD, sashan <sas...@openbsd.org>, a21b78cad0 (partial)
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 sys/netpfil/pf/pf.c | 19 +++++++++++++++++++
 sys/netpfil/pf/pf.h |  2 ++
 2 files changed, 21 insertions(+)

diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index be00aff1f5cb..450e465e926a 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -5633,6 +5633,9 @@ pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset 
*ruleset)
                        *ctx->rm = ctx->pd->related_rule;
                        break;
                }
+               PF_TEST_ATTRIB(r->rule_flag & PFRULE_EXPIRED,
+                   TAILQ_NEXT(r, entries));
+               /* Don't count expired rule evaluations. */
                pf_counter_u64_add(&r->evaluations, 1);
                PF_TEST_ATTRIB(pfi_kkif_match(r->kif, pd->kif) == r->ifnot,
                        r->skip[PF_SKIP_IFP]);
@@ -5736,6 +5739,21 @@ pf_match_rule(struct pf_test_ctx *ctx, struct 
pf_kruleset *ruleset)
                if (r->tag)
                        ctx->tag = r->tag;
                if (r->anchor == NULL) {
+
+                       if (r->rule_flag & PFRULE_ONCE) {
+                               uint32_t        rule_flag;
+
+                               rule_flag = r->rule_flag;
+                               if ((rule_flag & PFRULE_EXPIRED) == 0 &&
+                                   atomic_cmpset_int(&r->rule_flag, rule_flag,
+                                   rule_flag | PFRULE_EXPIRED)) {
+                                       //r->exptime = gettime();
+                               } else {
+                                       r = TAILQ_NEXT(r, entries);
+                                       continue;
+                               }
+                       }
+
                        if (r->action == PF_MATCH) {
                                /*
                                 * Apply translations before increasing 
counters,
@@ -5813,6 +5831,7 @@ pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset 
*ruleset)
                r = TAILQ_NEXT(r, entries);
        }
 
+
        return (ctx->test_status);
 }
 
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
index 54ffdbed3de5..bcd66fd17d5d 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -637,6 +637,8 @@ struct pf_rule {
 #define        PFRULE_PFLOW            0x00040000
 #define        PFRULE_ALLOW_RELATED    0x00080000
 #define        PFRULE_AFTO             0x00200000  /* af-to rule */
+#define        PFRULE_ONCE             0x00400000  /* one shot rule */
+#define        PFRULE_EXPIRED          0x00800000  /* one shot rule hit by pkt 
*/
 
 #ifdef _KERNEL
 #define        PFRULE_REFS             0x0080  /* rule has references */

Reply via email to