Chapter 9. Getting Your Setup Just Right

image with no caption

By now, you have spent significant time designing your network and implementing that design in your PF configuration. Getting your setup just right—removing any remaining setup bugs and inefficiencies—can be quite challenging at times.

This chapter describes some options and methods that will help you get the setup you need. First, we will take a look at global options and some settings that can have a profound influence on how your configuration behaves.

Things You Can Tweak and What You Probably Should Leave Alone

Network configurations are inherently very tweakable. While browsing the pf.conf man page or other reference documentation, it is easy to be overwhelmed by the number of options and settings that you could conceivably adjust in order to get that perfectly optimized setup.

Keep in mind that for PF in general, the defaults are sane for most setups. Some settings and variables lend themselves to tuning; others should come with a big warning that they should be adjusted only in highly unusual circumstances, if at all.

Here, we’ll look at some of the global settings that you should know about, although you won’t need to change them in most circumstances.

These options are written as set option setting and go after any macro definitions in your pf.conf file, but before translation or filtering rules.

Note

If you read the pf.conf man page, you will discover that a few other options are available. However, most of those are not relevant in a network testing and performance tuning context.

Block Policy

The block-policy option determines which feedback, if any, PF will give to hosts that try to create connections that are subsequently blocked. The option has two possible values:

  • drop drops blocked packets with no feedback.

  • return returns with status codes such as Connection refused or similar.

The correct strategy for block policies has been the subject of considerable discussion over the years. The default setting for block-policy is drop, which means that the packet is silently dropped without any feedback. However, silently dropping packets makes it likely that the sender will resend the unacknowledged packets, rather than drop the connection. Thus, the effort is kept up until the relevant timeout counter expires. Unless you can think of a good reason to set the block policy to something else, set it to return:

set block-policy return

This means that the sender’s networking stack will receive an unambiguous signal indicating that the connection was refused.

This setting specifies the global default for your block policy. If necessary, you can still vary the blocking type for specific rules.

For example, you could change the brute-force protection rule set from Chapter 6 to have block-policy set to return, but use block drop quick from <bruteforce> to make the brute forcers waste time if they stick around once they have been added to the <bruteforce> table. You could also specify drop for traffic from nonroutable addresses coming in on your Internet-facing interface.

Skip Interfaces

The skip option lets you exclude specific interfaces from all PF processing. The net effect is like a pass-all rule for the interface, but actually disables all PF processing on the interface. One common example is to disable filtering on the loopback interface group, where filtering in most configurations adds little in terms of security or convenience, as follows:

set skip on lo

In fact, filtering on the loopback interface is almost never useful, and can lead to odd results with a number of common programs and services. The default is that skip is unset, which means that all configured interfaces can take part in PF processing. In addition to making your rule set slightly simpler, setting skip on interfaces where you do not want to perform filtering results in a slight performance gain.

State Policy

The state-policy option specifies how PF matches packets to the state table. There are two possible values:

  • With the default floating state policy, traffic can match state on all interfaces, not just the one where the state was created.

  • With an if-bound policy, traffic will match only on the interface where the state is created; traffic on other interfaces will not match the existing state.

Like block-policy, this option specifies the global state-matching policy.

You can override state policy on a per-rule basis if needed. For example, in a rule set with the default floating state policy, you could have a rule like this:

pass out on egress inet proto tcp to any port $allowed modulate state (if-bound)

With this rule, any return traffic trying to pass back in would need to pass on the same interface where the state was created in order to match the state-table entry.

The situations in which state-policy if-bound is useful are rare enough that you should leave this setting at the default.

State Defaults

The state-defaults option was introduced in OpenBSD 4.5 to enable setting specific state options as the default options to all rules in the rule set unless specifically overridden by other options in individual rules.

Here is a common example:

set state-defaults pflow

This sets up all pass rules in the configuration to generate NetFlow data to be exported via a pflow device.

In some contexts, it makes sense to apply state-tracking options, such as connection limits, as a global state default for the entire rule set. Here is an example:

set state-defaults max 1500, max-src-conn 100, source-track rule

This sets the default maximum number of state entries per rule to 1,500, with a maximum of 100 simultaneous connections from any one host, with separate limits for each rule in the loaded rule set.

Any option that is valid inside parentheses for keep state in an individual rule can also be included in a set state-defaults statement. Setting state defaults in this way is useful if there are state options that are not already system defaults that you want to apply to all rules in your configuration.

Timeouts

The timeout option sets the timeouts and related options for various interactions with the state-table entries. The majority of the available parameters are protocol-specific values stored in seconds and prefixed tcp., udp., icmp., and other.. However, adaptive.start and adaptive.end denote the number of state-table entries.

The following timeout options affect state-table memory use and to some extent lookup speed:

  • The adaptive.start and adaptive.end values set the limits for scaling down timeout values once the number of state entries reach the adaptive.start value. When the number of states reaches adaptive.end, all timeouts are set to 0, essentially expiring all states immediately. The defaults are 6,000 and 12,000 (calculated as 60 percent and 120 percent of the state limit), respectively. These settings are intimately related to the memory pool limit parameters you set via the limit option.

  • The interval value denotes the number of seconds between purges of expired states and fragments. The default is 10 seconds.

  • The frag value denotes the number of seconds a fragment will be kept in an unassembled state before it is discarded. The default is 30 seconds.

  • When set, src.track denotes the number of seconds source-tracking data will be kept after the last state has expired. The default is 0 seconds.

You can inspect the current settings for all timeout parameters with pfctl -s timeouts. For example, the following display shows a system running with default values.

$ sudo pfctl -s timeouts
tcp.first                   120s
tcp.opening                  30s
tcp.established           86400s
tcp.closing                 900s
tcp.finwait                  45s
tcp.closed                   90s
tcp.tsdiff                   30s
udp.first                    60s
udp.single                   30s
udp.multiple                 60s
icmp.first                   20s
icmp.error                   10s
other.first                  60s
other.single                 30s
other.multiple               60s
frag                         30s
interval                     10s
adaptive.start             6000 states
adaptive.end              12000 states
src.track                     0s

These options can be used to tweak your setup for performance. However, changing the protocol-specific settings from the default values creates a significant risk that valid but idle connections might be dropped prematurely or blocked outright.

Limits

The limit option sets the size of the memory pools PF uses for state tables and address tables. These are hard limits, so you may need to increase or tune the values for various reasons. If your network is a busy one with larger numbers than the default values allow for, or if your setup requires large address tables or a large number of tables, then this section will be very relevant for you.

Keep in mind that the total amount of memory available through memory pools is taken from the kernel memory space, and the total available is a function of total available kernel memory. The kernel allocates a fixed amount of memory for its own use at system startup. However, since kernel memory is never swapped, the amount of memory allocated to the kernel can never equal or exceed all physical memory in the system. (If that happened, there would be no space for user mode programs to run.)

The amount of available pool memory depends on which hardware platform you use, as well as on a number of hard-to-predict variables specific to the local system. On the i386 architecture, the maximum kernel memory is in the 768MB to 1GB range, depending on a number of factors, including the number and kind of hardware devices in the system. The amount actually available for allocation to memory pools comes out of this total, again depending on a number of system-specific variables.

To inspect the current limit settings, use pfctl -sm. Typical output looks like this:

$ sudo pfctl -sm
states        hard limit    10000
src-nodes     hard limit    10000
frags         hard limit     5000
tables        hard limit     1000
table-entries hard limit   200000

To change these values, edit pf.conf to include one or more lines with new limit values. For example, you could use the following lines to raise the hard limit for number of states to 25,000 and table entries to 300,000:

set limit states 25000
set limit table-entries 300000

You can also set several limit parameters at the same time in a single line by enclosing them in brackets, like this:

set limit { states 25000, src-nodes 25000, table-entries 300000 }

In the end, you almost certainly should not change the limits at all. If you do, however, it is important to watch your system logs for any indication that your changed limits do not have undesirable side effects or do not fit in available memory. Setting the debug level to a higher value is potentially quite useful for watching the effects of tuning limit parameters.

Debug

The debug option determines what, if any, error information PF will generate at the kern.debug log level. The default value is err, which means that only serious errors will be logged. Since OpenBSD 4.7, the log levels here correspond to the ordinary syslog levels, which range from emerg (panics are logged), alert (correctable but very serious errors logged), crit (critical conditions logged), err (errors are logged), warning (warnings are logged), notice (unusual conditions are logged), info (informational messages are logged), to debug (full debugging information, likely only useful to developers).

In pre-OpenBSD 4.7 versions, PF used its own log-level system, with a default of urgent (equivalent to err in the new system). The other possible settings were none (no messages), misc (reporting slightly more than urgent), and loud (producing status messages for most operations). The pfctl parser still accepts the older-style debug levels for compatibility.

After running one of my gateways at the debug level for a little while, this is what a typical chunk of the /var/log/messages file looked like:

$ tail -f /var/log/messages
Oct  4 11:41:11 skapet /bsd: pf_map_addr: selected address 194.54.107.19
Oct  4 11:41:15 skapet /bsd: pf: loose state match: TCP 194.54.107.19:25
194.54.107.19:25 158.36.191.135:62458 [lo=3178647045 high=3178664421 win=33304
modulator=0 wscale=1] [lo=3111401744 high=3111468309 win=17376 modulator=0
wscale=0] 9:9 R seq=3178647045 (3178647044) ack=3111401744 len=0 ackskew=0
pkts=9:12
Oct  4 11:41:15 skapet /bsd: pf: loose state match: TCP 194.54.107.19:25
194.54.107.19:25 158.36.191.135:62458 [lo=3178647045 high=3178664421 win=33304
modulator=0 wscale=1] [lo=3111401744 high=3111468309 win=17376 modulator=0
wscale=0] 10:10 R seq=3178647045 (3178647044) ack=3111401744 len=0 ackskew=0
pkts=10:12
Oct  4 11:42:24 skapet /bsd: pf_map_addr: selected address 194.54.107.19

As you can see, the debug level gives you a level of detail where PF repeatedly reports the IP address for the interface it is currently handling. In between the selected address messages, PF warns twice for the same packet that the sequence number is at the very edge of the expected range. This level of detail seems almost breathtaking at first glance, but in some circumstances, studying this kind of output is the best way to diagnose a problem and later check to see if your solution actually helped.

Note

This option can be set from the command line with pfctl -x, followed by the debug level you want. The command pfctl -x debug gives you maximum debugging information; pfctl -x none turns off debug messages entirely.

Keep in mind that some debug settings can produce large amounts of log data, and in extreme cases, could impact performance all the way to self-denial-of-service level.

Rule Set Optimization

The ruleset-optimization option enables or sets the mode for the rule set optimizer. The default setting for ruleset-optimization in OpenBSD 4.1 and equivalents is none, which means that no rule set optimization is performed at load time. From OpenBSD 4.2 onward, the default is basic, which means that when the rule set loads, the optimizer does the following things:

  • Removes duplicate rules

  • Removes rules that are subsets of other rules

  • Merges rules into tables if appropriate (typical rule-to-table optimizations are rules that pass, redirect, or block based on identical criteria except source and/or target addresses)

  • Changes the order of rules to improve performance

For example, say you have the macro tcp_services = { ssh, www, https } combined with the rule pass proto tcp from any to self port $tcp_services. Elsewhere in your rule set, you have a different rule that says pass proto tcp from any to self port ssh. The second rule is clearly a subset of the first, and they can be merged into one. Another common combination is having a pass rule like pass proto tcp from any to int_if:network port $tcp_services with otherwise identical pass rules where the target addresses are all in the int_if:network range.

With ruleset-optimization set to profile, the optimizer analyzes the loaded rule set relative to the actual network traffic in order to determine the optimal order of quick rules.

You can also set the value of the optimization option from the command line with pfctl:

$ sudo pfctl -o basic

This example enables the rule set optimization in basic mode.

Since the optimization may remove or reorder rules, the meaning of some statistics—mainly the number of evaluations per rule—may change in ways that may be hard to predict. In most cases, however, the effect is negligible.

Optimization

The optimization option specifies profiles for state-timeout handling. The possible values are normal, high-latency, satellite, aggressive, and conservative. The recommendation is to keep the default normal setting unless you have very specific needs.

The values high-latency and satellite are synonyms, where states expire more slowly in order to compensate for potential high latency.

The aggressive setting expires states early in order to save memory. This could, in principle, increase the risk of dropping idle-but-valid connections if your system is already close to its load and traffic limits, but anecdotal evidence indicates that the aggressive optimization setting rarely, if ever, interferes with valid traffic.

The conservative setting goes to great lengths to preserve states and idle connections, at the cost of some additional memory use.

Fragment Reassembly

The fragment reassembly options tied to scrub were significantly reworked in OpenBSD 4.6, introducing the new set reassemble option to turn reassembly of fragmented packets on or off. If reassemble is set to off, fragmented packets are simply dropped. The default is set reassemble on, which means fragments are reassembled and reassembled packets where the do not fragment bit was set on individual fragments will have the bit cleared.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.75.165