When reproducing an issue with IP phones speaking SIP, I ran into the question of how to switch source port on my linux NAT router.

The problem the clients were having, were a result of a failing (or reset) NAT-gateway. The NAT-gateway would change the external source port mid-dialog (some SIP dialogs can persist for quite a long time).

So, how do you go about switching source port on an UDP connection on your Linux NAT router without resetting it (or disturbing anyone else using it)? The answer: conntrack(8)

Step (1): add a static SNAT mapping for a custom port, before the standard SNAT rules, for your specific application. In my case the phone is at 192.168.1.123, the destination at 123.123.123.123 and my external IP is 5.4.3.2.

# iptables -t nat -nvL POSTROUTING
Chain POSTROUTING (policy ACCEPT 25M packets, 1534M bytes)
 pkts bytes target     prot opt in     out     source               destination         
 348K   31M SNAT       0    --  *      eth2    0.0.0.0/0            0.0.0.0/0           to:5.4.3.2 
# iptables -t nat -I POSTROUTING -o eth2 -s 192.168.1.123 -d 123.123.123.123 -p udp --dport 5060 -j SNAT --to 5.4.3.2:7060

Step (2): start the dialog. It is now connection tracked:

# conntrack -L -s 192.168.1.123 -p udp --reply-port-dst 7060
udp      17 165 src=192.168.1.123 dst=123.123.123.123 sport=5060 dport=5060 packets=15 bytes=7670 src=123.123.123.123 dst=5.4.3.2 sport=5060 dport=7060 packets=37 bytes=10179 [ASSURED] mark=0 use=1

Step (3): remove static NAT mapping, optionally replacing it with a new one and kill the tracked connection.

# iptables -t nat -D POSTROUTING -o eth2 -s 192.168.1.123 -d 123.123.123.123 -p udp --dport 5060 -j SNAT --to 5.4.3.2:7060
# iptables -t nat -I POSTROUTING -o eth2 -s 192.168.1.123 -d 123.123.123.123 -p udp --dport 5060 -j SNAT --to 5.4.3.2:7061
# conntrack -D -s 192.168.1.123 -d 123.123.123.123 -p udp --orig-port-src 5060 --orig-port-dst 5060

Step (4): watch how the dialog continues on the new external source port. You're done.

# conntrack -L -s 192.168.1.123 -p udp --reply-port-dst 7061
udp      17 64 src=192.168.1.123 dst=123.123.123.123 sport=5060 dport=5060 packets=5 bytes=3208 src=123.123.123.123 dst=5.4.3.2 sport=5060 dport=7061 packets=6 bytes=2618 [ASSURED] mark=0 use=1

shell