DHCP Relay, Part 1: One Relay, One Server

This is part one of the DHCP relay mini-series:

In VLAN 60 there is a DHCP (Dynamic Host Configuration Protocol) client that will try to get an IP address from a DHCP server. There is no DHCP server in VLAN 60, but there is a DHCP relay configured on the router, and it will forward the DHCP requests from the clients to the central DHCP server that is located in VLAN 41. We will see how the DHCP relay works and how the DHCP packets look like when DHCP relay is used.

I have set up tcpdump on both the DHCP client and the DHCP server to capture the DHCP packets so that we can see what is actually contained in the packets during the DHCP request processing.

In this demonstration (and in the two following posts as well) the following three steps are shown:

  1. First DHCP client will request a new IP address
  2. Then DHCP client will renew its IP address lease
  3. Finally DHCP client will release the IP address lease

The results are inspected in three ways: how it looks in the DHCP client log, how it looks in the DHCP server log, and how it looks in the actual IP packets.

I’m using ISC DHCP client and ISC DHCP server (as packaged with Debian Linux), and Cisco CSR1000v as the router with DHCP relay.

On the DHCP client

On the DHCP client host (called “dhcptestclient”) I’ll enter “sudo dhclient ens192” command, and this is what is shown in /var/log/syslog:

Jun  7 20:35:39 dhcptestclient dhclient[5600]: DHCPDISCOVER on ens192 to 255.255.255.255 port 67 interval 3
Jun  7 20:35:39 dhcptestclient dhclient[5600]: DHCPOFFER of 192.168.60.100 from 192.168.60.1
Jun  7 20:35:39 dhcptestclient dhclient[5600]: DHCPREQUEST for 192.168.60.100 on ens192 to 255.255.255.255 port 67
Jun  7 20:35:39 dhcptestclient dhclient[5600]: DHCPACK of 192.168.60.100 from 192.168.60.1
Jun  7 20:35:39 dhcptestclient dhclient[5600]: bound to 192.168.60.100 -- renewal in 57 seconds.

This looks good: DHCPDISCOVER was sent to the broadcast address (255.255.255.255), DHCPOFFER was immediately received, DHCPREQUEST was issued to get the offered IP address (192.168.60.100), and finally DHCPACK was received to confirm the IP configuration.

The sender of the DHCPOFFER and DHCPACK packets was apparently 192.168.60.1, which is the IP address of the router interface in the network segment where the DHCP client resides. That is the DHCP relay agent address in this example.

The DHCP server has lease time configured as 120 seconds. The usual behaviour for the DHCP client is to start renewing the lease at half the lease time (with some variance), and this time the client chose 57 seconds.

Just to be sure: Using lease time this small (two minutes) is highly exceptional and is here used just for the demonstration purposes to get the renewals captured as well.

After 57 seconds was elapsed the DHCP client issued a renew request:

Jun  7 20:36:36 dhcptestclient dhclient[5600]: DHCPREQUEST for 192.168.60.100 on ens192 to 10.0.41.10 port 67
Jun  7 20:36:36 dhcptestclient dhclient[5600]: DHCPACK of 192.168.60.100 from 10.0.41.10
Jun  7 20:36:36 dhcptestclient dhclient[5600]: bound to 192.168.60.100 -- renewal in 50 seconds.

This looks quite similar to the previous DHCPREQUEST + DHCPACK, but there is an important difference: This time the request was sent as unicast to the DHCP server (10.0.41.10) instead of using broadcast address (255.255.255.255). Note that the DHCP relay agent (the router) was not involved in this renewal process at all. The DHCP client got the DHCP server IP address inside the original DHCP transaction.

Finally, let’s show what happens when the DHCP lease is released by the client (sudo dhclient ens192 -r):

Jun  7 20:36:49 dhcptestclient dhclient[5628]: DHCPRELEASE of 192.168.60.100 on ens192 to 10.0.41.10 port 67

The DHCPRELEASE message was sent directly to the DHCP server 10.0.41.10 that originally issued the lease.

On the DHCP server

Let’s see the DHCP server log (/var/log/syslog) as well, first when the DHCP client requested an address:

2023-06-07T20:35:39.533723+03:00 dhcp-server dhcpd[20998]: DHCPDISCOVER from 00:0c:29:67:42:7a via 192.168.60.1
2023-06-07T20:35:39.533873+03:00 dhcp-server dhcpd[20998]: DHCPOFFER on 192.168.60.100 to 00:0c:29:67:42:7a (dhcptestclient) via 192.168.60.1
2023-06-07T20:35:39.536814+03:00 dhcp-server dhcpd[20998]: DHCPREQUEST for 192.168.60.100 (10.0.41.10) from 00:0c:29:67:42:7a (dhcptestclient) via 192.168.60.1
2023-06-07T20:35:39.537726+03:00 dhcp-server dhcpd[20998]: DHCPACK on 192.168.60.100 to 00:0c:29:67:42:7a (dhcptestclient) via 192.168.60.1

In the DHCPDISCOVER message we see that the DHCP relay was involved: “via 192.168.60.1”.

DHCPOFFER was then sent back to the DHCP relay. The DHCP client MAC address is included in the offer, so the DHCP relay knows where to forward the offer in the connected network segment.

In the DHCPREQUEST packet there is the offered IP address (192.168.60.100) but also the DHCP server IP address (10.0.41.10). This is important because at this point the original DHCP client packet was still sent as broadcast, so having the DHCP server IP address in the DHCPREQUEST message ensures that the request is eventually targeted to the correct DHCP server (in case there would be many servers responding to the DHCP queries).

Finally DHCPACK was sent, again via the DHCP relay.

After 57 seconds the DHCP lease was renewed by the client:

2023-06-07T20:36:36.907740+03:00 dhcp-server dhcpd[20998]: DHCPREQUEST for 192.168.60.100 from 00:0c:29:67:42:7a (dhcptestclient) via ens224
2023-06-07T20:36:36.909098+03:00 dhcp-server dhcpd[20998]: DHCPACK on 192.168.60.100 to 00:0c:29:67:42:7a (dhcptestclient) via ens224

The DHCP client sent the DHCPREQUEST message as a unicast packet, and it is shown as “via ens224”, which means that it was received on that network interface.

The DHCP server responded directly to the client with DHCPACK.

And finally the unicast DHCPRELEASE message was seen as well:

2023-06-07T20:36:49.592903+03:00 dhcp-server dhcpd[20998]: DHCPRELEASE of 192.168.60.100 from 00:0c:29:67:42:7a (dhcptestclient) via ens224 (found)

In the IP packets

The packets captured at the DHCP client:

Packets 1-4 are from the initial IP address request, packets 5 and 6 are about the renewal, and the last packet is the release of the IP address.

Note how the DHCP server identifier is included in most of the packets after the IP address was initially leased to the client.

Also note how the DHCP relay is responding in the initial IP address request (in packets 2 and 4) instead of the DHCP server. The renewal and release are then communicated directly with the DHCP server.

The packets captured at the DHCP server:

This looks straigthforward: packets 1-4 are the DORA (Discover – Offer – Request – Acknowledgement) packets, then the DHCP client requested lease renewal in packet 5, server responded to it in packet 6, and finally the client released the lease in the last packet.

In the packets above, note the usage of UDP ports:

  • DHCP client always sends the packets to UDP destination port 67 (bootps, BOOTP server) and sources them from UDP source port 68 (bootpc, BOOTP client)
  • DHCP relay also always sends the packets to UDP destination port 67 (bootps) but sources them from UDP source port 67 (bootps)
  • DHCP server responds with whatever ports were used in the request (reversed as usual: original packet’s destination port is used as source port when responding, and vice versa)

In the RFCs below I didn’t find a requirement that DHCP client must use UDP source port 68, and I already found an example of a DHCP client using some random high port as source port. But the relay must send the responses to the DHCP client to UDP port 68 in any case.

The capture files can be downloaded here if you want to see the details of the captured packets:

See the next post for a demo with two DHCP servers in a load balancing configuration.

More information:

Updated: April 7, 2024 — 09:41

Leave a Reply