Go to main content

Securing the Network in Oracle® Solaris 11.3

Exit Print View

Updated: April 2019
 
 

Packet Filter Rule Syntax

The syntax of PF rules is deceptively similar to IPF syntax:

action match-parameter optional-action-1 optional-action-2 ...

However, the results of identical rule interpretation can be very different. IPF rules do not translate easily into PF rules.

For example, the following rule set is valid in PF and IPF. However, IPF lets client 198.51.100.2 connect to 203.0.113.2 using Secure Shell, while PF does not. The explanation of different behavior is explained in Differences Between PF and IPF in State Matching.

block in from 203.0.113.2 to 198.51.100.2
block in from 198.51.100.2 to 203.0.113.2 
pass in proto tcp from 198.51.100.2 to 203.0.113.2 port = 22 keep state

Note -  By default, any packet that does not match any rule in the configuration file is accepted by the firewall.

    A rule in PF uses actions, match parameters, and optional actions to process packets and determine whether they are accepted or dropped. PF applies the action to the packet if the packet matches the rule. You write a rule by using the following elements in order:

  1. Begin the rule with an action. For a list, see Packet Filter Rule Actions.

  2. Match desired parameters. For a list, see Packet Filter Rule Match Parameters.

  3. Include desired optional actions. For a list, see Packet Filter Rule Optional Actions.

For the complete grammar and syntax of PF rules, see the pf.conf(5) man page. For examples of rules whose policy or syntax differs between IPF and PF rules, see Examples of PF Rules Compared to IPF Rules.

Packet Filter Rule Actions

Each rule begins with an action. PF applies the action to the packet if the packet matches the rule. The following list includes the commonly used actions applied to a packet and indicates whether the action was also in IP Filter.

anchor

Only in PF. Opens a new rule set which should be further applied to a matching packet.

block

Prevents the packet from passing through the filter.

## without an in, out, or on match parameter,
## blocks all traffic on all interfaces
block all
pass

Allows the packet through the filter. Applies to all packets, incoming and outgoing.

pass all
match

Provides fine-grained filtering without altering the block or pass state of a packet.

match rules differ from block and pass rules in that the parameters are set every time a packet matches the rule, not only on the last matching rule. These sticky parameters are in effect until explicitly overridden. Parameters affected are nat-to, binat-to, rdr-to, and scrub.

Packet Filter Rule Match Parameters

Keywords in this list define criteria that determine whether a packet matches the rule.

in

Applies the rule only if rule matches an inbound packet.

pass in from any to any port = 22
out

Applies the rule only if the rule matches an outbound packet.

pass out log on net0
on interface-name

Matches a packet that is moving in or out of the specified interface.

set skip on lo0
from source to destination

Applies the rule only if the rule matches a packet from the specified source.

    A source or a destination can be one of the following:

  • An IP address.

    CIDR notation is accepted, for example, 198.51.100.0/27.

  • A reference to a table, such as a white list of approved email sources.

  • A network interface name such as net0, which gets expanded to list of IP addresses and those addresses are plumbed to the interface.

The following example illustrates that well-formed rules do not require the use of in, out, or on. This rule matches all inbound and outbound packets on any interface on the host.

pass from any to any

The any keyword is used to accept packets from all sources and to all destinations. The IP addresses can be modified by a port number. The following example rule matches every inbound packet coming to port 22.

pass in from any to any port = 22
flags a/b | any

Matches TCP packets based on the TCP flags that are set.

The rule applies only to TCP packets that have the flags a set out of set b. Flags not in b are ignored.

The flags are: (F)IN, (S)YN, (R)ST, (P)USH, (A)CK, (U)RG, (E)CE, and C(W)R.

    The following examples describe some sample flags settings:

  • flags S/S – Flag S is set and the other flags are ignored.

  • flags any – No flags are checked.

  • flags S/SA – Flag S is set. Default setting for stateful connections.

    SYN and ACK together cannot match a packet. SYN+PSH, SYN+RST, and SYN can match a packet, but SYN+ACK, ACK+RST, and ACK cannot.

  • flags /SFRA – If the a set is not specified, it defaults to none. All a flags must be unset before using this filter.

See the pf.conf(5) man page for further uses and consequences of flags settings.

icmp-type

Matches the packet based on ICMP type. This keyword is used only when the proto option is set to icmp and is not used if the flags option is used.

proto

Matches a specific protocol. You can use any of the protocol names specified in the /etc/protocols file, or use a decimal number to represent the protocol. The keyword tcp/udp matches either a TCP or a UDP packet.

tos

Matches the packet based on the type-of-service value expressed as either a hexadecimal or a decimal integer.

ttl

Matches the packet based on its time-to-live value. A packet's stored time-to-live value indicates the number of hops the packet can make before being discarded.

group

Matches incoming packets by the effective group ID.

user

Matches incoming packets by the effective user ID.

Packet Filter Rule Optional Actions

Keywords in this list define additional optional actions.

allow-opts

When specified for a pass rule, allows packets to pass the filter based on the last matching rule even if the packets contain IP options or routing extension headers. Without allow-opts, PF blocks IPv4 packets with IP options and IPv6 packets with routing extension headers.

keep keep-options

An abbreviation for keep state. Determines the information that is kept for a packet that matches a given rule. Because the pass rules in PF create a state by default, the keep option is useful to further specify options for the kept state, such as sloppy and if-bound.

nat-to

Specifies that IP addresses are to be changed as the packet traverses the given interface. This technique allows one or more IP addresses on the translating host to support network traffic for a larger range of systems on an internal network. Internal network addresses should conform to the address ranges defined in Address Allocation for Private Internets, RFC 1918.

rdr-to

Redirects the packet to another destination and possibly a different port. rdr-to can specify port ranges as well as single ports.

log

Logs the matching packet. If log is used in a match action, then the packet is logged immediately on match even if the state is not kept. Multiple matching match rules will log the packet multiple times.

    log accepts the following arguments:

  • (all) to log all packets regardless of state

  • (matches) to log the packet on all subsequent matching rules

  • (user) to add UID and PID socket information to the log

  • (to interface) to send logs to a specified capture interface

quick

Executes the rule containing the quick option if the packet matches the rule. All further rule checking stops.

route-to

Moves the matching packets to an outbound queue on a named interface. Additionally, route-to can send different packets to different interfaces or different next hops by way of specified interfaces. Implements policy-based routing (PBR).

    route-to accepts the following arguments. Note that the (interface-name + next-hop address) pairs have two syntax variants:

  • interface-name

  • (interface-name next-hop-address) or next-hop-address@interface-name

  • { (interface1-name next-hop1-address), (interface2-name next-hop2-address) [, ...] } or { next-hop1-address@interface1-name, next-hop2-address@interface2-name[, ...] }

For how to select which interface to use when specifying several interfaces, see http://www.openbsd.org/faq/pf/pools.html.

Packet Filter Macros and Tables

    PF provides macros and tables to ease the readability, extensibility, and reuse of PF rules. Macros and tables accept lists.

  • Macro – Defines one or more items to be treated as one item. Specify a name that is easy to understand. For example, the following rule uses two easily understood macros:

    emailserver = 192.1.2.223  
    email = "{ smtp, pop3, imap }"  ## mail services
    pass in from any to $emailserver port = $email
  • Table – Lists of IP addresses that can be manipulated without reloading the entire rule set. Promotes fast lookups.

    A table is defined by a rule or set of rules. If all rules which refer to a table are deleted, the table is destroyed. To create a table that would outlive its rules, use the persist keyword.

    For example, the following clients table is a list of clients. One address in the client range is disallowed:

    table <clients> persist { 198.51.100.0/27, !198.51.100.5 }
Example 2  NAT Rule in PF

This example illustrates how to construct a NAT rule. This rule translates the source address in the outbound packet to an address which is bound to the net2 interface. It rewrites a packet that goes out on the net2 device with a source address of 198.51.100.0/27 and externally shows its source address as net2:

## NAT rule that externally shows net2 as source address
pass out on net2 from 198.51.100.0/27 to any nat-to (net2)
Example 3  Spam Rule in PF

This example illustrates the use of tables in PF. The administrator creates a spam table for IP addresses that send spam. The following firewall rule blocks incoming packets from all addresses in the spam table.

table <spam> { 198.51.100.0/27 }
block in from <spam> to any

To block a new spam source, the administrator updates the table only.

# pfctl -t spam -T add 203.0.113.0/16

The pfctl command updates the firewall configuration without altering the rule set. In the kernel, the table now reads:

table <spam> { 198.51.100.0/27 203.0.113.0/16 }

For the complete grammar and syntax of PF rules, see the pf.conf(5) man page. For arguments to the ipadmcommand, see the ipadm(1M) man page.

Examples of PF Rules Compared to IPF Rules

PF and IPF have different default filtering behavior but use a similar syntax. Note that unmodified IPF rules in the PF configuration file are likely to implement the wrong security policy. The following examples illustrate some of the differences. For ways to verify your PF rules, see Using PF Features to Administer the Firewall.

Loopback Interface Filtering Is On by Default in PF

    Unlike IPF, PF processes the packets that are bound to the loopback interface by default. However, even a simple rule such as pass all can cause significant performance degradation of loopback traffic when PF is enabled. The collision of two features is responsible for the poor performance:

  • Stateful packet inspection, which is the default for pass rules in PF

  • TCP fusion, which is a feature specific to Oracle Solaris

TCP fusion increases throughput for loopback-bound connections by simplifying packet processing, as described in ip Probes in Oracle Solaris 11.3 DTrace (Dynamic Tracing) Guide. However, the simplification breaks TCP sequence numbering, which PF uses for state validation. Because TCP fused packets have invalid sequence numbers, PF drops them. TCP retransmits those packets, the packets continue to fail state validation, and throughput slows.

When implementing loopback policy in PF rules, you must be explicit. If your policy is to not process loopback packets, you must override the default that processes loopback packets.

## PF version - no filtering of packets on loopback interface
set skip on lo0

Note - set skip on directives are evaluated only when the rule set is being loaded. Thus, interfaces that are added later are not affected by the directive.

    If your policy is to process loopback packets, you have two options.

  • Do not keep the state of the pass rule that processes loopback interface packets:

    ## PF version - no state on loopback interface
    pass in on lo0 from any to any no state
  • Ignore TCP sequence numbers when performing a stateful inspection of loopback interface packets:

    ## PF version - no TCP sequence number inspection on loopback interface
    pass in on lo0 from any to any keep state (sloppy)

Differences Between PF and IPF in State Matching

PF and IPF match particular packets to state in different ways. Consider the following rule set on a router that forwards traffic between the 198.51.100.0/27 and 203.0.113.0/16 networks:

## Valid rule set in IPF and PF

block in from 198.51.100.0/27 to any
block in from 203.0.113.0/16 to any 
pass in from 198.51.100.2 to 203.0.113.2 keep state

    Although these rules are valid for both firewalls, they implement different policies.

  • In IPF, the 198.51.100.2 client can reach the 203.0.113.2 server and the server's response packets can reach the client.

    1. The block rules prevent all traffic between the 198.51.100.0/27 and 203.0.113.0/16 networks.

    2. The pass rule allows the 198.51.100.2 client to connect to the 203.0.113.2 server.

    3. The keep state action in the pass rule allows the server's responses to reach the client.

    As soon as the first request packet sent by 198.51.100.2 matches the pass rule, a state is created: 198.51.100.2 -> 203.0.113.2. The state also matches the reverse packets sent by the server back to the client: 203.0.113.2 -> 198.51.100.2.

  • In PF rule processing, the 198.51.100.2 client cannot connect to the 203.0.113.2 server.

    1. PF state inspection is stricter. The in match parameter to the pass rule instructs PF to match only inbound packets. Therefore, the state that the pass in rule creates matches only two kinds of packets:

      • Inbound forward packet, sent by client to server: 198.51.100.2 -> 203.0.113.2

      • Reverse outbound packet, sent by server to client: 203.0.113.2 -> 198.51.100.2

    2. When a response arrives from the server to the PF firewall, PF does not see the packet as a reverse packet but as inbound for the first time, so the packet does not match the state that the pass in rule creates. Rule processing continues to look for a rule that matches the packet to determine whether to forward the packet or drop it. The packet matches the second block rule, block in from 203.0.113.0/16 to any, so PF drops the response sent by the server.

      For the traffic to be routed in PF as it is in IPF, you have two options:

    • Add a rule that creates a state for forward outbound packets. This state will enable the inbound reverse packets from the server to be valid.

      ## PF rule set1 enforces IPF rule set policy
      
      block in from 198.51.100.0/27 to any
      block in from 203.0.113.0/16 to any
      pass in from 198.51.100.2 to 203.0.113.2 keep state
      pass out from 198.51.100.2 to 203.0.113.2 keep state

      The pass rules create two states:

      198.51.100.2 -> 203.0.113.2 @ in
      198.51.100.2 -> 203.0.113.2 @ out

      These states allow reverse inbound packets from the server:

      in: 203.0.113.2 -> 198.51.100.2
    • Write a simpler rule set that does not use packet direction:

      ## PF rule set2 enforces IPF rule set policy  
      
      block in from 198.51.100.0/27 to any
      block in from 203.0.113.0/16 to any
      pass from 198.51.100.2 to 203.0.113.2 keep state

      The pass rule without an in parameter matches packets sent by the client regardless of direction. Therefore, when PF intercepts a packet from 198.51.100.2 as inbound for the first time, it creates a state:

      198.51.100.2 -> 203.0.113.2 @ in

      After the packet is routed by the firewall, it is intercepted by PF again, this time as an outbound packet. When the packet hits the pass rule, PF creates a second state:

      198.51.100.2 -> 203.0.113.2 @ out

Network Address Translation in PF

The OpenBSD version of PF supports NAT-64 as described by RFC 6146, while the Oracle Solaris version of PF supports traditional NAT only, as described by RFC 2663.

NAT typically sets up mapping between a network with a private address range and the Internet. NAT enables hosts in private networks to talk to any host on the Internet. In a typical deployment, the mapping is one to many, which means that many hosts in a private network share one public IP address to connect to Internet servers.

In PF, NAT processing is an optional rule action. A NAT rule in PF effectively creates a state that tells the firewall to alter the IP address depending on the action type of either rdr-to or nat-to, and the matching packet's direction, either inbound or outbound.

In PF, the nat-to and rdr-to actions are executed as soon as a packet matches the rule with the nat-to or rdr-to optional action. The subsequent rules see an already-translated packet. Both actions also create state for the matching packet.

  • nat-to – Typically used for outbound packets. Allows a network client in a private network to talk to an Internet server. nat-to tells the firewall to overwrite the source address and port in the matching packet according to additional parameters in the rule.

    For example, the following rule changes a private source address from the address range 198.51.100.0/27 in a packet to a public IP address that is bound to the net0 interface:

    pass out on net0 from 198.51.100.0/27 to any nat-to (net0)
  • rdr-to – Typically used for inbound packets. The rdr-to optional action is the opposite of nat-to in that it allows a client on the Internet to talk to a server behind the PF firewall in a network with private IP addresses. rdr-to changes the destination address in a matching packet.

    For example, the following rule redirects inbound SMTP traffic to 203.0.113.2:2525, the IP address of the SMTP server on a private network behind the firewall. This rule matches any TCP inbound packet coming to the net0 interface and having the same destination IP address as the address that is bound to net0, and whose destination port is 25. The rdr-to action overwrites the destination address and port in the packet with 203.0.113.2:2525.

    pass in on net0 inet proto tcp from any to (net0) port = 25 rdr-to 203.0.113.2 port 2525

Changes from the rdr-to or nat-to actions are immediately visible to subsequent rules unless the affected packet does not match a pass or block rule that creates a state. In that case, the packet that leaves PF is not touched by the rdr-to or nat-to option. A log action still logs the packet.

## Block packets going to 198.51.100.2
match out to 198.51.100.2 nat-to 198.51.100.1
block from 198.51.100.1

The effect of the preceding example is that the packets sent by the *2 address hit the block rule because they are matched to *1, which is blocked. If the match out to rule were not in the rule set, the *2 packets would go through.

Rule Equivalents Using match and pass Actions

All of the parameters of match rules that require a pass or block rule create a state. Sometimes a pass rule can be equivalent to match plus pass rules. For example, the following PF rule sets are equivalent:

webserver = "198.51.100.7"
webports = "{ http, https }"
emailserver = "198.51.100.5"
email = "{ smtp, pop3, imap }"

### Rule set 1 - match action then pass action
match in on $ext_if proto tcp to $ext_if port $webports rdr-to $webserver
match in on $ext_if proto tcp to $ext_if port $email rdr-to $emailserver

pass proto tcp from any to $webserver port $webports 
pass proto tcp from any to $emailserver port $email 
pass proto tcp from $emailserver to any port smtp 

### Rule set 2 - no match action, only pass action
pass in on $ext_if inet proto tcp to $ext_if port $webports rdr-to $webserver
pass in on $ext_if inet proto tcp to $ext_if port $email rdr-to $mailserver
pass on $int_if inet proto tcp to $webserver port $webports
pass on $int_if inet proto tcp to $mailserver port $email