f --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index 127e2c257d69..ded74a6391f1 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -72,6 +72,8 @@ #include "pfctl_parser.h" #include "pfctl.h" +#define ISSET(_v, _m) ((_v) & (_m)) + static struct pfctl *pf = NULL; static int debug = 0; static int rulestate = 0; @@ -178,7 +180,8 @@ enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK, PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES, PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK, PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, - PF_STATE_OPT_PFLOW, PF_STATE_OPT_ALLOW_RELATED }; + PF_STATE_OPT_PFLOW, PF_STATE_OPT_ALLOW_RELATED, + PF_STATE_OPT_STATELIM, PF_STATE_OPT_SOURCELIM }; enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE }; @@ -284,6 +287,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 { int action; struct node_state_opt *options; @@ -362,6 +367,51 @@ static struct table_opts { struct node_tinithead init_nodes; } table_opts; +struct statelim_opts { + unsigned int marker; +#define STATELIM_M_ID 0x01 +#define STATELIM_M_LIMIT 0x02 +#define STATELIM_M_RATE 0x04 + + uint32_t id; + char name[PF_STATELIM_NAME_LEN]; + unsigned int limit; + struct { + unsigned int limit; + unsigned int seconds; + } rate; +}; + +static struct statelim_opts statelim_opts; + +struct sourcelim_opts { + unsigned int marker; +#define SOURCELIM_M_ID 0x01 +#define SOURCELIM_M_ENTRIES 0x02 +#define SOURCELIM_M_LIMIT 0x04 +#define SOURCELIM_M_RATE 0x08 +#define SOURCELIM_M_TABLE 0x10 +#define SOURCELIM_M_INET_MASK 0x20 +#define SOURCELIM_M_INET6_MASK 0x40 + + uint32_t id; + unsigned int entries; + unsigned int limit; + struct { + unsigned int limit; + unsigned int seconds; + } rate; + struct { + char name[PF_TABLE_NAME_SIZE]; + unsigned int above; + unsigned int below; + } table; + unsigned int inet_mask; + unsigned int inet6_mask; +}; + +static struct sourcelim_opts sourcelim_opts; + static struct codel_opts codel_opts; static struct node_hfsc_opts hfsc_opts; static struct node_fairq_opts fairq_opts; @@ -513,6 +563,8 @@ typedef struct { struct node_hfsc_opts hfsc_opts; struct node_fairq_opts fairq_opts; struct codel_opts codel_opts; + struct statelim_opts *statelim_opts; + struct sourcelim_opts *sourcelim_opts; struct pfctl_watermarks *watermarks; } v; int lineno; @@ -548,12 +600,13 @@ 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 STRING %token NUMBER %token PORTBINARY %type interface if_list if_item_not if_item %type number icmptype icmp6type uid gid -%type tos not yesno optnodf +%type tos not yesno optnodf sourcelim_opt_below %type probability %type no dir af fragcache optimizer syncookie_val %type sourcetrack flush unaryop statelock @@ -610,12 +663,19 @@ int parseport(char *, struct range *r, int); %type etherfrom etherto %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_opts +%type sourcelim_opts %% ruleset : /* empty */ | ruleset include '\n' | ruleset '\n' | ruleset option '\n' + | ruleset statelim '\n' + | ruleset sourcelim '\n' | ruleset etherrule '\n' | ruleset etheranchorrule '\n' | ruleset scrubrule '\n' @@ -2322,6 +2382,401 @@ qassign_item : STRING { } ; +statelim : statelim_nm statelim_opts { + struct pfctl_statelim *stlim; + size_t len; + + if (!ISSET($2->marker, STATELIM_M_ID)) { + yyerror("id not specified"); + free($1); + YYERROR; + } + if (!ISSET($2->marker, STATELIM_M_LIMIT)) { + yyerror("limit not specified"); + free($1); + YYERROR; + } + + stlim = calloc(1, sizeof(*stlim)); + if (stlim == NULL) + err(1, "state limiter: malloc"); + + len = strlcpy(stlim->ioc.name, $1, + sizeof(stlim->ioc.name)); + free($1); + if (len >= sizeof(stlim->ioc.name)) { + /* abort? */ + YYERROR; + } + + stlim->ioc.id = $2->id; + stlim->ioc.limit = $2->limit; + stlim->ioc.rate.limit = $2->rate.limit; + stlim->ioc.rate.seconds = $2->rate.seconds; + + if (pfctl_add_statelim(pf, stlim) != 0) { + yyerror("state limiter %s id %u" + " already exists", + stlim->ioc.name, stlim->ioc.id); + free(stlim); + YYERROR; + } + } + ; + +statelim_nm : STATE LIMITER string { + size_t len = strlen($3); + if (len < 1) { + yyerror("state limiter name is too short"); + free($3); + YYERROR; + } + if (len >= PF_STATELIM_NAME_LEN) { + yyerror("state limiter name is too long"); + free($3); + YYERROR; + } + $$ = $3; + } + ; + +statelim_id : ID NUMBER { + if ($2 < PF_STATELIM_ID_MIN || + $2 > PF_STATELIM_ID_MAX) { + yyerror("state limiter id %lld: " + "invalid identifier", $2); + YYERROR; + } + + $$ = $2; + } + ; + +statelim_opts : /* empty */ { + yyerror("state limiter missing options"); + YYERROR; + } + | { + memset(&statelim_opts, 0, sizeof(statelim_opts)); + } statelim_opts_l { + $$ = &statelim_opts; + } + ; + +statelim_opts_l : statelim_opts_l statelim_opt + | statelim_opt + ; + +statelim_opt : statelim_id { + if (ISSET(statelim_opts.marker, STATELIM_M_ID)) { + yyerror("id cannot be respecified"); + YYERROR; + } + + statelim_opts.id = $1; + + statelim_opts.marker |= STATELIM_M_ID; + } + | LIMIT NUMBER { + if (ISSET(statelim_opts.marker, STATELIM_M_LIMIT)) { + yyerror("limit cannot be respecified"); + YYERROR; + } + + if ($2 < PF_STATELIM_LIMIT_MIN || + $2 > PF_STATELIM_LIMIT_MAX) { + yyerror("invalid state limiter limit"); + YYERROR; + } + + statelim_opts.limit = $2; + + statelim_opts.marker |= STATELIM_M_LIMIT; + } + | RATE NUMBER '/' NUMBER { + if (ISSET(statelim_opts.marker, STATELIM_M_RATE)) { + yyerror("rate cannot be respecified"); + YYERROR; + } + if ($2 < 1) { + yyerror("invalid rate limit %lld", $2); + YYERROR; + } + if ($4 < 1) { + yyerror("invalid rate seconds %lld", $4); + YYERROR; + } + + statelim_opts.rate.limit = $2; + statelim_opts.rate.seconds = $4; + + statelim_opts.marker |= STATELIM_M_RATE; + } + ; + +statelim_filter_opt + : statelim_nm { + struct pfctl_statelim *stlim; + + stlim = pfctl_get_statelim_nm(pf, $1); + free($1); + if (stlim == NULL) { + yyerror("state limiter not found"); + YYERROR; + } + + $$ = stlim->ioc.id; + } + | STATE LIMITER statelim_id { + $$ = $3; + } + ; + +sourcelim : sourcelim_nm sourcelim_opts { + struct pfctl_sourcelim *srlim; + size_t len; + + if (!ISSET($2->marker, SOURCELIM_M_ID)) { + yyerror("id not specified"); + free($1); + YYERROR; + } + if (!ISSET($2->marker, SOURCELIM_M_ENTRIES)) { + yyerror("entries not specified"); + free($1); + YYERROR; + } + if (!ISSET($2->marker, SOURCELIM_M_LIMIT)) { + yyerror("state limit not specified"); + free($1); + YYERROR; + } + + srlim = calloc(1, sizeof(*srlim)); + if (srlim == NULL) + err(1, "source limiter: malloc"); + + len = strlcpy(srlim->ioc.name, $1, + sizeof(srlim->ioc.name)); + free($1); + if (len >= sizeof(srlim->ioc.name)) { + /* abort? */ + YYERROR; + } + + srlim->ioc.id = $2->id; + srlim->ioc.entries = $2->entries; + srlim->ioc.limit = $2->limit; + srlim->ioc.rate.limit = $2->rate.limit; + srlim->ioc.rate.seconds = $2->rate.seconds; + + if (ISSET($2->marker, SOURCELIM_M_TABLE)) { + if (strlcpy(srlim->ioc.overload_tblname, + $2->table.name, + sizeof(srlim->ioc.overload_tblname)) >= + sizeof(srlim->ioc.overload_tblname)) { + abort(); + } + srlim->ioc.overload_hwm = $2->table.above; + srlim->ioc.overload_lwm = $2->table.below; + } + + srlim->ioc.inet_prefix = $2->inet_mask; + srlim->ioc.inet6_prefix = $2->inet6_mask; + + if (pfctl_add_sourcelim(pf, srlim) != 0) { + yyerror("source limiter %s id %u" + " already exists", + srlim->ioc.name, srlim->ioc.id); + free(srlim); + YYERROR; + } + } + ; + +sourcelim_nm : SOURCE LIMITER string { + size_t len = strlen($3); + if (len < 1) { + yyerror("source limiter name is too short"); + free($3); + YYERROR; + } + if (len >= PF_SOURCELIM_NAME_LEN) { + yyerror("source limiter name is too long"); + free($3); + YYERROR; + } + $$ = $3; + } + ; + +sourcelim_id : ID NUMBER { + if ($2 < PF_SOURCELIM_ID_MIN || + $2 > PF_SOURCELIM_ID_MAX) { + yyerror("source limiter id %lld: " + "invalid identifier", $2); + YYERROR; + } + + $$ = $2; + } + ; + +sourcelim_opts : /* empty */ { + yyerror("source limiter missing options"); + YYERROR; + } + | { + memset(&sourcelim_opts, 0, sizeof(sourcelim_opts)); + sourcelim_opts.inet_mask = 32; + sourcelim_opts.inet6_mask = 128; + } sourcelim_opts_l { + $$ = &sourcelim_opts; + } + ; + +sourcelim_opts_l : sourcelim_opts_l sourcelim_opt + | sourcelim_opt + ; + +sourcelim_opt : sourcelim_id { + if (ISSET(sourcelim_opts.marker, SOURCELIM_M_ID)) { + yyerror("entries cannot be respecified"); + YYERROR; + } + + sourcelim_opts.id = $1; + + sourcelim_opts.marker |= SOURCELIM_M_ID; + } + | ENTRIES NUMBER { + if (ISSET(sourcelim_opts.marker, SOURCELIM_M_ENTRIES)) { + yyerror("entries cannot be respecified"); + YYERROR; + } + + sourcelim_opts.entries = $2; + + sourcelim_opts.marker |= SOURCELIM_M_ENTRIES; + } + | LIMIT NUMBER { + if (ISSET(sourcelim_opts.marker, SOURCELIM_M_LIMIT)) { + yyerror("state limit cannot be respecified"); + YYERROR; + } + + sourcelim_opts.limit = $2; + + sourcelim_opts.marker |= SOURCELIM_M_LIMIT; + } + | RATE NUMBER '/' NUMBER { + if (ISSET(sourcelim_opts.marker, SOURCELIM_M_RATE)) { + yyerror("rate cannot be respecified"); + YYERROR; + } + + sourcelim_opts.rate.limit = $2; + sourcelim_opts.rate.seconds = $4; + + sourcelim_opts.marker |= SOURCELIM_M_RATE; + } + | TABLE '<' STRING '>' ABOVE NUMBER sourcelim_opt_below { + size_t stringlen; + + if (ISSET(sourcelim_opts.marker, SOURCELIM_M_TABLE)) { + free($3); + yyerror("rate cannot be respecified"); + YYERROR; + } + + stringlen = strlcpy(sourcelim_opts.table.name, + $3, sizeof(sourcelim_opts.table.name)); + free($3); + if (stringlen == 0 || + stringlen >= PF_TABLE_NAME_SIZE) { + yyerror("invalid table name"); + YYERROR; + } + + if ($6 < 0) { + yyerror("above limit is invalid"); + YYERROR; + } + if ($7 > $6) { + yyerror("below limit higher than above limit"); + YYERROR; + } + + sourcelim_opts.table.above = $6; + sourcelim_opts.table.below = $7; + + sourcelim_opts.marker |= SOURCELIM_M_TABLE; + } + | INET MASK NUMBER { + if (ISSET(sourcelim_opts.marker, + SOURCELIM_M_INET_MASK)) { + yyerror("inet mask cannot be respecified"); + YYERROR; + } + + if ($3 < 1 || $3 > 32) { + yyerror("inet mask length out of range"); + YYERROR; + } + + sourcelim_opts.inet_mask = $3; + + sourcelim_opts.marker |= SOURCELIM_M_INET_MASK; + } + | INET6 MASK NUMBER { + if (ISSET(sourcelim_opts.marker, + SOURCELIM_M_INET6_MASK)) { + yyerror("inet6 mask cannot be respecified"); + YYERROR; + } + + if ($3 < 1 || $3 > 128) { + yyerror("inet6 mask length out of range"); + YYERROR; + } + + sourcelim_opts.inet6_mask = $3; + + sourcelim_opts.marker |= SOURCELIM_M_INET6_MASK; + } + ; + +sourcelim_opt_below + : /* empty */ { + $$ = 0; + } + | BELOW NUMBER { + if ($2 < 1) { + yyerror("below limit is invalid"); + YYERROR; + } + $$ = $2; + } + ; + +sourcelim_filter_opt + : sourcelim_nm { + struct pfctl_sourcelim *srlim; + + srlim = pfctl_get_sourcelim_nm(pf, $1); + free($1); + if (srlim == NULL) { + yyerror("source limiter not found"); + YYERROR; + } + + $$ = srlim->ioc.id; + } + | SOURCE LIMITER sourcelim_id { + $$ = $3; + } + ; + pfrule : action dir logquick interface route af proto fromto filter_opts { @@ -2562,6 +3017,7 @@ pfrule : action dir logquick interface route af proto fromto } r.timeout[o->data.timeout.number] = o->data.timeout.seconds; + break; } o = o->next; if (!defaults) @@ -2713,12 +3169,16 @@ 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.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.rtableid = -1; $$ = filter_opts; } @@ -2862,6 +3322,20 @@ filter_opt : USER uids { if (filter_opts.prob == 0) filter_opts.prob = 1; } + | statelim_filter_opt { + if (filter_opts.statelim != 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) { + yyerror("source limiter already specified"); + YYERROR; + } + filter_opts.sourcelim = $1; + } | RTABLE NUMBER { if ($2 < 0 || $2 > rt_tableid_max()) { yyerror("invalid rtable id"); @@ -6615,6 +7089,7 @@ lookup(char *s) { /* this has to be sorted always */ static const struct keywords keywords[] = { + { "above", ABOVE}, { "af-to", AFTO}, { "all", ALL}, { "allow-opts", ALLOWOPTS}, @@ -6624,6 +7099,7 @@ lookup(char *s) { "antispoof", ANTISPOOF}, { "any", ANY}, { "bandwidth", BANDWIDTH}, + { "below", BELOW}, { "binat", BINAT}, { "binat-anchor", BINATANCHOR}, { "binat-to", BINATTO}, @@ -6643,6 +7119,7 @@ lookup(char *s) { "drop", DROP}, { "dup-to", DUPTO}, { "endpoint-independent", ENDPI}, + { "entries", ENTRIES}, { "ether", ETHER}, { "fail-policy", FAILPOLICY}, { "fairq", FAIRQ}, @@ -6662,6 +7139,7 @@ lookup(char *s) { "hostid", HOSTID}, { "icmp-type", ICMPTYPE}, { "icmp6-type", ICMP6TYPE}, + { "id", ID}, { "if-bound", IFBOUND}, { "in", IN}, { "include", INCLUDE}, @@ -6673,11 +7151,13 @@ lookup(char *s) { "l3", L3}, { "label", LABEL}, { "limit", LIMIT}, + { "limiter", LIMITER}, { "linkshare", LINKSHARE}, { "load", LOAD}, { "log", LOG}, { "loginterface", LOGINTERFACE}, { "map-e-portset", MAPEPORTSET}, + { "mask", MASK}, { "match", MATCH}, { "matches", MATCHES}, { "max", MAXIMUM}, @@ -6717,6 +7197,7 @@ lookup(char *s) { "quick", QUICK}, { "random", RANDOM}, { "random-id", RANDOMID}, + { "rate", RATE}, { "rdr", RDR}, { "rdr-anchor", RDRANCHOR}, { "rdr-to", RDRTO}, @@ -6741,6 +7222,7 @@ lookup(char *s) { "set-tos", SETTOS}, { "skip", SKIP}, { "sloppy", SLOPPY}, + { "source", SOURCE}, { "source-hash", SOURCEHASH}, { "source-track", SOURCETRACK}, { "state", STATE}, @@ -7720,10 +8202,21 @@ 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) { + yyerror("state limiter only applies to pass rules"); + return (1); + } + if (opts->sourcelim != PF_SOURCELIM_ID_NONE && r->action != PF_PASS) { + yyerror("source limiter only applies to pass rules"); + return (1); + } + r->keep_state = opts->keep.action; 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->rtableid = opts->rtableid; r->ridentifier = opts->ridentifier; r->max_pkt_size = opts->max_pkt_size; diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8 index 58de54cdf923..d3c8b1273b79 100644 --- a/sbin/pfctl/pfctl.8 +++ b/sbin/pfctl/pfctl.8 @@ -24,7 +24,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd August 28, 2025 +.Dd December 30, 2025 .Dt PFCTL 8 .Os .Sh NAME @@ -524,6 +524,26 @@ When used together with interface statistics are also shown. .Fl i can be used to select an interface or a group of interfaces. +.It Cm Stlimiter +Show information about state limiters. +If +.Fl R Ar id +is specified as well, +only the state limiter identified by +.Ar id +is shown. +.It Cm Srclimiter +Show information about source limiters. +If +.Fl R Ar id +is specified as well, +only the state limiter identified by +.Ar id +is shown. +If +.Fl v +is specified, +the address entries for the source pools are shown too. .It Cm all Show all of the above, except for the lists of interfaces and operating system fingerprints. diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c index da27afb0a179..04deccf7e890 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -60,11 +60,14 @@ #include #include #include +#include #include #include "pfctl_parser.h" #include "pfctl.h" +struct pfctl_opt_id; + void usage(void); int pfctl_enable(int, int); int pfctl_disable(int, int); @@ -87,6 +90,7 @@ void pfctl_gateway_kill_states(int, const char *, int); void pfctl_label_kill_states(int, const char *, int); void pfctl_id_kill_states(int, const char *, int); void pfctl_key_kill_states(int, const char *, int); +void pfctl_kill_source(int, const char *, const char *, int); int pfctl_parse_host(char *, struct pf_rule_addr *); void pfctl_init_options(struct pfctl *); int pfctl_load_options(struct pfctl *); @@ -101,6 +105,8 @@ int pfctl_get_pool(int, struct pfctl_pool *, u_int32_t, u_int32_t, int, const char *, int); void pfctl_print_eth_rule_counters(struct pfctl_eth_rule *, int); void pfctl_print_rule_counters(struct pfctl_rule *, int); +int pfctl_show_statelims(int, enum pfctl_show); +int pfctl_show_sourcelims(int, enum pfctl_show, int, const char *); int pfctl_show_eth_rules(int, char *, int, enum pfctl_show, char *, int, int); int pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int, int); int pfctl_show_nat(int, const char *, int, char *, int, int); @@ -117,6 +123,10 @@ int pfctl_test_altqsupport(int, int); int pfctl_show_anchors(int, int, char *); int pfctl_show_eth_anchors(int, int, char *); int pfctl_ruleset_trans(struct pfctl *, char *, struct pfctl_anchor *, bool); +void pfctl_load_statelims(struct pfctl *); +void pfctl_load_statelim(struct pfctl *, struct pfctl_statelim *); +void pfctl_load_sourcelims(struct pfctl *); +void pfctl_load_sourcelim(struct pfctl *, struct pfctl_sourcelim *); int pfctl_eth_ruleset_trans(struct pfctl *, char *, struct pfctl_eth_anchor *); int pfctl_load_eth_ruleset(struct pfctl *, char *, @@ -127,6 +137,7 @@ int pfctl_load_ruleset(struct pfctl *, char *, struct pfctl_ruleset *, int, int); int pfctl_load_rule(struct pfctl *, char *, struct pfctl_rule *, int); const char *pfctl_lookup_option(char *, const char * const *); +int pfctl_lookup_id(const char *, const struct pfctl_opt_id *); void pfctl_reset(int, int); int pfctl_walk_show(int, struct pfioc_ruleset *, void *); int pfctl_walk_get(int, struct pfioc_ruleset *, void *); @@ -141,6 +152,38 @@ int pfctl_call_cleartables(int, int, struct pfr_anchoritem *); int pfctl_call_clearanchors(int, int, struct pfr_anchoritem *); int pfctl_call_showtables(int, int, struct pfr_anchoritem *); +RB_PROTOTYPE(pfctl_statelim_ids, pfctl_statelim, entry, + pfctl_statelim_id_cmp); +RB_PROTOTYPE(pfctl_statelim_nms, pfctl_statelim, entry, + pfctl_statelim_nm_cmp); +RB_PROTOTYPE(pfctl_sourcelim_ids, pfctl_sourcelim, entry, + pfctl_sourcelim_id_cmp); +RB_PROTOTYPE(pfctl_sourcelim_nms, pfctl_sourcelim, entry, + pfctl_sourcelim_nm_cmp); + +enum showopt_id { + SHOWOPT_NONE = 0, + SHOWOPT_ETHER, + SHOWOPT_NAT, + SHOWOPT_QUEUE, + SHOWOPT_RULES, + SHOWOPT_ANCHORS, + SHOWOPT_SOURCES, + SHOWOPT_STATES, + SHOWOPT_INFO, + SHOWOPT_IFACES, + SHOWOPT_LABELS, + SHOWOPT_TIMEOUTS, + SHOWOPT_MEMORY, + SHOWOPT_TABLES, + SHOWOPT_OSFP, + SHOWOPT_RUNNING, + SHOWOPT_STATELIMS, + SHOWOPT_SOURCELIMS, + SHOWOPT_CREATORIDS, + SHOWOPT_ALL, +}; + static struct pfctl_anchor_global pf_anchors; struct pfctl_anchor pf_main_anchor; struct pfctl_eth_anchor pf_eth_main_anchor; @@ -148,7 +191,7 @@ static struct pfr_buffer skip_b; static const char *clearopt; static char *rulesopt; -static const char *showopt; +static int showopt; static const char *debugopt; static char *anchoropt; static const char *optiopt = NULL; @@ -256,10 +299,33 @@ static const char * const clearopt_list[] = { "ethernet", "Reset", NULL }; -static const char * const showopt_list[] = { - "ether", "nat", "queue", "rules", "Anchors", "Sources", "states", - "info", "Interfaces", "labels", "timeouts", "memory", "Tables", - "osfp", "Running", "all", "creatorids", NULL +struct pfctl_opt_id { + const char *name; + int id; +}; + +static const struct pfctl_opt_id showopt_list[] = { + { "ether", SHOWOPT_ETHER }, + { "nat", SHOWOPT_NAT }, + { "queue", SHOWOPT_QUEUE }, + { "rules", SHOWOPT_RULES }, + { "Anchors", SHOWOPT_ANCHORS }, + { "Sources", SHOWOPT_SOURCES }, + { "states", SHOWOPT_STATES }, + { "info", SHOWOPT_INFO }, + { "Interfaces", SHOWOPT_IFACES }, + { "labels", SHOWOPT_LABELS }, + { "timeouts", SHOWOPT_TIMEOUTS }, + { "memory", SHOWOPT_MEMORY }, + { "Tables", SHOWOPT_TABLES }, + { "osfp", SHOWOPT_OSFP }, + { "Running", SHOWOPT_RUNNING }, + { "Stlimiters", SHOWOPT_STATELIMS }, *** 3470 LINES SKIPPED ***