<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ho Kok Tong</title>
    <description>The latest articles on DEV Community by Ho Kok Tong (@jelly5199).</description>
    <link>https://dev.to/jelly5199</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3608337%2Fac27cf98-e8f6-450a-a2e0-cbad8f97a4ee.png</url>
      <title>DEV Community: Ho Kok Tong</title>
      <link>https://dev.to/jelly5199</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jelly5199"/>
    <language>en</language>
    <item>
      <title>Tcpdump and SNAT on Linux NAT Server: Why Outgoing SNAT Packets Don’t Appear in Captures (and How to Fix It)</title>
      <dc:creator>Ho Kok Tong</dc:creator>
      <pubDate>Thu, 13 Nov 2025 14:57:00 +0000</pubDate>
      <link>https://dev.to/jelly5199/tcpdump-and-snat-on-linux-nat-server-why-outgoing-snat-packets-dont-appear-in-captures-and-how-252l</link>
      <guid>https://dev.to/jelly5199/tcpdump-and-snat-on-linux-nat-server-why-outgoing-snat-packets-dont-appear-in-captures-and-how-252l</guid>
      <description>&lt;p&gt;When troubleshooting NAT traffic, many administrators face a confusing scenario — &lt;strong&gt;&lt;code&gt;tcpdump&lt;/code&gt; doesn’t capture outgoing SNAT (POSTROUTING) packets&lt;/strong&gt;.&lt;br&gt;
This article explains &lt;em&gt;why&lt;/em&gt; that happens, visualizes the packet flow, and shows &lt;em&gt;how to capture post-SNAT packets correctly&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  🧩 Understanding the Issue: Why Tcpdump Misses SNAT Packets
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Core Reason
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tcpdump&lt;/code&gt; captures packets &lt;strong&gt;before NAT (SNAT)&lt;/strong&gt; occurs.&lt;br&gt;
It hooks into the packet path at a point &lt;strong&gt;before the POSTROUTING&lt;/strong&gt; chain.&lt;/p&gt;

&lt;p&gt;In other words, &lt;code&gt;tcpdump&lt;/code&gt; uses packet capture hooks from the kernel’s &lt;code&gt;AF_PACKET&lt;/code&gt; socket layer.&lt;br&gt;
These hooks see packets &lt;strong&gt;as they enter or leave a network interface&lt;/strong&gt;, but &lt;strong&gt;not after&lt;/strong&gt; all iptables &lt;code&gt;nat&lt;/code&gt; table processing — especially &lt;strong&gt;not after SNAT&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;Simplified Packet Flow:&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Incoming packets
      ↓
[PREROUTING] (DNAT) ← tcpdump sees here
      ↓
Routing / local process
      ↓
[POSTROUTING] (SNAT) ← tcpdump does NOT see here
      ↓
Network device
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why SNAT Packets Are Missing in tcpdump Captures
&lt;/h3&gt;

&lt;p&gt;SNAT takes place in the &lt;strong&gt;POSTROUTING&lt;/strong&gt; chain, which &lt;strong&gt;modifies packets after&lt;/strong&gt; they’ve been queued to the output device.&lt;br&gt;
By that time, &lt;code&gt;tcpdump&lt;/code&gt; has already captured the packet — showing the &lt;strong&gt;pre-SNAT source IP&lt;/strong&gt;.&lt;br&gt;
So even though the packets are being SNATed and sent out, &lt;code&gt;tcpdump -i any&lt;/code&gt; will only display &lt;strong&gt;their pre-SNAT&lt;/strong&gt; form.&lt;/p&gt;
&lt;h2&gt;
  
  
  ✅ Solution: Capturing Outgoing SNAT (POSTROUTING) Packets
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Option 1: Use NFLOG (iptables-based logging)
&lt;/h3&gt;

&lt;p&gt;You can explicitly log SNAT traffic using the NFLOG target and then capture it with tcpdump or nflog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using the NAT Table (Recommended ✅)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo iptables -t nat -A POSTROUTING -j NFLOG --nflog-group 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then capture it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Option 1
sudo nflog -i 5 -o postrouting.pcap

# Option 2
sudo tcpdump -i nflog:5 -w postrouting_nat.pcap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tcpdump -i nflog:5 -w file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uses libpcap interface directly — quick for analysis or &lt;code&gt;.pcap&lt;/code&gt; capture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nflog -i 5 -o file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Native Netfilter log reader — good for direct NFLOG dumps (older tool)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This approach logs packets &lt;strong&gt;after SNAT translation&lt;/strong&gt;, so you’ll see the &lt;strong&gt;post-NAT source IP&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Tcpdump on Network Interface (❌ Still Misses SNAT)
&lt;/h3&gt;

&lt;p&gt;Even if you try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo tcpdump -i eth0 -n -w out.pcap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may still &lt;strong&gt;not see SNAT packets&lt;/strong&gt;, because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tcpdump hooks into AF_PACKET, before packets reach the NIC driver.&lt;/li&gt;
&lt;li&gt;SNAT occurs very late in the path — after routing, checksum, and queuing.&lt;/li&gt;
&lt;li&gt;Unless you capture below the NAT hook (e.g., with eBPF, XDP, or hardware mirroring), you’ll only get pre-SNAT data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧹 How to Remove NFLOG Rule from iptables after captured
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; List Current Rules&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo iptables -t nat -L POSTROUTING -v --line-numbers

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
num  pkts bytes target  prot opt in  out  source  destination
1    0    0     NFLOG   all  --  any any  anywhere anywhere nflog-group 5
2    0    0     SNAT    all  --  any eth0 anywhere anywhere to:1.2.3.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Delete the Rule&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo iptables -t nat -D POSTROUTING 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Verify&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo iptables -t nat -L POSTROUTING -v --line-numbers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4 (Optional):&lt;/strong&gt; Save Configuration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo service iptables save
# or
sudo iptables-save &amp;gt; /etc/sysconfig/iptables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧠 Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key Point&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;tcpdump&lt;/code&gt; sees pre-NAT packets&lt;/td&gt;
&lt;td&gt;It hooks into AF_PACKET before POSTROUTING&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNAT happens after routing&lt;/td&gt;
&lt;td&gt;Hence not visible to tcpdump&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;To see SNAT’d packets&lt;/td&gt;
&lt;td&gt;Use NFLOG in the NAT table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mangle table captures pre-SNAT&lt;/td&gt;
&lt;td&gt;NAT table captures post-SNAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regular tcpdump still misses SNAT&lt;/td&gt;
&lt;td&gt;Needs NFLOG, eBPF, or hardware mirroring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🔍 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Tcpdump is an essential diagnostic tool — but it’s limited by where it hooks into the kernel’s packet path.&lt;br&gt;
When debugging NAT issues, always remember: &lt;strong&gt;SNAT occurs after tcpdump’s capture point&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Use &lt;strong&gt;NFLOG&lt;/strong&gt; on the &lt;strong&gt;NAT table&lt;/strong&gt; to view the real, post-SNAT traffic.&lt;br&gt;
With this method, you’ll finally see the &lt;strong&gt;true outgoing source IP&lt;/strong&gt; as it leaves your Linux NAT server.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>linux</category>
      <category>tooling</category>
      <category>networking</category>
    </item>
  </channel>
</rss>
