On Mon, Nov 07, 2022 at 06:53:47PM +0100, Alexandr Nedvedicky wrote:
> Hello,
>
> updated diff. It buys suggestions from Klemens:
>
> pf.conf(5) section which covers once rules reads as follows:
>
> once Creates a one shot rule. The first matching packet marks rule as
> expired. The expired rule is never evaluated then. pfctl(8)
> does not report expired rules unless run in verbose mode ('-vv').
> In verbose mode pfctl(8) appends '# expired' to note the once
> rule which got hit by packet other already.
Wording and markup can be improved, but I don't want to block this
change with boring churn, so I'm happy to do that later.
>
> this how pfctl reports expired once rules now:
>
> pf# pfctl -a test/foo/bar -vv -sr
> @0 pass out proto tcp from any to any port = 80 flags S/SA once #
> expired
> [ Evaluations: 25 Packets: 5489 Bytes: 4936817
> States: 0 ]
> [ Inserted: uid 0 pid 2768 State Creations: 1 ]
>
> updated diff is below.
OK kn
>
> thanks and
> regards
> sashan
>
> --------8<---------------8<---------------8<------------------8<--------
> diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
> index 6f39ad72384..fbe19747fc6 100644
> --- a/sbin/pfctl/pfctl_parser.c
> +++ b/sbin/pfctl/pfctl_parser.c
> @@ -1148,6 +1148,9 @@ print_rule(struct pf_rule *r, const char *anchor_call,
> int opts)
> printf(" ");
> print_pool(&r->route, 0, 0, r->af, PF_POOL_ROUTE, verbose);
> }
> +
> + if (r->rule_flag & PFRULE_EXPIRED)
> + printf(" # expired");
> }
>
> void
> diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5
> index 3e5a17acb95..b98b9f2fd9c 100644
> --- a/share/man/man5/pf.conf.5
> +++ b/share/man/man5/pf.conf.5
> @@ -661,10 +661,14 @@ When the rate is exceeded, all ICMP is blocked until
> the rate falls below
> 100 per 10 seconds again.
> .Pp
> .It Cm 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.
> +Creates a one shot rule. The first matching packet marks rule as expired.
> +The expired rule is never evaluated then.
> +.Xr pfctl 8
> +does not report expired rules unless run in verbose mode ('-vv'). In verbose
> +mode
> +.Xr pfctl 8
> +appends '# expired' to note the once rule which got hit by packet other
> +already.
> .Pp
> .It Cm probability Ar number Ns %
> A probability attribute can be attached to a rule,
> diff --git a/sys/net/pf.c b/sys/net/pf.c
> index 2c6124e74f2..6295b4eb9d7 100644
> --- a/sys/net/pf.c
> +++ b/sys/net/pf.c
> @@ -313,9 +313,6 @@ RB_GENERATE(pf_state_tree, pf_state_key, entry,
> pf_state_compare_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)
> {
> @@ -1478,23 +1475,6 @@ pf_state_import(const struct pfsync_state *sp, int
> flags)
>
> /* END state table stuff */
>
> -void
> -pf_purge_expired_rules(void)
> -{
> - struct pf_rule *r;
> -
> - PF_ASSERT_LOCKED();
> -
> - if (SLIST_EMPTY(&pf_rule_gcl))
> - return;
> -
> - 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);
> - }
> -}
> -
> void pf_purge_states(void *);
> struct task pf_purge_states_task =
> TASK_INITIALIZER(pf_purge_states, NULL);
> @@ -1588,7 +1568,6 @@ pf_purge(void *null)
> PF_LOCK();
>
> pf_purge_expired_src_nodes();
> - pf_purge_expired_rules();
>
> PF_UNLOCK();
>
> @@ -3916,8 +3895,11 @@ pf_match_rule(struct pf_test_ctx *ctx, struct
> pf_ruleset *ruleset)
> struct pf_rule *save_a;
> struct pf_ruleset *save_aruleset;
>
> +retry:
> r = TAILQ_FIRST(ruleset->rules.active.ptr);
> while (r != NULL) {
> + PF_TEST_ATTRIB(r->rule_flag & PFRULE_EXPIRED,
> + TAILQ_NEXT(r, entries));
> r->evaluations++;
> PF_TEST_ATTRIB(
> (pfi_kif_match(r->kif, ctx->pd->kif) == r->ifnot),
> @@ -4042,6 +4024,19 @@ pf_match_rule(struct pf_test_ctx *ctx, struct
> pf_ruleset *ruleset)
> if (r->tag)
> ctx->tag = r->tag;
> if (r->anchor == NULL) {
> +
> + if (r->rule_flag & PFRULE_ONCE) {
> + u_int32_t rule_flag;
> +
> + rule_flag = r->rule_flag;
> + if (((rule_flag & PFRULE_EXPIRED) == 0) &&
> + atomic_cas_uint(&r->rule_flag, rule_flag,
> + rule_flag | PFRULE_EXPIRED) == rule_flag)
> + r->exptime = gettime();
> + else
> + goto retry;
> + }
> +
> if (r->action == PF_MATCH) {
> if ((ctx->ri = pool_get(&pf_rule_item_pl,
> PR_NOWAIT)) == NULL) {
> @@ -4253,13 +4248,6 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm,
> struct pf_state **sm,
> if (r->action == PF_DROP)
> goto cleanup;
>
> - /*
> - * If an expired "once" rule has not been purged, drop any new matching
> - * packets.
> - */
> - if (r->rule_flag & PFRULE_EXPIRED)
> - goto cleanup;
> -
> pf_tag_packet(pd->m, ctx.tag, ctx.act.rtableid);
> if (ctx.act.rtableid >= 0 &&
> rtable_l2(ctx.act.rtableid) != pd->rdomain)
> @@ -4330,22 +4318,6 @@ pf_test_rule(struct pf_pdesc *pd, struct pf_rule **rm,
> struct pf_state **sm,
> m_copyback(pd->m, pd->off, pd->hdrlen, &pd->hdr, M_NOWAIT);
> }
>
> - if (r->rule_flag & PFRULE_ONCE) {
> - u_int32_t rule_flag;
> -
> - /*
> - * Use atomic_cas() to determine a clear winner, which will
> - * insert an expired rule to gcl.
> - */
> - rule_flag = r->rule_flag;
> - if (((rule_flag & PFRULE_EXPIRED) == 0) &&
> - atomic_cas_uint(&r->rule_flag, rule_flag,
> - rule_flag | PFRULE_EXPIRED) == rule_flag) {
> - r->exptime = gettime();
> - SLIST_INSERT_HEAD(&pf_rule_gcl, r, gcle);
> - }
> - }
> -
> #if NPFSYNC > 0
> if (*sm != NULL && !ISSET((*sm)->state_flags, PFSTATE_NOSYNC) &&
> pd->dir == PF_OUT && pfsync_up()) {
> diff --git a/sys/net/pf_ioctl.c b/sys/net/pf_ioctl.c
> index b5d3261f8d1..6744fc022fb 100644
> --- a/sys/net/pf_ioctl.c
> +++ b/sys/net/pf_ioctl.c
> @@ -348,27 +348,6 @@ pf_rm_rule(struct pf_rulequeue *rulequeue, struct
> pf_rule *rule)
> pool_put(&pf_rule_pl, rule);
> }
>
> -void
> -pf_purge_rule(struct pf_rule *rule)
> -{
> - u_int32_t nr = 0;
> - struct pf_ruleset *ruleset;
> -
> - KASSERT((rule != NULL) && (rule->ruleset != NULL));
> - ruleset = rule->ruleset;
> -
> - pf_rm_rule(ruleset->rules.active.ptr, rule);
> - ruleset->rules.active.rcount--;
> - TAILQ_FOREACH(rule, ruleset->rules.active.ptr, entries)
> - rule->nr = nr++;
> - ruleset->rules.active.ticket++;
> - pf_calc_skip_steps(ruleset->rules.active.ptr);
> - pf_remove_if_empty_ruleset(ruleset);
> -
> - if (ruleset == &pf_main_ruleset)
> - pf_calc_chksum(ruleset);
> -}
> -
> u_int16_t
> tagname2tag(struct pf_tags *head, char *tagname, int create)
> {
> @@ -837,9 +816,6 @@ pf_commit_rules(u_int32_t ticket, char *anchor)
> struct pf_rulequeue *old_rules;
> u_int32_t old_rcount;
>
> - /* Make sure any expired rules get removed from active rules first. */
> - pf_purge_expired_rules();
> -
> rs = pf_find_ruleset(anchor);
> if (rs == NULL || !rs->rules.inactive.open ||
> ticket != rs->rules.inactive.ticket)
> @@ -1447,7 +1423,6 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags,
> struct proc *p)
> }
> TAILQ_INSERT_TAIL(ruleset->rules.inactive.ptr,
> rule, entries);
> - rule->ruleset = ruleset;
> ruleset->rules.inactive.rcount++;
> PF_UNLOCK();
> NET_UNLOCK();
> @@ -1521,8 +1496,6 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags,
> struct proc *p)
> pr->rule.anchor = NULL;
> pr->rule.overload_tbl = NULL;
> pr->rule.pktrate.limit /= PF_THRESHOLD_MULT;
> - memset(&pr->rule.gcle, 0, sizeof(pr->rule.gcle));
> - pr->rule.ruleset = NULL;
> if (pf_anchor_copyout(ruleset, rule, pr)) {
> error = EBUSY;
> PF_UNLOCK();
> @@ -1713,7 +1686,6 @@ pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags,
> struct proc *p)
> ruleset->rules.active.ptr,
> oldrule, newrule, entries);
> ruleset->rules.active.rcount++;
> - newrule->ruleset = ruleset;
> }
>
> nr = 0;
> diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
> index 91b309ab729..bf6a91131a8 100644
> --- a/sys/net/pfvar.h
> +++ b/sys/net/pfvar.h
> @@ -599,8 +599,6 @@ struct pf_rule {
> u_int8_t type;
> } divert;
>
> - SLIST_ENTRY(pf_rule) gcle;
> - struct pf_ruleset *ruleset;
> time_t exptime;
> };
>
>