Hello,

mikeb has just pointed out the patch fell under the desk asking me to resend
it.

> 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
> 

updated version is below.

comments? O.K.?

--------8<---------------8<---------------8<------------------8<--------
diff -r ca96e396772c src/sbin/pfctl/pfctl_parser.c
--- a/src/sbin/pfctl/pfctl_parser.c     Mon Aug 29 23:23:38 2016 +0200
+++ b/src/sbin/pfctl/pfctl_parser.c     Tue Aug 30 00:41:03 2016 +0200
@@ -701,8 +701,12 @@
        int     verbose = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG);
        char    *p;
 
+       if ((r->rule_flag & PFRULE_EXPIRED) && (!verbose))
+               return;
+
        if (verbose)
                printf("@%d ", r->nr);
+
        if (r->action > PF_MATCH)
                printf("action(%d)", r->action);
        else if (anchor_call[0]) {
@@ -1120,6 +1124,9 @@
                printf(" ");
                print_pool(&r->route, 0, 0, r->af, PF_POOL_ROUTE, verbose);
        }
+
+       if (r->rule_flag & PFRULE_EXPIRED)
+               printf("[ rule expired ]");
 }
 
 void
diff -r ca96e396772c src/sys/net/pf.c
--- a/src/sys/net/pf.c  Mon Aug 29 23:23:38 2016 +0200
+++ b/src/sys/net/pf.c  Tue Aug 30 00:41:03 2016 +0200
@@ -311,6 +311,9 @@
 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)
 {
@@ -1174,6 +1177,27 @@
 /* END state table stuff */
 
 void
+pf_purge_expired_rules(int locked)
+{
+       struct pf_rule  *r;
+
+       if (SLIST_EMPTY(&pf_rule_gcl))
+               return;
+
+       if (!locked)
+               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);
+       }
+
+       if (!locked)
+               rw_exit_write(&pf_consistency_lock);
+}
+
+void
 pf_purge_thread(void *v)
 {
        int nloops = 0, s;
@@ -1191,6 +1215,7 @@
                if (++nloops >= pf_default_rule.timeout[PFTM_INTERVAL]) {
                        pf_purge_expired_fragments();
                        pf_purge_expired_src_nodes(0);
+                       pf_purge_expired_rules(0);
                        nloops = 0;
                }
 
@@ -3491,6 +3516,10 @@
        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);
@@ -3796,8 +3825,15 @@
        }
 #endif /* NPFSYNC > 0 */
 
-       if (r->rule_flag & PFRULE_ONCE)
-               pf_purge_rule(ruleset, r, aruleset, a);
+       if (r->rule_flag & PFRULE_ONCE) {
+               if ((a != NULL) && TAILQ_EMPTY(a->ruleset->rules.active.ptr)) {
+                       a->rule_flag |= PFRULE_EXPIRED;
+                       SLIST_INSERT_HEAD(&pf_rule_gcl, a, gcle);
+               }
+
+               r->rule_flag |= PFRULE_EXPIRED;
+               SLIST_INSERT_HEAD(&pf_rule_gcl, r, gcle);
+       }
 
        return (action);
 
diff -r ca96e396772c src/sys/net/pf_ioctl.c
--- a/src/sys/net/pf_ioctl.c    Mon Aug 29 23:23:38 2016 +0200
+++ b/src/sys/net/pf_ioctl.c    Tue Aug 30 00:41:03 2016 +0200
@@ -301,12 +301,13 @@
 }
 
 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_ruleset       *ruleset;
 
-       KASSERT(ruleset != NULL && rule != NULL);
+       KASSERT((rule != NULL) && (rule->ruleset != NULL));
+       ruleset = rule->ruleset;
 
        pf_rm_rule(ruleset->rules.active.ptr, rule);
        ruleset->rules.active.rcount--;
@@ -314,16 +315,6 @@
                rule->nr = nr++;
        ruleset->rules.active.ticket++;
        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)
-                       rule->nr = nr++;
-               aruleset->rules.active.ticket++;
-               pf_calc_skip_steps(aruleset->rules.active.ptr);
-       }
 }
 
 u_int16_t
@@ -775,6 +766,9 @@
        int                      s, error;
        u_int32_t                old_rcount;
 
+       /* Make sure any expired rules get removed from active rules first. */
+       pf_purge_expired_rules(1);
+
        rs = pf_find_ruleset(anchor);
        if (rs == NULL || !rs->rules.inactive.open ||
            ticket != rs->rules.inactive.ticket)
@@ -1209,6 +1203,7 @@
                }
                TAILQ_INSERT_TAIL(ruleset->rules.inactive.ptr,
                    rule, entries);
+               rule->ruleset = ruleset;
                ruleset->rules.inactive.rcount++;
                break;
        }
diff -r ca96e396772c src/sys/net/pfvar.h
--- a/src/sys/net/pfvar.h       Mon Aug 29 23:23:38 2016 +0200
+++ b/src/sys/net/pfvar.h       Tue Aug 30 00:41:03 2016 +0200
@@ -571,6 +571,9 @@
                struct pf_addr          addr;
                u_int16_t               port;
        }                       divert, divert_packet;
+
+       SLIST_ENTRY(pf_rule)     gcle;
+       struct pf_ruleset       *ruleset;
 };
 
 /* rule flags */
@@ -589,6 +592,7 @@
 #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 */
@@ -1666,6 +1670,7 @@
 extern void                     pf_purge_thread(void *);
 extern void                     pf_purge_expired_src_nodes(int);
 extern void                     pf_purge_expired_states(u_int32_t);
+extern void                     pf_purge_expired_rules(int);
 extern void                     pf_remove_state(struct pf_state *);
 extern void                     pf_remove_divert_state(struct pf_state_key *);
 extern void                     pf_free_state(struct pf_state *);
@@ -1695,9 +1700,7 @@
                                    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