diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c index a5abe1cadd6..63f61932519 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 670688893a6..d55267e56b4 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 ded74a6391f..67e0d30890a 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 STRING %token NUMBER %token PORTBINARY @@ -664,8 +670,8 @@ int parseport(char *, struct range *r, int); %type bridge %type xmac mac mac_list macspec %type statelim_nm sourcelim_nm -%type statelim_id sourcelim_id -%type statelim_filter_opt sourcelim_filter_opt +%type statelim_id sourcelim_id limiter_opt limiter_opt_spec +%type statelim_filter_opt sourcelim_filter_opt %type statelim_opts %type 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 f85c5065294..78a1034a3b4 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 def9533b1e6..9f1a8c8fb5c 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 e52afb6bff9..dc8882e1b87 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 6a778eed221..aa3899e4859 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 5329c5ebdd9..eb17c4ff5ef 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 79948b21842..13e2f5bb77f 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 333e5b53b0a..4c950c7eab9 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 ddca4fae940..5261b6e5ab4 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 9522fad1083..7a7655d7d9c 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 696b81f9434..84e9d3a9730 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 {