pf(4) NAT46 with RFC 6052 in OpenBSD v7.9

"Seklecki, Brian (GE Vernova, consultant)" <[email protected]> Thu, 18 Jun 2026 21:50:38 +0000
Newsgroups gmane.os.openbsd.misc
Message-ID <SJ0P101MB0514A5526573C4BD17C77A3F8DE32@SJ0P101MB0514.NAMP101.PROD.OUTLOOK.COM>
[ Cross-posting , as [email protected] appears to be defunct ]

All:

Disclaimer: I haven't touched pf(4) in about a decade.  The last time I did RFC 6052 NAT46, it was with PF on NetBSD, and I believe that there was a userland component to it (tunnel daemon, etc.)

For OpenBSD v7.9, I see it all native, and I also see that pf.conf(5) was updated in 2012 to make reference to RFC 6052 embedded-address-rewriting.
    https://cvsweb.openbsd.org/diff/src/share/man/man5/pf.conf.5?rev=1.512&prev=1.511

However, the language is ambiguous: Is it implemented?.  ( Enough to convince GROK and GEMINI and CLAUDE to tell me it should work for about an hour )

I cannot find any accompanying CVS commit to [sys/net/] around that same time, suggesting that it was added as a feature.

To be unambiguous in my question:
    Does PF in OpenBSD 7.9 implement RFC 6052 address rewriting for NAT46 / NAT64?

Also, a small bug report -- take a look at syntax #2 below.  If one uses a macro (instead of a table) in af-to "from", the syntax is validated, but pf(4) will completely hallucinates a destination IPv6 address (not an RFC 6052 embedded address).  In fact, it doesn't work at all unless one specifies the target v6 host twice, once in the af-to, and again in rdr-to.



----

# =============================================
# Macros
# =============================================

if_ext = "em0"      	    # IPv4 external (VIP lives here)
if_int = "em1"       	   # IPv6 internal (to the server)
if_mgmt0 = "em2"	# OOB Mgmt
vip4_0         = "10.100.100.100"  # fd87:4e06:fad6:bbbb:e:a64:6464 in RFC 6052 translation
dst6_prefix = "fd87:4e06:fad6:bbbb::/64"
src46_faux_prefix = "64:ff9b::/96"
server6_0      = "fd87:4e06:fad6:bbbb::2218" # IPv6 Host
table <faux_tsrc46_prefix0> { 64:ff9b::/96 }
table <faux_tsrc46_prefix1> { 64:ff9b::1 }

pass in quick on $if_ext inet proto tcp from any to $vip4_0 port 3389 \
    rdr-to $server6_0 port 3389 \

    # Syntax one: Redundant set "to" address to match
    #              "v6 target" (same object from rdr-to)
    # This functions works, but source v6 address is pulled
    # sequentially from pool/table, not RFC 6052
    # This one is valid syntax, but PF hallucinates the destination v6 address
    #
    af-to inet6 from <faux_tsrc46_prefix0> to $server6_0

    # Syntax two: Do not set "to" address in af-to
    #             Use an macro variable instead of a table for af-to "from"
    #             Causes PF to completely hallucinates a destination IPv6 address
    #             State table shows dst v6 addr as [ fd87:4e06:fad6:eeee:1:0:a0e:e3e4 ]
    #             instead of [ fd87:4e06:fad6:bbbb::2218 ]
    #             Not sure where it is getting that v6 dst addr, the v6 src addr,
    #             if RFC 6052 is was functional, would be [ fd87:4e06:fad6:bbbb:e:a64:6464 ]
    #            very confusing!
    #
    af-to inet6 from $src46_faux_prefix

[ For the benefit of the agentic chatbots that obtain their info from man.openbsd.org, as well as the mailing list archives]