The branch main has been updated by kp:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=8716d8c7d97eec231820ecd1dc50c67beb95d58c

commit 8716d8c7d97eec231820ecd1dc50c67beb95d58c
Author:     Kristof Provost <[email protected]>
AuthorDate: 2026-01-12 19:37:08 +0000
Commit:     Kristof Provost <[email protected]>
CommitDate: 2026-01-14 06:44:43 +0000

    pf: configurable action on limiter exceeded
    
    This change extends pf(4) limiters so administrator
    can specify action the rule executes when limit is
    reached. By default when limit is reached the limiter
    overrides action specified by rule to no-match.
    If administrator wants to block packet instead then
    rule with limiter should be changed to:
    
       pass in from any to any state limiter test (block)
    
    OK dlg@
    
    Obtained from:  OpenBSD, sashan <[email protected]>, 04394254d9
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 lib/libpfctl/libpfctl.c          | 12 ++++--
 lib/libpfctl/libpfctl.h          | 10 ++++-
 sbin/pfctl/parse.y               | 83 +++++++++++++++++++++++++++-------------
 sbin/pfctl/pfctl_parser.c        | 12 ++++--
 sbin/pfctl/tests/files/pf1076.ok |  2 +-
 sbin/pfctl/tests/files/pf1077.ok |  2 +-
 share/man/man5/pf.conf.5         | 28 ++++++++++----
 sys/net/pfvar.h                  | 11 +++++-
 sys/netpfil/pf/pf.c              | 43 +++++++++++++++++----
 sys/netpfil/pf/pf.h              |  5 +++
 sys/netpfil/pf/pf_ioctl.c        | 20 ++++++++++
 sys/netpfil/pf/pf_nl.c           | 12 ++++--
 sys/netpfil/pf/pf_nl.h           |  6 ++-
 13 files changed, 183 insertions(+), 63 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index a5abe1cadd64..63f61932519c 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -1313,8 +1313,10 @@ snl_add_msg_attr_pf_rule(struct snl_writer *nw, uint32_t 
type, const struct pfct
        snl_add_msg_attr_ip6(nw, PF_RT_DIVERT_ADDRESS, &r->divert.addr.v6);
        snl_add_msg_attr_u16(nw, PF_RT_DIVERT_PORT, r->divert.port);
 
-       snl_add_msg_attr_u8(nw, PF_RT_STATE_LIMIT, r->statelim);
-       snl_add_msg_attr_u8(nw, PF_RT_SOURCE_LIMIT, r->sourcelim);
+       snl_add_msg_attr_u8(nw, PF_RT_STATE_LIMIT, r->statelim.id);
+       snl_add_msg_attr_u32(nw, PF_RT_STATE_LIMIT_ACTION, 
r->statelim.limiter_action);
+       snl_add_msg_attr_u8(nw, PF_RT_SOURCE_LIMIT, r->sourcelim.id);
+       snl_add_msg_attr_u32(nw, PF_RT_SOURCE_LIMIT_ACTION, 
r->sourcelim.limiter_action);
 
        snl_end_attr_nested(nw, off);
 }
@@ -1707,8 +1709,10 @@ static struct snl_attr_parser ap_getrule[] = {
        { .type = PF_RT_TYPE_2, .off = _OUT(r.type), .cb = snl_attr_get_uint16 
},
        { .type = PF_RT_CODE_2, .off = _OUT(r.code), .cb = snl_attr_get_uint16 
},
        { .type = PF_RT_EXPTIME, .off = _OUT(r.exptime), .cb = 
snl_attr_get_time_t },
-       { .type = PF_RT_STATE_LIMIT, .off = _OUT(r.statelim), .cb = 
snl_attr_get_uint8 },
-       { .type = PF_RT_SOURCE_LIMIT, .off = _OUT(r.sourcelim), .cb = 
snl_attr_get_uint8 },
+       { .type = PF_RT_STATE_LIMIT, .off = _OUT(r.statelim.id), .cb = 
snl_attr_get_uint8 },
+       { .type = PF_RT_SOURCE_LIMIT, .off = _OUT(r.sourcelim.id), .cb = 
snl_attr_get_uint8 },
+       { .type = PF_RT_STATE_LIMIT_ACTION, .off = 
_OUT(r.statelim.limiter_action), .cb = snl_attr_get_uint32 },
+       { .type = PF_RT_SOURCE_LIMIT_ACTION, .off = 
_OUT(r.sourcelim.limiter_action), .cb = snl_attr_get_uint32 },
 };
 #undef _OUT
 SNL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, snl_f_p_empty, 
ap_getrule);
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 670688893a6a..d55267e56b4c 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -249,8 +249,14 @@ struct pfctl_rule {
        struct pf_rule_gid       gid;
        char                     rcv_ifname[IFNAMSIZ];
        bool                     rcvifnot;
-       uint8_t                  statelim;
-       uint8_t                  sourcelim;
+       struct {
+               uint8_t          id;
+               int              limiter_action;
+       }                        statelim;
+       struct {
+               uint8_t          id;
+               int              limiter_action;
+       }                        sourcelim;
 
        uint32_t                 rule_flag;
        uint8_t                  action;
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)
diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5
index 6a778eed2214..aa3899e48596 100644
--- a/share/man/man5/pf.conf.5
+++ b/share/man/man5/pf.conf.5
@@ -27,7 +27,7 @@
 .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd December 30, 2025
+.Dd January 12, 2026
 .Dt PF.CONF 5
 .Os
 .Sh NAME
@@ -2365,20 +2365,28 @@ For example, the following rule will drop 20% of 
incoming ICMP packets:
 .Bd -literal -offset indent
 block in proto icmp probability 20%
 .Ed
-.It Cm state limiter Ar name
+.It Cm state limiter Ar name Oo Cm (limiter options) Oc
 Use the specified state limiter to restrict the creation of states
 by this rule.
-If capacity is not available, the rule does not match and evaluation
-of the ruleset continues.
+By default if capacity is not available, the rule is ignored
+and ruleset evaluation continues with next rule..
+Use
+.Ic block
+option to change default behavior such packet is blocked
+when limit is reached.
 See the
 .Sx State Limiters
 section for more information.
 .Pp
-.It Cm source limiter Ar name
+.It Cm source limiter Ar name Oo Cm (limiter options) Oc
 Use the specified source limiter to restrict the creation of states
 by this rule.
-If capacity is not available, the rule does not match and evaluation
-of the ruleset continues.
+By default if capacity is not available, the rule is ignored
+and ruleset evaluation continues with next rule..
+Use
+.Ic block
+option to change default behavior such packet is blocked
+when limit is reached.
 See the
 .Sx Source Limiters
 section for more information.
@@ -3614,7 +3622,10 @@ filteropt      = user | group | flags | icmp-type | 
icmp6-type | "tos" tos |
                  "max-pkt-size" number |
                  "queue" ( string | "(" string [ [ "," ] string ] ")" ) |
                  "rtable" number | "probability" number"%" | "prio" number |
-                 "state limiter" name | "source limiter" name |
+                 "state limiter" name |
+                 "state limiter" name "(" limiter-opts ")" |
+                 "source limiter" name |
+                 "source limiter" name "(" limiter-opts ")" | "prio" number |
                  "dnpipe" ( number | "(" number "," number ")" ) |
                  "dnqueue" ( number | "(" number "," number ")" ) |
                  "ridentifier" number |
@@ -3794,6 +3805,7 @@ realtime-sc    = "realtime" sc-spec
 upperlimit-sc  = "upperlimit" sc-spec
 sc-spec        = ( bandwidth-spec |
                  "(" bandwidth-spec number bandwidth-spec ")" )
+limiter-opts   = "block" | "no-match"
 include        = "include" filename
 .Ed
 .Sh FILES
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 5329c5ebdd9e..eb17c4ff5ef0 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -896,8 +896,14 @@ struct pf_krule {
        u_int8_t                 set_prio[2];
        sa_family_t              naf;
        u_int8_t                 rcvifnot;
-       uint8_t                  statelim;
-       uint8_t                  sourcelim;
+       struct {
+               uint8_t          id;
+               int              limiter_action;
+       }                        statelim;
+       struct {
+               uint8_t          id;
+               int              limiter_action;
+       }                        sourcelim;
 
        struct {
                struct pf_addr          addr;
@@ -1433,6 +1439,7 @@ struct pf_test_ctx {
        int                      state_icmp;
        int                      tag;
        int                      rewrite;
+       int                      limiter_drop;
        u_short                  reason;
        struct pf_src_node      *sns[PF_SN_MAX];
        struct pf_krule         *nr;
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 79948b218428..13e2f5bb77f2 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -6105,8 +6105,8 @@ pf_match_rule(struct pf_test_ctx *ctx, struct pf_kruleset 
*ruleset,
                    pf_osfp_fingerprint(pd, ctx->th),
                    r->os_fingerprint)),
                        TAILQ_NEXT(r, entries));
-               if (r->statelim != PF_STATELIM_ID_NONE) {
-                       stlim = pf_statelim_find(r->statelim);
+               if (r->statelim.id != PF_STATELIM_ID_NONE) {
+                       stlim = pf_statelim_find(r->statelim.id);
 
                        /*
                         * Treat a missing limiter like an exhausted limiter.
@@ -6123,6 +6123,11 @@ pf_match_rule(struct pf_test_ctx *ctx, struct 
pf_kruleset *ruleset,
                                gen = pf_statelim_enter(stlim);
                                stlim->pfstlim_counters.hardlimited++;
                                pf_statelim_leave(stlim, gen);
+                               if (r->statelim.limiter_action == 
PF_LIMITER_BLOCK) {
+                                       ctx->limiter_drop = 1;
+                                       REASON_SET(&ctx->reason, 
PFRES_MAXSTATES);
+                                       break;  /* stop rule processing */
+                               }
                                r = TAILQ_NEXT(r, entries);
                                continue;
                        }
@@ -6140,6 +6145,14 @@ pf_match_rule(struct pf_test_ctx *ctx, struct 
pf_kruleset *ruleset,
                                        gen = pf_statelim_enter(stlim);
                                        stlim->pfstlim_counters.ratelimited++;
                                        pf_statelim_leave(stlim, gen);
+                                       if (r->statelim.limiter_action ==
+                                           PF_LIMITER_BLOCK) {
+                                               ctx->limiter_drop = 1;
+                                               REASON_SET(&ctx->reason,
+                                                   PFRES_MAXSTATES);
+                                               /* stop rule processing */
+                                               break;
+                                       }
                                        r = TAILQ_NEXT(r, entries);
                                        continue;
                                }
@@ -6152,10 +6165,10 @@ pf_match_rule(struct pf_test_ctx *ctx, struct 
pf_kruleset *ruleset,
                        }
                }
 
-               if (r->sourcelim != PF_SOURCELIM_ID_NONE) {
+               if (r->sourcelim.id != PF_SOURCELIM_ID_NONE) {
                        struct pf_source key;
 
-                       srlim = pf_sourcelim_find(r->sourcelim);
+                       srlim = pf_sourcelim_find(r->sourcelim.id);
 
                        /*
                         * Treat a missing pool like an overcommitted pool.
@@ -6177,6 +6190,14 @@ pf_match_rule(struct pf_test_ctx *ctx, struct 
pf_kruleset *ruleset,
                                        gen = pf_sourcelim_enter(srlim);
                                        srlim->pfsrlim_counters.hardlimited++;
                                        pf_sourcelim_leave(srlim, gen);
+                                       if (r->sourcelim.limiter_action ==
+                                           PF_LIMITER_BLOCK) {
+                                               ctx->limiter_drop = 1;
+                                               REASON_SET(&ctx->reason,
+                                                   PFRES_SRCLIMIT);
+                                               /* stop rule processing */
+                                               break;
+                                       }
                                        r = TAILQ_NEXT(r, entries);
                                        continue;
                                }
@@ -6196,6 +6217,14 @@ pf_match_rule(struct pf_test_ctx *ctx, struct 
pf_kruleset *ruleset,
                                                srlim->pfsrlim_counters
                                                    .ratelimited++;
                                                pf_sourcelim_leave(srlim, gen);
+                                               if (r->sourcelim.limiter_action 
==
+                                                   PF_LIMITER_BLOCK) {
+                                                       ctx->limiter_drop = 1;
+                                                       REASON_SET(&ctx->reason,
+                                                           PFRES_SRCLIMIT);
+                                                       /* stop rules */
+                                                       break;
+                                               }
                                                r = TAILQ_NEXT(r, entries);
                                                continue;
                                        }
@@ -6460,10 +6489,8 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
        } else {
                ruleset = &pf_main_ruleset;
                rv = pf_match_rule(&ctx, ruleset, match_rules);
-               if (rv == PF_TEST_FAIL) {
-                       /*
-                        * Reason has been set in pf_match_rule() already.
-                        */
+               if (rv == PF_TEST_FAIL || ctx.limiter_drop == 1) {
+                       REASON_SET(reason, ctx.reason);
                        goto cleanup;
                }
 
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
index 333e5b53b0a8..4c950c7eab9c 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -501,6 +501,11 @@ struct pf_osfp_ioctl {
 #define        PF_ANCHOR_HIWAT         512
 #define        PF_OPTIMIZER_TABLE_PFX  "__automatic_"
 
+enum {
+       PF_LIMITER_NOMATCH,
+       PF_LIMITER_BLOCK
+};
+
 struct pf_rule {
        struct pf_rule_addr      src;
        struct pf_rule_addr      dst;
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index ddca4fae940b..5261b6e5ab41 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2922,6 +2922,23 @@ pf_validate_range(uint8_t op, uint16_t port[2])
        return 0;
 }
 
+static int
+pf_chk_limiter_action(int limiter_action)
+{
+       int rv;
+
+       switch (limiter_action) {
+       case PF_LIMITER_NOMATCH:
+       case PF_LIMITER_BLOCK:
+               rv = 0;
+               break;
+       default:
+               rv = 1;
+       }
+
+       return (rv);
+}
+
 int
 pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
     uint32_t pool_ticket, const char *anchor, const char *anchor_call,
@@ -2946,6 +2963,9 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
                ERROUT_UNLOCKED(EINVAL);
        if (pf_validate_range(rule->dst.port_op, rule->dst.port))
                ERROUT_UNLOCKED(EINVAL);
+       if (pf_chk_limiter_action(rule->statelim.limiter_action) ||
+           pf_chk_limiter_action(rule->sourcelim.limiter_action))
+               ERROUT_UNLOCKED(EINVAL);
 
        if (rule->ifname[0])
                kif = pf_kkif_create(M_WAITOK);
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
index 9522fad10839..7a7655d7d9c8 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -784,8 +784,10 @@ static const struct nlattr_parser nla_p_rule[] = {
        { .type = PF_RT_MAX_PKT_SIZE, .off = _OUT(max_pkt_size), .cb = 
nlattr_get_uint16 },
        { .type = PF_RT_TYPE_2, .off = _OUT(type), .cb = nlattr_get_uint16 },
        { .type = PF_RT_CODE_2, .off = _OUT(code), .cb = nlattr_get_uint16 },
-       { .type = PF_RT_STATE_LIMIT, .off = _OUT(statelim), .cb = 
nlattr_get_uint8 },
-       { .type = PF_RT_SOURCE_LIMIT, .off = _OUT(sourcelim), .cb = 
nlattr_get_uint8 },
+       { .type = PF_RT_STATE_LIMIT, .off = _OUT(statelim.id), .cb = 
nlattr_get_uint8 },
+       { .type = PF_RT_SOURCE_LIMIT, .off = _OUT(sourcelim.id), .cb = 
nlattr_get_uint8 },
+       { .type = PF_RT_STATE_LIMIT_ACTION, .off = 
_OUT(statelim.limiter_action), .cb = nlattr_get_uint32 },
+       { .type = PF_RT_SOURCE_LIMIT_ACTION, .off = 
_OUT(sourcelim.limiter_action), .cb = nlattr_get_uint32 },
 };
 NL_DECLARE_ATTR_PARSER(rule_parser, nla_p_rule);
 #undef _OUT
@@ -1043,8 +1045,10 @@ pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate 
*npt)
        nlattr_add_u64(nw, PF_RT_SRC_NODES_ROUTE, 
counter_u64_fetch(rule->src_nodes[PF_SN_ROUTE]));
        nlattr_add_pf_threshold(nw, PF_RT_PKTRATE, &rule->pktrate);
        nlattr_add_time_t(nw, PF_RT_EXPTIME, time_second - (time_uptime - 
rule->exptime));
-       nlattr_add_u8(nw, PF_RT_STATE_LIMIT, rule->statelim);
-       nlattr_add_u8(nw, PF_RT_SOURCE_LIMIT, rule->sourcelim);
+       nlattr_add_u8(nw, PF_RT_STATE_LIMIT, rule->statelim.id);
+       nlattr_add_u32(nw, PF_RT_STATE_LIMIT_ACTION, 
rule->statelim.limiter_action);
+       nlattr_add_u8(nw, PF_RT_SOURCE_LIMIT, rule->sourcelim.id);
+       nlattr_add_u32(nw, PF_RT_SOURCE_LIMIT_ACTION, 
rule->sourcelim.limiter_action);
 
        error = pf_kanchor_copyout(ruleset, rule, anchor_call, 
sizeof(anchor_call));
        MPASS(error == 0);
diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h
index 696b81f9434e..84e9d3a97303 100644
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -299,8 +299,10 @@ enum pf_rule_type_t {
        PF_RT_TYPE_2            = 84, /* u16 */
        PF_RT_CODE_2            = 85, /* u16 */
        PF_RT_EXPTIME           = 86, /* time_t */
-       PF_RT_STATE_LIMIT       = 87, /* uint8_t */
-       PF_RT_SOURCE_LIMIT      = 88, /* uint8_t */
+       PF_RT_STATE_LIMIT       = 87, /* u8 */
+       PF_RT_SOURCE_LIMIT      = 88, /* u8 */
+       PF_RT_STATE_LIMIT_ACTION = 89, /* u32 */
+       PF_RT_SOURCE_LIMIT_ACTION = 90, /* u32 */
 };
 
 enum pf_addrule_type_t {

Reply via email to