Table of Contents

Ubuntu - iptables - Share an IP address between clients

To share a public IP address between two or more clients using iptables.


Scenario

Suppose that you have three machines that need access to the public Internet, but only one public IP address.

You have given the public address to a fourth machine which will act as a router.

All four machines are connected via a local area network on which the router is 192.168.0.1/24 and the three clients are 192.168.0.2, 192.168.0.3 and 192.168.0.4.

The external interface of the router is ppp0 and its address is 203.0.113.1.


Prerequisities

These instructions assume that:


Method

For outbound traffic to the public Internet there are two things which iptables must do:

The SNAT target performs both of these tasks. It should be used in the POSTROUTING chain of the nat table, however it should not be allowed to act on all traffic passing through that chain: only traffic destined for the public Internet. The simplest way to ensure this is to make the rule specific to the external interface of the router (ppp0):

iptables -t nat -A POSTROUTING -o ppp0 -j SNAT --to 203.0.113.1

Only the first packet of a connection traverses the POSTROUTING table: subsequent packets are automatically redirected. For this reason it is not necessary to provide a rule for the return path.

You should not use the SNAT target if you have a dynamic IP address. Instead use the MASQUERADE target, as described below. Also below are details of how to select the traffic by IP address as opposed to interface name, which may be preferable in some circumstances.


Testing

Testing must be done from a machine on the internal side of the router, which in this case is the local area network. A ping to a third-party public webserver will suffice for most purposes. You should try to avoid dependencies on services other than the one you are testing:


Troubleshooting

Use tcpdump or a similar tool to answer the following questions, stopping at the first one for which the answer is no:

  1. Does the outbound connection request reach the router?
  2. Does the router forward the request on to the external network?
  3. When the request leaves the router does it have an unchanged destination address, and a source address equal to the external address of the router?
  4. Does the response reach the router?
  5. Does the router forward the response on to the internal network?
  6. When the response leaves the router does it have an unchanged source address, and a destination address equal to that of the test machine?
  7. Does the response reach the test machine?

A failure at step 1, 4 or 7 indicates an issue that is unconnected with iptables or NAT, and which will need to be addressed before you can test further.

A failure at step 2 could indicate that:

A failure at step 3 could indicate that:

A failure at step 5 could indicate that:

Finally, a failure at step 6 could indicate that:

Further information about how to investigate these issues can be found in the troubleshooting guides for iptables and routing.


Variations

SNAT with a dynamic IP address

iptables provides an alternative to the SNAT target called MASQUERADE that is specifically intended for use with dynamic IP addresses. It has two benefits over SNAT in that situation:

If in the scenario above the public IP address had been assigned dynamically then an appropriate rule would be:

iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE

Traffic selection using IP addresses

The recommendation above was to make the SNAT rule specific to the external interface of the router. This is a safe option that gives precisely the right behaviour, however there may be circumstances where selection by IP address is preferred. For example:

There will usually be at least two address ranges that need to be excluded, therefore this cannot be done with a single rule. A solution that scales to any number of ranges is to remove them from the chain using the ACCEPT target:

iptables -t nat -A POSTROUTING -d 127.0.0.0/8 -j ACCEPT
iptables -t nat -A POSTROUTING -d 192.168.0.0/24 -j ACCEPT
iptables -t nat -A POSTROUTING -j SNAT --to 203.0.113.1