Configure a Linux host to act as a DHCP server and assign known IP addressees to the nodes on your network. Configuration and debug tips are provided for both Ubuntu 18.04 and Yocto systems.

The Dynamic Host Configuration Protocol (DHCP) is typically utilized on a network during boot of a device so it can obtain a valid network configuration, including IP address, from a DHCP server. This function is almost always included in firewalls found in both homes and businesses, and the vast majority of devices (e.g., tablets, PCs, Xbox, etc.) that you'll find on a network utilize DHCP by default.

This article describes the use of the Internet Software Consortium's (ISC) DHCP server on both Ubuntu 18.04 and a Yocto-built embedded Linux system. ISC claims that its DHCP server "is the most widely used open source DHCP implementation on the Internet and is a carrier and enterprise grade solution". You can read more at https://www.isc.org/downloads/dhcp/.

We make extensive use of the DHCP server to assign unique, consistent, private addresses to each supported device on our network. For our private networks, we use IPv4. However, the DHCP server can assign both IPv4 and IPv6 addresses, but it requires two daemon instances to be run to do so.

It's also important to point out that a Linux host can act as the DHCP server on a network that is served by an ISP provided firewall / router if its DHCP server is disabled. This can be very useful for taking control of your network's addressing, which is highly desirable when monitoring or filtering network activity. For example, prevent the server from granting an IP Address to an unknown device that joins your network.

Note that setting up a basic DHCP server on an internal, local network is relatively straightforward. Also, even if you don't want to manage your own server full time, it's invaluable to enable a basic server when setting up a new device for the first time, such as a surveillance camera. Many cameras ship with installation software that isn't necessary and potentially not trustworthy. Also, many cameras now ship without a MAC Address label, so it can be difficult to determine how it initially booted. However, by running your own DHCP server, you can typically force the camera to use an available, known IP Address and identify the MAC address. Once you have this, you can directly log into the camera via your browser without ever having to deal with the manufacturer's installation software.

Installation on Ubuntu 18.04

Prior to installation, you may notice that an /etc/dhcp directory already exists. If it does exist, then this directory most likely contains configuration files for the DHCP client (e.g, dhclient.conf). Keep in mind that a Linux host can act as both a DHCP server and a DHCP client for retrieving its own IP and DNS address from an upstream DHCP server.

After a fresh Ubuntu installation, we find the following files in /etc/dhcp:

$ ls debug dhclient.conf dhclient-enter-hooks.d dhclient-exit-hooks.d

$ sudo dhclient -v <interface> # e.g., enp2s0

If you want to see your dhclient in action, type the following:

To install the DHCP server, type the following:

$ sudo apt install isc-dhcp-server $ dhcpd --version isc-dhcpd-4.3.5

The DHCP server doesn't start automatically after installation since it first requires configuration:

$ ps -e | grep dhcp $

However, we can see that the DHCP server will start automatically on subsequent reboots:

ls -l /etc/rc5.d/*dhcp* lrwxrwxrwx 1 root root 25 Mar 29 01:13 /etc/rc5.d/S04isc-dhcp-server -> ../init.d/isc-dhcp-server

Configuration

Important Files

File Purpose /etc/dhcp/dhcpd.conf DHCP server configuration file for IPv4 /etc/dhcp/dhcpd6.conf DHCP server configuration file for IPv6 /etc/init.d/isc-dhcp-server DHCP initialization script /etc/default/isc-dhcp-server Defaults for DHCP initialization script /var/lib/dhcp/dhcpd.leases DHCP client lease database

For both our Ubuntu and Yocto systems, we define a test network at 192.168.3/24 using a second NIC, and this is where we will configure the DHCP server to assign addresses. For our Ubuntu machine, we define the following in our /etc/network/interfaces file:

# experimental / test network auto enp3s0 iface enp3s0 inet static address 192.168.3.1 network 192.168.3.0 netmask 255.255.255.0 broadcast 192.168.3.255

Next we modify the /etc/default/isc-dhcp-server file, which provides defaults for initialization of the dhcpd process. We only want the DHCP server to assign addresses on interface enp3s0. We do this by setting the INTERFACES parameter, and this parameter is ultimately passed onto the DHCP sever via its command line interface when it's started.

A snippet of /etc/default/isc-dhcp-server is shown below:

# On what interfaces should the DHCP server (dhcpd) serve DHCP requests? # Separate multiple interfaces with spaces, e.g. "eth0 eth1". INTERFACESv4="enp3s0"

Before we modify our configuration file, /etc/dhcpd.conf, we make a copy as shown below

$ sudo cp /etc/dhcpd.conf /etc/dhcpd.conf.orig

There are various options supported by the DHCP server that aren't utilized in our example configuration file. Two quick resources for finding out more about the available options are the original configuration file and the man page: "man dhcpd.conf".

The configuration file consists of parameter and declaration statements. Parameter statements can be used to assign settings (e.g., authoritative), and we use declaration statements to describe the network topology and clients on the network. We make extensive use of the host declaration statement to assign fixed IP addresses to our known client devices.

Note that most networked devices (e.g., web camera) support static IP address assignment via an administrative interface (e.g., local web server running on a web camera or a settings screen on a tablet). We prefer not to use this method to assign addresses since this is difficult to manage versus our centralized approach.

For this example, we only support our POE camera:

authoritative; subnet 192.168.3.0 netmask 255.255.255.0 { # range 192.168.3.100 192.168.3.101; default-lease-time 86400; max-lease-time 86400; option subnet-mask 255.255.255.0; option broadcast-address 192.168.3.255; option routers 192.168.3.1; } host poe_cam1 { hardware ethernet C0:99:34:51:77:01; fixed-address 192.168.3.200; }

A few notes to make about the configuration file:

When first bringing a new camera/device online with an unknown MAC address, we enable the range statement, restart the server, and inspect the system log or tcpdump output to ascertain the camera's MAC address. We then comment out the range statement and create a declaration for the new device.

The authoritative statement instructs the DHCP server to send a DHCPNAK in response to an invalid DHCPREQUEST

The subnet statement defines our local network and the option statements are propagated to each host. We are leaving range commented out to prevent the DHCP server from assigning IP addresses to unknown devices that may be attached to our network without our permission. However, in some cases it is very convenient to enable this line temporarily to configure a device and record its MAC address, which can be found in the lease file (see below) or by inspecting the system log file (/var/log/syslog).

Each host statement maps an Ethernet MAC address to an IP address. A name is also given to each map (e.g., poe_cam1)

Example / Test network to configure surveillance camera

For the above example topology where cameras are placed behind a Linux host, you may also want to employ Linux iptables / nftables to prevent the camera from connecting to a peer in China or allowing remote access.

By restricting this network to a limited 8-bit IPv4 subnet (255 devices), we can assume a great deal of control and visibility over this (dedicated camera) network.

Start the DHCP Server on Ubuntu

$ sudo service isc-dhcp-server start $ ps -e | grep dhcp 2295 ? 00:00:00 dhcpd

If your DHCP server didn't start, check your system log file. If you see "Not configured to listen on any interfaces!", then you still need to set the INTERFACES line in /etc/default/isc-dhcp-server.

We can see that our camera was assigned an IP address by examining /var/log/syslog:

$ grep DHCP /var/log/syslog dhcpd[2295]: DHCPREQUEST for ... dhcpd[2295]: DHCPACK on ...

Using the DHCP Server

The dhcpd.leases file is used as a cache by the server across reboots and power failures. It also provides visibility into what addresses have been granted to devices from the range pool within the defined subnet. New leases are appended to the end of the dhcpd.leases file.

In order to prevent the file from becoming arbitrarily large, from time to time dhcpd creates a new dhcpd.leases file from its in-core lease database. Once this file has been written to disk, the old file is renamed dhcpd.leases~, and the new file is renamed dhcpd.leases. If the system crashes in the middle of this process, whichever dhcpd.leases file remains will contain all the lease information, so there is no need for a special crash recovery process.

Each time the dhcpd.conf file is changed, the DHCP server must be restarted to process the changes. This can be accomplished as follows:

$ sudo service isc-dhcp-server restart

IETF RFC 2131

The IETF specifies the Dynamic Host Configuration Protocol in RFC2131 and defines the "Client-server interaction" in section 3.1. Within this section, you can find Table 2 which calls out each potential DCHP message (e.g., DHCPREQUEST) and also provides a timeline of the exchange in Figure 3.

Tips on Debugging

During boot, a client device will make a request to the broadcast IP address of 255.255.255.255 on destination port 67 using a unqiue transaction ID. This request will be made from an IP address of 0.0.0.0.

Nov 15 19:28:55 controller dhcpd: DHCPDISCOVER from C0:99:34:51:77:01 via enp3s0: network 192.168.3.0/24: no free leases

tcpdump is a very handy tool when debugging DHCP boot issues, especially if they are due to an improperly configured client or perhaps faulty iptables settings on the Linux host running dhcpd. Below we show an example along with a partial dump / result. Notice the wealth of information that can be seen in the dump.

# tcpdump -i eth4 -vvXn port 67 tcpdump: listening on eth4, link-type EN10MB (Ethernet), capture size 262144 bytes 10:56:49.379139 IP (tos 0x10, ttl 128, id 0, offset 0, flags [none], proto UDP (17), length 328) 0.0.0.0.68 > 255.255.255.255.67: [udp sum ok] BOOTP/DHCP, Request from 00:04:9f:23:3c:7f, length 300, xid 0x55a736d3, Flags [none] (0x0000) Client-Ethernet-Address 00:04:9f:23:3c:7f Vendor-rfc1048 Extensions Magic Cookie 0x63825363 DHCP-Message Option 53, length 1: Request Requested-IP Option 50, length 4: 192.168.3.208 Parameter-Request Option 55, length 9: Subnet-Mask, BR, Time-Zone, Default-Gateway Domain-Name, Domain-Name-Server, Hostname, Netbios-Name-Server Netbios-Scope 0x0000: 4510 0148 0000 0000 8011 3996 0000 0000 E..H......9..... 0x0010: ffff ffff 0044 0043 0134 c99a 0101 0600 .....D.C.4...... 0x0020: 55a7 36d3 0000 0000 0000 0000 0000 0000 U.6............. 0x0030: 0000 0000 0000 0000 0004 9f23 3c7f 0000 ............;... ...

It's important to note here that this message is transmitted using a broadcast MAC Address, so all hosts on the LAN will see this message. This has seecurity implications, which we'll discuss in a future revision of this article.

The DHCP server supports various command line options. To read about them, type "man dhcpd" or "man dhcp.conf" at a terminal prompt.

To see the options that were used by the system when starting the server, type the following at a terminal prompt:

$ ps -ef | grep dhcp dhcpd 9278 1 0 02:10 ? 00:00:00 dhcpd -user dhcpd -group dhcpd -f -4 -pf /run/dhcp-server/dhcpd.pid -cf /etc/dhcp/dhcpd

It can also be helpful to start the dhcpd process as a foreground task (doesn't fork to daemon) and have it dump its output to the console rather than syslog. We can do this by passing dhcpd the '-d' flag as shown below with our Yocto-built embedded Linux system:

# dhcpd -4 -d -lf /var/lib/dhcp/dhcpd.leases -cf /etc/dhcp/dhcpd.conf Internet Systems Consortium DHCP Server 4.4.1 Copyright 2004-2018 Internet Systems Consortium. All rights reserved. For info, please visit https://www.isc.org/software/dhcp/ Config file: /etc/dhcp/dhcpd.conf Database file: /var/lib/dhcp/dhcpd.leases PID file: /var/run/dhcpd.pid Wrote 0 deleted host decls to leases file. Wrote 0 new dynamic host decls to leases file. Wrote 1 leases to leases file. Listening on... DHCPREQUEST for ... DHCPACK on ...

In the above, after we started the server, we logged into another Linux host and ran "dhclient -v <interface>" which triggered the DHCP exchange shown.

We plan to continue updating this article to discuss additonal topics like IPv6 and managing security concerns.