DHCP
DHCP (Dynamic Host Configuration Protocol) automatically assigns IP addresses, netmask, gateway, DNS servers, and other network parameters to hosts. It’s the protocol that makes “plug and play” networking work — no manual IP configuration needed.
The DORA Process
DHCP uses a 4-message exchange:
Client Server
│ │
│─────── DISCOVER (broadcast) ─────────────▶│ "I need an IP"
│ │
│◀─────── OFFER (broadcast) ──────────────│ "Here's an offer:
│ yiaddr=192.168.1.100 │ you can have .100"
│ lease time=3600 │
│ │
│─────── REQUEST (broadcast) ──────────────▶│ "Yes, I'll take .100"
│ requested_addr=192.168.1.100 │
│ server_id=192.168.1.1 │
│ │
│◀─────── ACK (broadcast) ────────────────│ "OK, .100 is yours
│ yiaddr=192.168.1.100 │ for 3600 seconds"
│ lease time=3600 │
DISCOVER: Client broadcasts looking for DHCP servers OFFER: Server offers an IP address REQUEST: Client says “I want this offer” ACK: Server confirms, lease is active
All broadcast because the client doesn’t have an IP yet (uses 0.0.0.0 as source, 255.255.255.255 as destination).
DHCP on Linux
systemd-networkd
systemd-networkd is systemd’s built-in network manager (minimal, no GUI):
# /etc/systemd/network/eth0.network
[Match]
Name=eth0
[Network]
DHCP=ipv4
IPv6AcceptRA=yes
# Or static:
[Match]
Name=eth0
[Network]
Address=192.168.1.100/24
Gateway=192.168.1.1
DNS=8.8.8.8
DNS=1.1.1.1systemctl enable --now systemd-networkd
systemctl status systemd-networkd
# View DHCP lease info
cat /var/lib/systemd/network/*lease* 2>/dev/null
# or
networkctl status eth0NetworkManager
NetworkManager is the default on desktop/server Ubuntu, Fedora, and most distros with a GUI:
# CLI: nmcli
nmcli device status
nmcli device show eth0
nmcli connection show
nmcli connection modify "Wired connection 1" ipv4.addresses 192.168.1.100/24
nmcli connection modify "Wired connection 1" ipv4.gateway 192.168.1.1
nmcli connection modify "Wired connection 1" ipv4.dns "8.8.8.8,1.1.1.1"
nmcli connection modify "Wired connection 1" ipv4.method manual # static
nmcli connection modify "Wired connection 1" ipv4.method auto # DHCP
# Restart the connection
nmcli connection up "Wired connection 1"
# GUI: nmtui (text-based)
nmtuidhclient
dhclient is the classic ISC DHCP client:
# Request DHCP lease
dhclient eth0
# Release DHCP lease
dhclient -r eth0
# Verbose output
dhclient -v eth0
# Specific config file
dhclient -cf /etc/dhcp/dhclient.conf eth0
# Check lease
cat /var/lib/dhcp/dhclient.leasesDHCP Server (dnsmasq / isc-dhcp-server)
# dnsmasq as DHCP server (common in home routers, labs)
# /etc/dnsmasq.d/dhcp.conf
dhcp-range=192.168.1.50,192.168.1.150,12h
dhcp-option=option:router,192.168.1.1
dhcp-option=option:dns-server,8.8.8.8
dhcp-host=aa:bb:cc:dd:ee:ff,192.168.1.50,fixed # static lease by MAC
# ISC dhcpd
# /etc/dhcp/dhcpd.conf
subnet 192.168.1.0 netmask 255.255.255.0 {
range 192.168.1.50 192.168.1.150;
option routers 192.168.1.1;
option domain-name-servers 8.8.8.8;
}DHCP Lease File
# NetworkManager lease
cat /var/lib/NetworkManager/internal-*-eth0.lease
# systemd-networkd lease
cat /var/lib/systemd/network/*eth0*
# dhclient lease
cat /var/lib/dhcp/dhclient.leases
# lease {
# interface "eth0";
# fixed-address 192.168.1.100;
# option subnet-mask 255.255.255.0;
# option routers 192.168.1.1;
# option dhcp-lease-time 3600;
# option dhcp-message-type 5;
# option domain-name-servers 8.8.8.8,1.1.1.1;
# renew 2 2025/06/06 14:00:00;
# rebind 2 2025/06/06 15:30:00;
# expire 2 2025/06/06 16:00:00;
# }DHCP Options (Option Codes)
# Common DHCP options:
option routers # default gateway
option subnet-mask
option domain-name-servers # DNS servers
option domain-name
option broadcast-address
option ntp-servers
option lease-timeDHCP Relay (for multiple VLANs)
If DHCP servers and clients are on different networks, a DHCP relay agent (often the router) forwards DHCP requests:
VLAN10 client (broadcast DISCOVER)
→ Router (relay agent) adds giaddr (relay IP)
→ Router forwards to DHCP server at 10.0.0.10
→ Server responds via router to client
# On a Linux router, enable DHCP relay:
# With ISC Kea:
# /etc/kea/kea-agent.conf
{
"Dhcp4": {
"relay": {
"ip-addresses": ["10.0.0.10"]
}
}
}
# Or with ISC dhcpd:
# iptables -t nat -A PREROUTING -p udp --dport 67 -j DNAT --to 10.0.0.10:67Debugging DHCP Issues
# 1. Is the interface getting an address?
ip addr show eth0
# 2. Check NetworkManager state
nmcli device status
networkctl status eth0
# 3. View lease history
journalctl -u NetworkManager | grep DHCP
journalctl -u systemd-networkd | grep DHCP
# 4. Packet capture
tcpdump -i eth0 port 67 or port 68 -v
# port 67 = BOOTP/DHCP server
# port 68 = BOOTP/DHCP client
# 5. Force renew
nmcli connection up "Wired connection 1"
# or
dhclient -r eth0 && dhclient eth0
# 6. Check firewall (DHCP uses UDP 67/68)
iptables -L -n -v | grep 67DHCP and Containers
Containers get IP addresses from Docker’s internal DHCP server (dnsmasq) or directly from the host’s network namespace:
# Docker's built-in DHCP (in bridge network mode):
# Docker runs dnsmasq as 172.17.0.1, assigns 172.17.0.0/16
# Container: DHCP DISCOVER → dnsmasq on docker0 bridge → offers 172.17.0.x
# Kubernetes pod DHCP (CNI):
# Most CNI plugins don't use DHCP — they assign IPs statically from a CIDR
# Butcilico's macvlan and ipvlan can use DHCP
# Check container's DHCP lease (if using DHCP in container):
docker exec container1 cat /var/lib/dhcp/*lease*