Posted by endecotp on Wed 6 Dec 2006 at 10:11
This article describes how to use DHCP to supply information about static routes to the clients on your network. You may want to do this if you have two or more local networks with routers between them. The DHCP software doesn't support this out-of-the-box, but it can be configured to do so without too much effort.
The problem:
Imagine you have a small network using 192.168.1.* local IP addresses, connected to the outside world via a router. The machines on the network are all running Debian, of course. The router might be a Debian box, or maybe something lightweight like a WRT54G running OpenWRT. The router is also a DHCP server for the other machines. All is good and you are happy.
Then one day you decide you want another network, 192.168.2.*. (In my case I wanted to connect up some test systems with limited connectivity to the outside world.) So you add a second network interface to one of the machines which forwards between the two networks. Now you have something like this:
Internet <---> Router A <---> Network 1 <---> Router B <---> Network 2
For this to work the machines in Network 2 must be set up with Router B as their default router, while the machines in Network 1 must be set up with Router A as their default router. This can be done using DHCP or with static data in /etc/network/interfaces. Enable IP forwarding on Router B (/etc/sysctl.conf) and it will almost work, but not quite: you need to add rules on Router A and all the machines in Network 1 telling them how to reach Network 2. Here's what you need to do to set this up manually:
route add -net 192.168.2.0 netmask 255.255.255.0 gw router_b
Doing it manually on Router A is not a problem - it's only one machine. But it could be time consuming and error prone to do it manually on every machine in Network 1 - and where in /etc do I put these route commands anyway? Then, when a friend drops by with their laptop, you want it to be auto-configured, don't you?
The solution is to use DHCP to supply this extra routing information to the machines in Network 1. The complication is that this is not supported out-of-the-box. This article explains how I have done it. Hopefully some readers will be able to improve on this and maybe even get some of this included in the relevant Debian packages.
Routing information in DHCP
There is a DHCP option, static-routes, to supply routing information. Unfortunately it's only useable with single hosts since it doesn't include a subnet mask. So although it is described in dhcp-options(5) it isn't implemented by the DHCP client.
An improvement on this is described by RFC3442, "Classless Static Route Option for DHCPv4". This option does include a netmask (albeit in a slightly odd format) and is suitable for the problem we're solving here. Unfortunately there is no out-of-the-box support for it at all in any of the DHCP tools (as far as I can see - please correct me if I'm wrong). Luckily, however, the DHCP tools are all extensible, so we can add support just by editting a few files.
Format of the destination address and netmask
RFC3442 uses an unusual format to define an address and netmask: the first byte gives the number of significant bits, and the subsequent bytes - one to four of them - give the bytes containing those bits. So in our case the network address 192.168.2.0 and netmask 255.255.255.0 (also often written as 192.168.2.0/24) is encoded as 24.192.168.2.
I assume that you are using a /24 network in the rest of this article, because in this case the RFC3442 format has four bytes, the same as an IP address. If you have a smaller or much larger subnet it will have a different length which makes configuration more difficult. If someone would like to post a variation that works with arbitary subnet sizes that would be great.
Setting up dnsmasq
I use dnsmasq as DHCP server on "Router A". dnsmasq is available for Debian, though in my case I run it on OpenWRT. dnsmasq doesn't know about the classless-static-routes option, but it allows you to add arbitary options using their code number, as follows, in /etc/dnsmasq.conf:
dhcp-option=121,24.192.168.2.0,192.168.1.12
121 is the option code for classless-static-routes. 24.192.168.2 is the network address and netmask, encoded as described above. 192.168.1.12 is the IP address of Router B (unfortunatel you can't put a hostname there). Note that if you have more than one additional network you can add further pairs of addresses to the option.
The dnsmasq man page suggests that you should be able to write 192.168.2.0/24 and have it automatically encoded. This didn't work for me and it was sent as a string. That's quite possibly just because I don't have a sufficiently new version.
Note that in this case this will be served to all machines, which is wrong if this machine is also providing DHCP to Network 2. In that case you need to tell it to send this option only to machines in Network 1. This is possible; see the dnsmasq man page.
I imagine that other DHCP servers can be set up equally easily.
Setting up dhclient
You need to tell dhclient to request the classless-static-route option from the server. To do this, add it to the list of options in the "request" line in /etc/dhclient.conf or /etc/dhcp3/dhclient.conf. Since it knows nothing about the option by default you need to define it first:
option classless-static-routes code 121 = array of { ip-address, ip-address };
Adding the route
The dhclient program manages the communication with the DHCP server but uses an external program, dhclient-script, to perform the actual network configuration based on the data that it obtains. This is a shell script that receives the information from the server via environment variables.
In version 3 of dhclient, dhclient-script is customisable using scripts under /etc/dhcp3/dhclient-*-hooks.d. In earlier versions of dhclient, dhclient-script itself needs to be modified. I'll assume that you have the newer version.
All you need to do is to drop this script into /etc/dhcp3/dhclient-exit-hooks.d/configure_static_routes:
#!/bin/sh
function process_routes() {
while [ $# -ne 0 ]
do
dest=$1
gateway=$2
shift; shift
echo $dest | tr '.' ' ' | {
read a b c d
if [ "$a" = "24" ]
then
netmask="255.255.255.0"
netaddr="$b.$c.$d.0"
else
echo "Sorry, can't process destination with $a signficant bits"
exit 1
fi
route add -net $netaddr netmask $netmask gw $gateway
}
done
}
if [ "$reason" = "BOUND" ]
then
echo "classless_static_routes = $new_classless_static_routes"
process_routes $new_classless_static_routes
fi
I hope this is useful. Do let me know if you have any feedback.
Is there any chance of any of this being supported by the packages?
[ Parent ]
[ Parent ]
nephthys:~# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 0.0.0.0 192.168.1.12 0.0.0.0 UG 0 0 0 eth0 nephthys:~# traceroute bes traceroute to bes (192.168.20.204), 30 hops max, 38 byte packets 1 rt (192.168.1.12) 0.909 ms 0.888 ms 0.884 ms 2 rt-vpn-policy (192.168.1.224) 2.113 ms 1.584 ms 1.618 ms 3 bes (192.168.20.204) 32.685 ms 36.053 ms 32.565 ms nephthys:~# traceroute bes traceroute to bes (192.168.20.204), 30 hops max, 38 byte packets 1 rt-vpn-policy (192.168.1.224) 0.846 ms 0.773 ms 0.757 ms 2 bes (192.168.20.204) 32.460 ms 33.175 ms 35.103 ms
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
[ Parent ]
option option-249 <calculated output of hexroute>;because hexroute itself uses v3.0 syntax.
[ Parent ]
Kind regards
Wolfgang Karall
--
Debian GNU/Linux on an IBM Thinkpad T43p
[ Parent ]