Hi,
PF supports 'one shot rules'. Quoting pf.conf(5) "once - Creates a one
shot rule that will remove itself from an active ruleset after the first
match."
I'd like to simplify pf by removing them, unless there's a compelling
reason not to.
Particularly as there is no 'first match' under concurrent ruleset
evaluation (which we aim at). That obliges the code for one-shot rules to
either serialise evaluation to some degree (hurting performance) or fail
to do what it says on the tin in all cases (hurting correctness). Either
way the code creates surprises and has to do a lot of hoop-jumping.
Thoughts?
cheers,
Richard.
Index: sbin/pfctl/parse.y
===================================================================
RCS file: /cvs/src/sbin/pfctl/parse.y,v
retrieving revision 1.700
diff -u -p -u -p -r1.700 parse.y
--- sbin/pfctl/parse.y 15 Jan 2020 22:38:30 -0000 1.700
+++ sbin/pfctl/parse.y 24 Jan 2020 06:12:06 -0000
@@ -242,7 +242,6 @@ struct filter_opts {
#define FOM_SETTOS 0x0100
#define FOM_SCRUB_TCP 0x0200
#define FOM_SETPRIO 0x0400
-#define FOM_ONCE 0x1000
#define FOM_PRIO 0x2000
#define FOM_SETDELAY 0x4000
struct node_uid *uid;
@@ -487,7 +486,7 @@ int parseport(char *, struct range *r, i
%token BITMASK RANDOM SOURCEHASH ROUNDROBIN LEASTSTATES STATICPORT PROBABILITY
%token WEIGHT BANDWIDTH FLOWS QUANTUM
%token QUEUE PRIORITY QLIMIT RTABLE RDOMAIN MINIMUM BURST PARENT
-%token LOAD RULESET_OPTIMIZATION RTABLE RDOMAIN PRIO ONCE DEFAULT DELAY
+%token LOAD RULESET_OPTIMIZATION RTABLE RDOMAIN PRIO DEFAULT DELAY
%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW MAXPKTRATE
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE
@@ -2163,9 +2162,6 @@ filter_opt : USER uids {
filter_opts.rcv = $3;
filter_opts.rcv->not = $1;
}
- | ONCE {
- filter_opts.marker |= FOM_ONCE;
- }
| MAXPKTRATE NUMBER '/' NUMBER {
if ($2 < 0 || $2 > UINT_MAX ||
$4 < 0 || $4 > UINT_MAX) {
@@ -5086,7 +5082,6 @@ lookup(char *s)
{ "no-route", NOROUTE},
{ "no-sync", NOSYNC},
{ "on", ON},
- { "once", ONCE},
{ "optimization", OPTIMIZATION},
{ "os", OS},
{ "out", OUT},
@@ -5909,14 +5904,6 @@ rdomain_exists(u_int rdomain)
int
filteropts_to_rule(struct pf_rule *r, struct filter_opts *opts)
{
- if (opts->marker & FOM_ONCE) {
- if ((r->action != PF_PASS && r->action != PF_DROP) ||
r->anchor) {
- yyerror("'once' only applies to pass/block rules");
- return (1);
- }
- r->rule_flag |= PFRULE_ONCE;
- }
-
r->keep_state = opts->keep.action;
r->pktrate.limit = opts->pktrate.limit;
r->pktrate.seconds = opts->pktrate.seconds;
Index: sbin/pfctl/pfctl_parser.c
===================================================================
RCS file: /cvs/src/sbin/pfctl/pfctl_parser.c,v
retrieving revision 1.342
diff -u -p -u -p -r1.342 pfctl_parser.c
--- sbin/pfctl/pfctl_parser.c 17 Oct 2019 21:54:28 -0000 1.342
+++ sbin/pfctl/pfctl_parser.c 24 Jan 2020 06:12:06 -0000
@@ -1085,8 +1085,6 @@ print_rule(struct pf_rule *r, const char
printf(" allow-opts");
if (r->label[0])
printf(" label \"%s\"", r->label);
- if (r->rule_flag & PFRULE_ONCE)
- printf(" once");
if (r->tagname[0])
printf(" tag %s", r->tagname);
if (r->match_tagname[0]) {
Index: share/man/man5/pf.conf.5
===================================================================
RCS file: /cvs/src/share/man/man5/pf.conf.5,v
retrieving revision 1.583
diff -u -p -u -p -r1.583 pf.conf.5
--- share/man/man5/pf.conf.5 17 Jan 2020 09:07:35 -0000 1.583
+++ share/man/man5/pf.conf.5 24 Jan 2020 06:12:08 -0000
@@ -639,12 +639,6 @@ pass in proto icmp max-pkt-rate 100/10
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.
-.Pp
.It Cm probability Ar number Ns %
A probability attribute can be attached to a rule,
with a value set between 0 and 100%,
Index: sys/net/pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.1091
diff -u -p -u -p -r1.1091 pf.c
--- sys/net/pf.c 17 Nov 2019 08:25:05 -0000 1.1091
+++ sys/net/pf.c 24 Jan 2020 06:12:13 -0000
@@ -315,9 +315,6 @@ 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)
{
@@ -1233,23 +1230,6 @@ pf_state_export(struct pfsync_state *sp,
/* 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_timeout(void *unused)
{
task_add(net_tq(0), &pf_purge_task);
@@ -1277,7 +1257,6 @@ pf_purge(void *xnloops)
/* purge other expired types every PFTM_INTERVAL seconds */
if (++(*nloops) >= pf_default_rule.timeout[PFTM_INTERVAL]) {
pf_purge_expired_src_nodes();
- pf_purge_expired_rules();
}
PF_UNLOCK();
@@ -3964,22 +3943,6 @@ pf_test_rule(struct pf_pdesc *pd, struct
return (PF_DEFER);
}
#endif /* NPFSYNC > 0 */
-
- 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 = time_second;
- SLIST_INSERT_HEAD(&pf_rule_gcl, r, gcle);
- }
- }
return (action);
Index: sys/net/pf_ioctl.c
===================================================================
RCS file: /cvs/src/sys/net/pf_ioctl.c,v
retrieving revision 1.348
diff -u -p -u -p -r1.348 pf_ioctl.c
--- sys/net/pf_ioctl.c 8 Jan 2020 21:48:59 -0000 1.348
+++ sys/net/pf_ioctl.c 24 Jan 2020 06:12:17 -0000
@@ -321,24 +321,6 @@ pf_rm_rule(struct pf_rulequeue *rulequeu
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);
-}
-
u_int16_t
tagname2tag(struct pf_tags *head, char *tagname, int create)
{
@@ -808,9 +790,6 @@ pf_commit_rules(u_int32_t ticket, char *
int error;
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)
@@ -1280,7 +1259,6 @@ 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++;
PF_UNLOCK();
break;
@@ -1346,8 +1324,6 @@ pfioctl(dev_t dev, u_long cmd, caddr_t a
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();
Index: sys/net/pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.493
diff -u -p -u -p -r1.493 pfvar.h
--- sys/net/pfvar.h 17 Nov 2019 08:25:05 -0000 1.493
+++ sys/net/pfvar.h 24 Jan 2020 06:12:19 -0000
@@ -596,10 +596,6 @@ struct pf_rule {
u_int16_t port;
u_int8_t type;
} divert;
-
- SLIST_ENTRY(pf_rule) gcle;
- struct pf_ruleset *ruleset;
- time_t exptime;
};
/* rule flags */
@@ -617,7 +613,6 @@ struct pf_rule {
#define PFRULE_IFBOUND 0x00010000 /* if-bound */
#define PFRULE_STATESLOPPY 0x00020000 /* sloppy state tracking */
#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 */
@@ -1704,7 +1699,6 @@ extern void
pf_tbladdr_copyout(struct
extern void pf_calc_skip_steps(struct pf_rulequeue *);
extern void pf_purge_expired_src_nodes(void);
extern void pf_purge_expired_states(u_int32_t);
-extern void pf_purge_expired_rules(void);
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 *);
@@ -1734,7 +1728,6 @@ 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_rule *);
struct pf_divert *pf_find_divert(struct mbuf *);
int pf_setup_pdesc(struct pf_pdesc *, sa_family_t,
int, struct pfi_kif *, struct mbuf *,
Index: usr.sbin/tftp-proxy/filter.c
===================================================================
RCS file: /cvs/src/usr.sbin/tftp-proxy/filter.c,v
retrieving revision 1.2
diff -u -p -u -p -r1.2 filter.c
--- usr.sbin/tftp-proxy/filter.c 21 Jan 2015 21:50:33 -0000 1.2
+++ usr.sbin/tftp-proxy/filter.c 24 Jan 2020 06:12:34 -0000
@@ -208,9 +208,6 @@ prepare_rule(u_int32_t id, struct sockad
}
pfr.rule.dst.port_op = PF_OP_EQ;
pfr.rule.dst.port[0] = htons(d_port);
-#ifdef notyet
- pfr.rule.rule_flag = PFRULE_ONCE;
-#endif
pfr.rule.action = PF_PASS;
pfr.rule.quick = 1;
pfr.rule.log = rule_log;