So, you want to set up a linux router?

First, get yourself a 2x4 to beat yourself with when you are frustrated.

Sunday, June 25th, 2000: My net connection is down. Life sucks. I could be playing a game of Network Settlers right now, but I'm working on this doc instead.

Next, read this doc.


Since each individual requirement can differ greatly, I'll first describe my scenario, then get into the nitty gritty of the solution.

I currently have DSL line with 4 fixed ip addresses. Three of which are already in use, one for the site you are reading now. Those three are assigned currently to one machine yet different web sites that live on that machine. The fourth is assigned to another machine which I use on a day to day basis.

My requirements then were as follows:

  • Route incoming requests for my web sites to the server sitting behind the firewall for news, ftp, http, and https.
  • Route outgoing requests from internal machines through the fourth, unused ip address.
  • Move the DNS server to the router.
  • Provide DHCP support for all clients that aren't web servers (which will be provided fixed ip addresses).
  • Block all other traffic.


    My Hardware configuration

    The router is set up with two network cards, the first net card (eth0) is connected to the wonderful world of the internet (or it will be once I'm done configuring it). It has all four of the ip addresses that I want it to receive on. The second network card (eth1) runs my internal network and has the address 192.168.0.1, with a netmask of 255.255.255.0. More on how I set up the network cards below.


    Installation of Linux

    I installed the RedHat 6.2 distribution of linux (I tried Debian, but it failed to work with my net cards (NetGear--using the tulip drivers)). I then retrieved the most recent kernel version at the time (2.3.99-rev8), as well as the iptables source. Note that when getting the new kernel, put the .tar.gz file in /usr/src, delete the link from linux to what the current kernel version is, then extract the data as follows:

  • gzip -d linux-<kernel version>.tar.gz
  • tar -xvf linux-<kernel version>.tar

    This will create a new linux directory (it would have overwritten what you already had, and we don't want that). Rename the directory to linux-<kernel version> and recreate the linux link by using the commands:

  • mv linux linux-<kernel version>
  • ln -s linux linux-<kernel version> -- check the order of linux and linux- not sure

    This maintains consistency if you want to upgrade later.

    I built the kernel with the following settings:

  • cd /usr/src/linux
  • make menuconfig
  • make dep ; make clean ; make bzImage
  • cp /usr/src/linux/arch/i386/boot/bzImage /boot/vmlinuz-<kernel version>
  • make modules ; make modules_install

    Then lilo needs to be configured to recognize the new kernel. Edit the /etc/lilo.conf file and add the following:

  • You can look at my current lilo.conf file here. You can see where I added the new entry. I changed the old entry to be called "oldlinux" and set the new one to be called "linux"
  • Then run lilo to set the configuration when you boot.


    Configuring the network adapters

    You can do this linuxconf in RedHat, just be careful, since the wrong key will kill linuxconf. Here are copies of my eth0 and eth1 configuration. In RedHat these belong in /etc/sysconfig/network-scripts. If you look in ifcfg-eth0, (the link for eth0), you'll notice there isn't any specification for the additional ip addresses above. To configure those, the files ifcfg-eth0:0, ifcfg-eth0:1, and ifcfg-eth0:2 are needed in the same location. They contain the following lines:

  • IPADDR=<addr>
  • NETMASK=<netmask>

    All other properties it inherits from ifcfg-eth0. Note that this doesn't (shouldn't) work if your primary device is a DHCP client (your IP address is automatically assigned to you provider, not fixed IP addresses in my case).


    Configuring DHCP

    Make sure the dhcpd daemon is running on the ethernet adapter for the internal network (such as dhcpd eth1 in my configuration).

  • dhcpd.leases is required for dhcpd to run. It stores the ip addresses that are doled out to other computers and is persistent across reboots: touch /var/state/dhcp/dhcpd.leases
  • dhcpd in /etc/rc.d/rc.init contains the script for initializing dhcpd. We want it to run only on eth1, so edit /etc/rc.d/rc.init/dhcpd and change the line "/usr/sbin/dhcpd" to "/usr/sbin/dhcpd eth1".
  • A line needs to be added to the hosts file that looks like '255.255.255.255 dhcp'. This is the broadcast address for dhcp, and what will usually happen without this is it will be ANDed with the netcard address. We don't want that, we want it to go out "as is."
  • A route needs to be added for the host 'dhcp' so that all dhcp processing occurs only on eth1: 'route add -host dhcp dev eth1' This tells the routing system to route dhcp stuff through eth1. This will allow WinXX boxes to work with the dhcp daemon. I have this being done in my rc.local file (more on this below...).
  • Finally, we have to give it the range of IP addresses it will manage. Since my eth1 adapter is using 192.168.0.1 (netmask 255.255.255.0), I'll be providing IP addresses in the range of 192.168.0.64 to 192.168.0.127. You can sample the dhcpd.conf. This file belongs in /etc.

    The various entries in the dhcpd.conf are briefly described here:
    Line Description
    subnet 192.168.0.0 netmask 255.255.255.0 { This defines what network the dhcp daemon will manage, in this case, 192.168.0.0 with a network mask of 255.255.255.0.
    The brace indicates a beginning of scope for this subnet. You can actually set up multiple subnets on the same network adapter, or for different network adapters. This allows setting specific configurations for each.
    range 192.168.0.64 192.168.0.127; This specifies the range for the above subnet. It has to fit within the subnet specified. I'll explain why I chose this range later.
    default-lease-time 43200;
    max-lease-time 604800;
    This specifies how long a lease will live for when it is provided to a client computer. A client computer then is required to renew their lease after it has expired.
    option routers 192.168.0.1; This tells where the client computers receiving IP addresses to go when they don't know where to send packets. For example, if I want to hit linuxrouter.org, I would find out its IP address. Since that IP address isn't in my subnet, the request for linuxrouter.org would be sent to the router for it to forward on.
    option broadcast-address 192.168.0.255; I don't know much about this option, other than it makes it work <grin>
    option domain-name-servers 192.168.0.1; This will automatically configure the clients to use the router as a DNS server, too.
    option subnet-mask 255.255.255.0; Tells the client the scope of the subnet it lives on.
    } The end brace ends the scope for this subnet.


    Configuring DNS

    DNS uses the BIND package, you will want to make sure you have it installed. In particular, make sure you have a very recent version. There is a bug in older versions that will allow people to gain control of your machine from outside through DNS.

    By default, starting DNS ('ndc start') will cause it to listen on all available IP addresses on all devices. This is really convenient. You can use linuxconf to add domain names to the names you control. These will be names you have registered that are set up to point at your machine for resolving names for your domain.

    You will also probably want your ISP to host your secondary. If your connection to the ISP fails for some reason, at least people will still get the correct IP address, and, since they control the reverse lookup table for your IP address, they can set up the reverse lookup to correctly point to your domain instead of something meaningless.

    Then you will want to set up your DNS to propagate to your secondary. I haven't done this yet, but speculation would suggest just indicating the IP address in linuxconf.

    Finally, you will want to set up forwarder addresses in DNS. These are addresses of other DNS servers 'close' to you that might be faster to query for the IP address desired than necessarily going to one of the top level domain servers and working down to the desired name server for the domain being queried.

    Some other notes:

  • You will want to ensure host lookup occurs in the order hosts, then bind (the name server). This information is stored in /etc/host.conf.
  • You will want to remove some references to NIS servers (I don't know much about them) in the /etc/nsswitch.conf. In particular the line starting with 'hosts:'. Just remove the 'nis' and 'nisplus' references. Leaving the other references for nis and nisplus for other services does not seem to cause a problem on my machine.
  • /etc/resolv.conf should point to your local machine as the nameserver (local or 'lo' is almost always 127.0.0.1 for an IP address). DNS on your machine should automatically go to the other domain servers as necessary and cache requests for you. The search command is for domains to search if the address isn't found for what was typed.

    A 'sample' /etc/named.conf can be looked at here. For more info, you should look at the HOWTO for DNS (link above).


    IP Tables & Rules

    You need to download the source for this from The Net Filter homepage at Penguin Computing or from Rusty's Remarkably Unreliable Guides. Please keep in mind the minimum requirements for building IP Tables. This will do alot of the magic we need. There are a couple of other utilities of note, too: ip, tulip-diag, and mii-diag. The last two are net card diagnostic utilities which are primarily useful if you are using the Tulip drivers (which is what the NetGear FA310TX uses). The first (ip) expands upon what is possible to route in the routing tables, going much farther than 'route' does. Hopefully I'll have links to these sometime soon. My net connection (see notes above) is currently down, so I'm flying blind at this point for resource links (not that I have many).

    I've created a number of quick scripts to make my life easier. All they do is take some arguments and use them with commonly used settings for NAT (network address translation). They were created as separate scripts so I could adjust them on the fly, rerun them, and have the new settings take effect immediately. The paths specified are where I decided to place these commands.

  • /bin/inNAT takes a destination address and maps it to the real internal address that hosts the information. This is what you would do for wholesale NAT. For example, if I had packets destined for 216.254.8.9, I could NAT them to 192.168.0.9 using 'inNAT 216.254.8.9 192.168.0.9'.
  • /bin/inNATByPort takes a destination address, protocol, and port and maps it to the real internal address that hosts the information. This will only forward those bits of information you care about to the servers. So, if I really didn't want to do NAT for everything above, but just for say, http access, then I would use 'inNATByPort 216.254.8.9 192.168.0.9 tcp 80', and then again for udp.
  • /bin/outNAT takes a source address from the internal net, and maps it to the appropriate address on the outgoing interface card. So I want to map all of my internal addresses to 216.254.8.8, which means I use 'outNAT 192.168.0.64/26 216.254.8.8'.
  • /etc/rc.d/preNAT runs 'inNATByPort' to assign what will be routed from the outside world to the desired machines internally. I've commented out the calls to 'inNAT' at the top. I had them there for testing, but then became pickier about what I would allow to the web machines.
  • /etc/rc.d/postNAT does all the mapping from the inside to the outside. Since I trust me, I don't block alot. This is where you will notice the rather interesting line 'outNAT 192.168.0.64/26 216.254.8.8'. The dhcp range I chose (192.168.0.64 - 192.168.0.127) all have the same first 26 bits. This is what the 26 means for 192.168.0.64/26. The represents a subnet with a total range of 64 possible addresses, and whenever a new machine comes on and is given an IP address, it will be mapped to 216.254.8.8 for all internet requests.
  • /etc/rc.d/inDMZAllow is a simple script that takes a set of parameters to specify what is allowed to the web servers. This is being commonly called the 'DeMilitarized Zone' because we are allowing some connections through. Note that it only allows connections that are NEW. This is used in 'filter' (described below), which allows connections that are ESTABLISHED,RELATED. Since I want to specify icmp ping packets (echo-request packets), I needed to pass the type of destination (either --dport for tcp and udp, or --icmp-type for icmp). So except for this extra argument, it is very similar to 'inNATByPort'. So, to allow the connection to the web server above, I would use 'inDMZAllow 192.168.0.9 tcp --dport www', and again for udp as the protocol. You will want to forward anything that comes from the internal net (assuming you trust the users), as well as allowing input into the router from anyone.
  • /etc/rc.d/filter handles filtering of what will be forwarded from one network card to the other. It is absolutely crucial to allow packets through for connections that are either for the connection or related to it (such as icmp packets that say the request isn't possible). More precisely, if a connection request comes in for a web server, the web server will tell the client to use a different socket address so it can keep port 80 open for further requests. Without this command, any packets to this new address will be dropped, and the client will not receive anything. This also specifies my input filters to allow only DNS. You will want to stop services you don't need from running by altering your /etc/inetd.conf.

    This setup allows me to access my web servers from anywhere on my internal net using their "outside" address. So if I query www.oneoddsock.com from inside my network, I will get to it properly. However, this does not work from the firewall box itself. Take note of this when testing your configuration.

    Finally, you need to run all these handy commands from somewhere, such as /etc/rc.d/rc.local. You will notice it adds hosts for dhcp, as well as my other 'domains', not that I really use them. I call the filtering rules first to prevent an attack happening during boot getting in, then I set up NAT, then I set up IP forwarding, which is CRUCIAL if you want this to work. This is the last line in the file.


    Disclaimers

    This was put together as a quick guide as well as a store of information if I ever had my linux box compromised and had to rebuild it. It was also intended for anyone who was trying to do something similar. Why didn't I use Win2K or NT? I needed NAT, and I needed to run it on a machine that is a P133 with 32MB of RAM. WinNT and Win2K have lots of cool features, but they are both pigs. I am not responsible for the design decisions that put them in that state, though I sorely wish to do something about it. So many possibilities there...

    You can email me at my SpeakEasy address if you are having problems, and if I can't solve it, consider subscribing to the netfilter listserv at netfilter@lists.samba.org (assuming I have that right). The process is described on the NetFilter page (somewhere).


    If you have comments, suggestions or questions, mail me at:
    bscriver@speakeasy.org