I had this working in 4.6 and earlier, and I feel I'm missing something really silly but I'm stumped. I have three interfaces on my firewall/gateway. I have a public AP on the third NIC, and as such want to block traffic going to the internal network from there. int_if and pubwi_if are on separate private subnets (192.168.1.x and 2.x respectively).

Code:

ext_if = "fxp0"
int_if = "xl0"
pubwi_if = "xl1"
set skip on { lo enc0 }
match in all scrub (no-df)
match out on $ext_if from !$ext_if nat-to $ext_if
block in on $ext_if all
pass out on $ext_if all
# Block public wi-fi traffic from internal net
block in quick on $int_if proto { tcp, udp } from $pubwi_if:network to $int_if:network
pass in log on $ext_if inet proto tcp from any to $ext_if port ssh label "ssh"
pass in inet proto icmp all icmp-type echoreq

Yet I can ping through and browse SMB shares while connected to the public AP. Whad I miss?

Since 4.7's big nat re-write, nat rules are no longer separate from other rules, and rules that come after nat see the nat redirect ip, not the original source ip. (hxxp://marc.info/?l=openbsd-misc&m=125181847818600&w=2) (sorry for the hxxp, this is my second post haha)

Have you tried tagging the wifi packets and explicitly dropping those tagged packets on the internal interface?

Hey old thread I know. But it's mine and I wanted to make sure there was a solution posted. I didn't have the need for this right after posting, but did again recently and I followed the advice here to tag the packets. Now that the new 4.7 pf.conf syntax is more well known, I wanted to know if this is still the best method for achieving what I want, which is basically blocking traffic between subnets. In my case, a guest network blocked from seeing the office network.

Code:

pass quick on $pubwi_if all tag PUBLIC
block quick on $int_if tagged PUBLIC

Looking at your initial ruleset from five years ago, I believe I can see the error. If I'm reading it correctly, the rule is limited to inbound traffic on the wrong interface, which is why it failed. The apparent error is highlighted below:

I'm not a proponent of quick rules, unless necessary. I prefer last-matching-rule wins, allowing my rulesets to start with the general cases, then apply specific exceptions in later rules. But this is a personal preference.

Thanks for your reply. I'm curious about your comment though. Don't I want to block inbound traffic on the int_if interface from the pubwi_if? I know that's what I want spoken in plain English, but I realize a rule to accomplish this may be very different. You highlighted the error but didn't offer a corrected version. Are you stating that had there been no such error, I wouldn't need to two lines in my second post?

Every packet will have a from and to address. PF uses these addresses in rule matching for every match/pass/block rule. PF rules can use individual IP addresses, network blocks, lists, tables, or the explicit or implied keyword any, but every rule can be matched or ignored, based on these two addresses in the packet.

However, a packet being forwarded by a router must first arrive on some interface, then be routed via some interface. These two steps may have different rules, via PF direction matching -- the in or out directives. And rules may have the additional restriction: interfacematching -- the on directive.

Let's look at your rule once more. There are two restrictions: on $int_if, which will be on xl0 when the rule is loaded, and in, meaning only inbound packets. Combined, PF will only look at packets arriving on the xl0 interface from somewhere, and then it will compare from and to addresses.

And, that will never be from your public subnet -- those packets arrive at this system on a different interface: xl1. The rule is syntactically correct, but operationally invalid, because no packets will ever match the rule.