aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2025-01-20 17:25:37 +0000
committerKristof Provost <kp@FreeBSD.org>2025-01-24 10:20:30 +0000
commit0972294ef034d92f59857b8312dd2e1e3a7adc9c (patch)
tree98b23c194e18e2763a51f3bebdd39b28c81544eb
parent899e79760dcce8c9358caf2e2bddfe1ba3ad6dee (diff)
pf: add a dedicated pf pool for route options
As suggested by henning. Which unbreaks ie route-to after the recent pf changes. With much help debugging and pointing out of missing bits from claudio@ ok claudio@ "looks good" henning@ Obtained from: OpenBSD, jsg <jsg@openbsd.org>, 7fa5c09028 Sponsored by: Rubicon Communications, LLC ("Netgate")
-rw-r--r--lib/libpfctl/libpfctl.c2
-rw-r--r--lib/libpfctl/libpfctl.h1
-rw-r--r--sbin/pfctl/parse.y95
-rw-r--r--sbin/pfctl/pfctl.c16
-rw-r--r--sbin/pfctl/pfctl_optimize.c7
-rw-r--r--sbin/pfctl/pfctl_parser.c1
-rw-r--r--sys/net/pfvar.h3
-rw-r--r--sys/netpfil/pf/if_pfsync.c8
-rw-r--r--sys/netpfil/pf/pf.c11
-rw-r--r--sys/netpfil/pf/pf.h2
-rw-r--r--sys/netpfil/pf/pf_ioctl.c53
-rw-r--r--sys/netpfil/pf/pf_nl.c2
-rw-r--r--sys/netpfil/pf/pf_nl.h1
13 files changed, 147 insertions, 55 deletions
diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index f0708c8f0439..2297b24d37a0 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -1227,6 +1227,7 @@ snl_add_msg_attr_pf_rule(struct snl_writer *nw, uint32_t type, const struct pfct
snl_add_msg_attr_string(nw, PF_RT_OVERLOAD_TBLNAME, r->overload_tblname);
snl_add_msg_attr_rpool(nw, PF_RT_RPOOL_RDR, &r->rdr);
snl_add_msg_attr_rpool(nw, PF_RT_RPOOL_NAT, &r->nat);
+ snl_add_msg_attr_rpool(nw, PF_RT_RPOOL_RT, &r->route);
snl_add_msg_attr_u32(nw, PF_RT_OS_FINGERPRINT, r->os_fingerprint);
snl_add_msg_attr_u32(nw, PF_RT_RTABLEID, r->rtableid);
snl_add_msg_attr_timeouts(nw, PF_RT_TIMEOUT, r->timeout);
@@ -1661,6 +1662,7 @@ static struct snl_attr_parser ap_getrule[] = {
{ .type = PF_RT_MAX_SRC_CONN, .off = _OUT(r.max_src_conn), .cb = snl_attr_get_uint32 },
{ .type = PF_RT_RPOOL_NAT, .off = _OUT(r.nat), .arg = &pool_parser, .cb = snl_attr_get_nested },
{ .type = PF_RT_NAF, .off = _OUT(r.naf), .cb = snl_attr_get_uint8 },
+ { .type = PF_RT_RPOOL_RT, .off = _OUT(r.route), .arg = &pool_parser, .cb = snl_attr_get_nested },
};
#undef _OUT
SNL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, snl_f_p_empty, ap_getrule);
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 14ea06fd151a..2532894ffa21 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -180,6 +180,7 @@ struct pfctl_rule {
struct pfctl_pool rpool;
struct pfctl_pool rdr;
};
+ struct pfctl_pool route;
uint64_t evaluations;
uint64_t packets[2];
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index ab74d2dd57ab..830581c57f9c 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -304,6 +304,7 @@ static struct filter_opts {
} divert;
struct redirspec nat;
struct redirspec rdr;
+ struct redirspec rroute;
/* new-style scrub opts */
int nodf;
int minttl;
@@ -382,11 +383,12 @@ void expand_eth_rule(struct pfctl_eth_rule *,
struct node_host *, struct node_host *, const char *,
const char *);
void expand_rule(struct pfctl_rule *, struct node_if *,
- struct redirspec *, struct redirspec *, 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 *, struct node_icmp *, const char *);
+ 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 *,
+ struct node_icmp *, const char *);
int expand_altq(struct pf_altq *, struct node_if *,
struct node_queue *, struct node_queue_bw bwspec,
struct node_queue_opt *);
@@ -1080,9 +1082,9 @@ 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, $7, $8.src_os,
- $8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
- $9.uid, $9.gid, $9.rcv, $9.icmpspec,
+ expand_rule(&r, $5, NULL, NULL, NULL, 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);
free($2);
pf->astack[pf->asd + 1] = NULL;
@@ -1103,9 +1105,9 @@ 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, $5, $6.src_os,
- $6.src.host, $6.src.port, $6.dst.host, $6.dst.port,
- 0, 0, 0, 0, $2);
+ expand_rule(&r, $3, NULL, NULL, NULL, 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);
}
| RDRANCHOR string interface af proto fromto rtable {
@@ -1145,9 +1147,9 @@ 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, $5, $6.src_os,
- $6.src.host, $6.src.port, $6.dst.host, $6.dst.port,
- 0, 0, 0, 0, $2);
+ expand_rule(&r, $3, NULL, NULL, NULL, 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);
}
| BINATANCHOR string interface af proto fromto rtable {
@@ -1468,9 +1470,9 @@ 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, $6, $7.src_os,
- $7.src.host, $7.src.port, $7.dst.host, $7.dst.port,
- NULL, NULL, NULL, NULL, "");
+ expand_rule(&r, $4, NULL, NULL, NULL, NULL, NULL, NULL,
+ $6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host,
+ $7.dst.port, NULL, NULL, NULL, NULL, "");
}
;
@@ -1633,8 +1635,8 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
}
if (h != NULL)
- expand_rule(&r, j, NULL, NULL, NULL, NULL, NULL, NULL, h,
- NULL, NULL, NULL, NULL, NULL,
+ expand_rule(&r, j, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, h, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, "");
if ((i->ifa_flags & IFF_LOOPBACK) == 0) {
@@ -1656,7 +1658,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
h = ifa_lookup(i->ifname, 0);
if (h != NULL)
expand_rule(&r, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, h, NULL, NULL,
+ NULL, NULL, NULL, NULL, h, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, "");
} else
free(hh);
@@ -2726,9 +2728,9 @@ pfrule : action dir logquick interface route af proto fromto
YYERROR;
}
r.rt = $5.rt;
- r.rdr.opts = $5.pool_opts;
+ r.route.opts = $5.pool_opts;
if ($5.key != NULL)
- memcpy(&r.rdr.key, $5.key,
+ memcpy(&r.route.key, $5.key,
sizeof(struct pf_poolhashkey));
}
if (r.rt) {
@@ -2739,26 +2741,26 @@ pfrule : action dir logquick interface route af proto fromto
"matching address family found.");
YYERROR;
}
- if ((r.rdr.opts & PF_POOL_TYPEMASK) ==
+ 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.rdr.opts |= PF_POOL_ROUNDROBIN;
- if ((r.rdr.opts & PF_POOL_TYPEMASK) !=
+ r.route.opts |= PF_POOL_ROUNDROBIN;
+ if ((r.route.opts & PF_POOL_TYPEMASK) !=
PF_POOL_ROUNDROBIN &&
disallow_table($5.host, "tables are only "
"supported in round-robin routing pools"))
YYERROR;
- if ((r.rdr.opts & PF_POOL_TYPEMASK) !=
+ if ((r.route.opts & PF_POOL_TYPEMASK) !=
PF_POOL_ROUNDROBIN &&
disallow_alias($5.host, "interface (%s) "
"is only supported in round-robin "
"routing pools"))
YYERROR;
if ($5.host->next != NULL) {
- if ((r.rdr.opts & PF_POOL_TYPEMASK) !=
+ if ((r.route.opts & PF_POOL_TYPEMASK) !=
PF_POOL_ROUNDROBIN) {
- yyerror("r.rdr.opts must "
+ yyerror("r.route.opts must "
"be PF_POOL_ROUNDROBIN");
YYERROR;
}
@@ -2833,7 +2835,8 @@ pfrule : action dir logquick interface route af proto fromto
YYERROR;
}
- expand_rule(&r, $4, &$9.nat, &$9.rdr, $5.host, $9.nat.rdr ? $9.nat.rdr->host : NULL,
+ expand_rule(&r, $4, &$9.nat, &$9.rdr, &$9.rroute,
+ NULL, $9.nat.rdr ? $9.nat.rdr->host : NULL, $5.host,
$7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host,
$8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec, "");
}
@@ -4989,8 +4992,9 @@ natrule : nataction interface af proto fromto tag tagged rtable
o = o->next;
}
- expand_rule(&r, $2, NULL, NULL, $9 == NULL ? NULL : $9->host,
- NULL, $4, $5.src_os, $5.src.host, $5.src.port, $5.dst.host,
+ expand_rule(&r, $2, NULL, NULL, NULL,
+ $9 == NULL ? NULL : $9->host, NULL, NULL, $4,
+ $5.src_os, $5.src.host, $5.src.port, $5.dst.host,
$5.dst.port, 0, 0, 0, 0, "");
free($9);
}
@@ -6149,13 +6153,13 @@ expand_eth_rule(struct pfctl_eth_rule *r,
void
expand_rule(struct pfctl_rule *r,
struct node_if *interfaces, struct redirspec *nat,
- struct redirspec *rdr, struct node_host *rdr_hosts,
- struct node_host *nat_hosts,
- struct node_proto *protos, struct node_os *src_oses,
- struct node_host *src_hosts, struct node_port *src_ports,
- struct node_host *dst_hosts, struct node_port *dst_ports,
- struct node_uid *uids, struct node_gid *gids, struct node_if *rcv,
- struct node_icmp *icmp_types, const char *anchor_call)
+ 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_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)
{
sa_family_t af = r->af;
int added = 0, error = 0;
@@ -6331,6 +6335,21 @@ expand_rule(struct pfctl_rule *r,
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);
+ }
r->nat.proxy_port[0] = PF_NAT_PROXY_PORT_LOW;
r->nat.proxy_port[1] = PF_NAT_PROXY_PORT_HIGH;
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 7b54bc1c7c7a..ec07a5da999c 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1310,6 +1310,10 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
nr, ri.ticket, PF_SCRUB, path, PF_NAT) != 0)
goto error;
+ if (pfctl_get_pool(dev, &rule.route,
+ nr, ri.ticket, PF_SCRUB, path, PF_RT) != 0)
+ goto error;
+
switch (format) {
case PFCTL_SHOW_LABELS:
break;
@@ -1325,6 +1329,7 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
}
pfctl_clear_pool(&rule.rdr);
pfctl_clear_pool(&rule.nat);
+ pfctl_clear_pool(&rule.route);
}
ret = pfctl_get_rules_info_h(pfh, &ri, PF_PASS, path);
if (ret != 0) {
@@ -1346,6 +1351,10 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
nr, ri.ticket, PF_PASS, path, PF_NAT) != 0)
goto error;
+ if (pfctl_get_pool(dev, &rule.route,
+ nr, ri.ticket, PF_PASS, path, PF_RT) != 0)
+ goto error;
+
switch (format) {
case PFCTL_SHOW_LABELS: {
bool show = false;
@@ -1506,6 +1515,9 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth,
if (pfctl_get_pool(dev, &rule.nat, nr,
ri.ticket, nattype[i], path, PF_NAT) != 0)
return (-1);
+ if (pfctl_get_pool(dev, &rule.route, nr,
+ ri.ticket, nattype[i], path, PF_RT) != 0)
+ return (-1);
if (dotitle) {
pfctl_print_title("TRANSLATION RULES:");
@@ -1761,6 +1773,8 @@ pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
pfctl_move_pool(&r->rdr, &rule->rdr);
TAILQ_INIT(&rule->nat.list);
pfctl_move_pool(&r->nat, &rule->nat);
+ TAILQ_INIT(&rule->route.list);
+ pfctl_move_pool(&r->route, &rule->route);
TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries);
return (0);
@@ -2065,6 +2079,8 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
return (1);
if (pfctl_add_pool(pf, &r->nat, r->naf ? r->naf : r->af, PF_NAT))
return (1);
+ if (pfctl_add_pool(pf, &r->route, r->af, PF_RT))
+ return (1);
error = pfctl_add_rule_h(pf->h, r, anchor, name, ticket,
pf->paddr.ticket);
switch (error) {
diff --git a/sbin/pfctl/pfctl_optimize.c b/sbin/pfctl/pfctl_optimize.c
index a97664e0c929..7817bcfd284a 100644
--- a/sbin/pfctl/pfctl_optimize.c
+++ b/sbin/pfctl/pfctl_optimize.c
@@ -137,6 +137,7 @@ static struct pf_rule_field {
PF_RULE_FIELD(flush, BREAK),
PF_RULE_FIELD(rdr, BREAK),
PF_RULE_FIELD(nat, BREAK),
+ PF_RULE_FIELD(route, BREAK),
PF_RULE_FIELD(logif, BREAK),
/*
@@ -303,6 +304,12 @@ pfctl_optimize_ruleset(struct pfctl *pf, struct pfctl_ruleset *rs)
} else
bzero(&por->por_rule.nat,
sizeof(por->por_rule.nat));
+ if (TAILQ_FIRST(&r->route.list) != NULL) {
+ TAILQ_INIT(&por->por_rule.route.list);
+ pfctl_move_pool(&r->route, &por->por_rule.route);
+ } else
+ bzero(&por->por_rule.route,
+ sizeof(por->por_rule.route));
TAILQ_INSERT_TAIL(&opt_queue, por, por_entry);
}
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index 85f1797e58e1..df76f8312cf3 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -943,6 +943,7 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
printf(" dup-to");
printf(" ");
print_pool(&r->rdr, 0, 0, r->af, PF_PASS);
+ print_pool(&r->route, 0, 0, r->af, PF_PASS);
}
if (r->af) {
if (r->af == AF_INET)
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 6e9418f59aef..e50fbc96a8ba 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -787,6 +787,7 @@ struct pf_krule {
TAILQ_ENTRY(pf_krule) entries;
struct pf_kpool nat;
struct pf_kpool rdr;
+ struct pf_kpool route;
struct pf_counter_u64 evaluations;
struct pf_counter_u64 packets[2];
@@ -2217,7 +2218,7 @@ VNET_DECLARE(struct unrhdr64, pf_stateid);
TAILQ_HEAD(pf_altqqueue, pf_altq);
VNET_DECLARE(struct pf_altqqueue, pf_altqs[4]);
#define V_pf_altqs VNET(pf_altqs)
-VNET_DECLARE(struct pf_kpalist, pf_pabuf[2]);
+VNET_DECLARE(struct pf_kpalist, pf_pabuf[3]);
#define V_pf_pabuf VNET(pf_pabuf)
VNET_DECLARE(u_int32_t, ticket_altqs_active);
diff --git a/sys/netpfil/pf/if_pfsync.c b/sys/netpfil/pf/if_pfsync.c
index 60bfb05d1570..98a2367b79b0 100644
--- a/sys/netpfil/pf/if_pfsync.c
+++ b/sys/netpfil/pf/if_pfsync.c
@@ -574,6 +574,12 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
* from the local ruleset.
*/
if (r != &V_pf_default_rule) {
+ struct pf_kpool *pool = &r->route;
+
+ /* Backwards compatibility. */
+ if (TAILQ_EMPTY(&pool->list))
+ pool = &r->rdr;
+
/*
* The ruleset is identical, try to recover. If the rule
* has a redirection pool with a single interface, there
@@ -582,7 +588,7 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
* give up, as we can't be sure that we will pick the
* same one as the pfsync peer did.
*/
- rpool_first = TAILQ_FIRST(&(r->rdr.list));
+ rpool_first = TAILQ_FIRST(&(pool->list));
if ((rpool_first == NULL) ||
(TAILQ_NEXT(rpool_first, entries) != NULL)) {
DPFPRINTF(PF_DEBUG_MISC,
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 83eca735d2bb..00d6583234c7 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -164,7 +164,7 @@ SDT_PROBE_DEFINE2(pf, purge, state, rowcount, "int", "size_t");
/* state tables */
VNET_DEFINE(struct pf_altqqueue, pf_altqs[4]);
-VNET_DEFINE(struct pf_kpalist, pf_pabuf[2]);
+VNET_DEFINE(struct pf_kpalist, pf_pabuf[3]);
VNET_DEFINE(struct pf_altqqueue *, pf_altqs_active);
VNET_DEFINE(struct pf_altqqueue *, pf_altq_ifs_active);
VNET_DEFINE(struct pf_altqqueue *, pf_altqs_inactive);
@@ -1267,6 +1267,7 @@ pf_initialize(void)
TAILQ_INIT(&V_pf_altqs[3]);
TAILQ_INIT(&V_pf_pabuf[0]);
TAILQ_INIT(&V_pf_pabuf[1]);
+ TAILQ_INIT(&V_pf_pabuf[2]);
V_pf_altqs_active = &V_pf_altqs[0];
V_pf_altq_ifs_active = &V_pf_altqs[1];
V_pf_altqs_inactive = &V_pf_altqs[2];
@@ -5900,6 +5901,12 @@ nextrule:
if (r->rt) {
struct pf_ksrc_node *sn = NULL;
struct pf_srchash *snh = NULL;
+ struct pf_kpool *pool = &r->route;
+
+ /* Backwards compatibility. */
+ if (TAILQ_EMPTY(&pool->list))
+ pool = &r->rdr;
+
/*
* Set act.rt here instead of in pf_rule_to_actions() because
* it is applied only from the last pass rule.
@@ -5907,7 +5914,7 @@ nextrule:
pd->act.rt = r->rt;
/* Don't use REASON_SET, pf_map_addr increases the reason counters */
reason = pf_map_addr_sn(pd->af, r, pd->src, &pd->act.rt_addr,
- &pd->act.rt_kif, NULL, &sn, &snh, &r->rdr);
+ &pd->act.rt_kif, NULL, &sn, &snh, pool);
if (reason != 0)
goto cleanup;
}
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
index 5de85c1fe7ef..24249ead6ba2 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -49,7 +49,7 @@
enum { PF_INOUT, PF_IN, PF_OUT };
enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NOSCRUB, PF_NAT, PF_NONAT,
PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, PF_DEFER,
- PF_MATCH, PF_AFRT };
+ PF_MATCH, PF_AFRT, PF_RT };
enum { PF_RULESET_SCRUB, PF_RULESET_FILTER, PF_RULESET_NAT,
PF_RULESET_BINAT, PF_RULESET_RDR, PF_RULESET_MAX };
enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT,
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index d206a9f8da43..340e7c25a501 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -436,7 +436,7 @@ pf_get_kpool(const char *anchor, u_int32_t ticket, u_int8_t rule_action,
struct pf_krule *rule;
int rs_num;
- MPASS(which == PF_RDR || which == PF_NAT);
+ MPASS(which == PF_RDR || which == PF_NAT || which == PF_RT);
ruleset = pf_find_kruleset(anchor);
if (ruleset == NULL)
@@ -470,10 +470,16 @@ pf_get_kpool(const char *anchor, u_int32_t ticket, u_int8_t rule_action,
if (rule == NULL)
return (NULL);
- if (which == PF_NAT)
- return (&rule->nat);
- else
+ switch (which) {
+ case PF_RDR:
return (&rule->rdr);
+ case PF_NAT:
+ return (&rule->nat);
+ case PF_RT:
+ return (&rule->route);
+ default:
+ panic("Unknow pool type %d", which);
+ }
}
static void
@@ -612,6 +618,7 @@ pf_free_rule(struct pf_krule *rule)
pf_kanchor_remove(rule);
pf_empty_kpool(&rule->rdr.list);
pf_empty_kpool(&rule->nat.list);
+ pf_empty_kpool(&rule->route.list);
pf_krule_free(rule);
}
@@ -1832,6 +1839,7 @@ pf_krule_alloc(void)
rule = malloc(sizeof(struct pf_krule), M_PFRULE, M_WAITOK | M_ZERO);
mtx_init(&rule->nat.mtx, "pf_krule_nat_pool", NULL, MTX_DEF);
mtx_init(&rule->rdr.mtx, "pf_krule_rdr_pool", NULL, MTX_DEF);
+ mtx_init(&rule->route.mtx, "pf_krule_route_pool", NULL, MTX_DEF);
rule->timestamp = uma_zalloc_pcpu(pf_timestamp_pcpu_zone,
M_WAITOK | M_ZERO);
return (rule);
@@ -1871,6 +1879,7 @@ pf_krule_free(struct pf_krule *rule)
mtx_destroy(&rule->nat.mtx);
mtx_destroy(&rule->rdr.mtx);
+ mtx_destroy(&rule->route.mtx);
free(rule, M_PFRULE);
}
@@ -2106,6 +2115,7 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
rule->cpid = pid;
TAILQ_INIT(&rule->rdr.list);
TAILQ_INIT(&rule->nat.list);
+ TAILQ_INIT(&rule->route.list);
PF_CONFIG_LOCK();
PF_RULES_WLOCK();
@@ -2203,7 +2213,7 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
(rule->set_prio[0] > PF_PRIO_MAX ||
rule->set_prio[1] > PF_PRIO_MAX))
error = EINVAL;
- for (int i = 0; i < 2; i++) {
+ for (int i = 0; i < 3; i++) {
TAILQ_FOREACH(pa, &V_pf_pabuf[i], entries)
if (pa->addr.type == PF_ADDR_TABLE) {
pa->addr.p.tbl = pfr_attach_table(ruleset,
@@ -2225,10 +2235,12 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
pf_mv_kpool(&V_pf_pabuf[0], &rule->nat.list);
pf_mv_kpool(&V_pf_pabuf[1], &rule->rdr.list);
+ pf_mv_kpool(&V_pf_pabuf[2], &rule->route.list);
if (((((rule->action == PF_NAT) || (rule->action == PF_RDR) ||
(rule->action == PF_BINAT)) && rule->anchor == NULL) ||
(rule->rt > PF_NOPFROUTE)) &&
- (TAILQ_FIRST(&rule->rdr.list) == NULL))
+ (TAILQ_FIRST(&rule->rdr.list) == NULL &&
+ TAILQ_FIRST(&rule->route.list) == NULL))
error = EINVAL;
if (rule->action == PF_PASS && rule->rdr.opts & PF_POOL_STICKYADDR &&
@@ -2244,6 +2256,7 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
rule->nat.cur = TAILQ_FIRST(&rule->nat.list);
rule->rdr.cur = TAILQ_FIRST(&rule->rdr.list);
+ rule->route.cur = TAILQ_FIRST(&rule->route.list);
TAILQ_INSERT_TAIL(ruleset->rules[rs_num].inactive.ptr,
rule, entries);
ruleset->rules[rs_num].inactive.rcount++;
@@ -2553,6 +2566,7 @@ pf_ioctl_begin_addrs(uint32_t *ticket)
PF_RULES_WLOCK();
pf_empty_kpool(&V_pf_pabuf[0]);
pf_empty_kpool(&V_pf_pabuf[1]);
+ pf_empty_kpool(&V_pf_pabuf[2]);
*ticket = ++V_ticket_pabuf;
PF_RULES_WUNLOCK();
@@ -2566,7 +2580,8 @@ pf_ioctl_add_addr(struct pf_nl_pooladdr *pp)
struct pfi_kkif *kif = NULL;
int error;
- if (pp->which != PF_RDR && pp->which != PF_NAT)
+ if (pp->which != PF_RDR && pp->which != PF_NAT &&
+ pp->which != PF_RT)
return (EINVAL);
#ifndef INET
@@ -2613,8 +2628,17 @@ pf_ioctl_add_addr(struct pf_nl_pooladdr *pp)
PF_RULES_WUNLOCK();
goto out;
}
- TAILQ_INSERT_TAIL(&V_pf_pabuf[pp->which == PF_RDR ? 1 : 0],
- pa, entries);
+ switch (pp->which) {
+ case PF_NAT:
+ TAILQ_INSERT_TAIL(&V_pf_pabuf[0], pa, entries);
+ break;
+ case PF_RDR:
+ TAILQ_INSERT_TAIL(&V_pf_pabuf[1], pa, entries);
+ break;
+ case PF_RT:
+ TAILQ_INSERT_TAIL(&V_pf_pabuf[2], pa, entries);
+ break;
+ }
PF_RULES_WUNLOCK();
return (0);
@@ -2632,7 +2656,8 @@ pf_ioctl_get_addrs(struct pf_nl_pooladdr *pp)
PF_RULES_RLOCK_TRACKER;
- if (pp->which != PF_RDR && pp->which != PF_NAT)
+ if (pp->which != PF_RDR && pp->which != PF_NAT &&
+ pp->which != PF_RT)
return (EINVAL);
pp->anchor[sizeof(pp->anchor) - 1] = 0;
@@ -2659,7 +2684,8 @@ pf_ioctl_get_addr(struct pf_nl_pooladdr *pp)
struct pf_kpooladdr *pa;
u_int32_t nr = 0;
- if (pp->which != PF_RDR && pp->which != PF_NAT)
+ if (pp->which != PF_RDR && pp->which != PF_NAT &&
+ pp->which != PF_RT)
return (EINVAL);
PF_RULES_RLOCK_TRACKER;
@@ -3652,6 +3678,7 @@ DIOCGETRULENV_error:
newrule->cpid = td->td_proc ? td->td_proc->p_pid : 0;
TAILQ_INIT(&newrule->nat.list);
TAILQ_INIT(&newrule->rdr.list);
+ TAILQ_INIT(&newrule->route.list);
}
#define ERROUT(x) ERROUT_IOCTL(DIOCCHANGERULE_error, x)
@@ -3748,7 +3775,7 @@ DIOCGETRULENV_error:
error = ENOMEM;
if (pf_kanchor_setup(newrule, ruleset, pcr->anchor_call))
error = EINVAL;
- for (int i = 0; i < 2; i++) {
+ for (int i = 0; i < 3; i++) {
TAILQ_FOREACH(pa, &V_pf_pabuf[i], entries)
if (pa->addr.type == PF_ADDR_TABLE) {
pa->addr.p.tbl =
@@ -3772,6 +3799,7 @@ DIOCGETRULENV_error:
pf_mv_kpool(&V_pf_pabuf[0], &newrule->nat.list);
pf_mv_kpool(&V_pf_pabuf[1], &newrule->rdr.list);
+ pf_mv_kpool(&V_pf_pabuf[2], &newrule->route.list);
if (((((newrule->action == PF_NAT) ||
(newrule->action == PF_RDR) ||
(newrule->action == PF_BINAT) ||
@@ -3792,6 +3820,7 @@ DIOCGETRULENV_error:
}
pf_empty_kpool(&V_pf_pabuf[0]);
pf_empty_kpool(&V_pf_pabuf[1]);
+ pf_empty_kpool(&V_pf_pabuf[2]);
if (pcr->action == PF_CHANGE_ADD_HEAD)
oldrule = TAILQ_FIRST(
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
index 3e7a6965d387..c0f722b1fd18 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -736,6 +736,7 @@ static const struct nlattr_parser nla_p_rule[] = {
{ .type = PF_RT_MAX_SRC_CONN, .off = _OUT(max_src_conn), .cb = nlattr_get_uint32 },
{ .type = PF_RT_RPOOL_NAT, .off = _OUT(nat), .arg = &pool_parser, .cb = nlattr_get_nested },
{ .type = PF_RT_NAF, .off = _OUT(naf), .cb = nlattr_get_uint8 },
+ { .type = PF_RT_RPOOL_RT, .off = _OUT(route), .arg = &pool_parser, .cb = nlattr_get_nested },
};
NL_DECLARE_ATTR_PARSER(rule_parser, nla_p_rule);
#undef _OUT
@@ -909,6 +910,7 @@ pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
nlattr_add_string(nw, PF_RT_OVERLOAD_TBLNAME, rule->overload_tblname);
nlattr_add_pool(nw, PF_RT_RPOOL_RDR, &rule->rdr);
nlattr_add_pool(nw, PF_RT_RPOOL_NAT, &rule->nat);
+ nlattr_add_pool(nw, PF_RT_RPOOL_RT, &rule->route);
nlattr_add_u32(nw, PF_RT_OS_FINGERPRINT, rule->os_fingerprint);
nlattr_add_u32(nw, PF_RT_RTABLEID, rule->rtableid);
nlattr_add_timeout(nw, PF_RT_TIMEOUT, rule->timeout);
diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h
index 8c0149891773..d749ef3ab99e 100644
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -267,6 +267,7 @@ enum pf_rule_type_t {
PF_RT_MAX_SRC_CONN = 74, /* u32 */
PF_RT_RPOOL_NAT = 75, /* nested, pf_rpool_type_t */
PF_RT_NAF = 76, /* u8 */
+ PF_RT_RPOOL_RT = 77, /* nested, pf_rpool_type_t */
};
enum pf_addrule_type_t {