0

I want to use Source NAT to change the local IP address of UDP traffic. However, only locally-generated traffic has the NAT rule applied, replies to traffic generated from remote sources do not have the NAT applied.

Is locally-generated traffic different from replies to traffic from remote in a udp server?

I find a question from Redhat, but I can't access it...... The link is https://access.redhat.com/solutions/4427871

Environments:

  • OS: Debian 11 x64
  • iptables config
    iptables -t nat -A POSTROUTING -s 192.168.169.130 -p udp --sport 80 -j SNAT --to-source 192.168.169.129:10080
    
  • tcpdump results, the first seven items applied the SNAT rule, however the following items didn't apply the SNAT rule.

1 Answer 1

1

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.

6
  • It helps me a lot. I have made it work for UDP with -j CT --notrack. But I can't make it work for TCP. Can I setup a stateless SNAT for TCP with -j CT --notrack ? Thanks!
    – Peiyuan
    Commented Mar 22, 2022 at 17:43
  • This is my iptables rules: rules image
    – Peiyuan
    Commented Mar 22, 2022 at 17:57
  • TCP requires bidirectional communication. If you SNAT in one direction, you must equally DNAT in the other direction. (Normally conntrack would do that for you, but if you use notrack then you have to do it manually...) I would suggest using notrack only for UDP, because with TCP it's already clear which side is initiating the connection. Commented Mar 22, 2022 at 18:09
  • Thanks. I have described my problem in detail at iptables SNAT rules for TCP is only applied to some traffic. Could you give me some advice?
    – Peiyuan
    Commented Mar 23, 2022 at 8:03
  • I have solved this problem with nftables. Maybe iptables is too difficult to setup. Thanks for your help again!
    – Peiyuan
    Commented Mar 23, 2022 at 18:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .