PF pf firewall basic understanding

Yes, but the IP address is always routed to lo0:
Code:
192.168.101.1      link#2             UHS         lo0
192.168.101.65     link#10            UHS         lo0
192.168.101.129    link#11            UHS         lo0
192.168.101.193    link#12            UHS         lo0

I always imagine myself to be a network packet. The only thing a router (gateway_enable) cares about is the destination IP address. So packet gets sent to 192.168.101.1 and walks in on $ifW. Runs into a firewall rule, pass in on igb1 inet proto icmp all icmp-type echoreq. Good, allowed in. Then looks at routing table where to go next, sees 192.168.101.65 is right here. Hey, service with that IP. And the response is allowed back due to the state.

Ok, that's it, SirDice!! ?

Rule with keep state
pass in on $ifW inet proto icmp icmp-type echoreq
block out on $ifW inet proto icmp from 192.168.101.65
Explanation: The 1st rule allows incoming ICMP echo-request on $ifW. Keep state will be attached by default (Thanks to VladiBG) which causes that the returning traffic will pass as well, meaning that the block (2nd rule) is ignored as the packet has already reached the host. In addition because the IP address is attached to the host and not to the interface all other interface will respond as well.

Rule without keep state
pass in on $ifW inet proto icmp icmp-type echoreq no state
pass out on $ifW inet proto icmp from 192.168.101.65
Explanation: The 1st rule allows incoming ICMP echo-request on $ifW without keep state actived which causes that the returning traffic will not just simply pass. The 2nd rule is needed to that and to demonstration the fact the IP address is attached to the host and not to the interface the 2nd rule allows a different IP address then the one from the interface facing the actual senders network respond. But I got a question, right now gateway_enable="YES" is set. Does this have any impact here, it shouldn't if I understand correctly, right?

Rule with keep state
pass in on $ifW inet proto icmp icmp-type echoreq
block in on $ifW inet proto icmp to 192.168.101.65
Explanation: The 1st rule allows incoming ICMP echo-request on $ifW. Keep state will be attached by default which causes that returning traffic will pass, except for all incoming ICMP traffic with destination 192.168.101.65 which will be blocked.

Rule with keep state using quick
pass in quick on $ifW inet proto icmp icmp-type echoreq
block in on $ifW inet proto icmp to 192.168.101.65
Explanation: The 1st rule allows incoming ICMP echo-request on $ifW. Keep state will be attached by default which causes that returning traffic will pass, BUT the quick keyword causes that the 2nd rule (block statement) is ignored so all address will be reachable by ICMP echo-request on that host.

That's it, isn't it? ?
 
That's it, isn't it?
Yep. You are on the right track. Keep going.

Code:
pass in on $ifW inet proto icmp icmp-type echoreq
block in on $ifW inet proto icmp to 192.168.101.65
Alternatively, you could also do something like this:
Code:
pass in on $ifW inet proto icmp to !192.168.101.65 icmp-type echoreq
This passes everything except 192.168.101.65. Then the block all will apply and block the access to 192.168.101.65 specifically.
 
src / dst proto: ICMPvlan Wvlan Ovlan Svlan MWAN
vlan WPASSBLOCKBLOCKBLOCKPASS
vlan OPASSPASSPASSPASSPASS
vlan SBLOCKBLOCKPASSBLOCKBLOCK
vlan MBLOCKBLOCKBLOCKPASSBLOCK
WANBLOCKBLOCKBLOCKBLOCKPASS

That table is, what I'm looking for. :-/

So my rules are: ?
Code:
#1st rule
block all

#ICMP src vlan W
pass in on $ifW inet proto icmp icmp-type echoreq
block in quick on $ifW inet proto icmp to { $ifO:network $ifS:network $ifM:network }
pass out on $ifWifi inet proto icmp from { $ifW:network $ifO:network} icmp-type echoreq

#ICMP src vlan O
pass in on $ifO inet proto icmp icmp-type echoreq
pass out on $ifO inet proto icmp from $ifO:network icmp-type echoreq

#ICMP src vlan S
pass in on $ifS inet proto icmp to $ifS:network icmp-type echoreq
pass out on $ifS inet proto icmp from { $ifS:network $ifO:network } icmp-type echoreq

#ICMP src vlan M
pass in on $ifM inet proto icmp to $ifM:network icmp-type echoreq
pass out on $ifM inet proto icmp from { $ifM:network $ifO:network } icmp-type echoreq

That should do it. ?

Edit: I added the 1st rule: block all which is always my starting point to avoid any further misunderstanding. ?
 
Last edited:
If your host has 2 NICs, it would be quite possible for it to have 2 IP addresses. Even a consumer-grade router would have 2 IP addresses: an Internet-legal one, and an internal one, like 192.168.1.1.
--
an IP address gets bound to an interface on the host. In case of a router, one IP address (192.168.1.1) can be bound to several interfaces (ethernet plugs) - this is a case of "One or more".
--
If SSH is only listening on 192.168.101.3, port 22, it would not accept connections on 192.168.101.4, port 22.
Yes.

What I wanted to know and already tested has been:

SSH is listing on 192.168.101.1:22 and 192.168.101.65:22 and I use the following rule:

Code:
pass in on $ifW inet proto tcp to port ssh

I'm also able to reach 192.168.101.65:22 despite the fact it's a different subnet but on the same host, which is kind of important to know.?
 
Why are you are expecting $ifO:network to come from $ifW ?
pass out on $ifWifi inet proto icmp from { $ifW:network $ifO:network} icmp-type echoreq
Same for:
pass out on $ifS inet proto icmp from { $ifS:network $ifO:network } icmp-type echoreq
pass out on $ifM inet proto icmp from { $ifM:network $ifO:network } icmp-type echoreq

The "block in quick" rule is better to be above "pass in" this way the pass rule will be matched less number of times and it will optimize the rule processing.
pass in on $ifW inet proto icmp icmp-type echoreq
block in quick on $ifW inet proto icmp to { $ifO:network $ifS:network $ifM:network }
 
Because I want to achieve the following like in my table mentioned:
src: 192.168.101.80 dst: 192.168.101.4 proto ICMP type: echo-request

That's my rule to get there:

Code:
pass out on $ifW inet proto icmp from { ifW:network $ifO:network } icmp-type echoreq

If I remove $ifO:network, I'm not able to reach dst 192.168.101.4 anymore. :-/

Same here:
Code:
pass out on $ifS inet proto icmp from { $ifS:network $ifO:network } icmp-type echoreq
pass out on $ifM inet proto icmp from { $ifM:network $ifO:network } icmp-type echoreq

I tested it all, works all according to my table, so it seems I still lack some understanding here!??

Thanks for the advice on the "block in quick" rule! :)
 
Yes in my rules i have "pass out" right after block in, that's why i've never care about filtering the outgoing traffic.

Config
block in
pass out

block in quick $ifW inet proto icmp to $ifO:network
pass in on $ifW inet proto icmp icmp-type echoreq
pass in on $ifO inet proto icmp icmp-type echoreq
You are right that after routing you need $if0:network to pass out from $ifW. It will look like this

src: 192.168.101.80 ->dst 192.168.101.4

pass in on lagg0.64 192.168.101.80 > 192.168.101.4 ICMP8 (state: all icmp 192.168.101.4 <- 192.168.101.80)
pass out on igb1 192.168.101.80 > 192.168.101.4 ICMP8 (state: all icmp 192.168.101.80 -> 192.168.101.4)
pass in on igb1 192.168.101.4 > 192.168.101.80 ICMP0 (match state 192.168.101.80 -> 192.168.101.4)
pass out on lagg0.64 192.168.101.4 > 192.168.101.80 ICMP0 (match state 192.168.101.4 <- 192.168.101.80)

If you don't have "pass out" rule the outgoing packet from 192.168.101.80 will not match any rule and it will be allowed "if no rule matches the packet, the default action is to pass the packet without creating a state"

block in

block in quick $ifW inet proto icmp to $ifO:network
pass in on $ifW inet proto icmp icmp-type echoreq
pass in on $ifO inet proto icmp icmp-type echoreq

src: 192.168.101.80 ->dst 192.168.101.4

pass in on lagg0.64 192.168.101.80 > 192.168.101.4 ICMP8 (state all icmp 192.168.101.4 <- 192.168.101.80)
pass out all on igb1 no state 192.168.101.80 > 192.168.101.4 ICMP8 (NO STATE)
block in on igb1 192.168.101.4 > 192.168.101.80 ICMP0
 
Oh, yeah I'm so sorry for the misunderstanding! ?

My 1st rule is, like mentioned in my initial posting, to block all, and then allow traffic on step-by-step base to ensure only the "right" traffic is reaching the different vlans, and the WAN interface. So I never changed that throughout my postings, which means you have to put that "block all" in front of all my rule sets. I'll keep this in mind for my next posting to avoid any misunderstanding in the first place. Thank you again for pointing that out to me! :)
 
Since FreeBSD 7.0 the "keep state" is default for every rule. If you want to disable it use "no state" option.
Here's a perfect example. This is really helpful information, yet I have not seen it in ANY of the dozen tutorials I've read so far.
 
It's inside the manual page of pf.conf. Unfortunately most of the admins search for tutorials instead of reading the man pages.
set state-defaults
The state-defaults option sets the state options for states created
from rules without an explicit keep state. For example:

set state-defaults no-sync

Because flags S/SA is applied by default (unless no state is speci-
fied), only the initial SYN packet of a TCP handshake will create a
state for a TCP connection.
 
Back
Top