Hi David,

Nope, no pfsync running on this machine.

Regards,

Jordan

On 11/18/25 03:52, David Gwynne wrote:
just checking, but are you using pfsync? do you have defer enabled?

On 14 Nov 2025, at 10:51, Jordan Geoghegan <[email protected]> wrote:

Hello,

I've observed some interesting behaviour when using static-port with af-to 
rules.

I may be missing something obvious and have somehow misconfigured something, 
but this smells fishy to me, so I thought I'd report it.

I've recently set up an IPv6-mostly network and wanted cone NAT behaviour to 
allow things like STUN for FaceTime to work.

In the man pages it states that "static-port" is for use with nat-to rules, but 
it turns out it is also accepted by af-to, and largely works.

The interesting behaviour I've noticed is that when static-port is used in 
conjunction with af-to rules the initial packet of a connection attempt to the 
NAT64 prefix is leaked out the egress interface. No such behaviour is observed 
when the static-port option is not used.

Steps to reproduce:

1) Create af-to rule with static-port option
2) Notice how af-to rule misses the first packet / connection attempt and 
connection times out
3) Notice how af-to works as expected for subsequent packets

Heres the PF rules I'm using to implement NAT64:
...
pass in on $lan_if inet6 from !(egress:network) to 64:ff9b::/96 af-to inet from 
(egress:0) keep state
# Use static port for UDP NAT64 traffic so that STUN for video calling and 
gaming etc works.
pass in on $lan_if inet6 proto udp from !(egress:network) to 64:ff9b::/96 af-to 
inet from (egress:0) static-port keep state

To try and illustrate the issue, here are some tcpdump outputs from using dig 
to make a DNS query to 1.1.1.1 using IPv6 mapped format 64:ff9b::101:101.

thinkpad$ time dig @64:ff9b::101:101 +short google.ca
142.250.73.99
     0m05.04s real     0m00.00s user     0m00.00s system

That took 5 seconds, that's a super long time!

Lets run tcpdump on the router and see whats going on:

Egress:
er8pro$ doas tcpdump -lni cnmac4 port domain
...
15:06:28.255337 2006:420:69:7:254e:c5cd:b280:7e0c.23782 > 64:ff9b::101:101.53: 
63837+ [1au] A? google.ca.(38)
15:06:33.254922 55.44.33.161.23782 > 1.1.1.1.53: 63837+ [1au] A? google.ca.(38) 
(DF)
15:06:33.267623 1.1.1.1.53 > 55.44.33.161.23782: 63837 1/0/1 A 
142.250.73.99(54) (DF)

And here's how it looks from the LAN side:

er8pro$ doas tcpdump -lni cnmac5 port domain
...
15:13:20.331256 2006:420:69:7:254e:c5cd:b280:7e0c.48398 > 64:ff9b::101:101.53: 
65486+ [1au] A? google.ca.(38)
15:13:25.327384 2006:420:69:7:254e:c5cd:b280:7e0c.48398 > 64:ff9b::101:101.53: 
65486+ [1au] A? google.ca.(38)
15:13:25.341485 64:ff9b::101:101.53 > 2006:420:69:7:254e:c5cd:b280:7e0c.48398: 
65486 1/0/1 A 142.250.73.67(54)

Here we can see the initial connection attempt to 64:ff9b::101:101 is somehow 
"missed" by the af-to rule and leaks out onto egress and inevitably times out. 
The subsequent connection attempt 5 seconds later succeeds however.

To be clear, the behaviour only presents itself when using static-port in conjunction with af-to 
rules. As soon as af-to rule has static-port option removed, no such "leaks" occur. I 
know I could just make a pf rule to explicitly block outgoing traffic to the NAT64 prefix, but it 
doesn't change the fact that for some reason the af-to rule "misses" the first connection 
attempt and forces a connection timeout if static-port option is set.

I hope this makes sense. I know using af-to with static-port isn't explicitly documented, 
but I think having support for "cone" NAT64 is a pretty killer feature and is 
necessary to facilitate many if not most peer to peer communications for IPv4. Living in 
rural Canada it's quite inefficient to have my packets destined for the Yukon or Labrador 
get there by way of Apple TURN servers in Cupertino, so for me at least, its a hugely 
appreciated functionality.

I know that this email is getting long, but in case some more examples are 
desired, here is the NAT64 prefix leaking on egress when attempting a Facetime 
call and also mDNS queries leaking from my AndroidTV box that seems to get 
confused and sends out mDNS queries from its CLAT.

When attempting to make a FaceTime call to a relative whose on an IPv4 only 
connection, the initial connection attempts are missed by af-to rule, but on 
subsequent retries succeeds.

# Initial Connection Attempts leak
14:10:34.061299 2006:420:69:7:d934:b317:7d73:736b.16393 > 
64:ff9b::11bc:b219.3490: udp 172 [flowlabel 0xf10d4]
14:10:43.275992 2006:420:69:7:d934:b317:7d73:736b.16393 > 
64:ff9b::2a1f:1413.16393: udp 72 [flowlabel 0xd8709]
...
# After connection is reattempted af-to does the needful and starts translating 
as expected.
14:11:16.309508 55.44.33.161.16393 > 42.31.20.19.16393: udp 303
14:11:16.313229 42.31.20.19.16393 > 55.44.33.161.16393: udp 1050
14:11:16.317260 42.31.20.19.16393 > 55.44.33.161.16393: udp 1047
14:11:16.321257 42.31.20.19.16393 > 55.44.33.161.16393: udp 1049
14:11:16.321259 42.31.20.19.16393 > 55.44.33.161.16393: udp 1049

# Android TV with CLAT leaking
14:12:12.422220 2006:420:69:7:34fb:e138:ba30:c741.5353 > 64:ff9b::e000:fb.5353: 
0[|domain]
14:12:12.571486 2006:420:69:7:ef5d:8bb9:202e:fc60.5353 > 64:ff9b::e000:fb.5353: 
0[|domain]
14:13:48.965738 2006:420:69:7:34fb:e138:ba30:c741.5353 > 64:ff9b::e000:fb.5353: 
0 PTR? _googlecast._tcp.local.(40


Please let me know if there are any more details etc I can provide.

Regards,

Jordan


Reply via email to