Resolving 'iptables connection refused' and Packet Loss in Linux Networking
Comprehensive guide to diagnosing and fixing 'iptables connection refused' and packet loss. Learn to debug REJECT targets, conntrack limits, and rule ordering.
- Connection Refused implies an active rejection (TCP RST or ICMP Port Unreachable), typically caused by a REJECT target in iptables rather than a DROP target.
- Intermittent packet loss is frequently caused by kernel connection tracking exhaustion (nf_conntrack: table full), rate-limiting rules, or MTU/MSS clamping issues.
- Docker and Kubernetes heavily modify the nat and FORWARD chains, which can silently bypass or conflict with standard INPUT rules.
- Quick fix summary: Use 'iptables -nvL --line-numbers' to audit rule order, check 'dmesg -T | grep conntrack' for state table drops, and ensure the loopback interface is explicitly allowed.
| Method | When to Use | Time | Risk |
|---|---|---|---|
| iptables -nvL --line-numbers | Identifying misordered rules and hit counts on REJECT/DROP targets | 2 mins | None |
| dmesg -T | grep nf_conntrack | Diagnosing intermittent packet loss or dropped connections under load | 1 min | None |
| tcpdump -i any -n port <port> | Verifying if packets are reaching the interface before iptables processing | 5 mins | Low |
| iptables-save > backup.txt && iptables -F | Emergency isolation testing to prove iptables is the culprit | 1 min | High (Breaks routing/NAT/Docker) |
Understanding the Error: Connection Refused vs. Timeout
When troubleshooting network connectivity issues, it is critical to distinguish between a Connection timed out and a Connection refused error. These symptoms point to entirely different underlying causes within the Linux netfilter/iptables stack.
A timeout occurs when packets are silently dropped. The client sends a TCP SYN packet, receives no response, and retries until it gives up. In iptables, this is the hallmark of the DROP target or a default drop policy on the INPUT or FORWARD chains.
Conversely, curl: (7) Failed to connect to 10.0.0.5 port 8080: Connection refused means the client received an explicit rejection. At the network level, this is either a TCP RST (Reset) packet or an ICMP Port Unreachable message. If a service is simply not listening on the target port, the Linux kernel network stack will naturally return a TCP RST. However, if the service is running, the culprit is almost certainly an iptables rule using the REJECT target.
The Role of the REJECT Target
The REJECT target in iptables is designed to actively deny a connection and inform the sender. By default, an iptables rule like iptables -A INPUT -p tcp --dport 8080 -j REJECT will send an ICMP port unreachable message. You can also specify the exact rejection type:
iptables -A INPUT -p tcp --dport 8080 -j REJECT --reject-with tcp-reset
When diagnosing a 'connection refused' error, your first objective is to locate REJECT rules that are intercepting your traffic before it reaches the intended ACCEPT rule.
Diagnosing 'iptables connection refused'
Step 1: Rule Order Auditing
iptables evaluates rules sequentially from top to bottom. Once a packet matches a rule with a terminating target (ACCEPT, DROP, REJECT), evaluation stops for that chain. A common misconfiguration is appending (-A) an allow rule after a catch-all reject rule.
Run the following command to inspect your rules with line numbers and packet/byte counters:
sudo iptables -nvL INPUT --line-numbers
Look for rules matching your target port. If you see an ACCEPT rule at line 10, but a REJECT all -- 0.0.0.0/0 0.0.0.0/0 at line 5, the ACCEPT rule will never be reached. You must insert (-I) the rule above the rejection:
sudo iptables -I INPUT 5 -p tcp --dport 8080 -j ACCEPT
Step 2: Loopback Interface Restrictions
Another frequent cause of 'connection refused' for local services (e.g., a reverse proxy trying to talk to an application server on 127.0.0.1) is a missing loopback allow rule. If the default INPUT policy is DROP or REJECT and the loopback interface isn't explicitly whitelisted, local traffic will fail.
Ensure this rule exists at the top of your INPUT chain:
sudo iptables -I INPUT 1 -i lo -j ACCEPT
Troubleshooting iptables Packet Loss
While 'connection refused' is definitive, 'iptables packet loss' is insidious. It manifests as slow connections, intermittent timeouts, or partial data transfers. The root causes usually revolve around state tracking, rate limiting, or MTU issues.
Cause 1: Conntrack Table Exhaustion
The netfilter framework uses a state machine module called conntrack to track active connections. This is what allows iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT to function.
However, the conntrack table has a maximum size. Under heavy load (e.g., a DDoS attack, a massive traffic spike, or applications creating thousands of short-lived connections), the table can fill up. When this happens, the kernel starts dropping new packets because it cannot track them, resulting in severe packet loss.
Check your kernel logs for this exact error message:
dmesg -T | grep -i "nf_conntrack: table full, dropping packet"
If you see this, you need to increase the conntrack maximum. First, check the current limit:
sysctl net.netfilter.nf_conntrack_max
To temporarily increase it (e.g., to 256,000):
sudo sysctl -w net.netfilter.nf_conntrack_max=256000
To make this permanent, add net.netfilter.nf_conntrack_max = 256000 to /etc/sysctl.conf and run sysctl -p. You may also need to increase the hashsize module parameter for optimal performance.
Cause 2: Rate Limiting Rules (limit and hashlimit)
iptables includes modules to rate-limit traffic, often used to mitigate brute-force attacks or ICMP floods. If these limits are too aggressive, legitimate traffic will experience packet loss.
Look for rules containing -m limit or -m hashlimit:
sudo iptables -nvL | grep limit
If you see counters incrementing rapidly on a rule like iptables -A INPUT -p tcp --dport 443 -m limit --limit 20/sec -j ACCEPT, it means packets exceeding 20 per second are falling through to the next rule (which is likely a DROP or REJECT). Adjust the --limit and --limit-burst parameters to accommodate your baseline traffic.
Cause 3: Docker and FORWARD Chain Policies
If you are running Docker, Kubernetes, or Libvirt, packet loss or connection refusals often occur in the FORWARD chain, not the INPUT chain. Traffic routed to containers does not terminate on the host; it passes through it.
Docker manipulates iptables aggressively. It creates custom chains (e.g., DOCKER, DOCKER-USER). If you set the default FORWARD policy to DROP without explicitly allowing traffic through the DOCKER-USER chain, external connections to mapped container ports will fail.
Always insert custom filtering rules for Docker into the DOCKER-USER chain, as this chain is evaluated before Docker's automatic NAT rules. For example, to block a specific IP from reaching your containers:
iptables -I DOCKER-USER -s 198.51.100.5 -j REJECT
Step-by-Step Fix Methodology
- Verify the Listener: Before blaming iptables, confirm the service is actually listening on the expected interface and port using
ss -tlnp. If it's bound to127.0.0.1and you are connecting from the outside, the connection will be refused regardless of firewall rules. - Log Dropped/Rejected Packets: Add a logging rule right before your suspected catch-all reject/drop rule to see exactly what is being blocked.
iptables -I INPUT 5 -p tcp --dport 8080 -j LOG --log-prefix "IPTABLES_REJECT: " - Monitor the Logs: Tail the system log (
tail -f /var/log/syslogorjournalctl -kf) while attempting the connection. The log will show the source IP, destination IP, MAC addresses, and TCP flags. - Apply the Fix: Once the offending rule is identified via logging or counters, use
iptables -Dto delete the bad rule oriptables -Ito insert an override above it. - Persist the Changes: Iptables rules are ephemeral. Once you have resolved the connection refused or packet loss issue, save the rules. On Debian/Ubuntu:
netfilter-persistent save. On RHEL/CentOS:iptables-save > /etc/sysconfig/iptables.
Frequently Asked Questions
#!/bin/bash
# Diagnostic script for iptables connection refused & packet loss
# 1. Check for conntrack table exhaustion (Packet Loss)
echo "=== Checking Conntrack Limits ==="
CURRENT_CT=$(sysctl net.netfilter.nf_conntrack_count | awk '{print $3}')
MAX_CT=$(sysctl net.netfilter.nf_conntrack_max | awk '{print $3}')
echo "Conntrack usage: $CURRENT_CT / $MAX_CT"
if [ "$CURRENT_CT" -ge "$MAX_CT" ]; then
echo "WARNING: Conntrack table is full! Packets are being dropped."
fi
# 2. Look for REJECT targets in the INPUT chain (Connection Refused)
echo -e "\n=== Checking for REJECT targets in INPUT ==="
sudo iptables -nvL INPUT --line-numbers | grep REJECT
# 3. Insert a temporary LOG rule for a specific port to trace drops
TARGET_PORT=8080
echo -e "\n=== Adding LOG trace for port $TARGET_PORT ==="
sudo iptables -I INPUT 1 -p tcp --dport $TARGET_PORT -j LOG --log-prefix "TRACE_PORT_$TARGET_PORT: "
echo "Monitor logs using: dmesg -w | grep TRACE_PORT_$TARGET_PORT"
# 4. Ensure loopback is allowed
echo -e "\n=== Checking loopback rules ==="
sudo iptables -nvL INPUT | grep -E 'lo.*ACCEPT'
if [ $? -ne 0 ]; then
echo "WARNING: Loopback interface might not be explicitly allowed."
echo "Fix with: sudo iptables -I INPUT 1 -i lo -j ACCEPT"
fiError Medic Editorial
Written by the Error Medic editorial team. We specialize in Linux networking, kernel tuning, and distributed systems troubleshooting.