On Wed, 16 Dec 2015, Alexandr Nedvedicky wrote:

> On Wed, Dec 16, 2015 at 02:48:49PM +1300, Richard Procter wrote:

> > Quoting pf.conf(5) "once - Creates a one shot rule that will remove itself 
> > from an active ruleset after the first match."
> > 
> > A 'first' match presupposes a total ordering. This comes for free when pf 
> > is single-threaded but concurrent rule matching must take the trouble to 
> > re-impose it somewhere. (I assume that pf aims to do concurrent matching 
> > of packets against the ruleset.)
> 
> pf@solaris allows the race here. 

> The price for correct behavior, which is to allow one and only one 
> packet to hit the rule is too high (given the once rules are kind of 
> special case). 

I agree, at least, I do for 'once' rules that aren't 'quick'.

> What has to be granted is there is one 'winner' only, which puts the 
> rule to garbage collector list. The pragmatic approach wins here.

Right. I'll just note though that the patch as it stands allows multiple 
winners: consider the window between testing PFRULE_EXPIRED and setting it 
(quoted below). Multiple threads may execute in that window before one of 
them closes it by setting PFRULE_EXPIRED. Each of them will delete the 
same rule in turn, causing a probable crash. At least,

    SLIST_INSERT_HEAD(&pf_rule_gcl, r, gcle);
    SLIST_INSERT_HEAD(&pf_rule_gcl, r, gcle);

crashes my machine. 

Whether that's a realistic issue, I don't know. I have though been bitten 
by enough edge cases like this to be very wary of them.

best,
Richard.

On Wed, 16 Dec 2015, Alexandr Nedvedicky wrote:

@@ -3135,6 +3160,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);

> @@ -3433,8 +3462,15 @@ 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) {
> +             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);
> +     }
> 
>  #ifdef INET6
>       if (rewrite && skw->af != sks->af)

Reply via email to