DHCP Explained: How a Machine With No IP Address Gets One in Four Messages

DHCP Explained: How a Machine With No IP Address Gets One in Four Messages

Originally published at https://blog.pathvector.dev/protocol-lab-dhcp-22/ — part of the free Protocol Lab series. This post is part of Protocol Lab, a free, hands-on series for learning networking protocols by building and breaking them in a container lab. All the lab material — topologies, configs, and scripts — lives in the repo: github.com/pathvector-studio/protocol-lab. Every host in the labs so far had its address configured for it. But how does a machine that just booted — with no IP at all — get one? The answer is DHCP, and it takes exactly four messages, often called DORA: Discover — the client shouts "is there a DHCP server?" (broadcast, from 0.0.0.0), Offer — a server replies "you can have 10.0.0.193", Request — the client says "yes, I'll take 10.0.0.193", Ack — the server confirms "it's yours, for 3600 seconds". In this lab you'll start a client with an address-less link, run a DHCP client, and capture the whole DORA exchange on the wire. Reading guide: rfc-notes/dhcp-dora.md Prerequisite: TCP Lab 07: Handshake and Teardown (reading captures) Expected time: 40–55 minutes. The Goal By the end, you should be able to label this exchange: Step From → To Meaning Discover 0.0.0.0:68 → 255.255.255.255:67 any server out there? Offer server:67 → client:68 here is an address you can use Request 0.0.0.0:68 → 255.255.255.255:67 I request that offered address Ack server:67 → client:68 confirmed, with lease time What You Will Learn Why a host with no address must use broadcast to reach a server it does not yet know. The four DHCP messages (DORA) and what each one carries. What a lease is and why addresses are temporary. What options DHCP delivers besides the address (router, DNS, lease time). Why the DHCP ports are 67 (server) and 68 (client). This lab does not cover: DHCP relay (across subnets), reservations, or failover. DHCPv6 or IPv6 SLAAC (a different mechanism). Rebinding/renewing timers (T1/T2) in depth. Where to Read in the RFCs RFC Section What to focus on RFC 2131 3.1 How a client obtains an address (the DORA flow) RFC 2131 2 DHCP message format (built on BOOTP) RFC 2131 4.1 How broadcast is used, and ports 67/68 RFC 2132 3, 9 DHCP options (subnet, router, DNS, lease time, message type 53) The Big Picture A client (with no address) and a server (10.0.0.1, running udhcpd) are connected by a single link. client (no IP) ==== eth1/eth1 ==== server (10.0.0.1) udhcpc udhcpd, pool 10.0.0.100-.200 Enter fullscreen mode Exit fullscreen mode The client's eth1 has only its link brought up — no IP. It gets one via DHCP. sequenceDiagram participant C as client (0.0.0.0) participant S as server (10.0.0.1) C->>S: DISCOVER (broadcast 255.255.255.255:67) S->>C: OFFER 10.0.0.193 (to :68) C->>S: REQUEST 10.0.0.193 (broadcast :67) S->>C: ACK 10.0.0.193, lease 3600s (to :68) Note over C: configures eth1 = 10.0.0.193 Enter fullscreen mode Exit fullscreen mode Note: 10.0.0.0/24 is a local, closed range — nothing in this lab touches the real internet. What You Need Recommended environment: Linux / WSL2 / a Linux VM Docker containerlab Images used: nicolaka/netshoot:latest — it bundles udhcpd (the server), udhcpc (the client), and tcpdump. No additional images are required. The DHCP server configuration lives at examples/dhcp-22/udhcpd.conf. Running the Lab The quick path, which deploys, verifies, and tears down for you: ./scripts/labctl.sh run dhcp-22 Enter fullscreen mode Exit fullscreen mode Or step through it manually: 1. Move into the working directory cd protocol-lab/examples/dhcp-22 Enter fullscreen mode Exit fullscreen mode 2. Deploy and start the DHCP server sudo containerlab deploy -t dhcp-22.clab.yml docker exec clab-dhcp-22-server sh -c ": > /tmp/udhcpd.leases; udhcpd -f /etc/udhcpd.conf &" Enter fullscreen mode Exit fullscreen mode udhcpd.conf hands out 10.0.0.100–.200 and passes the router, DNS, and lease time as options. 3. Set up the capture and have the client get an address docker exec -d clab-dhcp-22-client tcpdump -i eth1 -n "udp port 67 or udp port 68" docker exec clab-dhcp-22-client udhcpc -i eth1 -q -f -n docker exec clab-dhcp-22-client ip -4 addr show eth1 Enter fullscreen mode Exit fullscreen mode udhcpc's output should include a line like lease of 10.0.0.193 obtained from 10.0.0.1, and ip addr should show an address on eth1. 4. Read the DORA exchange in the capture docker exec clab-dhcp-22-client tcpdump -n -vv -r /tmp/dhcp.pcap Enter fullscreen mode Exit fullscreen mode 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP ... DHCP-Message: Discover ... > ...68: DHCP-Message: Offer 0.0.0.0.68 > 255.255.255.255.67: DHCP-Message: Request, Requested-IP 10.0.0.193 ... > ...68: DHCP-Message: ACK Enter fullscreen mode Exit fullscreen mode Expected Output udhcpc: lease of 10.0.0.1xx obtained from 10.0.0.1. ip addr show eth1: inet 10.0.0.1xx. The capture: all four messages — Discover → Offer → Request (with a Requested-IP) → ACK. Why It Works DHCP is a mechanism for handing an address — and the settings that go with it — to a host that doesn't have one. The hard part is the starting point: the client doesn't know the server, and doesn't have an address of its own. Why broadcast. The client knows neither the server's IP nor its own. So it sends to 255.255.255.255 (broadcast) from source 0.0.0.0, reaching everyone on the link. Only the server responds. The four DORA steps: Discover: "Is there a DHCP server?" Broadcast. Offer: The server proposes a candidate — "how about this address?" If there are multiple servers, multiple Offers can arrive. Request: The client asks for that address, including which server's Offer it accepts. This is also broadcast — so the servers that weren't chosen learn their offers were declined. Ack: The server finalizes it, attaching the lease time and options. The lease. The address isn't bought outright — it's borrowed, with an expiry. Before the lease runs out, the client renews it. This lets addresses of departed hosts be reclaimed and reused, keeping a limited address space in circulation. More than just an address. DHCP also delivers the subnet mask, default router, DNS server, lease time, and more as options (RFC 2132). That's why connecting via DHCP configures your gateway and DNS automatically. Ports 67/68. The server uses 67, the client 68 — both UDP. Because the ports are fixed, the exchange works even before any IP is settled. The key insight: starting from "I know neither my own address nor the server's," broadcast plus four messages is enough to borrow a complete address setup. Common Pitfalls Thinking the R in DORA comes from the server. The Request is sent by the client (requesting the offered address). Thinking the Offer is final. The Ack is what finalizes it. An Offer is a candidate — with multiple servers, several may arrive. Missing why broadcast is needed. The client knows neither its own IP nor the server's. Assuming the address is permanent. A lease has an expiry. Fail to renew and you lose it. Assuming DHCP only hands out an address. It also delivers the router, DNS, lease time, and other options. Mixing up the ports. Server 67, client 68 — swap them and your capture filter won't match. Cleanup sudo containerlab destroy -t dhcp-22.clab.yml --cleanup Enter fullscreen mode Exit fullscreen mode If you used labctl.sh run dhcp-22, the script runs destroy for you at the end. Check Your Understanding What are the four DORA messages? Who sends each one? Why does a client with no address use broadcast? What is its source address? What's the difference between an Offer and an Ack? Which one finalizes the address? What is a lease, and why are addresses time-limited? Besides an address, what does DHCP deliver? (Name three things.) What are the DHCP server and client port numbers? References RFC 2131: Dynamic Host Configuration Protocol RFC 2132: DHCP Options and BOOTP Vendor Extensions RFC 5737: IPv4 Address Blocks Reserved for Documentation udhcpd / udhcpc (BusyBox) documentation Verified Run Log (2026-07-07) This lab has been confirmed reproducible on real hardware. Environment: Ubuntu 26.04 LTS (kernel 7.0.0-27-generic, x86_64) Docker 29.1.3 containerlab 0.77.0 client / server: nicolaka/netshoot:latest (udhcpd 1.38.0 / udhcpc, with tcpdump) Running PATH="/tmp/pl-shim:$PATH" ./scripts/labctl.sh run dhcp-22 performed deploy → verify → destroy, and verification.json returned "status": "verified". The client obtains an address $ docker exec clab-dhcp-22-client udhcpc -i eth1 -q -f -n udhcpc: lease of 10.0.0.193 obtained from 10.0.0.1, lease time 3600 $ docker exec clab-dhcp-22-client ip -4 addr show eth1 inet 10.0.0.193/24 ... scope global eth1 Enter fullscreen mode Exit fullscreen mode DORA is visible on the wire $ docker exec clab-dhcp-22-client tcpdump -n -vv -r dhcp.pcap 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request ... DHCP-Message: Discover ... > ...68: DHCP-Message: Offer 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request ... DHCP-Message: Request Requested-IP: 10.0.0.193 ... > ...68: DHCP-Message: ACK Enter fullscreen mode Exit fullscreen mode Discover (source 0.0.0.0, destination broadcast 255.255.255.255:67) → Offer → Request (Requested-IP 10.0.0.193) → Ack. Starting with no address and no known server, broadcast plus four messages borrows a complete address setup — here including the router, DNS, and lease. Cleanup containerlab destroy -t dhcp-22.clab.yml --cleanup Enter fullscreen mode Exit fullscreen mode That's DHCP: four messages — Discover, Offer, Request, Ack — that take a host from no address at all to a fully configured lease, with the gateway and DNS thrown in for free. Explore the full Protocol Lab series here: github.com/pathvector-studio/protocol-lab. If these labs are useful to you, please ⭐ star the repo on GitHub — it genuinely helps others find the project. Next up, we'll keep climbing the stack and look at what happens once a freshly configured host starts resolving names and moving packets.

Original Source

Read the full article at Dev →

KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.