Hello,

henning@ and mikeb@ showed some interest to change handling of once rules to
the same way as PF has it on Solaris. Just to refresh the audience on once
option offered by PF:

     once    Creates a one shot rule that will remove itself from an active
             ruleset after the first match.  In case this is the only rule in
             the anchor, the anchor will be destroyed automatically after the
             rule is matched.
                                                               -- pf.conf(5)

Currently the once rules are removed by matching packet. Patch makes life for
packets, which match once rules bit easier. Packets instead of removing rule
from ruleset just mark rule as expired and put it to garbage colloector list.
The list is processed by pf_purge_thread(), which just removes and deletes
those expired rules. To get there we need to simplify pf_purge_rule() image,
which currently looks as follows:

    void
    pf_purge_rule(struct pf_ruleset *ruleset, struct pf_rule *rule,
        struct pf_ruleset *aruleset, struct pf_rule *arule)

        - ruleset is the ruleset, where once rule is being removed from

        - rule is a once rule to remove

        - aruleset holds an anchor rule with once-rule we remove

        - arule an anchor which holds a once rule

To make pf_purge_rule() suitable for pf_purge_thread() it has to be changed to:

    void
    pf_purge_rule(struct pf_rule *once_rule)

To get there the ruleset and arule has to be carried by once_rule itself.
Therefore patch adds those members to pf_rule:
        struct pf_ruleset       *myruleset
        struct pf_rule          *myarule
        SLIST_ENTRY(pf_rule)     gcle
(the gcle is garbage colleter list link).

Patch sets myruleset as soon as rule gets inserted to ruleset in SIOCADDRULE
ioctl. The myarule is set in pf_test_rule(), when once rule is marked as
expired.

Don't forget to recompile all user-land bits (pfctl, proxies et. al.) when
you'll be testing the patch, since pf_rule structure gets changed.

regards
sasha

--------8<---------------8<---------------8<------------------8<--------

Index: net/pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.958
diff -u -p -r1.958 pf.c
--- net/pf.c    5 Dec 2015 14:58:06 -0000       1.958
+++ net/pf.c    5 Dec 2015 22:09:42 -0000
@@ -298,6 +298,9 @@ RB_GENERATE(pf_state_tree, pf_state_key,
 RB_GENERATE(pf_state_tree_id, pf_state,
     entry_id, pf_state_compare_id);
 
+SLIST_HEAD(pf_rule_gcl, pf_rule)       pf_rule_gcl =
+       SLIST_HEAD_INITIALIZER(pf_rule_gcl);
+
 __inline int
 pf_addr_compare(struct pf_addr *a, struct pf_addr *b, sa_family_t af)
 {
@@ -1140,6 +1143,24 @@ pf_state_export(struct pfsync_state *sp,
 /* END state table stuff */
 
 void
+pf_purge_expired_rules(void)
+{
+       struct pf_rule  *r;
+
+       if (SLIST_EMPTY(&pf_rule_gcl)) {
+               return;
+       }
+
+       rw_enter_write(&pf_consistency_lock);
+       while ((r = SLIST_FIRST(&pf_rule_gcl)) != NULL) {
+               SLIST_REMOVE(&pf_rule_gcl, r, pf_rule, gcle);
+               KASSERT(r->rule_flag & PFRULE_EXPIRED);
+               pf_purge_rule(r);
+       }
+       rw_exit_write(&pf_consistency_lock);
+}
+
+void
 pf_purge_thread(void *v)
 {
        int nloops = 0, s;
@@ -1157,6 +1178,7 @@ pf_purge_thread(void *v)
                if (++nloops >= pf_default_rule.timeout[PFTM_INTERVAL]) {
                        pf_purge_expired_fragments();
                        pf_purge_expired_src_nodes(0);
+                       pf_purge_expired_rules();
                        nloops = 0;
                }
 
@@ -3149,6 +3171,10 @@ pf_test_rule(struct pf_pdesc *pd, struct
        ruleset = &pf_main_ruleset;
        r = TAILQ_FIRST(pf_main_ruleset.rules.active.ptr);
        while (r != NULL) {
+               if (r->rule_flag & PFRULE_EXPIRED) {
+                       r = TAILQ_NEXT(r, entries);
+                       goto nextrule;
+               }
                r->evaluations++;
                PF_TEST_ATTRIB((pfi_kif_match(r->kif, pd->kif) == r->ifnot),
                        r->skip[PF_SKIP_IFP].ptr);
@@ -3447,8 +3473,11 @@ pf_test_rule(struct pf_pdesc *pd, struct
        }
 #endif /* NPFSYNC > 0 */
 
-       if (r->rule_flag & PFRULE_ONCE)
-               pf_purge_rule(ruleset, r, aruleset, a);
+       if (r->rule_flag & PFRULE_ONCE) {
+               r->rule_flag |= PFRULE_EXPIRED;
+               r->myarule = a;
+               SLIST_INSERT_HEAD(&pf_rule_gcl, r, gcle);
+       }
 
 #ifdef INET6
        if (rewrite && skw->af != sks->af)
Index: net/pf_ioctl.c
===================================================================
RCS file: /cvs/src/sys/net/pf_ioctl.c,v
retrieving revision 1.297
diff -u -p -r1.297 pf_ioctl.c
--- net/pf_ioctl.c      3 Dec 2015 13:30:18 -0000       1.297
+++ net/pf_ioctl.c      5 Dec 2015 22:09:42 -0000
@@ -301,12 +301,14 @@ pf_rm_rule(struct pf_rulequeue *rulequeu
 }
 
 void
-pf_purge_rule(struct pf_ruleset *ruleset, struct pf_rule *rule,
-    struct pf_ruleset *aruleset, struct pf_rule *arule)
+pf_purge_rule(struct pf_rule *rule)
 {
        u_int32_t                nr = 0;
+       struct pf_rule          *arule = rule->myarule;
+       struct pf_ruleset       *ruleset;
 
-       KASSERT(ruleset != NULL && rule != NULL);
+       KASSERT((rule != NULL) && (rule->myruleset != NULL));
+       ruleset = rule->myruleset;
 
        pf_rm_rule(ruleset->rules.active.ptr, rule);
        ruleset->rules.active.rcount--;
@@ -316,13 +318,15 @@ pf_purge_rule(struct pf_ruleset *ruleset
        pf_calc_skip_steps(ruleset->rules.active.ptr);
 
        /* remove the parent anchor rule */
-       if (nr == 0 && arule && aruleset) {
-               pf_rm_rule(aruleset->rules.active.ptr, arule);
-               aruleset->rules.active.rcount--;
-               TAILQ_FOREACH(rule, aruleset->rules.active.ptr, entries)
+       if (nr == 0 && arule) {
+               KASSERT(arule->myruleset != NULL);
+               ruleset = arule->myruleset;
+               pf_rm_rule(ruleset->rules.active.ptr, arule);
+               ruleset->rules.active.rcount--;
+               TAILQ_FOREACH(rule, ruleset->rules.active.ptr, entries)
                        rule->nr = nr++;
-               aruleset->rules.active.ticket++;
-               pf_calc_skip_steps(aruleset->rules.active.ptr);
+               ruleset->rules.active.ticket++;
+               pf_calc_skip_steps(ruleset->rules.active.ptr);
        }
 }
 
@@ -1209,6 +1213,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t a
                }
                TAILQ_INSERT_TAIL(ruleset->rules.inactive.ptr,
                    rule, entries);
+               rule->myruleset = ruleset;
                ruleset->rules.inactive.rcount++;
                break;
        }
Index: net/pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.426
diff -u -p -r1.426 pfvar.h
--- net/pfvar.h 3 Dec 2015 14:05:28 -0000       1.426
+++ net/pfvar.h 5 Dec 2015 22:09:42 -0000
@@ -563,6 +563,10 @@ struct pf_rule {
                struct pf_addr          addr;
                u_int16_t               port;
        }                       divert, divert_packet;
+
+       SLIST_ENTRY(pf_rule)     gcle;
+       struct pf_ruleset       *myruleset;
+       struct pf_rule          *myarule;
 };
 
 /* rule flags */
@@ -581,6 +585,7 @@ struct pf_rule {
 #define PFRULE_PFLOW           0x00040000
 #define PFRULE_ONCE            0x00100000      /* one shot rule */
 #define PFRULE_AFTO            0x00200000      /* af-to rule */
+#define        PFRULE_EXPIRED          0x00400000      /* one shot rule hit by 
pkt */
 
 #define PFSTATE_HIWAT          10000   /* default state table size */
 #define PFSTATE_ADAPT_START    6000    /* default adaptive timeout start */
@@ -1701,9 +1706,7 @@ extern void                        pf_addrcpy(struct 
pf_addr
                                    sa_family_t);
 void                            pf_rm_rule(struct pf_rulequeue *,
                                    struct pf_rule *);
-void                            pf_purge_rule(struct pf_ruleset *,
-                                   struct pf_rule *, struct pf_ruleset *,
-                                   struct pf_rule *);
+void                            pf_purge_rule(struct pf_rule *);
 struct pf_divert               *pf_find_divert(struct mbuf *);
 int                             pf_setup_pdesc(struct pf_pdesc *, void *,
                                    sa_family_t, int, struct pfi_kif *,

Reply via email to