There do appear to be contradictions in documentation as well as the pf
book. The Configuring NAT section is correct as you have seen with your
own rules.

Here is the minimum set of stateless rules that allows ICMP traffic
between my laptop and Cloudflare.

# Options.
set block-policy drop

# Macros.
wan = em0
wifi = vlan4
external = 76.154.165.21
laptop = 192.168.6.2
cflare = 1.1.1.1

# NAT.
match out on $wan inet proto icmp from $laptop nat-to $external static-port

# Filtering rules.
pass in quick on $wifi inet proto icmp from $laptop to $cflare no state
pass out quick on $wan inet proto icmp from $external to $cflare keep state 
(if-bound)
pass out quick on $wifi inet proto icmp from $cflare to $laptop no state
block quick

The second filter rule _must_ be stateful in order for the router to
map the ICMP Query ID back to the original source IP (i.e., my laptop).

When relying on only stateful rules, we can remove the last pass rule-
since the first filter rule when hit will allow the returning Echo
Response.

If instead of using match rules for NAT, you use pass out; then pf will
fail to load pf.conf(5) if the second rule is stateless:

/etc/pf.conf:13: nat-to and rdr-to require keep state
/etc/pf.conf:13: skipping rule due to errors
/etc/pf.conf:13: rule expands to no valid combination
pfctl: Syntax error in config file: pf rules not loaded

If you want to easily distinguish traffic sourced from your router
leaving em0 from traffic sourced from your (V)LAN devices, then
you will need to have a separate rule. For example:

pass out quick log on em0 inet from (em0)
match out log on em0 inet from athn0:network nat-to (em0)
pass out log quick on em0 inet

Reply via email to