aboutsummaryrefslogtreecommitdiff
path: root/sbin/pfctl/parse.y
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/pfctl/parse.y')
-rw-r--r--sbin/pfctl/parse.y332
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);
+}