To configure Source NAT for multi-interface systems, you must use policy-based routing combined with iptables rules that match traffic based on its incoming interface or source address. This ensures that outbound packets from different internal networks are translated to the correct public IP address associated with their respective gateway interfaces, preventing asymmetric routing and connection failures.
Understanding the Network Topology
Before applying any configuration, it is essential to visualize how your system handles traffic flow. In a typical multi-homed setup, you have multiple network interfaces, each connected to a different upstream provider or subnet. For example, Interface A might connect to Provider 1 with public IP
203.0.113.1, while Interface B connects to Provider 2 with public IP
198.51.100.1. Internal clients on Subnet A (e.g.,
10.0.1.0/24) should exit via Interface A, and clients on Subnet B (e.g.,
10.0.2.0/24) should exit via Interface B.
Without proper Source NAT configuration, packets from Subnet A might be routed out through Interface B if the default route points there, causing the return traffic to fail because the response would go to an IP address not owned by that interface. The goal is to ensure that every packet leaving an interface has a source IP that matches the IP assigned to that specific interface. This process is often referred to as masquerading when dealing with dynamic IPs or SNAT when using static addresses.
Configuring Policy-Based Routing
The foundation of this setup is Linux policy-based routing. You cannot rely solely on the main routing table because it typically contains only one default route. Instead, you need separate routing tables for each interface's subnet.
First, define custom routing tables in
/etc/iproute2/rt_tables. Add entries such as:
200 table_A
201 table_B
Next, create rules that direct traffic originating from specific subnets to use these custom tables. Use the
ip rule command to add policies. For example, to send traffic from
10.0.1.0/24 to
table_A:
ip rule add from 10.0.1.0/24 table table_A
ip rule add from 10.0.2.0/24 table table_B
Then, populate these tables with appropriate routes. Each table must know how to reach its local network and where to send default traffic. For
table_A:
ip route add 10.0.1.0/24 dev eth0 src 10.0.1.1
ip route add default via 203.0.113.254 dev eth0
Repeat this for
table_B using the corresponding interface (
eth1) and gateway. This step ensures that the kernel knows which interface to use for outgoing packets before NAT even comes into play. If you are migrating from a single-interface setup, check
How to Configure a Traffic Route from VM1 through Host to VM2 for foundational routing concepts.
Implementing Source NAT Rules
Once routing is established, you can apply iptables rules to translate the source addresses. The key is to match the traffic based on the outgoing interface or the source subnet. Using the
-o (outgoing interface) flag is the most robust method because it directly ties the NAT rule to the physical path the packet takes.
For Interface A (
eth0), add the following masquerade rule:
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
For Interface B (
eth1), add a similar rule:
iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
Using
MASQUERADE is preferred over
SNAT if your provider assigns dynamic IPs to these interfaces, as it automatically detects the current IP of the outgoing interface. If the IPs are static, you can use
-j SNAT --to-source 203.0.113.1 for better performance, but
MASQUERADE is safer for general configurations. Ensure that forwarding is enabled in the kernel by checking
net.ipv4.ip_forward=1 in
/etc/sysctl.conf.
Verification and Troubleshooting
After applying these changes, verify the configuration by testing connectivity from hosts in both subnets. From a host in
10.0.1.0/24, ping an external IP and then check the iptables counters using
iptables -t nat -L -v -n. You should see packet increments on the rule associated with
eth0. Similarly, test from
10.0.2.0/24 and confirm traffic hits the
eth1 rule.
If connections fail, check the routing tables first. Use
ip route get <external_ip> from the internal host to see which interface and gateway the kernel selects. If the wrong interface is chosen, review your
ip rule statements. Also, ensure that no other firewall rules are blocking the forwarded traffic. For more complex network issues, such as those involving VPNs, refer to
Troubleshooting: Cannot Connect to the Internet to isolate DNS or basic connectivity problems.
Frequently Asked Questions
Why do I need policy-based routing instead of just NAT?
NAT only changes the source IP; it does not determine which interface the packet leaves through. Without policy-based routing, the kernel may send the packet out the wrong interface based on the default route, causing the return traffic to be lost because the reply goes to an IP address not configured on that interface.
Can I use SNAT instead of MASQUERADE?
Yes, if your public IP addresses are static and do not change frequently, SNAT is slightly more efficient because it does not need to look up the current IP of the interface for every packet. However, MASQUERADE is recommended for dynamic IPs assigned by ISPs, as it automatically adapts to IP changes.
What happens if I forget to enable IP forwarding?
If IP forwarding is disabled, the system will act as a strict endpoint rather than a router. Packets arriving from one interface destined for another will be dropped immediately by the kernel, regardless of your NAT or routing rules. Always verify
sysctl net.ipv4.ip_forward returns 1.
How do I debug why a packet is taking the wrong route?
Use the
ip route get <destination_ip> command from the client machine. This shows the exact path the kernel calculates for that specific destination. It reveals which routing table is being used and which gateway is selected, helping you identify mismatches between your rules and actual traffic flow.
Does this configuration work with IPv6?
The principles are similar, but you must use ip6tables for IPv6 NAT and separate routing tables for IPv6 prefixes. IPv6 often uses SLAAC or DHCPv6 for address assignment, so ensure your policy rules match the correct source prefixes rather than relying on simple subnet masks.