Stateful Linux Packet Filtering with nftables & UFW: Netfilter Hooks & Connection Tracking
Created: 2026-11-08 · Last updated: 2026-11-08 · Nejat Hakan
Executive Summary: Beneath every Linux firewall lies the Netfilter kernel subsystem. For over two decades, administrators struggled with legacy
iptables,ip6tables, andarptables—monolithic, separate utilities that parsed rules sequentially with high performance penalties. In modern Linux distributions (Debian 11/12+),nftablesprovides a unified bytecode virtual machine inside the kernel. It delivers atomic ruleset replacement, dual-stackinet(IPv4 + IPv6) filtering, constant-time (O(1)) set lookups, and deep stateful connection tracking (conntrack).
-
Netfilter Kernel Hooks
Trace the 5 fundamental hook locations:
PREROUTING,INPUT,FORWARD,OUTPUT, andPOSTROUTINGinside the kernel packet pipeline. -
nftablesArchitecture
Understand the unified
inetfamily, base chains, verdict maps, dynamic sets for rate-limiting, and atomic transactions. -
Stateful
conntrack
Filter traffic based on packet relationship:
NEW,ESTABLISHED,RELATED, andINVALIDstates to protect internal networks. -
UFW for Edge Nodes
Manage perimeter access rapidly using Uncomplicated Firewall (
ufw) default policies, application profiles, and interface-specific rules.
1. The Netfilter Subsystem: The 5 Kernel Packet Hooks
The Linux kernel processes all inbound, routed, and outbound packets through specific interception points known as Netfilter Hooks:
%%{init: {'flowchart': {'wrappingWidth': 600}}}%%
flowchart TB
NIC_IN["Ingress Packet (NIC RX Ring)"] --> PRE["1. PREROUTING Hook<br/>(DNAT / Port Redirection / Raw Mangling)"]
PRE --> DEC{"Routing Decision<br/>Is destination IP local or remote?"}
DEC -->|"Local IP"| INP["2. INPUT Hook<br/>(Host Firewall: Protects Local Daemons)"]
DEC -->|"Remote IP"| FWD["3. FORWARD Hook<br/>(Router / VM / Container Forwarding)"]
INP --> APP["Local Application Socket (e.g., Nginx, SSHd)"]
APP --> OUT["4. OUTPUT Hook<br/>(Local Outbound Traffic)"]
OUT --> ROUTE_OUT["Reroute Check"]
ROUTE_OUT --> POST["5. POSTROUTING Hook<br/>(SNAT / Masquerade before wire)"]
FWD --> POST
POST --> NIC_OUT["Egress Packet (NIC TX Ring)"]
The 5 Netfilter Hooks Explained
PREROUTING: Intercepts packets immediately after they leave the network interface driver, before any routing decision is made. Used for Destination NAT (DNAT) and port forwarding.INPUT: Executed for packets destined for local user-space sockets on the machine. This is where standard host firewalls block unauthorized inbound traffic.FORWARD: Executed for packets whose destination IP address belongs to another machine. Used when the Linux host acts as a router, NAT gateway, or container hypervisor.OUTPUT: Intercepts packets originated locally by applications before they traverse the kernel network stack toward the physical wire.POSTROUTING: Executed after all routing decisions are final, just before the packet is placed into the network interface TX ring buffer. Used for Source NAT (SNAT) and IP masquerading.
2. Why nftables Supersedes iptables
| Feature | Legacy iptables |
Modern nftables |
|---|---|---|
| Protocol Families | 4 separate tools (iptables, ip6tables, arptables, ebtables). |
Unified inet family handles both IPv4 and IPv6 simultaneously in a single rule. |
| Rule Matching Speed | Linear scan: checks rules sequentially (O(n) latency). | Set lookups in constant time (O(1)) via internal hashes and trees. |
| Ruleset Updates | Non-atomic: updating one rule requires re-evaluating or rewriting entire chains. | Strictly atomic transactions: Entire /etc/nftables.conf loads instantaneously in RAM. |
| Execution Model | Monolithic C code inside kernel modules for every protocol match. | Lightweight kernel bytecode virtual machine driven by user-space syntax. |
| Port Filtering | Multiple lines or slow multiport modules. |
Native comma-separated sets: tcp dport { 80, 443, 8080 } accept. |
3. Stateful Connection Tracking (conntrack)
A stateless packet filter evaluates each packet in isolation, requiring administrators to open return ports manually. In contrast, stateful filtering tracks TCP/UDP/ICMP sessions in kernel RAM (conntrack table):
%%{init: {'flowchart': {'wrappingWidth': 600}}}%%
flowchart TB
PKT["Incoming Packet"] --> CT["conntrack Table Lookup"]
CT --> STATE{"Connection State?"}
STATE -->|"ESTABLISHED / RELATED"| ACC["Instant ACCEPT<br/>(Part of valid existing session or FTP/ICMP error helper)"]
STATE -->|"INVALID"| DRP["Instant DROP<br/>(Out-of-window TCP packets, malformed headers)"]
STATE -->|"NEW"| RULE["Evaluate Policy Rules<br/>(Check port 22, 443, rate limits)"]
The 4 Core Connection States
NEW: The packet initiates a new connection (e.g., a TCPSYNpacket).ESTABLISHED: The packet belongs to a bidirectional connection that has already seen packets in both directions (e.g., completed 3-way handshake).RELATED: The packet initiates a new connection associated with an existing connection (e.g., an ICMPDestination Unreachableresponse or FTP data channel).INVALID: The packet cannot be identified or violates protocol state machine rules (e.g., unexpectedFIN/RSTflags). Must always be dropped immediately.
4. Production /etc/nftables.conf Ruleset
Here is a hardened, production-ready /etc/nftables.conf designed for Debian servers. It filters IPv4 and IPv6 in a single unified table, enforces stateful inspection, mitigates SSH brute-force attacks via dynamic rate-limited sets, and permits essential ICMP:
#!/usr/sbin/nft -f
# File: /etc/nftables.conf
# Flush all existing tables and rules atomically
flush ruleset
table inet filter {
# Dynamic set to track and rate-limit SSH brute-force attempts
set ssh_meter {
type ipv4_addr
flags dynamic, timeout
timeout 1m
}
chain input {
type filter hook input priority filter; policy drop;
# 1. Accept established and related connections (Maximum performance)
ct state established,related accept
# 2. Drop malformed or out-of-order packets immediately
ct state invalid drop
# 3. Allow all traffic on the local loopback interface
iifname "lo" accept
# 4. ICMP & ICMPv6 Handling (MANDATORY for IPv6 Path MTU & NDP)
ip protocol icmp icmp type { echo-request, destination-unreachable, time-exceeded } limit rate 10/second accept
ip6 nexthdr icmpv6 icmpv6 type {
destination-unreachable, packet-too-big, time-exceeded,
parameter-problem, echo-request, nd-router-advert,
nd-neighbor-solicit, nd-neighbor-advert
} accept
# 5. OpenSSH (Port 22) with automated brute-force rate-limiting
# Permits a maximum of 6 new connection attempts per minute per source IP
tcp dport 22 ct state new add @ssh_meter { ip saddr limit rate 6/minute } accept
# 6. Public Web Services (HTTP & HTTPS)
tcp dport { 80, 443 } accept
# 7. Explicit counter for all silently dropped packets
counter drop
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
Validating and Enabling nftables on Debian
# Check syntax without modifying active kernel rules
sudo nft -c -f /etc/nftables.conf
# Apply ruleset atomically
sudo nft -f /etc/nftables.conf
# Enable systemd service to persist across reboots
sudo systemctl enable --now nftables
5. Uncomplicated Firewall (UFW) for Edge Nodes
For single-node workstations, edge servers, or developers who prefer a streamlined CLI over raw nftables syntax, UFW acts as an intuitive front-end.
# Install UFW on Debian
sudo apt update && sudo apt install -y ufw
# 1. Enforce strict default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# 2. Add rate-limited SSH rule (drops IP after 6 connections in 30 seconds)
sudo ufw limit 22/tcp comment 'Rate-limited OpenSSH'
# 3. Open Web Ports
sudo ufw allow 80/tcp comment 'HTTP ACME'
sudo ufw allow 443/tcp comment 'HTTPS Reverse Proxy'
# 4. Restrict internal database access strictly to a trusted management subnet
sudo ufw allow in on eth0 from 10.10.20.0/24 to any port 5432 proto tcp comment 'Internal Postgres'
# 5. Enable the firewall
sudo ufw enable
Inspecting and Managing UFW
# View active rules with line numbers (essential for deletions)
sudo ufw status numbered
# Delete a rule by its number
sudo ufw delete 4
# View detailed status and logging levels
sudo ufw status verbose
6. Live Firewall Debugging & Monitoring
1. Real-Time Packet Drop Monitoring
Monitor kernel packet drops live as they occur:
2. Inspecting the Connection Tracking Table
Inspect active tracked sessions in kernel RAM:
7. Next Steps
Now that your host perimeter is fortified with stateful packet filters, connect remote servers and branch offices securely with a modern in-kernel cryptographic tunnel:
-
WireGuard: Kernel-VPN & Cryptokey Routing
Deploy lightweight, high-throughput VPN tunnels using
wireguard.ko, Noise protocol key exchange, andAllowedIPsrouting.