On Tue, Jul 22, 2014 at 19:03 +0200, Mike Belopuhov wrote:
> Hi,
>
> Before I send a diff for pfctl to disable "once" on "match" rules,
> I've decided to try and see how much work is it to make it actually
> work. Turns out that I need to extend pf_rule_item by 3 pointers
> to track the match rule ruleset, anchor rule and the ruleset it
> belongs to.
>
> Here's what this means in practice. Consider a ruleset:
>
> block drop all
> match out log proto tcp to port 22 once
> anchor "foo" all {
> match out log proto tcp to port 22 once
> anchor "bar" all {
> match out log proto tcp to port 22 once
> pass out quick proto tcp to port 22 once
> }
> }
>
> Once we send a packet to port 22 the ruleset collapses to just:
>
> block drop all
>
> Thoughts?
Henning thinks it's a bit of an overkill. Any other opinions?
>
> diff --git sys/net/pf.c sys/net/pf.c
> index 9f0e2d6..5679a40 100644
> --- sys/net/pf.c
> +++ sys/net/pf.c
> @@ -3279,15 +3279,16 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule
> **rm, struct pf_state **sm,
> PR_NOWAIT)) == NULL) {
> REASON_SET(&reason, PFRES_MEMORY);
> goto cleanup;
> }
> ri->r = r;
> + ri->ar = a;
> + ri->rs = ruleset;
> + ri->ars = aruleset;
> /* order is irrelevant */
> SLIST_INSERT_HEAD(&rules, ri, entry);
> pf_rule_to_actions(r, &act);
> - if (r->rule_flag & PFRULE_AFTO)
> - pd->naf = r->naf;
> if (pf_get_transaddr(r, pd, sns, &nr) == -1) {
> REASON_SET(&reason, PFRES_TRANSLATE);
> goto cleanup;
> }
> if (r->log || act.log & PF_LOG_MATCHES) {
> @@ -3428,10 +3429,12 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule
> **rm, struct pf_state **sm,
> virtual_type, icmp_dir);
> }
> } else {
> while ((ri = SLIST_FIRST(&rules))) {
> SLIST_REMOVE_HEAD(&rules, entry);
> + if (ri->r->rule_flag & PFRULE_ONCE)
> + pf_purge_rule(ri->rs, ri->r, ri->ars, ri->ar);
> pool_put(&pf_rule_item_pl, ri);
> }
> }
>
> /* copy back packet headers if needed */
> @@ -3454,10 +3457,23 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule
> **rm, struct pf_state **sm,
> }
> #endif
>
> if (r->rule_flag & PFRULE_ONCE)
> pf_purge_rule(ruleset, r, aruleset, a);
> + if (*sm) {
> + SLIST_FOREACH(ri, &(*sm)->match_rules, entry) {
> + if (ri->r->rule_flag & PFRULE_ONCE)
> + /*
> + * We can be sure that pf_purge_rule won't
> + * pool_put the rule because when *sm != NULL
> + * STATE_INC_COUNTERS has increased states_cur.
> + * pf_rule_item's and rules will be g/c'ed by
> + * pf_free_state.
> + */
> + pf_purge_rule(ri->rs, ri->r, ri->ars, ri->ar);
> + }
> + }
>
> #if INET && INET6
> if (rewrite && skw->af != sks->af)
> return (PF_AFRT);
> #endif /* INET && INET6 */
> diff --git sys/net/pfvar.h sys/net/pfvar.h
> index a0d94f7..49af7b4 100644
> --- sys/net/pfvar.h
> +++ sys/net/pfvar.h
> @@ -691,10 +691,13 @@ struct pf_threshold {
> };
>
> struct pf_rule_item {
> SLIST_ENTRY(pf_rule_item) entry;
> struct pf_rule *r;
> + struct pf_rule *ar;
> + struct pf_ruleset *rs;
> + struct pf_ruleset *ars;
> };
>
> SLIST_HEAD(pf_rule_slist, pf_rule_item);
>
> enum pf_sn_types { PF_SN_NONE, PF_SN_NAT, PF_SN_RDR, PF_SN_ROUTE, PF_SN_MAX
> };