Debugging iptables Port Forwarding and Masquerading Failures: A Complete Troubleshooting Guide


2 views

When examining the provided iptables configuration and logs, several interesting patterns emerge. The UDP port 7887 forwarding works perfectly (showing 12166 packets processed), while TCP-based services like SSH (port 22) and HTTP (port 80) show zero successful connections despite SYN packets reaching the FORWARD chain.

The kernel logs reveal SYN packets reaching the router (eth1) destined for 192.168.1.99:80, but no corresponding replies. This suggests either:

  • The destination server isn't responding
  • Return traffic is being blocked
  • MASQUERADE isn't working for the return path

The most significant issue in the current configuration is missing state tracking for NEW connections in the FORWARD chain. Notice how all working rules contain state checking for ESTABLISHED/RELATED connections, but new connections aren't properly handled.

# Problematic original rule:
iptables -A FORWARD -i $LAN -o $WAN -j ACCEPT

# Should be replaced with:
iptables -A FORWARD -i $LAN -o $WAN -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

Here's the corrected firewall.sh script with all necessary fixes:

#!/bin/sh

WAN="eth1"
LAN="eth2"

echo 1 > /proc/sys/net/ipv4/ip_forward
sysctl -w net.ipv4.ip_forward=1

iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X

iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

iptables -A INPUT -i $LAN -j ACCEPT
iptables -A OUTPUT -o $WAN -j ACCEPT
iptables -A OUTPUT -o $LAN -j ACCEPT

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

iptables -A FORWARD -o $LAN -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i $LAN -o $WAN -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -o $WAN -j MASQUERADE

# ICMP rules remain unchanged

# Corrected port forwarding examples:
# SSH
iptables -t nat -A PREROUTING -p tcp -i $WAN --dport 22 -j DNAT --to 192.168.1.250:22
iptables -A FORWARD -p tcp -i $WAN -o $LAN -d 192.168.1.250 --dport 22 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

# HTTP/HTTPS
iptables -t nat -A PREROUTING -p tcp -i $WAN -m multiport --dports 80,443 -j DNAT --to 192.168.1.99
iptables -A FORWARD -p tcp -i $WAN -o $LAN -d 192.168.1.99 -m multiport --dports 80,443 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

After implementing these changes, verify with:

# Check NAT rules with counters
iptables -t nat -L -v -n

# Monitor real-time traffic
tcpdump -ni eth1 port 80 or port 22

# Check connection tracking
conntrack -L
  • Not specifying --state NEW for forwarded connections
  • Mixing multiport with single-port rules inconsistently
  • Forgetting that DNAT happens before routing decisions
  • Overlooking that MASQUERADE only handles outbound traffic

When examining the iptables configuration, we notice an interesting pattern: UDP port 7887 forwarding to 192.168.1.100 works perfectly (evident from the 12,166 packet count in NAT table), while all TCP-based port forwards (SSH, HTTP, Tor ports) fail completely with connection timeouts.

The kernel logs reveal SYN packets reaching the FORWARD chain but not being accepted:

May  8 21:04:18 router kernel: [11692.837693] FOWARD: IN=eth1 OUT=eth2 SRC=130.235.35.233 DST=192.168.1.99 LEN=60 TOS=0x00 PREC=0x00 TTL=52 ID=35931 DF PROTO=TCP SPT=52319 DPT=80 WINDOW=14600 RES=0x00 SYN URGP=0

The current firewall script has several problematic areas:

# Problematic OUTPUT policy
iptables -P OUTPUT DROP

# Missing state tracking for NEW connections in FORWARD chain
iptables -A FORWARD -i $LAN -o $WAN -j ACCEPT  # Too permissive
iptables -A FORWARD -o $LAN -m state --state ESTABLISHED,RELATED -j ACCEPT  # Only allows replies

Here's the fixed version with key improvements:

#!/bin/sh

WAN="eth1"
LAN="eth2"

echo 1 > /proc/sys/net/ipv4/ip_forward
sysctl -w net.ipv4.ip_forward=1

# Flush existing rules
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X

# Default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT  # Changed from DROP to ACCEPT

# Localhost
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# Allow LAN traffic
iptables -A INPUT -i $LAN -j ACCEPT
iptables -A OUTPUT -o $LAN -j ACCEPT

# Stateful rules
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow NEW connections from LAN to WAN
iptables -A FORWARD -i $LAN -o $WAN -m state --state NEW -j ACCEPT

# Masquerade
iptables -t nat -A POSTROUTING -o $WAN -j MASQUERADE

# ICMP
iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT

# Port forwards with explicit NEW state acceptance
forward_port() {
    local proto=$1
    local port=$2
    local dest=$3
    
    iptables -t nat -A PREROUTING -p $proto -i $WAN --dport $port -j DNAT --to $dest
    iptables -A FORWARD -p $proto -i $WAN -o $LAN -d $dest --dport $port -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
}

forward_port tcp 22 192.168.1.250
forward_port tcp "80,443" 192.168.1.99
forward_port tcp "9001,9030" 192.168.1.250
forward_port tcp 7887 192.168.1.100
forward_port udp 7887 192.168.1.100
forward_port tcp 8887 192.168.1.250
forward_port udp 8887 192.168.1.250
  • Changed OUTPUT policy to ACCEPT to allow return traffic
  • Added explicit NEW state tracking for forwarded connections
  • Created a forwarding helper function for consistent rule creation
  • Simplified the FORWARD chain logic while maintaining security

After applying the fixes, verify with:

# Check NAT table counters
iptables -t nat -L -v -n

# Monitor real-time traffic
tcpdump -i eth1 'port 22 or port 80'

# Test from external host
nc -zv WAN_IP 22
curl -v http://WAN_IP/