Differences

This shows you the differences between two versions of the page.

Link to this comparison view

openbsd_pf_users_guide_en [2017/09/05 12:18] (current)
Line 1: Line 1:
 +====== OpenBSD PF - User's guide ======
  
 +Packet Filter (from here on referred to as PF) is OpenBSD'​s system for filtering TCP/IP traffic and doing Network Address Translation. PF is also capable of normalizing and conditioning TCP/IP traffic, as well as providing bandwidth control and packet prioritization. PF has been a part of the GENERIC kernel since OpenBSD 3.0.
 +
 +PF was originally developed by Daniel Hartmeier and is now maintained and developed by the entire OpenBSD team.
 +
 +This set of documents is intended as a general introduction to the PF system as used in OpenBSD. Even if it covers all of PF's major features, it is only intended to be used as a supplement to the [[http://​man.openbsd.org/​|man pages]], not as a replacement for them.
 +
 +For a complete and in-depth view of what PF can do, please start by reading the [[http://​man.openbsd.org/​pf|pf(4)]] man page.
 +
 +As with the rest of the FAQ, this set of documents is focused on users of the most current release of OpenBSD. As PF is always growing and developing, there are changes and enhancements between the -release version and the version in [[http://​www.openbsd.org/​plus.html|OpenBSD -current]]. There are also differences between the current version and earlier versions. The reader is advised to see the man pages for the version of OpenBSD they are currently working with.
 +
 +====== Basic configuration ======
 +
 +===== Getting Started =====
 +
 +==== Activation ====
 +
 +PF is enabled by default. If you wish to disable it on boot, use the [[http://​man.openbsd.org/​rcctl|rcctl(8)]] tool to do so:
 +<sxh bash>
 +rcctl disable pf
 +</​sxh>​
 +
 +Reboot your system to have it take effect.
 +
 +You can also manually activate and deactivate PF by using the [[http://​man.openbsd.org/​pfctl|pfctl(8)]] program:
 +<sxh bash>
 +pfctl -e
 +pfctl -d
 +</​sxh>​
 +
 +These would enable and disable PF. Note that it doesn'​t actually load a ruleset, however. The ruleset must be loaded separately, either before or after PF is enabled.
 +
 +==== Configuration ====
 +
 +PF reads its configuration rules from [[http://​man.openbsd.org/​pf.conf|pf.conf(5)]] at boot time, as loaded by the [[http://​man.openbsd.org/​rc|rc scripts]]. Note that while [[http://​man.openbsd.org/​pf.conf|pf.conf(5)]] is the default and is loaded by the system rc scripts, it is just a text file loaded and interpreted by [[http://​man.openbsd.org/​pfctl|pfctl(8)]] and inserted into [[http://​man.openbsd.org/​pf|pf(4)]]. For some applications,​ other rulesets may be loaded from other files after boot.
 +
 +The **pf.conf** file has multiple parts:
 +  * **[[openbsd_pf_users_guide_en#​macros|Macros]]:​** User-defined variables that can hold IP addresses, interface names, etc.
 +  * **[[openbsd_pf_users_guide_en#​tables|Tables]]:​** A structure used to hold lists of IP addresses.
 +  * **Options:​** Various options to control how PF works.
 +  * **[[openbsd_pf_users_guide_en#​packet_filtering|Filter Rules]]:** Allows the selective filtering or blocking of packets as they pass through any of the interfaces. Filter rules can be given parameters to specify network address translation and packet redirection.
 +
 +
 +Blank lines are ignored, and lines beginning with # are treated as comments.
 +==== Control ====
 +
 +After boot, PF operation can be managed using the [[http://​man.openbsd.org/​pfctl|pfctl(8)]] program. Some example commands are:
 +^ Command ^ Description ^
 +|pfctl -f  /​etc/​pf.conf ​ |Load the pf.conf file|
 +|pfctl -nf /​etc/​pf.conf ​ |Parse the file, but don't load it|
 +|pfctl -sr               |Show the current ruleset|
 +|pfctl -ss               |Show the current state table|
 +|pfctl -si               |Show filter stats and counters|
 +|pfctl -sa               |Show EVERYTHING it can show|
 +
 +For a complete list of commands, please see the [[http://​man.openbsd.org/​pfctl|man page]].
 +
 +===== Lists and Macros =====
 +
 +==== Lists ====
 +
 +A list allows the specification of multiple similar criteria within a rule. For example, multiple protocols, port numbers, addresses, etc. So, instead of writing one filter rule for each IP address that needs to be blocked, one rule can be written by specifying the IP addresses in a list. Lists are defined by specifying items within { } brackets.
 +
 +When [[http://​man.openbsd.org/​pfctl|pfctl(8)]] encounters a list during loading of the ruleset, it creates multiple rules, one for each item in the list. For example:
 +<sxh pf>
 +block out on fxp0 from { 192.168.0.1,​ 10.5.32.6 } to any
 +</​sxh>​
 +
 +gets expanded to:
 +<sxh pf>
 +block out on fxp0 from 192.168.0.1 to any
 +block out on fxp0 from 10.5.32.6 to any
 +</​sxh>​
 +
 +Multiple lists can be specified within a rule:
 +<sxh pf>
 +match in on fxp0 proto tcp to port { 22 80 } rdr-to 192.168.0.6
 +block out on fxp0 proto { tcp udp } from { 192.168.0.1,​ 10.5.32.6 } \
 +   to any port { ssh telnet }
 +</​sxh>​
 +
 +
 +The commas between list items are optional.
 +
 +Lists can also contain nested lists:
 +<sxh pf>
 +trusted = "{ 192.168.1.2 192.168.5.36 }"
 +pass in inet proto tcp from { 10.10.0.0/​24 $trusted } to port 22
 +</​sxh>​
 +
 +Beware of constructs like the following, dubbed "​negated lists,"​ which are a common mistake:
 +<sxh pf>
 +pass in on fxp0 from { 10.0.0.0/8, !10.1.2.3 }
 +</​sxh>​
 +
 +While the intended meaning is usually to match "any address within 10.0.0.0/8, except for 10.1.2.3,"​ the rule expands to:
 +<sxh pf>
 +pass in on fxp0 from 10.0.0.0/8
 +pass in on fxp0 from !10.1.2.3
 +</​sxh>​
 +
 +which matches any possible address. Instead, a table should be used.
 +
 +==== Macros ====
 +
 +Macros are user-defined variables that can hold IP addresses, port numbers, interface names, etc. Macros can reduce the complexity of a PF ruleset and also make maintaining a ruleset much easier.
 +Macro names must start with a letter and may contain letters, digits and underscores. Macro names cannot be reserved words such as pass, out or queue.
 +<sxh pf>
 +ext_if = "​fxp0"​
 +
 +block in on $ext_if from any to any
 +</​sxh>​
 +
 +This creates a macro named ext_if. When a macro is referred to after it's been created, its name is preceded with a $ character.
 +Macros can also expand to lists, such as:
 +<sxh pf>
 +friends = "{ 192.168.1.1,​ 10.0.2.5, 192.168.43.53 }"
 +</​sxh>​
 +
 +Macros can be defined recursively. Since macros are not expanded within quotes the following syntax must be used:
 +<sxh pf>
 +host1      = "​192.168.1.1"​
 +host2      = "​192.168.1.2"​
 +all_hosts ​ = "​{"​ $host1 $host2 "​}"​
 +</​sxh>​
 +
 +The macro $all_hosts now expands to 192.168.1.1,​ 192.168.1.2.
 +
 +**Note:** Do not use comma between the macros in a list such above all_hosts
 +===== Tables =====
 +
 +==== Introduction ====
 +
 +A table is used to hold a group of IPv4 and/or IPv6 addresses. Lookups against a table are very fast and consume less memory and processor time than [[openbsd_pf_users_guide_en#​lists|lists]]. For this reason, a table is ideal for holding a large group of addresses as the lookup time on a table holding 50,000 addresses is only slightly more than for one holding 50 addresses. Tables can be used in the following ways:
 +
 +  * Source and/or destination address in rules.
 +  * Translation and redirection addresses nat-to and rdr-to rule options, respectively.
 +  * Destination address in route-to, reply-to and dup-to rule options.
 +
 +Tables are created either in [[http://​man.openbsd.org/​pf.conf|pf.conf(5)]] or by using [[http://​man.openbsd.org/​pfctl|pfctl(8)]].
 +
 +==== Configuration ====
 +
 +In **pf.conf**,​ tables are created using the table directive. The following attributes may be specified for each table:
 +
 +  * const - the contents of the table cannot be changed once the table is created. When this attribute is not specified, [[http://​man.openbsd.org/​pfctl|pfctl(8)]] may be used to add or remove addresses from the table at any time, even when running with a [[http://​man.openbsd.org/​securelevel|securelevel(7)]] of two or greater.
 +  * persist - causes the kernel to keep the table in memory even when no rules refer to it. Without this attribute, the kernel will automatically remove the table when the last rule referencing it is flushed.
 +
 +
 +Example:
 +<sxh pf>
 +table <​goodguys>​ { 192.0.2.0/​24 }
 +table <​rfc1918> ​ const { 192.168.0.0/​16,​ 172.16.0.0/​12,​ 10.0.0.0/8 }
 +table <​spammers>​ persist
 +block in on fxp0 from { <​rfc1918>,​ <​spammers>​ } to any
 +pass  in on fxp0 from <​goodguys>​ to any
 +</​sxh>​
 +
 +Addresses can also be specified using the negation (or "​not"​) modifier, such as:
 +<sxh pf>
 +table <​goodguys>​ { 192.0.2.0/​24,​ !192.0.2.5 }
 +</​sxh>​
 +The goodguys table will now match all addresses in the 192.0.2.0/​24 network except for 192.0.2.5.
 +Note that table names are always enclosed in < > angled brackets.
 +
 +Tables can also be populated from text files containing a list of IP addresses and networks:
 +<sxh pf>
 +table <​spammers>​ persist file "/​etc/​spammers"​
 +block in on fxp0 from <​spammers>​ to any
 +</​sxh>​
 +
 +The file /​etc/​spammers would contain a list of IP addresses and/or [[https://​web.archive.org/​web/​20150213012421/​http://​public.swbell.net/​dedicated/​cidr.html|CIDR]] network blocks, one per line.
 +
 +==== Manipulating with pfctl ====
 +
 +Tables can be manipulated on the fly by using [[http://​man.openbsd.org/​pfctl|pfctl(8)]]. For instance, to add entries to the <​spammers>​ table created above:
 +<sxh bash>
 +pfctl -t spammers -T add 203.0.113.0/​24
 +</​sxh>​
 +
 +This will also create the <​spammers>​ table if it doesn'​t already exist. To list the addresses in a table:
 +<sxh bash>
 +pfctl -t spammers -T show
 +</​sxh>​
 +
 +The -v argument can also be used with -T show to display statistics for each table entry. To remove addresses from a table:
 +<sxh bash>
 +pfctl -t spammers -T delete 203.0.113.0/​24
 +</​sxh>​
 +
 +For more information on manipulating tables with pfctl, please read the [[http://​man.openbsd.org/​pfctl|pfctl(8)]] man page.
 +
 +==== Specifying addresses ====
 +
 +In addition to being specified by IP address, hosts may also be specified by their hostname. When the hostname is resolved to an IP address, all resulting IPv4 and IPv6 addresses are placed into the table. IP addresses can also be entered into a table by specifying a valid interface name, interface group, or the self keyword. The table will then contain all IP addresses assigned to that interface or group, or to the machine (including loopback addresses), respectively.
 +
 +One limitation when specifying addresses is that 0.0.0.0/0 and 0/0 will not work in tables. The alternative is to hard code that address or use a [[http://​www.openbsd.org/​faq/​pf/​macros.html#​macros|macro]].
 +
 +==== Address matching ====
 +
 +An address lookup against a table will return the most narrowly matching entry. This allows for the creation of tables such as:
 +<sxh pf>
 +table <​goodguys>​ { 172.16.0.0/​16,​ !172.16.1.0/​24,​ 172.16.1.100 }
 +block in on dc0
 +pass  in on dc0 from <​goodguys>​
 +</​sxh>​
 +
 +Any packet coming in through dc0 will have its source address matched against the table <​goodguys>:​
 +  * 172.16.50.5 - narrowest match is 172.16.0.0/​16;​ packet matches the table and will be passed
 +  * 172.16.1.25 - narrowest match is !172.16.1.0/​24;​ packet matches an entry in the table but that entry is negated (uses the "​!"​ modifier); packet does not match the table and will be blocked
 +  * 172.16.1.100 - exactly matches 172.16.1.100;​ packet matches the table and will be passed
 +  * 10.1.4.55 - does not match the table and will be blocked
 +
 +===== Packet Filtering =====
 +
 +==== Introduction ====
 +
 +Packet filtering is the selective passing or blocking of data packets as they pass through a network interface. The criteria that [[http://​man.openbsd.org/​pf|pf(4)]] uses when inspecting packets are based on the Layer 3 ([[http://​man.openbsd.org/​ip|IPv4]] and [[http://​man.openbsd.org/​ip6|IPv6]]) and Layer 4 ([[http://​man.openbsd.org/​tcp|TCP]],​ [[http://​man.openbsd.org/​udp|UDP]],​ [[http://​man.openbsd.org/​icmp|ICMP]],​ and [[http://​man.openbsd.org/​icmp6|ICMPv6]]) headers. The most often used criteria are source and destination address, source and destination port, and protocol.
 +
 +Filter rules specify the criteria that a packet must match and the resulting action, either block or pass, that is taken when a match is found. Filter rules are evaluated in sequential order, first to last. Unless the packet matches a rule containing the **quick** keyword, the packet will be evaluated against **all** filter rules before the final action is taken. The last rule to match is the "​winner"​ and will dictate what action to take on the packet. There is an implicit **pass all** at the beginning of a filtering ruleset, meaning that if a packet does not match any filter rule the resulting action will be **pass**.
 +
 +==== Rule syntax ====
 +
 +The general, highly simplified syntax for filter rules is:
 +<sxh bash>
 +action [direction] [log] [quick] [on interface] [af] [proto protocol]
 +       [from src_addr [port src_port]] [to dst_addr [port dst_port]]
 +       ​[flags tcp_flags] [state]
 +</​sxh>​
 +
 +  * **action**
 +    * The action to be taken for matching packets, either pass or block. The pass action will pass the packet back to the kernel for further processing while the lock action will react based on the setting of the block-policy option. The default reaction may be overridden by specifying either block drop or block return.
 +  * **direction** ​
 +    * The direction the packet is moving on an interface, either in or out.
 +  * **log**
 +    * Specifies that the packet should be logged via [[http://​man.openbsd.org/​pflogd|pflogd(8)]]. If the rule creates state then only the packet which establishes the state is logged. To log all packets regardless, use log (all).
 +  * **quick**
 +    * If a packet matches a rule specifying quick, then that rule is considered the last matching rule and the specified action is taken.
 +  * **interface**
 +    * The name or group of the network interface the packet is moving through. Interfaces can be added to arbitrary groups using the [[http://​man.openbsd.org/​ifconfig|ifconfig(8)]] command. Several groups are also automatically created by the kernel:
 +      * The egress group, which contains the interface(s) that holds the default route(s).
 +      * Interface family group for cloned interfaces. For example: ppp or carp.
 +    * This would cause the rule to match for any packet traversing any ppp or carp interface, respectively.
 +  * **af**
 +    * The address family of the packet, either inet for IPv4 or inet6 for IPv6. PF is usually able to determine this parameter based on the source and/or destination address(es).
 +  * **protocol**
 +    * The Layer 4 protocol of the packet:
 +      * tcp
 +      * udp
 +      * icmp
 +      * icmp6
 +      * A valid protocol name from /​etc/​protocols
 +      * A protocol number between 0 and 255
 +      * A set of protocols using a list.
 +  * **src_addr, dst_addr**
 +    * The source/​destination address in the IP header. Addresses can be specified as:
 +      * A single IPv4 or IPv6 address.
 +      * A [[https://​web.archive.org/​web/​20150213012421/​http://​public.swbell.net/​dedicated/​cidr.html|CIDR]] network block.
 +      * A fully qualified domain name that will be resolved via DNS when the ruleset is loaded. All resulting IP addresses will be substituted into the rule.
 +      * The name of a network interface or group. Any IP addresses assigned to the interface will be substituted into the rule.
 +      * The name of a network interface followed by /netmask (i.e., /24). Each IP address on the interface is combined with the netmask to form a CIDR network block which is substituted into the rule.
 +      * The name of a network interface or group in parentheses ( ). This tells PF to update the rule if the IP address(es) on the named interface change. This is useful on an interface that gets its IP address via DHCP or dial-up as the ruleset doesn'​t have to be reloaded each time the address changes.
 +      * The name of a network interface followed by any one of these modifiers: ​
 +        * **:​network** - substitutes the CIDR network block (e.g., 192.168.0.0/​24)
 +        * **:​broadcast** - substitutes the network broadcast address (e.g., 192.168.0.255)
 +        * **:peer** - substitutes the peer's IP address on a point-to-point link
 +        * In addition, the :0 modifier can be appended to either an interface name or to any of the above modifiers to indicate that PF should not include aliased IP addresses in the substitution. These modifiers can also be used when the interface is contained in parentheses. Example: fxp0:​network:​0
 +      * A [[openbsd_pf_users_guide_en#​tables|tables]].
 +      * The keyword urpf-failed can be used for the source address to indicate that it should be run through the uRPF check.
 +      * Any of the above but negated using the ! ("​not"​) modifier.
 +      * A set of addresses using a [[openbsd_pf_users_guide_en#​lists|lists]].
 +      * The keyword any meaning all addresses
 +      * The keyword all which is short for from any to any.
 +  * **src_port, dst_port**
 +      * The source/​destination port in the Layer 4 packet header. Ports can be specified as:
 +        * A number between 1 and 65535
 +        * A valid service name from [[http://​man.openbsd.org/​services|/​etc/​services]]
 +        * A set of ports using a [[openbsd_pf_users_guide_en#​lists|lists]]
 +        * A range:
 +          * != (not equal)
 +          * < (less than)
 +          * > (greater than)
 +          * <​nowiki><​=</​nowiki>​ (less than or equal)
 +          * >= (greater than or equal)
 +          * >< (range)
 +          * <> (inverse range)  ​
 +        * The last two are binary operators (they take two arguments) and do not include the arguments in the range.
 +          * : (inclusive range)
 +        * The inclusive range operator is also a binary operator and does include the arguments in the range.  ​
 +  * **tcp_flags**
 +    * Specifies the flags that must be set in the TCP header when using proto tcp. Flags are specified as flags check/mask. For example: flags S/SA - this instructs PF to only look at the S and A (SYN and ACK) flags and to match if only the SYN flag is "​on"​ (and is applied to all TCP rules by default). flags any instructs PF not to check flags.
 +  * **state**
 +    * Specifies whether state information is kept on packets matching this rule.
 +      * **no state** - works with TCP, UDP, and ICMP. PF will not track this connection statefully. For TCP connections,​ **flags any** is usually also required.
 +      * **keep state** - works with TCP, UDP, and ICMP. This option is the default for all filter rules.
 +      * **modulate state** - works only with TCP. PF will generate strong Initial Sequence Numbers (ISNs) for packets matching this rule.
 +      * **synproxy state** - proxies incoming TCP connections to help protect servers from spoofed TCP SYN floods. This option includes the functionality of **keep state** and **modulate state**. ​  
 +
 +=== Default deny ===
 +
 +The recommended practice when setting up a firewall is to take a "​default deny" approach. That is to deny everything, and then selectively allow certain traffic through the firewall. This approach is recommended because it errs on the side of caution and also makes writing a ruleset easier.
 +
 +To create a default deny filter policy, the first filter rule should be:
 +<sxh pf>
 +block all
 +</​sxh>​
 +
 +This will block all traffic on all interfaces in either direction from anywhere to anywhere.
 +
 +=== Passing traffic ===
 +
 +Traffic must now be explicitly passed through the firewall or it will be dropped by the default deny policy. This is where packet criteria such as source/​destination port, source/​destination address and protocol come into play. Whenever traffic is permitted to pass through the firewall, the rule(s) should be written to be as restrictive as possible. This is to ensure that the intended traffic, and only the intended traffic, is permitted to pass.
 +
 +Some examples:
 +<sxh pf>
 +# Pass traffic in on dc0 from the local network, 192.168.0.0/​24,​ to the OpenBSD
 +# machine'​s IP address 192.168.0.1. Also, pass the return traffic out on dc0.
 +pass in  on dc0 from 192.168.0.0/​24 to 192.168.0.1
 +pass out on dc0 from 192.168.0.1 ​   to 192.168.0.0/​24
 +
 +# Pass TCP traffic in on fxp0 to the web server running on the OpenBSD machine.
 +# The interface name, fxp0, is used as the destination address so that packets
 +# will only match this rule if they'​re destined for the OpenBSD machine.
 +pass in on fxp0 proto tcp from any to fxp0 port www
 +</​sxh>​
 +
 +=== The quick keyword ===
 +
 +As indicated earlier, each packet is evaluated against the filter ruleset from top to bottom. By default, the packet is marked for passage, which can be changed by any rule, and could be changed back and forth several times before the end of the filter rules. **The last matching rule "​wins"​**. There is an exception to this: The quick option on a filtering rule has the effect of canceling any further rule processing and causes the specified action to be taken. Let's look at a couple examples:
 +
 +Wrong:
 +<sxh pf>
 +block in on fxp0 proto tcp to port ssh
 +pass  in all
 +</​sxh>​
 +
 +In this case, the block line may be evaluated, but will never have any effect, as it is then followed by a line which will pass everything.
 +
 +Better:
 +<sxh pf>
 +block in quick on fxp0 proto tcp to port ssh
 +pass  in all
 +</​sxh>​
 +
 +These rules are evaluated a little differently. If the block line is matched, due to the quick option, the packet will be blocked, and the rest of the ruleset will be ignored.
 +
 +=== Keeping state ===
 +
 +One of PF's important abilities is "​keeping state" or "​stateful inspection."​ Stateful inspection refers to PF's ability to track the state, or progress, of a network connection. By storing information about each connection in a state table, PF is able to quickly determine if a packet passing through the firewall belongs to an already-established connection. If it does, it is passed through the firewall without going through ruleset evaluation.
 +
 +Keeping state has many advantages, including simpler rulesets and better packet filtering performance. PF is able to match packets moving in either direction to state table entries, meaning that filter rules which pass returning traffic don't need to be written. Since packets matching stateful connections don't go through ruleset evaluation, the time PF spends processing those packets can be greatly lessened.
 +
 +When a rule creates state, the first packet matching the rule creates a "​state"​ between the sender and receiver. Now, not only do packets going from the sender to receiver match the state entry and bypass ruleset evaluation, but so do the reply packets from receiver to sender.
 +
 +All **pass** rules automatically create a state entry when a packet matches the rule. This can be explicitly disabled by using the **no state** option.
 +<sxh pf>
 +pass out on fxp0 proto tcp from any to any
 +</​sxh>​
 +
 +This rule allows any outbound TCP traffic on the fxp0 interface and also permits the reply traffic to pass back through the firewall. Keeping state significantly improves the performance of your firewall as state lookups are dramatically faster than running a packet through the filter rules.
 +
 +The modulate state option works just like **keep state**, except that it only applies to TCP packets. With **modulate state**, the Initial Sequence Number (ISN) of outgoing connections is randomized. This is useful for protecting connections initiated by certain operating systems that do a poor job of choosing ISNs. To allow simpler rulesets, the **modulate state** option can be used in rules that specify protocols other than TCP. In those cases, it is treated as **keep state**.
 +
 +Keep state on outgoing TCP, UDP and ICMP packets and modulate TCP ISNs:
 +<sxh pf>
 +pass out on fxp0 proto { tcp, udp, icmp } from any to any modulate state
 +</​sxh>​
 +
 +Another advantage of keeping state is that corresponding ICMP traffic will be passed through the firewall. For example, if a TCP connection passing through the firewall is being tracked statefully and an ICMP source-quench message referring to this TCP connection arrives, it will be matched to the appropriate state entry and passed through the firewall.
 +
 +The scope of a state entry is controlled globally by the state-policy runtime option, and on a per-rule basis by the **if-bound** and **floating** state option keywords. These per-rule keywords have the same meaning as when used with the **state-policy** option. For example:
 +<sxh pf>
 +pass out on fxp0 proto { tcp, udp, icmp } from any to any modulate state (if-bound)
 +</​sxh>​
 +
 +This rule would dictate that in order for packets to match the state entry, they must be transiting the fxp0 interface.
 +
 +=== Keeping state for UDP ===
 +
 +One will sometimes hear it said that "one cannot create state with UDP, as UDP is a stateless protocol!"​ While it is true that a UDP communication session does not have any concept of state (an explicit start and stop of communications),​ this does not have any impact on PF's ability to create state for a UDP session. In the case of protocols without "​start"​ and "​end"​ packets, PF simply keeps track of how long it has been since a matching packet has gone through. If the timeout is reached, the state is cleared. The timeout values can be set in the options section of the pf.conf file.
 +
 +=== Stateful tracking options ===
 +
 +Filter rules that create state entries can specify various options to control the behavior of the resulting state entry. The following options are available:
 +  * **max number**
 +    * Limit the maximum number of state entries the rule can create to number. If the maximum is reached, packets that would normally create state fail to match this rule until the number of existing states decreases below the limit.
 +  * **no state**
 +    * Prevents the rule from automatically creating a state entry.
 +  * **source-track**
 +    * This option enables the tracking of number of states created per source IP address. This option has two formats:
 +      * **source-track rule** - The maximum number of states created by this rule is limited by the rule's max-src-nodes and max-src-states options. Only state entries created by this particular rule count toward the rule's limits.
 +      * **source-track global** - The number of states created by all rules that use this option is limited. Each rule can specify different max-src-nodes and max-src-states options, however state entries created by any participating rule count towards each individual rule's limits.
 +    * The total number of source IP addresses tracked globally can be controlled via the src-nodes runtime option.
 +  * **max-src-nodes number**
 +    * When the source-track option is used, max-src-nodes will limit the number of source IP addresses that can simultaneously create state. This option can only be used with source-track rule.
 +  * **max-src-states number**
 +    * When the source-track option is used, max-src-states will limit the number of simultaneous state entries that can be created per source IP address. The scope of this limit (i.e., states created by this rule only or states created by all rules that use source-track) is dependent on the source-track option specified. ​  
 +
 +Options are specified inside parenthesis and immediately after one of the state keywords (keep state, modulate state, or synproxy state). Multiple options are separated by commas. In OpenBSD 4.1 and later, the keep state option became the implicit default for all filter rules. Despite this, when specifying stateful options, one of the state keywords must still be used in front of the options.
 +
 +An example rule:
 +<sxh pf>
 +pass in on $ext_if proto tcp to $web_server port www keep state   \
 +                  (max 200, source-track rule, max-src-nodes 100, \
 +                   ​max-src-states 3)
 +</​sxh>​
 +
 +The rule above defines the following behavior:
 +  * Limit the absolute maximum number of states that this rule can create to 200.
 +  * Enable source tracking: limit state creation based on states created by this rule only.
 +  * Limit the maximum number of nodes that can simultaneously create state to 100.
 +  * Limit the maximum number of simultaneous states per source IP to 3.
 +
 +A separate set of restrictions can be placed on stateful TCP connections that have completed the 3-way handshake.
 +  * **max-src-conn number**
 +    * Limit the maximum number of simultaneous TCP connections which have completed the 3-way handshake that a single host can make.
 +  * **max-src-conn-rate number / interval**
 +    * Limit the rate of new connections to a certain amount per time interval.
 +
 +
 +Both of these options automatically invoke the source-track rule option and are incompatible with source-track global.
 +
 +Since these limits are only being placed on TCP connections that have completed the 3-way handshake, more aggressive actions can be taken on offending IP addresses.
 +
 +  * **overload <​table>​**
 +    * Put an offending host's IP address into the named table.
 +  * **flush [global]**
 +    * Kill any other states that match this rule and that were created by this source IP. When global is specified, kill all states matching this source IP, regardless of which rule created the state.
 +
 +An example:
 +<sxh pf>
 +table <​abusive_hosts>​ persist
 +block in quick from <​abusive_hosts>​
 +
 +pass in on $ext_if proto tcp to $web_server port www flags S/SA keep state \
 +                                (max-src-conn 100, max-src-conn-rate 15/5, \
 +                                 ​overload <​abusive_hosts>​ flush)
 +</​sxh>​
 +
 +This does the following:
 +
 +  * Limits the maximum number of connections per source to 100.
 +  * Rate limits the number of connections to 15 in a 5 second span.
 +  * Puts the IP address of any host that breaks these limits into the <​abusive_hosts>​ table.
 +  * For any offending IP addresses, flush any states created by this rule.
 +
 +=== TCP flags ===
 +
 +Matching TCP packets based on flags is most often used to filter TCP packets that are attempting to open a new connection. The TCP flags and their meanings are listed here:
 +  * **F** : FIN - Finish; end of session
 +  * **S** : SYN - Synchronize;​ indicates request to start session
 +  * **R** : RST - Reset; drop a connection
 +  * **P** : PUSH - Push; packet is sent immediately
 +  * **A** : ACK - Acknowledgement
 +  * **U** : URG - Urgent
 +  * **E** : ECE - Explicit Congestion Notification Echo
 +  * **W** : CWR - Congestion Window Reduced
 +
 +To have PF inspect the TCP flags during evaluation of a rule, the flags keyword is used with the following syntax:
 +<sxh pf>
 +flags check/mask
 +flags any
 +</​sxh>​
 +
 +The mask part tells PF to only inspect the specified flags and the check part specifies which flag(s) must be "​on"​ in the header for a match to occur. Using the any keyword allows any combination of flags to be set in the header.
 +<sxh pf>
 +pass in on fxp0 proto tcp from any to any port ssh flags S/SA
 +pass in on fxp0 proto tcp from any to any port ssh
 +</​sxh>​
 +
 +As flags S/SA is set by default, the above rules are equivalent, Each of these rules passes TCP traffic with the SYN flag set while only looking at the SYN and ACK flags. A packet with the SYN and ECE flags would match the above rules, while a packet with SYN and ACK or just ACK would not.
 +
 +The default flags can be overridden by using the flags option as outlined above.
 +
 +One should be careful with using flags -- understand what you are doing and why, and be careful with the advice people give as a lot of it is bad. Some people have suggested creating state "only if the SYN flag is set and no others."​ Such a rule would end with:
 +<sxh pf>
 +[...] flags S/​FSRPAUEW ​ bad idea!!
 +</​sxh>​
 +
 +The theory is to create state only on the start of the TCP session, and the session should start with a SYN flag, and no others. The problem is some sites are starting to use the ECN flag and any site using ECN that tries to connect to you would be rejected by such a rule. A much better guideline is to not specify any flags at all and let PF apply the default flags to your rules. If you truly need to specify flags yourself, then this combination should be safe:
 +<sxh pf>
 +[...] flags S/SAFR
 +</​sxh>​
 +
 +While this is practical and safe, it is also unnecessary to check the FIN and RST flags if traffic is also being scrubbed. The scrubbing process will cause PF to drop any incoming packets with illegal TCP flag combinations (such as SYN and RST) and to normalize potentially ambiguous combinations (such as SYN and FIN).
 +
 +=== TCP SYN proxy ===
 +
 +Normally when a client initiates a TCP connection to a server, PF will pass the [[http://​www.inetdaemon.com/​tutorials/​internet/​tcp/​3-way_handshake.shtml|handshake]] packets between the two endpoints as they arrive. PF has the ability, however, to proxy the handshake. With the handshake proxied, PF itself will complete the handshake with the client, initiate a handshake with the server, and then pass packets between the two. In the case of a TCP SYN flood attack, the attacker never completes the three-way handshake, so the attacker'​s packets never reach the protected server, but legitimate clients will complete the handshake and get passed. This minimizes the impact of spoofed TCP SYN floods on the protected service, handling it in PF instead. Routine use of this option is not recommended,​ however, as it breaks expected TCP protocol behavior when the server can't process the request and when load balancers are involved.
 +
 +The TCP SYN proxy is enabled using the synproxy state keywords in filter rules. For example:
 +<sxh pf>
 +pass in on $ext_if proto tcp to $web_server port www synproxy state
 +</​sxh>​
 +
 +Here, connections to the web server will be TCP proxied by PF.
 +
 +Because of the way **synproxy state** works, it also includes the same functionality as **keep state** and **modulate state**.
 +
 +The SYN proxy will not work if PF is running on a [[http://​man.openbsd.org/​bridge|bridge(4)]].
 +
 +=== Blocking spoofed packets ===
 +
 +Address "​spoofing"​ is when a malicious user fakes the source IP address in packets they transmit in order to either hide their real address or to impersonate another node on the network. Once the user has spoofed their address, they can launch a network attack without revealing the true source of the attack or attempt to gain access to network services that are restricted to certain IP addresses.
 +
 +PF offers some protection against address spoofing through the antispoof keyword:
 +<sxh pf>
 +antispoof [log] [quick] for interface [af]
 +</​sxh>​
 +
 +  * **log**
 +    * Specifies that matching packets should be logged via [[http://​man.openbsd.org/​pflogd|pflogd(8)]].
 +  * **quick**
 +    * If a packet matches this rule then it will be considered the "​winning"​ rule and ruleset evaluation will stop.
 +  * **interface**
 +    * The network interface to activate spoofing protection on. This can also be a [[openbsd_pf_users_guide_en#​lists|lists]] of interfaces.
 +  * **af**
 +    * The address family to activate spoofing protection for, either inet for IPv4 or inet6 for IPv6.
 +
 +Example:
 +<sxh pf>
 +antispoof for fxp0 inet
 +</​sxh>​
 +
 +When a ruleset is loaded, any occurrences of the antispoof keyword are expanded into two filter rules. Assuming that interface fxp0 has IP address 10.0.0.1 and a subnet mask of 255.255.255.0 (i.e., a /24), the above antispoof rule would expand to:
 +<sxh pf>
 +block in on ! fxp0 inet from 10.0.0.0/24 to any
 +block in inet from 10.0.0.1 to any
 +</​sxh>​
 +
 +These rules accomplish two things:
 +
 +  * Blocks all traffic coming from the 10.0.0.0/24 network that does not pass in through fxp0. Since the 10.0.0.0/24 network is on the fxp0 interface, packets with a source address in that network block should never be seen coming in on any other interface.
 +  * Blocks all incoming traffic from 10.0.0.1, the IP address on fxp0. The host machine should never send packets to itself through an external interface, so any incoming packets with a source address belonging to the machine can be considered malicious.
 +
 +**NOTE:** The filter rules that the **antispoof** rule expands to will also block packets sent over the loopback interface to local addresses. It's best practice to skip filtering on loopback interfaces anyways, but this becomes a necessity when using antispoof rules:
 +
 +<sxh pf>
 +set skip on lo0
 +antispoof for fxp0 inet
 +</​sxh>​
 +
 +Usage of antispoof should be restricted to interfaces that have been assigned an IP address. Using **antispoof** on an interface without an IP address will result in filter rules such as:
 +<sxh pf>
 +block drop in on ! fxp0 inet all
 +block drop in net all
 +</​sxh>​
 +
 +With these rules, there is a risk of blocking **all** inbound traffic on **all** interfaces.
 +
 +=== Unicast Reverse Path Forwarding ===
 +
 +PF offers a Unicast Reverse Path Forwarding (uRPF) feature. When a packet is run through the uRPF check, the source IP address of the packet is looked up in the routing table. If the outbound interface found in the routing table entry is the same as the interface that the packet just came in on, then the uRPF check passes. If the interfaces don't match, then it's possible the packet has had its source address spoofed.
 +
 +The uRPF check can be performed on packets by using the urpf-failed keyword in filter rules:
 +<sxh pf>
 +block in quick from urpf-failed label uRPF
 +</​sxh>​
 +
 +Note that the uRPF check only makes sense in an environment where routing is symmetric.
 +
 +uRPF provides the same functionality as [[openbsd_pf_users_guide_en#​blocking_spoofed_packets|antispoof]] rules.
 +
 +=== Passive operating system fingerprinting ===
 +
 +Passive OS fingerprinting (OSFP) is a method for passively detecting the operating system of a remote host based on certain characteristics within that host's TCP SYN packets. This information can then be used as criteria within filter rules.
 +
 +PF determines the remote operating system by comparing characteristics of a TCP SYN packet against the fingerprints file, which by default is [[http://​man.openbsd.org/​pf.os|pf.os(5)]]. Once PF is enabled, the current fingerprint list can be viewed with this command:
 +<sxh pf>
 +pfctl -s osfp
 +</​sxh>​
 +
 +Within a filter rule, a fingerprint may be specified by OS class, version, or subtype/​patch level. Each of these items is listed in the output of the pfctl command shown above. To specify a fingerprint in a filter rule, the os keyword is used:
 +<sxh pf>
 +pass  in on $ext_if proto tcp from any os OpenBSD keep state
 +block in on $ext_if proto tcp from any os "​Windows 2000"
 +block in on $ext_if proto tcp from any os "Linux 2.4 ts"
 +block in on $ext_if proto tcp from any os unknown
 +</​sxh>​
 +
 +The special operating system class unknown allows for matching packets when the OS fingerprint is not known.
 +
 +**TAKE NOTE** of the following:
 +
 +  * Operating system fingerprints are occasionally wrong due to spoofed and/or crafted packets that are made to look like they originated from a specific operating system.
 +  * Certain revisions or patchlevels of an operating system may change the stack'​s behavior and cause it to either not match what's in the fingerprints file or to match another entry altogether.
 +  * OSFP only works on the TCP SYN packet; it will not work on other protocols or on already established connections.
 +
 +=== IP options ===
 +
 +By default, PF blocks packets with IP options set. This can make the job more difficult for OS fingerprinting utilities like nmap. If you have an application that requires the passing of these packets, such as multicast or IGMP, you can use the allow-opts directive:
 +<sxh pf>
 +pass in quick on fxp0 all allow-opts
 +</​sxh>​
 +
 +=== Filtering ruleset example ===
 +
 +Below is an example of a filtering ruleset. The machine running PF is acting as a firewall between a small, internal network and the Internet. Only the filter rules are shown; queueing, nat, rdr, etc, have been left out of this example.
 +
 +<sxh pf>
 +ext_if ​ = "​fxp0"​
 +int_if ​ = "​dc0"​
 +lan_net = "​192.168.0.0/​24"​
 +
 +# table containing all IP addresses assigned to the firewall
 +table <​firewall>​ const { self }
 +
 +# don't filter on the loopback interface
 +set skip on lo0
 +
 +# scrub incoming packets
 +# The scrubbing process will cause PF to drop any incoming packets with illegal ​
 +# TCP flag combinations (such as SYN and RST) and to normalize potentially ​
 +# ambiguous combinations (such as SYN and FIN).
 +match in all scrub (no-df)
 +
 +# set up a default deny policy
 +block all
 +
 +# activate spoofing protection for all interfaces
 +block in quick from urpf-failed
 +
 +# only allow ssh connections from the local network if it's from the
 +# trusted computer, 192.168.0.15. use "block return"​ so that a TCP RST is
 +# sent to close blocked connections right away. use "​quick"​ so that this
 +# rule is not overridden by the "​pass"​ rules below.
 +block return in quick on $int_if proto tcp from ! 192.168.0.15 to $int_if port ssh
 +
 +# pass all traffic to and from the local network.
 +# these rules will create state entries due to the default
 +# "keep state" option which will automatically be applied.
 +pass in  on $int_if from $lan_net
 +pass out on $int_if to   ​$lan_net
 +
 +# pass tcp, udp, and icmp out on the external (Internet) interface.
 +# tcp connections will be modulated, udp/icmp will be tracked statefully.
 +pass out on $ext_if proto { tcp udp icmp } all modulate state
 +
 +# allow ssh connections in on the external interface as long as they'​re
 +# NOT destined for the firewall (i.e., they'​re destined for a machine on
 +# the local network). log the initial packet so that we can later tell
 +# who is trying to connect.
 +# Uncomment last part to use the tcp syn proxy to proxy the connection.
 +pass in log on $ext_if proto tcp to ! <​firewall>​ port ssh # synproxy state
 +</​sxh>​
 +
 +===== Network Address Translation =====
 +
 +==== Introduction ====
 +
 +Network Address Translation (NAT) is a way to map an entire network (or networks) to a single IP address. NAT is necessary when the number of IP addresses assigned to you by your Internet Service Provider is less than the total number of computers that you wish to provide internet access for. NAT is described in [[https://​tools.ietf.org/​html/​rfc1631|RFC 1631]].
 +
 +NAT allows you to take advantage of the reserved address blocks described in [[http://​tools.ietf.org/​html/​rfc1918|RFC 1918]]. Typically, your internal network will be setup to use one or more of these network blocks. They are:
 +<sxh bash>
 +10.0.0.0/​8 ​      ​(10.0.0.0 - 10.255.255.255)
 +172.16.0.0/​12 ​   (172.16.0.0 - 172.31.255.255)
 +192.168.0.0/​16 ​  ​(192.168.0.0 - 192.168.255.255)
 +</​sxh>​
 +
 +An OpenBSD system doing NAT will have at least two network interfaces, one to the internet, the other to your internal network. NAT will be translating requests from the internal network so they appear to all be coming from your OpenBSD NAT system.
 +
 +==== How NAT works ====
 +
 +When a client on the internal network contacts a machine on the internet, it sends out IP packets destined for that machine. These packets contain all the addressing information necessary to get them to their destination. NAT is concerned with these pieces of information:​
 +  * Source IP address (for example, 192.168.1.35)
 +  * Source TCP or UDP port (for example, 2132)
 +
 +When the packets pass through the NAT gateway, they will be modified so that they appear to be coming from the NAT gateway itself. The NAT gateway will record the changes it makes in its state table so that it can **a)** reverse the changes on return packets and **b)** ensure that return packets are passed through the firewall and are not blocked. For example, the following changes might be made:
 +  * Source IP: replaced with the external address of the gateway (for example, 198.51.100.1)
 +  * Source port: replaced with a randomly chosen, unused port on the gateway (for example, 53136)
 +
 +Neither the internal machine nor the Internet host is aware of these translation steps. To the internal machine, the NAT system is simply an internet gateway. To the internet host, the packets appear to come directly from the NAT system; it is completely unaware that the internal workstation even exists.
 +
 +When the internet host replies to the internal machine'​s packets, they will be addressed to the NAT gateway'​s external IP (198.51.100.1) at the translation port (53136). The NAT gateway will then search the state table to determine if the reply packets match an already established connection. A unique match will be found based on the IP/port combination which tells PF the packets belong to a connection initiated by the internal machine 192.168.1.35. PF will then make the opposite changes it made to the outgoing packets and forward the reply packets on to the internal machine.
 +
 +Translation of ICMP packets happens in a similar fashion but without the source port modification.
 +
 +==== IP forwarding ====
 +
 +Since NAT is almost always used on routers and network gateways, it will probably be necessary to enable IP forwarding so that packets can travel between network interfaces on the OpenBSD machine. IP forwarding is enabled using the [[http://​man.openbsd.org/​sysctl.3|sysctl(3)]] mechanism:
 +<sxh bash>
 +sysctl net.inet.ip.forwarding=1
 +echo  '​net.inet.ip.forwarding=1'​ >> /​etc/​sysctl.conf
 +</​sxh>​
 +
 +Or, for IPv6:
 +<sxh bash>
 +sysctl net.inet6.ip6.forwarding=1
 +echo  '​net.inet6.ip6.forwarding=1'​ >> /​etc/​sysctl.conf
 +</​sxh>​
 +
 +==== Configuring NAT ====
 +
 +NAT is specified as an optional **nat-to** parameter to an outbound **pass** rule. Often, rather than being set directly on the **pass** rule, a **match** rule is used. When a packet is selected by a **match** rule, parameters (e.g. **nat-to**) in that rule are remembered and are applied to the packet when a **pass** rule matching the packet is reached. This permits a whole class of packets to be handled by a single **match** rule and then specific decisions on whether to allow the traffic can be made with **block** and **pass** rules.
 +
 +The general format in **pf.conf** looks something like this:
 +<sxh bash>
 +match out on interface [af] \
 +   from src_addr to dst_addr \
 +   ​nat-to ext_addr [pool_type] [static-port]
 +[...]
 +pass out [log] on interface [af] [proto protocol] \
 +   from ext_addr [port src_port] \
 +   to dst_addr [port dst_port]
 +</​sxh>​
 +
 +  * **match**
 +    * When a packet traverses the ruleset and matches a match rule, any optional parameters specified in that rule are remembered for future use (made "​sticky"​).
 +  * **pass**
 +    * This rule allows the packet to be transmitted. If the packet was previously matched by a match rule where parameters were specified, they will be applied to this packet. pass rules may have their own parameters; these take priority over parameters specified in a match rule.
 +  * **out**
 +    * Specifies the direction of packet flow where this rule applies. nat-to may only be specified for outbound packets.
 +  * **log**
 +    * Log matching packets via [[http://​man.openbsd.org/​pflogd|pflogd(8)]]. Normally, only the first packet that matches will be logged. To log all matching packets, use log (all).
 +  * **interface**
 +    * The name or group of the network interface to transmit packets on.
 +  * **af**
 +    * The address family, either inet for IPv4 or inet6 for IPv6. PF is usually able to determine this parameter based on the source/​destination address(es).
 +  * **protocol**
 +    * The protocol (e.g. tcp, udp, icmp) of packets to allow. If src_port or dst_port is specified, the protocol must also be given.
 +  * **src_addr**
 +    * The source (internal) address of packets that will be translated. The source address can be specified as:
 +      * A single IPv4 or IPv6 address.
 +      * A [[https://​web.archive.org/​web/​20150213012421/​http://​public.swbell.net/​dedicated/​cidr.html|CIDR]] network block.
 +      * A fully qualified domain name that will be resolved via DNS when the ruleset is loaded. All resulting IP addresses will be substituted into the rule.
 +      * The name or group of a network interface. Any IPv4 and IPv6 addresses assigned to the interface will be substituted into the rule at load time.
 +      * The name of a network interface followed by /​**netmask** (e.g. /24). Each IP address on the interface is combined with the netmask to form a CIDR network block which is substituted into the rule.
 +      * The name or group of a network interface followed by any one of these modifiers:
 +        * :​**network** - substitutes the CIDR network block (e.g., 192.168.0.0/​24)
 +        * :​**broadcast** - substitutes the network broadcast address (e.g., 192.168.0.255)
 +        * :**peer** - substitutes the peer's IP address on a point-to-point link
 +      * In addition, the :0 modifier can be appended to either an interface name/group or to any of the above modifiers to indicate that PF should not include aliased IP addresses in the substitution. These modifiers can also be used when the interface is contained in parentheses. Example: fxp0:​network:​0
 +      * A [[openbsd_pf_users_guide_en#​tables|table]].
 +      * Any of the above but negated using the ! ("​not"​) modifier.
 +      * A set of addresses using a [[openbsd_pf_users_guide_en#​lists|list]].
 +      * The keyword any meaning all addresses ​
 +  * **src_port**
 +    * The source port in the Layer 4 packet header. Ports can be specified as:
 +      * A number between 1 and 65535
 +      * A valid service name from [[http://​man.openbsd.org/​services|services(5)]]
 +      * A set of ports using a [[openbsd_pf_users_guide_en#​lists|list]]
 +      * A range:
 +        * != (not equal)
 +        * < (less than)
 +        * > (greater than)
 +        * <​nowiki><​=</​nowiki>​ (less than or equal)
 +        * <​nowiki>>​=</​nowiki>​ (greater than or equal)
 +        * >< (range)
 +        * <> (inverse range)
 +      * The last two are binary operators (they take two arguments) and do not include the arguments in the range.
 +        * : (inclusive range)
 +       * The inclusive range operator is also a binary operator and does include the arguments in the range.
 +       * The port option is not usually used in nat rules because the goal is usually to NAT all traffic regardless of the port(s) being used.
 +  * **dst_addr** ​
 +    * The destination address of packets to be translated. The destination address is specified in the same way as the source address.
 +  * **dst_port**
 +    * The destination port in the Layer 4 packet header. This port is specified in the same way as the source port.
 +  * **ext_addr**
 +    * The external (translation) address on the NAT gateway that packets will be translated to. The external address can be specified as:
 +      * A single IPv4 or IPv6 address.
 +      * A [[https://​web.archive.org/​web/​20150611113606/​http://​public.swbell.net/​dedicated/​cidr.html|CIDR]] network block.
 +      * A fully qualified domain name that will be resolved via DNS when the ruleset is loaded.
 +      * The name or group of the external network interface. Any IP addresses assigned to the interface will be substituted into the rule at load time.
 +      * The name or group of the external network interface in parentheses ( ). This tells PF to update the rule if the IP address(es) on the named interface changes. This is highly useful when the external interface gets its IP address via DHCP or dial-up, as the ruleset doesn'​t have to be reloaded each time the address changes.
 +      * The name or group of a network interface followed by either one of these modifiers: ​
 +        * :​**network** - substitutes the CIDR network block (e.g., 192.168.0.0/​24)
 +        * :**peer** - substitutes the peer's IP address on a point-to-point link
 +      * In addition, the :0 modifier can be appended to either an interface name/group or to any of the above modifiers to indicate that PF should not include aliased IP addresses in the substitution. These modifiers can also be used when the interface is contained in parentheses. Example: fxp0:​network:​0
 +      * A set of addresses using a [[openbsd_pf_users_guide_en#​lists|list]].
 +  * **pool_type**
 +    * Specifies the type of [[http://​www.openbsd.org/​faq/​pf/​pools.html|address pool]] to use for translation. ​
 +  * **static-port**
 +    * Tells PF not to translate the source port in TCP and UDP packets.
 +
 +This would lead to a most basic form of these lines similar to this:
 +<sxh pf>
 +match out on tl0 from 192.168.1.0/​24 to any nat-to 198.51.100.1
 +pass on tl0 from 192.168.1.0/​24 to any
 +</​sxh>​
 +Or you may simply use:
 +<sxh pf>
 +pass out on tl0 from 192.168.1.0/​24 to any nat-to 198.51.100.1
 +</​sxh>​
 +
 +This rule says to perform NAT on the tl0 interface for any packets coming from 192.168.1.0/​24 and to replace the source IP address with 198.51.100.1.
 +
 +While the above rule is correct, it is not recommended form. Maintenance could be difficult as any change of the external or internal network numbers would require the line be changed. Compare instead with this easier to maintain line (tl0 is external, dc0 internal):
 +<sxh pf>
 +pass out on tl0 inet from dc0:network to any nat-to tl0
 +</​sxh>​
 +
 +The advantage should be fairly clear: you can change the IP addresses of either interface without changing this rule. Note that inet should be specified in this case to ensure that only IPv4 addresses are used, avoiding unexpected surprises.
 +
 +When specifying an interface name for the translation address as above, the IP address is determined at pf.conf load time, not on the fly. If you are using DHCP to configure your external interface, this can be a problem. If your assigned IP address changes, NAT will continue translating outgoing packets using the old IP address. This will cause outgoing connections to stop functioning. To get around this, you can tell PF to automatically update the translation address by putting parentheses around the interface name:
 +<sxh pf>
 +pass out on tl0 inet from dc0:network to any nat-to (tl0)
 +</​sxh>​
 +
 +This method works for translation to both IPv4 and IPv6 addresses.
 +
 +=== Bidirectional mapping (1:1 mapping) ===
 +
 +A bidirectional mapping can be established by using the **binat-to** parameter. A **binat-to** rule establishes a one to one mapping between an internal IP address and an external address. This can be useful, for example, to provide a web server on the internal network with its own external IP address. Connections from the Internet to the external address will be translated to the internal address and connections from the web server (such as DNS requests) will be translated to the external address. TCP and UDP ports are never modified with **binat-to** rules as they are with **nat** rules.
 +
 +Example:
 +<sxh pf>
 +web_serv_int = "​192.168.1.100"​
 +web_serv_ext = "​198.51.100.6"​
 +
 +pass on tl0 from $web_serv_int to any binat-to $web_serv_ext
 +</​sxh>​
 +
 +=== Translation rule exceptions ===
 +
 +If you need to translate most traffic, but provide exceptions in some cases, make sure that the exceptions are handled by a filter rule which does not include the nat-to parameter. For example, if the NAT example above was modified to look like this:
 +<sxh pf>
 +pass  out on tl0 from 192.168.1.0/​24 to any nat-to 198.51.100.79
 +pass  out on tl0 from 192.168.1.208 ​ to any
 +</​sxh>​
 +
 +Then the entire 192.168.1.0/​24 network would have its packets translated to the external address 198.51.100.79 except for 192.168.1.208.
 +
 +=== Checking NAT status ===
 +
 +To view the active NAT translations,​ [[http://​man.openbsd.org/​pfctl|pfctl(8)]] is used with the **-s state** option. This option will list all the current NAT sessions:
 +<sxh pf>
 +pfctl -s state
 +fxp0 tcp 192.168.1.35:​2132 (198.51.100.1:​53136) -> 198.51.100.10:​22 TIME_WAIT:​TIME_WAIT
 +fxp0 udp 192.168.1.35:​2491 (198.51.100.1:​60527) -> 198.51.100.33:​53 ​  ​MULTIPLE:​SINGLE
 +</​sxh>​
 +
 +Explanations (first line only):
 +  * **fxp0**
 +    * Indicates the interface that the state is bound to. The word self will appear if the state is floating.
 +  * **TCP**
 +    * The protocol being used by the connection.
 +  * **192.168.1.35:​2132**
 +    * The IP address (192.168.1.35) of the machine on the internal network. The source port (2132) is shown after the address. This is also the address that is replaced in the IP header.
 +  * **198.51.100.1:​53136**
 +    * The IP address (198.51.100.1) and port (53136) on the gateway that packets are being translated to.
 +  * **198.51.100.10:​22**
 +    * The IP address (198.51.100.10) and the port (22) that the internal machine is connecting to.
 +  * **TIME_WAIT:​TIME_WAIT**
 +    * This indicates what state PF believes the TCP connection to be in.
 +
 +===== Traffic Redirection (Port Forwarding) =====
 +
 +==== Introduction ====
 +
 +When you have NAT running in your office, you have the entire internet available to all your machines. What if you have a machine behind the NAT gateway that needs to be accessed from outside? This is where redirection comes in. Redirection allows incoming traffic to be sent to a machine behind the NAT gateway.
 +
 +Let's look at an example:
 +<sxh pf>
 +pass in on tl0 proto tcp from any to any port 80 rdr-to 192.168.1.20
 +</​sxh>​
 +
 +This line redirects TCP port 80 (web server) traffic to a machine inside the network at 192.168.1.20. So, even though 192.168.1.20 is behind your gateway and inside your network, the outside world can access it.
 +
 +The **from any to any** part of the above **rdr** line can be quite useful. If you know what addresses or subnets are supposed to have access to the web server at port 80, you can restrict them here:
 +<sxh pf>
 +pass in on tl0 proto tcp from 203.0.113.0/​24 to any port 80 rdr-to 192.168.1.20
 +</​sxh>​
 +
 +This will redirect only the specified subnet. Note this implies you can redirect different incoming hosts to different machines behind the gateway. This can be quite useful as well. For example, you could have users at remote sites access their own desktop computers using the same port and IP address on the gateway as long as you know the IP address they will be connecting from.
 +<sxh pf>
 +pass in on tl0 proto tcp from 203.0.113.14 to any port 80 rdr-to 192.168.1.20
 +pass in on tl0 proto tcp from 198.51.100.89 to any port 80 rdr-to 192.168.1.22
 +pass in on tl0 proto tcp from 198.51.100.178 to any port 80 rdr-to 192.168.1.23
 +</​sxh>​
 +A range of ports can also be redirected within the same rule:
 +<sxh pf>
 +pass in on tl0 proto tcp from any to any port 5000:5500 \
 +   ​rdr-to 192.168.1.20
 +pass in on tl0 proto tcp from any to any port 5000:5500 \
 +   ​rdr-to 192.168.1.20 port 6000
 +pass in on tl0 proto tcp from any to any port 5000:5500 \
 +   ​rdr-to 192.168.1.20 port 7000:*
 +</​sxh>​
 +
 +These examples show ports 5000 to 5500 inclusive being redirected to 192.168.1.20. In rule #1, port 5000 is redirected to 5000, 5001 to 5001, etc. In rule #2, the entire port range is redirected to port 6000. And in rule #3, port 5000 is redirected to 7000, 5001 to 7001, etc.
 +
 +==== Security implications ====
 +
 +Redirection does have security implications. Punching a hole in the firewall to allow traffic into the internal, protected network potentially opens up the internal machine to compromise. If traffic is forwarded to an internal web server and a vulnerability is discovered in the web server daemon, that machine can be compromised from an intruder on the internet. From there, the intruder has a doorway to the internal network, one that is permitted to pass right through the firewall.
 +
 +These risks can be minimized by keeping the externally accessed system tightly confined on a separate network. This network is often referred to as a Demilitarized Zone (DMZ) or a Private Service Network (PSN). This way, if the web server is compromised,​ the effects can be limited to the DMZ/PSN network by careful filtering of the traffic permitted to and from the DMZ/PSN.
 +
 +==== Redirection and reflection ====
 +
 +Often, redirection rules are used to forward incoming connections from the internet to a local server with a private address in the internal network or LAN, as in:
 +<sxh pf>
 +server = 192.168.1.40
 +pass in on $ext_if proto tcp from any to $ext_if port 80 rdr-to $server port 80
 +</​sxh>​
 +
 +But when the redirection rule is tested from a client on the LAN, it doesn'​t work. The reason is that redirection rules apply only to packets that pass through the specified interface ($ext_if, the external interface, in the example). Connecting to the external address of the firewall from a host on the LAN, however, does not mean the packets will actually pass through its external interface. The TCP/IP stack on the firewall compares the destination address of incoming packets with its own addresses and aliases and detects connections to itself as soon as they have passed the internal interface. Such packets do not physically pass through the external interface, and the stack does not simulate such a passage in any way. Thus, PF never sees these packets on the external interface, and the redirection rule, specifying the external interface, does not apply.
 +
 +Adding a second redirection rule for the internal interface does not have the desired effect either. When the local client connects to the external address of the firewall, the initial packet of the TCP handshake reaches the firewall through the internal interface. The redirection rule does apply and the destination address gets replaced with that of the internal server. The packet gets forwarded back through the internal interface and reaches the internal server. But the source address has not been translated, and still contains the local client'​s address, so the server sends its replies directly to the client. The firewall never sees the reply and has no chance to properly reverse the translation. The client receives a reply from a source it never expected and drops it. The TCP handshake then fails and no connection can be established.
 +
 +Still, it's often desirable for clients on the LAN to connect to the same internal server as external clients and to do so transparently. There are several solutions for this problem:
 +
 +=== Split-horizon DNS ===
 +
 +It's possible to configure DNS servers to answer queries from local hosts differently than external queries so that local clients will receive the internal server'​s address during name resolution. They will then connect directly to the local server, and the firewall isn't involved at all. This reduces local traffic since packets don't have to be sent through the firewall.
 +
 +=== Moving the server into a separate local network ===
 +
 +Adding an additional network interface to the firewall and moving the local server from the client'​s network into a dedicated network (DMZ) allows redirecting of connections from local clients in the same way as the redirection of external connections. Use of separate networks has several advantages, including improving security by isolating the server from the remaining local hosts. Should the server (which in our case is reachable from the Internet) ever become compromised,​ it can't access other local hosts directly as all connections have to pass through the firewall.
 +
 +=== TCP proxying ===
 +
 +A generic TCP proxy can be setup on the firewall, either listening on the port to be forwarded or getting connections on the internal interface redirected to the port it's listening on. When a local client connects to the firewall, the proxy accepts the connection, establishes a second connection to the internal server, and forwards data between those two connections.
 +
 +=== RDR-TO and NAT-TO combination ===
 +
 +With an additional NAT rule on the internal interface, the lacking source address translation described above can be achieved.
 +<sxh pf>
 +pass in on $int_if proto tcp from $int_net to $ext_if port 80 rdr-to $server
 +pass out on $int_if proto tcp to $server port 80 received-on $int_if nat-to $int_if
 +</​sxh>​
 +This will cause the initial packet from the client to be translated again when it's forwarded back through the internal interface, replacing the client'​s source address with the firewall'​s internal address. The internal server will reply back to the firewall, which can reverse both NAT and RDR translations when forwarding to the local client. This construct is rather complex as it creates two separate states for each reflected connection. Care must be taken to prevent the NAT rule from applying to other traffic, for instance connections originating from external hosts (through other redirections) or the firewall itself. Note that the **rdr-to** rule above will cause the TCP/IP stack to see packets arriving on the internal interface with a destination address inside the internal network.
 +
 +
 +===== Shortcuts For Creating Rulesets =====
 +
 +==== Introduction ====
 +
 +PF offers many ways in which a ruleset can be simplified. Some good examples are by using [[openbsd_pf_users_guide_en#​macros|macros]] and [[openbsd_pf_users_guide_en#​lists|lists]]. In addition, the ruleset language, or grammar, also offers some shortcuts for making a ruleset simpler. As a general rule of thumb, the simpler a ruleset is, the easier it is to understand and to maintain.
 +
 +==== Using macros ====
 +
 +Macros are useful because they provide an alternative to hard-coding addresses, port numbers, interfaces names, etc., into a ruleset. Did a server'​s IP address change? No problem, just update the macro; no need to mess around with the filter rules that you've spent time and energy perfecting for your needs.
 +
 +A common convention in PF rulesets is to define a macro for each network interface. If a network card ever needs to be replaced with one that uses a different driver, the macro can be updated and the filter rules will function as before. Another benefit is when installing the same ruleset on multiple machines. Certain machines may have different network cards in them, and using macros to define the network interfaces allows the rulesets to be installed with minimal editing. Using macros to define information in a ruleset that is subject to change, such as port numbers, IP addresses, and interface names, is recommended practice.
 +<sxh pf>
 +# define macros for each network interface
 +IntIF = "​dc0"​
 +ExtIF = "​fxp0"​
 +DmzIF = "​fxp1"​
 +</​sxh>​
 +
 +Another common convention is using macros to define IP addresses and network blocks. This can greatly reduce the maintenance of a ruleset when IP addresses change.
 +<sxh pf>
 +# define our networks
 +IntNet = "​192.168.0.0/​24"​
 +ExtAdd = "​192.0.2.4"​
 +DmzNet = "​10.0.0.0/​24"​
 +</​sxh>​
 +
 +If the internal network ever expanded or was renumbered into a different IP block, the macro can be updated:
 +<sxh pf>
 +IntNet = "{ 192.168.0.0/​24,​ 192.168.1.0/​24 }"
 +</​sxh>​
 +
 +Once the ruleset is reloaded, everything will work as before.
 +
 +==== Using lists ====
 +
 +Let's look at a good set of rules to have in your ruleset to handle [[https://​tools.ietf.org/​html/​rfc1918|RFC 1918]] addresses that just shouldn'​t be floating around the Internet, and when they are, are usually trying to cause trouble:
 +<sxh pf>
 +block in  quick on tl0 inet from 127.0.0.0/8 to any
 +block in  quick on tl0 inet from 192.168.0.0/​16 to any
 +block in  quick on tl0 inet from 172.16.0.0/​12 to any
 +block in  quick on tl0 inet from 10.0.0.0/8 to any
 +block out quick on tl0 inet from any to 127.0.0.0/8
 +block out quick on tl0 inet from any to 192.168.0.0/​16
 +block out quick on tl0 inet from any to 172.16.0.0/​12
 +block out quick on tl0 inet from any to 10.0.0.0/8
 +</​sxh>​
 +
 +Now look at the following simplification:​
 +<sxh pf>
 +block in quick on tl0 inet from { 127.0.0.0/​8,​ 192.168.0.0/​16,​ \
 +   ​172.16.0.0/​12,​ 10.0.0.0/8 } to any
 +block out quick on tl0 inet from any to { 127.0.0.0/​8,​ \
 +   ​192.168.0.0/​16,​ 172.16.0.0/​12,​ 10.0.0.0/8 }
 +</​sxh>​
 +
 +The ruleset has been reduced from eight lines down to two. Things get even better when macros are used in conjunction with a list:
 +<sxh pf>
 +NoRouteIPs = "{ 127.0.0.0/​8,​ 192.168.0.0/​16,​ 172.16.0.0/​12,​ 10.0.0.0/8 }"
 +
 +ExtIF = "​tl0"​
 +block in  quick on $ExtIF from $NoRouteIPs to any
 +block out quick on $ExtIF from any to $NoRouteIPs
 +</​sxh>​
 +
 +Note that macros and lists simplify the pf.conf file, but the lines are actually expanded by [[http://​man.openbsd.org/​pfctl|pfctl(8)]] into multiple rules. So, the above example actually expands to the following rules:
 +<sxh pf>
 +block in  quick on tl0 inet from 127.0.0.0/8 to any
 +block in  quick on tl0 inet from 192.168.0.0/​16 to any
 +block in  quick on tl0 inet from 172.16.0.0/​12 to any
 +block in  quick on tl0 inet from 10.0.0.0/8 to any
 +block out quick on tl0 inet from any to 10.0.0.0/8
 +block out quick on tl0 inet from any to 172.16.0.0/​12
 +block out quick on tl0 inet from any to 192.168.0.0/​16
 +block out quick on tl0 inet from any to 127.0.0.0/8
 +</​sxh>​
 +
 +As you can see, the PF expansion is purely a convenience for the writer and maintainer of the pf.conf file, not an actual simplification of the rules processed by [[http://​man.openbsd.org/​pf|pf(4)]].
 +
 +Macros can be used to define more than just addresses and ports; they can be used anywhere in a PF rules file:
 +<sxh pf>
 +pre  = "pass in quick on ep0 inet proto tcp from "
 +post = "to any port { 80, 6667 }"
 +
 +$pre 198.51.100.80 $post
 +$pre 203.0.113.79 ​ $post
 +$pre 203.0.113.178 $post
 +</​sxh>​
 +
 +Expands to:
 +<sxh pf>
 +pass in quick on ep0 inet proto tcp from 198.51.100.80 to any port = 80
 +pass in quick on ep0 inet proto tcp from 198.51.100.80 to any port = 6667
 +pass in quick on ep0 inet proto tcp from 203.0.113.79 ​ to any port = 80
 +pass in quick on ep0 inet proto tcp from 203.0.113.79 ​ to any port = 6667
 +pass in quick on ep0 inet proto tcp from 203.0.113.178 to any port = 80
 +pass in quick on ep0 inet proto tcp from 203.0.113.178 to any port = 6667
 +</​sxh>​
 +
 +==== PF grammar ====
 +
 +PF's grammar is quite flexible which, in turn, allows for great flexibility in a ruleset. PF is able to infer certain keywords which means that they don't have to be explicitly stated in a rule, and keyword ordering is relaxed such that it isn't necessary to memorize strict syntax.
 +
 +=== Elimination of keywords ===
 +
 +To define a "​default deny" policy, two rules are used:
 +<sxh pf>
 +block in  all
 +block out all
 +</​sxh>​
 +
 +This can now be reduced to:
 +<sxh pf>
 +block
 +</​sxh>​
 +
 +When no direction is specified, PF will assume the rule applies to packets moving in both directions.
 +
 +Similarly, the "​**from any to any**" and "​**all**"​ clauses can be left out of a rule, for example:
 +<sxh pf>
 +block in on rl0 all
 +pass  in quick log on rl0 proto tcp from any to any port 22
 +</​sxh>​
 +
 +can be simplified as:
 +<sxh pf>
 +block in on rl0
 +pass  in quick log on rl0 proto tcp to port 22 keep state
 +</​sxh>​
 +
 +The first rule blocks all incoming packets from anywhere to anywhere on rl0, and the second rule passes in TCP traffic on rl0 to port 22.
 +
 +=== Return simplification ===
 +
 +A ruleset used to block packets and reply with a TCP RST or ICMP Unreachable response could look like this:
 +<sxh pf>
 +block in all
 +block return-rst ​ in proto tcp all
 +block return-icmp in proto udp all
 +block out all
 +block return-rst ​ out proto tcp all
 +block return-icmp out proto udp all
 +</​sxh>​
 +
 +This can be simplified as:
 +<sxh pf>
 +block return
 +</​sxh>​
 +
 +When PF sees the return keyword, it's smart enough to send the proper response, or no response at all, depending on the protocol of the packet being blocked.
 +
 +=== Keyword ordering ===
 +
 +The order in which keywords are specified is flexible in most cases. For example, a rule written as:
 +<sxh pf>
 +pass in log quick on rl0 proto tcp to port 22 flags S/SA keep state queue ssh label ssh
 +</​sxh>​
 +
 +can also be written as:
 +<sxh pf>
 +pass in quick log on rl0 proto tcp to port 22 queue ssh keep state label ssh flags S/SA
 +</​sxh>​
 +
 +Other, similar variations will also work.
 +
 +===== DQS PF.CONF Example =====
 +
 +Here is an example of a pf.conf working with the drop policy and with wan, dmz and lan interfaces.
 +<sxh pf>
 +vim /​etc/​pf.conf
 +#       ​$OpenBSD:​ pf.conf,v 1.54 2014/08/23 05:49:42 deraadt Exp $
 +#
 +# See pf.conf(5) and /​etc/​examples/​pf.conf
 +# Created by: DQS CONSULTORIA E TREINAMENTOS
 +# Date: 23/08/2016
 +# Modified date: 24/08/2016 by: DQS CONSULTORIA E TREINAMENTOS
 +# Email: douglas.q.santos@gmail.com
 +
 +### MACROS INTERFACES
 +dmz_if = "​em1"​ # 192.168.56.180
 +wan_if = "​em0"​ # 10.0.2.15 -> NAT Via Modem
 +lan_if = "​em2"​ # 192.168.25.180
 +all_if = "​{"​ $dmz_if $wan_if $lan_if "​}"​
 +
 +### MACROS NETWORKS
 +net_dmz = "{ 192.168.56.0/​24 }"
 +net_lan = "{ 192.168.25.0/​24 }"
 +
 +### FTP PKGS SERVERS
 +ftp_servers_openbsd = "{ mirrors.unb.br openbsd.c3sl.ufpr.br }"
 +ftp_servers_debian = "{ ftp.br.debian.org security.debian.org }"
 +
 +### MACRO PORTS
 +ports_tcp ="{ 20 21 53 80 443 }"
 +ports_udp ="{ 53 123 }"
 +alt_ssh_port = "​22022"​
 +http_port = "​80"​
 +
 +### Setting up the Runtime options
 +set skip on lo
 +set block-policy drop
 +set loginterface egress
 +set state-policy floating
 +
 +### Normalizing incoming packets
 +match in all scrub (no-df random-id max-mss 1440)
 +
 +### Default Policy
 +block all
 +
 +### AntiSpoof
 +antispoof for $all_if inet
 +
 +### Allow ICMP From Localhost to Anywhere
 +pass out on $wan_if proto icmp from $wan_if to any
 +
 +### Allow ICMP From LAN to Anywhere
 +pass out on $lan_if proto icmp from $net_lan to any
 +pass in  on $lan_if proto icmp from $net_lan to any
 +
 +### ALLOW SSH From Localhost to Anywhere
 +pass out on $dmz_if proto tcp from $dmz_if to any port $alt_ssh_port
 +pass out on $wan_if proto tcp from $wan_if to any port $alt_ssh_port
 +pass out on $lan_if proto tcp from $lan_if to $net_lan port $alt_ssh_port
 +
 +# ALLOW NTP AND DNS from Localhost To Anywhere
 +pass out on $wan_if proto udp from $wan_if to any port $ports_udp
 +
 +# ALLOW NTP AND DNS From LAN to Anywhere
 +pass out on $lan_if inet proto udp from $lan_if:​network to any port $ports_udp
 +pass in  on $lan_if inet proto udp from $lan_if:​network to any port $ports_udp
 +
 +# ALLOW DNS, HTTP, FTP AND HTTPS From Localhost to Anywhere
 +pass out on $wan_if proto tcp from $wan_if to any port $ports_tcp
 +
 +# ALLOW DNS, HTTP AND HTTPS From LAN to Anywhere
 +pass in  on $lan_if proto tcp from $lan_if:​network to any port $ports_tcp
 +pass out on $lan_if proto tcp from $lan_if:​network to any port $ports_tcp
 +
 +# ALLOW PASSIVE FTP CONNECTIONS FROM Localhost To The FTP SERVERS USED by: pkg_add
 +pass out on $wan_if proto tcp from $wan_if to $ftp_servers_openbsd ​ port > 1024
 +
 +# ALLOW PASSIVE FTP CONNECTIONS FROM LAN To The Debian FTP SERVERS USED by apt-get and aptitude
 +pass out on $wan_if proto tcp from $lan_if:​network to $ftp_servers_debian ​ port > 1024
 +
 +# ALLOW FTP FROM DMZ AND LAN TO ANYWHERE
 +# Note: http://​www.openbsd.org/​faq/​pf/​ftp.html
 +pass in quick on $dmz_if inet proto tcp to port 21 divert-to 127.0.0.1 port 8021
 +pass in quick on $lan_if inet proto tcp to port 21 divert-to 127.0.0.1 port 8021
 +anchor "​ftp-proxy/​*"​
 +
 +### ALLOW ACCESS TO SSH On Localhost on DMZ Interface
 +pass in quick on $dmz_if proto tcp from $net_dmz to $dmz_if port $alt_ssh_port
 +pass in on $dmz_if proto icmp from $net_dmz to $dmz_if
 +
 +### REDIRECT Connections ON Firewall DMZ ports 80 and 22022 to 192.168.25.153
 +### Note: We need following three rules because the policy here is drop
 +pass in on $dmz_if proto tcp from 192.168.56.1 to 192.168.56.180 port $http_port rdr-to 192.168.25.153 port $http_port
 +pass in on $dmz_if proto tcp from 192.168.56.1 to 192.168.56.180 port 22023 rdr-to 192.168.25.153 port $alt_ssh_port
 +pass out on $lan_if inet proto { tcp } from 192.168.56.1 to 192.168.25.153 port { $http_port $alt_ssh_port }
 +
 +### EQUIVALENT TO MASQUERADE IPTABLES
 +pass out on $wan_if inet from { $net_dmz $net_lan } to any nat-to ($wan_if)
 +
 +# PASS ALL TCP PROTOCOL FROM THE SERVER TO ANYWHERE
 +#pass out proto tcp from self to any keep state
 +</​sxh>​
 +
 +====== Advanced configuration ======
 +
 +===== Runtime options =====
 +
 +Options are used to control PF's operation. Options are specified in **pf.conf** using the set directive.
 +
 +  * **set block-policy option**
 +    * Sets the default behavior for filter rules that specify the block action.
 +      * **drop** - packet is silently dropped.
 +      * **return** - a TCP RST packet is returned for blocked TCP packets and an ICMP Unreachable packet is returned for all others.
 +    * Note that individual filter rules can override the default response. The default is drop.
 +  * **set debug option**
 +    * Set pf's debugging level. Choices include **emerg, alert, crit, err, warning, notice, info** and **debug**.
 +  * **set fingerprints file**
 +    * Sets the file to load operating system fingerprints from. For use with [[openbsd_pf_users_guide_en#​passive_operating_system_fingerprinting|passive OS fingerprinting]]. The default is /etc/pf.os.
 +  * **set limit option value**
 +    * Set various limits on pf's operation.
 +      * **frags** - maximum number of entries in the memory pool used for packet reassembly (scrub rules). Default is 5000.
 +      * **src-nodes** - maximum number of entries in the memory pool used for tracking source IP addresses (generated by the **sticky-address** and **source-track** options). Default is 10000.
 +      * **states** - maximum number of entries in the memory pool used for state table entries ([[openbsd_pf_users_guide_en#​packet_filtering|filter]] rules that specify **keep state**). Default is 10000.
 +      * **tables** - maximum number of tables that can be created. Default is 1000.
 +      * **table-entries** - the overall limit on how many addresses can be stored in all tables. Default is 200000. If the system has less than 100MB of physical memory, the default is set to 100000.
 +  * **set loginterface interface**
 +    * Sets the interface for which PF should gather statistics such as bytes in/out and packets passed/​blocked. Statistics can only be gathered for one interface at a time. Note that the match, bad-offset, etc., counters and the state table counters are recorded regardless of whether loginterface is set or not. To turn this option off, set it to none. Default is none. 
 +  * **set optimization option**
 +    * Optimize PF for one of the following network environments: ​
 +      * **normal** - suitable for almost all networks.
 +      * **high-latency** - high latency networks such as satellite connections.
 +      * **aggressive** - aggressively expires connections from the state table. This can greatly reduce the memory requirements on a busy firewall at the risk of dropping idle connections early.
 +      * **conservative** - extremely conservative settings. This avoids dropping idle connections at the expense of greater memory utilization and slightly increased processor utilization.
 +    * The default is normal.
 +  * **set ruleset-optimization option**
 +    * Control operation of the PF ruleset optimizer.
 +      * **none** - disable the optimizer altogether.
 +      * **basic** - enables the following ruleset optimizations:​
 +        * remove duplicate rules
 +        * remove rules that are a subset of another rule
 +        * combine multiple rules into a table when advantageous
 +        * re-order the rules to improve evaluation performance
 +      * **profile** - uses the currently loaded ruleset as a feedback profile to tailor the ordering of quick rules to actual network traffic.
 +    * Starting in OpenBSD 4.2, the default is basic. See [[http://​man.openbsd.org/​pf.conf|pf.conf(5)]] for a more complete description. ​
 +  * **set skip on interface**
 +    * Skip all PF processing on **interface**. This can be useful on loopback interfaces where filtering, normalization,​ queueing, etc, are not required. This option can be used multiple times. By default, this option is not set.
 +  * set state-policy option
 +    * Sets PF's behavior when it comes to keeping state. This behavior can be overridden on a per-rule basis. See [[openbsd_pf_users_guide_en#​keeping_state|keeping state]].
 +      * **if-bound** - states are bound to the interface they'​re created on. If traffic matches a state table entry but is not crossing the interface recorded in that state entry, the match is rejected. The packet must then match a filter rule or will be dropped/​rejected altogether.
 +      * **floating** - states can match packets on any interface. As long as the packet matches a state entry and is passing in the same direction as it was on the interface when the state was created, it does not matter what interface it's crossing. It will pass.
 +    * The default is floating. ​
 +  * **set timeout option value**
 +    * Set various timeouts (in seconds).
 +      * **interval** - seconds between purges of expired states and packet fragments. Default is 10.
 +      * **frag** - seconds before an unassembled fragment is expired. Default is 30.
 +      * **src.track** - seconds to keep a [[openbsd_pf_users_guide_en##​stateful_tracking_options|source tracking]] entry in memory after the last state expires. Default is 0. 
 +
 +Example:
 +<sxh pf>
 +set timeout interval 10
 +set timeout frag 30
 +set limit { frags 5000, states 2500 }
 +set optimization high-latency
 +set block-policy return
 +set loginterface dc0
 +set fingerprints "/​etc/​pf.os.test"​
 +set skip on lo0
 +set state-policy if-bound
 +</​sxh>​
 +
 +===== Anchors =====
 +
 +==== Introduction ====
 +
 +In addition to the main ruleset, PF can also evaluate sub-rulesets. Since sub-rulesets can be manipulated on the fly by using [[http://​man.openbsd.org/​pfctl|pfctl(8)]],​ they provide a convenient way of dynamically altering an active ruleset. Whereas a [[openbsd_pf_users_guide_en#​tables|table]] is used to hold a dynamic list of addresses, a sub-ruleset is used to hold a dynamic set of rules. A sub-ruleset is attached to the main ruleset by using an anchor.
 +
 +Anchors can be nested which allows for sub-rulesets to be chained together. Anchor rules will be evaluated relative to the anchor in which they are loaded. For example, anchor rules in the main ruleset will create anchor attachment points with the main ruleset as their parent, and anchor rules loaded from files with the load anchor directive will create anchor points with that anchor as their parent.
 +
 +==== Anchors ====
 +
 +An anchor is a collection of rules, tables and other anchors that has been assigned a name. When PF comes across an **anchor** rule in the main ruleset, it will evaluate the rules contained within the anchor point as it evaluates rules in the main ruleset. Processing will then continue in the main ruleset unless the packet matches a filter rule that uses the **quick** option, in which case the match will be considered final and will abort the evaluation of rules in both the anchor and the main rulesets.
 +
 +For example:
 +<sxh pf>
 +ext_if = "​fxp0"​
 +block on $ext_if
 +pass  out on $ext_if
 +anchor goodguys
 +</​sxh>​
 +
 +This ruleset sets a default deny policy on **fxp0** for both incoming and outgoing traffic, which is then statefully passed out, and an anchor rule is created named **goodguys**. Anchors can be populated with rules by three methods:
 +  * using a load rule
 +  * using [[http://​man.openbsd.org/​pfctl|pfctl(8)]]
 +  * specifying the rules inline of the main ruleset
 +
 +The **load** rule causes **pfctl** to populate the specified anchor by reading rules from a text file. The **load** rule must be placed after the **anchor** rule.
 +
 +For example:
 +<sxh pf>
 +anchor goodguys
 +load anchor goodguys from "/​etc/​anchor-goodguys-ssh"​
 +</​sxh>​
 +
 +To add rules to an anchor using pfctl, the following type of command can be used:
 +<sxh pf>
 +echo "pass in proto tcp from 192.0.2.3 to any port 22" | pfctl -a goodguys -f -
 +</​sxh>​
 +
 +Rules can also be saved to (and loaded from) a text file. For example, you could append the following two lines to the /​etc/​anchor-goodguys-www file:
 +<sxh pf>
 +pass in proto tcp from 192.0.2.3 to any port 80
 +pass in proto tcp from 192.0.2.4 to any port { 80 443 }
 +</​sxh>​
 +
 +Then applied with:
 +<sxh pf>
 +pfctl -a goodguys -f /​etc/​anchor-goodguys-www
 +</​sxh>​
 +
 +To load rules directly from the main ruleset, enclose the anchor rules in a brace-delimited block:
 +<sxh pf>
 +anchor "​goodguys"​ {
 +        pass in proto tcp from 192.168.2.3 to port 22
 +}
 +</​sxh>​
 +
 +Inline anchors can also contain more anchors.
 +<sxh pf>
 +allow = "{ 192.0.2.3 192.0.2.4 }"
 +
 +anchor "​goodguys"​ {
 +  anchor {
 +       pass in proto tcp from 192.0.2.3 to port 80
 +  }
 +  pass in proto tcp from $allow to port 22
 +}
 +</​sxh>​
 +
 +With inline anchors, the name of the anchor becomes optional. Note how the nested anchor in the above example does not have a name. The macro **$allow** is created outside of the anchor (in the main ruleset) and is then used within the anchor.
 +
 +Rules can be loaded into an anchor using the same syntax and options as rules loaded into the main ruleset. One caveat is that, unless you're using inline anchors, any [[openbsd_pf_users_guide_en#​macros| macros]] that are used must also be defined within the anchor itself; macros that are defined in the parent ruleset are **not** visible from the anchor.
 +
 +Since anchors can be nested, it's possible to specify that all child anchors within a specified anchor be evaluated:
 +<sxh pf>
 +anchor "​spam/​*"​
 +</​sxh>​
 +
 +This syntax causes each rule within each anchor attached to the spam anchor to be evaluated. The child anchors will be evaluated in alphabetical order but are not descended into recursively. Anchors are always evaluated relative to the anchor in which they'​re defined.
 +
 +Each anchor, as well as the main ruleset, exist separately from the other rulesets. Operations done on one ruleset, such as flushing the rules, do not affect any of the others. In addition, removing an anchor point from the main ruleset does not destroy the anchor or any child anchors that are attached to that anchor. An anchor is not destroyed until it's flushed of all rules using pfctl(8) and there are no child anchors within the anchor.
 +
 +==== Anchor options ====
 +
 +Optionally, **anchor** rules can specify interface, protocol, source and destination address, tag, etc, using the same syntax as other rules. When such information is given, **anchor** rules are only processed if the packet matches the **anchor** rule's definition.
 +
 +For example:
 +<sxh pf>
 +ext_if = "​fxp0"​
 +block          on $ext_if
 +pass       out on $ext_if
 +anchor ssh in  on $ext_if proto tcp to port 22
 +</​sxh>​
 +
 +The rules in the anchor **ssh** are only evaluated for TCP packets destined for port 22 that come in on **fxp0**. Rules are then added to the **anchor** like so:
 +<sxh pf>
 +echo "pass in from 192.0.2.10 to any" | pfctl -a ssh -f -
 +</​sxh>​
 +
 +So, even though the filter rule doesn'​t specify an interface, protocol or port, the host 192.0.2.10 will only be permitted to connect using SSH because of the **anchor** rule's definition.
 +
 +The same syntax can be applied to inline anchors.
 +<sxh pf>
 +allow = "{ 192.0.2.3 192.0.2.4 }"
 +anchor "​goodguys"​ in proto tcp {
 +   ​anchor proto tcp to port 80 {
 +      pass from 192.0.2.3
 +   }
 +   ​anchor proto tcp to port 22 {
 +      pass from $allow
 +   }
 +}
 +</​sxh>​
 +
 +==== Manipulating anchors ====
 +
 +Manipulation of anchors is performed via [[http://​man.openbsd.org/​pfctl|pfctl(8)]]. It can be used to add and remove rules from an anchor without reloading the main ruleset.
 +
 +To list all the rules in the anchor named **ssh**:
 +<sxh pf>
 +pfctl -a ssh -s rules
 +</​sxh>​
 +
 +To flush all rules from the same anchor:
 +<sxh pf>
 +pfctl -a ssh -F rules
 +</​sxh>​
 +
 +For a full list of commands, please see [[http://​man.openbsd.org/​pfctl|pfctl(8)]].
 +
 +===== Address Pools and Load Balancing =====
 +
 +==== Introduction ====
 +
 +An address pool is a supply of two or more addresses whose use is shared among a group of users. It can be specified as the target address in nat-to, rdr-to,​route-to,​ reply-to and dup-to filter options.
 +
 +There are four methods for using an address pool:
 +  * **bitmask** - grafts the network portion of the pool address over top of the address that is being modified (source address for nat-to rules, destination address for rdr-to rules). Example: if the address pool is 192.0.2.1/​24 and the address being modified is 10.0.0.50, then the resulting address will be 192.0.2.50. If the address pool is 192.0.2.1/​25 and the address being modified is 10.0.0.130, then the resulting address will be 192.0.2.2.
 +  * **random** - randomly selects an address from the pool.
 +  * **source-hash** - uses a hash of the source address to determine which address to use from the pool. This method ensures that a given source address is always mapped to the same pool address. The key that is fed to the hashing algorithm can optionally be specified after the source-hash keyword in hex format or as a string. By default, [[http://​man.openbsd.org/​pfctl|pfctl(8)]] will generate a random key every time the ruleset is loaded.
 +  * **round-robin** - loops through the address pool in sequence. This is the default method and also the only method allowed when the address pool is specified using a [[openbsd_pf_users_guide_en#​tables|table]].
 +
 +Except for the **round-robin** method, the address pool must be expressed as a [[https://​web.archive.org/​web/​20150213012421/​http://​public.swbell.net/​dedicated/​cidr.html|CIDR]] (Classless Inter-Domain Routing) network block. The **round-robin** method will accept multiple individual addresses using a [[openbsd_pf_users_guide_en#​lists|list]] or [[openbsd_pf_users_guide_en#​tables|table]].
 +
 +The **sticky-address** option can be used with the **random** and **round-robin** pool types to ensure that a particular source address is always mapped to the same redirection address.
 +
 +==== NAT address pool ====
 +
 +An address pool can be used as the translation address in [[openbsd_pf_users_guide_en#​network_address_translation|nat-to]] rules. Connections will have their source address translated to an address from the pool based on the method chosen. This can be useful in situations where PF is performing NAT for a very large network. Since the number of NATed connections per translation address is limited, adding additional translation addresses will allow the NAT gateway to scale to serve a larger number of users.
 +
 +In this example, a pool of two addresses is being used to translate outgoing packets. For each outgoing connection, PF will rotate through the addresses in a round-robin manner.
 +
 +<sxh pf>
 +match out on $ext_if inet nat-to { 192.0.2.5, 192.0.2.10 }
 +</​sxh>​
 +
 +One drawback with this method is that successive connections from the same internal address will not always be translated to the same translation address. This can cause interference,​ for example, when browsing websites that track user logins based on IP address. An alternate approach is to use the source-hash method so that each internal address is always translated to the same translation address. To do this, the address pool must be a [[https://​web.archive.org/​web/​20150213012421/​http://​public.swbell.net/​dedicated/​cidr.html|CIDR]] network block.
 +
 +<sxh pf>
 +match out on $ext_if inet nat-to 192.0.2.4/​31 source-hash
 +</​sxh>​
 +
 +This rule uses the address pool 192.0.2.4/​31 (192.0.2.4 - 192.0.2.5) as the translation address for outgoing packets. Each internal address will always be translated to the same translation address because of the **source-hash** keyword.
 +
 +==== Load balance incoming connections ====
 +
 +Address pools can also be used to load balance incoming connections. For example, incoming web server connections can be distributed across a web server farm:
 +
 +<sxh pf>
 +web_servers = "{ 10.0.0.10, 10.0.0.11, 10.0.0.13 }"
 +
 +match in on $ext_if proto tcp to port 80 rdr-to $web_servers \
 +    round-robin sticky-address
 +</​sxh>​
 +
 +Successive connections will be redirected to the web servers in a round-robin manner with connections from the same source being sent to the same web server. This "​sticky connection"​ will exist as long as there are states that refer to this connection. Once the states expire, so will the sticky connection. Further connections from that host will be redirected to the next web server in the round robin.
 +
 +==== Load balance outgoing traffic ====
 +
 +Address pools can be used in combination with the route-to filter option to load balance two or more Internet connections when a proper multi-path routing protocol (like [[http://​www.rfc-editor.org/​rfc/​rfc1771.txt|BGP4]]) is unavailable. By using **route-to** with a **round-robin** address pool, outbound connections can be evenly distributed among multiple outbound paths.
 +
 +One additional piece of information that's needed to do this is the IP address of the adjacent router on each Internet connection. This is fed to the **route-to** option to control the destination of outgoing packets.
 +
 +The following example balances outgoing traffic across two Internet connections:​
 +<sxh pf>
 +lan_net = "​192.168.0.0/​24"​
 +int_if ​ = "​dc0"​
 +ext_if1 = "​fxp0"​
 +ext_if2 = "​fxp1"​
 +ext_gw1 = "​198.51.100.100"​
 +ext_gw2 = "​203.0.113.200"​
 +
 +pass in on $int_if from $lan_net route-to \
 +   { ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } round-robin
 +</​sxh>​
 +
 +The **route-to** option is used on traffic coming in on the **internal** interface to specify the outgoing network interfaces that traffic will be balanced across along with their respective gateways. Note that the **route-to** option must be present on each filter rule that traffic is to be balanced for (it cannot be used with **match** rules).
 +
 +To ensure that packets with a source address belonging to **$ext_if1** are always routed to **$ext_gw1** (and similarly for **$ext_if2** and **$ext_gw2**),​ the following two lines should be included in the ruleset:
 +<sxh pf>
 +pass out on $ext_if1 from $ext_if2 route-to ($ext_if2 $ext_gw2)
 +pass out on $ext_if2 from $ext_if1 route-to ($ext_if1 $ext_gw1)
 +</​sxh>​
 +
 +Finally, NAT can also be used on each outgoing interface:
 +<sxh pf>
 +match out on $ext_if1 from $lan_net nat-to ($ext_if1)
 +match out on $ext_if2 from $lan_net nat-to ($ext_if2)
 +</​sxh>​
 +
 +A complete example that load balances outgoing traffic might look something like this:
 +<sxh pf>
 +lan_net = "​192.168.0.0/​24"​
 +int_if ​ = "​dc0"​
 +ext_if1 = "​fxp0"​
 +ext_if2 = "​fxp1"​
 +ext_gw1 = "​198.51.100.100"​
 +ext_gw2 = "​203.0.113.200"​
 +
 +#  nat outgoing connections on each internet interface
 +match out on $ext_if1 from $lan_net nat-to ($ext_if1)
 +match out on $ext_if2 from $lan_net nat-to ($ext_if2)
 +
 +#  default deny
 +block in
 +block out
 +
 +#  pass all outgoing packets on internal interface
 +pass out on $int_if to $lan_net
 +#  pass in quick any packets destined for the gateway itself
 +pass in quick on $int_if from $lan_net to $int_if
 +#  load balance outgoing traffic from internal network.
 +pass in on $int_if from $lan_net \
 +    route-to { ($ext_if1 $ext_gw1), ($ext_if2 $ext_gw2) } \
 +    round-robin
 +#  keep https traffic on a single connection; some web applications,​
 +#  especially "​secure"​ ones, don't allow it to change mid-session
 +pass in on $int_if proto tcp from $lan_net to port https \
 +    route-to ($ext_if1 $ext_gw1)
 +
 +#  general "pass out" rules for external interfaces
 +pass out on $ext_if1
 +pass out on $ext_if2
 +
 +#  route packets from any IPs on $ext_if1 to $ext_gw1 and the same for
 +#  $ext_if2 and $ext_gw2
 +pass out on $ext_if1 from $ext_if2 route-to ($ext_if2 $ext_gw2)
 +pass out on $ext_if2 from $ext_if1 route-to ($ext_if1 $ext_gw1)
 +</​sxh>​
 +
 +=====  Packet Tagging (Policy Filtering) =====
 +
 +==== Introduction ====
 +
 +Packet tagging is a way of marking packets with an internal identifier that can later be used in filter and translation rule criteria. With tagging, it's possible to do such things as create "​trusts"​ between interfaces and determine if packets have been processed by translation rules. It's also possible to move away from rule-based filtering and to start doing policy-based filtering.
 +
 +==== Assigning tags to Packets ====
 +
 +To add a tag to a packet, use the tag keyword:
 +<sxh pf>
 +pass in on $int_if all tag INTERNAL_NET
 +</​sxh>​
 +
 +The tag **INTERNAL_NET** will be added to any packet which matches the above rule.
 +
 +A tag can also be assigned using a [[openbsd_pf_users_guide_en#​macros|macro]]. For instance:
 +<sxh pf>
 +name = "​INTERNAL_NET"​
 +pass in on $int_if all tag $name
 +</​sxh>​
 +
 +There are a set of predefined macros which can also be used.
 +  * **$if** - The interface
 +  * **$srcaddr** - Source IP address
 +  * **$dstaddr** - Destination IP address
 +  * **$srcport** - The source port specification
 +  * **$dstport** - The destination port specification
 +  * **$proto** - The protocol
 +  * **$nr** - The rule number
 +
 +These macros are expanded at ruleset load time and NOT at runtime.
 +
 +Tagging follows these rules:
 +
 +  * Tags are "​sticky."​ Once a tag is applied to a packet by a matching rule, it is never removed. It can, however, be replaced with a different tag.
 +  * Because of a tag's "​stickiness,"​ a packet can have a tag even if the last matching rule doesn'​t use the tag keyword.
 +  * A packet is only ever assigned a maximum of one tag at a time.
 +  * Tags are internal identifiers. Tags are not sent out over the wire.
 +  * Tag names can be up to 63 characters long.
 +
 +Take the following ruleset as an example.
 +<sxh pf>
 +pass in on $int_if tag INT_NET
 +pass in quick on $int_if proto tcp to port 80 tag INT_NET_HTTP
 +pass in quick on $int_if from 192.168.1.5
 +</​sxh>​
 +
 +  * Packets coming in on **$int_if** will be assigned a tag of **INT_NET** by rule #1.
 +  * TCP packets coming in on **$int_if** and destined for port 80 will first be assigned a tag of **INT_NET** by rule #1. That tag will then be replaced with the **INT_NET_HTTP** tag by rule #2.
 +  * Packets coming in on **$int_if** from 192.168.1.5 will be tagged one of two ways. If the packet is destined for TCP port 80 it will match rule #2 and be tagged with **INT_NET_HTTP**. Otherwise, the packet will match rule #3 but will be tagged with **INT_NET**. Because the packet matches rule #1, the INT_NET tag is applied and is not removed unless a subsequently matching rule specifies a tag (this is the "​stickiness"​ of a tag).
 +
 +==== Checking for applied tags ====
 +
 +To check for previously applied tags, use the tagged keyword:
 +<sxh pf>
 +pass out on $ext_if tagged INT_NET
 +</​sxh>​
 +
 +Outgoing packets on **$ext_if** must be tagged with the **INT_NET** tag in order to match the above rule. Inverse matching can also be done by using the ! operator:
 +<sxh pf>
 +pass out on $ext_if ! tagged WIFI_NET
 +</​sxh>​
 +
 +==== Policy filtering ====
 +
 +Policy filtering takes a different approach to writing a filter ruleset. A policy is defined which sets the rules for what types of traffic is passed and what types are blocked. Packets are then classified into the policy based on the traditional criteria of source/​destination IP address/​port,​ protocol, etc. For example, examine the following firewall policy:
 +
 +  * Traffic from the internal **LAN** to the Internet is permitted (**LAN_INET**) and must be translated (**LAN_INET_NAT**).
 +  * Traffic from the internal **LAN** to the **DMZ** is permitted (**LAN_DMZ**).
 +  * Traffic from the Internet to servers in the **DMZ** is permitted (**INET_DMZ**).
 +  * Traffic from the Internet that's being redirected to [[http://​man.openbsd.org/​spamd|spamd(8)]] is permitted (**SPAMD**).
 +  * All other traffic is blocked.
 +
 +Note how the policy covers **all** traffic that will be passing through the firewall. The item in parenthesis indicates the tag that will be used for that policy item.
 +
 +Rules now need to be written to classify packets into the policy.
 +<sxh pf>
 +block all
 +pass out on $ext_if inet tag LAN_INET_NAT tagged LAN_INET nat-to ($ext_if)
 +pass in  on $int_if from $int_net tag LAN_INET
 +pass in  on $int_if from $int_net to $dmz_net tag LAN_DMZ
 +pass in  on $ext_if proto tcp to $www_server port 80 tag INET_DMZ
 +pass in  on $ext_if proto tcp from <​spamd>​ to port smtp tag SPAMD rdr-to 127.0.0.1 port 8025
 +</​sxh>​
 +
 +Now the rules that define the policy are set.
 +<sxh pf>
 +pass in  quick on $ext_if tagged SPAMD
 +pass out quick on $ext_if tagged LAN_INET_NAT
 +pass out quick on $dmz_if tagged LAN_DMZ
 +pass out quick on $dmz_if tagged INET_DMZ
 +</​sxh>​
 +
 +Now that the whole ruleset is setup, changes are a matter of modifying the classification rules. For example, if a POP3/SMTP server is added to the DMZ, it will be necessary to add classification rules for POP3 and SMTP traffic, like so:
 +<sxh pf>
 +mail_server = "​192.168.0.10"​
 +[...]
 +pass in on $ext_if proto tcp to $mail_server port { smtp, pop3 } tag INET_DMZ
 +</​sxh>​
 +
 +Email traffic will now be passed as part of the INET_DMZ policy entry.
 +
 +The complete ruleset:
 +<sxh pf>
 +# macros
 +int_if ​     = "​dc0"​
 +dmz_if ​     = "​dc1"​
 +ext_if ​     = "​ep0"​
 +int_net ​    = "​10.0.0.0/​24"​
 +dmz_net ​    = "​192.168.0.0/​24"​
 +www_server ​ = "​192.168.0.5"​
 +mail_server = "​192.168.0.10"​
 +
 +table <​spamd>​ persist file "/​etc/​spammers"​
 +# classification -- classify packets based on the defined firewall policy.
 +block all
 +pass out on $ext_if inet tag LAN_INET_NAT tagged LAN_INET nat-to ($ext_if)
 +pass in on $int_if from $int_net tag LAN_INET
 +pass in on $int_if from $int_net to $dmz_net tag LAN_DMZ
 +pass in on $ext_if proto tcp to $www_server port 80 tag INET_DMZ
 +pass in on $ext_if proto tcp from <​spamd>​ to port smtp tag SPAMD rdr-to 127.0.0.1 port 8025
 +
 +# policy enforcement -- pass/block based on the defined firewall policy.
 +pass in  quick on $ext_if tagged SPAMD
 +pass out quick on $ext_if tagged LAN_INET_NAT
 +pass out quick on $dmz_if tagged LAN_DMZ
 +pass out quick on $dmz_if tagged INET_DMZ
 +</​sxh>​
 +
 +==== Tagging ethernet frames ====
 +
 +Tagging can be performed at the ethernet level if the machine doing the tagging/​filtering is also acting as a [[http://​man.openbsd.org/​bridge.4|bridge(4)]]. By creating bridge(4) filter rules that use the tag keyword, PF can be made to filter based on the source or destination MAC address. Bridge(4) rules are created using the [[http://​man.openbsd.org/​ifconfig|ifconfig(8)]] command. Example:
 +
 +<sxh pf>
 +ifconfig bridge0 rule pass in on fxp0 src 0:​de:​ad:​be:​ef:​0 tag USER1
 +</​sxh>​
 +
 +And then in pf.conf:
 +<sxh pf>
 +pass in on fxp0 tagged USER1
 +</​sxh>​
 +
 +==== DQS PF.CONF example using TAGs ====
 +
 +<sxh pf>
 +vim /​etc/​pf.conf
 +#       ​$OpenBSD:​ pf.conf,v 1.54 2014/08/23 05:49:42 deraadt Exp $
 +#
 +# See pf.conf(5) and /​etc/​examples/​pf.conf
 +# Created by: DQS CONSULTORIA E TREINAMENTOS
 +# Date: 23/08/2016
 +# Modified date: 29/08/2016 by: DQS CONSULTORIA E TREINAMENTOS
 +# Email: douglas.q.santos@gmail.com
 +
 +### MACROS INTERFACES
 +wan_if ​ = "​em0" ​ # 10.0.2.15 -> NAT Via Modem
 +wan2_if = "​em1" ​ # 10.0.3.15 -> NAT Via Modem
 +dmz_if ​ = "​em2" ​ # 192.168.56.180
 +lan_if ​ = "​em3" ​ # 192.168.25.180
 +all_if ​ = "​{"​ $wan_if $wan2_if $dmz_if $lan_if "​}"​
 +
 +### MACROS NETWORKS
 +net_dmz ​ = "{ 192.168.56.0/​24 }"
 +net_lan ​ = "{ 192.168.25.0/​24 }"
 +net_wan ​ = "{ 10.0.2.0/24 }"
 +net_wan2 = "{ 10.0.3.0/24 }"
 +
 +### SETTING UP THE RUNTIME OPTIONS
 +set skip on lo
 +set block-policy drop
 +set loginterface egress
 +set state-policy floating
 +
 +### NORMALIZING INCOMING PACKETS
 +match in all scrub (no-df random-id max-mss 1440)
 +
 +### DEFAULT POLICY
 +block all
 +#pass all
 +
 +### ANTISPOOF
 +antispoof for $all_if inet
 +
 +# ALLOW SSH TO FW VIA DMZ
 +pass in on $dmz_if inet proto tcp from any to $dmz_if port 22022
 +
 +# ICMP FROM FW TO ANY
 +pass out inet proto icmp from { $wan_if $dmz_if $lan_if } tag FW_ICMP
 +
 +# ICMP FROM LAN TO ANY
 +pass in on $lan_if inet proto icmp from $lan_if:​network tag LAN_ICMP
 +
 +# DNS VIA UDP FROM LAN TO ANY
 +pass in  on $lan_if inet proto udp from $lan_if:​network to port 53 tag LAN_DNS_UDP
 +
 +# DNS VIA UDP FROM WAN TO ANY
 +pass out on $wan_if inet proto udp from $wan_if to port 53 tag FW_DNS_UDP
 +
 +# HTTP FROM LAN TP ANY
 +pass in on $lan_if inet proto tcp from $lan_if:​network to port 80 tag LAN_HTTP
 +
 +# FTP FROM LAN AND DMZ TO ANY
 +anchor "​ftp-proxy/​*"​
 +pass in on $lan_if proto tcp to port ftp divert-to 127.0.0.1 port 8021 tag FTP_PROXY
 +pass in on $dmz_if proto tcp to port ftp divert-to 127.0.0.1 port 8021 tag FTP_PROXY
 +pass out inet proto tcp from (self) to any port ftp tag FTP_PROXY
 +
 +##### POLICY ENFORCEMENT -- PASS/BLOCK BASED ON THE DEFINIED FIREWALL POLICY.
 +
 +### LAN ###
 +
 +# ALLOW ICMP
 +pass in quick on $lan_if tagged LAN_ICMP
 +
 +# ALLOW DNS VIA UDP
 +pass in quick on $lan_if tagged LAN_DNS_UDP
 +
 +# ALLOW HTTP VIA TCP
 +pass in quick on $lan_if tagged LAN_HTTP
 +
 +### FW ###
 +
 +# ALLOW ICMP
 +pass out quick on $wan_if tagged FW_ICMP
 +
 +# ALLOW DNS VIA UDP
 +pass out quick on $wan_if tagged FW_DNS_UDP
 +
 +# ALLOW FTP FROM LAN, DMZ TO ANY
 +pass out quick on $wan_if tagged FTP_PROXY
 +pass in  quick on $wan_if tagged FTP_PROXY
 +
 +### NAT ###
 +
 +# NAT LAN AND DMZ
 +pass out on $wan_if inet from { $lan_if:​network,​ $dmz_if:​network } to any nat-to ($wan_if)
 +</​sxh>​
 +
 +====== Additional topics ======
 +
 +===== Logging =====
 +
 +==== Introduction ====
 +
 +When a packet is logged by PF, a copy of the packet header is sent to a [[http://​man.openbsd.org/​pflog|pflog(4)]] interface along with some additional data such as the interface the packet was transiting, the action that PF took (pass or block), etc. The pflog(4) interface allows user-space applications to receive PF's logging data from the kernel. If PF is enabled when the system is booted, the [[http://​man.openbsd.org/​pflogd|pflogd(8)]] daemon is started. By default, [[http://​man.openbsd.org/​pflogd|pflogd(8)]] listens on the **pflog0** interface and writes all logged data to the /​var/​log/​pflog file.
 +
 +==== Logging packets ====
 +
 +In order to log packets passing through PF, the **log** keyword must be used. The **log** keyword causes all packets that match the rule to be logged. In the case where the rule is [[openbsd_pf_users_guide_en#​keeping_state|creating state]], only the first packet seen (the one that causes the state to be created) will be logged.
 +
 +The options that can be given to the **log** keyword are:
 +
 +  * **all**
 +    * Causes all matching packets, not just the initial packet, to be logged. Useful for rules that create state.
 +  * **to pflogN**
 +    * Causes all matching packets to be logged to the specified [[http://​man.openbsd.org/​OpenBSD-current/​man4/​pflog.4|pflog(4)]] interface. For example, when using [[http://​man.openbsd.org/​spamlogd|spamlogd(8)]],​ all SMTP traffic can be logged to a dedicated [[http://​man.openbsd.org/​OpenBSD-current/​man4/​pflog.4|pflog(4)]] interface by PF. The [[http://​man.openbsd.org/​spamlogd|spamlogd(8)]] daemon can then be told to listen on that interface. This keeps the main PF logfile clean of SMTP traffic which otherwise would not need to be logged. Use [[http://​man.openbsd.org/​ifconfig|ifconfig(8)]] to create [[http://​man.openbsd.org/​OpenBSD-current/​man4/​pflog.4|pflog(4)]] interfaces. The default log interface **pflog0** is created automatically.
 +  * **user**
 +    * Causes the user id and group id that owns the socket that the packet is sourced from/​destined to (whichever socket is local) to be logged along with the standard log information.
 +
 +Options are given in parenthesis after the log keyword; multiple options can be separated by a comma or space.
 +<sxh pf>
 +pass in log (all, to pflog1) on $ext_if inet proto tcp to $ext_if port 22 keep state
 +</​sxh>​
 +
 +==== Reading a log file ====
 +
 +The log file written by pflogd is in binary format and cannot be read using a text editor. [[http://​man.openbsd.org/​tcpdump|tcpdump(8)]] must be used instead.
 +
 +To view the log file:
 +<sxh pf>
 +tcpdump -n -e -ttt -r /​var/​log/​pflog
 +</​sxh>​
 +
 +Note that using [[http://​man.openbsd.org/​tcpdump|tcpdump(8)]] to watch the pflog file does not give a real-time display. A real-time display of logged packets is achieved by using the pflog0 interface:
 +<sxh pf>
 +tcpdump -n -e -ttt -i pflog0
 +</​sxh>​
 +
 +**NOTE:** When examining the logs, special care should be taken with tcpdump'​s verbose protocol decoding (activated via the **-v** command line option). tcpdump'​s protocol decoders do not have a perfect security history. At least in theory, a delayed attack could be possible via the partial packet payloads recorded by the logging device. It is recommended practice to move the log files off of the firewall machine before examining them in this way.
 +
 +Additional care should also be taken to secure access to the logs. By default, pflogd will record 160 bytes of the packet in the log file. Access to the logs could provide partial access to sensitive packet payloads.
 +
 +==== Filtering log output ====
 +
 +Because pflogd logs in tcpdump binary format, the full range of tcpdump features can be used when reviewing the logs. For example, to only see packets that match a certain port:
 +<sxh pf>
 +tcpdump -n -e -ttt -r /​var/​log/​pflog port 80
 +</​sxh>​
 +
 +This can be further refined by limiting the display of packets to a certain host and port combination:​
 +<sxh pf>
 +tcpdump -n -e -ttt -r /​var/​log/​pflog port 80 and host 192.168.1.3
 +</​sxh>​
 +
 +The same idea can be applied when reading from the pflog0 interface:
 +<sxh pf>
 +tcpdump -n -e -ttt -i pflog0 host 192.168.4.2
 +</​sxh>​
 +
 +Note that this has no impact on which packets are logged to the pflogd log file; the above commands only display packets as they are being logged.
 +
 +In addition to using the standard [[http://​man.openbsd.org/​tcpdump.8|tcpdump(8)]] filter rules, the tcpdump filter language has been extended for reading pflogd output:
 +
 +  * **ip** - address family is IPv4.
 +  * **ip6** - address family is IPv6.
 +  * **on int** - packet passed through the interface int.
 +  * **ifname int** - same as on int.
 +  * **ruleset name** - the [[openbsd_pf_users_guide_en#​anchors|ruleset/​anchor]] that the packet was matched in.
 +  * **rulenum num** - the filter rule that the packet matched was rule number num.
 +  * **action act** - the action taken on the packet. Possible actions are **pass** and **block**.
 +  * **reason res** - the reason that **action** was taken. Possible reasons are **match, bad-offset, fragment, short, normalize, memory, bad-timestamp,​ congestion, ip-option, proto-cksum,​ state-mismatch,​ state-insert,​ state-limit,​ src-limit** and **synproxy**.
 +  * **inbound** - packet was inbound.
 +  * **outbound** - packet was outbound.
 +
 +Example:
 +<sxh pf>
 +tcpdump -n -e -ttt -i pflog0 inbound and action block and on wi0
 +</​sxh>​
 +
 +This display the log, in real-time, of inbound packets that were blocked on the wi0 interface.
 +
 +==== Packet logging through syslog ====
 +
 +In many situations it is desirable to have the firewall logs available in ASCII format and/or to send them to a remote logging server. All this can be accomplished with a small shell script, some minor changes of the OpenBSD configuration files, and [[http://​man.openbsd.org/​syslogd|syslogd(8)]],​ the logging daemon. syslogd logs in ASCII and is also able to log to a remote logging server.
 +
 +Create the following script as **/​etc/​pflogrotate**.
 +<sxh pf>
 +#!/bin/sh
 +PFLOG=/​var/​log/​pflog
 +FILE=/​var/​log/​pflog5min.$(date "​+%Y%m%d%H%M"​)
 +
 +pkill -ALRM -u root -U root -t - -x pflogd
 +if [ -r $PFLOG ] && [ $(stat -f %z $PFLOG) -gt 24 ]; then
 +   mv $PFLOG $FILE
 +   pkill -HUP -u root -U root -t - -x pflogd
 +   ​tcpdump -n -e -s 160 -ttt -r $FILE | logger -t pf -p local0.info
 +   rm $FILE
 +fi
 +</​sxh>​
 +
 +Edit root's cron table:
 +<sxh pf>
 +crontab -u root -e
 +</​sxh>​
 +
 +Add the following two lines:
 +<sxh pf>
 +# rotate pf log file every 5 minutes
 +0-59/5 *       ​* ​      ​* ​      ​* ​      /​bin/​sh /​etc/​pflogrotate
 +</​sxh>​
 +
 +Add the following line to **/​etc/​syslog.conf**:​
 +<sxh pf>
 +local0.info ​    /​var/​log/​pflog.txt
 +</​sxh>​
 +
 +If you also want to log to a remote log server, add the line:
 +<sxh pf>
 +local0.info ​    ​@syslogger
 +</​sxh>​
 +
 +Make sure host syslogger has been defined in the [[http://​man.openbsd.org/​hosts|hosts(5)]] file.
 +
 +Create the file /​var/​log/​pflog.txt to allow syslog to log to that file, and give it the same permissions as the pflog file.
 +<sxh pf>
 +touch /​var/​log/​pflog.txt
 +chmod 600 /​var/​log/​pflog.txt
 +</​sxh>​
 +
 +Make syslogd notice the changes by restarting it:
 +<sxh pf>
 +kill -HUP $(cat /​var/​run/​syslog.pid)
 +</​sxh>​
 +
 +All logged packets are now sent to **/​var/​log/​pflog.txt**. If the second line is added, they are sent to the remote logging host **syslogger** as well.
 +
 +The script **/​etc/​pflogrotate** now processes and then deletes **/​var/​log/​pflog** so rotation of pflog by [[http://​man.openbsd.org/​newsyslog|newsyslog(8)]] is no longer necessary and should be disabled. However, **/​var/​log/​pflog.txt** replaces **/​var/​log/​pflog** and rotation of it should be activated. Change **/​etc/​newsyslog.conf** as follows:
 +
 +<sxh pf>
 +#/​var/​log/​pflog ​      ​600 ​   3    250    *    ZB "pkill -HUP -u root -U root -t - -x pflogd"​
 +/​var/​log/​pflog.txt ​   600    7    *      24
 +</​sxh>​
 +
 +PF will now log in ASCII to **/​var/​log/​pflog.txt**. If so configured in **/​etc/​syslog.conf**,​ it will also log to a remote server. The logging is not immediate but it can take up to about 5-6 minutes (the cron job interval) before the logged packets appear in the file.
 +
 +===== Performance =====
 +
 +**"How much bandwidth can PF handle?"​ **
 +
 +** "How much computer do I need to handle my Internet connection?"​**
 +
 +There are no easy answers to those questions. For a simple home connection, most hardware should be more than enough. Other applications may require a faster machine with high-end NICs. The real question is not the number of bits per second, but rather the number of packets per second and the complexity of the ruleset.
 +
 +PF performance is determined by several variables:
 +  * Number of packets per second. Almost the same amount of processing needs to be done on a packet with 1500 byte payload as for a packet with a one byte payload. The number of packets per second determines the number of times the state table and, in case of no match there, filter rules have to be evaluated every second, determining the effective demand on the system.
 +  * Performance of your system bus. The ISA bus has a maximum bandwidth of 8MB/sec, and when the processor is accessing it, it has to slow itself to the effective speed of a 80286 running at 8MHz, no matter how fast the processor really is. The PCI bus has a much greater effective bandwidth, and has less impact on the processor, and of course, PCIe offers even more performance.
 +  * Efficiency of your network card. Some network adapters are just more efficient than others. Older [[http://​man.openbsd.org/​rl|rl(4)]] Realtek 8139-based cards tend to be relatively poor performers (newer [[http://​man.openbsd.org/​re|re(4)]]-based Realtek cards are a bit better), while Intel 21143 ([[http://​man.openbsd.org/​dc|dc(4)]]) based cards tend to perform very well. For maximum performance,​ consider using gigabit or ten gigabit ethernet cards, as they have much more advanced buffering.
 +  * Complexity and design of your ruleset. The more complex your ruleset, the slower it is. The more packets that are filtered by keep state and quick rules, the better the performance. The more lines that have to be evaluated for each packet, the lower the performance.
 +  * Barely worth mentioning: CPU and RAM. As PF is a kernel-based process, it will not use swap space. So, if you have enough RAM, it runs, if not, it panics due to [[http://​man.openbsd.org/​pool|pool(9)]] exhaustion. Huge amounts of RAM are not needed -- 32MB should be plenty for close to 30,000 states, which is a lot of states for a small office or home application. Most users will find a "​recycled"​ computer more than enough for a PF system -- a 300MHz system will move a large number of packets rapidly, at least if backed up with good NICs and a good ruleset.
 +
 +==== Will multiple processors help? ====
 +
 +PF will only use one processor, so multiple processors (or multiple cores) WILL NOT improve PF performance. However, under some circumstances,​ running the SMP version of OpenBSD (**bsd.mp**) instead of bsd will give better performance due to differences in how interrupt handling is done. IF you are seeing performance problems, experiment with this, most users will never hit any limits to worry about it.
 +
 +==== Are there any benchmarks? ====
 +
 +People often ask for PF benchmarks. The only benchmark that counts is your system performance in your environment. A benchmark that doesn'​t replicate your environment will not properly help you plan your firewall system. The best course of action is to benchmark PF for yourself under the same, or as close as possible to, network conditions that the actual firewall would experience running on the same hardware the firewall would use.
 +PF is used in some very large, high-traffic applications,​ and the developers are "power users" of PF. Odds are it will do very well for you.
 +
 +===== Issues with FTP =====
 +
 +==== FTP modes ====
 +
 +FTP is a protocol that dates back to when the internet was a small, friendly collection of computers and everyone knew everyone else. At that time, the need for filtering or tight security wasn't necessary. FTP wasn't designed for filtering, for passing through firewalls, or for working with NAT.
 +
 +You can use FTP in one of two ways: passive or active. Generally, the choice of active or passive is made to determine who has the problem with firewalling. Realistically,​ you will have to support both to have happy users.
 +
 +With active FTP, when a user connects to a remote FTP server and requests information or a file, the FTP server makes a new connection back to the client to transfer the requested data. This is called the **data connection**. To start, the FTP client chooses a random port to receive the data connection. The client sends the port number it chose to the FTP server and listens for an incoming connection on that port. The FTP server then initiates a connection to the client'​s address at the chosen port and transfers the data. This is a problem for users attempting to gain access to FTP servers from behind a NAT gateway. Because of how NAT works, the FTP server initiates the data connection by connecting to the external address of the NAT gateway on the chosen port. The NAT machine will receive this, but because it has no mapping for the packet in its state table, it will drop the packet and won't deliver it to the client.
 +
 +With passive mode FTP (the default mode with OpenBSD'​s [[http://​man.openbsd.org/​ftp|ftp(1)]] client), the client requests that the server pick a random port to listen on for the data connection. The server informs the client of the port it has chosen, and the client connects to this port to transfer the data. Unfortunately,​ this is not always possible or desirable because of the possibility of a firewall in front of the FTP server blocking the incoming data connection. OpenBSD'​s [[http://​man.openbsd.org/​ftp|ftp(1)]] uses passive mode by default. To force active mode FTP, use the **-A** flag to **ftp**, or set passive mode to "​off"​ by issuing the command "​**passive off**" at the "​**ftp>​**"​ prompt.
 +
 +==== FTP client behind the firewall ====
 +
 +**NOTE:** If you are using OpenBSD box into Virtualbox remember to enable the promiscuo mode in the interfaces otherwise you will access the ftp from the OpenBSD box but the clientes will not.
 +
 +As indicated earlier, FTP does not go through NAT and firewalls very well.
 +
 +Packet Filter provides a solution for this situation by diverting FTP traffic through an FTP proxy server. This process acts to "​guide"​ your FTP traffic through the NAT gateway/​firewall,​ by actively adding needed rules to PF system and removing them when done, by means of the PF [[openbsd_pf_users_guide_en#​anchors|anchors]] system. The FTP proxy used by PF is [[http://​man.openbsd.org/​ftp-proxy|ftp-proxy(8)]].
 +
 +To activate it, put something like this early in the rules section of **pf.conf**:​
 +<sxh pf>
 +pass in quick on $int_if inet proto tcp to port 21 divert-to 127.0.0.1 port 8021
 +</​sxh>​
 +
 +This diverts FTP from your clients to the [[http://​man.openbsd.org/​ftp-proxy|ftp-proxy(8)]] program, which is listening on your machine to port 8021.
 +
 +You also need an anchor in the rules section:
 +<sxh pf>
 +anchor "​ftp-proxy/​*"​
 +</​sxh>​
 +
 +The proxy server has to be started and running on the OpenBSD box.
 +<sxh pf>
 +rcctl enable ftpproxy
 +rcctl start  ftpproxy
 +</​sxh>​
 +
 +The ftp-proxy utility listens on port 8021, the same port the above divert-to statement is sending FTP traffic to.
 +
 +To support active mode connections from certain (fussy) clients, you may need the -r switch on [[http://​man.openbsd.org/​ftp-proxy|ftp-proxy(8)]].
 +
 +In some cases we can pass the debug option with the verbose option so we have a much better output in the log file /​var/​log/​daemon.log
 +
 +To enable it put in /​etc/​rc.conf.local
 +<sxh bash>
 +vim /​etc/​rc.conf.local
 +ftpproxy_flags="​-D7 -v"
 +</​sxh>​
 +
 +After that just restart the ftpproxy
 +<sxh bash>
 +rcctl restart ftpproxy
 +</​sxh>​
 +==== PF "​self-protecting"​ an FTP server ====
 +
 +In this case, PF is running on the FTP server itself rather than a dedicated firewall computer. When servicing a passive FTP connection, FTP will use a randomly chosen, high TCP port for incoming data. By default, OpenBSD'​s native [[http://​man.openbsd.org/​ftpd|ftpd(8)]] uses the range 49152 to 65535. Obviously, these must be passed through the filter rules, along with port 21 (the FTP control port):
 +<sxh pf>
 +pass in on $ext_if proto tcp to port 21
 +pass in on $ext_if proto tcp to port > 49151
 +</​sxh>​
 +
 +You can tighten up that range of ports considerably if you want to. In the case of [[http://​man.openbsd.org/​ftpd|ftpd(8)]],​ that is done using the [[http://​man.openbsd.org/​sysctl|sysctl(8)]] variables **net.inet.ip.porthifirst** and **net.inet.ip.porthilast**.
 +
 +==== FTP server protected by an external PF firewall running NAT ====
 +
 +In this case, the firewall must redirect traffic to the FTP server in addition to not blocking the required ports. In order to accomplish this, we turn again to [[http://​man.openbsd.org/​ftp-proxy|ftp-proxy(8)]].
 +
 +[[http://​man.openbsd.org/​ftp-proxy|ftp-proxy(8)]] can be run in a mode that causes it to forward all FTP connections to a specific FTP server. Basically, we'll set up the proxy to listen on port 21 of the firewall and forward all connections to the backend server.
 +<sxh pf>
 +rcctl set ftpproxy flags -R 10.10.10.1 -p 21 -b 192.168.0.1
 +</​sxh>​
 +
 +Here 10.10.10.1 is the IP address of the actual FTP server, 21 is the port we want ftp-proxy(8) to listen on, and 192.168.0.1 is the address on the firewall that we want the proxy to bind to.
 +
 +Now for the pf.conf rules:
 +<sxh pf>
 +ext_ip = "​192.168.0.1"​
 +ftp_ip = "​10.10.10.1"​
 +match out on $ext_if inet from $int_if nat-to ($ext_if)
 +anchor "​ftp-proxy/​*"​
 +pass in  on $ext_if inet proto tcp to $ext_ip port 21
 +pass out on $int_if inet proto tcp to $ftp_ip port 21 user proxy
 +</​sxh>​
 +
 +Here we allow the connection inbound to port 21 on the external interface, as well as the corresponding outbound connection to the FTP server. The "user proxy" addition to the outbound rule ensures that only connections initiated by [[http://​man.openbsd.org/​ftp-proxy|ftp-proxy(8)]] are permitted.
 +
 +Note that if you want to run [[http://​man.openbsd.org/​ftp-proxy|ftp-proxy(8)]] to protect an FTP server as well as allow clients to FTP out from behind the firewall, two instances of ftp-proxy will be required.
 +
 +==== More information on FTP ====
 +
 +More information on filtering FTP and how FTP works in general can be found in this whitepaper:
 +  * [[http://​www.pintday.org/​whitepapers/​ftp-review.shtml|FTP Reviewed]]
 +
 +==== Proxying TFTP ====
 +
 +Trivial File Transfer Protocol (TFTP) suffers from some of the same limitations as FTP does when it comes to passing through a firewall. Luckily, PF has a helper proxy for TFTP called [[http://​man.openbsd.org/​tftp-proxy|tftp-proxy(8)]].
 +
 +[[http://​man.openbsd.org/​tftp-proxy|tftp-proxy(8)]] is setup in much the same way as [[http://​man.openbsd.org/​ftp-proxy|ftp-proxy(8)]] was in the [[openbsd_pf_users_guide_en#​ftp_client_behind_the_firewall|FTP client behind the firewall]] section above.
 +<sxh pf>
 +match out on $ext_if inet from $int_if nat-to ($ext_if)
 +anchor "​tftp-proxy/​*"​
 +pass in quick on $int_if inet proto udp from $int_if to port tftp \
 +    divert-to 127.0.0.1 port 6969
 +anchor "​tftp-proxy/​*"​
 +</​sxh>​
 +
 +The rules above allow TFTP outbound from the internal network to TFTP servers on the external network.
 +
 +The last step is to enable tftp-proxy in [[http://​man.openbsd.org/​inetd.conf|inetd.conf(5)]] so that it listens on the same port that the divert-to rule specified above, in this case 6969.
 +<sxh pf>
 +127.0.0.1:​6969 ​ dgram   ​udp ​  ​wait ​ root  /​usr/​libexec/​tftp-proxy tftp-proxy
 +</​sxh>​
 +
 +Unlike [[http://​man.openbsd.org/​ftp-proxy|ftp-proxy(8)]],​ [[http://​man.openbsd.org/​tftp-proxy|tftp-proxy(8)]] is spawned from inetd.
 +
 +===== User Shell for Authenticating Gateways (authpf) =====
 +
 +==== Introduction ====
 +
 +The [[http://​man.openbsd.org/​authpf|authpf(8)]] utility is a user shell for authenticating gateways. An authenticating gateway is just like a regular network gateway (also known as a router) except that users must first authenticate themselves to it before their traffic is allowed to pass through. When a user's shell is set to **/​usr/​sbin/​authpf** and they log in using SSH, authpf will make the necessary changes to the active [[http://​man.openbsd.org/​pf|pf(4)]] ruleset so that the user's traffic is passed through the filter and/or translated using NAT/​redirection. Once the user logs out or their session is disconnected,​ authpf will remove any rules loaded for the user and kill any stateful connections the user has open. Because of this, the ability of the user to pass traffic through the gateway only exists while the user keeps their SSH session open.
 +
 +A user's rules are loaded into a unique [[openbsd_pf_users_guide_en#​anchors|anchor]] point by authpf. The anchor is named by combining the username and the authpf process-id in the username(PID) format. Each user's anchor is stored within the **authpf** [[openbsd_pf_users_guide_en#​anchors|anchor]] which is in turn anchored to the main ruleset. The fully qualified anchor path then becomes:
 +<sxh pf>
 +main_ruleset/​authpf/​username(PID)
 +</​sxh>​
 +
 +The rules that authpf loads can be configured on a per-user or global basis.
 +
 +Example uses of authpf include:
 +
 +  * Requiring users to authenticate before allowing internet access.
 +  * Granting certain users -- such as administrators -- access to restricted parts of the network.
 +  * Allowing only known users to access the rest of the network or internet from a wireless network segment.
 +  * Allowing workers from home or on the road access to resources on the company network. Users outside the office can not only open access to the company network, but can also be redirected to particular resources (their own desktop, for example) based on the username they authenticate with.
 +  * In a setting such as a library or other place with public Internet terminals, PF may be configured to allow limited Internet access to guest users. Registered users can then be provided with complete access.
 +
 +The authpf tool logs the username and IP of each user who authenticates successfully,​ as well as the start and end times of their login session via [[http://​man.openbsd.org/​syslogd|syslogd(8)]]. By using this information,​ an administrator can determine who was logged in when and also make users accountable for their network traffic.
 +
 +==== Configuration ====
 +
 +The basic steps needed to configure authpf are outlined here. For a complete description of authpf configuration,​ please refer to the [[http://​man.openbsd.org/​authpf|man page]].
 +
 +=== Enabling authpf ===
 +
 +The authpf utility will not run if the **/​etc/​authpf/​authpf.conf** config file is not present. The file may be empty, but unless it is present, authpf will exit immediately after a user authenticates successfully.
 +
 +The following configuration directives can be placed in **authpf.conf**:​
 +  * anchor=name - Use the specified anchor name instead of authpf
 +  * table=name - Use the specified table name instead of authpf_users
 +
 +=== Linking authpf into the main ruleset ===
 +
 +You can link authpf into the main ruleset by using an anchor rule:
 +<sxh pf>
 +anchor "​authpf/​*"​
 +</​sxh>​
 +
 +Wherever the **anchor** rule is placed within the ruleset is where PF will branch off from the main ruleset to evaluate the authpf rules.
 +
 +=== Configuring loaded rules ===
 +
 +The authpf rules can be loaded from one of these two files:
 +  * /​etc/​authpf/​users/​$USER/​authpf.rules
 +  * /​etc/​authpf/​authpf.rules
 +
 +The first file contains rules that are only loaded when the user $USER (which is replaced with the user's username) logs in. The per-user rule configuration is used when a specific user, such as an administrator,​ requires a set of rules that is different than the default set. The second file contains the default rules which are loaded for any user that doesn'​t have their own authpf.rules file. If the user-specific file exists, it will override the default file. At least one of the files must exist or authpf will not run.
 +
 +Rules have the same syntax as in any other PF ruleset with one exception: authpf allows for the use of two predefined macros:
 +
 +  * **$user_ip** - the IP address of the logged in user
 +  * **$user_id** - the username of the logged in user
 +
 +It's recommended practice to use the **$user_ip** macro to only permit traffic through the gateway from the authenticated user's computer.
 +
 +In addition to the $user_ip macro, authpf will make use of the **authpf_users** table (if it exists) for storing the IP addresses of all authenticated users. Be sure to define the table before using it:
 +<sxh pf>
 +table <​authpf_users>​ persist
 +pass in on $ext_if proto tcp from <​authpf_users>​ to port smtp
 +</​sxh>​
 +
 +This table should only be used in rules that are meant to apply to all authenticated users.
 +
 +=== Access Control Lists ===
 +
 +Users can be prevented from using authpf by creating a file in the **/​etc/​authpf/​banned/​** directory and naming it after their username. The contents of the file will be displayed to the user before authpf disconnects them. This provides a handy way to notify the user of why they'​re disallowed access and who to contact to have their access restored.
 +
 +Conversely, it's also possible to allow only specific users access by placing usernames in the **/​etc/​authpf/​authpf.allow** file. If the **/​etc/​authpf/​authpf.allow** file does not exist or "​*"​ is entered into the file, then authpf will permit access to any user who successfully logs in via SSH as long as they are not explicitly banned.
 +
 +If authpf is unable to determine if a username is allowed or denied, it will print a brief message and then disconnect the user. An entry in **/​etc/​authpf/​banned/​** always overrides an entry in **/​etc/​authpf/​authpf.allow**.
 +
 +=== Displaying a login message ===
 +
 +Whenever a user successfully authenticates to authpf, a greeting is printed which indicates that the user is authenticated.
 +<sxh pf>
 +Hello charlie. You are authenticated from host "​198.51.100.10"​
 +</​sxh>​
 +
 +This message can be supplemented by putting a custom message in **/​etc/​authpf/​authpf.message**. The contents of this file will be displayed after the default welcome message.
 +
 +=== Assigning authpf as a user's shell ===
 +
 +In order for authpf to work, it must be assigned as the user's login shell. When the user successfully authenticates to [[http://​man.openbsd.org/​sshd|sshd(8)]],​ authpf will be executed as the user's shell. It will then check if the user is allowed to use authpf, load the rules from the appropriate file, etc.
 +There are a couple ways of assigning authpf as a user's shell:
 +
 +  - Manually for each user using [[http://​man.openbsd.org/​chsh|chsh(1)]],​ [[http://​man.openbsd.org/​vipw|vipw(8)]],​ [[http://​man.openbsd.org/​useradd|useradd(8)]],​ [[http://​man.openbsd.org/​usermod|usermod(8)]],​ etc.
 +  - By assigning users to a login class and changing the class'​s **shell** option in [[http://​man.openbsd.org/​login.conf|login.conf(5)]].
 +
 +==== Creating an authpf login class ====
 +
 +When using authpf on a system that has regular user accounts and authpf user accounts, it can be beneficial to use a separate login class for the authpf users. This allows for certain changes to those accounts to be made on a global basis, and also allows different policies to be placed on regular accounts and authpf accounts. Some examples of what policies can be set:
 +
 +  * **shell** - Specify a user's login shell. This can be used to force a user's shell to authpf regardless of the entry in the [[http://​man.openbsd.org/​passwd|passwd(5)]] database.
 +  * **welcome** - Specify which [[http://​man.openbsd.org/​motd|motd(5)]] file to display when a user logs in. This is useful for displaying messages that are relevant only to authpf users.
 +
 +Login classes are created in the [[http://​man.openbsd.org/​login.conf|login.conf(5)]] file. OpenBSD comes with an authpf login class defined as:
 +<sxh pf>
 +authpf:\
 +    :​welcome=/​etc/​motd.authpf:​\
 +    :​shell=/​usr/​sbin/​authpf:​\
 +    :​tc=default:​
 +</​sxh>​
 +
 +Users are assigned to a login class by editing the class field of the user's [[http://​man.openbsd.org/​passwd|passwd(5)]] database entry. One way to do this is with the [[http://​man.openbsd.org/​chsh|chsh(1)]] command.
 +
 +==== Seeing who is logged in ====
 +
 +Once a user has successfully logged in and authpf has adjusted the PF rules, authpf changes its process title to indicate the username and IP address of the logged in user:
 +<sxh pf>
 +ps -ax | grep authpf
 +23664 p0  Is+     ​0:​00.11 -authpf: charlie@192.168.1.3 (authpf)
 +</​sxh>​
 +
 +Here the user charlie is logged in from the machine 192.168.1.3. By sending a SIGTERM signal to the authpf process, the user can be forcefully logged out. Any rules loaded for the user will be removed and any stateful connections the user has open will be killed.
 +<sxh pf>
 +kill -TERM 23664
 +</​sxh>​
 +
 +==== Example ====
 +
 +The authpf tool is being used on an OpenBSD gateway to authenticate users on a wireless network which is part of a larger campus network. Once a user has authenticated,​ assuming they'​re not on the banned list, they will be permitted to SSH out and to browse the web (including secure websites) in addition to accessing either of the campus DNS servers.
 +
 +Start with creating an empty /​etc/​authpf/​authpf.conf. It needs to be there for authpf to work, but doesn'​t actually need any content.
 +<sxh bash>
 +touch /​etc/​authpf/​authpf.conf
 +</​sxh>​
 +
 +Make sure the usershell of the users that will connect into pf have the following shell: /​usr/​sbin/​authpf
 +<sxh bash>
 +usermod -s /​usr/​sbin/​authpf username
 +</​sxh>​
 +
 +The **/​etc/​authpf/​authpf.rules** file contains the following rules:
 +<sxh pf>
 +wifi_if = "​wi0"​
 +
 +pass in quick on $wifi_if \
 +   proto tcp from $user_ip to any port { ssh, http, https }
 +</​sxh>​
 +
 +The administrative user charlie needs to be able to access the campus SMTP and POP3 servers in addition to surfing the web and using SSH. The following rules are set up in **/​etc/​authpf/​users/​charlie/​authpf.rules**:​
 +
 +<sxh pf>
 +wifi_if = "​wi0"​
 +smtp_server = "​10.0.1.50"​
 +pop3_server = "​10.0.1.51"​
 +
 +pass in quick on $wifi_if \
 +   proto tcp from $user_ip to $smtp_server port smtp
 +pass in quick on $wifi_if \
 +   proto tcp from $user_ip to $pop3_server port pop3
 +pass in quick on $wifi_if \
 +   proto tcp from $user_ip to port { ssh, http, https }
 +</​sxh>​
 +
 +The main ruleset<​nowiki>​ -- </​nowiki>​located in **/​etc/​pf.conf** <​nowiki>​--</​nowiki>​ is set up as follows:
 +<sxh pf>
 +wifi_if = "​wi0"​
 +ext_if ​ = "​fxp0"​
 +dns_servers = "{ 10.0.1.56, 10.0.2.56 }"
 +
 +table <​authpf_users>​ persist
 +
 +block drop all
 +
 +pass out quick on $ext_if \
 +   inet proto { tcp, udp, icmp } from { $wifi_if:​network,​ $ext_if }
 +
 +pass in quick on $wifi_if \
 +   inet proto tcp from $wifi_if:​network to $wifi_if port ssh
 +
 +pass in quick on $wifi_if \
 +   inet proto { tcp, udp } from <​authpf_users>​ to $dns_servers port domain
 +
 +anchor "​authpf/​*"​ in on $wifi_if
 +</​sxh>​
 +
 +The ruleset is very simple and does the following:
 +  * Block everything (default deny).
 +  * Pass outgoing TCP, UDP and ICMP traffic on the external interface from the wireless network and from the gateway itself.
 +  * Pass incoming SSH traffic from the wireless network destined for the gateway itself. This rule is necessary to permit users to log in.
 +  * Pass incoming DNS requests from all authenticated authpf users to the campus DNS servers.
 +  * Create the anchor point "​authpf"​ for incoming traffic on the wireless interface.
 +
 +The idea behind the main ruleset is to block everything and allow the least amount of traffic through as possible. Traffic is free to flow out on the external interface but is blocked from entering the wireless interface by the default deny policy. Once a user authenticates,​ their traffic is permitted to pass in on the wireless interface and to then flow through the gateway into the rest of the network. The quick keyword is used throughout so that PF doesn'​t have to evaluate each named ruleset when a new connection passes through the gateway.
 +
 +===== Firewall Redundancy (CARP and pfsync) =====
 +
 +==== Introduction to CARP ====
 +
 +CARP is the Common Address Redundancy Protocol. Its primary purpose is to allow multiple hosts on the same network segment to share an IP address. CARP is a secure, free alternative to the [[http://​www.ietf.org/​rfc/​rfc3768.txt|Virtual Router Redundancy Protocol]] (VRRP) and the [[http://​www.ietf.org/​rfc/​rfc2281.txt|Hot Standby Router Protocol]] (HSRP).
 +
 +CARP works by allowing a group of hosts on the same network segment to share an IP address. This group of hosts is referred to as a "​redundancy group."​ The redundancy group is assigned an IP address that is shared amongst the group members. Within the group, one host is designated the "​master"​ and the rest as "​backups."​ The master host is the one that currently "​holds"​ the shared IP; it responds to any traffic or ARP requests directed towards it. Each host may belong to more than one redundancy group at a time.
 +
 +One common use for CARP is to create a group of redundant firewalls. The virtual IP that is assigned to the redundancy group is configured on client machines as the default gateway. In the event that the master firewall suffers a failure or is taken offline, the IP will move to one of the backup firewalls and service will continue unaffected.
 +
 +CARP supports IPv4 and IPv6.
 +
 +=== CARP operation ===
 +
 +The master host in the group sends regular advertisements to the local network so that the backup hosts know it's still alive. If the backup hosts don't hear an advertisement from the master for a set period of time, then one of them will take over the duties of master (whichever backup host has the lowest configured **advbase** and **advskew** values).
 +
 +It's possible for multiple CARP groups to exist on the same network segment. CARP advertisements contain the Virtual Host ID which allows group members to identify which redundancy group the advertisement belongs to.
 +
 +In order to prevent a malicious user on the network segment from spoofing CARP advertisements,​ each group can be configured with a password. Each CARP packet sent to the group is then protected by an SHA1 HMAC.
 +
 +Since CARP is its own protocol, it should have an explicit pass rule in filter rulesets:
 +<sxh pf>
 +pass out on $carp_dev proto carp keep state
 +</​sxh>​
 +
 +**$carp_dev** should be the physical interface that CARP is communicating over.
 +
 +=== Configuring CARP ===
 +
 +Each redundancy group is represented by a [[http://​man.openbsd.org/​carp|carp(4)]] virtual network interface. As such, CARP is configured using [[http://​man.openbsd.org/​ifconfig|ifconfig(8)]].
 +
 +<sxh bash>
 +ifconfig carpN create
 +ifconfig carpN vhid vhid [pass password] [carpdev carpdev] \
 +   ​[advbase advbase] [advskew advskew] [state state] [group|-group group] \
 +   ​ipaddress netmask mask
 +</​sxh>​
 +
 +  * **carpN**
 +    * The name of the carp(4) virtual interface where N is an integer that represents the interface'​s number (e.g. carp10).
 +  * **vhid**
 +    * The Virtual Host ID. This is a unique number that is used to identify the redundancy group to other nodes in the group, and to distinguish between groups on the same network. Acceptable values are from 1 to 255. This must be the same on all members of the group.
 +  * **password**
 +    * The authentication password to use when talking to other CARP-enabled hosts in this redundancy group. This must be the same on all members of the group.
 +  * **carpdev**
 +    * This optional parameter specifies the physical network interface that belongs to this redundancy group. By default, CARP will try to determine which interface to use by looking for a physical interface that is in the same subnet as the ipaddress and mask combination given to the carp(4) interface.
 +  * **advbase**
 +    * This optional parameter specifies how often, in seconds, to advertise that we're a member of the redundancy group. The default is 1 second. Acceptable values are from 1 to 255.
 +  * **advskew**
 +    * This optional parameter specifies how much to skew the advbase when sending CARP advertisements. By manipulating advskew, the master CARP host can be chosen. The higher the number, the less preferred the host will be when choosing a master. The default is 0. Acceptable values are from 0 to 254.
 +  * **state**
 +    * Force a carp(4) interface into a certain state. Valid states are init, backup, and master.
 +  * **group, -group**
 +    * Add or remove a carp(4) interface to a certain interface group. By default, all carp(4) interfaces are added to the carp group. Each group has a carpdemote counter affecting all carp(4) interfaces belonging to that group. As described below, it can be useful to group certain interfaces together for failover purposes.
 +  * **ipaddress**
 +    * This is the shared IP address assigned to the redundancy group. This address does not have to be in the same subnet as the IP address on the physical interface (if present). This address needs to be the same on all hosts in the group, however.
 +  * **mask**
 +    * The subnet mask of the shared IP.
 +
 +Further CARP behavior can be controlled via [[http://​man.openbsd.org/​sysctl|sysctl(8)]].
 +
 +  * **net.inet.carp.allow**
 +    * Accept incoming CARP packets or not. Default is 1 (yes).
 +  * **net.inet.carp.preempt**
 +    * Allow hosts within a redundancy group that have a better advbase and advskew to preempt the master. In addition, this option also enables failing over a group of interfaces together in the event that one interface goes down. If one physical CARP-enabled interface goes down, CARP will increase the demotion counter, carpdemote, by 1 on interface groups that the carp(4) interface is a member of, in effect causing all group members to fail-over together. net.inet.carp.preempt is 0 (disabled) by default.
 +  * **net.inet.carp.log**
 +    * Log state changes, bad packets and other errors. This value may be between 0 and 7, corresponding with syslog(3) priorities. The default is 2 (state changes only).
 +
 +=== CARP example ===
 +
 +Here is an example CARP configuration:​
 +<sxh bash>
 +sysctl net.inet.carp.allow=1
 +echo '​net.inet.carp.allow=1'​ >> /​etc/​sysctl.conf
 +ifconfig carp1 create
 +ifconfig carp1 vhid 1 pass mekmitasdigoat carpdev em0 advskew 100 10.0.0.1 netmask 255.255.255.0
 +</​sxh>​
 +
 +This sets up the following:
 +  * Enables receipt of CARP packets (this is the default setting).
 +  * Creates a carp(4) interface, carp1.
 +  * Configures carp1 for virtual host #1, enables a password, sets em0 as the interface belonging to the group, and makes this host a backup due to the advskew of 100 (assuming of course that the master is set up with an advskew less than 100). The shared IP assigned to this group is 10.0.0.1/​255.255.255.0.
 +
 +
 +Running ifconfig on carp1 shows the status of the interface.
 +<sxh bash>
 +ifconfig carp1
 +carp1: flags=8802<​UP,​BROADCAST,​SIMPLEX,​MULTICAST>​ mtu 1500
 +     carp: BACKUP carpdev em0 vhid 1 advbase 1 advskew 100
 +     ​groups:​ carp
 +     inet 10.0.0.1 netmask 0xffffff00 broadcast 10.0.0.255
 +</​sxh>​
 +
 +==== Introduction to pfsync ====
 +
 +The [[http://​man.openbsd.org/​pfsync|pfsync(4)]] network interface exposes certain changes made to the [[http://​man.openbsd.org/​pf|pf(4)]] state table. By monitoring this device using [[http://​man.openbsd.org/​tcpdump|tcpdump(8)]],​ state table changes can be observed in real time. In addition, the [[http://​man.openbsd.org/​pfsync|pfsync(4)]] interface can send these state change messages out on the network so that other nodes running PF can merge the changes into their own state tables. Likewise, [[http://​man.openbsd.org/​pfsync|pfsync(4)]] can also listen on the network for incoming messages.
 +
 +=== pfsync operation ===
 +
 +By default, [[http://​man.openbsd.org/​pfsync|pfsync(4)]] does not send or receive state table updates on the network; however, updates can still be monitored using [[http://​man.openbsd.org/​tcpdump|tcpdump(8)]] or other such tools on the local machine.
 +When [[http://​man.openbsd.org/​pfsync|pfsync(4)]] is set up to send and receive updates on the network, the default behavior is to multicast updates out on the local network. All updates are sent without authentication. Best common practice is either:
 +
 +  - Connect the two nodes that will be exchanging updates back-to-back using a crossover cable and use that interface as the syncdev (see below).
 +  - Use the [[http://​man.openbsd.org/​ifconfig|ifconfig(8)]] syncpeer option (see below) so that updates are unicast directly to the peer, then configure [[http://​man.openbsd.org/​ipsec|ipsec(4)]] between the hosts to secure the [[http://​man.openbsd.org/​pfsync|pfsync(4)]] traffic.
 +When updates are being sent and received on the network, pfsync packets should be passed in the filter ruleset:
 +<sxh pf>
 +pass on $sync_if proto pfsync
 +</​sxh>​
 +
 +**$sync_if** should be the physical interface that [[http://​man.openbsd.org/​pfsync|pfsync(4)]] is communicating over.
 +
 +=== Configuring pfsync ===
 +
 +Since pfsync(4) is a virtual network interface, it is configured using [[http://​man.openbsd.org/​ifconfig|ifconfig(8)]].
 +<sxh bash>
 +ifconfig pfsyncN syncdev syncdev [syncpeer syncpeer] [defer|-defer]
 +</​sxh>​
 +
 +  * **pfsyncN**
 +    * The name of the pfsync(4) interface. **pfsync0** exists by default when using the **GENERIC** kernel.
 +  * **syncdev**
 +    * The name of the physical interface used to send pfsync updates out.
 +  * **syncpeer**
 +    * This optional parameter specifies the IP address of a host to exchange pfsync updates with. By default, pfsync updates are multicast on the local network. This option overrides that behavior and instead unicasts the update to the specified **syncpeer**.
 +  * **defer**
 +    * If the **defer** flag is used, the initial packet of a new connection passing through the firewall will not be transmitted until either another [[http://​man.openbsd.org/​pfsync|pfsync(4)]] system has acknowledged the state table addition, or a timeout has expired. This adds small delays but allows traffic to flow when more than one firewall might actively handle packets ("​active/​active"​),​ e.g. with certain [[http://​man.openbsd.org/​OpenBSD-current/​man8/​ospfd.8|ospfd(8)]],​ [[http://​man.openbsd.org/​OpenBSD-current/​man8/​bgpd.8|bgpd(8)]] or [[http://​man.openbsd.org/​carp|carp(4)]] configurations.
 +
 +=== pfsync example ===
 +
 +Here is an example pfsync configuration:​
 +<sxh bash>
 +ifconfig pfsync0 syncdev em1 up
 +</​sxh>​
 +
 +This enables pfsync on the em1 interface. Outgoing updates will be multicast on the network allowing any other host running pfsync to receive them.
 +
 +==== Combining CARP and pfsync for failover ====
 +
 +By combining the features of CARP and pfsync, a group of two or more firewalls can be used to create a highly-available,​ fully redundant firewall cluster.
 +  * **CARP**:
 +    * Handles the automatic failover of one firewall to another.
 +  * **pfsync**:
 +    * Synchronizes the state table amongst all the firewalls. In the event of a failover, traffic can flow uninterrupted through the new master firewall.
 +
 +An example scenario. Two firewalls, fw1 and fw2.
 +<sxh bash>
 +--
 +         ​+----| WAN/​Internet |----+
 +         ​| ​                       |
 +      em2|                        |em2
 +      +-----+ ​                 +-----+
 +      | fw1 |-em1----------em1-| fw2 |
 +      +-----+ ​                 +-----+
 +      em0|                        |em0
 +         ​| ​                       |
 +      ---+-------Shared LAN-------+---
 +</​sxh>​
 +
 +The firewalls are connected back-to-back using a crossover cable on em1. Both are connected to the LAN on em0 and to a WAN/​Internet connection on em2. IP addresses are as follows:
 +  * **fw1 em0**: 172.16.0.1
 +  * **fw1 em1**: 10.10.10.1
 +  * **fw1 em2**: 192.0.2.1
 +  * **fw2 em0**: 172.16.0.2
 +  * **fw2 em1**: 10.10.10.2
 +  * **fw2 em2**: 192.0.2.2
 +  * **LAN shared IP**: 172.16.0.100
 +  * **WAN/​internet shared IP**: 192.0.2.100
 +
 +The network policy is that fw1 will be the preferred master.
 +
 +=== Configure fw1 ===
 +
 +Enable preemption and group interface failover
 +<sxh bash>
 +sysctl net.inet.carp.preempt=1
 +echo '​net.inet.carp.preempt=1'​ >> /​etc/​sysctl.conf
 +</​sxh>​
 +
 +Configure pfsync
 +<sxh bash>
 +ifconfig em1 10.10.10.1 netmask 255.255.255.0
 +ifconfig pfsync0 syncdev em1
 +ifconfig pfsync0 up
 +</​sxh>​
 +
 +Configure CARP on the LAN side
 +<sxh bash>
 +ifconfig carp1 create
 +ifconfig carp1 vhid 1 carpdev em0 pass lanpasswd \
 +     ​172.16.0.100 netmask 255.255.255.0
 +</​sxh>​
 +
 +Configure CARP on the WAN/​Internet side
 +<sxh bash>
 +ifconfig carp2 create
 +ifconfig carp2 vhid 2 carpdev em2 pass netpasswd \
 +     ​192.0.2.100 netmask 255.255.255.0
 +</​sxh>​
 +
 +=== Configure fw2 ===
 +
 +Enable preemption and group interface failover
 +<sxh bash>
 +sysctl net.inet.carp.preempt=1
 +echo '​net.inet.carp.preempt=1'​ >> /​etc/​sysctl.conf
 +</​sxh>​
 +
 +Configure pfsync
 +<sxh bash>
 +ifconfig em1 10.10.10.2 netmask 255.255.255.0
 +ifconfig pfsync0 syncdev em1
 +ifconfig pfsync0 up
 +</​sxh>​
 +
 +Configure CARP on the LAN side
 +<sxh bash>
 +ifconfig carp1 create
 +ifconfig carp1 vhid 1 carpdev em0 pass lanpasswd \
 +     ​advskew 128 172.16.0.100 netmask 255.255.255.0
 +</​sxh>​
 +
 +Configure CARP on the WAN/​Internet side
 +<sxh bash>
 +ifconfig carp2 create
 +ifconfig carp2 vhid 2 carpdev em2 pass netpasswd \
 +     ​advskew 128 192.0.2.100 netmask 255.255.255.0
 +</​sxh>​
 +
 +==== Operational issues ====
 +
 +Some common operational issues encountered with CARP/​pfsync.
 +
 +=== Configuring CARP and pfsync during boot ===
 +
 +Since [[http://​man.openbsd.org/​OpenBSD-current/​man4/​carp.4|carp(4)]] and [[http://​man.openbsd.org/​OpenBSD-current/​man4/​pfsync.4|pfsync(4)]] are both types of network interfaces, they can be configured at boot by creating a [[http://​man.openbsd.org/​hostname.if|hostname.if(5)]] file. The [[http://​man.openbsd.org/​netstart|netstart]] startup script will take care of creating the interface and configuring it.
 +
 +Examples:
 +
 +Carp interface
 +<sxh bash>
 +/​etc/​hostname.carp1
 +inet 172.16.0.100 255.255.255.0 172.16.0.255 vhid 1 carpdev em0 pass lanpasswd
 +</​sxh>​
 +
 +pfsync interface
 +<sxh bash>
 +/​etc/​hostname.pfsync0
 +up syncdev em1
 +</​sxh>​
 +
 +=== Forcing failover of the master ===
 +
 +There can be times when it's necessary to failover or demote the master node on purpose. Examples include taking the master node down for maintenance or when troubleshooting a problem. The objective here is to gracefully fail over traffic to one of the backup hosts so that users do not notice any impact.
 +
 +To failover a particular CARP group, shut down the [[http://​man.openbsd.org/​OpenBSD-current/​man4/​carp.4|carp(4)]] interface on the master node. This will cause the master to advertise itself with an "​infinite"​ **advbase** and **advskew**. The backup host(s) will see this and immediately take over the role of master.
 +
 +<sxh bash>
 +ifconfig carp1 down
 +</​sxh>​
 +
 +An alternative is to increase the **advskew** to a value that's higher than the **advskew** on the backup host(s). This will cause a failover but still allow the master to participate in the CARP group.
 +
 +Another method of failover is to tweak the CARP demotion counter. The demotion counter is a measure of how "​ready"​ a host is to become master of a CARP group. For example, while a host is in the middle of booting up it's a bad idea for it to become the CARP master until all interfaces have been configured, all network daemons have been started, etc. Hosts advertising a high demotion value will be less preferred as the master.
 +
 +A demotion counter is stored in each interface group that the CARP interface belongs to. By default, all CARP interfaces are members of the "​carp"​ interface group. The current value of a demotion counter can be viewed using [[http://​man.openbsd.org/​OpenBSD-current/​man8/​ifconfig.8|ifconfig(8)]]:​
 +
 +<sxh bash>
 +ifconfig -g carp
 +carp: carp demote count 0
 +</​sxh>​
 +
 +In this example, the counter associated with the "​carp"​ interface group is shown. When a CARP host advertises itself on the network, it takes the sum of the demotion counters for each interface group the [[http://​man.openbsd.org/​OpenBSD-current/​man4/​carp.4|carp(4)]] interface belongs to and advertises that value as its demotion value.
 +
 +Now assume the following example. Two firewalls running CARP with the following CARP interfaces:
 +  * carp1 -- Accounting Department
 +  * carp2 -- Regular Employees
 +  * carp3 -- Internet
 +  * carp4 -- DMZ
 +
 +The objective is to failover just the carp1 and carp2 groups to the secondary firewall.
 +
 +First, assign each to a new interface group, in this case named "​internal":​
 +
 +<sxh bash>
 +ifconfig carp1 group internal
 +ifconfig carp2 group internal
 +ifconfig internal
 +carp1: flags=8843<​UP,​BROADCAST,​RUNNING,​SIMPLEX,​MULTICAST>​ mtu 1500
 +     carp: MASTER carpdev em0 vhid 1 advbase 1 advskew 100
 +     ​groups:​ carp internal
 +     inet 10.0.0.1 netmask 0xffffff00 broadcast 10.0.0.255
 +carp2: flags=8843<​UP,​BROADCAST,​RUNNING,​SIMPLEX,​MULTICAST>​ mtu 1500
 +     carp: MASTER carpdev em1 vhid 2 advbase 1 advskew 100
 +     ​groups:​ carp internal
 +     inet 10.0.1.1 netmask 0xffffff00 broadcast 10.0.1.255
 +</​sxh>​
 +
 +Now increase the demotion counter for the "​internal"​ group using [[http://​man.openbsd.org/​OpenBSD-current/​man8/​ifconfig.8|ifconfig(8)]]:​
 +<sxh bash>
 +ifconfig -g internal
 +internal: carp demote count 0
 +ifconfig -g internal carpdemote 50
 +ifconfig -g internal
 +internal: carp demote count 50
 +</​sxh>​
 +
 +The firewall will now gracefully failover on the carp1 and carp2 groups to the other firewall in the cluster while still remaining the master on carp3 and carp4. If the other firewall started advertising itself with a demotion value higher than 50, or if the other firewall stopped advertising altogether, then this firewall would again take over mastership on carp1 and carp2.
 +
 +To fail back to the primary firewall, reverse the changes:
 +<sxh bash>
 +ifconfig -g internal -carpdemote 50
 +ifconfig -g internal
 +internal: carp demote count 0
 +</​sxh>​
 +
 +Network daemons such as [[http://​man.openbsd.org/​bgpd|OpenBGPD]] and [[http://​man.openbsd.org/​sasyncd|sasyncd(8)]] make use of the demotion counter to ensure that the firewall does not become master until BGP sessions become established and IPsec SAs are synchronized.
 +
 +=== Ruleset tips ===
 +
 +**Filter the physical interface**. As far as PF is concerned, network traffic comes from the physical interface, not the CARP virtual interface (i.e., carp0). So, write your rule sets accordingly. Don't forget that an interface name in a PF rule can be either the name of a physical interface or an address associated with that interface. For example, this rule could be correct:
 +<sxh pf>
 +pass in on fxp0 inet proto tcp from any to carp0 port 22
 +</​sxh>​
 +
 +But replacing the **fxp0** with **carp0** would not work as you desire.
 +
 +**DON'​T forget** to pass **proto carp** and **proto pfsync!**
 +
 +=== Other references ===
 +
 +Please see these other sources for more information:​
 +  * [[http://​man.openbsd.org/​carp|carp(4)]]
 +  * [[http://​man.openbsd.org/​pfsync|pfsync(4)]]
 +  * [[http://​man.openbsd.org/​ifconfig|ifconfig(8)]]
 +  * [[http://​man.openbsd.org/​hostname.if|hostname.if(5)]]
 +  * [[http://​man.openbsd.org/​pf.conf|pf.conf(5)]]
 +  * [[http://​man.openbsd.org/​ifstated|ifstated(8)]]
 +  * [[http://​man.openbsd.org/​ifstated.conf|ifstated.conf(5)]]