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 *,