Why DHCP exists

Every device on an IPv4 network needs a few settings before it can talk beyond its own cable: an address, a subnet mask, a default gateway and at least one Domain Name System (DNS) server (see IPv4 Addressing). You can type these in by hand — that is called a static configuration — and for routers, switches and servers that is exactly what you do. For hundreds of laptops, phones, printers and IP phones it does not scale:

  • Someone has to keep a list of which address went to which device, and that list is out of date the moment a laptop is replaced.
  • A typo in a mask or gateway gives a device that "has network" but cannot reach anything — a classic first-line ticket.
  • Two devices with the same address disturb each other, and the fault moves around as they take turns answering.
  • Changing a setting for everyone, such as a new DNS server, would mean visiting every device.

The Dynamic Host Configuration Protocol (DHCP) solves this. A DHCP server holds a range of free addresses and the common settings, and hands them out automatically to every client that asks. DHCP for IPv4 is defined in RFC 2131 and grew out of the older Bootstrap Protocol (BOOTP), which is why it still uses the BOOTP port numbers.

What a client receives

Apart from the address itself, everything a DHCP server hands out is an option with a number. The ones you meet every day:

Setting Where it lives Example
IPv4 address the yiaddr ("your address") field 192.168.10.11
Subnet mask option 1 255.255.255.0
Default gateway (router) option 3 192.168.10.1
DNS servers option 6 192.168.10.5, 192.168.10.6
Domain name option 15 example.com
Lease time option 51 86400 seconds (1 day)
Trivial File Transfer Protocol (TFTP) server for phones or network boot option 66 (Cisco IP phones: option 150) 10.0.0.20

Two more options belong to the exchange itself. Option 53, the message type (Discover, Offer and so on), is in every DHCP message. Option 54, the server identifier — the address of the server that made the offer — is in every server reply and in the client's Request when it picks an offer.

DORA: getting an address in four steps

A client without an address has a problem: it cannot send to a server it does not know, from an address it does not have. DHCP solves this with broadcasts and a fixed pair of User Datagram Protocol (UDP) ports — the server listens on UDP 67, the client on UDP 68. The exchange has four messages, known by their initials as DORA: Discover, Offer, Request, Acknowledge.

DORA sequence between a client and a DHCP server: the client broadcasts DHCPDISCOVER, the server answers with DHCPOFFER, the client broadcasts DHCPREQUEST for the offered address, and the server confirms with DHCPACK. Client DHCP server 1 DHCPDISCOVER (broadcast) 0.0.0.0:68 → 255.255.255.255:67 2 DHCPOFFER: 192.168.10.11 for 1 day 192.168.10.1:67 → client port 68 3 DHCPREQUEST (broadcast): I take .11 0.0.0.0:68 → 255.255.255.255:67 4 DHCPACK: lease confirmed 192.168.10.1:67 → client port 68
The DORA exchange. The client has no address yet, so it sends from 0.0.0.0 to the broadcast address 255.255.255.255.
  1. Discover. The client broadcasts a DHCPDISCOVER from source 0.0.0.0 to 255.255.255.255, and its media access control (MAC) address is in the message so servers know who is asking. Every device in the broadcast domain receives it; only DHCP servers reply.
  2. Offer. Each server that has a free address sends a DHCPOFFER with an address and its options. Whether the offer is sent as a broadcast or as a unicast to the client's MAC address depends on a broadcast flag the client set in its Discover: some clients cannot accept a unicast before they have an address.
  3. Request. The client picks one offer (normally the first) and broadcasts a DHCPREQUEST naming that server (option 54) and the address. It is a broadcast on purpose: other servers that made an offer see it too, and put their offered address back in the pool.
  4. Acknowledge. The chosen server confirms with a DHCPACK, and the lease starts. Before using the address, most clients check that nobody else has it by sending an Address Resolution Protocol (ARP) probe; if someone answers, the client sends a DHCPDECLINE and starts again. If the server can no longer give the address, it sends a DHCPNAK instead.

Two other messages complete the picture: a client that gives its lease back, for example with ipconfig /release, sends a DHCPRELEASE (Windows does not release on shutdown by default), and a client with a static address that only wants the options (such as DNS servers) sends a DHCPINFORM.

Leases and renewal

A server never gives an address away for good. It hands out a DHCP lease: the right to use that address for a fixed time, after which it returns to the pool. Leases are what let a pool with 200 addresses serve a guest network with thousands of visitors a week, and they clean up after devices that leave without saying goodbye.

A client does not wait until the lease runs out. Two timers, both set as the lease starts, decide when it acts:

  • T1, the renewal time, at 50% of the lease. The client sends a DHCPREQUEST as a unicast straight to the server that gave the lease. Normally the server answers with a DHCPACK and the lease starts over — the user notices nothing.
  • T2, the rebinding time, at 87.5% of the lease. If the original server has not answered, the client broadcasts its DHCPREQUEST, so that any server can extend the lease.
  • Expiry, at 100%. Without any answer the client must stop using the address and start over with a Discover.

Servers can send other values in option 58 (T1) and option 59 (T2), but the defaults are used almost everywhere. The calculation is simple enough to do in a few lines of Python:

def hms(seconds):
    hours, rest = divmod(int(seconds), 3600)
    minutes, secs = divmod(rest, 60)
    return f"{hours:>3}h {minutes:02}m {secs:02}s"

def timers(lease):
    t1 = lease * 0.5      # renew: unicast to the same server
    t2 = lease * 0.875    # rebind: broadcast to any server
    return t1, t2

for name, lease in [("Cisco IOS default (1 day)", 86_400),
                    ("Windows Server default (8 days)", 8 * 86_400),
                    ("Guest Wi-Fi (2 hours)", 7_200)]:
    t1, t2 = timers(lease)
    print(name)
    print("  T1 renew :", hms(t1))
    print("  T2 rebind:", hms(t2))
    print("  expires  :", hms(lease))
Cisco IOS default (1 day)
  T1 renew :  12h 00m 00s
  T2 rebind:  21h 00m 00s
  expires  :  24h 00m 00s
Windows Server default (8 days)
  T1 renew :  96h 00m 00s
  T2 rebind: 168h 00m 00s
  expires  : 192h 00m 00s
Guest Wi-Fi (2 hours)
  T1 renew :   1h 00m 00s
  T2 rebind:   1h 45m 00s
  expires  :   2h 00m 00s

Choosing a lease time is a trade-off. Short leases free addresses quickly on busy networks with many passing devices, such as guest Wi-Fi. Long leases mean fewer renewals and a network that keeps working longer if the DHCP server is down. An office LAN with fixed desktops can use days; a conference hall is better off with hours.

Scopes, exclusions and reservations

A DHCP scope (a pool in Cisco terms) is the set of addresses a server may hand out on one subnet, together with the options for that subnet: mask, gateway, DNS servers, lease time. A server that serves ten virtual local area networks (VLANs) has ten scopes. When a request arrives, the server picks the scope that matches the subnet the request came from.

Some addresses in a subnet must never be handed out: the router's own address, servers, switches, printers with a static configuration. An exclusion removes them from the scope. Exclusions are usually a block at the start of the subnet, such as .1 to .10, so that "low addresses are static" becomes an easy rule for the whole team.

A DHCP reservation is the opposite idea: the device still uses DHCP, but the server always gives it the same address, tied to its MAC address (or client identifier). Reservations suit printers, access points and other devices that others need to find at a known address, while you keep changing their other settings centrally.

Python's ipaddress module (see the standard library) makes it easy to count what is left in a scope:

import ipaddress

network = ipaddress.ip_network("192.168.10.0/24")
excluded = range(1, 11)                     # .1 to .10: router, servers
reservations = {"00-00-5E-00-53-20": "192.168.10.20"}   # network printer

hosts = list(network.hosts())
excluded_addrs = {network.network_address + n for n in excluded}
reserved_addrs = {ipaddress.ip_address(a) for a in reservations.values()}
pool = [a for a in hosts if a not in excluded_addrs | reserved_addrs]

print("Usable host addresses:", len(hosts))
print("Excluded:", len(excluded_addrs), " Reserved:", len(reserved_addrs))
print("Leasable to any client:", len(pool))
print("First three offers:", ", ".join(str(a) for a in pool[:3]))
print("Last address:", pool[-1])
Usable host addresses: 254
Excluded: 10  Reserved: 1
Leasable to any client: 243
First three offers: 192.168.10.11, 192.168.10.12, 192.168.10.13
Last address: 192.168.10.254

DHCP relay: crossing a router

Routers do not forward broadcasts — that is what keeps a broadcast domain small (see Network Devices). So a Discover sent in VLAN 20 never reaches a DHCP server in the data center on its own. Putting a DHCP server in every subnet would work but is hard to manage. The usual answer is a DHCP relay agent: a router or layer 3 switch that listens for DHCP broadcasts on the client subnet and forwards them as unicast to a server elsewhere.

DHCP relay: a PC broadcasts a DHCPDISCOVER through switch SW1. Router R1, the default gateway of 192.168.10.0/24, has ip helper-address 10.0.0.5 on its interface and forwards the message as a unicast to the DHCP server in 10.0.0.0/24, with giaddr set to 192.168.10.1. DHCPDISCOVER · broadcast unicast · giaddr 192.168.10.1 PC SW1 R1 DHCP server 192.168.10.0/24 10.0.0.0/24 · server 10.0.0.5 ip helper-address 10.0.0.5
The relay agent turns a local broadcast into a routed unicast and writes its own interface address into the giaddr field.

The relay does one more important thing: it writes the address of the interface where the broadcast arrived into the giaddr (gateway IP address) field of the message. The server uses giaddr to choose the right scope — a request with giaddr 192.168.10.1 gets an address from 192.168.10.0/24 — and sends its reply back to the relay, which delivers it to the client.

On Cisco routers and layer 3 switches, the relay is one command on the interface where the clients are, often a VLAN interface (switched virtual interface, SVI):

R1(config)# interface GigabitEthernet0/1
R1(config-if)# ip address 192.168.10.1 255.255.255.0
R1(config-if)# ip helper-address 10.0.0.5
  • interface GigabitEthernet0/1 — the interface in the client subnet; this router is the clients' default gateway.
  • ip helper-address 10.0.0.5 — forward DHCP broadcasts received here to 10.0.0.5. You can add several helper addresses for redundant servers. By default this command also forwards a few other UDP broadcast services, such as TFTP and DNS.

A Cisco router as DHCP server

In branch offices a dedicated server is often overkill, and the router itself hands out addresses. The configuration below creates a scope for 192.168.10.0/24 and keeps the first ten addresses free for static devices.

R1(config)# ip dhcp excluded-address 192.168.10.1 192.168.10.10
R1(config)# ip dhcp pool LAN10
R1(dhcp-config)# network 192.168.10.0 255.255.255.0
R1(dhcp-config)# default-router 192.168.10.1
R1(dhcp-config)# dns-server 192.168.10.5 192.168.10.6
R1(dhcp-config)# domain-name example.com
R1(dhcp-config)# lease 7
R1(dhcp-config)# exit
  • ip dhcp excluded-address 192.168.10.1 192.168.10.10 — a global command: never hand out .1 through .10. Give it before clients start asking.
  • ip dhcp pool LAN10 — create a pool (scope) named LAN10 and enter DHCP pool configuration mode.
  • network 192.168.10.0 255.255.255.0 — the subnet the pool serves (a prefix such as /24 is accepted too).
  • default-router — the gateway (option 3); dns-server — up to eight DNS servers (option 6); domain-name — option 15.
  • lease 7 — seven days. The syntax is lease days [hours [minutes]] or lease infinite; without this command IOS uses one day.

A reservation on IOS is a separate pool with a single host, matched on the client identifier: 01 (Ethernet) followed by the MAC address.

R1(config)# ip dhcp pool PRINTER-2F
R1(dhcp-config)# host 192.168.10.20 255.255.255.0
R1(dhcp-config)# client-identifier 0100.005e.0053.20
R1(dhcp-config)# default-router 192.168.10.1

Verifying on the router

show ip dhcp binding lists the leases the router has handed out. Automatic means a normal lease from the pool, Manual a reservation.

R1# show ip dhcp binding
Bindings from all pools not associated with VRF:
IP address          Client-ID/              Lease expiration        Type
                    Hardware address/
                    User name
192.168.10.11       0100.005e.0053.1a       Oct 07 2026 09:14 AM    Automatic
192.168.10.12       0100.005e.0053.2b       Oct 07 2026 09:20 AM    Automatic
192.168.10.20       0100.005e.0053.20       Infinite                Manual

Two more commands help in a ticket. show ip dhcp pool shows how many addresses are leased and how many are left — an exhausted pool looks exactly like a dead server from the client's side. show ip dhcp conflict lists addresses the router found already in use (IOS pings an address before offering it) and will not hand out until you clear them with clear ip dhcp conflict *.

When DHCP fails

APIPA: the symptom to recognize

A Windows client that gets no answer to its Discover does not stay without an address. It gives itself one from 169.254.0.0/16, with mask 255.255.0.0 and no default gateway — Automatic Private IP Addressing (APIPA), described on IPv4 Addressing. It keeps sending Discovers in the background. Linux and macOS can do the same (IPv4 link-local addressing). For a service desk, a 169.254 address is a clear message: this client asked, and no DHCP server answered. The cause is somewhere between the client and the server — cable, access port in the wrong VLAN, missing ip helper-address, a stopped server or an exhausted pool — not in the client's IP settings.

Ethernet adapter Ethernet:

   Connection-specific DNS Suffix  . :
   Autoconfiguration IPv4 Address. . : 169.254.83.17(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.255.0.0
   Default Gateway . . . . . . . . . :

Commands on the client

On Windows, ipconfig /all shows the lease and the server that gave it. ipconfig /release gives the lease back (a DHCPRELEASE), and ipconfig /renew asks for a new one — watch the address change from 169.254 to a real one.

ipconfig /release
ipconfig /renew
ipconfig /all
Ethernet adapter Ethernet:

   Connection-specific DNS Suffix  . : example.com
   …
   DHCP Enabled. . . . . . . . . . . : Yes
   Autoconfiguration Enabled . . . . : Yes
   IPv4 Address. . . . . . . . . . . : 192.168.10.11(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Lease Obtained. . . . . . . . . . : Wednesday, September 30, 2026 9:14:02 AM
   Lease Expires . . . . . . . . . . : Wednesday, October 7, 2026 9:14:02 AM
   Default Gateway . . . . . . . . . : 192.168.10.1
   DHCP Server . . . . . . . . . . . : 192.168.10.1
   DNS Servers . . . . . . . . . . . : 192.168.10.5
                                       192.168.10.6

On Linux it depends on who manages the interface. Desktops and many servers use NetworkManager: take the connection down and up again with nmcli, then check the address with ip. The word dynamic and the valid_lft countdown show that the address came from DHCP.

sudo nmcli connection down "Wired connection 1"
sudo nmcli connection up "Wired connection 1"
ip -4 addr show dev enp3s0
Connection 'Wired connection 1' successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/4)
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/5)
2: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 192.168.10.12/24 brd 192.168.10.255 scope global dynamic noprefixroute enp3s0
       valid_lft 604794sec preferred_lft 604794sec

On systems that still use the Internet Systems Consortium (ISC) client dhclient (no longer developed, but common on older installs), the verbose flag shows DORA happening live:

sudo dhclient -r enp3s0
sudo dhclient -v enp3s0
Listening on LPF/enp3s0/00:00:5e:00:53:2b
Sending on   LPF/enp3s0/00:00:5e:00:53:2b
Sending on   Socket/fallback
DHCPDISCOVER on enp3s0 to 255.255.255.255 port 67 interval 3 (xid=0x5d1e7a42)
DHCPOFFER of 192.168.10.12 from 192.168.10.1
DHCPREQUEST for 192.168.10.12 on enp3s0 to 255.255.255.255 port 67 (xid=0x5d1e7a42)
DHCPACK of 192.168.10.12 from 192.168.10.1 (xid=0x5d1e7a42)
bound to 192.168.10.12 -- renewal in 287654 seconds.

The renewal time is a little under half of the 7-day lease (302,400 seconds). That is deliberate: RFC 2131 recommends that clients add some random variation to their timers, so that thousands of devices that booted together do not all renew in the same second.

DHCPv6 and DHCP security

IPv6 has two ways to configure hosts automatically: Stateless Address Autoconfiguration (SLAAC), where the host builds its own address from the prefix a router advertises, and DHCPv6, which works like DHCP for IPv4 but uses UDP ports 546 (client) and 547 (server) and multicast instead of broadcast. A difference that surprises people: DHCPv6 does not hand out a default gateway — hosts always learn that from router advertisements. See IPv6 Basics.

DHCP has no authentication: a client believes the first server that answers. A laptop or home router plugged into an office port can act as a rogue DHCP server and hand out a wrong gateway — by accident, taking users offline, or on purpose, to route their traffic through an attacker. Switches defend against this with DHCP snooping: only ports marked trusted (uplinks toward the real server) may send Offers and Acks, and the switch keeps a table of which MAC address got which IP address on which port. See Network Security Basics.