aboutsummaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
Diffstat (limited to 'sbin')
-rw-r--r--sbin/mount_nfs/mount_nfs.c59
-rw-r--r--sbin/pfctl/parse.y569
-rw-r--r--sbin/pfctl/pfctl.c10
-rw-r--r--sbin/pfctl/pfctl.h2
-rw-r--r--sbin/pfctl/pfctl_parser.h1
-rw-r--r--sbin/pfctl/pfctl_radix.c23
-rw-r--r--sbin/pfctl/pfctl_table.c2
-rw-r--r--sbin/pfctl/tests/files/Makefile2
-rw-r--r--sbin/pfctl/tests/files/pf1026.ok2
-rw-r--r--sbin/pfctl/tests/files/pf1027.ok2
-rw-r--r--sbin/pfctl/tests/files/pf1028.in1
-rw-r--r--sbin/pfctl/tests/files/pf1028.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1029.in1
-rw-r--r--sbin/pfctl/tests/files/pf1029.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1030.in1
-rw-r--r--sbin/pfctl/tests/files/pf1030.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1031.in1
-rw-r--r--sbin/pfctl/tests/files/pf1031.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1032.in1
-rw-r--r--sbin/pfctl/tests/files/pf1032.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1033.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1033.in1
-rw-r--r--sbin/pfctl/tests/files/pf1034.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1034.in1
-rw-r--r--sbin/pfctl/tests/files/pf1035.in1
-rw-r--r--sbin/pfctl/tests/files/pf1035.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1036.in1
-rw-r--r--sbin/pfctl/tests/files/pf1036.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1037.in1
-rw-r--r--sbin/pfctl/tests/files/pf1037.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1038.in1
-rw-r--r--sbin/pfctl/tests/files/pf1038.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1039.in1
-rw-r--r--sbin/pfctl/tests/files/pf1039.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1040.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1040.in1
-rw-r--r--sbin/pfctl/tests/files/pf1040.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1041.in1
-rw-r--r--sbin/pfctl/tests/files/pf1041.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1042.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1042.in1
-rw-r--r--sbin/pfctl/tests/files/pf1043.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1043.in1
-rw-r--r--sbin/pfctl/tests/files/pf1044.in1
-rw-r--r--sbin/pfctl/tests/files/pf1044.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1045.in1
-rw-r--r--sbin/pfctl/tests/files/pf1045.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1046.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1046.in1
-rw-r--r--sbin/pfctl/tests/files/pf1047.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1047.in1
-rw-r--r--sbin/pfctl/tests/files/pf1048.in1
-rw-r--r--sbin/pfctl/tests/files/pf1048.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1049.in1
-rw-r--r--sbin/pfctl/tests/files/pf1049.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1050.in1
-rw-r--r--sbin/pfctl/tests/files/pf1050.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1051.in1
-rw-r--r--sbin/pfctl/tests/files/pf1051.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1052.in1
-rw-r--r--sbin/pfctl/tests/files/pf1052.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1053.in1
-rw-r--r--sbin/pfctl/tests/files/pf1053.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1054.in3
-rw-r--r--sbin/pfctl/tests/files/pf1054.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1055.in1
-rw-r--r--sbin/pfctl/tests/files/pf1055.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1056.in1
-rw-r--r--sbin/pfctl/tests/files/pf1056.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1057.in1
-rw-r--r--sbin/pfctl/tests/files/pf1057.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1058.in1
-rw-r--r--sbin/pfctl/tests/files/pf1058.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1059.in1
-rw-r--r--sbin/pfctl/tests/files/pf1059.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1060.in1
-rw-r--r--sbin/pfctl/tests/files/pf1060.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1061.in1
-rw-r--r--sbin/pfctl/tests/files/pf1061.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1062.in1
-rw-r--r--sbin/pfctl/tests/files/pf1062.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1063.in1
-rw-r--r--sbin/pfctl/tests/files/pf1063.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1064.in1
-rw-r--r--sbin/pfctl/tests/files/pf1064.ok1
-rw-r--r--sbin/pfctl/tests/pfctl_test.c188
-rw-r--r--sbin/pfctl/tests/pfctl_test_list.inc37
87 files changed, 582 insertions, 392 deletions
diff --git a/sbin/mount_nfs/mount_nfs.c b/sbin/mount_nfs/mount_nfs.c
index 189bdd70b398..6ba51eeec588 100644
--- a/sbin/mount_nfs/mount_nfs.c
+++ b/sbin/mount_nfs/mount_nfs.c
@@ -587,6 +587,7 @@ getnfsargs(char **specp, char **hostpp, struct iovec **iov, int *iovlen)
char *hostp, *delimp, *errstr, *spec;
size_t len;
static char nam[MNAMELEN + 1], pname[MAXHOSTNAMELEN + 5];
+ bool resolved;
spec = *specp;
if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL &&
@@ -643,30 +644,7 @@ getnfsargs(char **specp, char **hostpp, struct iovec **iov, int *iovlen)
else if (nfsproto == IPPROTO_UDP)
hints.ai_socktype = SOCK_DGRAM;
- if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) {
- hints.ai_flags = AI_CANONNAME;
- if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs))
- != 0) {
- if (portspec == NULL)
- errx(1, "%s: %s", hostp, gai_strerror(ecode));
- else
- errx(1, "%s:%s: %s", hostp, portspec,
- gai_strerror(ecode));
- return (0);
- }
-
- /*
- * For a Kerberized nfs mount where the "principal"
- * argument has not been set, add it here.
- */
- if (got_principal == 0 && secflavor != AUTH_SYS &&
- ai_nfs->ai_canonname != NULL) {
- snprintf(pname, sizeof (pname), "nfs@%s",
- ai_nfs->ai_canonname);
- build_iovec(iov, iovlen, "principal", pname,
- strlen(pname) + 1);
- }
- }
+ resolved = (getaddrinfo(hostp, portspec, &hints, &ai_nfs) == 0);
if ((opflags & (BGRNDNOW | ISBGRND)) == BGRNDNOW) {
warnx("Mount %s:%s, backgrounding",
@@ -678,6 +656,37 @@ getnfsargs(char **specp, char **hostpp, struct iovec **iov, int *iovlen)
ret = TRYRET_LOCALERR;
for (;;) {
+ if (!resolved) {
+ hints.ai_flags = AI_CANONNAME;
+ if ((ecode = getaddrinfo(hostp, portspec, &hints,
+ &ai_nfs)) != 0) {
+ if (portspec == NULL)
+ warnx("%s: %s", hostp,
+ gai_strerror(ecode));
+ else
+ warnx("%s:%s: %s", hostp, portspec,
+ gai_strerror(ecode));
+ if (ecode == EAI_AGAIN &&
+ (opflags & (BGRNDNOW | BGRND)))
+ goto retry;
+ else
+ exit(1);
+ }
+ resolved = true;
+ /*
+ * For a Kerberized nfs mount where the
+ * "principal" argument has not been set, add
+ * it here.
+ */
+ if (got_principal == 0 && secflavor != AUTH_SYS &&
+ ai_nfs->ai_canonname != NULL) {
+ snprintf(pname, sizeof (pname), "nfs@%s",
+ ai_nfs->ai_canonname);
+ build_iovec(iov, iovlen, "principal", pname,
+ strlen(pname) + 1);
+ }
+ }
+
/*
* Try each entry returned by getaddrinfo(). Note the
* occurrence of remote errors by setting `remoteerr'.
@@ -705,7 +714,7 @@ getnfsargs(char **specp, char **hostpp, struct iovec **iov, int *iovlen)
/* Exit if all errors were local. */
if (!remoteerr)
exit(1);
-
+retry:
/*
* If retrycnt == 0, we are to keep retrying forever.
* Otherwise decrement it, and exit if it hits zero.
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 8f732bdf268a..ea81efd7fd94 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -227,10 +227,6 @@ struct range {
int b;
int t;
};
-struct redirection {
- struct node_host *host;
- struct range rport;
-};
static struct pool_opts {
int marker;
@@ -245,9 +241,10 @@ static struct pool_opts {
} pool_opts;
struct redirspec {
- struct redirection *rdr;
+ struct node_host *host;
+ struct range rport;
struct pool_opts pool_opts;
- int af;
+ int af;
};
static struct filter_opts {
@@ -302,9 +299,8 @@ static struct filter_opts {
struct node_host *addr;
u_int16_t port;
} divert;
- struct redirspec nat;
- struct redirspec rdr;
- struct redirspec rroute;
+ struct redirspec *nat;
+ struct redirspec *rdr;
/* new-style scrub opts */
int nodf;
int minttl;
@@ -382,9 +378,11 @@ void expand_eth_rule(struct pfctl_eth_rule *,
struct node_mac *, struct node_mac *,
struct node_host *, struct node_host *, const char *,
const char *);
+int apply_rdr_ports(struct pfctl_rule *r, struct pfctl_pool *, struct redirspec *);
+int apply_nat_ports(struct pfctl_pool *, struct redirspec *);
+int apply_redirspec(struct pfctl_pool *, struct redirspec *);
void expand_rule(struct pfctl_rule *, struct node_if *,
struct redirspec *, struct redirspec *, struct redirspec *,
- struct node_host *, struct node_host *, 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_if *,
@@ -463,13 +461,10 @@ typedef struct {
} etheraddr;
char *bridge_to;
struct {
- struct node_host *host;
+ struct redirspec *redirspec;
u_int8_t rt;
- u_int8_t pool_opts;
- sa_family_t af;
- struct pf_poolhashkey *key;
} route;
- struct redirection *redirection;
+ struct redirspec *redirspec;
struct {
int action;
struct node_state_opt *options;
@@ -554,14 +549,15 @@ int parseport(char *, struct range *r, int);
%type <v.fromto> fromto l3fromto
%type <v.peer> ipportspec from to
%type <v.host> ipspec toipspec xhost host dynaddr host_list
-%type <v.host> redir_host_list redirspec
-%type <v.host> route_host route_host_list routespec
+%type <v.host> redir_host redir_host_list routespec
+%type <v.host> route_host route_host_list
%type <v.os> os xos os_list
%type <v.port> portspec port_list port_item
%type <v.uid> uids uid_list uid_item
%type <v.gid> gids gid_list gid_item
%type <v.route> route
-%type <v.redirection> redirection redirpool
+%type <v.redirspec> no_port_redirspec port_redirspec route_redirspec
+%type <v.redirspec> binat_redirspec nat_redirspec
%type <v.string> label stringall tag anchorname
%type <v.string> string varstring numberstring
%type <v.keep_state> keep
@@ -965,7 +961,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
YYERROR;
}
- memset(&r, 0, sizeof(r));
+ pfctl_init_rule(&r);
+
if (pf->astack[pf->asd + 1]) {
if ($2 && strchr($2, '/') != NULL) {
free($2);
@@ -978,7 +975,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
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");
@@ -1082,7 +1079,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
decide_address_family($8.src.host, &r.af);
decide_address_family($8.dst.host, &r.af);
- expand_rule(&r, $5, NULL, NULL, NULL, NULL, NULL, NULL,
+ expand_rule(&r, $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);
@@ -1097,7 +1094,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
YYERROR;
}
- memset(&r, 0, sizeof(r));
+ pfctl_init_rule(&r);
+
r.action = PF_NAT;
r.af = $4;
r.rtableid = $7;
@@ -1105,7 +1103,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);
- expand_rule(&r, $3, NULL, NULL, NULL, NULL, NULL, NULL,
+ expand_rule(&r, $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);
free($2);
@@ -1118,7 +1116,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
YYERROR;
}
- memset(&r, 0, sizeof(r));
+ pfctl_init_rule(&r);
+
r.action = PF_RDR;
r.af = $4;
r.rtableid = $7;
@@ -1147,7 +1146,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
r.dst.port_op = $6.dst.port->op;
}
- expand_rule(&r, $3, NULL, NULL, NULL, NULL, NULL, NULL,
+ expand_rule(&r, $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);
free($2);
@@ -1160,7 +1159,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
YYERROR;
}
- memset(&r, 0, sizeof(r));
+ pfctl_init_rule(&r);
+
r.action = PF_BINAT;
r.af = $4;
r.rtableid = $7;
@@ -1425,7 +1425,7 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts
if (check_rulestate(PFCTL_STATE_SCRUB))
YYERROR;
- memset(&r, 0, sizeof(r));
+ pfctl_init_rule(&r);
r.action = $1.b1;
r.direction = $2;
@@ -1470,7 +1470,7 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts
r.match_tag_not = $8.match_tag_not;
r.rtableid = $8.rtableid;
- expand_rule(&r, $4, NULL, NULL, NULL, NULL, NULL, NULL,
+ expand_rule(&r, $4, NULL, NULL, NULL,
$6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host,
$7.dst.port, NULL, NULL, NULL, NULL, "");
}
@@ -1587,7 +1587,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
YYERROR;
for (i = $3; i; i = i->next) {
- bzero(&r, sizeof(r));
+ pfctl_init_rule(&r);
r.action = PF_DROP;
r.direction = PF_IN;
@@ -1635,9 +1635,9 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
}
if (h != NULL)
- expand_rule(&r, j, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, h, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, "");
+ expand_rule(&r, j, NULL, NULL, NULL,
+ NULL, NULL, h, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, "");
if ((i->ifa_flags & IFF_LOOPBACK) == 0) {
bzero(&r, sizeof(r));
@@ -1657,9 +1657,10 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
else
h = ifa_lookup(i->ifname, 0);
if (h != NULL)
- expand_rule(&r, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, h, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, "");
+ expand_rule(&r, NULL, NULL,
+ NULL, NULL, NULL, NULL, h,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, "");
} else
free(hh);
}
@@ -2371,7 +2372,7 @@ pfrule : action dir logquick interface route af proto fromto
if (check_rulestate(PFCTL_STATE_FILTER))
YYERROR;
- memset(&r, 0, sizeof(r));
+ pfctl_init_rule(&r);
r.action = $1.b1;
switch ($1.b2) {
@@ -2728,44 +2729,14 @@ pfrule : action dir logquick interface route af proto fromto
YYERROR;
}
r.rt = $5.rt;
- r.route.opts = $5.pool_opts;
- if ($5.key != NULL)
- memcpy(&r.route.key, $5.key,
- sizeof(struct pf_poolhashkey));
- }
- if (r.rt) {
- decide_address_family($5.host, &r.af);
+ decide_address_family($5.redirspec->host, &r.af);
if (!(r.rule_flag & PFRULE_AFTO))
- remove_invalid_hosts(&$5.host, &r.af);
- if ($5.host == NULL) {
+ remove_invalid_hosts(&($5.redirspec->host), &r.af);
+ if ($5.redirspec->host == NULL) {
yyerror("no routing address with "
"matching address family found.");
YYERROR;
}
- if ((r.route.opts & PF_POOL_TYPEMASK) ==
- PF_POOL_NONE && ($5.host->next != NULL ||
- $5.host->addr.type == PF_ADDR_TABLE ||
- DYNIF_MULTIADDR($5.host->addr)))
- r.route.opts |= PF_POOL_ROUNDROBIN;
- if ($5.host->next != NULL &&
- !PF_POOL_DYNTYPE(r.route.opts)) {
- yyerror("address pool option "
- "not supported by type");
- }
- if (!PF_POOL_DYNTYPE(r.route.opts) &&
- (disallow_table($5.host,
- "tables are not supported by pool type") ||
- disallow_alias($5.host,
- "interface (%s) is not supported by pool type")))
- YYERROR;
- if ($5.host->next != NULL) {
- if ((r.route.opts & PF_POOL_TYPEMASK) !=
- PF_POOL_ROUNDROBIN) {
- yyerror("r.route.opts must "
- "be PF_POOL_ROUNDROBIN");
- YYERROR;
- }
- }
}
if ($9.queues.qname != NULL) {
if (strlcpy(r.qname, $9.queues.qname,
@@ -2824,19 +2795,10 @@ pfrule : action dir logquick interface route af proto fromto
}
if ($9.marker & FOM_AFTO) {
- r.naf = $9.nat.af;
-
- r.nat.opts = $9.nat.pool_opts.type;
- r.nat.opts |= $9.nat.pool_opts.opts;
-
- if (!PF_POOL_DYNTYPE(r.nat.opts) &&
- disallow_table($9.nat.rdr->host, "tables are not "
- "supported by pool type"))
- YYERROR;
+ r.naf = $9.nat->af;
}
- expand_rule(&r, $4, &$9.nat, &$9.rdr, &$9.rroute,
- NULL, $9.nat.rdr ? $9.nat.rdr->host : NULL, $5.host,
+ expand_rule(&r, $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, "");
}
@@ -3053,8 +3015,8 @@ filter_opt : USER uids {
filter_opts.marker |= FOM_SCRUB_TCP;
filter_opts.marker |= $3.marker;
}
- | AFTO af FROM redirspec pool_opts {
- if (filter_opts.nat.rdr) {
+ | AFTO af FROM port_redirspec {
+ if (filter_opts.nat) {
yyerror("cannot respecify af-to");
YYERROR;
}
@@ -3062,26 +3024,18 @@ filter_opt : USER uids {
yyerror("no address family specified");
YYERROR;
}
+
+ filter_opts.nat = $4;
+ filter_opts.nat->af = $2;
if ($4->af && $4->af != $2) {
yyerror("af-to addresses must be in the "
"target address family");
YYERROR;
}
- filter_opts.nat.af = $2;
- filter_opts.nat.rdr = calloc(1, sizeof(struct redirection));
- if (filter_opts.nat.rdr == NULL)
- err(1, "af-to: calloc");
- filter_opts.nat.rdr->host = $4;
- memcpy(&filter_opts.nat.pool_opts, &$5,
- sizeof(filter_opts.nat.pool_opts));
- filter_opts.rdr.rdr =
- calloc(1, sizeof(struct redirection));
- bzero(&filter_opts.rdr.pool_opts,
- sizeof(filter_opts.rdr.pool_opts));
filter_opts.marker |= FOM_AFTO;
}
- | AFTO af FROM redirspec pool_opts TO redirspec pool_opts {
- if (filter_opts.nat.rdr) {
+ | AFTO af FROM port_redirspec TO port_redirspec {
+ if (filter_opts.nat) {
yyerror("cannot respecify af-to");
YYERROR;
}
@@ -3089,26 +3043,16 @@ filter_opt : USER uids {
yyerror("no address family specified");
YYERROR;
}
- if (($4->af && $4->af != $2) ||
- ($7->af && $7->af != $2)) {
+ filter_opts.nat = $4;
+ 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)) {
yyerror("af-to addresses must be in the "
"target address family");
YYERROR;
}
- filter_opts.nat.af = $2;
- filter_opts.nat.rdr = calloc(1, sizeof(struct redirection));
- if (filter_opts.nat.rdr == NULL)
- err(1, "af-to: calloc");
- filter_opts.nat.rdr->host = $4;
- memcpy(&filter_opts.nat.pool_opts, &$5,
- sizeof(filter_opts.nat.pool_opts));
- filter_opts.rdr.af = $2;
- filter_opts.rdr.rdr = calloc(1, sizeof(struct redirection));
- if (filter_opts.rdr.rdr == NULL)
- err(1, "af-to: calloc");
- filter_opts.rdr.rdr->host = $7;
- memcpy(&filter_opts.nat.pool_opts, &$8,
- sizeof(filter_opts.nat.pool_opts));
filter_opts.marker |= FOM_AFTO;
}
| filter_sets
@@ -4580,7 +4524,7 @@ portstar : numberstring {
}
;
-redirspec : host { $$ = $1; }
+redir_host : host { $$ = $1; }
| '{' optnl redir_host_list '}' { $$ = $3; }
;
@@ -4592,20 +4536,41 @@ redir_host_list : host optnl { $$ = $1; }
}
;
-redirpool : /* empty */ { $$ = NULL; }
- | ARROW redirspec {
- $$ = calloc(1, sizeof(struct redirection));
+/* Redirection without port */
+no_port_redirspec: redir_host pool_opts {
+ $$ = calloc(1, sizeof(struct redirspec));
if ($$ == NULL)
- err(1, "redirection: calloc");
- $$->host = $2;
+ err(1, "redirspec: calloc");
+ $$->host = $1;
+ $$->pool_opts = $2;
$$->rport.a = $$->rport.b = $$->rport.t = 0;
}
- | ARROW redirspec PORT portstar {
- $$ = calloc(1, sizeof(struct redirection));
+ ;
+
+/* Redirection with optional port */
+port_redirspec : no_port_redirspec;
+ | redir_host PORT portstar pool_opts {
+ $$ = calloc(1, sizeof(struct redirspec));
if ($$ == NULL)
- err(1, "redirection: calloc");
- $$->host = $2;
- $$->rport = $4;
+ err(1, "redirspec: calloc");
+ $$->host = $1;
+ $$->rport = $3;
+ $$->pool_opts = $4;
+ }
+
+/* Redirection with an arrow and an optional port: FreeBSD NAT rules */
+nat_redirspec : ARROW port_redirspec {
+ $$ = $2;
+ }
+ ;
+
+/* Redirection with interfaces and without ports: route-to rules */
+route_redirspec : routespec pool_opts {
+ $$ = calloc(1, sizeof(struct redirspec));
+ if ($$ == NULL)
+ err(1, "redirspec: calloc");
+ $$->host = $1;
+ $$->pool_opts = $2;
}
;
@@ -4756,18 +4721,18 @@ pool_opt : BITMASK {
}
;
-redirection : /* empty */ { $$ = NULL; }
+binat_redirspec : /* empty */ { $$ = NULL; }
| ARROW host {
- $$ = calloc(1, sizeof(struct redirection));
+ $$ = calloc(1, sizeof(struct redirspec));
if ($$ == NULL)
- err(1, "redirection: calloc");
+ err(1, "redirspec: calloc");
$$->host = $2;
$$->rport.a = $$->rport.b = $$->rport.t = 0;
}
| ARROW host PORT portstar {
- $$ = calloc(1, sizeof(struct redirection));
+ $$ = calloc(1, sizeof(struct redirspec));
if ($$ == NULL)
- err(1, "redirection: calloc");
+ err(1, "redirspec: calloc");
$$->host = $2;
$$->rport = $4;
}
@@ -4808,7 +4773,7 @@ nataction : no NAT natpasslog {
;
natrule : nataction interface af proto fromto tag tagged rtable
- redirpool pool_opts
+ nat_redirspec
{
struct pfctl_rule r;
struct node_state_opt *o;
@@ -4816,7 +4781,7 @@ natrule : nataction interface af proto fromto tag tagged rtable
if (check_rulestate(PFCTL_STATE_NAT))
YYERROR;
- memset(&r, 0, sizeof(r));
+ pfctl_init_rule(&r);
r.action = $1.b1;
r.natpass = $1.b2;
@@ -4875,110 +4840,6 @@ natrule : nataction interface af proto fromto tag tagged rtable
}
if (check_netmask($9->host, r.af))
YYERROR;
-
- r.rdr.proxy_port[0] = ntohs($9->rport.a);
-
- switch (r.action) {
- case PF_RDR:
- if (!$9->rport.b && $9->rport.t &&
- $5.dst.port != NULL) {
- r.rdr.proxy_port[1] =
- ntohs($9->rport.a) +
- (ntohs(
- $5.dst.port->port[1]) -
- ntohs(
- $5.dst.port->port[0]));
- } else
- r.rdr.proxy_port[1] =
- ntohs($9->rport.b);
- break;
- case PF_NAT:
- r.rdr.proxy_port[1] =
- ntohs($9->rport.b);
- if (!r.rdr.proxy_port[0] &&
- !r.rdr.proxy_port[1]) {
- r.rdr.proxy_port[0] =
- PF_NAT_PROXY_PORT_LOW;
- r.rdr.proxy_port[1] =
- PF_NAT_PROXY_PORT_HIGH;
- } else if (!r.rdr.proxy_port[1])
- r.rdr.proxy_port[1] =
- r.rdr.proxy_port[0];
- break;
- default:
- break;
- }
-
- r.rdr.opts = $10.type;
- if ((r.rdr.opts & PF_POOL_TYPEMASK) ==
- PF_POOL_NONE && ($9->host->next != NULL ||
- $9->host->addr.type == PF_ADDR_TABLE ||
- DYNIF_MULTIADDR($9->host->addr)))
- r.rdr.opts = PF_POOL_ROUNDROBIN;
- if (!PF_POOL_DYNTYPE(r.rdr.opts) &&
- (disallow_table($9->host,
- "tables are not supported by pool type") ||
- disallow_alias($9->host,
- "interface (%s) is not supported by pool type")))
- YYERROR;
- if ($9->host->next != NULL) {
- if ((r.rdr.opts & PF_POOL_TYPEMASK) !=
- PF_POOL_ROUNDROBIN) {
- yyerror("only round-robin "
- "valid for multiple "
- "redirection addresses");
- YYERROR;
- }
- }
- }
-
- if ($10.key != NULL)
- memcpy(&r.rdr.key, $10.key,
- sizeof(struct pf_poolhashkey));
-
- if ($10.opts)
- r.rdr.opts |= $10.opts;
-
- if ($10.staticport) {
- if (r.action != PF_NAT) {
- yyerror("the 'static-port' option is "
- "only valid with nat rules");
- YYERROR;
- }
- if (r.rdr.proxy_port[0] !=
- PF_NAT_PROXY_PORT_LOW &&
- r.rdr.proxy_port[1] !=
- PF_NAT_PROXY_PORT_HIGH) {
- yyerror("the 'static-port' option can't"
- " be used when specifying a port"
- " range");
- YYERROR;
- }
- r.rdr.proxy_port[0] = 0;
- r.rdr.proxy_port[1] = 0;
- }
-
- if ($10.mape.offset) {
- if (r.action != PF_NAT) {
- yyerror("the 'map-e-portset' option is"
- " only valid with nat rules");
- YYERROR;
- }
- if ($10.staticport) {
- yyerror("the 'map-e-portset' option"
- " can't be used 'static-port'");
- YYERROR;
- }
- if (r.rdr.proxy_port[0] !=
- PF_NAT_PROXY_PORT_LOW &&
- r.rdr.proxy_port[1] !=
- PF_NAT_PROXY_PORT_HIGH) {
- yyerror("the 'map-e-portset' option"
- " can't be used when specifying"
- " a port range");
- YYERROR;
- }
- r.rdr.mape = $10.mape;
}
o = keep_state_defaults;
@@ -4996,16 +4857,14 @@ natrule : nataction interface af proto fromto tag tagged rtable
o = o->next;
}
- expand_rule(&r, $2, NULL, NULL, NULL,
- $9 == NULL ? NULL : $9->host, NULL, NULL, $4,
+ expand_rule(&r, $2, NULL, $9, NULL, $4,
$5.src_os, $5.src.host, $5.src.port, $5.dst.host,
$5.dst.port, 0, 0, 0, 0, "");
- free($9);
}
;
binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag
- tagged rtable redirection
+ tagged rtable binat_redirspec
{
struct pfctl_rule binat;
struct pf_pooladdr *pa;
@@ -5016,7 +4875,7 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag
"permitted as a binat destination"))
YYERROR;
- memset(&binat, 0, sizeof(binat));
+ pfctl_init_rule(&binat);
if ($1 && $3.b1) {
yyerror("\"pass\" not valid with \"no\"");
@@ -5249,36 +5108,23 @@ routespec : route_host { $$ = $1; }
;
route : /* empty */ {
- $$.host = NULL;
- $$.rt = 0;
- $$.pool_opts = 0;
+ $$.rt = PF_NOPFROUTE;
}
| FASTROUTE {
/* backwards-compat */
- $$.host = NULL;
- $$.rt = 0;
- $$.pool_opts = 0;
+ $$.rt = PF_NOPFROUTE;
}
- | ROUTETO routespec pool_opts {
- $$.host = $2;
+ | ROUTETO route_redirspec {
$$.rt = PF_ROUTETO;
- $$.pool_opts = $3.type | $3.opts;
- if ($3.key != NULL)
- $$.key = $3.key;
+ $$.redirspec = $2;
}
- | REPLYTO routespec pool_opts {
- $$.host = $2;
+ | REPLYTO route_redirspec {
$$.rt = PF_REPLYTO;
- $$.pool_opts = $3.type | $3.opts;
- if ($3.key != NULL)
- $$.key = $3.key;
+ $$.redirspec = $2;
}
- | DUPTO routespec pool_opts {
- $$.host = $2;
+ | DUPTO route_redirspec {
$$.rt = PF_DUPTO;
- $$.pool_opts = $3.type | $3.opts;
- if ($3.key != NULL)
- $$.key = $3.key;
+ $$.redirspec = $2;
}
;
@@ -6161,12 +6007,144 @@ expand_eth_rule(struct pfctl_eth_rule *r,
FREE_LIST(struct node_host, ipdsts);
}
+int
+apply_rdr_ports(struct pfctl_rule *r, struct pfctl_pool *rpool, struct redirspec *rs)
+{
+ if (rs == NULL)
+ return 0;
+
+ rpool->proxy_port[0] = ntohs(rs->rport.a);
+
+ if (!rs->rport.b && rs->rport.t && r->dst.port != NULL) {
+ rpool->proxy_port[1] = ntohs(rs->rport.a) +
+ (ntohs(r->dst.port[1]) - ntohs(r->dst.port[0]));
+ } else
+ r->rdr.proxy_port[1] = ntohs(rs->rport.b);
+
+ if (rs->pool_opts.staticport) {
+ yyerror("the 'static-port' option is only valid with nat rules");
+ return 1;
+ }
+
+ if (rs->pool_opts.mape.offset) {
+ yyerror("the 'map-e-portset' option is only valid with nat rules");
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+apply_nat_ports(struct pfctl_pool *rpool, struct redirspec *rs)
+{
+ if (rs == NULL)
+ return 0;
+
+ rpool->proxy_port[0] = ntohs(rs->rport.a);
+ rpool->proxy_port[1] = ntohs(rs->rport.b);
+ if (!rpool->proxy_port[0] && !rpool->proxy_port[1]) {
+ rpool->proxy_port[0] = PF_NAT_PROXY_PORT_LOW;
+ rpool->proxy_port[1] = PF_NAT_PROXY_PORT_HIGH;
+ } else if (!rpool->proxy_port[1])
+ rpool->proxy_port[1] = rpool->proxy_port[0];
+
+ if (rs->pool_opts.staticport) {
+ if (rpool->proxy_port[0] != PF_NAT_PROXY_PORT_LOW &&
+ rpool->proxy_port[1] != PF_NAT_PROXY_PORT_HIGH) {
+ yyerror("the 'static-port' option can't"
+ " be used when specifying a port"
+ " range");
+ return 1;
+ }
+ rpool->proxy_port[0] = 0;
+ rpool->proxy_port[1] = 0;
+ }
+
+ if (rs->pool_opts.mape.offset) {
+ if (rs->pool_opts.staticport) {
+ yyerror("the 'map-e-portset' option"
+ " can't be used 'static-port'");
+ return 1;
+ }
+ if (rpool->proxy_port[0] != PF_NAT_PROXY_PORT_LOW &&
+ rpool->proxy_port[1] != PF_NAT_PROXY_PORT_HIGH) {
+ yyerror("the 'map-e-portset' option"
+ " can't be used when specifying"
+ " a port range");
+ return 1;
+ }
+ rpool->mape = rs->pool_opts.mape;
+ }
+
+ return 0;
+}
+
+int
+apply_redirspec(struct pfctl_pool *rpool, struct redirspec *rs)
+{
+ struct node_host *h;
+ struct pf_pooladdr *pa;
+
+ if (rs == NULL)
+ return 0;
+
+ rpool->opts = rs->pool_opts.type;
+
+ if ((rpool->opts & PF_POOL_TYPEMASK) == PF_POOL_NONE &&
+ (rs->host->next != NULL ||
+ rs->host->addr.type == PF_ADDR_TABLE ||
+ DYNIF_MULTIADDR(rs->host->addr)))
+ rpool->opts = PF_POOL_ROUNDROBIN;
+
+ if (!PF_POOL_DYNTYPE(rpool->opts) &&
+ (disallow_table(rs->host, "tables are not supported by pool type") ||
+ disallow_alias(rs->host, "interface (%s) is not supported by pool type")))
+ return 1;
+
+ if (rs->host->next != NULL &&
+ ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN)) {
+ yyerror("r.route.opts must be PF_POOL_ROUNDROBIN");
+ return 1;
+ }
+
+ if (rs->host->next != NULL) {
+ if ((rpool->opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN) {
+ yyerror("only round-robin valid for multiple "
+ "redirection addresses");
+ return 1;
+ }
+ }
+
+ rpool->opts |= rs->pool_opts.opts;
+
+ if (rs->pool_opts.key != NULL)
+ memcpy(&(rpool->key), rs->pool_opts.key,
+ sizeof(struct pf_poolhashkey));
+
+ TAILQ_INIT(&(rpool->list));
+ for (h = rs->host; h != NULL; h = h->next) {
+ pa = calloc(1, sizeof(struct pf_pooladdr));
+ if (pa == NULL)
+ err(1, "expand_rule: calloc");
+ pa->addr = h->addr;
+ if (h->ifname != NULL) {
+ if (strlcpy(pa->ifname, h->ifname,
+ sizeof(pa->ifname)) >= sizeof(pa->ifname))
+ errx(1, "expand_rule: strlcpy");
+ } else
+ pa->ifname[0] = 0;
+ TAILQ_INSERT_TAIL(&(rpool->list), pa, entries);
+ }
+
+ return 0;
+}
+
void
expand_rule(struct pfctl_rule *r,
struct node_if *interfaces, struct redirspec *nat,
struct redirspec *rdr, struct redirspec *route,
- struct node_host *rdr_hosts, struct node_host *nat_hosts,
- struct node_host *route_hosts, struct node_proto *protos,
+ 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,
@@ -6178,8 +6156,7 @@ expand_rule(struct pfctl_rule *r,
char label[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
char tagname[PF_TAG_NAME_SIZE];
char match_tagname[PF_TAG_NAME_SIZE];
- struct pf_pooladdr *pa;
- struct node_host *h, *osrch, *odsth;
+ struct node_host *osrch, *odsth;
u_int8_t flags, flagset, keep_state;
memcpy(label, r->label, sizeof(r->label));
@@ -6317,52 +6294,16 @@ expand_rule(struct pfctl_rule *r,
r->os_fingerprint = PF_OSFP_ANY;
}
- TAILQ_INIT(&r->rdr.list);
- for (h = rdr_hosts; h != NULL; h = h->next) {
- pa = calloc(1, sizeof(struct pf_pooladdr));
- if (pa == NULL)
- err(1, "expand_rule: calloc");
- pa->addr = h->addr;
- if (h->ifname != NULL) {
- if (strlcpy(pa->ifname, h->ifname,
- sizeof(pa->ifname)) >=
- sizeof(pa->ifname))
- errx(1, "expand_rule: strlcpy");
- } else
- pa->ifname[0] = 0;
- TAILQ_INSERT_TAIL(&r->rdr.list, pa, entries);
- }
- TAILQ_INIT(&r->nat.list);
- for (h = nat_hosts; h != NULL; h = h->next) {
- pa = calloc(1, sizeof(struct pf_pooladdr));
- if (pa == NULL)
- err(1, "expand_rule: calloc");
- pa->addr = h->addr;
- if (h->ifname != NULL) {
- if (strlcpy(pa->ifname, h->ifname,
- sizeof(pa->ifname)) >=
- sizeof(pa->ifname))
- errx(1, "expand_rule: strlcpy");
- } else
- pa->ifname[0] = 0;
- TAILQ_INSERT_TAIL(&r->nat.list, pa, entries);
- }
- TAILQ_INIT(&r->route.list);
- for (h = route_hosts; h != NULL; h = h->next) {
- pa = calloc(1, sizeof(struct pf_pooladdr));
- if (pa == NULL)
- err(1, "expand_rule: calloc");
- pa->addr = h->addr;
- if (h->ifname != NULL) {
- if (strlcpy(pa->ifname, h->ifname,
- sizeof(pa->ifname)) >=
- sizeof(pa->ifname))
- errx(1, "expand_rule: strlcpy");
- } else
- pa->ifname[0] = 0;
- TAILQ_INSERT_TAIL(&r->route.list, pa, entries);
+ if (r->action == PF_RDR) {
+ error += apply_rdr_ports(r, &(r->rdr), rdr);
+ } else if (r->action == PF_NAT) {
+ error += apply_nat_ports(&(r->rdr), rdr);
}
+ error += apply_redirspec(&(r->nat), nat);
+ error += apply_redirspec(&(r->rdr), rdr);
+ error += apply_redirspec(&(r->route), route);
+
r->nat.proxy_port[0] = PF_NAT_PROXY_PORT_LOW;
r->nat.proxy_port[1] = PF_NAT_PROXY_PORT_HIGH;
@@ -6395,8 +6336,18 @@ expand_rule(struct pfctl_rule *r,
FREE_LIST(struct node_uid, uids);
FREE_LIST(struct node_gid, gids);
FREE_LIST(struct node_icmp, icmp_types);
- FREE_LIST(struct node_host, rdr_hosts);
- FREE_LIST(struct node_host, nat_hosts);
+ if (nat) {
+ FREE_LIST(struct node_host, nat->host);
+ free(nat);
+ }
+ if (rdr) {
+ FREE_LIST(struct node_host, rdr->host);
+ free(rdr);
+ }
+ if (route) {
+ FREE_LIST(struct node_host, route->host);
+ free(route);
+ }
if (!added)
yyerror("rule expands to no valid combination");
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index fd76a1926457..7e06f4136686 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1737,6 +1737,16 @@ pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, sa_family_t af, int which
return (0);
}
+void
+pfctl_init_rule(struct pfctl_rule *r)
+{
+
+ memset(r, 0, sizeof(struct pfctl_rule));
+ TAILQ_INIT(&(r->rdr.list));
+ TAILQ_INIT(&(r->nat.list));
+ TAILQ_INIT(&(r->route.list));
+}
+
int
pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
const char *anchor_call)
diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h
index ffd37cf023a6..b4f8c97854d1 100644
--- a/sbin/pfctl/pfctl.h
+++ b/sbin/pfctl/pfctl.h
@@ -59,8 +59,6 @@ int pfr_get_fd(void);
int pfr_add_table(struct pfr_table *, int *, int);
int pfr_del_table(struct pfr_table *, int *, int);
int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int);
-int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int);
-int pfr_clr_tstats(struct pfr_table *, int, int *, int);
int pfr_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_clr_addrs(struct pfr_table *, int *, int);
int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index e916f5e7c100..7ab872c6ee41 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -277,6 +277,7 @@ TAILQ_HEAD(pf_opt_queue, pf_opt_rule);
int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *);
int pfctl_optimize_ruleset(struct pfctl *, struct pfctl_ruleset *);
+void pfctl_init_rule(struct pfctl_rule *r);
int pfctl_append_rule(struct pfctl *, struct pfctl_rule *, const char *);
int pfctl_append_eth_rule(struct pfctl *, struct pfctl_eth_rule *,
const char *);
diff --git a/sbin/pfctl/pfctl_radix.c b/sbin/pfctl/pfctl_radix.c
index 3b0cc615e5a2..1d1918e29f44 100644
--- a/sbin/pfctl/pfctl_radix.c
+++ b/sbin/pfctl/pfctl_radix.c
@@ -235,29 +235,6 @@ pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size,
}
int
-pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags)
-{
- struct pfioc_table io;
-
- if (size < 0 || (size && !tbl)) {
- errno = EINVAL;
- return (-1);
- }
- bzero(&io, sizeof io);
- io.pfrio_flags = flags;
- io.pfrio_buffer = tbl;
- io.pfrio_esize = sizeof(*tbl);
- io.pfrio_size = size;
- if (ioctl(dev, DIOCRCLRTSTATS, &io)) {
- pfr_report_error(tbl, &io, "clear tstats from");
- return (-1);
- }
- if (nzero)
- *nzero = io.pfrio_nzero;
- return (0);
-}
-
-int
pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nmatch, int flags)
{
diff --git a/sbin/pfctl/pfctl_table.c b/sbin/pfctl/pfctl_table.c
index 57f7354b0172..834f74811ea2 100644
--- a/sbin/pfctl/pfctl_table.c
+++ b/sbin/pfctl/pfctl_table.c
@@ -396,7 +396,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "zero")) {
flags |= PFR_FLAG_ADDRSTOO;
- RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags));
+ RVTEST(pfctl_clear_tstats(pfh, &table, &nzero, flags));
xprintf(opts, "%d table/stats cleared", nzero);
} else
warnx("pfctl_table: unknown command '%s'", command);
diff --git a/sbin/pfctl/tests/files/Makefile b/sbin/pfctl/tests/files/Makefile
index 27cf512d81c5..fc52b1db3c30 100644
--- a/sbin/pfctl/tests/files/Makefile
+++ b/sbin/pfctl/tests/files/Makefile
@@ -4,6 +4,6 @@ TESTSDIR= ${TESTSBASE}/sbin/pfctl/files
BINDIR= ${TESTSDIR}
# We use ${.CURDIR} as workaround so that the glob patterns work.
-FILES!= echo ${.CURDIR}/pf????.in ${.CURDIR}/pf????.include ${.CURDIR}/pf????.ok
+FILES!= echo ${.CURDIR}/pf????.in ${.CURDIR}/pf????.include ${.CURDIR}/pf????.ok ${.CURDIR}/pf????.fail
.include <bsd.progs.mk>
diff --git a/sbin/pfctl/tests/files/pf1026.ok b/sbin/pfctl/tests/files/pf1026.ok
index 5b849fe80bd3..323036f2b800 100644
--- a/sbin/pfctl/tests/files/pf1026.ok
+++ b/sbin/pfctl/tests/files/pf1026.ok
@@ -1 +1 @@
-pass in on epair2b route-to (epair0a 192.0.2.2) inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from (epair0a)
+pass in on epair2b route-to (epair0a 192.0.2.2) inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from (epair0a) round-robin
diff --git a/sbin/pfctl/tests/files/pf1027.ok b/sbin/pfctl/tests/files/pf1027.ok
index 5a3f30ae1592..b50f1e216837 100644
--- a/sbin/pfctl/tests/files/pf1027.ok
+++ b/sbin/pfctl/tests/files/pf1027.ok
@@ -1 +1 @@
-pass in on epair2b reply-to (epair0a 2001:db8::1) inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from (epair0a)
+pass in on epair2b reply-to (epair0a 2001:db8::1) inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from (epair0a) round-robin
diff --git a/sbin/pfctl/tests/files/pf1028.in b/sbin/pfctl/tests/files/pf1028.in
new file mode 100644
index 000000000000..2386fcb52249
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1028.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1028.ok b/sbin/pfctl/tests/files/pf1028.ok
new file mode 100644
index 000000000000..07be890f4e05
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1028.ok
@@ -0,0 +1 @@
+rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1029.in b/sbin/pfctl/tests/files/pf1029.in
new file mode 100644
index 000000000000..73815839aadd
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1029.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1002
diff --git a/sbin/pfctl/tests/files/pf1029.ok b/sbin/pfctl/tests/files/pf1029.ok
new file mode 100644
index 000000000000..6e9083bf856a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1029.ok
@@ -0,0 +1 @@
+rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1002
diff --git a/sbin/pfctl/tests/files/pf1030.in b/sbin/pfctl/tests/files/pf1030.in
new file mode 100644
index 000000000000..b6f891998a71
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1030.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 50001:65535
diff --git a/sbin/pfctl/tests/files/pf1030.ok b/sbin/pfctl/tests/files/pf1030.ok
new file mode 100644
index 000000000000..4f6b2eba2f39
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1030.ok
@@ -0,0 +1 @@
+rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 50001:65535
diff --git a/sbin/pfctl/tests/files/pf1031.in b/sbin/pfctl/tests/files/pf1031.in
new file mode 100644
index 000000000000..7cad4ae64000
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1031.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 port 1004:2004 -> 192.0.2.3 port 1004
diff --git a/sbin/pfctl/tests/files/pf1031.ok b/sbin/pfctl/tests/files/pf1031.ok
new file mode 100644
index 000000000000..8dd7fe027716
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1031.ok
@@ -0,0 +1 @@
+rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 port 1004:2004 -> 192.0.2.3 port 1004
diff --git a/sbin/pfctl/tests/files/pf1032.in b/sbin/pfctl/tests/files/pf1032.in
new file mode 100644
index 000000000000..a2eec78da045
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1032.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 port 1005:2005 -> 192.0.2.3 port 3004:*
diff --git a/sbin/pfctl/tests/files/pf1032.ok b/sbin/pfctl/tests/files/pf1032.ok
new file mode 100644
index 000000000000..3b3f124efc33
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1032.ok
@@ -0,0 +1 @@
+rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 port 1005:2005 -> 192.0.2.3 port 3004:4004
diff --git a/sbin/pfctl/tests/files/pf1033.fail b/sbin/pfctl/tests/files/pf1033.fail
new file mode 100644
index 000000000000..d9fbfe4296e3
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1033.fail
@@ -0,0 +1 @@
+the 'static-port' option is only valid with nat rules
diff --git a/sbin/pfctl/tests/files/pf1033.in b/sbin/pfctl/tests/files/pf1033.in
new file mode 100644
index 000000000000..76f33e7e8f0e
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1033.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port
diff --git a/sbin/pfctl/tests/files/pf1034.fail b/sbin/pfctl/tests/files/pf1034.fail
new file mode 100644
index 000000000000..e407996a8fa3
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1034.fail
@@ -0,0 +1 @@
+the 'map-e-portset' option is only valid with nat rules
diff --git a/sbin/pfctl/tests/files/pf1034.in b/sbin/pfctl/tests/files/pf1034.in
new file mode 100644
index 000000000000..be847a8af241
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1034.in
@@ -0,0 +1 @@
+rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/0x34
diff --git a/sbin/pfctl/tests/files/pf1035.in b/sbin/pfctl/tests/files/pf1035.in
new file mode 100644
index 000000000000..9382ffedc8c9
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1035.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1035.ok b/sbin/pfctl/tests/files/pf1035.ok
new file mode 100644
index 000000000000..be573ef460f5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1035.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1036.in b/sbin/pfctl/tests/files/pf1036.in
new file mode 100644
index 000000000000..81718c908303
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1036.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 50001:65535
diff --git a/sbin/pfctl/tests/files/pf1036.ok b/sbin/pfctl/tests/files/pf1036.ok
new file mode 100644
index 000000000000..be573ef460f5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1036.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1037.in b/sbin/pfctl/tests/files/pf1037.in
new file mode 100644
index 000000000000..a30f6c0e7bbe
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1037.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1003
diff --git a/sbin/pfctl/tests/files/pf1037.ok b/sbin/pfctl/tests/files/pf1037.ok
new file mode 100644
index 000000000000..020e2de28dec
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1037.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1003
diff --git a/sbin/pfctl/tests/files/pf1038.in b/sbin/pfctl/tests/files/pf1038.in
new file mode 100644
index 000000000000..532060e56494
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1038.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1004:2004
diff --git a/sbin/pfctl/tests/files/pf1038.ok b/sbin/pfctl/tests/files/pf1038.ok
new file mode 100644
index 000000000000..a4021db7b1b2
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1038.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1004:2004
diff --git a/sbin/pfctl/tests/files/pf1039.in b/sbin/pfctl/tests/files/pf1039.in
new file mode 100644
index 000000000000..dba14b0625de
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1039.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port
diff --git a/sbin/pfctl/tests/files/pf1039.ok b/sbin/pfctl/tests/files/pf1039.ok
new file mode 100644
index 000000000000..80cfbe742865
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1039.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port
diff --git a/sbin/pfctl/tests/files/pf1040.fail b/sbin/pfctl/tests/files/pf1040.fail
new file mode 100644
index 000000000000..5b9afc22b441
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1040.fail
@@ -0,0 +1 @@
+the 'static-port' option can't be used when specifying a port range
diff --git a/sbin/pfctl/tests/files/pf1040.in b/sbin/pfctl/tests/files/pf1040.in
new file mode 100644
index 000000000000..38d7292a560a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1040.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1006 static-port
diff --git a/sbin/pfctl/tests/files/pf1040.ok b/sbin/pfctl/tests/files/pf1040.ok
new file mode 100644
index 000000000000..ffe2e023f77c
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1040.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/52
diff --git a/sbin/pfctl/tests/files/pf1041.in b/sbin/pfctl/tests/files/pf1041.in
new file mode 100644
index 000000000000..4c384ac70e05
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1041.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/0x34
diff --git a/sbin/pfctl/tests/files/pf1041.ok b/sbin/pfctl/tests/files/pf1041.ok
new file mode 100644
index 000000000000..ffe2e023f77c
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1041.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/52
diff --git a/sbin/pfctl/tests/files/pf1042.fail b/sbin/pfctl/tests/files/pf1042.fail
new file mode 100644
index 000000000000..56e174a5ece5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1042.fail
@@ -0,0 +1 @@
+the 'map-e-portset' option can't be used 'static-port'
diff --git a/sbin/pfctl/tests/files/pf1042.in b/sbin/pfctl/tests/files/pf1042.in
new file mode 100644
index 000000000000..906f637b6a0a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1042.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port map-e-portset 6/8/0x34
diff --git a/sbin/pfctl/tests/files/pf1043.fail b/sbin/pfctl/tests/files/pf1043.fail
new file mode 100644
index 000000000000..cdfab00916a2
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1043.fail
@@ -0,0 +1 @@
+the 'map-e-portset' option can't be used when specifying a port range
diff --git a/sbin/pfctl/tests/files/pf1043.in b/sbin/pfctl/tests/files/pf1043.in
new file mode 100644
index 000000000000..15428a9e54bc
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1043.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1007 map-e-portset 6/8/0x34
diff --git a/sbin/pfctl/tests/files/pf1044.in b/sbin/pfctl/tests/files/pf1044.in
new file mode 100644
index 000000000000..6a927b66b83f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1044.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> sticky-address
diff --git a/sbin/pfctl/tests/files/pf1044.ok b/sbin/pfctl/tests/files/pf1044.ok
new file mode 100644
index 000000000000..a68b1daaa73a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1044.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> round-robin sticky-address
diff --git a/sbin/pfctl/tests/files/pf1045.in b/sbin/pfctl/tests/files/pf1045.in
new file mode 100644
index 000000000000..38f708ce19b8
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1045.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 bitmask
diff --git a/sbin/pfctl/tests/files/pf1045.ok b/sbin/pfctl/tests/files/pf1045.ok
new file mode 100644
index 000000000000..5388db7e58a4
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1045.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 bitmask
diff --git a/sbin/pfctl/tests/files/pf1046.fail b/sbin/pfctl/tests/files/pf1046.fail
new file mode 100644
index 000000000000..b152f9063241
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1046.fail
@@ -0,0 +1 @@
+tables are not supported by pool type
diff --git a/sbin/pfctl/tests/files/pf1046.in b/sbin/pfctl/tests/files/pf1046.in
new file mode 100644
index 000000000000..e4a9f79efd6f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1046.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> bitmask
diff --git a/sbin/pfctl/tests/files/pf1047.fail b/sbin/pfctl/tests/files/pf1047.fail
new file mode 100644
index 000000000000..239b96b2fed4
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1047.fail
@@ -0,0 +1 @@
+interface \(vtnet1\) is not supported by pool type
diff --git a/sbin/pfctl/tests/files/pf1047.in b/sbin/pfctl/tests/files/pf1047.in
new file mode 100644
index 000000000000..369bfcb0fb26
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1047.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> (vtnet1) bitmask
diff --git a/sbin/pfctl/tests/files/pf1048.in b/sbin/pfctl/tests/files/pf1048.in
new file mode 100644
index 000000000000..01232a33b5d8
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1048.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 random
diff --git a/sbin/pfctl/tests/files/pf1048.ok b/sbin/pfctl/tests/files/pf1048.ok
new file mode 100644
index 000000000000..35e86fc676fc
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1048.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 random
diff --git a/sbin/pfctl/tests/files/pf1049.in b/sbin/pfctl/tests/files/pf1049.in
new file mode 100644
index 000000000000..3f2e5acf8265
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1049.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 192.0.2.3 }
diff --git a/sbin/pfctl/tests/files/pf1049.ok b/sbin/pfctl/tests/files/pf1049.ok
new file mode 100644
index 000000000000..be573ef460f5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1049.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3
diff --git a/sbin/pfctl/tests/files/pf1050.in b/sbin/pfctl/tests/files/pf1050.in
new file mode 100644
index 000000000000..69ccaf445c3b
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1050.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets>
diff --git a/sbin/pfctl/tests/files/pf1050.ok b/sbin/pfctl/tests/files/pf1050.ok
new file mode 100644
index 000000000000..24ca9b459bb7
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1050.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> round-robin
diff --git a/sbin/pfctl/tests/files/pf1051.in b/sbin/pfctl/tests/files/pf1051.in
new file mode 100644
index 000000000000..734da64a372c
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1051.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1 203.0.113.2 }
diff --git a/sbin/pfctl/tests/files/pf1051.ok b/sbin/pfctl/tests/files/pf1051.ok
new file mode 100644
index 000000000000..86f23488be41
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1051.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1, 203.0.113.2 } round-robin
diff --git a/sbin/pfctl/tests/files/pf1052.in b/sbin/pfctl/tests/files/pf1052.in
new file mode 100644
index 000000000000..2ea770f3c06e
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1052.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1 <targets> }
diff --git a/sbin/pfctl/tests/files/pf1052.ok b/sbin/pfctl/tests/files/pf1052.ok
new file mode 100644
index 000000000000..b71d105eb77a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1052.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1, <targets> } round-robin
diff --git a/sbin/pfctl/tests/files/pf1053.in b/sbin/pfctl/tests/files/pf1053.in
new file mode 100644
index 000000000000..f0cced0b64a2
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1053.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24
diff --git a/sbin/pfctl/tests/files/pf1053.ok b/sbin/pfctl/tests/files/pf1053.ok
new file mode 100644
index 000000000000..de321b8c738f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1053.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24
diff --git a/sbin/pfctl/tests/files/pf1054.in b/sbin/pfctl/tests/files/pf1054.in
new file mode 100644
index 000000000000..9e66bb2a81d6
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1054.in
@@ -0,0 +1,3 @@
+# XXX: it causes just the 0th address to be used without cycling
+# Probably a bug
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 round-robin
diff --git a/sbin/pfctl/tests/files/pf1054.ok b/sbin/pfctl/tests/files/pf1054.ok
new file mode 100644
index 000000000000..3d7ab7974d87
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1054.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 round-robin
diff --git a/sbin/pfctl/tests/files/pf1055.in b/sbin/pfctl/tests/files/pf1055.in
new file mode 100644
index 000000000000..c116ef5fd43e
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1055.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 source-hash 0x42424242424242424242424242424242
diff --git a/sbin/pfctl/tests/files/pf1055.ok b/sbin/pfctl/tests/files/pf1055.ok
new file mode 100644
index 000000000000..468e47012169
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1055.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 source-hash 0x42424242424242424242424242424242
diff --git a/sbin/pfctl/tests/files/pf1056.in b/sbin/pfctl/tests/files/pf1056.in
new file mode 100644
index 000000000000..bd2af077fc3f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1056.in
@@ -0,0 +1 @@
+pass in on vtnet0 inet6 from any to 64:ff9b::/96 af-to inet from 203.0.113.1 to 203.0.113.2
diff --git a/sbin/pfctl/tests/files/pf1056.ok b/sbin/pfctl/tests/files/pf1056.ok
new file mode 100644
index 000000000000..0397570dbce0
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1056.ok
@@ -0,0 +1 @@
+pass in on vtnet0 inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from 203.0.113.1 to 203.0.113.2
diff --git a/sbin/pfctl/tests/files/pf1057.in b/sbin/pfctl/tests/files/pf1057.in
new file mode 100644
index 000000000000..0e26976e5a0d
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1057.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> vlan1057
diff --git a/sbin/pfctl/tests/files/pf1057.ok b/sbin/pfctl/tests/files/pf1057.ok
new file mode 100644
index 000000000000..7626951e138c
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1057.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.5
diff --git a/sbin/pfctl/tests/files/pf1058.in b/sbin/pfctl/tests/files/pf1058.in
new file mode 100644
index 000000000000..27c0ef1d69b3
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1058.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1 vlan1058 }
diff --git a/sbin/pfctl/tests/files/pf1058.ok b/sbin/pfctl/tests/files/pf1058.ok
new file mode 100644
index 000000000000..b1d2b07a58b4
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1058.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1, 203.0.113.5 } round-robin
diff --git a/sbin/pfctl/tests/files/pf1059.in b/sbin/pfctl/tests/files/pf1059.in
new file mode 100644
index 000000000000..92ed5c50656b
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1059.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> (vlan1059)
diff --git a/sbin/pfctl/tests/files/pf1059.ok b/sbin/pfctl/tests/files/pf1059.ok
new file mode 100644
index 000000000000..6b028f18ee60
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1059.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> (vlan1059) round-robin
diff --git a/sbin/pfctl/tests/files/pf1060.in b/sbin/pfctl/tests/files/pf1060.in
new file mode 100644
index 000000000000..85cdd19f2897
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1060.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.0 (vlan1060) }
diff --git a/sbin/pfctl/tests/files/pf1060.ok b/sbin/pfctl/tests/files/pf1060.ok
new file mode 100644
index 000000000000..3364b3cbdcc5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1060.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.0, (vlan1060) } round-robin
diff --git a/sbin/pfctl/tests/files/pf1061.in b/sbin/pfctl/tests/files/pf1061.in
new file mode 100644
index 000000000000..32eb8272db8b
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1061.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> vlan1061:0
diff --git a/sbin/pfctl/tests/files/pf1061.ok b/sbin/pfctl/tests/files/pf1061.ok
new file mode 100644
index 000000000000..d2e6d969cb11
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1061.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> 2001:db8::cb00:7105
diff --git a/sbin/pfctl/tests/files/pf1062.in b/sbin/pfctl/tests/files/pf1062.in
new file mode 100644
index 000000000000..4d6a0ecc2e92
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1062.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> { 2001:db8::3 vlan1062:0 }
diff --git a/sbin/pfctl/tests/files/pf1062.ok b/sbin/pfctl/tests/files/pf1062.ok
new file mode 100644
index 000000000000..cb5db62ded1d
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1062.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> { 2001:db8::3, 2001:db8::cb00:7105 } round-robin
diff --git a/sbin/pfctl/tests/files/pf1063.in b/sbin/pfctl/tests/files/pf1063.in
new file mode 100644
index 000000000000..3d164538640d
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1063.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> (vlan1063)
diff --git a/sbin/pfctl/tests/files/pf1063.ok b/sbin/pfctl/tests/files/pf1063.ok
new file mode 100644
index 000000000000..13189e00cc8a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1063.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> (vlan1063) round-robin
diff --git a/sbin/pfctl/tests/files/pf1064.in b/sbin/pfctl/tests/files/pf1064.in
new file mode 100644
index 000000000000..78d04135154f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1064.in
@@ -0,0 +1 @@
+nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> { fe80::2 (vlan1064) }
diff --git a/sbin/pfctl/tests/files/pf1064.ok b/sbin/pfctl/tests/files/pf1064.ok
new file mode 100644
index 000000000000..ed15d054ab34
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1064.ok
@@ -0,0 +1 @@
+nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> { fe80::2, (vlan1064) } round-robin
diff --git a/sbin/pfctl/tests/pfctl_test.c b/sbin/pfctl/tests/pfctl_test.c
index cc69ed4e002a..dbdcaa4900ea 100644
--- a/sbin/pfctl/tests/pfctl_test.c
+++ b/sbin/pfctl/tests/pfctl_test.c
@@ -119,27 +119,14 @@ read_file(const char *filename)
}
static void
-run_pfctl_test(const char *input_path, const char *expected_path,
- const atf_tc_t *tc)
+run_command_pipe(const char *argv[], struct sbuf **output)
{
- int status;
+ posix_spawn_file_actions_t action;
pid_t pid;
int pipefds[2];
- char input_files_path[PATH_MAX];
- struct sbuf *expected_output;
- struct sbuf *real_output;
- posix_spawn_file_actions_t action;
-
- if (!check_pf_module_available())
- atf_tc_skip("pf(4) is not loaded");
-
- /* The test inputs need to be able to use relative includes. */
- snprintf(input_files_path, sizeof(input_files_path), "%s/files",
- atf_tc_get_config_var(tc, "srcdir"));
- ATF_REQUIRE_ERRNO(0, chdir(input_files_path) == 0);
+ int status;
ATF_REQUIRE_ERRNO(0, pipe(pipefds) == 0);
- expected_output = read_file(expected_path);
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_addclose(&action, STDIN_FILENO);
@@ -147,27 +134,118 @@ run_pfctl_test(const char *input_path, const char *expected_path,
posix_spawn_file_actions_adddup2(&action, pipefds[0], STDOUT_FILENO);
posix_spawn_file_actions_adddup2(&action, pipefds[0], STDERR_FILENO);
- const char *argv[] = { "pfctl", "-o", "none", "-nvf", input_path,
- NULL };
- printf("Running %s %s %s %s %s\n", argv[0], argv[1], argv[2], argv[3],
- argv[4]);
+ printf("Running ");
+ for (int i=0; argv[i] != NULL; i++)
+ printf("%s ", argv[i]);
+ printf("\n");
+
status = posix_spawnp(
- &pid, "pfctl", &action, NULL, __DECONST(char **, argv), environ);
+ &pid, argv[0], &action, NULL, __DECONST(char **, argv), environ);
ATF_REQUIRE_EQ_MSG(
status, 0, "posix_spawn failed: %s", strerror(errno));
posix_spawn_file_actions_destroy(&action);
close(pipefds[0]);
- real_output = read_fd(pipefds[1], 0);
- printf("---\n%s---\n", sbuf_data(real_output));
+ (*output) = read_fd(pipefds[1], 0);
+ printf("---\n%s---\n", sbuf_data(*output));
ATF_REQUIRE_EQ(waitpid(pid, &status, 0), pid);
ATF_REQUIRE_MSG(WIFEXITED(status),
- "pfctl returned non-zero! Output:\n %s", sbuf_data(real_output));
+ "%s returned non-zero! Output:\n %s", argv[0], sbuf_data(*output));
+ close(pipefds[1]);
+}
+
+static void
+run_command(const char *argv[])
+{
+ posix_spawn_file_actions_t action;
+ pid_t pid;
+ int status;
+
+ posix_spawn_file_actions_init(&action);
+ posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0);
+ posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0);
+ posix_spawn_file_actions_addopen(&action, STDIN_FILENO, "/dev/zero", O_RDONLY, 0);
+
+ printf("Running ");
+ for (int i=0; argv[i] != NULL; i++)
+ printf("%s ", argv[i]);
+ printf("\n");
+
+ status = posix_spawnp(
+ &pid, argv[0], &action, NULL, __DECONST(char **, argv), environ);
+ posix_spawn_file_actions_destroy(&action);
+ waitpid(pid, &status, 0);
+}
+
+static void
+run_pfctl_test(const char *input_path, const char *output_path,
+ const atf_tc_t *tc, bool test_failure)
+{
+ char input_files_path[PATH_MAX];
+ struct sbuf *expected_output;
+ struct sbuf *real_output;
+
+ if (!check_pf_module_available())
+ atf_tc_skip("pf(4) is not loaded");
+
+ /* The test inputs need to be able to use relative includes. */
+ snprintf(input_files_path, sizeof(input_files_path), "%s/files",
+ atf_tc_get_config_var(tc, "srcdir"));
+ ATF_REQUIRE_ERRNO(0, chdir(input_files_path) == 0);
+ expected_output = read_file(output_path);
+
+ const char *argv[] = { "pfctl", "-o", "none", "-nvf", input_path,
+ NULL };
+ run_command_pipe(argv, &real_output);
+
+ if (test_failure) {
+ /*
+ * Error output contains additional strings like line number
+ * or "skipping rule due to errors", so use regexp to see
+ * if the expected error message is there somewhere.
+ */
+ ATF_CHECK_MATCH(sbuf_data(expected_output), sbuf_data(real_output));
+ sbuf_delete(expected_output);
+ } else {
+ ATF_CHECK_STREQ(sbuf_data(expected_output), sbuf_data(real_output));
+ sbuf_delete(expected_output);
+ }
- ATF_CHECK_STREQ(sbuf_data(expected_output), sbuf_data(real_output));
- sbuf_delete(expected_output);
sbuf_delete(real_output);
- close(pipefds[1]);
+}
+
+static void
+do_pf_test_iface_create(const char *number)
+{
+ struct sbuf *ifconfig_output;
+ char ifname[16] = {0};
+
+ snprintf(ifname, sizeof(ifname), "vlan%s", number);
+ const char *argv[] = { "ifconfig", ifname, "create", NULL};
+ run_command_pipe(argv, &ifconfig_output);
+ sbuf_delete(ifconfig_output);
+
+ const char *argv_inet[] = { "ifconfig", ifname, "inet", "203.0.113.5/30", NULL};
+ run_command_pipe(argv_inet, &ifconfig_output);
+ sbuf_delete(ifconfig_output);
+
+ const char *argv_inet6[] = { "ifconfig", ifname, "inet6", "2001:db8::203.0.113.5/126", NULL};
+ run_command_pipe(argv_inet6, &ifconfig_output);
+ sbuf_delete(ifconfig_output);
+
+ const char *argv_show[] = { "ifconfig", ifname, NULL};
+ run_command_pipe(argv_show, &ifconfig_output);
+ sbuf_delete(ifconfig_output);
+}
+
+static void
+do_pf_test_iface_remove(const char *number)
+{
+ char ifname[16] = {0};
+
+ snprintf(ifname, sizeof(ifname), "vlan%s", number);
+ const char *argv[] = { "ifconfig", ifname, "destroy", NULL};
+ run_command(argv);
}
static void
@@ -179,7 +257,21 @@ do_pf_test(const char *number, const atf_tc_t *tc)
atf_tc_get_config_var(tc, "srcdir"), number);
asprintf(&expected_path, "%s/files/pf%s.ok",
atf_tc_get_config_var(tc, "srcdir"), number);
- run_pfctl_test(input_path, expected_path, tc);
+ run_pfctl_test(input_path, expected_path, tc, false);
+ free(input_path);
+ free(expected_path);
+}
+
+static void
+do_pf_test_fail(const char *number, const atf_tc_t *tc)
+{
+ char *input_path;
+ char *expected_path;
+ asprintf(&input_path, "%s/files/pf%s.in",
+ atf_tc_get_config_var(tc, "srcdir"), number);
+ asprintf(&expected_path, "%s/files/pf%s.fail",
+ atf_tc_get_config_var(tc, "srcdir"), number);
+ run_pfctl_test(input_path, expected_path, tc, true);
free(input_path);
free(expected_path);
}
@@ -190,10 +282,11 @@ do_selfpf_test(const char *number, const atf_tc_t *tc)
char *expected_path;
asprintf(&expected_path, "%s/files/pf%s.ok",
atf_tc_get_config_var(tc, "srcdir"), number);
- run_pfctl_test(expected_path, expected_path, tc);
+ run_pfctl_test(expected_path, expected_path, tc, false);
free(expected_path);
}
+/* Standard tests perform the normal test and then the selfpf test */
#define PFCTL_TEST(number, descr) \
ATF_TC(pf##number); \
ATF_TC_HEAD(pf##number, tc) \
@@ -213,16 +306,53 @@ do_selfpf_test(const char *number, const atf_tc_t *tc)
{ \
do_selfpf_test(#number, tc); \
}
+/* Tests for failure perform only the normal test */
+#define PFCTL_TEST_FAIL(number, descr) \
+ ATF_TC(pf##number); \
+ ATF_TC_HEAD(pf##number, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", descr); \
+ } \
+ ATF_TC_BODY(pf##number, tc) \
+ { \
+ do_pf_test_fail(#number, tc); \
+ }
+/* Tests with interface perform only the normal test */
+#define PFCTL_TEST_IFACE(number, descr) \
+ ATF_TC_WITH_CLEANUP(pf##number); \
+ ATF_TC_HEAD(pf##number, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", descr); \
+ atf_tc_set_md_var(tc, "execenv", "jail"); \
+ atf_tc_set_md_var(tc, "execenv.jail.params", "vnet"); \
+ } \
+ ATF_TC_BODY(pf##number, tc) \
+ { \
+ do_pf_test_iface_create(#number); \
+ do_pf_test(#number, tc); \
+ } \
+ ATF_TC_CLEANUP(pf##number, tc) \
+ { \
+ do_pf_test_iface_remove(#number); \
+ }
#include "pfctl_test_list.inc"
#undef PFCTL_TEST
+#undef PFCTL_TEST_FAIL
+#undef PFCTL_TEST_IFACE
ATF_TP_ADD_TCS(tp)
{
#define PFCTL_TEST(number, descr) \
ATF_TP_ADD_TC(tp, pf##number); \
ATF_TP_ADD_TC(tp, selfpf##number);
+#define PFCTL_TEST_FAIL(number, descr) \
+ ATF_TP_ADD_TC(tp, pf##number);
+#define PFCTL_TEST_IFACE(number, descr) \
+ ATF_TP_ADD_TC(tp, pf##number);
#include "pfctl_test_list.inc"
#undef PFCTL_TEST
+#undef PFCTL_TEST_FAIL
+#undef PFCTL_TEST_IFACE
return atf_no_error();
}
diff --git a/sbin/pfctl/tests/pfctl_test_list.inc b/sbin/pfctl/tests/pfctl_test_list.inc
index 7dd3a2af0eea..c6329b978a21 100644
--- a/sbin/pfctl/tests/pfctl_test_list.inc
+++ b/sbin/pfctl/tests/pfctl_test_list.inc
@@ -136,3 +136,40 @@ PFCTL_TEST(1024, "nat64")
PFCTL_TEST(1025, "nat64 with implicit address family")
PFCTL_TEST(1026, "nat64 with route-to")
PFCTL_TEST(1027, "nat64 with reply-to")
+PFCTL_TEST(1028, "RDR pool: For RDR rules no port specified means keep port")
+PFCTL_TEST(1029, "RDR pool: A single port is shown")
+PFCTL_TEST(1030, "RDR pool: The default values are shown for RDR rules")
+PFCTL_TEST(1031, "RDR pool: Multiple ports redirected to a single port")
+PFCTL_TEST(1032, "RDR pool: Multiple ports redirected to a port range")
+PFCTL_TEST_FAIL(1033, "RDR pool: static-port can't be used with RDR rules")
+PFCTL_TEST_FAIL(1034, "RDR pool: MAP-E port can't be used with RDR rules")
+PFCTL_TEST(1035, "NAT pool: For NAT rules no port specified means default values")
+PFCTL_TEST(1036, "NAT pool: Default port numbers are not shown, even if explicitly applied")
+PFCTL_TEST(1037, "NAT pool: Single port")
+PFCTL_TEST(1038, "NAT pool: Two ports")
+PFCTL_TEST(1039, "NAT pool: Static port")
+PFCTL_TEST_FAIL(1040, "NAT pool: Static port can't be used with port numbers")
+PFCTL_TEST(1041, "NAT pool: MAP-E is displayed using decimal system")
+PFCTL_TEST_FAIL(1042, "NAT pool: MAP-E port can't be used with static port")
+PFCTL_TEST_FAIL(1043, "NAT pool: MAP-E port can't be used with port numbers")
+PFCTL_TEST(1044, "pool: sticky-address is applied on top of round-robin")
+PFCTL_TEST(1045, "pool: bitmask is allowed for prefixes")
+PFCTL_TEST_FAIL(1046, "pool: bitmask is not allowed for tables")
+PFCTL_TEST_FAIL(1047, "pool: bitmask is not allowed for interfaces in brackets")
+PFCTL_TEST(1048, "pool: random is allowed for prefixes")
+PFCTL_TEST(1049, "pool: round-robin is not set for a single host, even if it looks like a table")
+PFCTL_TEST(1050, "pool: round-robin is set automatically for tables")
+PFCTL_TEST(1051, "pool: round-robin is set automatically for multiple targets")
+PFCTL_TEST(1052, "pool: hosts and table are allowed, round-robin is automatically set")
+PFCTL_TEST(1053, "pool: round-robin is not set automatically for prefixes")
+PFCTL_TEST(1054, "pool: round-robin is allowed for prefixes")
+PFCTL_TEST(1055, "pool: source hash")
+PFCTL_TEST(1056, "af-to: from and to")
+PFCTL_TEST_IFACE(1057, "Interface translation: IPv4 rule, interface without brackets is translated")
+PFCTL_TEST_IFACE(1058, "Interface translation: IPv4 rule, interface without brackets is translated, extra host, round-robin is applied")
+PFCTL_TEST_IFACE(1059, "Interface translation: IPv4 rule, interface with brackets is not translated, round-robin is applied")
+PFCTL_TEST_IFACE(1060, "Interface translation: IPv4 rule, interface with brackets is not translated, extra host, round-robin is applied")
+PFCTL_TEST_IFACE(1061, "Interface translation: IPv6 rule, interface without brackets is translated")
+PFCTL_TEST_IFACE(1062, "Interface translation: IPv6 rule, interface without brackets is translated, extra host, round-robin is applied")
+PFCTL_TEST_IFACE(1063, "Interface translation: IPv6 rule, interface with brackets is not translated, round-robin is applied")
+PFCTL_TEST_IFACE(1064, "Interface translation: IPv6 rule, interface with brackets is not translated, extra host, round robin is applied")