Hello, 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 --------8<---------------8<---------------8<------------------8<-------- Index: net/pf.c =================================================================== RCS file: /cvs/src/sys/net/pf.c,v retrieving revision 1.958 diff -u -p -r1.958 pf.c --- net/pf.c 5 Dec 2015 14:58:06 -0000 1.958 +++ net/pf.c 5 Dec 2015 22:09:42 -0000 @@ -298,6 +298,9 @@ RB_GENERATE(pf_state_tree, pf_state_key, 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) { @@ -1140,6 +1143,24 @@ pf_state_export(struct pfsync_state *sp, /* END state table stuff */ void +pf_purge_expired_rules(void) +{ + struct pf_rule *r; + + if (SLIST_EMPTY(&pf_rule_gcl)) { + return; + } + + 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); + } + rw_exit_write(&pf_consistency_lock); +} + +void pf_purge_thread(void *v) { int nloops = 0, s; @@ -1157,6 +1178,7 @@ pf_purge_thread(void *v) if (++nloops >= pf_default_rule.timeout[PFTM_INTERVAL]) { pf_purge_expired_fragments(); pf_purge_expired_src_nodes(0); + pf_purge_expired_rules(); nloops = 0; } @@ -3149,6 +3171,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); @@ -3447,8 +3473,11 @@ 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) { + r->rule_flag |= PFRULE_EXPIRED; + r->myarule = a; + SLIST_INSERT_HEAD(&pf_rule_gcl, r, gcle); + } #ifdef INET6 if (rewrite && skw->af != sks->af) Index: net/pf_ioctl.c =================================================================== RCS file: /cvs/src/sys/net/pf_ioctl.c,v retrieving revision 1.297 diff -u -p -r1.297 pf_ioctl.c --- net/pf_ioctl.c 3 Dec 2015 13:30:18 -0000 1.297 +++ net/pf_ioctl.c 5 Dec 2015 22:09:42 -0000 @@ -301,12 +301,14 @@ pf_rm_rule(struct pf_rulequeue *rulequeu } 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_rule *arule = rule->myarule; + struct pf_ruleset *ruleset; - KASSERT(ruleset != NULL && rule != NULL); + KASSERT((rule != NULL) && (rule->myruleset != NULL)); + ruleset = rule->myruleset; pf_rm_rule(ruleset->rules.active.ptr, rule); ruleset->rules.active.rcount--; @@ -316,13 +318,15 @@ pf_purge_rule(struct pf_ruleset *ruleset 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) + if (nr == 0 && arule) { + KASSERT(arule->myruleset != NULL); + ruleset = arule->myruleset; + pf_rm_rule(ruleset->rules.active.ptr, arule); + ruleset->rules.active.rcount--; + TAILQ_FOREACH(rule, ruleset->rules.active.ptr, entries) rule->nr = nr++; - aruleset->rules.active.ticket++; - pf_calc_skip_steps(aruleset->rules.active.ptr); + ruleset->rules.active.ticket++; + pf_calc_skip_steps(ruleset->rules.active.ptr); } } @@ -1209,6 +1213,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t a } TAILQ_INSERT_TAIL(ruleset->rules.inactive.ptr, rule, entries); + rule->myruleset = ruleset; ruleset->rules.inactive.rcount++; break; } Index: net/pfvar.h =================================================================== RCS file: /cvs/src/sys/net/pfvar.h,v retrieving revision 1.426 diff -u -p -r1.426 pfvar.h --- net/pfvar.h 3 Dec 2015 14:05:28 -0000 1.426 +++ net/pfvar.h 5 Dec 2015 22:09:42 -0000 @@ -563,6 +563,10 @@ struct pf_rule { struct pf_addr addr; u_int16_t port; } divert, divert_packet; + + SLIST_ENTRY(pf_rule) gcle; + struct pf_ruleset *myruleset; + struct pf_rule *myarule; }; /* rule flags */ @@ -581,6 +585,7 @@ struct pf_rule { #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 */ @@ -1701,9 +1706,7 @@ extern void pf_addrcpy(struct pf_addr 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 *,