On Sat, Oct 31, 2009 at 03:00:41PM -0600, ghe wrote:
> I'm fresh off the boat from Debian. I love OpenBSD's attitude, and
> the documentation is even pretty decipherable, but I'm still a
> little confused by pf. I managed to build a trivial filter, but
> there are a few things I don't understand.
> 
> I read somewhere (3 books, google, the website docs, and man) that a
> longer rule takes longer to do its work. 

I can't speak for the books, and I KNOW google is full of lies, but can
you point out specifically what parts of the website docs and man page
talks about this? It should be removed.


> Why? I don't understand how pf works -- I'd expect pfctl, while it's
> munging pf.conf, to make most of the conditions into a big mask that
> could just && with the IP header and make a decision on the result. 

PF is designed to have a considerably more flexible and fine-grained
filtering mechanism, so what goes on is considerably more complicated
than just a bitwise && against the header.


> So specifying the proto and both addresses and flags shouldn't make
> much difference in efficiency. No?

Actually, under many circumstances specifying the proto and addresses
will IMPROVE the performance of the ruleset evaluation even though it
makes the individual rule evaluation slower.

The number of rules evaluated makes a lot more difference than the 
number of parameters evaluated per rule.


> pf.conf consists largely of anchors (to fork on protocol) and sub-
> anchors below them to fork on service -- I'm trying to reduce the
> count of rules seen by a packet to a minimum. But

This approach is almost guaranteed to have the opposite effect.



My number one advice for people who want to optimize their rulesets for
performance is: DON'T.

Seriously.

Writing firewall rules is hard, anything more than a trivial ruleset is
easy to screw up and challenging to test. So the #1 goal for your
ruleset should be readability and maintainability. While you're at it,
put your ruleset under revision control, and figure out a good way to
test any ruleset changes that get made.


That being said, here are some things you can do while you're doing the
above which will help performance.

        - stateful filtering (don't use 'no state')
        - pfctl optimizer (don't use 'set ruleset-optimization none')
        - use tables for lists of addresses
        - use as few rules as possible to get the filtering you want
          while keeping the ruleset readable.


Now, if you really, really need to optimize your ruleset for
performance, it's important to know that PF doesn't simply walk through
the rules as you've specified in your pf.conf when; pfctl optimizes the
rules as they are loaded into the kernel, and PF has a mechanism called
'skip steps' which will skip evaluation of rules if it's known in
advance that the rules cannot possibly match.

The skip steps attributes are the following:

        * interface
        * direction
        * address family
        * protocol
        * source address
        * source port
        * destination address
        * destination port

The best thing that you can actively do for ruleset performance is to
get out of the way of these mechanisms.

- Make use of the rule expansion in pf.conf (rules with items listed in
  braces { }) rather than manually expanding them. The expansion is done
  by pfctl in the skip steps order.
- Group your rules by the skip-steps parameters, in the order above.
  (ie, all rules for em0 together; within that group, all the 'in' rules
  together, within that, all ipv4 rules together...)
- For the above parameters, specify as much detail as possible without
  adding more rules; increased detail will give skip-steps more to work
  with.
- Make sparing use of "barrier" rule options, which prevent the ruleset
  optimizer from reordering the ruleset efficiently.
        * label
        * prob 
        * state limits
                * max_states 
                * max_src_nodes 
                * max_src_stats
                * max_src_conn 
                * max_src_conn_rate 
        * anchor
- This means: Don't break the ruleset into anchors for performance
  reasons unless you _really_ know what you're doing. If you DO, it's
  probably best to break it up in skip-steps order (ie, by interface
  first).




If you're STILL having performance issues after this, there are a few
more things you can do. Remember though:

        "Premature optimization is the root of all evil"
                -- Donald Knuth

- Write your ruleset with 'first match' ordering (quick) on
  all/most rules, and use the 'profile' ruleset-optimization
- Filter only on one side of the firewall, using 'set skip' for the
  interface on the other side.
- Group rules using optimizer "breaks" together; the optimizer
  will only reorder or merge them if they are the same. (Roughly stated,
  these are the 'actions' that a rule can perform).
        * tag
        * keep state
        * queue
        * routing (rt, route-to, dup-to, reply-to)
        * nat/rdr
        * options
        * action (pass/block)
        * logging
        * quick
        * return_ttl
- Think hard about whether some categories of rules are necessary.
  Things like "stealth firewall" filtering against nmap probes, are
  more-or-less useless for security, do you really need these in your
  ruleset?

These shouldn't be done unless you _really_ understand the effects,
because these depend a great deal on your ruleset specifics, your
traffic, and your configuration. At this point you probably also want to
be looking at hardware and network architecture changes to improve
performance as well.



Oh, did I mention that I don't recommend optimizing your ruleset for
performance? 

-Ryan

Reply via email to