Relentless Coding

A Developer’s Blog

Convert Linux into NAT Router

Sometimes it comes in handy to have a Linux box act as a router. You might not have a switch lying around. You might want to monitor all traffic coming and going to some device. In this post, we will set up Linux to act as a simple IPv4 NAT router.

We will call the Linux machine linux and the machine using the Linux box for internet breakout remote.

  1. You need 2 NICs on your Linux. They can be 2 ethernet ports. But you could also use your wireless interface for this. In this post, we assume they are cabled connections called eth0 and eth1.

  2. Connect eth0 to the gateway.

  3. Connect the two machines with an ethernet cable. The cable in the Linux machine should go in eth1.

  4. Make sure the interfaces on both sides are up:

    linux# ip link set eth1 up
    remote# ip link set eth0 up
    

    Reminder: you can abbreviate these commands to ip l s eth<n> up.

  5. Assign both ends an RFC 1918 private IPv4 address. You could use link-local addresses (APIPA):

    linux# ip addr add 169.254.0.1/32 peer 169.254.0.2 dev eth1
    remote# ip addr add 169.254.0.2/32 peer 169.254.0.1 dev eth0
    

    Shorter: ip a a 169.254.0.1/32 peer 169.254.0.2 dev eth1

    I configure the addresses to be point-to-point, because there are only 2 hosts on this network and they are directly connected.

  6. Enable IPv4 forwarding in the kernel on the Linux router

    linux# sysctl -w net.ipv4.ip_forward=1
    
  7. Enable NAT on the Linux router.

    linux# iptables -t nat -A POSTROUTING \
        -s 169.254.0.0/16 \
        -o eth0 \
        -j MASQUERADE
    

    This will rewrite the source address of the packets with the original source address in the 169.254/16 network to the primary source address of the outgoing interface eth0. The MASQUERADE target is easiest to use, but:

    It should only be used with dynamically assigned IP (dialup) connections: if you have a static IP address, you should use the SNAT target. Masquerading is equivalent to specifying a mapping to the IP address of the interface the packet is going out, but also has the effect that connections are forgotten when the interface goes down. This is the correct behavior when the next dialup is unlikely to have the same interface address (and hence any established connections are lost anyway).

    iptables-extensions(8)

    The alternative is to use the SNAT target with a static (private) IPv4 address. It should be an address configured on the interface which you either configured statically yourself, or you got from a DHCP server. You can check with ip -br addr show eth0. Here, I am using 192.168.1.123:

    linux# iptables -t nat -A POSTROUTING \
        -s 169.254.0.0/16 \
        -o eth0 \
        -j SNAT --to-source 192.168.1.123
    
  8. If you have a strict firewall on your Linux router, you might have set iptables -P FORWARD DROP. You need to allow traffic to flow between the 2 NICs. For example:

    linux# iptables -I FORWARD -i eth1 -o eth0 -j ACCEPT
    linux# iptables -I FORWARD -i eth0 -o eth1 -j ACCEPT
    
  9. Finally, make the Linux router the default gateway of the dependent box:

    remote# ip route add default via 169.254.0.1 dev eth0
    

    You can abbreviate route add to r a.

A Note on Interface Names

If you have complicated setup with virtual interfaces (VPN, VLAN) and a firewall in place, add logging to your firewall rules when packets get dropped, so you know which interfaces to use in your source NAT (-t nat -A POSTROUTING) and FORWARD rules.