Skip to the content.

Stateful filtering

TCP is a connection-oriented protocol, which means that network stacks have a state structure for each connection. The state is updated during the connection. A specific connection is determined by the source and destination IP addresses, port numbers and the direction of the initial packet. Additionally, TCP is responsible for reliable transmission, which is achieved using TCP sequence and window numbers. Validating the data of each packet according to the data in the state structure, as well as updating the state structure, is called TCP state tracking. Since packet filters are the middle points between the hosts (i.e. senders and receivers) they have to perform their own TCP state tracking for each connection in order to reliably distinguish different TCP connections and perform connection-based filtering.

Heuristic algorithms are used to handle out-of-order packets, packet losses and prevent connections from malicious packet injections. Using the conceptually same technique, limited tracking of message-based protocols, mainly UDP and ICMP, can also be done. Packet filters which have the described functionality are called stateful packet filters. For a more detailed description of the mechanism, one can refer to Rooij G., “Real stateful TCP packet filtering in IP Filter”, 10th USENIX Security Symposium invited talk, Aug. 2001 paper.

NPF is a stateful packet filter capable of tracking TCP connections, as well as performing limited UDP and ICMP tracking. Stateful filtering is enabled using the stateful or stateful-all keywords. The former creates a state which is uniquely identified by a 5-tuple (source and destination IP addresses, port numbers and an interface identifier). The latter excludes the interface identifier and must be used with precaution. Once the state is created, as described in the previous paragraph, all further packets of the connection are tracked. Packets in the backwards stream, after having been confirmed to belong to the same connection, are passed without ruleset inspection. Example configuration fragment with stateful rules:

group "external" on $ext_if {
  block all
  pass stateful in final proto tcp flags S/SA to $ext_if port ssh
}

In this example, all incoming and outgoing traffic on the $ext_if interface will be blocked, with the exception of incoming SSH traffic (with the destination being an IP address of this interface) and the implicitly passed backwards stream (outgoing reply packets) of these SSH connections. Since initial TCP packets opening a connection are SYN packets, such rules often have additional TCP filter criterion. The expression flags S/SA extracts SYN and ACK flags and checks that SYN is set and ACK is not. If there are no flags specified, then stateful rules imply flags S/SAFR for the TCP connections, i.e. the rules will pass and create the state only on TCP connection request (SYN packets). This is not the case if pcap-filter(7) is used.


IMPORTANT: Stateful rules imply flags S/SAFR for TCP packets.


It is important to understand the implications of stateful-all. Bypassing the ruleset on other interfaces can have undesirable effects, e.g. a packet with a spoofed IP address might bypass ingress filtering. Associating a state with two interfaces (forwarding case) may also cause problems if the routes change. On the other hand, picking up the state on any interface may lead to higher performance in certain configurations and may also handle some asymmetric routing cases. The administrator is free to choose whether stateful or stateful-all is more suitable.


WARNING: The stateful-all keyword must be used with precaution.


// Connection save/restore