Skip to main content
RunBook Academy

OPNsenseXI · Firewall StatesFirewall state operations

Clearing states — pfctl -k vs pfctl -F state, and when each is right

Intermediate⏱ ~12 minpfctl

What you'll learn

  • Use pfctl -k to kill specific state entries by source, destination, or label
  • Use pfctl -F state to flush the entire state table when appropriate
  • Recognise the service-affecting implications of bulk state clearing
  • Apply the operational discipline that picks targeted vs bulk clearing

Prerequisites

Verified against OPNsense 25.x · FreeBSD 14.x · PF (FreeBSD packet filter) FreeBSD 14.x · Unbound 1.20+ · Kea DHCP OPNsense 25.x plugin · WireGuard in-kernel + OPNsense plugin · strongSwan (IPsec plugin) OPNsense 25.x plugin · OpenVPN 2.6.x · Suricata 7.x · 2026-08-14

Not yet marked complete on this device.

The state table is the firewall’s working memory. Sometimes that memory needs to be cleared. A state entry for a connection that no longer exists (the application crashed and the client never sent a FIN). A state entry that is causing a problem (a stuck connection that should be terminated). A state entry whose associated rule has changed and the operator wants the new rule to apply.

PF provides two clearing commands: pfctl -k for targeted removal of specific entries, and pfctl -F state for bulk flush of the entire table. The two commands are not interchangeable; the wrong choice is service-affecting.

This lesson covers when each is appropriate and the operational discipline that picks the right tool.

The targeted kill: pfctl -k

pfctl -k removes specific state entries based on filter criteria. The syntax:

pfctl -k <source-or-destination>
pfctl -k <source> -k <destination>
pfctl -k label <rule_label>

The simplest form kills every state involving a specific IP:

pfctl -k 192.0.2.50

This kills all states where 192.0.2.50 is either source or destination. Useful when a host’s connections need to be terminated.

The two-criterion form kills only states matching both:

pfctl -k 192.0.2.50 -k 203.0.113.50

The label form kills states that match a specific rule label:

pfctl -k label -k 'id <rule_id>'

This requires the rule to have a label in the GUI.

Destructivepfctl -k (single IP)
$ pfctl -k 192.0.2.50 && echo OK
1 states killed
OK

Illustrative output

The bulk flush: pfctl -F state

pfctl -F state removes every entry in the state table. The firewall starts with an empty state table; all subsequent packets are evaluated against the rules.

pfctl -F state

There is no filter, no confirmation prompt, no rollback. Every active TCP session that traverses the firewall is terminated. Every VPN tunnel, every SSH session, every long-poll HTTP request — all gone.

This is a service-affecting command. In a production environment, it should be run only during a maintenance window with advance notice.

Destructivepfctl -F state
$ pfctl -F state && echo 'all state cleared'
43 states cleared
all state cleared

Illustrative output

When each is appropriate

Targeted kill: pfctl -k

Use pfctl -k when:

  • A specific host’s connections need to be terminated. An IP reassignment, a misbehaving host, a security incident. Use pfctl -k <host_ip>.
  • A specific flow needs to be terminated. A stuck TCP session. Use pfctl -k <src> -k <dst>.
  • A rule change needs to apply to existing flows. After editing a firewall rule, existing states continue to match the old rule. Clear the relevant states with pfctl -k <src> for the affected flows.
  • A specific rule’s states need to be cleared. If the rule has a label, use pfctl -k label -k <label>.

Bulk flush: pfctl -F state

Use pfctl -F state when:

  • A maintenance window is in effect. The operator has notified users, and a complete reset is part of the planned work.
  • A config apply that drops all state is unavoidable. Some operations (firmware upgrade, major config change) require a full state reset.
  • The state table is corrupted or exhausted. A pathological state requires a flush to recover.

In all other cases, pfctl -k is the right tool.

Clearing states after a rule change

The most common operational reason to clear states is a rule change. PF caches the rule decision in the state entry; an existing flow continues to match the state, not the new rule. To apply the new rule to the flow, clear the state.

The pattern:

  1. Apply the new rule via the GUI. OPNsense applies the change.
  2. Identify the affected flows. Use pfctl -s state | grep <criteria> to find the flows that match the old rule.
  3. Kill the affected states with pfctl -k. For specific IPs, flows, or rule labels.
  4. Verify. The next packet of the affected flow is evaluated against the new rule.

The discipline: do not blanket-flush the entire state table to apply a rule change.

Killing states by rule label

If the rule has a label (set in the GUI under Advanced Options → Label), pfctl -k can target all states created by that rule.

For example, a rule that permits outbound HTTPS from the LAN could have the label lan_https_out. After a change to that rule, the operator can clear all states with:

pfctl -k label -k lan_https_out
Destructivepfctl -k label
$ pfctl -k label -k lan_https_out
127 states killed

Illustrative output

The discipline: assign labels to rules that will need targeted state clearing. Without a label, the operator must identify the affected states by IP, which is fragile.

Operational discipline

The discipline for state clearing has three parts:

Document before clearing

Before running pfctl -k or pfctl -F state, document:

  • What you are about to do.
  • Why.
  • The expected impact.
  • The time of the action.

This is the change record. It is what the next operator reads when the action’s effects are being investigated.

Verify with a test packet

After clearing, verify the firewall is doing what you expected. Send a test packet from a host that matches the cleared flow; confirm the firewall’s behaviour matches the new rule.

Have a rollback

For non-emergency state clearing, have a rollback plan. If the new rule or the state clearing causes an incident, what is the recovery path?

Common mistakes

Three patterns cause incidents:

  • Bulk flush to apply a rule change. The operator did not realise they could clear specific states; they flushed everything. The fix is to use pfctl -k with the affected IP, destination, or label.
  • Targeted kill of the wrong IP. The operator mistyped an IP or a label and killed the wrong states. The fix is to verify the target with pfctl -s state before killing.
  • State clearing without a change record. The operator cleared states as part of a maintenance task; the next operator sees the cleared table and has no idea why. The fix is to document the action before executing.

Summary

  • pfctl -k <ip> kills states involving a specific IP; pfctl -k <src> -k <dst> kills specific flows; pfctl -k label -k <label> kills states by rule label.
  • pfctl -F state flushes the entire state table. Service-affecting.
  • Use targeted kill for routine changes (rule edits, IP reassignments, misbehaving hosts). Reserve bulk flush for maintenance windows.
  • After a rule change, clear the affected states with pfctl -k to apply the new rule to existing flows.
  • Assign labels to rules that will need targeted state clearing.
  • Document before clearing; verify after; have a rollback.

Knowledge check · 4 questions

  1. Q1. You change a firewall rule that permits outbound HTTPS from the LAN. Existing HTTPS sessions from the LAN continue to use the old rule behaviour. What is the most appropriate action?

  2. Q2. pfctl -F state is appropriate to run during normal production hours when a rule change needs to apply to existing flows.

  3. Q3. Which of the following are valid uses of pfctl -k? Select all that apply.

  4. Q4. You run pfctl -k label -k lan_https_out and see "0 states killed". The rule has the label lan_https_out and was active. What is the most likely explanation?

Passing score: 75%. Answers are checked in this browser.