Linux – IPtables SNAT eats packets

iptableslinuxnat;

I am trying to translate outgoing UDP packets with a source port of X to a source port of Y.

I have done this using the following iptables rule:

iptables -t nat -A POSTROUTING -s 10.0.0.1 -p udp --sport X -j SNAT --to-source 10.0.0.1:Y

The counters for this rule increase when packets with a source port of X are generated, but completely vanish after that. I cannot find them in the counters of any other chain or table, and cannot see them on any interface using tcpdump.

If I remove that rule, then the packets are received fine with the source port of X. But as soon as I put the rule back, the packets vanish.

I am using iptables version v1.2.11 running on Voyage Linux. I am unable to easily update as this will need to be done on a few hundred remote devices.

What am I doing wrong?

Edit: iptables config added below, rules related to specific applications that can't affect this have been removed.

# Clear any existing rules 
iptables -v -t filter   -F
iptables -v -t nat      -F
iptables -v -t mangle   -F

iptables -v -t filter   -X
iptables -v -t nat      -X
iptables -v -t mangle   -X

# Policies
iptables -t mangle      -P PREROUTING   ACCEPT
iptables -t nat         -P PREROUTING   ACCEPT
iptables -t filter      -P INPUT        DROP
iptables -t filter      -P OUTPUT       ACCEPT
iptables -t filter      -P FORWARD      DROP

# Allow established connections.
iptables -t filter      -A INPUT      -m state --state ESTABLISHED -j ACCEPT
iptables -t filter      -A INPUT      -m state --state ESTABLISHED,RELATED -j ACCEPT  
iptables -t filter      -A FORWARD      -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t filter      -A FORWARD      -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t filter      -A OUTPUT       -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t filter      -A OUTPUT       -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow localhost to talk to itself.
iptables -t filter  -A INPUT      -i lo -j ACCEPT

# Drop stealth scan. 
iptables -t filter      -A INPUT      -p tcp -s 0/0 -d 0/0 --tcp-flags ALL NONE -j DROP                      
iptables -t filter      -A INPUT      -p tcp -s 0/0 -d 0/0 --tcp-flags ALL ALL -j DROP                       
iptables -t filter      -A INPUT      -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -j DROP
iptables -t filter      -A INPUT      -p tcp -m tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
iptables -t filter      -A INPUT      -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -j DROP
iptables -t filter      -A INPUT      -p tcp -m tcp --tcp-flags FIN,RST FIN,RST -j DROP 
iptables -t filter      -A INPUT      -p tcp -m tcp --tcp-flags ACK,FIN FIN -j DROP
iptables -t filter      -A INPUT      -p tcp -m tcp --tcp-flags ACK,URG URG -j DROP

# Allow forwarding from LAN to WAN.
iptables -t filter      -A FORWARD      -i lanif -o wanif -j ACCEPT

# The NAT, strict with fixed IP address - might be different with a DHCP assigned WAN_IP
iptables -t nat         -A POSTROUTING  -o wanif -j SNAT --to-source $WAN_IP
iptables -t nat         -A POSTROUTING  -m mark --mark 11 -j ACCEPT

# Change source port X to Y - why does this not work???
iptables -t nat     -A POSTROUTING  -s lanif -p udp --sport X -j SNAT --to-source wanif:Y

Best Answer

What am I doing wrong?

Probably nothing. It's the way packets traverse Netfilter.

Check this diagram as a reference:

(Source: Iptables Tutorial 1.2.1 by Oskar Andreasson, 2006)

I cannot find them in the counters of any other chain or table

SNAT is a final Netfilter target, packets will not show up in the same chain afterwards. The POSTROUTING-chain in the nat-table is the absolute final table a packet can traverse the Netfilter-framework. Tcpdump is attached to a fairly ealier stage, I think in mangle/POSTROUTING.

and cannot see them on any interface using tcpdump. That's because tcpdump sees the packet's before they are getting SNATed.

Is something actually going wrong? It sounds like perfectly normal Netfilter-tcpdump-oddities.

Edit: Your SNAT statement happens in the end. Maybe you need to insert it before the -o wanif -j SNAT --to-source $WAN_IP statement. Since I have no more details, I can't tell if it's a mistake or intentional.

Related Topic