For years, sysadmins like me were told that you could not run more than one DHCP server in a broadcast domain. Then the IETF created a draft for a failover protocol between two DHCP servers. ISC implemented this draft in the DHCP reference server. With Windows Server 2012 Microsoft implemented the same draft into their DHCP server.

Now sysadmins like me run around and configure DHCP failover pairs of servers. This is an improvement, because now you can take down one of DHCP servers for maintenance without taking down DHCP as a service completely. However, this failover thing has some shortcomings:

  • Only two DHCP servers per broadcast domain
  • Only active/passive pairing, no active/active
  • Too many configuration parameters

A simpler solution to this prevents the shortcommings of the IETF draft. Of course, it comes from the developers in the OpenBSD project. IP multicast is the technology dhcpd(8) uses to overcome the limitations of the IETF draft. In some networks multicast is problematic or simply not supported. For such a environments dhcpd(8) also supports unicast messages to sync the DHCP servers, at the cost of more configuration.

In this post, I suppose that you will run two identical DHCP servers for redundancy in your local network. Where is the benefit of using OpenBSD if you only run two servers? Well, with dhcpd(8) you can easily add and remove DHCP servers in the network without touching the configuration of the existing ones (if you use multicast).

The first step to configure your new reduandant DCHP servers is to find out, if your network supports multicast traffic. On OpenBSD this is easy. Just run ifconfig <interface> and check its output for the MULTICAST flag:

$ ifconfig vr0

vr0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
       lladdr 00:11:22:33:44:55
       description: 81_LAN
       index 1 priority 0 llprio 3
       groups: egress
       media: Ethernet autoselect (100baseTX full-duplex)
       status: active
       inet 192.0.2.21 netmask 0xffffff00 broadcast 192.0.2.255

The interface vr0 on this server supports multicast traffic. While this is good news, it is not yet the guarantee that multicast will work for you. Check the capabilities and configs of all active network devices in the network paths between the designated DHCP servers. If one of the switches does block multicast traffic or handle it wrong this is a showstopper.

Your whole network is ready to handle multicast traffic? Perfect. Now you can start with the configuration of your OpenBSD DHCP server. A fresh install of OpenBSD comes with multicast support disabled. You have to run the following two commands to enable it:

$ doas rcctl enable multicast
$ doas route delete 224/4

Your server is now able to send and receive multicast packets. As a security-aware admin you run pf(4) on your server. By default pf(4) blocks IP packets with options set. IP multicast packets in turn require IP options. So you have to add the following rules to your pf.conf(5):

pass on $sync_if allow-opts
pass on vr0 proto udp from vr0:network to 224.0.1.240 port dhcpd-sync allow-opts

Enable the new rules with the following command:

$ doas pfctl -f /etc/pf.conf

The security part is not finished with pf(4). You should also create a key file which dhcpd(8) can use to sign its messages to the other DHCP servers in the network. Execute the following command to create the file on the first DHCP server you set up:

$ doas dd if=/dev/random of=/var/db/dhcpd.key bs=2048 count=1

Copy this key file to the other DHCP servers in your network in secure manner:

$ scp /var/db/dhcpd.key dhcp2.example.com

Now it is time to configure the DHCP server itself. Open /etc/dhcpd.conf in your favorite editor. The man page dhcpd.conf(5) explains the file format while dhcp-options(5) explains the available DHCP options. The below example file gives you an idea how a typical dhcpd.conf(5) file could look like:

authoritative;

subnet 192.0.2.0 netmask 255.255.255.0 {
	default-lease-time 86400;
	max-lease-time 604800;
	option domain-name "example.com";
	option domain-name-servers 192.0.2.11, 192.0.2.12;
	option routers 192.0.2.1;
	option subnet-mask 255.255.255.0;
	range 192.0.2.100 192.0.2.254;
	server-identifier 192.0.2.21;
}

All of the options in this file must be identical on all DHCP servers, with one very important exception: server-identifier. This option uses the IP address of the DHCP server to identify it in the multicast messages, so it must be different on all DHCP servers. If you forget to change this option on a DHCP server, the other servers will ignore its messages.

Where are the options that configure message exchange between the DHCP servers? There are none, the required settings for the multicast messages are done using parameters for dhcpd(8). You can set the options with the following two commands:

$ doas rcctl enable dhcpd
$ doas rcctl set dhcpd flags -Y vr0 -y vr0

The flag -Y vr0 makes dhcpd(8) send multicast messages to the group 224.0.1.240 using interface vr0. The flag -y vr0 make dhcpd(8) receive multicast messages to the group 224.0.1.240 on interface vr0.

Repeat the above steps on all DHCP servers you want to run in your broadcast domain. As soon as the second one is up you can check if the sync is working by watching /var/log/daemon:

$ tail -f /var/log/daemon | grep dhcpd
Dec 4 11:14:40 dhcp1 dhcpd[21561]: DHCP_SYNC_LEASE from 192.0.2.22 for hw 00:12:34:56:78:9a -> ip 192.0.2.103, start 1511653196, end 1511696396
Dec 4 12:09:36 dhcp1 dhcpd[21561]: DHCPREQUEST for 192.0.2.150 from 00:ab:cd:ef:01:23 via vr0
Dec 4 12:09:36 mars dhcpd[21561]: DHCPACK on 192.0.2.150 to 00:ab:cd:ef:01:23 via vr0
Dec 4 12:09:36 mars dhcpd[21561]: sending DHCP_SYNC_LEASE for hw 00:ab:cd:ef:01:23 -> ip 192.0.2.150, start 1512385776, end 1512428976

This shows how the DHCP server receives a sync message from another DHCP server, a client system requests an address, the DHCP server grants the requested address and the DHCP server sends a sync message to the other DHCP servers in the same broadcast domain.