[git commit] udhcpd: send DHCPOFFERs as unicast (unless clients specifically asks for bcast)

Denys Vlasenko vda.linux at googlemail.com
Mon Apr 7 00:46:39 UTC 2025


commit: https://git.busybox.net/busybox/commit/?id=d2d23c848acd6f336d9b0edae613eef443a6de20
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

RFC 2131 says we should do that.
Evidently, since for so many years no one complained, sending them broadcast
works too, but finally we've got someone who wants RFC-compliand behavior.

function                                             old     new   delta
send_packet                                          141     179     +38
.rodata                                           105680  105681      +1
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 39/0)               Total: 39 bytes

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 networking/udhcp/dhcpd.c | 58 +++++++++++++++++++++++++++++++++++-------------
 1 file changed, 42 insertions(+), 16 deletions(-)

diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index 2904119e5..b9cbd6464 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -575,29 +575,51 @@ static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadc
 	const uint8_t *chaddr;
 	uint32_t ciaddr;
 
-	// Was:
+	// Logic:
 	//if (force_broadcast) { /* broadcast */ }
 	//else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
+	// ^^^ dhcp_pkt->ciaddr comes from client's request packet.
+	// We expect such clients to have an UDP socket listening on that IP.
 	//else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
 	//else { /* unicast to dhcp_pkt->yiaddr */ }
-	// But this is wrong: yiaddr is _our_ idea what client's IP is
-	// (for example, from lease file). Client may not know that,
-	// and may not have UDP socket listening on that IP!
-	// We should never unicast to dhcp_pkt->yiaddr!
-	// dhcp_pkt->ciaddr, OTOH, comes from client's request packet,
-	// and can be used.
-
-	if (force_broadcast
-	 || (dhcp_pkt->flags & htons(BROADCAST_FLAG))
-	 || dhcp_pkt->ciaddr == 0
+	// ^^^ The last case is confusing, but *should* work.
+	// It's a case where client have sent a DISCOVER
+	// and does not have a kernel UDP socket listening on the IP
+	// we are offering in yiaddr (it does not know the IP yet)!
+	// This *should* work because client *should* listen on a raw socket
+	// instead at this time (IOW: it should examine ALL IPv4 packets
+	// "by hand", not relying on kernel's UDP stack.)
+
+	chaddr = dhcp_pkt->chaddr;
+
+	if (dhcp_pkt->ciaddr == 0
+	 || force_broadcast /* sending DHCPNAK pkt? */
 	) {
-		log1s("broadcasting packet to client");
-		ciaddr = INADDR_BROADCAST;
-		chaddr = MAC_BCAST_ADDR;
+		if (dhcp_pkt->flags & htons(BROADCAST_FLAG)
+		 || force_broadcast /* sending DHCPNAK pkt? */
+		) {
+// RFC 2131:
+// If 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is
+// set, then the server broadcasts DHCPOFFER and DHCPACK messages to
+// 0xffffffff. ...
+// In all cases, when 'giaddr' is zero, the server broadcasts any DHCPNAK
+// messages to 0xffffffff.
+			ciaddr = INADDR_BROADCAST;
+			chaddr = MAC_BCAST_ADDR;
+			log1s("broadcasting packet to client");
+		} else {
+// If the broadcast bit is not set and 'giaddr' is zero and
+// 'ciaddr' is zero, then the server unicasts DHCPOFFER and DHCPACK
+// messages to the client's hardware address and 'yiaddr' address.
+			ciaddr = dhcp_pkt->yiaddr;
+			log1("unicasting packet to client %ciaddr", 'y');
+		}
 	} else {
-		log1s("unicasting packet to client ciaddr");
+// If the 'giaddr'
+// field is zero and the 'ciaddr' field is nonzero, then the server
+// unicasts DHCPOFFER and DHCPACK messages to the address in 'ciaddr'.
 		ciaddr = dhcp_pkt->ciaddr;
-		chaddr = dhcp_pkt->chaddr;
+		log1("unicasting packet to client %ciaddr", 'c');
 	}
 
 	udhcp_send_raw_packet(dhcp_pkt,
@@ -624,6 +646,10 @@ static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
 static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
 {
 	if (dhcp_pkt->gateway_nip)
+// RFC 2131:
+// If the 'giaddr' field in a DHCP message from a client is non-zero,
+// the server sends any return messages to the 'DHCP server' port on the
+// BOOTP relay agent whose address appears in 'giaddr'.
 		send_packet_to_relay(dhcp_pkt);
 	else
 		send_packet_to_client(dhcp_pkt, force_broadcast);


More information about the busybox-cvs mailing list