diff options
Diffstat (limited to 'contrib/pf/pfctl/parse.y')
-rw-r--r-- | contrib/pf/pfctl/parse.y | 642 |
1 files changed, 474 insertions, 168 deletions
diff --git a/contrib/pf/pfctl/parse.y b/contrib/pf/pfctl/parse.y index 179898f..ef5d77b 100644 --- a/contrib/pf/pfctl/parse.y +++ b/contrib/pf/pfctl/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.482 2005/03/07 13:20:03 henning Exp $ */ +/* $OpenBSD: parse.y,v 1.517 2007/02/03 23:26:40 dhartmei Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -199,10 +199,12 @@ struct filter_opts { char *tag; char *match_tag; u_int8_t match_tag_not; + int rtableid; } filter_opts; struct antispoof_opts { char *label; + int rtableid; } antispoof_opts; struct scrub_opts { @@ -216,6 +218,7 @@ struct scrub_opts { int fragcache; int randomid; int reassemble_tcp; + int rtableid; } scrub_opts; struct queue_opts { @@ -254,9 +257,10 @@ struct node_hfsc_opts hfsc_opts; int yyerror(const char *, ...); int disallow_table(struct node_host *, const char *); +int disallow_urpf_failed(struct node_host *, const char *); int disallow_alias(struct node_host *, const char *); -int rule_consistent(struct pf_rule *); -int filter_consistent(struct pf_rule *); +int rule_consistent(struct pf_rule *, int); +int filter_consistent(struct pf_rule *, int); int nat_consistent(struct pf_rule *); int rdr_consistent(struct pf_rule *); int process_tabledef(char *, struct table_opts *); @@ -306,6 +310,7 @@ struct sym { int symset(const char *, const char *, int); char *symget(const char *); +void mv_rules(struct pf_ruleset *, struct pf_ruleset *); void decide_address_family(struct node_host *, sa_family_t *); void remove_invalid_hosts(struct node_host **, sa_family_t *); int invalid_redirect(struct node_host *, sa_family_t); @@ -325,6 +330,7 @@ typedef struct { u_int32_t number; int i; char *string; + int rtableid; struct { u_int8_t b1; u_int8_t b2; @@ -367,6 +373,7 @@ typedef struct { } keep_state; struct { u_int8_t log; + u_int8_t logif; u_int8_t quick; } logquick; struct { @@ -395,30 +402,30 @@ typedef struct { %} -%token PASS BLOCK SCRUB RETURN IN OS OUT LOG LOGALL QUICK ON FROM TO FLAGS +%token PASS BLOCK SCRUB RETURN IN OS OUT LOG QUICK ON FROM TO FLAGS %token RETURNRST RETURNICMP RETURNICMP6 PROTO INET INET6 ALL ANY ICMPTYPE %token ICMP6TYPE CODE KEEP MODULATE STATE PORT RDR NAT BINAT ARROW NODF %token MINTTL ERROR ALLOWOPTS FASTROUTE FILENAME ROUTETO DUPTO REPLYTO NO LABEL -%token NOROUTE FRAGMENT USER GROUP MAXMSS MAXIMUM TTL TOS DROP TABLE +%token NOROUTE URPFFAILED FRAGMENT USER GROUP MAXMSS MAXIMUM TTL TOS DROP TABLE %token REASSEMBLE FRAGDROP FRAGCROP ANCHOR NATANCHOR RDRANCHOR BINATANCHOR %token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID %token REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID %token ANTISPOOF FOR %token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY %token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT -%token QUEUE PRIORITY QLIMIT -%token LOAD +%token QUEUE PRIORITY QLIMIT RTABLE +%token LOAD RULESET_OPTIMIZATION %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH -%token TAGGED TAG IFBOUND GRBOUND FLOATING STATEPOLICY ROUTE +%token TAGGED TAG IFBOUND FLOATING STATEPOLICY ROUTE %token <v.string> STRING %token <v.i> PORTBINARY %type <v.interface> interface if_list if_item_not if_item %type <v.number> number icmptype icmp6type uid gid -%type <v.number> tos not yesno natpass -%type <v.i> no dir log af fragcache sourcetrack flush -%type <v.i> unaryop statelock -%type <v.b> action nataction scrubaction +%type <v.number> tos not yesno +%type <v.i> no dir af fragcache optimizer +%type <v.i> sourcetrack flush unaryop statelock +%type <v.b> action nataction natpass scrubaction %type <v.b> flags flag blockspec %type <v.range> port rport %type <v.hashkey> hashkey @@ -437,10 +444,10 @@ typedef struct { %type <v.gid> gids gid_list gid_item %type <v.route> route %type <v.redirection> redirection redirpool -%type <v.string> label string tag +%type <v.string> label string tag anchorname %type <v.keep_state> keep %type <v.state_opt> state_opt_spec state_opt_list state_opt_item -%type <v.logquick> logquick +%type <v.logquick> logquick quick log logopts logopt %type <v.interface> antispoof_ifspc antispoof_iflst antispoof_if %type <v.qassign> qname %type <v.queue> qassign qassign_list qassign_item @@ -456,6 +463,7 @@ typedef struct { %type <v.table_opts> table_opts table_opt table_opts_l %type <v.pool_opts> pool_opts pool_opt pool_opts_l %type <v.tagged> tagged +%type <v.rtableid> rtable %% ruleset : /* empty */ @@ -472,9 +480,36 @@ ruleset : /* empty */ | ruleset varset '\n' | ruleset antispoof '\n' | ruleset tabledef '\n' + | '{' fakeanchor '}' '\n'; | ruleset error '\n' { errors++; } ; +/* + * apply to previouslys specified rule: must be careful to note + * what that is: pf or nat or binat or rdr + */ +fakeanchor : fakeanchor '\n' + | fakeanchor anchorrule '\n' + | fakeanchor binatrule '\n' + | fakeanchor natrule '\n' + | fakeanchor pfrule '\n' + | fakeanchor error '\n' + ; + +optimizer : string { + if (!strcmp($1, "none")) + $$ = 0; + else if (!strcmp($1, "basic")) + $$ = PF_OPTIMIZE_BASIC; + else if (!strcmp($1, "profile")) + $$ = PF_OPTIMIZE_BASIC | PF_OPTIMIZE_PROFILE; + else { + yyerror("unknown ruleset-optimization %s", $$); + YYERROR; + } + } + ; + option : SET OPTIMIZATION STRING { if (check_rulestate(PFCTL_STATE_OPTION)) { free($3); @@ -485,7 +520,13 @@ option : SET OPTIMIZATION STRING { free($3); YYERROR; } - free ($3); + free($3); + } + | SET RULESET_OPTIMIZATION optimizer { + if (!(pf->opts & PF_OPT_OPTIMIZE)) { + pf->opts |= PF_OPT_OPTIMIZE; + pf->optimize = $3; + } } | SET TIMEOUT timeout_spec | SET TIMEOUT '{' timeout_list '}' @@ -535,12 +576,12 @@ option : SET OPTIMIZATION STRING { } | SET FINGERPRINTS STRING { if (pf->opts & PF_OPT_VERBOSE) - printf("set fingerprints %s\n", $3); + printf("set fingerprints \"%s\"\n", $3); if (check_rulestate(PFCTL_STATE_OPTION)) { free($3); YYERROR; } - if (!pf->anchor[0]) { + if (!pf->anchor->name[0]) { if (pfctl_file_fingerprints(pf->dev, pf->opts, $3)) { yyerror("error loading " @@ -560,10 +601,6 @@ option : SET OPTIMIZATION STRING { case PFRULE_IFBOUND: printf("set state-policy if-bound\n"); break; - case PFRULE_GRBOUND: - printf("set state-policy " - "group-bound\n"); - break; } default_statelock = $3; } @@ -606,37 +643,120 @@ varset : STRING '=' string { } ; -anchorrule : ANCHOR string dir interface af proto fromto filter_opts { +anchorname : STRING { $$ = $1; } + | /* empty */ { $$ = NULL; } + ; + +optnl : optnl '\n' + | + ; + +pfa_anchorlist : pfrule optnl + | anchorrule optnl + | pfa_anchorlist pfrule optnl + | pfa_anchorlist anchorrule optnl + ; + +pfa_anchor : '{' + { + char ta[PF_ANCHOR_NAME_SIZE]; + struct pf_ruleset *rs; + + /* steping into a brace anchor */ + pf->asd++; + pf->bn++; + pf->brace = 1; + + /* create a holding ruleset in the root */ + snprintf(ta, PF_ANCHOR_NAME_SIZE, "_%d", pf->bn); + rs = pf_find_or_create_ruleset(ta); + if (rs == NULL) + err(1, "pfa_anchor: pf_find_or_create_ruleset"); + pf->astack[pf->asd] = rs->anchor; + pf->anchor = rs->anchor; + } '\n' pfa_anchorlist '}' + { + pf->alast = pf->anchor; + pf->asd--; + pf->anchor = pf->astack[pf->asd]; + } + | /* empty */ + ; + +anchorrule : ANCHOR anchorname dir quick interface af proto fromto + filter_opts pfa_anchor + { struct pf_rule r; if (check_rulestate(PFCTL_STATE_FILTER)) { + if ($2) + free($2); + YYERROR; + } + + if ($2 && ($2[0] == '_' || strstr($2, "/_") != NULL)) { free($2); + yyerror("anchor names beginning with '_' " + "are reserved for internal use"); YYERROR; } memset(&r, 0, sizeof(r)); + if (pf->astack[pf->asd + 1]) { + /* move inline rules into relative location */ + pf_anchor_setup(&r, + &pf->astack[pf->asd]->ruleset, + $2 ? $2 : pf->alast->name); + + if (r.anchor == NULL) + err(1, "anchorrule: unable to " + "create ruleset"); + + if (pf->alast != r.anchor) { + if (r.anchor->match) { + yyerror("inline anchor '%s' " + "already exists", + r.anchor->name); + YYERROR; + } + mv_rules(&pf->alast->ruleset, + &r.anchor->ruleset); + } + pf_remove_if_empty_ruleset(&pf->alast->ruleset); + pf->alast = r.anchor; + } else { + if (!$2) { + yyerror("anchors without explicit " + "rules must specify a name"); + YYERROR; + } + } r.direction = $3; - r.af = $5; - r.prob = $8.prob; + r.quick = $4.quick; + r.af = $6; + r.prob = $9.prob; + r.rtableid = $9.rtableid; - if ($8.match_tag) - if (strlcpy(r.match_tagname, $8.match_tag, + if ($9.match_tag) + if (strlcpy(r.match_tagname, $9.match_tag, PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { yyerror("tag too long, max %u chars", PF_TAG_NAME_SIZE - 1); YYERROR; } - r.match_tag_not = $8.match_tag_not; + r.match_tag_not = $9.match_tag_not; - decide_address_family($7.src.host, &r.af); - decide_address_family($7.dst.host, &r.af); + decide_address_family($8.src.host, &r.af); + decide_address_family($8.dst.host, &r.af); - expand_rule(&r, $4, NULL, $6, $7.src_os, - $7.src.host, $7.src.port, $7.dst.host, $7.dst.port, - 0, 0, 0, $2); + expand_rule(&r, $5, NULL, $7, $8.src_os, + $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, + 0, 0, 0, pf->astack[pf->asd + 1] ? + pf->alast->name : $2); free($2); + pf->astack[pf->asd + 1] = NULL; } - | NATANCHOR string interface af proto fromto { + | NATANCHOR string interface af proto fromto rtable { struct pf_rule r; if (check_rulestate(PFCTL_STATE_NAT)) { @@ -647,6 +767,7 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { memset(&r, 0, sizeof(r)); r.action = PF_NAT; r.af = $4; + r.rtableid = $7; decide_address_family($6.src.host, &r.af); decide_address_family($6.dst.host, &r.af); @@ -656,7 +777,7 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { 0, 0, 0, $2); free($2); } - | RDRANCHOR string interface af proto fromto { + | RDRANCHOR string interface af proto fromto rtable { struct pf_rule r; if (check_rulestate(PFCTL_STATE_NAT)) { @@ -667,6 +788,7 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { memset(&r, 0, sizeof(r)); r.action = PF_RDR; r.af = $4; + r.rtableid = $7; decide_address_family($6.src.host, &r.af); decide_address_family($6.dst.host, &r.af); @@ -697,7 +819,7 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { 0, 0, 0, $2); free($2); } - | BINATANCHOR string interface af proto fromto { + | BINATANCHOR string interface af proto fromto rtable { struct pf_rule r; if (check_rulestate(PFCTL_STATE_NAT)) { @@ -708,6 +830,7 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { memset(&r, 0, sizeof(r)); r.action = PF_BINAT; r.af = $4; + r.rtableid = $7; if ($5 != NULL) { if ($5->next != NULL) { yyerror("proto list expansion" @@ -736,7 +859,8 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { loadrule : LOAD ANCHOR string FROM string { struct loadanchors *loadanchor; - if (strlen($3) >= MAXPATHLEN) { + if (strlen(pf->anchor->name) + 1 + + strlen($3) >= MAXPATHLEN) { yyerror("anchorname %s too long, max %u\n", $3, MAXPATHLEN - 1); free($3); @@ -745,8 +869,14 @@ loadrule : LOAD ANCHOR string FROM string { loadanchor = calloc(1, sizeof(struct loadanchors)); if (loadanchor == NULL) err(1, "loadrule: calloc"); - if ((loadanchor->anchorname = strdup($3)) == NULL) - err(1, "loadrule: strdup"); + if ((loadanchor->anchorname = malloc(MAXPATHLEN)) == + NULL) + err(1, "loadrule: malloc"); + if (pf->anchor->name[0]) + snprintf(loadanchor->anchorname, MAXPATHLEN, + "%s/%s", pf->anchor->name, $3); + else + strlcpy(loadanchor->anchorname, $3, MAXPATHLEN); if ((loadanchor->filename = strdup($5)) == NULL) err(1, "loadrule: strdup"); @@ -779,6 +909,7 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts r.direction = $2; r.log = $3.log; + r.logif = $3.logif; if ($3.quick) { yyerror("scrub rules do not support 'quick'"); YYERROR; @@ -803,6 +934,7 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts r.max_mss = $8.maxmss; if ($8.fragcache) r.rule_flag |= $8.fragcache; + r.rtableid = $8.rtableid; expand_rule(&r, $4, NULL, $6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host, $7.dst.port, @@ -811,12 +943,14 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts ; scrub_opts : { - bzero(&scrub_opts, sizeof scrub_opts); - } + bzero(&scrub_opts, sizeof scrub_opts); + scrub_opts.rtableid = -1; + } scrub_opts_l { $$ = scrub_opts; } | /* empty */ { bzero(&scrub_opts, sizeof scrub_opts); + scrub_opts.rtableid = -1; $$ = scrub_opts; } ; @@ -885,6 +1019,13 @@ scrub_opt : NODF { } scrub_opts.randomid = 1; } + | RTABLE number { + if ($2 > RT_TABLEID_MAX || $2 < 0) { + yyerror("invalid rtable id"); + YYERROR; + } + scrub_opts.rtableid = $2; + } ; fragcache : FRAGMENT REASSEMBLE { $$ = 0; /* default */ } @@ -906,10 +1047,12 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { r.action = PF_DROP; r.direction = PF_IN; r.log = $2.log; + r.logif = $2.logif; r.quick = $2.quick; r.af = $4; if (rule_label(&r, $5.label)) YYERROR; + r.rtableid = $5.rtableid; j = calloc(1, sizeof(struct node_if)); if (j == NULL) err(1, "antispoof: calloc"); @@ -960,6 +1103,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { r.af = $4; if (rule_label(&r, $5.label)) YYERROR; + r.rtableid = $5.rtableid; if (hh != NULL) h = hh; else @@ -994,11 +1138,15 @@ antispoof_if : if_item { $$ = $1; } } ; -antispoof_opts : { bzero(&antispoof_opts, sizeof antispoof_opts); } +antispoof_opts : { + bzero(&antispoof_opts, sizeof antispoof_opts); + antispoof_opts.rtableid = -1; + } antispoof_opts_l { $$ = antispoof_opts; } | /* empty */ { bzero(&antispoof_opts, sizeof antispoof_opts); + antispoof_opts.rtableid = -1; $$ = antispoof_opts; } ; @@ -1014,6 +1162,13 @@ antispoof_opt : label { } antispoof_opts.label = $1; } + | RTABLE number { + if ($2 > RT_TABLEID_MAX || $2 < 0) { + yyerror("invalid rtable id"); + YYERROR; + } + antispoof_opts.rtableid = $2; + } ; not : '!' { $$ = 1; } @@ -1100,6 +1255,10 @@ table_opt : STRING { yyerror("\"no-route\" is not permitted " "inside tables"); break; + case PF_ADDR_URPFFAILED: + yyerror("\"urpf-failed\" is not " + "permitted inside tables"); + break; default: yyerror("unknown address type %d", n->addr.type); @@ -1499,6 +1658,7 @@ pfrule : action dir logquick interface route af proto fromto struct node_proto *proto; int srctrack = 0; int statelock = 0; + int adaptive = 0; if (check_rulestate(PFCTL_STATE_FILTER)) YYERROR; @@ -1524,8 +1684,10 @@ pfrule : action dir logquick interface route af proto fromto } r.direction = $2; r.log = $3.log; + r.logif = $3.logif; r.quick = $3.quick; r.prob = $9.prob; + r.rtableid = $9.rtableid; r.af = $6; if ($9.tag) @@ -1543,11 +1705,15 @@ pfrule : action dir logquick interface route af proto fromto YYERROR; } r.match_tag_not = $9.match_tag_not; - r.flags = $9.flags.b1; - r.flagset = $9.flags.b2; if (rule_label(&r, $9.label)) YYERROR; free($9.label); + r.flags = $9.flags.b1; + r.flagset = $9.flags.b2; + if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) { + yyerror("flags always false"); + YYERROR; + } if ($9.flags.b1 || $9.flags.b2 || $8.src_os) { for (proto = $7; proto != NULL && proto->proto != IPPROTO_TCP; @@ -1575,6 +1741,12 @@ pfrule : action dir logquick interface route af proto fromto r.tos = $9.tos; r.keep_state = $9.keep.action; + + /* 'keep state' by default on pass rules. */ + if (!r.keep_state && !r.action && + !($9.marker & FOM_KEEP)) + r.keep_state = PF_STATE_NORMAL; + o = $9.keep.options; while (o) { struct node_state_opt *p = o; @@ -1671,8 +1843,8 @@ pfrule : action dir logquick interface route af proto fromto if (o->data.max_src_conn_rate.limit > PF_THRESHOLD_MAX) { yyerror("'max-src-conn-rate' " - "maximum rate must be < %u", - PF_THRESHOLD_MAX); + "maximum rate must be < %u", + PF_THRESHOLD_MAX); YYERROR; } r.max_src_conn_rate.limit = @@ -1709,6 +1881,11 @@ pfrule : action dir logquick interface route af proto fromto r.rule_flag |= o->data.statelock; break; case PF_STATE_OPT_TIMEOUT: + if (o->data.timeout.number == + PFTM_ADAPTIVE_START || + o->data.timeout.number == + PFTM_ADAPTIVE_END) + adaptive = 1; if (r.timeout[o->data.timeout.number]) { yyerror("state timeout %s " "multiple definitions", @@ -1722,6 +1899,20 @@ pfrule : action dir logquick interface route af proto fromto o = o->next; free(p); } + + /* 'flags S/SA' by default on stateful rules */ + if (!r.action && !r.flags && !r.flagset && + !$9.fragment && !($9.marker & FOM_FLAGS) && + r.keep_state) { + r.flags = parse_flags("S"); + r.flagset = parse_flags("SA"); + } + if (!adaptive && r.max_states) { + r.timeout[PFTM_ADAPTIVE_START] = + (r.max_states / 10) * 6; + r.timeout[PFTM_ADAPTIVE_END] = + (r.max_states / 10) * 12; + } if (r.rule_flag & PFRULE_SRCTRACK) { if (srctrack == PF_SRCTRACK_GLOBAL && r.max_src_nodes) { @@ -1832,11 +2023,15 @@ pfrule : action dir logquick interface route af proto fromto } ; -filter_opts : { bzero(&filter_opts, sizeof filter_opts); } +filter_opts : { + bzero(&filter_opts, sizeof filter_opts); + filter_opts.rtableid = -1; + } filter_opts_l { $$ = filter_opts; } | /* empty */ { bzero(&filter_opts, sizeof filter_opts); + filter_opts.rtableid = -1; $$ = filter_opts; } ; @@ -1940,6 +2135,13 @@ filter_opt : USER uids { filter_opts.prob = (u_int32_t)p; free($2); } + | RTABLE number { + if ($2 > RT_TABLEID_MAX || $2 < 0) { + yyerror("invalid rtable id"); + YYERROR; + } + filter_opts.rtableid = $2; + } ; action : PASS { $$.b1 = PF_PASS; $$.b2 = $$.w = 0; } @@ -2021,15 +2223,55 @@ dir : /* empty */ { $$ = 0; } | OUT { $$ = PF_OUT; } ; -logquick : /* empty */ { $$.log = 0; $$.quick = 0; } - | log { $$.log = $1; $$.quick = 0; } - | QUICK { $$.log = 0; $$.quick = 1; } - | log QUICK { $$.log = $1; $$.quick = 1; } - | QUICK log { $$.log = $2; $$.quick = 1; } +quick : /* empty */ { $$.quick = 0; } + | QUICK { $$.quick = 1; } + ; + +logquick : /* empty */ { $$.log = 0; $$.quick = 0; $$.logif = 0; } + | log { $$ = $1; $$.quick = 0; } + | QUICK { $$.quick = 1; $$.log = 0; $$.logif = 0; } + | log QUICK { $$ = $1; $$.quick = 1; } + | QUICK log { $$ = $2; $$.quick = 1; } + ; + +log : LOG { $$.log = PF_LOG; $$.logif = 0; } + | LOG '(' logopts ')' { + $$.log = PF_LOG | $3.log; + $$.logif = $3.logif; + } ; -log : LOG { $$ = 1; } - | LOGALL { $$ = 2; } +logopts : logopt { $$ = $1; } + | logopts comma logopt { + $$.log = $1.log | $3.log; + $$.logif = $3.logif; + if ($$.logif == 0) + $$.logif = $1.logif; + } + ; + +logopt : ALL { $$.log = PF_LOG_ALL; $$.logif = 0; } + | USER { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; } + | GROUP { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; } + | TO string { + const char *errstr; + u_int i; + + $$.log = 0; + if (strncmp($2, "pflog", 5)) { + yyerror("%s: should be a pflog interface", $2); + free($2); + YYERROR; + } + i = strtonum($2 + 5, 0, 255, &errstr); + if (errstr) { + yyerror("%s: %s", $2, errstr); + free($2); + YYERROR; + } + free($2); + $$.logif = i; + } ; interface : /* empty */ { $$ = NULL; } @@ -2062,7 +2304,7 @@ if_item : STRING { YYERROR; } - if ((n = ifa_exists($1, 1)) != NULL) + if ((n = ifa_exists($1)) != NULL) $$->ifa_flags = n->ifa_flags; free($1); @@ -2176,6 +2418,9 @@ to : /* empty */ { $$.port = NULL; } | TO ipportspec { + if (disallow_urpf_failed($2.host, "\"urpf-failed\" is " + "not permitted in a destination address")) + YYERROR; $$ = $2; } ; @@ -2199,8 +2444,8 @@ ipspec : ANY { $$ = NULL; } | '{' host_list '}' { $$ = $2; } ; -host_list : xhost { $$ = $1; } - | host_list comma xhost { +host_list : ipspec { $$ = $1; } + | host_list comma ipspec { if ($3 == NULL) $$ = $1; else if ($1 == NULL) @@ -2220,12 +2465,22 @@ xhost : not host { n->not = $1; $$ = $2; } - | NOROUTE { + | not NOROUTE { $$ = calloc(1, sizeof(struct node_host)); if ($$ == NULL) err(1, "xhost: calloc"); $$->addr.type = PF_ADDR_NOROUTE; $$->next = NULL; + $$->not = $1; + $$->tail = $$; + } + | not URPFFAILED { + $$ = calloc(1, sizeof(struct node_host)); + if ($$ == NULL) + err(1, "xhost: calloc"); + $$->addr.type = PF_ADDR_URPFFAILED; + $$->next = NULL; + $$->not = $1; $$->tail = $$; } ; @@ -2428,31 +2683,13 @@ port_item : port { port : STRING { char *p = strchr($1, ':'); - struct servent *s = NULL; - u_long ulval; if (p == NULL) { - if (atoul($1, &ulval) == 0) { - if (ulval > 65535) { - free($1); - yyerror("illegal port value %lu", - ulval); - YYERROR; - } - $$.a = htons(ulval); - } else { - s = getservbyname($1, "tcp"); - if (s == NULL) - s = getservbyname($1, "udp"); - if (s == NULL) { - yyerror("unknown port %s", $1); - free($1); - YYERROR; - } - $$.a = s->s_port; + if (($$.a = getservice($1)) == -1) { + free($1); + YYERROR; } - $$.b = 0; - $$.t = 0; + $$.b = $$.t = 0; } else { int port[2]; @@ -2649,6 +2886,7 @@ flag : STRING { flags : FLAGS flag '/' flag { $$.b1 = $2.b1; $$.b2 = $4.b1; } | FLAGS '/' flag { $$.b1 = 0; $$.b2 = $3.b1; } + | FLAGS ANY { $$.b1 = 0; $$.b2 = 0; } ; icmpspec : ICMPTYPE icmp_item { $$ = $2; } @@ -2786,7 +3024,8 @@ icmp6type : STRING { if (atoul($1, &ulval) == 0) { if (ulval > 255) { - yyerror("illegal icmp6-type %lu", ulval); + yyerror("illegal icmp6-type %lu", + ulval); free($1); YYERROR; } @@ -2832,15 +3071,16 @@ sourcetrack : SOURCETRACK { $$ = PF_SRCTRACK; } statelock : IFBOUND { $$ = PFRULE_IFBOUND; } - | GRBOUND { - $$ = PFRULE_GRBOUND; - } | FLOATING { $$ = 0; } ; -keep : KEEP STATE state_opt_spec { +keep : NO STATE { + $$.action = 0; + $$.options = NULL; + } + | KEEP STATE state_opt_spec { $$.action = PF_STATE_NORMAL; $$.options = $3; } @@ -3199,29 +3439,41 @@ redirection : /* empty */ { $$ = NULL; } } ; -natpass : /* empty */ { $$ = 0; } - | PASS { $$ = 1; } +natpass : /* empty */ { $$.b1 = $$.b2 = 0; } + | PASS { $$.b1 = 1; $$.b2 = 0; } + | PASS log { $$.b1 = 1; $$.b2 = $2.log; $$.w2 = $2.logif; } ; nataction : no NAT natpass { - $$.b2 = $$.w = 0; + if ($1 && $3.b1) { + yyerror("\"pass\" not valid with \"no\""); + YYERROR; + } if ($1) $$.b1 = PF_NONAT; else $$.b1 = PF_NAT; - $$.b2 = $3; + $$.b2 = $3.b1; + $$.w = $3.b2; + $$.w2 = $3.w2; } | no RDR natpass { - $$.b2 = $$.w = 0; + if ($1 && $3.b1) { + yyerror("\"pass\" not valid with \"no\""); + YYERROR; + } if ($1) $$.b1 = PF_NORDR; else $$.b1 = PF_RDR; - $$.b2 = $3; + $$.b2 = $3.b1; + $$.w = $3.b2; + $$.w2 = $3.w2; } ; -natrule : nataction interface af proto fromto tag tagged redirpool pool_opts +natrule : nataction interface af proto fromto tag tagged rtable + redirpool pool_opts { struct pf_rule r; @@ -3232,6 +3484,8 @@ natrule : nataction interface af proto fromto tag tagged redirpool pool_opts r.action = $1.b1; r.natpass = $1.b2; + r.log = $1.w; + r.logif = $1.w2; r.af = $3; if (!r.af) { @@ -3259,47 +3513,48 @@ natrule : nataction interface af proto fromto tag tagged redirpool pool_opts YYERROR; } r.match_tag_not = $7.neg; + r.rtableid = $8; if (r.action == PF_NONAT || r.action == PF_NORDR) { - if ($8 != NULL) { + if ($9 != NULL) { yyerror("translation rule with 'no' " "does not need '->'"); YYERROR; } } else { - if ($8 == NULL || $8->host == NULL) { + if ($9 == NULL || $9->host == NULL) { yyerror("translation rule requires '-> " "address'"); YYERROR; } - if (!r.af && ! $8->host->ifindex) - r.af = $8->host->af; + if (!r.af && ! $9->host->ifindex) + r.af = $9->host->af; - remove_invalid_hosts(&$8->host, &r.af); - if (invalid_redirect($8->host, r.af)) + remove_invalid_hosts(&$9->host, &r.af); + if (invalid_redirect($9->host, r.af)) YYERROR; - if (check_netmask($8->host, r.af)) + if (check_netmask($9->host, r.af)) YYERROR; - r.rpool.proxy_port[0] = ntohs($8->rport.a); + r.rpool.proxy_port[0] = ntohs($9->rport.a); switch (r.action) { case PF_RDR: - if (!$8->rport.b && $8->rport.t && + if (!$9->rport.b && $9->rport.t && $5.dst.port != NULL) { r.rpool.proxy_port[1] = - ntohs($8->rport.a) + + ntohs($9->rport.a) + (ntohs( $5.dst.port->port[1]) - ntohs( $5.dst.port->port[0])); } else r.rpool.proxy_port[1] = - ntohs($8->rport.b); + ntohs($9->rport.b); break; case PF_NAT: r.rpool.proxy_port[1] = - ntohs($8->rport.b); + ntohs($9->rport.b); if (!r.rpool.proxy_port[0] && !r.rpool.proxy_port[1]) { r.rpool.proxy_port[0] = @@ -3314,25 +3569,25 @@ natrule : nataction interface af proto fromto tag tagged redirpool pool_opts break; } - r.rpool.opts = $9.type; + r.rpool.opts = $10.type; if ((r.rpool.opts & PF_POOL_TYPEMASK) == - PF_POOL_NONE && ($8->host->next != NULL || - $8->host->addr.type == PF_ADDR_TABLE || - DYNIF_MULTIADDR($8->host->addr))) + PF_POOL_NONE && ($9->host->next != NULL || + $9->host->addr.type == PF_ADDR_TABLE || + DYNIF_MULTIADDR($9->host->addr))) r.rpool.opts = PF_POOL_ROUNDROBIN; if ((r.rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN && - disallow_table($8->host, "tables are only " + disallow_table($9->host, "tables are only " "supported in round-robin redirection " "pools")) YYERROR; if ((r.rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN && - disallow_alias($8->host, "interface (%s) " + disallow_alias($9->host, "interface (%s) " "is only supported in round-robin " "redirection pools")) YYERROR; - if ($8->host->next != NULL) { + if ($9->host->next != NULL) { if ((r.rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) { yyerror("only round-robin " @@ -3343,14 +3598,14 @@ natrule : nataction interface af proto fromto tag tagged redirpool pool_opts } } - if ($9.key != NULL) - memcpy(&r.rpool.key, $9.key, + if ($10.key != NULL) + memcpy(&r.rpool.key, $10.key, sizeof(struct pf_poolhashkey)); - if ($9.opts) - r.rpool.opts |= $9.opts; + if ($10.opts) + r.rpool.opts |= $10.opts; - if ($9.staticport) { + if ($10.staticport) { if (r.action != PF_NAT) { yyerror("the 'static-port' option is " "only valid with nat rules"); @@ -3369,37 +3624,46 @@ natrule : nataction interface af proto fromto tag tagged redirpool pool_opts r.rpool.proxy_port[1] = 0; } - expand_rule(&r, $2, $8 == NULL ? NULL : $8->host, $4, + expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $4, $5.src_os, $5.src.host, $5.src.port, $5.dst.host, $5.dst.port, 0, 0, 0, ""); - free($8); + free($9); } ; -binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag tagged - redirection +binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag + tagged rtable redirection { struct pf_rule binat; struct pf_pooladdr *pa; if (check_rulestate(PFCTL_STATE_NAT)) YYERROR; + if (disallow_urpf_failed($10, "\"urpf-failed\" is not " + "permitted as a binat destination")) + YYERROR; memset(&binat, 0, sizeof(binat)); + if ($1 && $3.b1) { + yyerror("\"pass\" not valid with \"no\""); + YYERROR; + } if ($1) binat.action = PF_NOBINAT; else binat.action = PF_BINAT; - binat.natpass = $3; + binat.natpass = $3.b1; + binat.log = $3.b2; + binat.logif = $3.w2; binat.af = $5; if (!binat.af && $8 != NULL && $8->af) binat.af = $8->af; if (!binat.af && $10 != NULL && $10->af) binat.af = $10->af; - if (!binat.af && $13 != NULL && $13->host) - binat.af = $13->host->af; + if (!binat.af && $14 != NULL && $14->host) + binat.af = $14->host->af; if (!binat.af) { yyerror("address family (inet/inet6) " "undefined"); @@ -3428,6 +3692,7 @@ binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag tagged YYERROR; } binat.match_tag_not = $12.neg; + binat.rtableid = $13; if ($6 != NULL) { binat.proto = $6->proto; @@ -3441,12 +3706,12 @@ binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag tagged "interface (%s) as the source address of a binat " "rule")) YYERROR; - if ($13 != NULL && $13->host != NULL && disallow_table( - $13->host, "invalid use of table <%s> as the " + if ($14 != NULL && $14->host != NULL && disallow_table( + $14->host, "invalid use of table <%s> as the " "redirect address of a binat rule")) YYERROR; - if ($13 != NULL && $13->host != NULL && disallow_alias( - $13->host, "invalid use of interface (%s) as the " + if ($14 != NULL && $14->host != NULL && disallow_alias( + $14->host, "invalid use of interface (%s) as the " "redirect address of a binat rule")) YYERROR; @@ -3485,33 +3750,33 @@ binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag tagged } if (binat.action == PF_NOBINAT) { - if ($13 != NULL) { + if ($14 != NULL) { yyerror("'no binat' rule does not need" " '->'"); YYERROR; } } else { - if ($13 == NULL || $13->host == NULL) { + if ($14 == NULL || $14->host == NULL) { yyerror("'binat' rule requires" " '-> address'"); YYERROR; } - remove_invalid_hosts(&$13->host, &binat.af); - if (invalid_redirect($13->host, binat.af)) + remove_invalid_hosts(&$14->host, &binat.af); + if (invalid_redirect($14->host, binat.af)) YYERROR; - if ($13->host->next != NULL) { + if ($14->host->next != NULL) { yyerror("binat rule must redirect to " "a single address"); YYERROR; } - if (check_netmask($13->host, binat.af)) + if (check_netmask($14->host, binat.af)) YYERROR; if (!PF_AZERO(&binat.src.addr.v.a.mask, binat.af) && !PF_AEQ(&binat.src.addr.v.a.mask, - &$13->host->addr.v.a.mask, binat.af)) { + &$14->host->addr.v.a.mask, binat.af)) { yyerror("'binat' source mask and " "redirect mask must be the same"); YYERROR; @@ -3521,12 +3786,12 @@ binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag tagged pa = calloc(1, sizeof(struct pf_pooladdr)); if (pa == NULL) err(1, "binat: calloc"); - pa->addr = $13->host->addr; + pa->addr = $14->host->addr; pa->ifname[0] = 0; TAILQ_INSERT_TAIL(&binat.rpool.list, pa, entries); - free($13); + free($14); } pfctl_add_rule(pf, &binat, ""); @@ -3541,6 +3806,16 @@ tagged : /* empty */ { $$.neg = 0; $$.name = NULL; } | not TAGGED string { $$.neg = $1; $$.name = $3; } ; +rtable : /* empty */ { $$ = -1; } + | RTABLE number { + if ($2 > RT_TABLEID_MAX || $2 < 0) { + yyerror("invalid rtable id"); + YYERROR; + } + $$ = $2; + } + ; + route_host : STRING { $$ = calloc(1, sizeof(struct node_host)); if ($$ == NULL) @@ -3701,6 +3976,17 @@ disallow_table(struct node_host *h, const char *fmt) } int +disallow_urpf_failed(struct node_host *h, const char *fmt) +{ + for (; h != NULL; h = h->next) + if (h->addr.type == PF_ADDR_URPFFAILED) { + yyerror(fmt); + return (1); + } + return (0); +} + +int disallow_alias(struct node_host *h, const char *fmt) { for (; h != NULL; h = h->next) @@ -3712,7 +3998,7 @@ disallow_alias(struct node_host *h, const char *fmt) } int -rule_consistent(struct pf_rule *r) +rule_consistent(struct pf_rule *r, int anchor_call) { int problems = 0; @@ -3721,7 +4007,7 @@ rule_consistent(struct pf_rule *r) case PF_DROP: case PF_SCRUB: case PF_NOSCRUB: - problems = filter_consistent(r); + problems = filter_consistent(r, anchor_call); break; case PF_NAT: case PF_NONAT: @@ -3740,7 +4026,7 @@ rule_consistent(struct pf_rule *r) } int -filter_consistent(struct pf_rule *r) +filter_consistent(struct pf_rule *r, int anchor_call) { int problems = 0; @@ -3792,11 +4078,6 @@ filter_consistent(struct pf_rule *r) yyerror("keep state on block rules doesn't make sense"); problems++; } - if ((r->tagname[0] || r->match_tagname[0]) && !r->keep_state && - r->action == PF_PASS) { - yyerror("tags cannot be used without keep state"); - problems++; - } return (-problems); } @@ -3864,7 +4145,7 @@ process_tabledef(char *name, struct table_opts *opts) &opts->init_nodes); if (!(pf->opts & PF_OPT_NOACTION) && pfctl_define_table(name, opts->flags, opts->init_addr, - pf->anchor, &ab, pf->tticket)) { + pf->anchor->name, &ab, pf->anchor->ruleset.tticket)) { yyerror("cannot define table %s: %s", name, pfr_strerror(errno)); goto _error; @@ -3963,6 +4244,9 @@ expand_label_addr(const char *name, char *label, size_t len, sa_family_t af, case PF_ADDR_NOROUTE: snprintf(tmp, sizeof(tmp), "no-route"); break; + case PF_ADDR_URPFFAILED: + snprintf(tmp, sizeof(tmp), "urpf-failed"); + break; case PF_ADDR_ADDRMASK: if (!af || (PF_AZERO(&h->addr.v.a.addr, af) && PF_AZERO(&h->addr.v.a.mask, af))) @@ -4053,7 +4337,7 @@ expand_label_nr(const char *name, char *label, size_t len) char n[11]; if (strstr(label, name) != NULL) { - snprintf(n, sizeof(n), "%u", pf->rule_nr); + snprintf(n, sizeof(n), "%u", pf->anchor->match); expand_label_str(label, len, name, n); } } @@ -4480,10 +4764,10 @@ expand_rule(struct pf_rule *r, TAILQ_INSERT_TAIL(&r->rpool.list, pa, entries); } - if (rule_consistent(r) < 0 || error) + if (rule_consistent(r, anchor_call[0]) < 0 || error) yyerror("skipping rule due to errors"); else { - r->nr = pf->rule_nr++; + r->nr = pf->astack[pf->asd]->match++; pfctl_add_rule(pf, r, anchor_call); added++; } @@ -4598,7 +4882,6 @@ lookup(char *s) { "from", FROM}, { "global", GLOBAL}, { "group", GROUP}, - { "group-bound", GRBOUND}, { "hfsc", HFSC}, { "hostid", HOSTID}, { "icmp-type", ICMPTYPE}, @@ -4613,7 +4896,6 @@ lookup(char *s) { "linkshare", LINKSHARE}, { "load", LOAD}, { "log", LOG}, - { "log-all", LOGALL}, { "loginterface", LOGINTERFACE}, { "max", MAXIMUM}, { "max-mss", MAXMSS}, @@ -4658,7 +4940,9 @@ lookup(char *s) { "round-robin", ROUNDROBIN}, { "route", ROUTE}, { "route-to", ROUTETO}, + { "rtable", RTABLE}, { "rule", RULE}, + { "ruleset-optimization", RULESET_OPTIMIZATION}, { "scrub", SCRUB}, { "set", SET}, { "skip", SKIP}, @@ -4678,6 +4962,7 @@ lookup(char *s) { "tos", TOS}, { "ttl", TTL}, { "upperlimit", UPPERLIMIT}, + { "urpf-failed", URPFFAILED}, { "user", USER}, }; const struct keywords *p; @@ -4725,9 +5010,7 @@ lgetc(FILE *f) while ((c = getc(f)) == '\\') { next = getc(f); if (next != '\n') { - if (isspace(next)) - yyerror("whitespace after \\"); - ungetc(next, f); + c = next; break; } yylval.lineno = lineno; @@ -5015,21 +5298,40 @@ symget(const char *nam) } void -decide_address_family(struct node_host *n, sa_family_t *af) +mv_rules(struct pf_ruleset *src, struct pf_ruleset *dst) { - sa_family_t target_af = 0; + int i; + struct pf_rule *r; + + for (i = 0; i < PF_RULESET_MAX; ++i) { + while ((r = TAILQ_FIRST(src->rules[i].active.ptr)) + != NULL) { + TAILQ_REMOVE(src->rules[i].active.ptr, r, entries); + TAILQ_INSERT_TAIL(dst->rules[i].active.ptr, r, entries); + dst->anchor->match++; + } + src->anchor->match = 0; + while ((r = TAILQ_FIRST(src->rules[i].inactive.ptr)) + != NULL) { + TAILQ_REMOVE(src->rules[i].inactive.ptr, r, entries); + TAILQ_INSERT_TAIL(dst->rules[i].inactive.ptr, + r, entries); + } + } +} - while (!*af && n != NULL) { - if (n->af) { - if (target_af == 0) - target_af = n->af; - if (target_af != n->af) - return; +void +decide_address_family(struct node_host *n, sa_family_t *af) +{ + if (*af != 0 || n == NULL) + return; + *af = n->af; + while ((n = n->next) != NULL) { + if (n->af != *af) { + *af = 0; + return; } - n = n->next; } - if (!*af && target_af) - *af = target_af; } void @@ -5170,19 +5472,23 @@ parseicmpspec(char *w, sa_family_t af) } int -pfctl_load_anchors(int dev, int opts, struct pfr_buffer *trans) +pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans) { struct loadanchors *la; + FILE *fin; TAILQ_FOREACH(la, &loadanchorshead, entries) { - if (opts & PF_OPT_VERBOSE) + if (pf->opts & PF_OPT_VERBOSE) fprintf(stderr, "\nLoading anchor %s from %s\n", la->anchorname, la->filename); - if (pfctl_rules(dev, la->filename, opts, la->anchorname, - trans) == -1) + if ((fin = pfctl_fopen(la->filename, "r")) == NULL) { + warn("%s", la->filename); + continue; + } + if (pfctl_rules(dev, la->filename, fin, pf->opts, pf->optimize, + la->anchorname, trans) == -1) return (-1); } return (0); } - |