Hello, there was still one more glitch catched by mikeb:
I have to sanitize pointer when copying rule to userland. The other thing pointed out by mike is the Expired time should be printed for expired rules only in debug mode output (pfctl -sr -g) Incremental patch is as follows: --------8<---------------8<---------------8<------------------8<-------- diff -r 7a93d568ede3 -r b366ba821dfb src/sbin/pfctl/pfctl.c --- a/src/sbin/pfctl/pfctl.c Sat Sep 03 15:06:16 2016 +0200 +++ b/src/sbin/pfctl/pfctl.c Sat Sep 03 16:20:24 2016 +0200 @@ -702,14 +702,9 @@ pfctl_print_rule_counters(struct pf_rule printf(" [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n", rule->qname, rule->qid, rule->pqname, rule->pqid); - if (rule->rule_flag & PFRULE_ONCE) - if (rule->rule_flag & PFRULE_EXPIRED) - printf(" [ Expired: %lld secs ago ]\n", - (long long)(time(NULL) - rule->exptime)); - else - printf(" [ Expired: not yet ]\n"); - else - printf(" [ Expired: never ]\n"); + if (rule->rule_flag & PFRULE_EXPIRED) + printf(" [ Expired: %lld secs ago ]\n", + (long long)(time(NULL) - rule->exptime)); } if (opts & PF_OPT_VERBOSE) { printf(" [ Evaluations: %-8llu Packets: %-8llu " diff -r 7a93d568ede3 -r b366ba821dfb src/sys/net/pf_ioctl.c --- a/src/sys/net/pf_ioctl.c Sat Sep 03 15:06:16 2016 +0200 +++ b/src/sys/net/pf_ioctl.c Sat Sep 03 16:20:24 2016 +0200 @@ -1268,6 +1268,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t a pr->rule.rcv_kif = NULL; pr->rule.anchor = NULL; pr->rule.overload_tbl = NULL; + bzero(&pr->rule.gcle, sizeof(pr->rule.gcle)); + pr->rule.ruleset = NULL; if (pf_anchor_copyout(ruleset, rule, pr)) { error = EBUSY; break; --------8<---------------8<---------------8<------------------8<-------- complete patch I'd like to commit is further below. regards sasha --------8<---------------8<---------------8<------------------8<-------- diff -r 8006a1eca673 src/sbin/pfctl/pfctl.c --- a/src/sbin/pfctl/pfctl.c Sat Sep 03 14:19:17 2016 +0200 +++ b/src/sbin/pfctl/pfctl.c Sat Sep 03 16:26:08 2016 +0200 @@ -701,6 +701,10 @@ pfctl_print_rule_counters(struct pf_rule printf(" [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n", rule->qname, rule->qid, rule->pqname, rule->pqid); + + if (rule->rule_flag & PFRULE_EXPIRED) + printf(" [ Expired: %lld secs ago ]\n", + (long long)(time(NULL) - rule->exptime)); } if (opts & PF_OPT_VERBOSE) { printf(" [ Evaluations: %-8llu Packets: %-8llu " @@ -848,7 +852,13 @@ pfctl_show_rules(int dev, char *path, in INDENT(depth, !(opts & PF_OPT_VERBOSE)); printf("}\n"); } else { - printf("\n"); + /* + * Do not print newline, when we have not + * printed expired rule. + */ + if (!(pr.rule.rule_flag & PFRULE_EXPIRED) || + (opts & (PF_OPT_VERBOSE2|PF_OPT_DEBUG))) + printf("\n"); pfctl_print_rule_counters(&pr.rule, opts); } break; diff -r 8006a1eca673 src/sbin/pfctl/pfctl_parser.c --- a/src/sbin/pfctl/pfctl_parser.c Sat Sep 03 14:19:17 2016 +0200 +++ b/src/sbin/pfctl/pfctl_parser.c Sat Sep 03 16:26:08 2016 +0200 @@ -701,8 +701,12 @@ print_rule(struct pf_rule *r, const char int verbose = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG); char *p; + if ((r->rule_flag & PFRULE_EXPIRED) && (!verbose)) + return; + if (verbose) printf("@%d ", r->nr); + if (r->action > PF_MATCH) printf("action(%d)", r->action); else if (anchor_call[0]) { diff -r 8006a1eca673 src/sys/net/pf.c --- a/src/sys/net/pf.c Sat Sep 03 14:19:17 2016 +0200 +++ b/src/sys/net/pf.c Sat Sep 03 16:26:08 2016 +0200 @@ -311,6 +311,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) { @@ -1174,6 +1177,29 @@ pf_state_export(struct pfsync_state *sp, /* END state table stuff */ void +pf_purge_expired_rules(int locked) +{ + struct pf_rule *r; + + if (SLIST_EMPTY(&pf_rule_gcl)) + return; + + if (!locked) + rw_enter_write(&pf_consistency_lock); + else + rw_assert_wrlock(&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); + } + + if (!locked) + rw_exit_write(&pf_consistency_lock); +} + +void pf_purge_thread(void *v) { int nloops = 0, s; @@ -1191,6 +1217,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(0); nloops = 0; } @@ -3491,6 +3518,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); @@ -3796,8 +3827,17 @@ 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) { + if ((a != NULL) && TAILQ_EMPTY(a->ruleset->rules.active.ptr)) { + a->rule_flag |= PFRULE_EXPIRED; + a->exptime = time_second; + SLIST_INSERT_HEAD(&pf_rule_gcl, a, gcle); + } + + r->rule_flag |= PFRULE_EXPIRED; + r->exptime = time_second; + SLIST_INSERT_HEAD(&pf_rule_gcl, r, gcle); + } return (action); diff -r 8006a1eca673 src/sys/net/pf_ioctl.c --- a/src/sys/net/pf_ioctl.c Sat Sep 03 14:19:17 2016 +0200 +++ b/src/sys/net/pf_ioctl.c Sat Sep 03 16:26:08 2016 +0200 @@ -309,12 +309,13 @@ 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_ruleset *ruleset; - KASSERT(ruleset != NULL && rule != NULL); + KASSERT((rule != NULL) && (rule->ruleset != NULL)); + ruleset = rule->ruleset; pf_rm_rule(ruleset->rules.active.ptr, rule); ruleset->rules.active.rcount--; @@ -322,16 +323,6 @@ pf_purge_rule(struct pf_ruleset *ruleset rule->nr = nr++; ruleset->rules.active.ticket++; 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) - rule->nr = nr++; - aruleset->rules.active.ticket++; - pf_calc_skip_steps(aruleset->rules.active.ptr); - } } u_int16_t @@ -783,6 +774,9 @@ pf_commit_rules(u_int32_t ticket, char * int s, error; u_int32_t old_rcount; + /* Make sure any expired rules get removed from active rules first. */ + pf_purge_expired_rules(1); + rs = pf_find_ruleset(anchor); if (rs == NULL || !rs->rules.inactive.open || ticket != rs->rules.inactive.ticket) @@ -1217,6 +1211,7 @@ pfioctl(dev_t dev, u_long cmd, caddr_t a } TAILQ_INSERT_TAIL(ruleset->rules.inactive.ptr, rule, entries); + rule->ruleset = ruleset; ruleset->rules.inactive.rcount++; break; } @@ -1273,6 +1268,8 @@ pfioctl(dev_t dev, u_long cmd, caddr_t a pr->rule.rcv_kif = NULL; pr->rule.anchor = NULL; pr->rule.overload_tbl = NULL; + bzero(&pr->rule.gcle, sizeof(pr->rule.gcle)); + pr->rule.ruleset = NULL; if (pf_anchor_copyout(ruleset, rule, pr)) { error = EBUSY; break; diff -r 8006a1eca673 src/sys/net/pfvar.h --- a/src/sys/net/pfvar.h Sat Sep 03 14:19:17 2016 +0200 +++ b/src/sys/net/pfvar.h Sat Sep 03 16:26:08 2016 +0200 @@ -571,6 +571,10 @@ struct pf_rule { struct pf_addr addr; u_int16_t port; } divert, divert_packet; + + SLIST_ENTRY(pf_rule) gcle; + struct pf_ruleset *ruleset; + time_t exptime; }; /* rule flags */ @@ -589,6 +593,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 */ @@ -1666,6 +1671,7 @@ extern void pf_calc_skip_steps(struct extern void pf_purge_thread(void *); extern void pf_purge_expired_src_nodes(int); extern void pf_purge_expired_states(u_int32_t); +extern void pf_purge_expired_rules(int); extern void pf_remove_state(struct pf_state *); extern void pf_remove_divert_state(struct pf_state_key *); extern void pf_free_state(struct pf_state *); @@ -1695,9 +1701,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 *,