ISC DHCP Server on Linux: dhcpd.conf Configuration Guide - 夜莺博客

ISC DHCP Server on Linux: dhcpd.conf Configuration Guide

While home routers hide DHCP behind a web page, running a dedicated ISC DHCP server on Linux gives you precise control: multiple address pools per subnet, static reservations, custom options such as NTP and PXE boot, and failover between two servers. The entire configuration lives in /etc/dhcp/dhcpd.conf, and the workflow is always the same - install, bind to the LAN interface, declare subnets and reservations, test with dhcpd -t, and start the service. This guide walks through a production-style configuration on Ubuntu/Debian with notes for RHEL-family systems.

Why Run a Dedicated DHCP Server Instead of the Router

Every consumer router ships with a tiny DHCP server, and for a single flat LAN that is usually enough. The calculus changes as soon as any of the following is true: you have more than one VLAN and want each served by the same authoritative server; you need PXE or UEFI network boot with a specific boot file per client class; you want DNS to be updated automatically when a lease is handed out; you want per-department option sets such as different NTP or SIP servers; or you simply want an address plan that is documented in version control rather than hidden inside a web GUI.

ISC DHCP solves all of these in a single text configuration file. Its advantages over a router-based server are concrete:

  • One file to review and version. /etc/dhcp/dhcpd.conf can live in Git and be deployed by Ansible or any other configuration management tool.
  • Real scoping. Global, subnet, shared-network, pool, class and host scopes nest in a defined order, so options can be inherited and overridden precisely.
  • Classes and conditional logic. You can match on vendor class, client identifier, hostname pattern or MAC prefix and branch the response accordingly.
  • Failover. Two servers can share a pool with automatic lease synchronisation, which routers almost never offer.
  • Observability. Leases are plain text and syslog records every DISCOVER, OFFER, REQUEST and ACK, which makes troubleshooting straightforward.

The trade-off is that you now own a service with a single point of failure if you only run one instance, which is exactly why the failover section further down matters on any network you would call production.

How dhcpd.conf Is Structured: Scopes and Inheritance

Before editing anything, understand that dhcpd.conf is a scoped language. Parameters set in an outer scope are inherited by inner scopes and can be overridden at any level. The nesting order a packet effectively sees is:

global                    # everything below inherits from here
  shared-network NAME {   # groups several subnets that share physical wiring
    subnet A.B.C.0 netmask 255.255.255.0 {
      option routers ...;  # subnet-level defaults
      pool { range ...; }  # pool-level overrides, can carry allow/deny rules
      group { ... }        # bundles host declarations under common options
    }
  }

Three practical rules follow from this. First, option statements placed at the top of the file apply everywhere unless overridden, so put your DNS servers and domain name there. Second, pool blocks are what let a single subnet hand out different option sets to different clients. Third, host declarations can either sit at global level (recommended, they are matched by MAC regardless of subnet) or inside a subnet, and a global host block is safer because a client that moves between VLANs keeps its reservation.

Install and Bind to the Right Interface

sudo apt update && sudo apt install isc-dhcp-server    # Debian/Ubuntu
sudo dnf install dhcp-server                           # RHEL/Rocky

On Debian/Ubuntu tell dhcpd which interface to listen on in /etc/default/isc-dhcp-server:

INTERFACESv4="eth1"

Basic dhcpd.conf with Subnet and Range

# /etc/dhcp/dhcpd.conf
default-lease-time 86400;      # 24 hours
max-lease-time 604800;         # 7 days
authoritative;                 # send DHCPNAK for foreign ranges
option domain-name "home.local";
option domain-name-servers 192.168.1.1, 1.1.1.1;

subnet 192.168.1.0 netmask 255.255.255.0 {
    range 192.168.1.100 192.168.1.200;
    option routers 192.168.1.1;
    option broadcast-address 192.168.1.255;
    option subnet-mask 255.255.255.0;
}

authoritative; makes the server reply with DHCPNAK when a client requests an address outside its subnets - include it or stale leases survive forever. Multiple range statements inside one subnet create separate pools.

Multiple Subnets, Ranges and Client Classes

A server that answers on more than one interface simply declares more than one subnet block; dhcpd picks the right one based on the interface the request arrived on, and in relayed setups by the gateway address inside the packet. Multiple range statements create separate pools inside a subnet, which is useful for splitting a static-ish "infrastructure" half from a fully dynamic "guest" half:

subnet 192.168.10.0 netmask 255.255.255.0 {
    option routers 192.168.10.1;
    option domain-name-servers 192.168.10.53;
    option domain-name "corp.example.com";

    pool {
        range 192.168.10.100 192.168.10.180;
        default-lease-time 43200;
        max-lease-time 86400;
    }

    pool {
        range 192.168.10.200 192.168.10.250;
        default-lease-time 1800;
        max-lease-time 3600;
        allow members of "guests";
    }
}

class "guests" {
    match if substring(hardware, 1, 3) = 00:0c:29;   # example: match a VM prefix
}

allow members of and deny members of bind a pool to a class, and classes can match on almost anything the client sends. A frequently used pattern is matching on the vendor class identifier so that handsets get a different registry (for example a SIP proxy) than laptops, with no extra configuration on the client side.

Static Reservations with host Blocks

host printer-main {
    hardware ethernet AA:BB:CC:DD:EE:02;
    fixed-address 192.168.1.20;
}
host web-server {
    hardware ethernet AA:BB:CC:DD:EE:10;
    fixed-address 192.168.1.10;
    option host-name "web-server";
}

Custom Options: NTP, SIP, TFTP and PXE Boot

Reservations and ranges cover addressing; the value of running dhcpd yourself shows up in the options. Every option that has a standard DHCP code can be set either by its number or, more readably, by name:

option ntp-servers 192.168.10.123;              # option 42
option time-offset -18000;                      # option 2, seconds from UTC
option domain-search "corp.example.com";
option domain-name-servers 192.168.10.53, 192.168.10.54;

# PXE / network boot for BIOS and UEFI clients
option space PXE;
option PXE.mtftp-ip code 1 = ip-address;
option PXE.mtftp-cport code 2 = unsigned integer 16;
option PXE.mtftp-sport code 3 = unsigned integer 16;
option PXE.mtftp-tmout code 4 = unsigned integer 8;
option PXE.mtftp-delay code 5 = unsigned integer 8;
option PXE.discovery-control code 6 = unsigned integer 8;
option PXE.discovery-mcast-addr code 7 = ip-address;

class "pxeclients" {
    match if substring(option vendor-class-identifier, 0, 9) = "PXEClient";
    next-server 192.168.10.20;
    filename "pxelinux.0";
}

class "uefi-pxeclients" {
    match if substring(option vendor-class-identifier, 0, 9) = "PXEClient"
          and substring(option vendor-class-identifier, 15, 5) = "00007";
    next-server 192.168.10.20;
    filename "grubx64.efi";
}

Two details trip people up constantly. First, the UEFI architecture is encoded inside the vendor class identifier string, so a single PXEClient match will happily offer a BIOS boot file to a UEFI machine, producing the classic "no boot filename received" error. Second, extra DHCP options that are not standard require you to declare the option grammar with code N = type before you can set it — dhcpd refuses to start otherwise, and dhcpd -t tells you exactly which declaration is missing.

Lease Times, Dynamic DNS and the Lease Database

Lease duration is a balance between churn and renewal traffic. A default-lease-time of 86400 (24 hours) with a max-lease-time of 604800 (7 days) suits office networks; guest and lab networks are usually better off with 30 to 60 minute leases so the pool recycles quickly. Clients begin renewing at 50 percent of the lease and enter rebinding at 87.5 percent, so short leases are not as chatty as they sound.

Recording leases in DNS is a two-line change that most people never make and later wish they had:

ddns-update-style interim;
ddns-domainname "corp.example.com";
ddns-rev-domainname "in-addr.arpa.";
option domain-name-servers 192.168.10.53;

zone corp.example.com. {
    primary 192.168.10.53;
    key "ddns-key";
}
key "ddns-key" {
    algorithm hmac-sha256;
    secret "base64secretgoeshere==";
}

Create the key with tsig-keygen -a hmac-sha256 ddns-key and paste the resulting key block into both dhcpd.conf and BIND, then add update-policy statements on the zone. The reward is that every workstation becomes reachable by name the moment it takes a lease. Without it, the lease file is still useful — it is the closest thing to a live inventory of what is on your network — which is why the verification section below includes reading it directly.

Validate, Start and Manage Leases

sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf   # syntax check - always run this first
sudo systemctl enable --now isc-dhcp-server
sudo systemctl status isc-dhcp-server
cat /var/lib/dhcp/dhcpd.leases           # active leases
sudo dhcp-lease-list                     # human-readable view (if installed)

For high availability, two servers can share a failover peer declaration with mclt 1800; split 128;, and each pool then uses pool { failover peer "dhcp-failover"; range ...; }. Note that ISC DHCP is in maintenance mode - new deployments should evaluate its successor Kea - but dhcpd remains the default on Ubuntu and is perfectly fine for small-to-medium networks.

Failover: Two Servers Sharing One Address Pool

ISC DHCP has a built-in failover protocol; the two peers exchange lease updates over TCP port 647 and are identified by a shared name. There are two roles, primary and secondary, and the split is typically 128, meaning the primary is responsible for the whole pool until the secondary asks for a share. A minimal pair looks like this:

# ------------------------------------------------------------------
# primary: 192.168.10.5
# ------------------------------------------------------------------
failover peer "dhcp-failover" {
    primary;
    address 192.168.10.5;
    port 647;
    peer address 192.168.10.6;
    peer port 647;
    max-response-delay 60;
    max-unacked-updates 10;
    mclt 3600;              # maximum client lead time
    split 128;
    load balance max seconds 3;
}

subnet 192.168.10.0 netmask 255.255.255.0 {
    pool {
        failover peer "dhcp-failover";
        range 192.168.10.100 192.168.10.180;
    }
}

# ------------------------------------------------------------------
# secondary: 192.168.10.6   (mirror of the above, role flipped)
# ------------------------------------------------------------------
failover peer "dhcp-failover" {
    secondary;
    address 192.168.10.6;
    port 647;
    peer address 192.168.10.5;
    peer port 647;
    max-response-delay 60;
    max-unacked-updates 10;
    mclt 3600;
    split 128;
    load balance max seconds 3;
}

Rules worth internalising: every range that should be redundant must be inside a pool that names the failover peer, and both servers must agree on the peer name and the mclt value. If the pair loses contact, the secondary keeps serving from its half of the lease database until the maximum client lead time expires, at which point it can take over the whole range — this is called communications-interrupted state and is the reason mclt is measured in seconds of acceptable uncertainty rather than minutes.

Logging, Troubleshooting and Common Errors

dhcpd logs through syslog under the dhcpd facility, so a dedicated log file keeps it out of the noisy general messages log:

# /etc/rsyslog.d/50-dhcpd.conf
if $programname == 'dhcpd' then /var/log/dhcpd.log
& stop

sudo systemctl restart rsyslog isc-dhcp-server
sudo tail -f /var/log/dhcpd.log

When something does not work, work down this list in order. It covers the overwhelming majority of real-world tickets:

  • Service starts then dies immediately. Almost always a syntax or duplicate-declaration error. Run sudo dhcpd -t -cf /etc/dhcp/dhcpd.conf and read the line number it prints.
  • "No subnet declaration for eth1". The server has no subnet block that matches the interface address. Either add one or correct INTERFACESv4 so dhcpd is not listening on that interface at all.
  • Client gets an address from the wrong range. A rogue DHCP server (often a consumer router, a printer or a misconfigured virtual switch) is on the segment. Confirm with tcpdump -i eth1 -n port 67 or port 68 and then fix it at layer 2.
  • Addresses are handed out twice. Two dhcpd instances, or a router also serving the same subnet. The lease in /var/lib/dhcp/dhcpd.leases and the ARP table will both show the conflict.
  • Pool exhausted. Check with a lease count versus the range size, and consider lowering lease times for the affected pool.

omshell, shipped with the server package, lets you query and modify the running server's state without editing the file — useful for inspecting what dhcpd actually loaded versus what you think you wrote:

sudo omshell
> server localhost
> connect
> new "subnet"
> set subnet = 00:00:00:00:00:00:00:00
> open
> display

Hardening the DHCP Service

A DHCP server is one of the few services where an impostor can silently redirect an entire subnet, so a few protections are worth the effort:

  • Pin the interface list. Set INTERFACESv4 explicitly instead of letting dhcpd discover interfaces, so it cannot accidentally answer on a WAN or management NIC.
  • Keep the configuration file root-owned with mode 0644, and the lease directory 0755 owned by dhcpd.
  • Never place the server on an untrusted segment. Run it on a dedicated management or server VLAN.
  • Enable DHCP snooping on access switches and trust only the uplink toward the real server. On Cisco this is ip dhcp snooping plus ip dhcp snooping trust on the uplink, and the equivalent features exist on ArubaOS-CX and Huawei VRP.
  • Filter at the edge. Block UDP 67/68 from the WAN and from guest VLANs toward the servers.
  • Rate-limit ICMP and ARP accordingly, and monitor for DHCPNAK spikes, which usually indicate a second server.

For the switching side of these controls, see ArubaOS-CX DHCP snooping with trusted ports and Huawei DHCP snooping and IPSG.

ISC DHCP vs Kea: When to Migrate

ISC has been clear that dhcpd is in maintenance mode and Kea is the successor. Kea is a different daemon with a JSON configuration model, a control agent for REST-style management, a lease database in MySQL, PostgreSQL or memfile, and much better performance. It is the right choice for new large deployments and for anyone who needs the lease database in SQL for reporting.

That said, dhcpd remains packaged and supported on current Ubuntu, Debian, RHEL and their derivatives, is trivially simple to reason about, and has thousands of well-tested configuration examples. For a small-to-medium network — a lab, an office, a branch — running dhcpd is a perfectly defensible choice today. A sensible migration path is to keep dhcpd and rewrite the same scopes in Kea's kea-dhcp4.conf on a spare host, validate the address plan there, and cut over one VLAN at a time; the scope hierarchy maps over in a fairly mechanical way, with subnet4 replacing subnet and pools replacing range.

Related Guides on This Site

See the router side of DHCP in Cisco IOS DHCP pools and exclusions and ip helper-address DHCP relay, or secure the L2 path with DHCP snooping.

Related reading on this site: DNS is the natural companion to DHCP, so see BIND9 authoritative zone configuration and systemd-resolved and resolvectl troubleshooting. For IPv6 equivalents of what dhcpd does here, read IPv6 Neighbor Discovery and Router Advertisements. On the routing side, Arista EOS DHCP relay covers the relay configuration in the same style as the Cisco article below, and NetBox IPAM is how you keep the address plan itself documented.

原文链接:https://ubuntu.com/server/docs/how-to/networking/install-isc-dhcp-server