aboutsummaryrefslogtreecommitdiff
path: root/sbin/pfctl
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2026-01-12 19:37:08 +0000
committerKristof Provost <kp@FreeBSD.org>2026-01-14 06:44:43 +0000
commit8716d8c7d97eec231820ecd1dc50c67beb95d58c (patch)
tree2568106cfd450333b70c8803e86ce559837fb695 /sbin/pfctl
parent1ee4405a00d7bcfa5545bba7a78b71cdd4cfdc20 (diff)
Diffstat (limited to 'sbin/pfctl')
-rw-r--r--sbin/pfctl/parse.y83
-rw-r--r--sbin/pfctl/pfctl_parser.c12
-rw-r--r--sbin/pfctl/tests/files/pf1076.ok2
-rw-r--r--sbin/pfctl/tests/files/pf1077.ok2
4 files changed, 66 insertions, 33 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index ded74a6391f1..67e0d30890a8 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -257,6 +257,11 @@ struct redirspec {
bool binat;
};
+struct limiterspec {
+ uint32_t id;
+ int limiter_action;
+};
+
static struct filter_opts {
int marker;
#define FOM_FLAGS 0x0001
@@ -287,8 +292,8 @@ static struct filter_opts {
u_int32_t tos;
u_int32_t prob;
u_int32_t ridentifier;
- u_int32_t statelim;
- u_int32_t sourcelim;
+ struct limiterspec statelim;
+ struct limiterspec sourcelim;
struct {
int action;
struct node_state_opt *options;
@@ -566,6 +571,7 @@ typedef struct {
struct statelim_opts *statelim_opts;
struct sourcelim_opts *sourcelim_opts;
struct pfctl_watermarks *watermarks;
+ struct limiterspec limiterspec;
} v;
int lineno;
} YYSTYPE;
@@ -600,7 +606,7 @@ int parseport(char *, struct range *r, int);
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
%token DIVERTTO DIVERTREPLY BRIDGE_TO RECEIVEDON NE LE GE AFTO NATTO RDRTO
%token BINATTO MAXPKTRATE MAXPKTSIZE IPV6NH
-%token LIMITER ID RATE SOURCE ENTRIES ABOVE BELOW MASK
+%token LIMITER ID RATE SOURCE ENTRIES ABOVE BELOW MASK NOMATCH
%token <v.string> STRING
%token <v.number> NUMBER
%token <v.i> PORTBINARY
@@ -664,8 +670,8 @@ int parseport(char *, struct range *r, int);
%type <v.bridge_to> bridge
%type <v.mac> xmac mac mac_list macspec
%type <v.string> statelim_nm sourcelim_nm
-%type <v.number> statelim_id sourcelim_id
-%type <v.number> statelim_filter_opt sourcelim_filter_opt
+%type <v.number> statelim_id sourcelim_id limiter_opt limiter_opt_spec
+%type <v.limiterspec> statelim_filter_opt sourcelim_filter_opt
%type <v.statelim_opts> statelim_opts
%type <v.sourcelim_opts> sourcelim_opts
%%
@@ -2515,20 +2521,22 @@ statelim_opt : statelim_id {
;
statelim_filter_opt
- : statelim_nm {
+ : STATE LIMITER STRING limiter_opt_spec {
struct pfctl_statelim *stlim;
- stlim = pfctl_get_statelim_nm(pf, $1);
- free($1);
+ stlim = pfctl_get_statelim_nm(pf, $3);
+ free($3);
if (stlim == NULL) {
yyerror("state limiter not found");
YYERROR;
}
- $$ = stlim->ioc.id;
+ $$.id = stlim->ioc.id;
+ $$.limiter_action = $4;
}
- | STATE LIMITER statelim_id {
- $$ = $3;
+ | STATE LIMITER statelim_id limiter_opt_spec {
+ $$.id = $3;
+ $$.limiter_action = $4;
}
;
@@ -2760,20 +2768,34 @@ sourcelim_opt_below
;
sourcelim_filter_opt
- : sourcelim_nm {
+ : SOURCE LIMITER STRING limiter_opt_spec {
struct pfctl_sourcelim *srlim;
- srlim = pfctl_get_sourcelim_nm(pf, $1);
- free($1);
+ srlim = pfctl_get_sourcelim_nm(pf, $3);
+ free($3);
if (srlim == NULL) {
yyerror("source limiter not found");
YYERROR;
}
- $$ = srlim->ioc.id;
+ $$.id = srlim->ioc.id;
+ $$.limiter_action = $4;
}
- | SOURCE LIMITER sourcelim_id {
- $$ = $3;
+ | SOURCE LIMITER sourcelim_id limiter_opt_spec {
+ $$.id = $3;
+ $$.limiter_action = $4;
+ }
+ ;
+
+limiter_opt_spec: /* empty */ { $$ = PF_LIMITER_NOMATCH; }
+ | '(' limiter_opt ')' { $$ = $2; }
+ ;
+
+limiter_opt: BLOCK {
+ $$ = PF_LIMITER_BLOCK;
+ }
+ | NOMATCH {
+ $$ = PF_LIMITER_NOMATCH;
}
;
@@ -3169,16 +3191,20 @@ pfrule : action dir logquick interface route af proto fromto
filter_opts : {
bzero(&filter_opts, sizeof filter_opts);
- filter_opts.statelim = PF_STATELIM_ID_NONE;
- filter_opts.sourcelim = PF_SOURCELIM_ID_NONE;
+ filter_opts.statelim.id = PF_STATELIM_ID_NONE;
+ filter_opts.statelim.limiter_action = PF_LIMITER_NOMATCH;
+ filter_opts.sourcelim.id = PF_SOURCELIM_ID_NONE;
+ filter_opts.sourcelim.limiter_action = PF_LIMITER_NOMATCH;
filter_opts.rtableid = -1;
}
filter_opts_l
{ $$ = filter_opts; }
| /* empty */ {
bzero(&filter_opts, sizeof filter_opts);
- filter_opts.statelim = PF_STATELIM_ID_NONE;
- filter_opts.sourcelim = PF_SOURCELIM_ID_NONE;
+ filter_opts.statelim.id = PF_STATELIM_ID_NONE;
+ filter_opts.statelim.limiter_action = PF_LIMITER_NOMATCH;
+ filter_opts.sourcelim.id = PF_SOURCELIM_ID_NONE;
+ filter_opts.sourcelim.limiter_action = PF_LIMITER_NOMATCH;
filter_opts.rtableid = -1;
$$ = filter_opts;
}
@@ -3323,14 +3349,14 @@ filter_opt : USER uids {
filter_opts.prob = 1;
}
| statelim_filter_opt {
- if (filter_opts.statelim != PF_STATELIM_ID_NONE) {
+ if (filter_opts.statelim.id != PF_STATELIM_ID_NONE) {
yyerror("state limiter already specified");
YYERROR;
}
filter_opts.statelim = $1;
}
| sourcelim_filter_opt {
- if (filter_opts.sourcelim != PF_SOURCELIM_ID_NONE) {
+ if (filter_opts.sourcelim.id != PF_SOURCELIM_ID_NONE) {
yyerror("source limiter already specified");
YYERROR;
}
@@ -7175,6 +7201,7 @@ lookup(char *s)
{ "nat-to", NATTO},
{ "no", NO},
{ "no-df", NODF},
+ { "no-match", NOMATCH},
{ "no-route", NOROUTE},
{ "no-sync", NOSYNC},
{ "on", ON},
@@ -8202,11 +8229,11 @@ filteropts_to_rule(struct pfctl_rule *r, struct filter_opts *opts)
r->rule_flag |= PFRULE_ONCE;
}
- if (opts->statelim != PF_STATELIM_ID_NONE && r->action != PF_PASS) {
+ if (opts->statelim.id != PF_STATELIM_ID_NONE && r->action != PF_PASS) {
yyerror("state limiter only applies to pass rules");
return (1);
}
- if (opts->sourcelim != PF_SOURCELIM_ID_NONE && r->action != PF_PASS) {
+ if (opts->sourcelim.id != PF_SOURCELIM_ID_NONE && r->action != PF_PASS) {
yyerror("source limiter only applies to pass rules");
return (1);
}
@@ -8215,8 +8242,10 @@ filteropts_to_rule(struct pfctl_rule *r, struct filter_opts *opts)
r->pktrate.limit = opts->pktrate.limit;
r->pktrate.seconds = opts->pktrate.seconds;
r->prob = opts->prob;
- r->statelim = opts->statelim;
- r->sourcelim = opts->sourcelim;
+ r->statelim.id = opts->statelim.id;
+ r->statelim.limiter_action = opts->statelim.limiter_action;
+ r->sourcelim.id = opts->sourcelim.id;
+ r->sourcelim.limiter_action = opts->sourcelim.limiter_action;
r->rtableid = opts->rtableid;
r->ridentifier = opts->ridentifier;
r->max_pkt_size = opts->max_pkt_size;
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index f85c50652944..78a1034a3b43 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -1112,7 +1112,7 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int opts, int numeric)
}
printf(" probability %s%%", buf);
}
- if (r->statelim != PF_STATELIM_ID_NONE) {
+ if (r->statelim.id != PF_STATELIM_ID_NONE) {
#if 0 /* XXX need pf to find statelims */
struct pfctl_statelim *stlim =
pfctl_get_statelim_id(pf, r->statelim);
@@ -1121,9 +1121,11 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int opts, int numeric)
printf(" state limiter %s", stlim->ioc.name);
else
#endif
- printf(" state limiter id %u", r->statelim);
+ printf(" state limiter id %u (%s)", r->statelim.id,
+ (r->statelim.limiter_action == PF_LIMITER_BLOCK) ?
+ "block" : "no-match");
}
- if (r->sourcelim != PF_SOURCELIM_ID_NONE) {
+ if (r->sourcelim.id != PF_SOURCELIM_ID_NONE) {
#if 0 /* XXX need pf to find sourcelims */
struct pfctl_sourcelim *srlim =
pfctl_get_sourcelim_id(pf, r->sourcelim);
@@ -1132,7 +1134,9 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int opts, int numeric)
printf(" source limiter %s", srlim->ioc.name);
else
#endif
- printf(" source limiter id %u", r->sourcelim);
+ printf(" source limiter id %u (%s)", r->sourcelim.id,
+ (r->sourcelim.limiter_action == PF_LIMITER_BLOCK) ?
+ "block" : "no-match");
}
ropts = 0;
diff --git a/sbin/pfctl/tests/files/pf1076.ok b/sbin/pfctl/tests/files/pf1076.ok
index def9533b1e60..9f1a8c8fb5cf 100644
--- a/sbin/pfctl/tests/files/pf1076.ok
+++ b/sbin/pfctl/tests/files/pf1076.ok
@@ -1,2 +1,2 @@
state limiter dns-server id 1 limit 1000 rate 1/10
-pass in proto tcp from any to any port = domain flags S/SA keep state state limiter id 1
+pass in proto tcp from any to any port = domain flags S/SA keep state state limiter id 1 (no-match)
diff --git a/sbin/pfctl/tests/files/pf1077.ok b/sbin/pfctl/tests/files/pf1077.ok
index e52afb6bff9c..dc8882e1b87b 100644
--- a/sbin/pfctl/tests/files/pf1077.ok
+++ b/sbin/pfctl/tests/files/pf1077.ok
@@ -1,2 +1,2 @@
source limiter dns-server id 1 limit 2 states 3 rate 4/5 inet mask 16
-pass in proto tcp from any to any port = domain flags S/SA keep state source limiter id 1
+pass in proto tcp from any to any port = domain flags S/SA keep state source limiter id 1 (no-match)