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 {
