Subject: Re: confusing pf behaviour, block drop still returns messages?
To: Daniel Hartmeier <>
From: Nino Dehne <>
List: tech-net
Date: 04/15/2006 00:42:51
On Fri, Apr 14, 2006 at 04:51:12PM +0200, Daniel Hartmeier wrote:
> The problem is that you're blocking these packets on their output path
> instead of on their input path (block in on $int_if).

I see. gw-ext has more than one internal interface. One goes to gw, one is
serving a test network to simulate traffic from outside (testing rate limits
on gw for example). So instead of dealing with RFC1918 stuff and bogons
separately on the internal interfaces, I figured it was best if I handled
them with one table on the external interface where it actually matters and
makes the most sense.

As a side note, this issue of handling packets on their way out instead of
in has bitten me before when playing with synproxy on gw. I found out that
synproxy actually doesn't work when using it in the out direction. Took me
quite some time to figure that out.

> The question is, why would you pass those packets in on $int_if, just to
> later block them out on $ext_if? Blocking them on the input path is
> faster and saves the stack the trouble of trying to forward the packet.

I don't explicitly pass anything. The ruleset is default pass.

The main "policy" of what to block and what to allow, so to speak, is handled
by the host gw so that I don't have to deal with NAT in my ruleset, keeping
it cleaner and more straightforward (at least for me). gw-ext is meant to be
purely a NAT device without a lot of filtering. Apart from some nat/rdr rules
and route-to magic due to more than one WAN interface, the 4 block rules are
all there is.[1]

The idea is basically: "I don't care what goes on internally from the test net
to the LAN. Let gw handle everything like synproxying, blocking services,
handling rate limiting etc. However, bogons and RFC1918 stuff ends here, no
matter what." And since I like small rulesets, the current state seems best.

Anyway, thanks for the lengthy explanation. I understand the issue now. Maybe
I'll sit down and refine my ruleset. Otherwise I'll just deal with not having
my ICMP code of choice. :)

Best regards,


[1] The whole ruleset minus some stuff is as follows. I'm always open to
suggestions. :)

## Macros
ext4_dyn_if = "pppoe0"
ext4_fix_if = "pppoe1"
ext4_grp    = "{" $ext4_dyn_if $ext4_fix_if "}"

ssh4_host = [...]

## Tables
# RFC1918, 0/8, 240/4, etc., bogons
table <evil> persist file "/etc/pf/reserved" file "/etc/pf/bogons"
# hosts allowed for NAT
table <nat4> persist file "/etc/pf/nat4"

## Options
set skip on lo
set block-policy drop

## Scrub
scrub in all
scrub out all random-id max-mss 1452

## Translation
# don't rewrite to/from <evil>, saves a state entry and gets squashed below in filter rules
no nat on $ext4_grp inet from any to <evil>
no rdr on $ext4_grp inet from <evil> to any

nat on $ext4_dyn_if inet from <nat4> to any -> ($ext4_dyn_if)

# second WAN interface, don't blindly pass here since the replies wouldn't go out on the proper interface
# instead, tag incoming packets and handle them with an exception in filter rules
rdr on $ext4_fix_if inet proto tcp from any to ($ext4_fix_if) port 22 tag RDR -> $ssh4_host

rdr pass on $ext4_dyn_if inet proto tcp from any to ($ext4_dyn_if) port 22 -> $ssh4_host

## Filter Rules
# exception for tagged packets coming in on the second WAN interface
# force replies to go out on the proper interface
pass in quick on $ext4_fix_if reply-to $ext4_fix_if tagged RDR keep state

# block to/from <evil> that we didn't rewrite above
block in log quick on $ext4_grp inet from any to <evil>
block in log quick on $ext4_grp inet from <evil> to any
block return-icmp(13) out log quick on $ext4_grp inet from any to <evil>
block return-icmp(13) out log quick on $ext4_grp inet from <evil> to any

# before I got the idea of returning a special ICMP code, the blocking was simply:
block log quick on $ext4_grp inet from any to <evil>
block log quick on $ext4_grp inet from <evil> to any