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

Reply via email to