Hey Peter,

Thanks for the reminder to do a bit of house keeping and that I might be
little overzealous with ‘quick’.

With that said, I did find that the likely problem is that 464 CLAT client
implementations don't support connecting to RFC 1918 IPs. I couldn’t
understand that with logging turned on I wasn’t seeing anything come in
when trying to connect to these RFC 1918 IPs, so I decided to capture
packets on the client (macOS 15.5). Lo and behold when I tried pinging an
RFC 1918 IP on my network / VPN networks from a IPv6 only client, it was
not leaving the Mac’s interfaces. Doing some further digging I found that
CLAT clients do not support RFC 1918 and will work with Internet routable
addresses.

I can connect to the NAT64 prefix address but only certain protocols (eg.
SSH). If I try to use a web browser to connect to the NAT64 prefix address
it just boots me off to the search engine. I guess it’s still early days
for web browsers supporting IPv6 transition technologies.

-Chris

On Jul 20, 2025 at 12:22:28 PM, Peter Hessler <phess...@theapt.org> wrote:

> "quick", quickly fucked you.  try re-writing your rules without it.
>
> Also, think about what IP address ranges you are using, and what IP
> address ranges you are trying to block in your ruleset.  Don't try to be
> clever before you understand what went wrong.
>
>
>
> On 2025 Jul 20 (Sun) at 11:25:42 -0700 (-0700), Chris Jones wrote:
> :Good day,
> :
> :I’m wondering if someone familiar with IPv6 and NAT64 can help me
> :understand what I’m missing here as I’m struggling to get NAT64 working
> :with RFC 1918 IPv4-only hosts (including VPN hosts and hairpining to
> :internal hosts) . I appreciate any insight you may be able to provide.
> :
> :I’ve configured two af-to rules in my pf.conf to provide IPv6 NAT64
> :Provider-side transLATor (PLAT) to 64:ff9b::/96 from the egress interface
> :em0, and to 64:ff9b::a00:0/104 (translates to 10/8 IPv4 prefix) from an
> :internal interface (to reach remote VPN networks and hairpin NAT). I’ve
> :also configured the IPv6-Only Preferred DHCP option 108 (RFC 8925) on my
> :home network lab.
> :
> :Most day to day things work, but I’m struggling to connect to IPv4
> :addresses on the other side of an IPSec tunnel from IPv6-only hosts using
> :NAT64, and also to hairpin and reach hosts that don’t support IPv6 only
> :(eg. IoT devices) on the vport99 network (10.1.0.0/24 prefix) from IPv6
> :only hosts.
> :
> :I ran the following tests:
> :
> :   1. Ping an IPv4-only host on the Internet from an IPv6-only host -
> :   *SUCCESS*
> :   2. Ping an IPv6-only (Inernet and locally) host from an IPv6-only host
> -
> :   *SUCCESS*
> :   3. Ping a IPv6 NAT64 prefix (64:ff9b::a02:1) which translates to a IPv4
> :   address (10.2.0.1) on the other side of the IPSec tunnel and confirmed
> the
> :   traffic on sec(4) interface using tcpdump - *SUCCESS*
> :   4. Ping the IPv4 address 10.2.0.1 (from test 3) on the other side of
> the
> :   IPSec tunnel (I’m not seeing this hit a rule and I’m not seeing any
> traffic
> :   on the sec(4) interface) - *FAILED*
> :   5. Ping the IPv4 address (10.2.0.1 from test 3) from the IPv4 address
> :   internal interface vport99 - *SUCCESS*
> :
> :Below is the relevant portions of my router/firewall configuration. Please
> :let me know if I have missed some details or you would like to see
> :something else.
> :
> :*/etc/hostname.em0*
> :
> :descr "Internet"
> :inet autoconf
> :inet6 autoconf
> :
> :*/etc/hostname.em1*
> :
> :descr "Internal VLAN Trunk"
> :up
> :
> :*/etc/hostname.vlan99*
> :
> :parent em1
> :vnetid 99
> :up
> :
> :*/etc/hostname.vport99*
> :
> :group internal
> :descr "Home Network"
> :inet 10.1.0.1/24
> :inet6 autoconf
> :inet6 alias fd34:1107:d22e::1/64
> :up
> :
> :*/etc/hostname.veb99*
> :
> :add vlan99
> :add vport99
> :up
> :
> :*/etc/hostname.sec0*
> :
> :inet 10.250.0.1 255.255.255.252 10.250.0.2
> :up
> :!route add -net 10.2.0/24 10.250.0.2
> :
> :*/etc/dhcp6leased.conf*
> :
> :## Global
> :request rapid commit
> :
> :## PD
> :request prefix delegation on em0 for {
> :vport99/64
> :#vport100/64
> :#vport105/64
> :}
> :
> :*/etc/rad.conf*
> :
> :interface vport99
> :  dns {
> :    nameserver fd34:1107:d22e::1
> :  }
> :#interface vport100
> :#interface vport105
> :
> :# Advertise the NAT64 prefix available on this network
> :nat64 prefix 64:ff9b::/96
> :
> :*/etc/dhcpd.conf*
> :
> :subnet 10.1.0.0 netmask 255.255.255.0 {
> : option domain-name-servers 10.1.0.1;
> : option ntp-servers 10.1.0.1;
> : option routers 10.1.0.1;
> :
> : # Send DHCP Option 108 (RFC 8925) to tell clients
> : option ipv6-only-preferred 900;
> :
> : range 10.1.0.100 10.1.0.199;
> :}
> :
> :*/etc/pf.conf*
> :
> :home_if = “vport99"
> :
> :nat64_prefix = "64:ff9b::/96”
> :
> :table <martians> { 0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16     \
> :  172.16.0.0/12 192.0.0.0/24 192.0.2.0/24 224.0.0.0/3 192.168.0.0/16   \
> :  198.18.0.0/15 198.51.100.0/24 203.0.113.0/24                         \
> :  ::/128 ::/96 ::1/128 ::ffff:0:0/96 100::/64 2001:10::/28 2001:2::/48 \
> :  2001:db8::/32 3ffe::/16 fec0::/10 fc00::/7 }
> :
> :icmp_types = "echoreq"
> :icmp6_types = "{ unreach toobig timex paramprob echoreq routersol
> routeradv \
> :  neighbrsol neighbradv redir routrrenum }"
> :
> :set block-policy drop
> :set loginterface egress
> :set skip on lo0
> :
> :match in all scrub (no-df random-id max-mss 1440)
> :match out on egress inet from !(egress:network) to any nat-to (egress:0)
> :
> :antispoof quick for { egress $home_if $guest $kids }
> :block in quick on egress from <martians> to any
> :block return out quick on egress from any to <martians>
> :
> :pass in quick on egress inet proto { tcp udp } from any to (egress:0)
> :port bootpc
> :pass out quick on egress inet proto { tcp udp } from (egress:0) to any
> :port bootps
> :
> :block all
> :
> :pass out quick inet
> :pass out quick inet6
> :
> :pass out log (all) quick on enc0 keep state (if-bound)
> :
> :pass in on egress proto udp from any to (egress) port {isakmp
> :ipsec-nat-t} tag IKED
> :pass in on egress proto esp from any to (egress) tag IKED
> :
> :pass in on enc0 proto ipencap from any to (egress) keep state (if-bound)
> :
> :pass in log on sec0 from 10.250.0.2/32 to 10.250.0.1/32
> :
> :pass in on $home_if inet from $home_if:network
> :pass in on $home_if inet6
> :
> :pass in quick on egress inet6 proto icmp6 all icmp6-type $icmp6_types
> :pass in quick on egress proto udp from fe80::/10 port dhcpv6-server to
> :fe80::/10 port dhcpv6-client
> :
> :# NAT64 for IPSec tunnel traffic
> :pass in log on $home_if inet6 from any to 64:ff9b::a00:0/104 af-to
> :inet from 10.1.0.1
> :
> :# NAT64 Provider-side transLATor (PLAT)
> :pass in log on $home_if inet6 from any to $nat64_prefix af-to inet
> :from (egress:0)
>
> --
> Do not believe in miracles -- rely on them.
>

Reply via email to