Configuring DNS and a DHCP server

The DSL router/modem (an Alcatel Speedtouch 510) that brokers Internet connectivity for hydrus.org.uk locks up every couple of months. The usual symptoms are that the DHCP server and DNS stop working (these are the 'added value' facilities it offers that I make use of in my local area network). The last time this happened, I decided I would transfer the DHCP and DNS capabilities to the FreeBSD box, crimson. Maybe this would take some load of the Alcatel router, leading to less frequent reboots. The following is a recipe for defining DNS and DHCP server services for a small network.

DNS

Since the build process on crimson had been changed to stop compiling bind, it had to be rebuilt manually. This is fairly easy, by issuing the following commands (as root):

# cd /usr/src/lib/bind
# make obj && make depend && make && make install
# cd /usr/src/usr.sbin/named
# make obj && make depend && make && make install 

Now that bind was working (don't forget to add named_enable="YES" in /etc/rc.conf), I could create the configuration files. After poking about on the web, and reading the O'Reilly "DNS and BIND" book, I came up with the following configuration files:

named.conf

// Standard FreeBSD settings
options {
	directory	"/etc/namedb";
	pid-file	"/var/run/named/pid";
	dump-file	"/var/dump/named_dump.db";
	statistics-file	"/var/stats/named.stats";

// In addition to the "forwarders" clause, you can force your name
// server to never initiate queries of its own, but always ask its
// forwarders only, by enabling the following line:
//
      forward only;

// If you've got a DNS server around at your upstream provider, enter
// its IP address here, and enable the line below.  This will make you
// benefit from its cache, thus reduce overall DNS traffic in the Internet.
// Note: real IP's removed.

	forwarders {
		xx.xx.xx.xx; yy.yy.yy.yy;
	};

	/*
	 * If there is a firewall between you and nameservers you want
	 * to talk to, you might need to uncomment the query-source
	 * directive below.  Previous versions of BIND always asked
	 * questions using port 53, but BIND versions 8 and later
	 * use a pseudo-random unprivileged UDP port by default.
	 */
	 query-source address * port 53;
};

zone "." {
	type hint;
	file "named.root";
};

zone "0.0.127.IN-ADDR.ARPA" {
	type master;
	file "dynamic/db.127.0.0";
};

zone "hydrus.org.uk" in {
    type master;
    file "dynamic/db.hydrus.org.uk";
    allow-update { 192.168.0.15; };
};

zone "0.168.192.in-addr.arpa" in {
    type master;
    file "dynamic/db.192.168.0";
    allow-update { 192.168.0.15; };
};

Note that the allow-update line ensures that a DHCP server is able to dynamically update DNS. Note further that the configuration files for bind (shown below), live in the dynamic subdirectory; this is because only the dynamic and slave subdirectories in the /etc/namedb directory are owned by bind. Dynamic DNS requires the ability to create and modify files in the directory where its configuration files live (a .jnl file for one).

The other configuration files are are shown below. There are the three key files that must be defined. Note the the files shown below are as I originally defined them; dynamic DNS will cause them to change when in operation.

db.hydrus.org.uk

This file defines the forward lookups, that is from name to IP address.

$TTL    3h
@  IN  SOA crimson.hydrus.org.uk.  root.crimson.hydrus.org.uk. (
           1           ; Serial
           3h          ; Refresh after 3 hours
           1h          ; Retry after 1 hour
           1w          ; Expire after 1 week
           1h )        ; Negative caching TTL of 1 hour

; Name server

           IN  NS  crimson.hydrus.org.uk.

; Static addresses

localhost        IN  A   127.0.0.1
crimson          IN  A   192.168.0.15
speedtouch       IN  A   192.168.0.1

; Aliases

www              IN CNAME crimson

db.192.168.0

This file defines the reverse lookups, i.e. from IP address to name.

$TTL    3h
@  IN  SOA crimson.hydrus.org.uk.  root.crimson.hydrus.org.uk. (
           1           ; Serial
           3h          ; Refresh after 3 hours
           1h          ; Retry after 1 hour
           1w          ; Expire after 1 week
           1h )        ; Negative caching TTL of 1 hour

; Name server

           IN  NS  crimson.hydrus.org.uk.

; Addresses point to canonical name

15         IN  PTR crimson.hydrus.org.uk.
1          IN  PTR speedtouch.hydrus.org.uk.

db.127.0.0

You also need definitions for the local loopback address reverse lookup.

$TTL    3h
@  IN  SOA crimson.hydrus.org.uk.  root.crimson.hydrus.org.uk. (
           1           ; Serial
           3h          ; Refresh after 3 hours
           1h          ; Retry after 1 hour
           1w          ; Expire after 1 week
           1h )        ; Negative caching TTL of 1 hour

; Name server

           IN  NS  crimson.hydrus.org.uk.

; Addresses point to canonical name

1          IN  PTR localhost.

Testing DNS

I started named via the /etc/rc.d/named script, and tested it with nslookup. Once it seemed to be working, for both internal and external names, I changed /etc/resolv.conf to use the localhost (127.0.0.1) as the first nameserver.

DHCP Server

The ports contain the ISC DHCP server, isc-dhcp3-server:

isc-dhcp3-server-3.0.3 The ISC Dynamic Host Configuration Protocol server

Downloading and installing the package was the work of a few moments with portupgrade:

  portupgrade -NPP isc-dhcp3-server

The configuration, via /usr/local/etc/dhcpd.conf was not too difficult, but it did take a while to figure out how to assign a fixed IP address to chrome. At first, I tried the host directive but, while that certainly gave out the right IP address, the host name was never entered into the DNS. Dynamic DNS updates did work for addresses assigned via the subnet directive; so it seemed I was misusing the host directive. Eventually, I figured out the scheme shown below. This sets up a class to which only chrome can possibly belong (by virtue of its MAC address). The class is granted access to a pool with one address, and denied access to the general pool.

However, this whole scheme was a waste of time, since I realised that chrome could not be dependent on crimson for IP address, as a disaster backup machine cannot rely on crimson for anything.

Anyway, here's the dhcpd.conf file I arrived at.

# dhcpd.conf
#
# crimson configuration file for ISC dhcpd
#
# mpw 12th March, 2006

# option definitions common to all supported networks...
option domain-name "hydrus.org.uk";
option domain-name-servers crimson.hydrus.org.uk, xx.xx.xx.xx, yy.yy.yy.yy;
option routers 192.168.0.1;

default-lease-time 7200;
max-lease-time 7200;

# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
authoritative;

# ad-hoc DNS update scheme - set to "none" to disable dynamic DNS updates.
ddns-update-style interim;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
log-facility local7;

# define chrome class for static address of chrome
class "chrome" {
    match if substring ( hardware,1,6)  = 00:41:15:a1:75:59;
}

subnet 192.168.0.0 netmask 255.255.255.0 {
  pool {
    allow members of "chrome";
    range 192.168.0.30;
  }
  pool {
    deny members of "chrome";
    range 192.168.0.35 192.168.0.45;
  }
}

The final DNS configuration has chrome defined as a fixed IP address. I mirrored the DNS configuration files to chrome, so that it had its own named running. In case of disaster, all I'd need to do would be to start dhcpd running.

In fact, I wrote a local rc script to start the dhcpd daemon automatically if crimson was down:

#!/bin/sh
#
# If crimson is down, start dhcpd 
#

DHCPD='dhcpd_enable="YES"'

case "$1" in
    start)
        ping -c 1 -o -t 1 crimson >/dev/null 2>amp;1
        if [ $? -ne 0 ]; then
            echo "$0: crimson down.  Starting dhcpd..."
            echo ${DHCPD} >>/etc/rc.conf
            sh /usr/local/etc/rc.d/isc-dhcpd start
        fi
    ;;
    stop)
	if grep -q ${DHCPD} /etc/rc.conf; then
            sh /usr/local/etc/rc.d/isc-dhcpd stop
            cat /etc/rc.conf | sed -e "/${DHCPD}/d" >/tmp/$$
            cp /tmp/$$ /etc/rc.conf;rm /tmp/$$
	fi
    ;;
esac