diff options
Diffstat (limited to 'sbin/pfctl/parse.y')
-rw-r--r-- | sbin/pfctl/parse.y | 332 |
1 files changed, 247 insertions, 85 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index 00c36b218055..0f7702fc4630 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -166,8 +166,8 @@ struct node_gid { }; struct node_icmp { - u_int8_t code; - u_int8_t type; + uint16_t code; + uint16_t type; u_int8_t proto; struct node_icmp *next; struct node_icmp *tail; @@ -238,6 +238,7 @@ static struct pool_opts { #define POM_TYPE 0x01 #define POM_STICKYADDRESS 0x02 #define POM_ENDPI 0x04 +#define POM_IPV6NH 0x08 u_int8_t opts; int type; int staticport; @@ -266,7 +267,7 @@ static struct filter_opts { #define FOM_SETTOS 0x0100 #define FOM_SCRUB_TCP 0x0200 #define FOM_SETPRIO 0x0400 -#define FOM_ONCE 0x1000 /* not yet implemmented */ +#define FOM_ONCE 0x1000 #define FOM_PRIO 0x2000 #define FOM_SETDELAY 0x4000 #define FOM_FRAGCACHE 0x8000 /* does not exist in OpenBSD */ @@ -371,8 +372,8 @@ int validate_range(uint8_t, uint16_t, uint16_t); 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 pfctl_rule *, int); -int filter_consistent(struct pfctl_rule *, int); +int rule_consistent(struct pfctl_rule *); +int filter_consistent(struct pfctl_rule *); int nat_consistent(struct pfctl_rule *); int rdr_consistent(struct pfctl_rule *); int process_tabledef(char *, struct table_opts *, int); @@ -402,7 +403,7 @@ void expand_rule(struct pfctl_rule *, bool, struct node_if *, 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_if *, - struct node_icmp *, const char *); + struct node_icmp *); int expand_altq(struct pf_altq *, struct node_if *, struct node_queue *, struct node_queue_bw bwspec, struct node_queue_opt *); @@ -419,6 +420,8 @@ int rt_tableid_max(void); void mv_rules(struct pfctl_ruleset *, struct pfctl_ruleset *); void mv_eth_rules(struct pfctl_eth_ruleset *, struct pfctl_eth_ruleset *); +void mv_tables(struct pfctl *, struct pfr_ktablehead *, + struct pfctl_anchor *, struct pfctl_anchor *); 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); @@ -429,6 +432,7 @@ int filteropts_to_rule(struct pfctl_rule *, struct filter_opts *); struct node_mac* node_mac_from_string(const char *); struct node_mac* node_mac_from_string_masklen(const char *, int); struct node_mac* node_mac_from_string_mask(const char *, const char *); +static bool pfctl_setup_anchor(struct pfctl_rule *, struct pfctl *, char *); static TAILQ_HEAD(loadanchorshead, loadanchors) loadanchorshead = TAILQ_HEAD_INITIALIZER(loadanchorshead); @@ -538,12 +542,12 @@ int parseport(char *, struct range *r, int); %token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME %token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL %token DNPIPE DNQUEUE RIDENTIFIER -%token LOAD RULESET_OPTIMIZATION PRIO +%token LOAD RULESET_OPTIMIZATION PRIO ONCE %token STICKYADDRESS ENDPI MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW ALLOW_RELATED %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 +%token BINATTO MAXPKTRATE MAXPKTSIZE IPV6NH %token <v.string> STRING %token <v.number> NUMBER %token <v.i> PORTBINARY @@ -948,6 +952,7 @@ anchorname : STRING { pfa_anchorlist : /* empty */ | pfa_anchorlist '\n' + | pfa_anchorlist tabledef '\n' | pfa_anchorlist pfrule '\n' | pfa_anchorlist anchorrule '\n' | pfa_anchorlist include '\n' @@ -973,7 +978,7 @@ pfa_anchor : '{' 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"); + err(1, "pfa_anchor: pf_find_or_create_ruleset (%s)", ta); pf->astack[pf->asd] = rs->anchor; pf->anchor = rs->anchor; } '\n' pfa_anchorlist '}' @@ -998,43 +1003,9 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto } pfctl_init_rule(&r); + if (! pfctl_setup_anchor(&r, pf, $2)) + YYERROR; - if (pf->astack[pf->asd + 1]) { - if ($2 && strchr($2, '/') != NULL) { - free($2); - yyerror("anchor paths containing '/' " - "cannot be used for inline anchors."); - YYERROR; - } - - /* Move inline rules into relative location. */ - pfctl_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.quick = $4.quick; r.af = $6; @@ -1070,8 +1041,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto expand_rule(&r, false, $5, NULL, NULL, NULL, $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, - $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec, - pf->astack[pf->asd + 1] ? pf->alast->name : $2); + $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec); free($2); pf->astack[pf->asd + 1] = NULL; } @@ -1084,6 +1054,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto } pfctl_init_rule(&r); + if (! pfctl_setup_anchor(&r, pf, $2)) + YYERROR; r.action = PF_NAT; r.af = $4; @@ -1094,7 +1066,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto expand_rule(&r, false, $3, NULL, NULL, NULL, $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, - $6.dst.port, 0, 0, 0, 0, $2); + $6.dst.port, 0, 0, 0, 0); free($2); } | RDRANCHOR string interface af proto fromto rtable { @@ -1106,6 +1078,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto } pfctl_init_rule(&r); + if (! pfctl_setup_anchor(&r, pf, $2)) + YYERROR; r.action = PF_RDR; r.af = $4; @@ -1137,7 +1111,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto expand_rule(&r, false, $3, NULL, NULL, NULL, $5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host, - $6.dst.port, 0, 0, 0, 0, $2); + $6.dst.port, 0, 0, 0, 0); free($2); } | BINATANCHOR string interface af proto fromto rtable { @@ -1149,6 +1123,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto } pfctl_init_rule(&r); + if (! pfctl_setup_anchor(&r, pf, $2)) + YYERROR; r.action = PF_BINAT; r.af = $4; @@ -1173,7 +1149,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto decide_address_family($6.src.host, &r.af); decide_address_family($6.dst.host, &r.af); - pfctl_append_rule(pf, &r, $2); + pfctl_append_rule(pf, &r); free($2); } ; @@ -1460,7 +1436,7 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts expand_rule(&r, false, $4, NULL, NULL, NULL, $6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host, - $7.dst.port, NULL, NULL, NULL, NULL, ""); + $7.dst.port, NULL, NULL, NULL, NULL); } ; @@ -1625,7 +1601,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { if (h != NULL) expand_rule(&r, false, j, NULL, NULL, 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)); @@ -1648,7 +1624,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { expand_rule(&r, false, NULL, NULL, NULL, NULL, NULL, NULL, h, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, ""); + NULL, NULL, NULL, NULL); } else free(hh); } @@ -2648,13 +2624,16 @@ pfrule : action dir logquick interface route af proto fromto YYERROR; } r.rt = $5.rt; - decide_address_family($5.redirspec->host, &r.af); - if (!(r.rule_flag & PFRULE_AFTO)) - remove_invalid_hosts(&($5.redirspec->host), &r.af); - if ($5.redirspec->host == NULL) { - yyerror("no routing address with " - "matching address family found."); - YYERROR; + + if (!($5.redirspec->pool_opts.opts & PF_POOL_IPV6NH)) { + decide_address_family($5.redirspec->host, &r.af); + if (!(r.rule_flag & PFRULE_AFTO)) + remove_invalid_hosts(&($5.redirspec->host), &r.af); + if ($5.redirspec->host == NULL) { + yyerror("no routing address with " + "matching address family found."); + YYERROR; + } } } #ifdef __FreeBSD__ @@ -2728,7 +2707,7 @@ pfrule : action dir logquick interface route af proto fromto expand_rule(&r, false, $4, $9.nat, $9.rdr, $5.redirspec, $7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host, - $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec, ""); + $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec); } ; @@ -2978,7 +2957,8 @@ filter_opt : USER uids { filter_opts.nat = $4; filter_opts.nat->af = $2; - if ($4->af && $4->af != $2) { + remove_invalid_hosts(&($4->host), &(filter_opts.nat->af)); + if ($4->host == NULL) { yyerror("af-to addresses must be in the " "target address family"); YYERROR; @@ -2998,8 +2978,9 @@ filter_opt : USER uids { filter_opts.nat->af = $2; filter_opts.rdr = $6; filter_opts.rdr->af = $2; - if (($4->af && $4->host->af != $2) || - ($6->af && $6->host->af != $2)) { + remove_invalid_hosts(&($4->host), &(filter_opts.nat->af)); + remove_invalid_hosts(&($6->host), &(filter_opts.rdr->af)); + if ($4->host == NULL || $6->host == NULL) { yyerror("af-to addresses must be in the " "target address family"); YYERROR; @@ -3026,6 +3007,9 @@ filter_opt : USER uids { } filter_opts.max_pkt_size = $2; } + | ONCE { + filter_opts.marker |= FOM_ONCE; + } | filter_sets ; @@ -4674,6 +4658,14 @@ pool_opt : BITMASK { pool_opts.marker |= POM_ENDPI; pool_opts.opts |= PF_POOL_ENDPI; } + | IPV6NH { + if (pool_opts.marker & POM_IPV6NH) { + yyerror("prefer-ipv6-nexthop cannot be redefined"); + YYERROR; + } + pool_opts.marker |= POM_IPV6NH; + pool_opts.opts |= PF_POOL_IPV6NH; + } | MAPEPORTSET number '/' number '/' number { if (pool_opts.mape.offset) { yyerror("map-e-portset cannot be redefined"); @@ -4813,6 +4805,12 @@ natrule : nataction interface af proto fromto tag tagged rtable "address'"); YYERROR; } + if ($9->pool_opts.opts & PF_POOL_IPV6NH) { + yyerror("The prefer-ipv6-nexthop option " + "can't be used for nat/rdr/binat pools" + ); + YYERROR; + } if (!r.af && ! $9->host->ifindex) r.af = $9->host->af; @@ -4844,7 +4842,7 @@ natrule : nataction interface af proto fromto tag tagged rtable expand_rule(&r, false, $2, NULL, $9, NULL, $4, $5.src_os, $5.src.host, $5.src.port, $5.dst.host, - $5.dst.port, 0, 0, 0, 0, ""); + $5.dst.port, 0, 0, 0, 0); } ; @@ -5023,7 +5021,7 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag free($13); } - pfctl_append_rule(pf, &binat, ""); + pfctl_append_rule(pf, &binat); } ; @@ -5074,13 +5072,6 @@ route_host : STRING { route_host_list : route_host optnl { $$ = $1; } | route_host_list comma route_host optnl { - if ($1->af == 0) - $1->af = $3->af; - if ($1->af != $3->af) { - yyerror("all pool addresses must be in the " - "same address family"); - YYERROR; - } $1->tail->next = $3; $1->tail = $3->tail; $$ = $1; @@ -5257,7 +5248,7 @@ disallow_alias(struct node_host *h, const char *fmt) } int -rule_consistent(struct pfctl_rule *r, int anchor_call) +rule_consistent(struct pfctl_rule *r) { int problems = 0; @@ -5267,7 +5258,7 @@ rule_consistent(struct pfctl_rule *r, int anchor_call) case PF_DROP: case PF_SCRUB: case PF_NOSCRUB: - problems = filter_consistent(r, anchor_call); + problems = filter_consistent(r); break; case PF_NAT: case PF_NONAT: @@ -5286,7 +5277,7 @@ rule_consistent(struct pfctl_rule *r, int anchor_call) } int -filter_consistent(struct pfctl_rule *r, int anchor_call) +filter_consistent(struct pfctl_rule *r) { int problems = 0; @@ -5443,6 +5434,7 @@ process_tabledef(char *name, struct table_opts *opts, int popts) { struct pfr_buffer ab; struct node_tinit *ti; + struct pfr_uktable *ukt; unsigned long maxcount; size_t s = sizeof(maxcount); @@ -5475,9 +5467,23 @@ process_tabledef(char *name, struct table_opts *opts, int popts) else if (pf->opts & PF_OPT_VERBOSE) fprintf(stderr, "%s:%d: skipping duplicate table checks" " for <%s>\n", file->name, yylval.lineno, name); + /* + * postpone definition of non-root tables to moment + * when path is fully resolved. + */ + if (pf->asd > 0) { + ukt = calloc(1, sizeof(struct pfr_uktable)); + if (ukt == NULL) { + DBGPRINT( + "%s:%d: not enough memory for <%s>\n", file->name, + yylval.lineno, name); + goto _error; + } + } else + ukt = NULL; if (!(pf->opts & PF_OPT_NOACTION) && pfctl_define_table(name, opts->flags, opts->init_addr, - pf->anchor->path, &ab, pf->anchor->ruleset.tticket)) { + pf->anchor->path, &ab, pf->anchor->ruleset.tticket, ukt)) { if (sysctlbyname("net.pf.request_maxcount", &maxcount, &s, NULL, 0) == -1) @@ -5493,6 +5499,28 @@ process_tabledef(char *name, struct table_opts *opts, int popts) goto _error; } + + if (ukt != NULL) { + ukt->pfrukt_init_addr = opts->init_addr; + if (RB_INSERT(pfr_ktablehead, &pfr_ktables, + &ukt->pfrukt_kt) != NULL) { + /* + * I think this should not happen, because + * pfctl_define_table() above does the same check + * effectively. + */ + DBGPRINT( + "%s:%d table %s already exists in %s\n", + file->name, yylval.lineno, + ukt->pfrukt_name, pf->anchor->path); + free(ukt); + goto _error; + } + DBGPRINT("%s %s@%s inserted to tree\n", + __func__, ukt->pfrukt_name, pf->anchor->path); + } else + DBGPRINT("%s ukt is null\n", __func__); + pf->tdirty = 1; pfr_buf_clear(&ab); return (0); @@ -6289,7 +6317,7 @@ expand_rule(struct pfctl_rule *r, bool keeprule, 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_if *rcv, struct node_icmp *icmp_types, const char *anchor_call) + struct node_if *rcv, struct node_icmp *icmp_types) { sa_family_t af = r->af; int added = 0, error = 0; @@ -6456,11 +6484,11 @@ expand_rule(struct pfctl_rule *r, bool keeprule, error += check_binat_redirspec(src_host, r, af); } - if (rule_consistent(r, anchor_call[0]) < 0 || error) + if (rule_consistent(r) < 0 || error) yyerror("skipping rule due to errors"); else { r->nr = pf->astack[pf->asd]->match++; - pfctl_append_rule(pf, r, anchor_call); + pfctl_append_rule(pf, r); added++; } @@ -6476,8 +6504,7 @@ expand_rule(struct pfctl_rule *r, bool keeprule, expand_rule(&rdr_rule, true, interface, NULL, rdr_redirspec, NULL, proto, src_os, dst_host, dst_port, - rdr_dst_host, src_port, uid, gid, rcv, icmp_type, - ""); + rdr_dst_host, src_port, uid, gid, rcv, icmp_type); } if (osrch && src_host->addr.type == PF_ADDR_DYNIFTL) { @@ -6671,6 +6698,7 @@ lookup(char *s) { "no-route", NOROUTE}, { "no-sync", NOSYNC}, { "on", ON}, + { "once", ONCE}, { "optimization", OPTIMIZATION}, { "os", OS}, { "out", OUT}, @@ -6678,6 +6706,7 @@ lookup(char *s) { "pass", PASS}, { "pflow", PFLOW}, { "port", PORT}, + { "prefer-ipv6-nexthop", IPV6NH}, { "prio", PRIO}, { "priority", PRIORITY}, { "priq", PRIQ}, @@ -7077,7 +7106,7 @@ pushfile(const char *name, int secret) free(nfile); return (NULL); } - } else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { + } else if ((nfile->stream = pfctl_fopen(nfile->name, "r")) == NULL) { warn("%s: %s", __func__, nfile->name); free(nfile->name); free(nfile); @@ -7263,6 +7292,61 @@ mv_eth_rules(struct pfctl_eth_ruleset *src, struct pfctl_eth_ruleset *dst) } void +mv_tables(struct pfctl *pf, struct pfr_ktablehead *ktables, + struct pfctl_anchor *a, struct pfctl_anchor *alast) +{ + struct pfr_ktable *kt, *kt_safe; + char new_path[PF_ANCHOR_MAXPATH]; + char *path_cut; + int sz; + struct pfr_uktable *ukt; + SLIST_HEAD(, pfr_uktable) ukt_list; + + /* + * Here we need to rename anchor path from temporal names such as + * _1/_2/foo to _1/bar/foo etc. + * + * This also means we need to remove and insert table to ktables + * tree as anchor path is being updated. + */ + SLIST_INIT(&ukt_list); + DBGPRINT("%s [ %s ] (%s)\n", __func__, a->path, alast->path); + RB_FOREACH_SAFE(kt, pfr_ktablehead, ktables, kt_safe) { + path_cut = strstr(kt->pfrkt_anchor, alast->path); + if (path_cut != NULL) { + path_cut += strlen(alast->path); + if (*path_cut) + sz = snprintf(new_path, sizeof (new_path), + "%s%s", a->path, path_cut); + else + sz = snprintf(new_path, sizeof (new_path), + "%s", a->path); + if (sz >= sizeof (new_path)) + errx(1, "new path is too long for %s@%s\n", + kt->pfrkt_name, kt->pfrkt_anchor); + + DBGPRINT("%s %s@%s -> %s@%s\n", __func__, + kt->pfrkt_name, kt->pfrkt_anchor, + kt->pfrkt_name, new_path); + RB_REMOVE(pfr_ktablehead, ktables, kt); + strlcpy(kt->pfrkt_anchor, new_path, + sizeof(kt->pfrkt_anchor)); + SLIST_INSERT_HEAD(&ukt_list, (struct pfr_uktable *)kt, + pfrukt_entry); + } + } + + while ((ukt = SLIST_FIRST(&ukt_list)) != NULL) { + SLIST_REMOVE_HEAD(&ukt_list, pfrukt_entry); + if (RB_INSERT(pfr_ktablehead, ktables, + (struct pfr_ktable *)ukt) != NULL) + errx(1, "%s@%s exists already\n", + ukt->pfrukt_name, + ukt->pfrukt_anchor); + } +} + +void decide_address_family(struct node_host *n, sa_family_t *af) { if (*af != 0 || n == NULL) @@ -7471,7 +7555,7 @@ parseport(char *port, struct range *r, int extensions) } int -pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans) +pfctl_load_anchors(int dev, struct pfctl *pf) { struct loadanchors *la; @@ -7480,7 +7564,7 @@ pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans) fprintf(stderr, "\nLoading anchor %s from %s\n", la->anchorname, la->filename); if (pfctl_rules(dev, la->filename, pf->opts, pf->optimize, - la->anchorname, trans) == -1) + la->anchorname, pf->trans) == -1) return (-1); } @@ -7628,6 +7712,14 @@ node_mac_from_string_mask(const char *str, const char *mask) int filteropts_to_rule(struct pfctl_rule *r, struct filter_opts *opts) { + if (opts->marker & FOM_ONCE) { + if ((r->action != PF_PASS && r->action != PF_DROP) || r->anchor) { + yyerror("'once' only applies to pass/block rules"); + return (1); + } + r->rule_flag |= PFRULE_ONCE; + } + r->keep_state = opts->keep.action; r->pktrate.limit = opts->pktrate.limit; r->pktrate.seconds = opts->pktrate.seconds; @@ -7715,3 +7807,73 @@ filteropts_to_rule(struct pfctl_rule *r, struct filter_opts *opts) return (0); } + +static bool +pfctl_setup_anchor(struct pfctl_rule *r, struct pfctl *pf, char *anchorname) +{ + char *p; + + if (pf->astack[pf->asd + 1]) { + if (anchorname && strchr(anchorname, '/') != NULL) { + free(anchorname); + yyerror("anchor paths containing '/' " + "cannot be used for inline anchors."); + return (false); + } + + /* Move inline rules into relative location. */ + pfctl_anchor_setup(r, + &pf->astack[pf->asd]->ruleset, + anchorname ? anchorname : 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); + return (false); + } + mv_rules(&pf->alast->ruleset, + &r->anchor->ruleset); + mv_tables(pf, &pfr_ktables, r->anchor, pf->alast); + } + pf_remove_if_empty_ruleset(&pf->alast->ruleset); + pf->alast = r->anchor; + } else { + if (! anchorname) { + yyerror("anchors without explicit " + "rules must specify a name"); + return (false); + } + /* + * Don't make non-brace anchors part of the main anchor pool. + */ + if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL) { + err(1, "anchorrule: calloc"); + } + pf_init_ruleset(&r->anchor->ruleset); + r->anchor->ruleset.anchor = r->anchor; + if (strlcpy(r->anchor->path, anchorname, + sizeof(r->anchor->path)) >= sizeof(r->anchor->path)) { + errx(1, "anchorrule: strlcpy"); + } + if ((p = strrchr(anchorname, '/')) != NULL) { + if (strlen(p) == 1) { + yyerror("anchorrule: bad anchor name %s", + anchorname); + return (false); + } + } else + p = anchorname; + if (strlcpy(r->anchor->name, p, + sizeof(r->anchor->name)) >= sizeof(r->anchor->name)) { + errx(1, "anchorrule: strlcpy"); + } + } + + return (true); +} |