DHCP relay enables multi-subnet DHCP
Without a relay agent, DHCP broadcasts do not cross router boundaries. Configure relay agents on every subnet that uses DHCP.
The previous chapters established how switches forward frames, how VLANs segment traffic, and how routers move packets between subnets. Those mechanisms move data, but they do not answer a basic question: how does a device get an IP address, find a hostname, or synchronize its clock? Network services fill that gap.
Every device on an IP network needs at least three things: an IP address, a way to resolve names to addresses, and an accurate clock. Configuring these manually on hundreds of devices is error-prone and does not scale. Network services automate this configuration and keep it consistent across the entire network.
DHCP (Dynamic Host Configuration Protocol) assigns IP addresses, subnet masks, default gateways, and DNS server addresses to clients automatically. It uses a four-step exchange called DORA (Discover, Offer, Request, Acknowledge).
At the start of the process, the client has no IP address. It sends a broadcast on UDP port 67. The server responds with an offer, the client accepts, and the server confirms.
The transaction ID (xid) is a random 32-bit number chosen by the client. It matches requests to responses when multiple DHCP servers respond.
The client renews at 50% of the lease time (T1) by unicasting to the server. If that fails, it broadcasts at 87.5% (T2). If both fail, the lease expires and the client restarts DORA.
The DHCPOFFER and DHCPACK messages carry DHCP options, each identified by a numeric code. These options deliver configuration beyond the IP address itself.
| Option | Name | Purpose |
|---|---|---|
| 1 | Subnet Mask | Defines the network portion of the address |
| 3 | Router (Default Gateway) | Tells the client where to send non-local traffic |
| 6 | DNS Servers | Provides one or more DNS resolver addresses |
| 15 | Domain Name | Sets the client’s DNS search domain |
| 51 | Lease Time | Duration in seconds before the client renews |
| 66 | TFTP Server Name | Points to a server for firmware or config downloads |
| 67 | Bootfile Name | Specifies the file to download from the TFTP server |
| 150 | TFTP Server Address | Cisco-specific alternative to option 66 |
Options 66 and 67 matter in OT environments where IP phones and thin clients download firmware at boot. A misconfigured option 66 causes devices to fail their boot sequence silently.
DHCP uses broadcast, and routers do not forward broadcasts. In a multi-subnet network, a DHCP relay agent (also called an IP helper) solves this. The relay agent listens for DHCP broadcasts on the local subnet, wraps them in a unicast packet addressed to the DHCP server on a remote subnet, and forwards the server’s response back to the client.
The giaddr (Gateway IP Address) field tells the server which subnet the client belongs to. The server uses giaddr to select the correct address pool. Without a relay agent, clients on remote subnets never receive an address.
On Hirschmann HiOS, configure the relay agent at Routing → DHCP Relay Agent and specify the DHCP server IP.
When a client sends a DHCPDISCOVER and receives no response, it assigns itself an address from the APIPA (Automatic Private IP Addressing) range: 169.254.0.0/16. APIPA addresses are link-local only. They allow communication with other APIPA devices on the same segment but cannot reach any routed network.
If you see a 169.254.x.x address on a device, it means DHCP failed. Common causes: no DHCP server on the subnet, no relay agent configured, the DHCP server’s address pool is exhausted, or a firewall blocks UDP ports 67/68.
The following script captures live DHCP traffic and decodes each message type, making it straightforward to spot rogue DHCP servers or failed renewals.
from scapy.all import sniff, DHCP, BOOTP, Ether, IP
DHCP_MSG_TYPES = {1: "DISCOVER", 2: "OFFER", 3: "REQUEST", 4: "DECLINE", 5: "ACK", 6: "NAK", 7: "RELEASE", 8: "INFORM"}
def decode_dhcp(pkt): if not pkt.haslayer(DHCP): return bootp = pkt[BOOTP] dhcp_options = {opt[0]: opt[1] for opt in pkt[DHCP].options if isinstance(opt, tuple)} msg_type = DHCP_MSG_TYPES.get(dhcp_options.get("message-type", 0), "UNKNOWN") client_mac = pkt[Ether].src offered_ip = bootp.yiaddr if bootp.yiaddr != "0.0.0.0" else "none" server_id = dhcp_options.get("server_id", "unknown") print(f"[DHCP {msg_type:8s}] client={client_mac} offered={offered_ip} server={server_id}") if msg_type == "OFFER": # Two OFFERs from different servers for the same DISCOVER = rogue server print(f" gateway={dhcp_options.get('router', 'none')} " f"dns={dhcp_options.get('name_server', 'none')}")
sniff(iface="eth0", filter="udp port 67 or udp port 68", prn=decode_dhcp, store=False)Running this on a mirror port reveals every DHCP exchange on the segment. Two OFFER messages from different server IDs for the same DISCOVER indicate a rogue DHCP server.
DHCP relay enables multi-subnet DHCP
Without a relay agent, DHCP broadcasts do not cross router boundaries. Configure relay agents on every subnet that uses DHCP.
DORA is the four-step handshake
Discover, Offer, Request, Acknowledge. The transaction ID (xid) matches requests to responses.
Static IPs for critical OT devices
Use DHCP for HMIs and workstations. Assign static addresses to PLCs, drives, and safety controllers.
DHCP assigns addresses, but devices still need to translate hostnames into those addresses. The next page covers DNS, the distributed database that maps names to IPs.