Friday, October 31, 2008

Enabling routing and NAT with iptables

(This post is the 5th part of my Ubuntu Linux Router Upgrade Project.)

After my last post, I now have a persistent Internet connection configured through PPP. Now it's time to make the connection available to the rest of my Local Area Network.

Enabling Routing

IPv4 (and also IPv6) routing is already built-in to the Linux kernel (at least under Ubuntu Hardy Heron), and simply needs to be enabled. The easiest way to do this is to edit "/etc/sysctl.conf", which like many of the other configuration changes made in this project, needs to be edited as root. (Use sudo.)

At least under Ubuntu Hardy Heron, a sysctl.conf file already exists with a few default options, and many other commented and documented options. The option that needs to be enabled is "net.ipv4.ip_forward=1", either by removing the prefixed "#" comment character, or by adding the line somewhere in the file.

This setting can then be immediately placed into effect without rebooting, etc., by executing "/sbin/sysctl -p".

Note that there are many related pages on the web that refer to echoing a "1" into "/proc/sys/net/ipv4/ip_forward". While this may work temporarily, it will most likely be set during reboot, so the above method should almost always be used instead.

The case for NAT

At this point, assuming there is both a public / WAN (Internet) interface and a private / LAN interface, Linux will route between them. However, assuming the LAN is configured with RFC1918 non-routable addresses e.g. 192.168.*.* (see also Private network on Wikipedia), these addresses aren't recognized on the Internet, and Linux shouldn't (and probably doesn't) even try to forward traffic from them. Even if this wasn't the case and traffic from the LAN destined for the Internet was forwarded, no other router on the Internet would know where to send the responses back to, without having configured routing rules back to the local router.

Most ISP consumers only receive one public IP address, which is typically dynamic and changes regularly. NAT, or Network Address Translation is the most commonly used method to allow multiple computers / devices on a LAN to share this one address. (This is the same method that is used by almost all SOHO routers, e.g. Linksys, Netgear, and D-Link.)

Configuring iptables and NAT

NAT is commonly implemented in Linux within iptables. At least under Ubuntu Hardy Heron, iptables is already installed and running, just using a default set of rules that effectively allows everything. (This is not necessarily an issue for a new installation as there are no services installed and running to connect to that can be compromised.)

iptables is typically configured using the "iptables" command (which is actually located at "/sbin/iptables"). Usually several steps are needed for a desired configuration, and are typically entered as separate calls to iptables. These are typically consolidated into a shell script.

Here is the simplest configuration using iptables necessary to enable sharing from a WAN to a LAN:

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

Again, since this alters the configuration of the system, the commands or script containing the commands need to be run with sudo. If forgotten, an error containing "Permission denied (you must be root)" should be displayed.

At least under the default iptables configuration in Ubuntu Hardy Heron, this is all that is needed as the default policy for everything is ACCEPT. This command basically changes the source address of any outgoing packets (in this case, on ppp0) to the outgoing interface's address, so return packets are sent to the router's public / WAN address. This is really the only valid option, as the original private / LAN addresses are invalid, as described above. These masqueraded connections are then tracked, such that when a response is received, the process can be reversed, and the response is sent to the original requesting device.

Here is a more comprehensive example, one that I saved as "/etc/iptables.conf":


logger -i $0 Starting...
iptables -F
iptables -F -t nat


if [ -d "/proc/sys/net/ipv4/conf/$WAN_IF" ]; then

 #WAN_IP=$(ifconfig $WAN_IF | sed -n 's/ *inet addr:\([0-9.]*\).*/\1/p')

 iptables -t nat -A POSTROUTING -o $WAN_IF -j MASQUERADE


logger -i $0 Complete.
  • This version first flushes all existing rules ("iptables -F"), ensuring a consistent configuration.
  • The WAN-dependent portions run only if the WAN interface exists.
  • It also logs to the system logger using logger, for future diagnosis.
  • Variables e.g. "WAN_IF" are used for maintainability, making it easier to update when the configuration changes. "WAN_IP" and "LAN_IF" are currently not used, but shown for example.

This is not the most secure configuration by any means, but it's a simple solution for solving the immediate need that can be extended upon. Some additional details are available in the "Masquerading Made Simple HOWTO".

Making it Automatic

So far, everything configured with iptables will be lost on reboot. The best way I found to handle this is by placing a link to the above "/etc/iptables.conf" in "/etc/network/if-up.d". Use "ln -s" to do this, or write a new script that calls the above. The same may be desirable for capturing when the interface goes down, using "/etc/network/if-down.d". As documented in run-parts, ensure that any desired files are marked executable, and follow the proper naming (consist entirely of upper and lower case letters, digits, underscores, and hyphens - no periods!).

For connections that utilize IP leases using DHCP, and if utilizing a DHCP IP address within iptables, it is probably also necessary to make sure that the rules are updated by re-running iptables when a lease is renewed. Similar to the above, scripts can be put in or linked to from the "dhclient-enter-hooks.d" and/or "dhclient-exit-hooks.d" directories, which executed by the default "/sbin/dhclient-script" script when using dhclient.

As I'm currently using a PPP connection which doesn't utilize DHCP, this wasn't necessary for me. Instead, I tied into the PPP connection process by linking to "/etc/iptables.conf" in "/etc/ppp/ip-up.d", same as described above.

To be continued...

Next up: Configuring LAN DHCP and Dynamic DNS.

No comments: