When working with complex iptables configurations, tracing packet flow becomes as essential as debugging code. The standard -j LOG
target is like using print()
statements - it works but lacks precision and control.
The most effective technique combines rule numbering with conditional logging. First, list your rules with numbers:
iptables -L --line-numbers
Then create a debugging chain:
iptables -N DEBUG
iptables -A DEBUG -j LOG --log-prefix "DEBUG-RULE "
iptables -A DEBUG -j RETURN
Insert debugging jumps only when needed:
# For specific rule debugging
iptables -I INPUT 3 -j DEBUG
# For specific port debugging
iptables -I INPUT -p tcp --dport 80 -j DEBUG
For kernels ≥ 2.6.20, use the TRACE target (requires kernel module):
modprobe xt_TRACE
iptables -t raw -A PREROUTING -p tcp --dport 22 -j TRACE
iptables -t raw -A OUTPUT -p tcp --sport 22 -j TRACE
View traces in syslog or via:
dmesg | grep TRACE
Here's a script to toggle debugging:
#!/bin/bash
DEBUG_RULES=(
"-p tcp --dport 80"
"-s 192.168.1.100"
)
enable_debug() {
for rule in "${DEBUG_RULES[@]}"; do
iptables -I INPUT 1 $rule -j DEBUG
done
}
disable_debug() {
iptables -L INPUT --line-numbers | grep DEBUG | awk '{print $1}' | sort -nr | while read num; do
iptables -D INPUT $num
done
}
For more visual debugging:
ulogd2
: Advanced userspace loggingnfdebug
: Netfilter debug toolsystemtap
: Kernel-level tracing
Debugging a NAT issue:
iptables -t nat -A POSTROUTING -o eth0 -j LOG --log-prefix "NAT-DEBUG " --log-level 4
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
tail -f /var/log/syslog | grep NAT-DEBUG
Remember to:
- Limit debug rules to specific ports/IPs
- Use higher rule positions for debugging
- Disable debugging after issue resolution
- Consider rate limiting for busy systems
When working with complex iptables configurations, traditional logging often falls short for debugging purposes. Unlike application debugging where we can step through code, iptables lacks built-in granular inspection capabilities for individual packet journeys through rule chains.
This technique lets you dynamically insert and remove debugging rules without modifying your production ruleset:
# Add debug rule
iptables -I INPUT 1 -p tcp --dport 80 -m state --state NEW -j LOG --log-level debug --log-prefix "DEBUG-HTTP-IN: "
# View debug output
tail -f /var/log/kern.log | grep DEBUG-HTTP-IN
# Remove when done
iptables -D INPUT 1
Use packet counters to identify which rules are being matched:
iptables -L -v -n --line-numbers
# Example output:
Chain INPUT (policy DROP 0 packets, 0 bytes)
num pkts bytes target prot opt in out source destination
1 5 420 ACCEPT tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
2 0 0 DROP all -- * * 192.168.1.100 0.0.0.0/0
NFLOG provides more flexible logging than the standard LOG target:
iptables -A FORWARD -j NFLOG --nflog-group 1 --nflog-threshold 100
# Monitor with ulogd or wireshark:
tcpdump -ni nflog:1 -w debug.pcap
wireshark -k -i nflog:1
The TRACE target provides detailed packet traversal information:
modprobe nf_log_ipv4
sysctl -w net.netfilter.nf_log.2=nf_log_ipv4
# Add trace rule
iptables -t raw -A PREROUTING -p tcp --dport 80 -j TRACE
# View trace output
dmesg -w | grep TRACE
Create reusable debugging rules using iptables-save/restore:
# debug_rules.iptables
:DEBUG_HTTP - [0:0]
-A DEBUG_HTTP -j LOG --log-prefix "HTTP-DEBUG: " --log-level 7
-A INPUT -p tcp --dport 80 -j DEBUG_HTTP
# Enable debugging
iptables-restore < debug_rules.iptables
# Disable debugging
iptables -D INPUT -p tcp --dport 80 -j DEBUG_HTTP
iptables -F DEBUG_HTTP
iptables -X DEBUG_HTTP
- Start with rule counters (-L -v) to identify suspicious rules
- Add targeted TRACE or LOG rules for problematic traffic
- Gradually narrow down using more specific match criteria
- Remove debugging rules when done
For complex debugging sessions, consider this bash function:
debug_iptables() {
local protocol=$1
local port=$2
local timeout=${3:-30}
echo "Debugging $protocol port $port for $timeout seconds..."
iptables -I INPUT 1 -p $protocol --dport $port -j LOG --log-prefix "IPT-DEBUG: "
timeout $timeout tail -f /var/log/kern.log | grep "IPT-DEBUG:"
iptables -D INPUT 1
echo "Debugging session ended"
}