Hi,

This diff finally implements what's stated in the man page regarding
parent anchors for "once" rules: "In case this is the only rule in
the anchor, the anchor will be destroyed automatically after the
rule is matched."  Previous code was largely incorrect, sorry for that.

The additional aruleset/arsm pointer pair lets us keep track of the
parent ruleset containing our anchor that we want to get rid of.
Unfortunately(?) rule object doesn't have a direct way of referring
to the ruleset it belongs to so that's why we need additional entities
to keep track of that.

OK?

diff --git sys/net/pf.c sys/net/pf.c
index 8708417..9f0e2d6 100644
--- sys/net/pf.c
+++ sys/net/pf.c
@@ -3100,10 +3100,12 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, 
struct pf_state **sm,
     struct pf_rule **am, struct pf_ruleset **rsm)
 {
        struct pf_rule          *r;
        struct pf_rule          *nr = NULL;
        struct pf_rule          *a = NULL;
+       struct pf_ruleset       *arsm = NULL;
+       struct pf_ruleset       *aruleset = NULL;
        struct pf_ruleset       *ruleset = NULL;
        struct pf_rule_slist     rules;
        struct pf_rule_item     *ri;
        struct pf_src_node      *sns[PF_SN_MAX];
        struct tcphdr           *th = pd->hdr.tcp;
@@ -3295,30 +3297,34 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, 
struct pf_state **sm,
                        } else {
                                match = asd;
                                *rm = r;
                                *am = a;
                                *rsm = ruleset;
+                               arsm = aruleset;
                                if (act.log & PF_LOG_MATCHES) {
                                        REASON_SET(&reason, PFRES_MATCH);
                                        PFLOG_PACKET(pd, reason, r, a, ruleset);
                                }
                        }
 
                        if (r->quick)
                                break;
                        r = TAILQ_NEXT(r, entries);
-               } else
+               } else {
+                       aruleset = ruleset;
                        pf_step_into_anchor(&asd, &ruleset, &r, &a);
+               }
 
  nextrule:
                if (r == NULL && pf_step_out_of_anchor(&asd, &ruleset,
                    &r, &a, &match))
                        break;
        }
-       r = *rm;
-       a = *am;
-       ruleset = *rsm;
+       r = *rm;        /* matching rule */
+       a = *am;        /* rule that defines an anchor containing 'r' */
+       ruleset = *rsm; /* ruleset of the anchor defined by the rule 'a' */
+       aruleset = arsm;/* ruleset of the 'a' rule itself */
 
        /* apply actions for last matching pass/block rule */
        pf_rule_to_actions(r, &act);
        if (r->rule_flag & PFRULE_AFTO)
                pd->naf = r->naf;
@@ -3447,11 +3453,11 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm, 
struct pf_state **sm,
                        return (PF_DEFER);
        }
 #endif
 
        if (r->rule_flag & PFRULE_ONCE)
-               pf_purge_rule(ruleset, r);
+               pf_purge_rule(ruleset, r, aruleset, a);
 
 #if INET && INET6
        if (rewrite && skw->af != sks->af)
                return (PF_AFRT);
 #endif /* INET && INET6 */
diff --git sys/net/pf_ioctl.c sys/net/pf_ioctl.c
index 2868297..3932a89 100644
--- sys/net/pf_ioctl.c
+++ sys/net/pf_ioctl.c
@@ -306,23 +306,33 @@ pf_rm_rule(struct pf_rulequeue *rulequeue, struct pf_rule 
*rule)
        pf_anchor_remove(rule);
        pool_put(&pf_rule_pl, rule);
 }
 
 void
-pf_purge_rule(struct pf_ruleset *ruleset, struct pf_rule *rule)
+pf_purge_rule(struct pf_ruleset *ruleset, struct pf_rule *rule,
+    struct pf_ruleset *aruleset, struct pf_rule *arule)
 {
-       u_int32_t        nr = 0;
+       u_int32_t                nr = 0;
 
        KASSERT(ruleset != NULL && rule != NULL);
 
        pf_rm_rule(ruleset->rules.active.ptr, rule);
        ruleset->rules.active.rcount--;
        TAILQ_FOREACH(rule, ruleset->rules.active.ptr, entries)
                rule->nr = nr++;
        ruleset->rules.active.ticket++;
        pf_calc_skip_steps(ruleset->rules.active.ptr);
-       pf_remove_if_empty_ruleset(ruleset);
+
+       /* 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
 tagname2tag(struct pf_tags *head, char *tagname, int create)
 {
diff --git sys/net/pfvar.h sys/net/pfvar.h
index f51dc49..a0d94f7 100644
--- sys/net/pfvar.h
+++ sys/net/pfvar.h
@@ -1784,10 +1784,11 @@ extern struct pf_rule            pf_default_rule;
 extern void                     pf_addrcpy(struct pf_addr *, struct pf_addr *,
                                    u_int8_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 *);
 struct pf_divert               *pf_find_divert(struct mbuf *);
 int                             pf_setup_pdesc(struct pf_pdesc *, void *,
                                    sa_family_t, int, struct pfi_kif *,
                                    struct mbuf *, u_short *);

Reply via email to