OPNsenseXI · Firewall StatesFirewall state operations
State lifetimes and timeouts — TCP, UDP and ICMP
What you'll learn
- Explain the default timeouts for TCP, UDP and ICMP state entries
- Describe how the TCP state machine affects timeout selection
- Identify production scenarios where the default timeouts are too short
- Apply per-rule timeout overrides for protocols with application-level pacing
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
State entries are not permanent. Every entry has a timeout, and the timeout depends on the protocol and (for TCP) the state machine position. The default timeouts are conservative — designed for typical traffic — but production environments include flows that do not match the assumptions.
This lesson covers how the timeouts are set, how they cascade through the TCP state machine, and the production tuning patterns that keep the right flows alive.
The three default timeouts
PF uses three primary timeouts, one per protocol family:
- TCP. Variable. The default for an established TCP connection is 24 hours (86400 seconds).
- UDP. 60 seconds. Refreshed by any matching packet in either direction.
- ICMP. 20 seconds. Refreshed by any matching packet in either direction.
These are the defaults in the normal optimisation profile under System → Firewall → Advanced → Optimisation.
The TCP timeout cascade
PF does not use a single timeout for TCP. It uses a cascade based on the TCP state machine position. The same flow has different timeouts at different states.
The cascade:
| State | Default timeout | Notes |
|---|---|---|
SYN_SENT:SYN_SENT | 30 seconds | First SYN observed. |
SYN_SENT:ESTABLISHED | 30 seconds | SYN-ACK arrived, final ACK pending. |
ESTABLISHED:ESTABLISHED | 86400 seconds (24 hours) | Handshake complete. |
FIN_WAIT_1 | 30 seconds | First FIN sent. |
FIN_WAIT_2 | 30 seconds | First FIN acknowledged. |
TIME_WAIT | 30 seconds | Both sides closed. |
CLOSE_WAIT | 1 hour | Peer sent FIN. |
LAST_ACK | 30 seconds | Local side sent FIN. |
$ pfctl -s state -v | grep -E 'ESTABLISHED|TIME_WAIT|FIN_WAIT|CLOSE_WAIT' | head -10all tcp 192.0.2.50:22 <- 203.0.113.50:51820 ESTABLISHED:ESTABLISHED
age 12:34:56, expires in 11:25:04, packets: 245 bytes: 31200
all tcp 192.0.2.50:22 <- 203.0.113.50:51821 ESTABLISHED:ESTABLISHED
age 00:00:12, expires in 23:59:48, packets: 5 bytes: 240
all tcp 192.0.2.50:443 <- 10.0.0.50:54321 TIME_WAIT:TIME_WAIT
age 00:00:02, expires in 00:00:28, packets: 142 bytes: 21800
all tcp 192.0.2.50:443 <- 10.0.0.50:54322 FIN_WAIT_2:FIN_WAIT_2
age 00:05:00, expires in 00:00:00, packets: 50 bytes: 8000Illustrative output
Why ESTABLISHED gets the long timeout
ESTABLISHED is the steady state of a normal TCP connection. The handshake is complete, data is flowing or could flow at any moment, and the connection should survive long periods of inactivity. A short ESTABLISHED timeout would force idle connections to be re-established repeatedly.
The 24-hour default assumes that legitimate TCP connections are either actively transferring data or will be re-established by the application within 24 hours. This is not always true:
- Legacy applications with no keepalive. A database connection that the application opens once and uses intermittently.
- Long-running monitoring sessions. A monitoring tool that opens a TCP session and holds it open for days.
- Backup streams. A backup client that opens a TCP session, transfers for hours, then is idle for hours.
UDP: 60 seconds, refreshed by traffic
UDP has no state machine; there is no handshake and no closing sequence. The firewall creates a state on the first datagram in one direction; matching packets in the reverse direction match the state. The state is removed when no matching packet has been seen for the timeout period (60 seconds default).
The 60-second default assumes that a UDP flow that has not exchanged any packet in 60 seconds is dead. This is not true for:
- SIP over UDP. The default 30-second SIP keepalive is below the PF timeout, but jitter and busy CPU can push the keepalive past 60 seconds.
- IPsec NAT-T keepalives. NAT-T keepalives are sent every 20-30 seconds.
- Long-lived streaming. A streaming protocol that sends a packet every 90 seconds will see the state expire between packets.
ICMP: 20 seconds
ICMP state is the simplest. The first packet creates a state; the matching reply matches the state. The default timeout is 20 seconds.
For Path MTU Discovery, ICMP type 3 code 4 messages are exchanged during the initial handshake of a TCP connection. The 20-second timeout is sufficient because PMTUD messages only flow during the handshake.
Per-rule timeouts
OPNsense allows per-rule timeout overrides. In the rule edit page, set “State timeout” in seconds. The rule’s timeout overrides the global default for matching flows.
The discipline: do not raise the global timeout to fix a single protocol’s needs. The global timeout affects every flow; per-rule overrides affect only matching flows.
$ pfctl -s rules | grep -E 'udp|state-timeout'@200 pass in quick on igb0 inet proto udp from any to any port = 5060
[ SIP signalling — UDP state timeout 120s. Owner: NetOps. Ticket: NET-2010. ]
@210 pass in quick on igb0 inet proto udp from any to any port = 4500
[ IPsec NAT-T keepalives — UDP state timeout 60s. Owner: NetOps. Ticket: NET-2011. ]Illustrative output
Production tuning patterns
Three patterns show up in production environments:
Long-lived TCP without application keepalive
A legacy application opens a TCP connection and uses it intermittently without keepalives. The state expires after 24 hours of inactivity. The fix options:
- Application keepalive. Configure the application to send a TCP keepalive or an application-level ping every 30 minutes.
- Per-rule timeout. Raise the per-rule timeout to 7 days or longer.
- Wrapper. Wrap the application with a tool that maintains the TCP session.
UDP-based real-time protocols
VoIP (SIP, RTP), video conferencing, real-time gaming. The UDP state expires between packets. The fix:
- Per-rule timeout. Raise the UDP timeout to cover the protocol’s worst-case packet interval.
- Application keepalive. Some protocols send application-level keepalives on the same five-tuple.
TCP sessions that should never expire
VPN tunnels, long-lived monitoring, replication streams. The fix:
- TCP keepalive. The application enables TCP keepalive.
- Application-level traffic. The application sends data or pings on a regular interval.
Summary
- TCP ESTABLISHED default: 24 hours. Other TCP states have shorter timeouts.
- UDP default: 60 seconds, refreshed by traffic.
- ICMP default: 20 seconds.
- Per-rule timeout overrides are the right tool for protocols that need longer timeouts.
- UDP refreshes only on packets matching the five-tuple, not on application-level keepalives.
- Timeouts are set at state creation; existing states keep their original timeout.
Knowledge check · 4 questions
Q1. A SIP phone sends a keepalive every 30 seconds. The SIP server is on a remote network. PF state shows the state expires every time the keepalive is delayed past 60 seconds by jitter. The next keepalive is treated as a new INVITE and the SIP server drops the call. What is the correct fix?
Q2. PF refreshes a UDP state entry when the application sends an application-level keepalive packet inside the UDP payload.
Q3. Which of the following are reasons to apply a per-rule timeout override in OPNsense? Select all that apply.
Q4. You raise the per-rule timeout on a rule from 60 seconds to 600 seconds. Existing TCP sessions that match the rule continue to expire at 60 seconds. What is the most likely explanation?
Passing score: 75%. Answers are checked in this browser.