pf not matching packets to state

cjc

Developer
I'm doing something a little wonky. I'm trying to set up a FreeBSD router as a NAT64 PLAT. I've been using pf as my firewall on this system forever, but FreeBSD pf does not have NAT64 capabilities. ipfw does have NAT64. You can use both at once, so I'm trying to have pf still do my firewall and IPv4 NAT to the Internet, while ipfw just does the NAT64.

It actually is partially working. I can see the IPv6 to IPv4 translated packets heading out and coming back with a packet capture on the external interface. However, the packets do not get through the pf firewall on the way back. They get blocked after being translated back to IPv6. Here's what I see from the pflog,

Code:
22:50:51.948337 rule 0/0(match): block in on igb0: (class 0x20, hlim 118, next-header ICMPv6 (58) payload length: 64) 64:ff96::808:404 > 2601:644:8b80:771a:e088:f9f4:7273:9363: ICMP6, echo reply, seq 2141
22:50:53.042151 rule 0/0(match): block in on igb0: (class 0x20, hlim 118, next-header ICMPv6 (58) payload length: 64) 64:ff96::808:404 > 2601:644:8b80:771a:e088:f9f4:7273:9363: ICMP6, echo reply, seq 2142
22:50:53.996210 rule 0/0(match): block in on igb0: (class 0x20, hlim 118, next-header ICMPv6 (58) payload length: 64) 64:ff96::808:404 > 2601:644:8b80:771a:e088:f9f4:7273:9363: ICMP6, echo reply, seq 2143

That's a ping coming back from 8.8.4.4 after being translated to the Well-Known IPv6 NAT64 XLAT prefix and headed to the original sender's real IPv6. Rule 0 is a simple catch-all for any traffic that doesn't match another policy,

Code:
@0 block drop log all

And igb0 is the box's external interface. But this traffic does match an existing allowed state!

Code:
$ sudo pfctl -v -s state
...[snip]...
all ipv6-icmp 64:ff96::808:404[2] <- 2601:644:8b80:771a:e088:f9f4:7273:9363[2]       NO_TRAFFIC:NO_TRAFFIC
   age 00:28:46, expires in 00:00:09, 1686:0 pkts, 175344:0 bytes, rule 12
...[snip]...

Just if you're curious, rule 12,

Code:
@12 pass quick on igb1 all flags S/SA keep state

Is a rule to pass traffic on the internal, trusted-side interface.

I think I'm missing something obvious, but it's not obvious to me right now. Why is pf dropping the responses if it has a good state for them?

Note that there are some similarities to this thread,

In that it works for traffic within my LAN when it bounces through the internal interface only. Flipping the sysctl mentioned at the end of that thread has an odd effect. Things don't start to work, but one packet gets through... my guess the packet already out the door successfully makes it back in, but breaks the rest of the traffic going out. Outgoing traffic doesn't get IPv4 NAT applied by pf with the sysctl set.
 
Last edited by a moderator:
I am by no means an expert, rather the contrary.

But the log says ICMP rejected.
I do not know: which protocols are allowed by default?
All?
Or only proto { tcp, udp } ?

So no idea whether it is just a nonsense idea... but I guess I would try explicitly allowing ICMP by something like
pass quick on igb1 proto { tcp, udp, icmp } all flags S/SA keep state

If this doesn't help, sorry for the noise.
 
If you look at the rule 12 that I quoted and the state matches, it is an "all" rule. And the state says "ipv6-icmp" just like the pflog, "ICMP6, echo reply."

I had a thought about another way of doing this shortly after posting. I don't need to do the NAT64 on the network's gateway. It's perfectly reasonable to put it on another box on the network. I just need to route the Well-Known NAT64 prefix and the IPv4 NAT64 pool to this other box. That should get around any catches to having pf and ipfw living on the same box.
 
if you're curious, rule 12,


@12 pass quick on igb1 all flags S/SA keep state


Is a rule to pass traffic on the internal, trusted-side interface.
Thanks for telling me, because I couldn't tell which direction it was going from the rule. Maybe if you told pf it would help matters. That's how my rules are set up top down:

Code:
### Reassemble fragmented packets
scrub in on $ext_if all fragment reassemble

### Default deny everything
block log all

### Pass loopback
set skip on lo0

### Block all IPv6
block in quick inet6 all
block out quick inet6 all

## Block specific ports
block in quick log on $ext_if proto tcp from any to any port $netbios_tcp
block in quick log on $ext_if proto udp from any to any port $netbios_udp

### Keep and modulate state of outbound tcp, udp and icmp traffic
pass out on $ext_if proto { tcp, udp, icmp } from any to any modulate state
 
Mixing two firewalls is not a supported configuration. It's possible, but there are many, many sharp corners and you may have found one.

The current policy on running two firewalls at once is "If you break it you get to keep the pieces.".
 
Back
Top