Skip to content

dhcp server

Thomas Mangin edited this page Jun 24, 2026 · 5 revisions

Pre-Alpha. This page describes behavior that may change.

DHCP server plugin serving leases to LAN clients per RFC 2131/2132. Address pool with bitmap allocation, lease tracking with expiry timers, full DISCOVER/OFFER/REQUEST/ACK/NAK/RELEASE/DECLINE state machine, static MAC-to-IP mappings, subnet matching for multi-handler dispatch, support for multiple named address ranges per subnet, and PXE boot options (RFC 4578) for network provisioning.

PXE boot

The server can hand PXE clients a bootfile and next-server, which is how Zero-Touch Provisioning bootstraps bare-metal targets. It detects PXE clients via option 60 (PXEClient:), reads option 93 (client architecture) per RFC 4578 to select a BIOS or UEFI bootfile, and serves options 43 (vendor-specific), 66 (next-server), and 67 (bootfile name). The ze install remote command wires these up automatically; see Zero-Touch Provisioning for the full PXE flow.

Category

Subsystem.

Configuration

service {
    dhcp-server {
        enabled true;
        listen-interface eth1;

        shared-network lan {
            subnet 192.168.1.0/24 {
                range {
                    start 192.168.1.100;
                    stop 192.168.1.200;
                }
                lease-time 86400;
                default-router 192.168.1.1;
                dns-server 8.8.8.8;
                dns-server 8.8.4.4;
                domain-name example.com;

                static-mapping printer {
                    mac-address aa:bb:cc:dd:ee:ff;
                    ip-address 192.168.1.10;
                }
            }
        }
    }
}

Multiple named ranges

A subnet can define multiple named address ranges for segmented allocation:

shared-network lan {
    subnet 192.168.1.0/24 {
        range clients {
            start 192.168.1.100;
            stop 192.168.1.199;
        }
        range servers {
            start 192.168.1.200;
            stop 192.168.1.250;
        }
    }
}

Each range has an independent bitmap pool. Address allocation prefers the client's last-assigned range.

Configuration reference

Path Type Default Description
dhcp-server/enabled boolean false Enable the DHCP server.
dhcp-server/listen-interface leaf-list -- Interfaces to serve DHCP on.
dhcp-server/shared-network/<name>/subnet/<prefix>/range/start string -- First allocatable address.
dhcp-server/shared-network/<name>/subnet/<prefix>/range/stop string -- Last allocatable address.
dhcp-server/shared-network/<name>/subnet/<prefix>/lease-time uint32 86400 Lease duration in seconds (60-604800).
dhcp-server/shared-network/<name>/subnet/<prefix>/default-router string -- Default gateway (option 3).
dhcp-server/shared-network/<name>/subnet/<prefix>/dns-server leaf-list -- DNS servers (option 6).
dhcp-server/shared-network/<name>/subnet/<prefix>/domain-name string -- Domain name (option 15).
dhcp-server/shared-network/<name>/subnet/<prefix>/static-mapping/<name>/mac-address string -- Client MAC address.
dhcp-server/shared-network/<name>/subnet/<prefix>/static-mapping/<name>/ip-address string -- Fixed IP for this client.

Behaviour

The server binds to the specified interfaces using SO_BINDTODEVICE (Linux). For each DHCPDISCOVER, it finds the matching subnet by comparing the giaddr (relay agent) or the receiving interface's address against configured subnets. Addresses are allocated from the bitmap pool, preferring the client's previously-assigned address if still available.

Static mappings take priority over the dynamic pool. A client whose MAC matches a static mapping always receives that fixed address regardless of the pool state.

Leases are tracked with expiry timers. Expired leases return their address to the pool. The server handles RELEASE and DECLINE messages to free addresses early.

Robustness

Two operational safeguards keep the server honest about what it is actually doing.

Guard against zero listeners

The server binds one UDP listener per configured interface. If none of them bind (a missing or misnamed interface, a permission failure), the server does not pretend it started. Instead of logging a false "started" line, it logs an error naming the interfaces it could not bind and serves nothing. This matches the same silent-failure class already fixed in the TFTP and image servers: a server with zero listeners must never look healthy in the logs.

ERROR dhcpserver: no interfaces bound; server not serving  interfaces=[eth1]

When at least one listener binds, the "started" line reports the count of shared networks, the interface list, and the number of listeners that actually bound, so a partial bind is visible rather than hidden.

Name the competing server in unanswered-REQUEST logs

A DHCPREQUEST goes unanswered only when it is not addressed to this server: either the client's option 54 (server-id) names a different DHCP server, or the requested address is outside any configured subnet. When the server-id is valid but is not one of our own, the log line names it explicitly:

INFO dhcpserver: no reply to REQUEST (client selected another DHCP server)
     mac=... selected-server-id=192.168.1.250 our-server-ids=[192.168.1.1] requested-ip=...

This makes a competing DHCP server on the provisioning segment diagnosable from this log alone. A booted kernel's ip=dhcp racing a second server is the classic cause of a target that PXE-boots but then cannot reach us. When the REQUEST is simply outside our subnet (no foreign server-id), the log says so without the extra detail.

See also

Source

main/internal/plugins/dhcpserver/

Adapted from main/docs/plugin-overview.md and main/internal/plugins/dhcpserver/register.go.

Home

About

First Steps

Configuration

Operation

Interfaces

Plugins

Plugin Development

Chaos Testing

Blueprints

Development

Reference

Clone this wiki locally