Netfilter Architecture¶
What is Netfilter?¶
Netfilter is the packet filtering framework built into the Linux kernel. It provides:
- Packet filtering (firewalling)
- Network Address Translation (NAT)
- Packet mangling (modification)
- Connection tracking
- Packet logging
Everything else (iptables, nftables, UFW) is a frontend to netfilter.
The Hook Points¶
Netfilter inserts "hooks" at specific points in the network stack where packets can be intercepted:
┌─────────────────┐
│ Local Process │
└────────┬────────┘
│
┌────────▼────────┐
│ OUTPUT │
│ (hook 4) │
└────────┬────────┘
│
┌──────────────┐ ┌───────────────┐ ┌───▼───┐ ┌──────────────┐
│ PREROUTING │───▶│ ROUTING │ │ROUTING│───▶│ POSTROUTING │
│ (hook 1) │ │ DECISION │ │ │ │ (hook 5) │
└──────────────┘ └───────┬───────┘ └───────┘ └──────┬───────┘
│ │ │
│ ▼ (forward) │
│ ┌───────────────┐ │
│ │ FORWARD │────────────────────────┘
│ │ (hook 3) │
│ └───────────────┘
│
│ (local)
▼
┌──────────────┐
│ INPUT │
│ (hook 2) │
└──────┬───────┘
│
▼
┌──────────────┐
│ Local Process│
└──────────────┘
Hook Descriptions¶
| Hook | Number | When Triggered |
|---|---|---|
| PREROUTING | 1 | Immediately after packet received, before routing |
| INPUT | 2 | Packet is destined for local process |
| FORWARD | 3 | Packet is being routed through the host |
| OUTPUT | 4 | Packet generated by local process |
| POSTROUTING | 5 | Just before packet leaves the interface |
Connection Tracking (conntrack)¶
Netfilter tracks connections to enable stateful firewalling:
Connection States¶
| State | Description |
|---|---|
| NEW | First packet of a connection |
| ESTABLISHED | Part of an established connection |
| RELATED | New connection related to existing (e.g., FTP data) |
| INVALID | Packet doesn't match known connection |
| UNTRACKED | Explicitly not tracked |
Viewing Connections¶
# Install conntrack tools
sudo apt install conntrack
# View all tracked connections
sudo conntrack -L
# Count connections
sudo conntrack -C
# View connections in real-time
sudo conntrack -E
# Filter by state
sudo conntrack -L -p tcp --state ESTABLISHED
Connection Tracking Table¶
# View conntrack table size
cat /proc/sys/net/netfilter/nf_conntrack_max
# View current count
cat /proc/sys/net/netfilter/nf_conntrack_count
# Increase if needed
echo 262144 | sudo tee /proc/sys/net/netfilter/nf_conntrack_max
Example Connection Entry¶
tcp 6 431999 ESTABLISHED src=192.168.1.100 dst=93.184.216.34 sport=54321 dport=443
src=93.184.216.34 dst=192.168.1.100 sport=443 dport=54321 [ASSURED] mark=0 use=1
Components: - Protocol (tcp, 6) - TTL (431999 seconds) - State (ESTABLISHED) - Original direction (src/dst/sport/dport) - Reply direction (reversed) - Flags (ASSURED = seen traffic both ways)
NAT Types¶
Netfilter supports several NAT types:
Source NAT (SNAT)¶
Changes the source address of outgoing packets:
Internal Host NAT Gateway External Server
192.168.1.100:54321 ───────▶ [SNAT to 203.0.113.1] ───────▶ 93.184.216.34:443
│
Response tracks back automatically
Used for: - Allowing private IPs to access the internet - Hiding internal network structure
Masquerade¶
Dynamic SNAT that uses the outgoing interface's address:
# SNAT with static IP
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 203.0.113.1
# Masquerade (dynamic IP)
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Destination NAT (DNAT)¶
Changes the destination address of incoming packets:
External Client NAT Gateway Internal Server
93.184.216.34:54321 ───────▶ [DNAT to 192.168.1.10] ──────▶ 192.168.1.10:80
│
Response tracks back automatically
Used for: - Port forwarding - Load balancing - Exposing internal services
Redirect¶
DNAT to the local machine:
# Redirect port 80 to 8080 locally
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080
Tables and Chains¶
Netfilter organizes rules into tables, each with predefined chains:
Tables¶
| Table | Purpose | Chains |
|---|---|---|
| filter | Packet filtering | INPUT, FORWARD, OUTPUT |
| nat | Address translation | PREROUTING, OUTPUT, POSTROUTING |
| mangle | Packet modification | All five hooks |
| raw | Connection tracking exceptions | PREROUTING, OUTPUT |
| security | SELinux marking | INPUT, FORWARD, OUTPUT |
Processing Order¶
When a packet arrives, tables are processed in this order:
PREROUTING: raw → mangle → nat
INPUT: mangle → filter → security
FORWARD: mangle → filter → security
OUTPUT: raw → mangle → nat → filter → security
POSTROUTING: mangle → nat
Custom Chains¶
You can create custom chains for organization:
# Create custom chain
iptables -N MY_CHAIN
# Jump to custom chain
iptables -A INPUT -j MY_CHAIN
# Add rules to custom chain
iptables -A MY_CHAIN -p tcp --dport 22 -j ACCEPT
# Return to calling chain
iptables -A MY_CHAIN -j RETURN
Targets¶
When a rule matches, a target determines what happens:
Terminating Targets¶
| Target | Action |
|---|---|
| ACCEPT | Allow packet |
| DROP | Silently discard |
| REJECT | Discard with ICMP error |
| QUEUE | Send to userspace |
Non-Terminating Targets¶
| Target | Action |
|---|---|
| LOG | Log and continue processing |
| MARK | Set packet mark |
| CONNMARK | Set connection mark |
| RETURN | Return to calling chain |
NAT Targets¶
| Target | Action |
|---|---|
| SNAT | Change source address |
| DNAT | Change destination address |
| MASQUERADE | Dynamic source NAT |
| REDIRECT | Redirect to local port |
Packet Flow Examples¶
Local Delivery¶
Packet arrives for local process:
1. NIC receives packet
2. PREROUTING (raw → mangle → nat)
- DNAT possible here
3. Routing decision: local delivery
4. INPUT (mangle → filter)
- Filtering happens here
5. Delivered to socket
Forwarding¶
Packet routed through host:
1. NIC receives packet
2. PREROUTING (raw → mangle → nat)
- DNAT possible here
3. Routing decision: forward
4. FORWARD (mangle → filter)
- Filtering happens here
5. POSTROUTING (mangle → nat)
- SNAT/MASQUERADE here
6. Transmitted out interface
Local Generation¶
Packet generated by local process:
1. Process creates packet
2. OUTPUT (raw → mangle → nat → filter)
- All operations possible
3. Routing decision
4. POSTROUTING (mangle → nat)
- SNAT/MASQUERADE here
5. Transmitted out interface
Connection Tracking Helpers¶
Some protocols need special handling:
FTP Helper¶
FTP uses separate control and data connections:
# Load FTP helper
sudo modprobe nf_conntrack_ftp
# View loaded helpers
lsmod | grep nf_conntrack
# Allow related connections
iptables -A INPUT -m conntrack --ctstate RELATED -j ACCEPT
Other Helpers¶
nf_conntrack_tftp- TFTPnf_conntrack_sip- SIP (VoIP)nf_conntrack_h323- H.323 (VoIP)nf_conntrack_pptp- PPTP VPN
Security Considerations¶
Helpers can be security risks. In secure environments:
# Disable automatic helper assignment
echo 0 | sudo tee /proc/sys/net/netfilter/nf_conntrack_helper
# Explicitly assign helpers only where needed
iptables -t raw -A PREROUTING -p tcp --dport 21 -j CT --helper ftp
Performance Considerations¶
Conntrack Table Sizing¶
# Current size
cat /proc/sys/net/netfilter/nf_conntrack_max
# Memory usage per entry: ~300 bytes
# For 262144 entries: ~75 MB
# Set larger size
echo "net.netfilter.nf_conntrack_max = 524288" >> /etc/sysctl.conf
Hash Table Size¶
# View hash table size
cat /sys/module/nf_conntrack/parameters/hashsize
# Optimal: nf_conntrack_max / 4
echo 131072 | sudo tee /sys/module/nf_conntrack/parameters/hashsize
Timeouts¶
# View timeouts
sysctl net.netfilter.nf_conntrack_tcp_timeout_established
# Default: 432000 (5 days)
# Reduce for high-traffic servers
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=86400
Netfilter and Containers¶
Docker, LXC, and other container runtimes interact with netfilter:
Network Namespaces¶
Each network namespace has its own netfilter tables:
Bridge Filtering¶
By default, bridged traffic is filtered:
# Check bridge filtering
cat /proc/sys/net/bridge/bridge-nf-call-iptables
# Disable (for performance, reduces security)
echo 0 | sudo tee /proc/sys/net/bridge/bridge-nf-call-iptables
The Problem¶
When enabled, packets crossing a bridge are processed by iptables:
- Packet enters bridge port
- Bridge decides to forward
- iptables FORWARD chain processes packet
- This is where Docker/UFW conflicts occur
We'll explore this in detail in the Docker sections.