EthicsComputerCourses
Join training +91 90532 10052

DHCP

Computer Networks · Lesson 24 of 32 Advanced 7 min read Updated

By the end of this lesson you will be able to describe the four DHCP messages in order, explain leases and renewal times, read DHCP options, say why relay agents are needed, and simulate a small DHCP server in Python.

When you join the Wi-Fi at a café in Pune, your phone gets an IP address, a subnet mask, a gateway and DNS servers in about a second, without you typing anything. That is DHCP, the Dynamic Host Configuration Protocol (RFC 2131). Without it, someone would have to configure every device by hand.

What DHCP hands out

A DHCP server gives each client a set of settings, called a lease:

  • an IP address from a pool (also called a scope);
  • the subnet mask;
  • the default gateway (router);
  • DNS server addresses;
  • the lease time: how long the client may keep the address.

Think of a library lending books. You borrow a book for 14 days. Before the due date you can renew it. If you never come back, the book returns to the shelf for someone else. IP addresses work the same way: borrowed, renewed, and eventually returned to the pool.

The DORA exchange

A new client has no IP address and does not know where the server is. So DHCP starts with broadcasts, over UDP: servers listen on port 67, clients on port 68.

  1. Discover. The client broadcasts: "Is there a DHCP server here?" Source address 0.0.0.0, destination 255.255.255.255.
  2. Offer. A server replies with an offer: "You can have 192.168.1.23, mask /24, gateway .1, DNS, for 24 hours."
  3. Request. The client broadcasts: "I accept the offer from that server." It broadcasts so that any other servers that made offers know they were not chosen.
  4. Acknowledge (ACK). The chosen server confirms. The client may now use the address.

The first letters make DORA, an easy way to remember the order. Many clients also check that nobody else is using the address (with ARP) before using it.

A DHCP server in Python

Let us build a small server that hands out addresses from a pool and remembers which device has which, using each device's MAC address as the key.

PythonRun it yourself
import ipaddress

class DhcpServer:
    def __init__(self, network, first, last, gateway, dns, lease):
        net = ipaddress.ip_network(network)
        self.mask = net.netmask
        self.free = [ipaddress.ip_address(first) + i
                     for i in range(int(ipaddress.ip_address(last)) - int(ipaddress.ip_address(first)) + 1)]
        self.leases = {}
        self.gateway, self.dns, self.lease = gateway, dns, lease

    def dora(self, mac):
        print(f"DISCOVER from {mac}")
        ip = self.leases.get(mac) or self.free.pop(0)
        print(f"OFFER    {ip} mask {self.mask} gw {self.gateway} dns {self.dns} lease {self.lease}s")
        print(f"REQUEST  {mac} wants {ip}")
        self.leases[mac] = ip
        print(f"ACK      {ip} is leased to {mac}\n")

server = DhcpServer("192.168.1.0/24", "192.168.1.100", "192.168.1.199",
                    gateway="192.168.1.1", dns="192.168.1.1", lease=86400)
server.dora("3c:22:fb:12:34:56")
server.dora("da:a1:19:7c:20:01")
server.dora("3c:22:fb:12:34:56")      # same laptop rejoins
print("Free addresses left:", len(server.free))
Output
DISCOVER from 3c:22:fb:12:34:56
OFFER    192.168.1.100 mask 255.255.255.0 gw 192.168.1.1 dns 192.168.1.1 lease 86400s
REQUEST  3c:22:fb:12:34:56 wants 192.168.1.100
ACK      192.168.1.100 is leased to 3c:22:fb:12:34:56

DISCOVER from da:a1:19:7c:20:01
OFFER    192.168.1.101 mask 255.255.255.0 gw 192.168.1.1 dns 192.168.1.1 lease 86400s
REQUEST  da:a1:19:7c:20:01 wants 192.168.1.101
ACK      192.168.1.101 is leased to da:a1:19:7c:20:01

DISCOVER from 3c:22:fb:12:34:56
OFFER    192.168.1.100 mask 255.255.255.0 gw 192.168.1.1 dns 192.168.1.1 lease 86400s
REQUEST  3c:22:fb:12:34:56 wants 192.168.1.100
ACK      192.168.1.100 is leased to 3c:22:fb:12:34:56

Free addresses left: 98

When the first laptop rejoins, the server gives it the same address again, because the lease is still recorded. Real servers try to do this too, which is why your laptop often keeps the same address at home.

Notice the pool runs from .100 to .199. Addresses below .100 are left out of the pool on purpose, for devices that need fixed addresses such as the router and a printer. Keeping fixed addresses outside the pool avoids conflicts.

Lease renewal

A client does not wait until its lease runs out. RFC 2131 sets two timers by default:

  • T1 = 50% of the lease time: the client asks its own server directly (unicast) to renew.
  • T2 = 87.5% of the lease time: if the server has not answered, the client broadcasts to any server.
  • If the lease ends with no renewal, the client must stop using the address and start DORA again.
PythonRun it yourself
lease = 24 * 3600            # 24 hours, in seconds
t1 = lease * 0.5
t2 = lease * 0.875
for name, t in [("T1 (renew)", t1), ("T2 (rebind)", t2), ("Lease ends", lease)]:
    print(f"{name:<12} after {t / 3600:>5.1f} hours")
Output
T1 (renew)   after  12.0 hours
T2 (rebind)  after  21.0 hours
Lease ends   after  24.0 hours

DHCP options

All the extra settings travel as options at the end of the DHCP message. Each option is coded as type, length, value (TLV): one byte for the option number, one byte for the length, then the value. Some common options:

OptionMeaning
1Subnet mask
3Router (default gateway)
6DNS servers
51Lease time in seconds
53DHCP message type (1 Discover, 2 Offer, 3 Request, 5 ACK)
255End of options

Let us decode a small block of options.

PythonRun it yourself
import ipaddress, struct

raw = bytes.fromhex("350105" "0104ffffff00" "0304c0a80101" "3304" "00015180" "ff")
types = {1: "Discover", 2: "Offer", 3: "Request", 5: "ACK"}
i = 0
while raw[i] != 255:
    code, length = raw[i], raw[i + 1]
    value = raw[i + 2:i + 2 + length]
    if code == 53:
        print("Message type:", types[value[0]])
    elif code in (1, 3):
        name = "Subnet mask" if code == 1 else "Router"
        print(f"{name}:", ipaddress.ip_address(value))
    elif code == 51:
        print("Lease time:", struct.unpack("!I", value)[0], "seconds")
    i += 2 + length
Output
Message type: ACK
Subnet mask: 255.255.255.0
Router: 192.168.1.1
Lease time: 86400 seconds

DHCP across routers: relay agents

DORA starts with a broadcast, and routers do not forward broadcasts. So a DHCP server in one subnet cannot hear clients in other subnets. Instead of putting a server in every VLAN, network engineers configure a DHCP relay agent on the router (on Cisco, the ip helper-address command). The relay receives the client's broadcast and forwards it as a unicast to the central server, adding the subnet it came from, so the server picks the right pool.

Reservations and useful commands

A reservation ties an address to a MAC address, so a device such as a shop's billing printer always gets the same address while still using DHCP.

On Windows you can release and renew your own lease:

PowerShell
ipconfig /release
ipconfig /renew
ipconfig /all

ipconfig /all shows the DHCP server, the lease obtained and the lease expiry time. On Linux, ip addr shows the address and NetworkManager tools show the lease.

Common mistakes

  • Getting the DORA order wrong. It is Discover, Offer, Request, Acknowledge.
  • Thinking DHCP uses TCP. It uses UDP: port 67 for servers and 68 for clients.
  • Putting static addresses inside the pool. The server may hand the same address to someone else.
  • Forgetting the relay. Without a relay agent, clients in other subnets never reach the server.

Key points

  • DHCP gives clients an address, mask, gateway, DNS servers and a lease time automatically.
  • The exchange is Discover, Offer, Request, Acknowledge (DORA), starting with broadcasts over UDP 67/68.
  • Clients renew at T1 (50%) and rebind at T2 (87.5%) of the lease.
  • Options are type-length-value fields such as 1 (mask), 3 (router), 6 (DNS), 51 (lease) and 53 (message type).
  • Relay agents forward DHCP between subnets because routers do not pass broadcasts.
  • Use reservations for fixed devices and DHCP snooping against rogue servers.

Exercise

Extend DhcpServer with a release(mac) method that puts the address back at the front of the free list and deletes the lease. Lease addresses to three devices, release the second one, then lease to a fourth device. Which address does the fourth device get?

Show solution
Python
import ipaddress

class DhcpServer:
    def __init__(self, first, count):
        start = ipaddress.ip_address(first)
        self.free = [start + i for i in range(count)]
        self.leases = {}

    def lease(self, mac):
        ip = self.leases.get(mac) or self.free.pop(0)
        self.leases[mac] = ip
        print(f"{mac} -> {ip}")

    def release(self, mac):
        ip = self.leases.pop(mac)
        self.free.insert(0, ip)
        print(f"{mac} released {ip}")

s = DhcpServer("192.168.1.100", 10)
for mac in ["aa:01", "aa:02", "aa:03"]:
    s.lease(mac)
s.release("aa:02")
s.lease("aa:04")
Output
aa:01 -> 192.168.1.100
aa:02 -> 192.168.1.101
aa:03 -> 192.168.1.102
aa:02 released 192.168.1.101
aa:04 -> 192.168.1.101

The released address goes back to the front of the pool, so the fourth device receives 192.168.1.101. Real servers often prefer to hand out addresses that have been free the longest, to avoid confusing old connections.

Quick Quiz

1. What is the correct order of DHCP messages?

2. Which UDP port does a DHCP server listen on?

3. With an 8-hour lease, when does the client first try to renew (T1)?

4. Why is a DHCP relay agent needed?

Learn Computer Networks with a mentor

Free lessons get you started. Our Computer Science Fundamentals training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call