pf: Make nat-to and rdr-to work properly both on in and out rules

New-style address translation is done by nat-to and rdr-to actions on
normal match and pass rules. Those rules, when used without address
translation, can be specified without direction. But that allows users
to specify pre-routing nat and post-routing rdr. This case is not
handled properly and causes pre-routing nat to modify destination
address, as if it was a rdr rule, and post-routing rdr to modify source
address, as if it was a nat rule.

Ensure that nat-to action modifies source address and rdr-to destination
address no matter in which direction the rule is applied. The man page
for pf.conf already specifies that nat-to and rdr-to rules should be
limited to respective directions.

PR:		288577
Reviewed by:	kp
MFC after:	3 days
Sponsored by:	InnoGames GmbH
Differential Revision:	https://reviews.freebsd.org/D53216
This commit is contained in:
Kajetan Staszkiewicz
2025-10-01 15:51:46 +02:00
parent 05d4858460
commit 646798b678
2 changed files with 55 additions and 8 deletions
+14 -2
View File
@@ -974,6 +974,7 @@ pf_get_transaddr(struct pf_test_ctx *ctx, struct pf_krule *r,
{
struct pf_pdesc *pd = ctx->pd;
struct pf_addr *naddr;
int idx;
uint16_t *nportp;
uint16_t low, high;
u_short reason;
@@ -988,8 +989,19 @@ pf_get_transaddr(struct pf_test_ctx *ctx, struct pf_krule *r,
return (PFRES_MEMORY);
}
naddr = &ctx->nk->addr[1];
nportp = &ctx->nk->port[1];
switch (nat_action) {
case PF_NAT:
idx = pd->sidx;
break;
case PF_BINAT:
idx = 1;
break;
case PF_RDR:
idx = pd->didx;
break;
}
naddr = &ctx->nk->addr[idx];
nportp = &ctx->nk->port[idx];
switch (nat_action) {
case PF_NAT:
+41 -6
View File
@@ -477,15 +477,49 @@ no_addrs_random_cleanup()
pft_cleanup
}
atf_test_case "nat_pass" "cleanup"
nat_pass_head()
atf_test_case "nat_pass_in" "cleanup"
nat_pass_in_head()
{
atf_set descr 'IPv4 NAT on pass rule'
atf_set descr 'IPv4 NAT on inbound pass rule'
atf_set require.user root
atf_set require.progs scapy
}
nat_pass_body()
nat_pass_in_body()
{
setup_router_server_ipv4
# Delete the route back to make sure that the traffic has been NAT-ed
jexec server route del -net ${net_tester} ${net_server_host_router}
# Provide routing back to the NAT address
jexec server route add 203.0.113.0/24 ${net_server_host_router}
jexec router route add 203.0.113.0/24 -iface ${epair_tester}b
pft_set_rules router \
"block" \
"pass in on ${epair_tester}b inet proto tcp nat-to 203.0.113.0 keep state" \
"pass out on ${epair_server}a inet proto tcp keep state"
ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201
jexec router pfctl -qvvsr
jexec router pfctl -qvvss
jexec router ifconfig
jexec router netstat -rn
}
nat_pass_in_cleanup()
{
pft_cleanup
}
nat_pass_out_head()
{
atf_set descr 'IPv4 NAT on outbound pass rule'
atf_set require.user root
atf_set require.progs scapy
}
nat_pass_out_body()
{
setup_router_server_ipv4
# Delete the route back to make sure that the traffic has been NAT-ed
@@ -504,7 +538,7 @@ nat_pass_body()
jexec router netstat -rn
}
nat_pass_cleanup()
nat_pass_out_cleanup()
{
pft_cleanup
}
@@ -874,7 +908,8 @@ atf_init_test_cases()
atf_add_test_case "no_addrs_random"
atf_add_test_case "map_e_compat"
atf_add_test_case "map_e_pass"
atf_add_test_case "nat_pass"
atf_add_test_case "nat_pass_in"
atf_add_test_case "nat_pass_out"
atf_add_test_case "nat_match"
atf_add_test_case "binat_compat"
atf_add_test_case "binat_match"