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?
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 };