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
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)
SNAT
is a final Netfilter target, packets will not show up in the same chain afterwards. ThePOSTROUTING
-chain in thenat
-table is the absolute final table a packet can traverse the Netfilter-framework. Tcpdump is attached to a fairly ealier stage, I think inmangle/POSTROUTING
.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.I would have expected to see something like this:
Maybe there is a service that already listens on that port.
iptables
's SNAT, when doing the translation will try to keep the same port as the one in the original packet. If this port is not available, it will use another one.I had this problem trying to SNAT a DNS server's replies. This resulted in the replies being sent from port 1 as the DNS server was already making use of port 53.
The solution was to have the dns server listen on a different port, REDIRECT the incoming traffic from destination port 53 to destination port X and then SNAT also rewriting the port from X to 53.