How to Configure iptables NAT Logging to Show Original Destination IPs for Incoming WAN Traffic


7 views

When setting up iptables as a router with MASQUERADE/NAT, many administrators face this logging behavior where incoming WAN traffic appears to terminate at the router's IP rather than showing the actual LAN destination. This occurs because the DNAT (Destination NAT) translation happens after the logging point in the packet flow.

Here's what happens to incoming packets:

1. Packet arrives at WAN interface (src=WAN_IP, dst=ROUTER_WAN_IP)
2. PREROUTING chain (logging happens here if enabled)
3. DNAT translation occurs (dst changes to LAN_IP)
4. Packet gets routed to LAN interface

To capture the post-NAT destination, we need to log either:

  • In the FORWARD chain (after NAT translation)
  • Using the NFLOG target to capture complete connection tracking

Here's the optimal iptables configuration:

# Log outgoing (LAN→WAN) connections in POSTROUTING
iptables -t nat -A POSTROUTING -o eth0 -j LOG --log-prefix "OUTBOUND: "

# Log incoming (WAN→LAN) connections in FORWARD chain
iptables -A FORWARD -i eth0 -j LOG --log-prefix "INBOUND: " --log-ip-options

For more detailed logging including NAT translations:

iptables -A FORWARD -j NFLOG --nflog-group 1 --nflog-prefix "FWD: "
iptables -t nat -A POSTROUTING -j NFLOG --nflog-group 1 --nflog-prefix "NAT: "

# Then monitor with:
ulogd -d

Here's a complete working configuration for a router with eth0 (WAN) and eth1 (LAN):

# Enable routing
sysctl -w net.ipv4.ip_forward=1

# Basic NAT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

# Logging rules
iptables -A FORWARD -i eth0 -m conntrack --ctstate NEW -j LOG \
  --log-prefix "NEW INBOUND: " --log-ip-options --log-tcp-sequence

iptables -A FORWARD -o eth0 -m conntrack --ctstate NEW -j LOG \
  --log-prefix "NEW OUTBOUND: " --log-ip-options

Sample log output after configuration:

NEW OUTBOUND: IN=eth1 OUT=eth0 SRC=192.168.1.100 DST=8.8.8.8
NEW INBOUND: IN=eth0 OUT=eth1 SRC=8.8.8.8 DST=192.168.1.100

Key parameters to include in logs for better analysis:

  • --log-ip-options (shows full IP headers)
  • --log-tcp-sequence (for TCP analysis)
  • --log-tcp-options (shows TCP flags)

When implementing iptables NAT (Network Address Translation) with masquerading on a Linux router, you'll notice an important logging limitation: incoming WAN-to-LAN connections get logged with the router's internal IP as the destination rather than the true LAN host IP. This breaks end-to-end flow visibility in your logs.

The sequence looks like this in raw logs:

# Outbound (correctly logged):
[OUT] SRC=192.168.32.10 DST=60.242.67.190

# Inbound (problematic logging):
[IN] SRC=60.242.67.190 DST=192.168.32.199 (router WAN IP)
[IN] SRC=192.168.32.199 DST=192.168.32.10 (router LAN IP)

This occurs because iptables processes the DNAT (destination NAT) translation before logging occurs in the PREROUTING chain.

The key is to capture packets in the raw PREROUTING chain before NAT translation occurs:

# For IPv4:
iptables -t raw -I PREROUTING -i eth0 -j TRACE

# For IPv6 (if needed):
ip6tables -t raw -I PREROUTING -i eth0 -j TRACE

Here's a production-ready configuration that preserves original IPs:

# Enable connection tracking logging
modprobe nf_conntrack
echo 1 > /proc/sys/net/netfilter/nf_conntrack_acct

# Log before NAT processing
iptables -t raw -A PREROUTING -j LOG --log-prefix "RAW-PRE: " --log-level 6
iptables -t raw -A OUTPUT -j LOG --log-prefix "RAW-OUT: " --log-level 6

# Main logging rules (post-NAT)
iptables -A FORWARD -j LOG --log-prefix "FWD: " --log-level 6
iptables -A INPUT -j LOG --log-prefix "IN: " --log-level 6
iptables -A OUTPUT -j LOG --log-prefix "OUT: " --log-level 6

For high-traffic routers, consider ULOG which sends logs to userspace:

iptables -A PREROUTING -t raw -j ULOG --ulog-nlgroup 1 --ulog-prefix "PRE-NAT: "
iptables -A POSTROUTING -t mangle -j ULOG --ulog-nlgroup 2 --ulog-prefix "POST-NAT: "

Your syslog will now contain both pre-and-post NAT information. Use this awk script to correlate flows:

awk '/RAW-PRE:/ { pre_src=$6; pre_dst=$8 } 
     /FWD:/ { if($6==pre_dst) print "Full flow:", pre_src, "->", $8, "via", $6 }' /var/log/syslog
  • Ensure conntrack is enabled (lsmod | grep conntrack)
  • Verify logging chain order (iptables -t raw -vnL)
  • Check kernel ring buffer if logs don't appear (dmesg | grep TRACE)

For persistent configuration, add these rules to your network init scripts or use iptables-persistent package.