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