diff options
Diffstat (limited to 'contrib/pf/pfctl/parse.y')
-rw-r--r-- | contrib/pf/pfctl/parse.y | 644 |
1 files changed, 457 insertions, 187 deletions
diff --git a/contrib/pf/pfctl/parse.y b/contrib/pf/pfctl/parse.y index 2c82bcd..a06a5de 100644 --- a/contrib/pf/pfctl/parse.y +++ b/contrib/pf/pfctl/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.449 2004/03/20 23:20:20 david Exp $ */ +/* $OpenBSD: parse.y,v 1.482 2005/03/07 13:20:03 henning Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include <errno.h> #include <string.h> #include <ctype.h> +#include <math.h> #include <err.h> #include <limits.h> #include <pwd.h> @@ -124,8 +125,10 @@ struct node_icmp { }; enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK, - PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_NODES, - PF_STATE_OPT_STATELOCK, PF_STATE_OPT_TIMEOUT }; + PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN, + 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 }; enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE }; @@ -134,6 +137,15 @@ struct node_state_opt { union { u_int32_t max_states; u_int32_t max_src_states; + u_int32_t max_src_conn; + struct { + u_int32_t limit; + u_int32_t seconds; + } max_src_conn_rate; + struct { + u_int8_t flush; + char tblname[PF_TABLE_NAME_SIZE]; + } overload; u_int32_t max_src_nodes; u_int8_t src_track; u_int32_t statelock; @@ -182,6 +194,7 @@ struct filter_opts { } flags; struct node_icmp *icmpspec; u_int32_t tos; + u_int32_t prob; struct { int action; struct node_state_opt *options; @@ -268,11 +281,13 @@ void expand_label(char *, size_t, const char *, u_int8_t, struct node_host *, void expand_rule(struct pf_rule *, struct node_if *, struct node_host *, struct node_proto *, struct node_os*, struct node_host *, struct node_port *, struct node_host *, struct node_port *, - struct node_uid *, struct node_gid *, struct node_icmp *); + struct node_uid *, struct node_gid *, struct node_icmp *, + const char *); int expand_altq(struct pf_altq *, struct node_if *, struct node_queue *, struct node_queue_bw bwspec, struct node_queue_opt *); int expand_queue(struct pf_altq *, struct node_if *, struct node_queue *, struct node_queue_bw, struct node_queue_opt *); +int expand_skip_interface(struct node_if *); int check_rulestate(int); int kw_cmp(const void *, const void *); @@ -309,7 +324,6 @@ TAILQ_HEAD(loadanchorshead, loadanchors) struct loadanchors { TAILQ_ENTRY(loadanchors) entries; char *anchorname; - char *rulesetname; char *filename; }; @@ -362,6 +376,10 @@ typedef struct { u_int8_t log; u_int8_t quick; } logquick; + struct { + int neg; + char *name; + } tagged; struct pf_poolhashkey *hashkey; struct node_queue *queue; struct node_queue_opt queue_options; @@ -378,18 +396,6 @@ typedef struct { int lineno; } YYSTYPE; -#define PREPARE_ANCHOR_RULE(r, a) \ - do { \ - memset(&(r), 0, sizeof(r)); \ - if (strlcpy(r.anchorname, (a), \ - sizeof(r.anchorname)) >= \ - sizeof(r.anchorname)) { \ - yyerror("anchor name '%s' too long", \ - (a)); \ - YYERROR; \ - } \ - } while (0) - #define DYNIF_MULTIADDR(addr) ((addr).type == PF_ADDR_DYNIFTL && \ (!((addr).iflags & PFI_AFLAG_NOALIAS) || \ !isdigit((addr).v.ifname[strlen((addr).v.ifname)-1]))) @@ -403,22 +409,24 @@ typedef struct { %token NOROUTE 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 HOSTID +%token REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID %token ANTISPOOF FOR -%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT +%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY %token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT %token QUEUE PRIORITY QLIMIT %token LOAD %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE -%token TAGGED TAG IFBOUND GRBOUND FLOATING STATEPOLICY +%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH +%token TAGGED TAG IFBOUND GRBOUND 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 +%type <v.i> no dir log af fragcache sourcetrack flush %type <v.i> unaryop statelock -%type <v.b> action nataction flags flag blockspec +%type <v.b> action nataction scrubaction +%type <v.b> flags flag blockspec %type <v.range> port rport %type <v.hashkey> hashkey %type <v.proto> proto proto_list proto_item @@ -440,7 +448,7 @@ typedef struct { %type <v.keep_state> keep %type <v.state_opt> state_opt_spec state_opt_list state_opt_item %type <v.logquick> logquick -%type <v.interface> antispoof_ifspc antispoof_iflst +%type <v.interface> antispoof_ifspc antispoof_iflst antispoof_if %type <v.qassign> qname %type <v.queue> qassign qassign_list qassign_item %type <v.queue_options> scheduler @@ -454,6 +462,7 @@ typedef struct { %type <v.scrub_opts> scrub_opts scrub_opt scrub_opts_l %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 %% ruleset : /* empty */ @@ -494,11 +503,6 @@ option : SET OPTIMIZATION STRING { free($3); YYERROR; } - if ((ifa_exists($3, 0) == NULL) && strcmp($3, "none")) { - yyerror("interface %s doesn't exist", $3); - free($3); - YYERROR; - } if (pfctl_set_logif(pf, $3) != 0) { yyerror("error setting loginterface %s", $3); free($3); @@ -512,7 +516,7 @@ option : SET OPTIMIZATION STRING { YYERROR; } if (pfctl_set_hostid(pf, $3) != 0) { - yyerror("error setting loginterface %08x", $3); + yyerror("error setting hostid %08x", $3); YYERROR; } } @@ -538,15 +542,19 @@ option : SET OPTIMIZATION STRING { } | SET FINGERPRINTS STRING { if (pf->opts & PF_OPT_VERBOSE) - printf("fingerprints %s\n", $3); + printf("set fingerprints %s\n", $3); if (check_rulestate(PFCTL_STATE_OPTION)) { free($3); YYERROR; } - if (pfctl_file_fingerprints(pf->dev, pf->opts, $3)) { - yyerror("error loading fingerprints %s", $3); - free($3); - YYERROR; + if (!pf->anchor[0]) { + if (pfctl_file_fingerprints(pf->dev, + pf->opts, $3)) { + yyerror("error loading " + "fingerprints %s", $3); + free($3); + YYERROR; + } } free($3); } @@ -578,6 +586,12 @@ option : SET OPTIMIZATION STRING { } free($3); } + | SET SKIP interface { + if (expand_skip_interface($3) != 0) { + yyerror("error setting skip interface(s)"); + YYERROR; + } + } ; string : string STRING { @@ -607,9 +621,10 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { YYERROR; } - PREPARE_ANCHOR_RULE(r, $2); + memset(&r, 0, sizeof(r)); r.direction = $3; r.af = $5; + r.prob = $8.prob; if ($8.match_tag) if (strlcpy(r.match_tagname, $8.match_tag, @@ -625,7 +640,8 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { expand_rule(&r, $4, NULL, $6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host, $7.dst.port, - 0, 0, 0); + 0, 0, 0, $2); + free($2); } | NATANCHOR string interface af proto fromto { struct pf_rule r; @@ -635,8 +651,7 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { YYERROR; } - PREPARE_ANCHOR_RULE(r, $2); - free($2); + memset(&r, 0, sizeof(r)); r.action = PF_NAT; r.af = $4; @@ -645,7 +660,8 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { expand_rule(&r, $3, NULL, $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, - 0, 0, 0); + 0, 0, 0, $2); + free($2); } | RDRANCHOR string interface af proto fromto { struct pf_rule r; @@ -655,8 +671,7 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { YYERROR; } - PREPARE_ANCHOR_RULE(r, $2); - free($2); + memset(&r, 0, sizeof(r)); r.action = PF_RDR; r.af = $4; @@ -686,7 +701,8 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { expand_rule(&r, $3, NULL, $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, - 0, 0, 0); + 0, 0, 0, $2); + free($2); } | BINATANCHOR string interface af proto fromto { struct pf_rule r; @@ -696,8 +712,7 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { YYERROR; } - PREPARE_ANCHOR_RULE(r, $2); - free($2); + memset(&r, 0, sizeof(r)); r.action = PF_BINAT; r.af = $4; if ($5 != NULL) { @@ -720,39 +735,24 @@ anchorrule : ANCHOR string dir interface af proto fromto filter_opts { decide_address_family($6.src.host, &r.af); decide_address_family($6.dst.host, &r.af); - pfctl_add_rule(pf, &r); + pfctl_add_rule(pf, &r, $2); + free($2); } ; loadrule : LOAD ANCHOR string FROM string { - char *t; struct loadanchors *loadanchor; - t = strsep(&$3, ":"); - if (*t == '\0' || $3 == NULL || *$3 == '\0') { - yyerror("anchor '%s' invalid\n", $3); - free(t); - YYERROR; - } - if (strlen(t) >= PF_ANCHOR_NAME_SIZE) { + if (strlen($3) >= MAXPATHLEN) { yyerror("anchorname %s too long, max %u\n", - t, PF_ANCHOR_NAME_SIZE - 1); - free(t); - YYERROR; - } - if (strlen($3) >= PF_RULESET_NAME_SIZE) { - yyerror("rulesetname %s too long, max %u\n", - $3, PF_RULESET_NAME_SIZE - 1); - free(t); + $3, MAXPATHLEN - 1); + free($3); YYERROR; } - loadanchor = calloc(1, sizeof(struct loadanchors)); if (loadanchor == NULL) err(1, "loadrule: calloc"); - if ((loadanchor->anchorname = strdup(t)) == NULL) - err(1, "loadrule: strdup"); - if ((loadanchor->rulesetname = strdup($3)) == NULL) + if ((loadanchor->anchorname = strdup($3)) == NULL) err(1, "loadrule: strdup"); if ((loadanchor->filename = strdup($5)) == NULL) err(1, "loadrule: strdup"); @@ -760,11 +760,20 @@ loadrule : LOAD ANCHOR string FROM string { TAILQ_INSERT_TAIL(&loadanchorshead, loadanchor, entries); - free(t); /* not $3 */ + free($3); free($5); }; -scrubrule : SCRUB dir logquick interface af proto fromto scrub_opts +scrubaction : no SCRUB { + $$.b2 = $$.w = 0; + if ($1) + $$.b1 = PF_NOSCRUB; + else + $$.b1 = PF_SCRUB; + } + ; + +scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts { struct pf_rule r; @@ -773,7 +782,7 @@ scrubrule : SCRUB dir logquick interface af proto fromto scrub_opts memset(&r, 0, sizeof(r)); - r.action = PF_SCRUB; + r.action = $1.b1; r.direction = $2; r.log = $3.log; @@ -804,7 +813,7 @@ scrubrule : SCRUB dir logquick interface af proto fromto scrub_opts expand_rule(&r, $4, NULL, $6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host, $7.dst.port, - NULL, NULL, NULL); + NULL, NULL, NULL, ""); } ; @@ -864,6 +873,8 @@ scrub_opt : NODF { } | REASSEMBLE STRING { if (strcasecmp($2, "tcp") != 0) { + yyerror("scrub reassemble supports only tcp, " + "not '%s'", $2); free($2); YYERROR; } @@ -890,7 +901,7 @@ fragcache : FRAGMENT REASSEMBLE { $$ = 0; /* default */ } antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { struct pf_rule r; - struct node_host *h = NULL; + struct node_host *h = NULL, *hh; struct node_if *i, *j; if (check_rulestate(PFCTL_STATE_FILTER)) @@ -916,11 +927,35 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { YYERROR; } j->not = 1; - h = ifa_lookup(j->ifname, PFI_AFLAG_NETWORK); + if (i->dynamic) { + h = calloc(1, sizeof(*h)); + if (h == NULL) + err(1, "address: calloc"); + h->addr.type = PF_ADDR_DYNIFTL; + set_ipmask(h, 128); + if (strlcpy(h->addr.v.ifname, i->ifname, + sizeof(h->addr.v.ifname)) >= + sizeof(h->addr.v.ifname)) { + free(h); + yyerror( + "interface name too long"); + YYERROR; + } + hh = malloc(sizeof(*hh)); + if (hh == NULL) + err(1, "address: malloc"); + bcopy(h, hh, sizeof(*hh)); + h->addr.iflags = PFI_AFLAG_NETWORK; + } else { + h = ifa_lookup(j->ifname, + PFI_AFLAG_NETWORK); + hh = NULL; + } if (h != NULL) expand_rule(&r, j, NULL, NULL, NULL, h, - NULL, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL, + NULL, ""); if ((i->ifa_flags & IFF_LOOPBACK) == 0) { bzero(&r, sizeof(r)); @@ -932,29 +967,40 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { r.af = $4; if (rule_label(&r, $5.label)) YYERROR; - h = ifa_lookup(i->ifname, 0); + if (hh != NULL) + h = hh; + else + h = ifa_lookup(i->ifname, 0); if (h != NULL) expand_rule(&r, NULL, NULL, NULL, NULL, h, NULL, NULL, - NULL, NULL, NULL, NULL); - } + NULL, NULL, NULL, NULL, ""); + } else + free(hh); } free($5.label); } ; -antispoof_ifspc : FOR if_item { $$ = $2; } +antispoof_ifspc : FOR antispoof_if { $$ = $2; } | FOR '{' antispoof_iflst '}' { $$ = $3; } ; -antispoof_iflst : if_item { $$ = $1; } - | antispoof_iflst comma if_item { +antispoof_iflst : antispoof_if { $$ = $1; } + | antispoof_iflst comma antispoof_if { $1->tail->next = $3; $1->tail = $3; $$ = $1; } ; +antispoof_if : if_item { $$ = $1; } + | '(' if_item ')' { + $2->dynamic = 1; + $$ = $2; + } + ; + antispoof_opts : { bzero(&antispoof_opts, sizeof antispoof_opts); } antispoof_opts_l { $$ = antispoof_opts; } @@ -1035,6 +1081,7 @@ table_opt : STRING { else if (!strcmp($1, "persist")) table_opts.flags |= PFR_TFLAG_PERSIST; else { + yyerror("invalid table option '%s'", $1); free($1); YYERROR; } @@ -1353,14 +1400,15 @@ hfscopts_item : LINKSHARE bandwidth { hfsc_opts.linkshare.m2 = $2; hfsc_opts.linkshare.used = 1; } - | LINKSHARE '(' bandwidth number bandwidth ')' { + | LINKSHARE '(' bandwidth comma number comma bandwidth ')' + { if (hfsc_opts.linkshare.used) { yyerror("linkshare already specified"); YYERROR; } hfsc_opts.linkshare.m1 = $3; - hfsc_opts.linkshare.d = $4; - hfsc_opts.linkshare.m2 = $5; + hfsc_opts.linkshare.d = $5; + hfsc_opts.linkshare.m2 = $7; hfsc_opts.linkshare.used = 1; } | REALTIME bandwidth { @@ -1371,14 +1419,15 @@ hfscopts_item : LINKSHARE bandwidth { hfsc_opts.realtime.m2 = $2; hfsc_opts.realtime.used = 1; } - | REALTIME '(' bandwidth number bandwidth ')' { + | REALTIME '(' bandwidth comma number comma bandwidth ')' + { if (hfsc_opts.realtime.used) { yyerror("realtime already specified"); YYERROR; } hfsc_opts.realtime.m1 = $3; - hfsc_opts.realtime.d = $4; - hfsc_opts.realtime.m2 = $5; + hfsc_opts.realtime.d = $5; + hfsc_opts.realtime.m2 = $7; hfsc_opts.realtime.used = 1; } | UPPERLIMIT bandwidth { @@ -1389,14 +1438,15 @@ hfscopts_item : LINKSHARE bandwidth { hfsc_opts.upperlimit.m2 = $2; hfsc_opts.upperlimit.used = 1; } - | UPPERLIMIT '(' bandwidth number bandwidth ')' { + | UPPERLIMIT '(' bandwidth comma number comma bandwidth ')' + { if (hfsc_opts.upperlimit.used) { yyerror("upperlimit already specified"); YYERROR; } hfsc_opts.upperlimit.m1 = $3; - hfsc_opts.upperlimit.d = $4; - hfsc_opts.upperlimit.m2 = $5; + hfsc_opts.upperlimit.d = $5; + hfsc_opts.upperlimit.m2 = $7; hfsc_opts.upperlimit.used = 1; } | STRING { @@ -1482,6 +1532,7 @@ pfrule : action dir logquick interface route af proto fromto r.direction = $2; r.log = $3.log; r.quick = $3.quick; + r.prob = $9.prob; r.af = $6; if ($9.tag) @@ -1560,6 +1611,7 @@ pfrule : action dir logquick interface route af proto fromto YYERROR; } srctrack = o->data.src_track; + r.rule_flag |= PFRULE_SRCTRACK; break; case PF_STATE_OPT_MAX_SRC_STATES: if (r.max_src_states) { @@ -1568,7 +1620,7 @@ pfrule : action dir logquick interface route af proto fromto "multiple definitions"); YYERROR; } - if (o->data.max_src_nodes == 0) { + if (o->data.max_src_states == 0) { yyerror("'max-src-states' must " "be > 0"); YYERROR; @@ -1577,6 +1629,66 @@ pfrule : action dir logquick interface route af proto fromto o->data.max_src_states; r.rule_flag |= PFRULE_SRCTRACK; break; + case PF_STATE_OPT_OVERLOAD: + if (r.overload_tblname[0]) { + yyerror("multiple 'overload' " + "table definitions"); + YYERROR; + } + if (strlcpy(r.overload_tblname, + o->data.overload.tblname, + PF_TABLE_NAME_SIZE) >= + PF_TABLE_NAME_SIZE) { + yyerror("state option: " + "strlcpy"); + YYERROR; + } + r.flush = o->data.overload.flush; + break; + case PF_STATE_OPT_MAX_SRC_CONN: + if (r.max_src_conn) { + yyerror("state option " + "'max-src-conn' " + "multiple definitions"); + YYERROR; + } + if (o->data.max_src_conn == 0) { + yyerror("'max-src-conn' " + "must be > 0"); + YYERROR; + } + r.max_src_conn = + o->data.max_src_conn; + r.rule_flag |= PFRULE_SRCTRACK | + PFRULE_RULESRCTRACK; + break; + case PF_STATE_OPT_MAX_SRC_CONN_RATE: + if (r.max_src_conn_rate.limit) { + yyerror("state option " + "'max-src-conn-rate' " + "multiple definitions"); + YYERROR; + } + if (!o->data.max_src_conn_rate.limit || + !o->data.max_src_conn_rate.seconds) { + yyerror("'max-src-conn-rate' " + "values must be > 0"); + YYERROR; + } + if (o->data.max_src_conn_rate.limit > + PF_THRESHOLD_MAX) { + yyerror("'max-src-conn-rate' " + "maximum rate must be < %u", + PF_THRESHOLD_MAX); + YYERROR; + } + r.max_src_conn_rate.limit = + o->data.max_src_conn_rate.limit; + r.max_src_conn_rate.seconds = + o->data.max_src_conn_rate.seconds; + r.rule_flag |= PFRULE_SRCTRACK | + PFRULE_RULESRCTRACK; + break; case PF_STATE_OPT_MAX_SRC_NODES: if (r.max_src_nodes) { yyerror("state option " @@ -1617,7 +1729,7 @@ pfrule : action dir logquick interface route af proto fromto o = o->next; free(p); } - if (srctrack) { + if (r.rule_flag & PFRULE_SRCTRACK) { if (srctrack == PF_SRCTRACK_GLOBAL && r.max_src_nodes) { yyerror("'max-src-nodes' is " @@ -1625,6 +1737,24 @@ pfrule : action dir logquick interface route af proto fromto "'source-track global'"); YYERROR; } + if (srctrack == PF_SRCTRACK_GLOBAL && + r.max_src_conn) { + yyerror("'max-src-conn' is " + "incompatible with " + "'source-track global'"); + YYERROR; + } + if (srctrack == PF_SRCTRACK_GLOBAL && + r.max_src_conn_rate.seconds) { + yyerror("'max-src-conn-rate' is " + "incompatible with " + "'source-track global'"); + YYERROR; + } + if (r.timeout[PFTM_SRC_NODE] < + r.max_src_conn_rate.seconds) + r.timeout[PFTM_SRC_NODE] = + r.max_src_conn_rate.seconds; r.rule_flag |= PFRULE_SRCTRACK; if (srctrack == PF_SRCTRACK_RULE) r.rule_flag |= PFRULE_RULESRCTRACK; @@ -1705,7 +1835,7 @@ pfrule : action dir logquick interface route af proto fromto expand_rule(&r, $4, $5.host, $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, - $9.uid, $9.gid, $9.icmpspec); + $9.uid, $9.gid, $9.icmpspec, ""); } ; @@ -1795,6 +1925,28 @@ filter_opt : USER uids { filter_opts.match_tag = $3; filter_opts.match_tag_not = $1; } + | PROBABILITY STRING { + char *e; + double p = strtod($2, &e); + + if (*e == '%') { + p *= 0.01; + e++; + } + if (*e) { + yyerror("invalid probability: %s", $2); + free($2); + YYERROR; + } + p = floor(p * (UINT_MAX+1.0) + 0.5); + if (p < 1.0 || p >= (UINT_MAX+1.0)) { + yyerror("invalid probability: %s", $2); + free($2); + YYERROR; + } + filter_opts.prob = (u_int32_t)p; + free($2); + } ; action : PASS { $$.b1 = PF_PASS; $$.b2 = $$.w = 0; } @@ -1906,13 +2058,6 @@ if_item_not : not if_item { $$ = $2; $$->not = $1; } if_item : STRING { struct node_host *n; - if ((n = ifa_exists($1, 1)) == NULL) { -#ifndef __FreeBSD__ - yyerror("unknown interface %s", $1); - free($1); - YYERROR; -#endif - } $$ = calloc(1, sizeof(struct node_if)); if ($$ == NULL) err(1, "if_item: calloc"); @@ -1923,13 +2068,11 @@ if_item : STRING { yyerror("interface name too long"); YYERROR; } + + if ((n = ifa_exists($1, 1)) != NULL) + $$->ifa_flags = n->ifa_flags; + free($1); -#ifdef __FreeBSD__ - if (n == NULL) - $$->ifa_flags = PF_IFA_FLAG_DYNAMIC; - else /* XXX ugly */ -#endif - $$->ifa_flags = n->ifa_flags; $$->not = 0; $$->next = NULL; $$->tail = $$; @@ -2144,6 +2287,26 @@ host : STRING { $$->next = NULL; $$->tail = $$; } + | ROUTE STRING { + $$ = calloc(1, sizeof(struct node_host)); + if ($$ == NULL) { + free($2); + err(1, "host: calloc"); + } + $$->addr.type = PF_ADDR_RTLABEL; + if (strlcpy($$->addr.v.rtlabelname, $2, + sizeof($$->addr.v.rtlabelname)) >= + sizeof($$->addr.v.rtlabelname)) { + yyerror("route label too long, max %u chars", + sizeof($$->addr.v.rtlabelname) - 1); + free($2); + free($$); + YYERROR; + } + $$->next = NULL; + $$->tail = $$; + free($2); + } ; number : STRING { @@ -2164,6 +2327,11 @@ dynaddr : '(' STRING ')' { char *p, *op; op = $2; + if (!isalpha(op[0])) { + yyerror("invalid interface name '%s'", op); + free(op); + YYERROR; + } while ((p = strrchr($2, ':')) != NULL) { if (!strcmp(p+1, "network")) flags |= PFI_AFLAG_NETWORK; @@ -2187,13 +2355,6 @@ dynaddr : '(' STRING ')' { "interface modifiers"); YYERROR; } - if (ifa_exists($2, 1) == NULL && strcmp($2, "self")) { -#ifndef __FreeBSD__ - yyerror("interface %s does not exist", $2); - free(op); - YYERROR; -#endif - } $$ = calloc(1, sizeof(struct node_host)); if ($$ == NULL) err(1, "address: calloc"); @@ -2281,7 +2442,7 @@ port : STRING { if (atoul($1, &ulval) == 0) { if (ulval > 65535) { free($1); - yyerror("illegal port value %d", + yyerror("illegal port value %lu", ulval); YYERROR; } @@ -2536,7 +2697,7 @@ icmp_item : icmptype { if (atoul($3, &ulval) == 0) { if (ulval > 255) { free($3); - yyerror("illegal icmp-code %d", ulval); + yyerror("illegal icmp-code %lu", ulval); YYERROR; } } else { @@ -2576,7 +2737,7 @@ icmp6_item : icmp6type { if (atoul($3, &ulval) == 0) { if (ulval > 255) { - yyerror("illegal icmp6-code %ld", + yyerror("illegal icmp6-code %lu", ulval); free($3); YYERROR; @@ -2608,7 +2769,7 @@ icmptype : STRING { if (atoul($1, &ulval) == 0) { if (ulval > 255) { - yyerror("illegal icmp-type %d", ulval); + yyerror("illegal icmp-type %lu", ulval); free($1); YYERROR; } @@ -2632,7 +2793,7 @@ icmp6type : STRING { if (atoul($1, &ulval) == 0) { if (ulval > 255) { - yyerror("illegal icmp6-type %d", ulval); + yyerror("illegal icmp6-type %lu", ulval); free($1); YYERROR; } @@ -2700,6 +2861,13 @@ keep : KEEP STATE state_opt_spec { } ; +flush : /* empty */ { $$ = 0; } + | FLUSH { $$ = PF_FLUSH; } + | FLUSH GLOBAL { + $$ = PF_FLUSH | PF_FLUSH_GLOBAL; + } + ; + state_opt_spec : '(' state_opt_list ')' { $$ = $2; } | /* empty */ { $$ = NULL; } ; @@ -2738,6 +2906,43 @@ state_opt_item : MAXIMUM number { $$->next = NULL; $$->tail = $$; } + | MAXSRCCONN number { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_MAX_SRC_CONN; + $$->data.max_src_conn = $2; + $$->next = NULL; + $$->tail = $$; + } + | MAXSRCCONNRATE number '/' number { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_MAX_SRC_CONN_RATE; + $$->data.max_src_conn_rate.limit = $2; + $$->data.max_src_conn_rate.seconds = $4; + $$->next = NULL; + $$->tail = $$; + } + | OVERLOAD '<' STRING '>' flush { + if (strlen($3) >= PF_TABLE_NAME_SIZE) { + yyerror("table name '%s' too long", $3); + free($3); + YYERROR; + } + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + if (strlcpy($$->data.overload.tblname, $3, + PF_TABLE_NAME_SIZE) >= PF_TABLE_NAME_SIZE) + errx(1, "state_opt_item: strlcpy"); + free($3); + $$->type = PF_STATE_OPT_OVERLOAD; + $$->data.overload.flush = $5; + $$->next = NULL; + $$->tail = $$; + } | MAXSRCNODES number { $$ = calloc(1, sizeof(struct node_state_opt)); if ($$ == NULL) @@ -3023,7 +3228,7 @@ nataction : no NAT natpass { } ; -natrule : nataction interface af proto fromto tag redirpool pool_opts +natrule : nataction interface af proto fromto tag tagged redirpool pool_opts { struct pf_rule r; @@ -3053,46 +3258,55 @@ natrule : nataction interface af proto fromto tag redirpool pool_opts YYERROR; } + if ($7.name) + if (strlcpy(r.match_tagname, $7.name, + 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 = $7.neg; + if (r.action == PF_NONAT || r.action == PF_NORDR) { - if ($7 != NULL) { + if ($8 != NULL) { yyerror("translation rule with 'no' " "does not need '->'"); YYERROR; } } else { - if ($7 == NULL || $7->host == NULL) { + if ($8 == NULL || $8->host == NULL) { yyerror("translation rule requires '-> " "address'"); YYERROR; } - if (!r.af && ! $7->host->ifindex) - r.af = $7->host->af; + if (!r.af && ! $8->host->ifindex) + r.af = $8->host->af; - remove_invalid_hosts(&$7->host, &r.af); - if (invalid_redirect($7->host, r.af)) + remove_invalid_hosts(&$8->host, &r.af); + if (invalid_redirect($8->host, r.af)) YYERROR; - if (check_netmask($7->host, r.af)) + if (check_netmask($8->host, r.af)) YYERROR; - r.rpool.proxy_port[0] = ntohs($7->rport.a); + r.rpool.proxy_port[0] = ntohs($8->rport.a); switch (r.action) { case PF_RDR: - if (!$7->rport.b && $7->rport.t && + if (!$8->rport.b && $8->rport.t && $5.dst.port != NULL) { r.rpool.proxy_port[1] = - ntohs($7->rport.a) + + ntohs($8->rport.a) + (ntohs( $5.dst.port->port[1]) - ntohs( $5.dst.port->port[0])); } else r.rpool.proxy_port[1] = - ntohs($7->rport.b); + ntohs($8->rport.b); break; case PF_NAT: r.rpool.proxy_port[1] = - ntohs($7->rport.b); + ntohs($8->rport.b); if (!r.rpool.proxy_port[0] && !r.rpool.proxy_port[1]) { r.rpool.proxy_port[0] = @@ -3107,25 +3321,25 @@ natrule : nataction interface af proto fromto tag redirpool pool_opts break; } - r.rpool.opts = $8.type; + r.rpool.opts = $9.type; if ((r.rpool.opts & PF_POOL_TYPEMASK) == - PF_POOL_NONE && ($7->host->next != NULL || - $7->host->addr.type == PF_ADDR_TABLE || - DYNIF_MULTIADDR($7->host->addr))) + PF_POOL_NONE && ($8->host->next != NULL || + $8->host->addr.type == PF_ADDR_TABLE || + DYNIF_MULTIADDR($8->host->addr))) r.rpool.opts = PF_POOL_ROUNDROBIN; if ((r.rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN && - disallow_table($7->host, "tables are only " + disallow_table($8->host, "tables are only " "supported in round-robin redirection " "pools")) YYERROR; if ((r.rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN && - disallow_alias($7->host, "interface (%s) " + disallow_alias($8->host, "interface (%s) " "is only supported in round-robin " "redirection pools")) YYERROR; - if ($7->host->next != NULL) { + if ($8->host->next != NULL) { if ((r.rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) { yyerror("only round-robin " @@ -3136,14 +3350,14 @@ natrule : nataction interface af proto fromto tag redirpool pool_opts } } - if ($8.key != NULL) - memcpy(&r.rpool.key, $8.key, + if ($9.key != NULL) + memcpy(&r.rpool.key, $9.key, sizeof(struct pf_poolhashkey)); - if ($8.opts) - r.rpool.opts |= $8.opts; + if ($9.opts) + r.rpool.opts |= $9.opts; - if ($8.staticport) { + if ($9.staticport) { if (r.action != PF_NAT) { yyerror("the 'static-port' option is " "only valid with nat rules"); @@ -3162,14 +3376,14 @@ natrule : nataction interface af proto fromto tag redirpool pool_opts r.rpool.proxy_port[1] = 0; } - expand_rule(&r, $2, $7 == NULL ? NULL : $7->host, $4, + expand_rule(&r, $2, $8 == NULL ? NULL : $8->host, $4, $5.src_os, $5.src.host, $5.src.port, $5.dst.host, - $5.dst.port, 0, 0, 0); - free($7); + $5.dst.port, 0, 0, 0, ""); + free($8); } ; -binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag +binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag tagged redirection { struct pf_rule binat; @@ -3190,8 +3404,9 @@ binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag binat.af = $8->af; if (!binat.af && $10 != NULL && $10->af) binat.af = $10->af; - if (!binat.af && $12 != NULL && $12->host) - binat.af = $12->host->af; + + if (!binat.af && $13 != NULL && $13->host) + binat.af = $13->host->af; if (!binat.af) { yyerror("address family (inet/inet6) " "undefined"); @@ -3204,6 +3419,7 @@ binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag binat.ifnot = $4->not; free($4); } + if ($11 != NULL) if (strlcpy(binat.tagname, $11, PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { @@ -3211,6 +3427,14 @@ binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag PF_TAG_NAME_SIZE - 1); YYERROR; } + if ($12.name) + if (strlcpy(binat.match_tagname, $12.name, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + YYERROR; + } + binat.match_tag_not = $12.neg; if ($6 != NULL) { binat.proto = $6->proto; @@ -3224,12 +3448,12 @@ binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag "interface (%s) as the source address of a binat " "rule")) YYERROR; - if ($12 != NULL && $12->host != NULL && disallow_table( - $12->host, "invalid use of table <%s> as the " + if ($13 != NULL && $13->host != NULL && disallow_table( + $13->host, "invalid use of table <%s> as the " "redirect address of a binat rule")) YYERROR; - if ($12 != NULL && $12->host != NULL && disallow_alias( - $12->host, "invalid use of interface (%s) as the " + if ($13 != NULL && $13->host != NULL && disallow_alias( + $13->host, "invalid use of interface (%s) as the " "redirect address of a binat rule")) YYERROR; @@ -3263,38 +3487,38 @@ binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag YYERROR; memcpy(&binat.dst.addr, &$10->addr, sizeof(binat.dst.addr)); - binat.dst.not = $10->not; + binat.dst.neg = $10->not; free($10); } if (binat.action == PF_NOBINAT) { - if ($12 != NULL) { + if ($13 != NULL) { yyerror("'no binat' rule does not need" " '->'"); YYERROR; } } else { - if ($12 == NULL || $12->host == NULL) { + if ($13 == NULL || $13->host == NULL) { yyerror("'binat' rule requires" " '-> address'"); YYERROR; } - remove_invalid_hosts(&$12->host, &binat.af); - if (invalid_redirect($12->host, binat.af)) + remove_invalid_hosts(&$13->host, &binat.af); + if (invalid_redirect($13->host, binat.af)) YYERROR; - if ($12->host->next != NULL) { + if ($13->host->next != NULL) { yyerror("binat rule must redirect to " "a single address"); YYERROR; } - if (check_netmask($12->host, binat.af)) + if (check_netmask($13->host, binat.af)) YYERROR; if (!PF_AZERO(&binat.src.addr.v.a.mask, binat.af) && !PF_AEQ(&binat.src.addr.v.a.mask, - &$12->host->addr.v.a.mask, binat.af)) { + &$13->host->addr.v.a.mask, binat.af)) { yyerror("'binat' source mask and " "redirect mask must be the same"); YYERROR; @@ -3304,15 +3528,15 @@ binatrule : no BINAT natpass interface af proto FROM host TO ipspec tag pa = calloc(1, sizeof(struct pf_pooladdr)); if (pa == NULL) err(1, "binat: calloc"); - pa->addr = $12->host->addr; + pa->addr = $13->host->addr; pa->ifname[0] = 0; TAILQ_INSERT_TAIL(&binat.rpool.list, pa, entries); - free($12); + free($13); } - pfctl_add_rule(pf, &binat); + pfctl_add_rule(pf, &binat, ""); } ; @@ -3320,18 +3544,15 @@ tag : /* empty */ { $$ = NULL; } | TAG STRING { $$ = $2; } ; +tagged : /* empty */ { $$.neg = 0; $$.name = NULL; } + | not TAGGED string { $$.neg = $1; $$.name = $3; } + ; + route_host : STRING { $$ = calloc(1, sizeof(struct node_host)); if ($$ == NULL) err(1, "route_host: calloc"); $$->ifname = $1; - if (ifa_exists($$->ifname, 0) == NULL) { - yyerror("routeto: unknown interface %s", - $$->ifname); - free($1); - free($$); - YYERROR; - } set_ipmask($$, 128); $$->next = NULL; $$->tail = $$; @@ -3339,11 +3560,6 @@ route_host : STRING { | '(' STRING host ')' { $$ = $3; $$->ifname = $2; - if (ifa_exists($$->ifname, 0) == NULL) { - yyerror("routeto: unknown interface %s", - $$->ifname); - YYERROR; - } } ; @@ -3446,6 +3662,8 @@ yesno : NO { $$ = 0; } if (!strcmp($1, "yes")) $$ = 1; else { + yyerror("invalid value '%s', expected 'yes' " + "or 'no'", $1); free($1); YYERROR; } @@ -3509,6 +3727,7 @@ rule_consistent(struct pf_rule *r) case PF_PASS: case PF_DROP: case PF_SCRUB: + case PF_NOSCRUB: problems = filter_consistent(r); break; case PF_NAT: @@ -3546,6 +3765,12 @@ filter_consistent(struct pf_rule *r) yyerror("must indicate address family with icmp-type/code"); problems++; } + if (r->overload_tblname[0] && + r->max_src_conn == 0 && r->max_src_conn_rate.seconds == 0) { + yyerror("'overload' requires 'max-src-conn' " + "or 'max-src-conn-rate'"); + problems++; + } if ((r->proto == IPPROTO_ICMP && r->af == AF_INET6) || (r->proto == IPPROTO_ICMPV6 && r->af == AF_INET)) { yyerror("proto %s doesn't match address family %s", @@ -3575,7 +3800,7 @@ filter_consistent(struct pf_rule *r) problems++; } if ((r->tagname[0] || r->match_tagname[0]) && !r->keep_state && - r->action == PF_PASS && !r->anchorname[0]) { + r->action == PF_PASS) { yyerror("tags cannot be used without keep state"); problems++; } @@ -3646,7 +3871,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, pf->ruleset, &ab, pf->tticket)) { + pf->anchor, &ab, pf->tticket)) { yyerror("cannot define table %s: %s", name, pfr_strerror(errno)); goto _error; @@ -4112,7 +4337,8 @@ expand_rule(struct pf_rule *r, struct node_proto *protos, struct node_os *src_oses, struct node_host *src_hosts, struct node_port *src_ports, struct node_host *dst_hosts, struct node_port *dst_ports, - struct node_uid *uids, struct node_gid *gids, struct node_icmp *icmp_types) + struct node_uid *uids, struct node_gid *gids, struct node_icmp *icmp_types, + const char *anchor_call) { sa_family_t af = r->af; int added = 0, error = 0; @@ -4165,11 +4391,12 @@ expand_rule(struct pf_rule *r, r->af = dst_host->af; if (*interface->ifname) - memcpy(r->ifname, interface->ifname, sizeof(r->ifname)); + strlcpy(r->ifname, interface->ifname, + sizeof(r->ifname)); else if (if_indextoname(src_host->ifindex, ifname)) - memcpy(r->ifname, ifname, sizeof(r->ifname)); + strlcpy(r->ifname, ifname, sizeof(r->ifname)); else if (if_indextoname(dst_host->ifindex, ifname)) - memcpy(r->ifname, ifname, sizeof(r->ifname)); + strlcpy(r->ifname, ifname, sizeof(r->ifname)); else memset(r->ifname, '\0', sizeof(r->ifname)); @@ -4196,12 +4423,12 @@ expand_rule(struct pf_rule *r, r->ifnot = interface->not; r->proto = proto->proto; r->src.addr = src_host->addr; - r->src.not = src_host->not; + r->src.neg = src_host->not; r->src.port[0] = src_port->port[0]; r->src.port[1] = src_port->port[1]; r->src.port_op = src_port->op; r->dst.addr = dst_host->addr; - r->dst.not = dst_host->not; + r->dst.neg = dst_host->not; r->dst.port[0] = dst_port->port[0]; r->dst.port[1] = dst_port->port[1]; r->dst.port_op = dst_port->op; @@ -4264,7 +4491,7 @@ expand_rule(struct pf_rule *r, yyerror("skipping rule due to errors"); else { r->nr = pf->rule_nr++; - pfctl_add_rule(pf, r); + pfctl_add_rule(pf, r, anchor_call); added++; } @@ -4286,6 +4513,42 @@ expand_rule(struct pf_rule *r, yyerror("rule expands to no valid combination"); } +int +expand_skip_interface(struct node_if *interfaces) +{ + int errs = 0; + + if (!interfaces || (!interfaces->next && !interfaces->not && + !strcmp(interfaces->ifname, "none"))) { + if (pf->opts & PF_OPT_VERBOSE) + printf("set skip on none\n"); + errs = pfctl_set_interface_flags(pf, "", PFI_IFLAG_SKIP, 0); + return (errs); + } + + if (pf->opts & PF_OPT_VERBOSE) + printf("set skip on {"); + LOOP_THROUGH(struct node_if, interface, interfaces, + if (pf->opts & PF_OPT_VERBOSE) + printf(" %s", interface->ifname); + if (interface->not) { + yyerror("skip on ! <interface> is not supported"); + errs++; + } else + errs += pfctl_set_interface_flags(pf, + interface->ifname, PFI_IFLAG_SKIP, 1); + ); + if (pf->opts & PF_OPT_VERBOSE) + printf(" }\n"); + + FREE_LIST(struct node_if, interfaces); + + if (errs) + return (1); + else + return (0); +} + #undef FREE_LIST #undef LOOP_THROUGH @@ -4336,6 +4599,7 @@ lookup(char *s) { "fingerprints", FINGERPRINTS}, { "flags", FLAGS}, { "floating", FLOATING}, + { "flush", FLUSH}, { "for", FOR}, { "fragment", FRAGMENT}, { "from", FROM}, @@ -4360,6 +4624,8 @@ lookup(char *s) { "loginterface", LOGINTERFACE}, { "max", MAXIMUM}, { "max-mss", MAXMSS}, + { "max-src-conn", MAXSRCCONN}, + { "max-src-conn-rate", MAXSRCCONNRATE}, { "max-src-nodes", MAXSRCNODES}, { "max-src-states", MAXSRCSTATES}, { "min-ttl", MINTTL}, @@ -4374,10 +4640,12 @@ lookup(char *s) { "optimization", OPTIMIZATION}, { "os", OS}, { "out", OUT}, + { "overload", OVERLOAD}, { "pass", PASS}, { "port", PORT}, { "priority", PRIORITY}, { "priq", PRIQ}, + { "probability", PROBABILITY}, { "proto", PROTO}, { "qlimit", QLIMIT}, { "queue", QUEUE}, @@ -4395,10 +4663,12 @@ lookup(char *s) { "return-icmp6", RETURNICMP6}, { "return-rst", RETURNRST}, { "round-robin", ROUNDROBIN}, + { "route", ROUTE}, { "route-to", ROUTETO}, { "rule", RULE}, { "scrub", SCRUB}, { "set", SET}, + { "skip", SKIP}, { "source-hash", SOURCEHASH}, { "source-track", SOURCETRACK}, { "state", STATE}, @@ -4850,7 +5120,7 @@ getservice(char *n) if (atoul(n, &ulval) == 0) { if (ulval > 65535) { - yyerror("illegal port value %d", ulval); + yyerror("illegal port value %lu", ulval); return (-1); } return (htons(ulval)); @@ -4900,7 +5170,7 @@ parseicmpspec(char *w, sa_family_t af) ulval = p->code; } if (ulval > 255) { - yyerror("invalid icmp code %ld", ulval); + yyerror("invalid icmp code %lu", ulval); return (0); } return (icmptype << 8 | ulval); @@ -4913,10 +5183,10 @@ pfctl_load_anchors(int dev, int opts, struct pfr_buffer *trans) TAILQ_FOREACH(la, &loadanchorshead, entries) { if (opts & PF_OPT_VERBOSE) - fprintf(stderr, "\nLoading anchor %s:%s from %s\n", - la->anchorname, la->rulesetname, la->filename); + fprintf(stderr, "\nLoading anchor %s from %s\n", + la->anchorname, la->filename); if (pfctl_rules(dev, la->filename, opts, la->anchorname, - la->rulesetname, trans) == -1) + trans) == -1) return (-1); } |