Linux iptables NAT is applied to conntrack states rather than individual packets.
If conntrack is already tracking a flow (e.g. after it has received some inbound UDP packets), further packets matching that flow won't touch the nat
table at all – they'll only have forward or reverse translations applied according to what's already in conntrack.
For example, if there was an outbound DNS query (which was SNATed to 193.299.181.993)1, conntrack -L
might show a state like this, with its "reply-to" fields being used to match packets in the opposite direction:
src |
dst |
sport |
dport |
reply-to src |
reply-to dst 1 |
r-to sport |
r-to dport |
10.1.124.56 |
83.171.22.22 |
39202 |
53 |
83.171.22.22 |
193.299.181.993 |
53 |
39202 |
So if your system sent some outbound packets first, they will match the iptables rule and get SNATed. But if conntrack thinks that the system sent those packets as a reply to non-DNATed inbound packets, then the outbound ones will not be checked against iptables nat
rules.
Use the conntrack
tool to verify this – for example, try deleting the matching states and see if NAT suddenly starts working.
If this turns out to be the problem, I can think of a few potential workarounds:
- Apply DNAT
--to-dest 192.168.169.129:10080
to inbound packets, then all matching outbound replies will be SNATed accordingly as well.
- Do something with
-j CT --notrack
flag, which prevents that packet from being stored as a conntrack state.
- Convert your ruleset to nftables, which has a more flexible chain/hook priority sstem, and where it is possible to apply stateless NAT by just setting the
ip saddr
field. (See example.) It may be possible to stack iptables and nftables on the same system.
1 Yes, that's supposed to be an obviously fake IP address.