[PATCH] udhcpd: Serve BOOTP clients

Adam Goldman adamg at pobox.com
Mon May 29 01:29:52 UTC 2023


This patch makes udhcpd respond correctly to queries from BOOTP clients.

It contains the following changes:

The end field, or DHCP_END option, is required in DHCP requests but 
optional in BOOTP requests. Only complain about missing end fields if 
there are DHCP options in the packet. However, we still send an end 
field in all replies, because some BOOTP clients expect one in replies 
even if they didn't send one in the request.

Requests without a DHCP_MESSAGE_TYPE are recognized as BOOTP requests 
and handled appropriately, instead of being discarded. We still require 
an RFC 1048 options field, but we allow it to be empty.

Since a BOOTP client will keep using the assigned IP forever, we only 
send a BOOTP reply if a static lease exists for that client.

BOOTP replies shouldn't contain DHCP_* options, so we omit them if there 
was no DHCP_MESSAGE_TYPE in the request. Options other than DHCP_* 
options are still sent.

The options field of a BOOTP reply must be exactly 64 bytes. If we 
construct a reply with more than 64 bytes of options, we give up and log 
an error instead of sending it. udhcp_send_raw_packet already pads the 
options field to 64 bytes if it is too short.

This implementation has been tested against an HP PA-RISC client.


--- networking/udhcp/common.c.orig	2023-05-22 18:41:39.000000000 -0700
+++ networking/udhcp/common.c	2023-05-21 19:18:15.000000000 -0700
@@ -234,6 +234,7 @@
 void FAST_FUNC init_scan_state(struct dhcp_packet *packet, struct dhcp_scan_state *scan_state)
 {
 	scan_state->overload = 0;
+	scan_state->is_dhcp = 0;
 	scan_state->rem = sizeof(packet->options);
 	scan_state->optionptr = packet->options;
 }
@@ -251,12 +252,17 @@
 
 	/* option bytes: [code][len][data1][data2]..[dataLEN] */
 	while (1) {
+		if (scan_state->rem == 0 && !scan_state->is_dhcp)
+			break; /* BOOTP packet without end field */
 		if (scan_state->rem <= 0) {
  complain:
 			bb_simple_error_msg("bad packet, malformed option field");
 			return NULL;
 		}
 
+		if (scan_state->optionptr[OPT_CODE] >= DHCP_REQUESTED_IP && scan_state->optionptr[OPT_CODE] <= DHCP_CLIENT_ID)
+			scan_state->is_dhcp = 1;
+
 		/* DHCP_PADDING and DHCP_END have no [len] byte */
 		if (scan_state->optionptr[OPT_CODE] == DHCP_PADDING) {
 			scan_state->rem--;
--- networking/udhcp/common.h.orig	2023-01-03 06:17:01.000000000 -0800
+++ networking/udhcp/common.h	2023-05-21 19:01:24.000000000 -0700
@@ -123,6 +123,7 @@
 
 struct dhcp_scan_state {
 	int overload;
+	int is_dhcp;
 	int rem;
 	uint8_t *optionptr;
 };
--- networking/udhcp/packet.c.orig	2023-01-03 06:17:01.000000000 -0800
+++ networking/udhcp/packet.c	2023-05-23 00:22:45.000000000 -0700
@@ -18,6 +18,7 @@
 	memset(packet, 0, sizeof(*packet));
 	packet->op = BOOTREQUEST; /* if client to a server */
 	switch (type) {
+	case 0:
 	case DHCPOFFER:
 	case DHCPACK:
 	case DHCPNAK:
@@ -28,7 +29,8 @@
 	packet->cookie = htonl(DHCP_MAGIC);
 	if (DHCP_END != 0)
 		packet->options[0] = DHCP_END;
-	udhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
+	if (type)
+		udhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
 }
 #endif
 
--- networking/udhcp/dhcpd.c.orig	2023-01-03 06:17:01.000000000 -0800
+++ networking/udhcp/dhcpd.c	2023-05-28 17:08:51.000000000 -0700
@@ -649,7 +649,8 @@
 	packet->flags = oldpacket->flags;
 	packet->gateway_nip = oldpacket->gateway_nip;
 	packet->ciaddr = oldpacket->ciaddr;
-	udhcp_add_simple_option(packet, DHCP_SERVER_ID, server_data.server_nip);
+	if(type)
+		udhcp_add_simple_option(packet, DHCP_SERVER_ID, server_data.server_nip);
 }
 
 /* Fill options field, siaddr_nip, and sname and boot_file fields.
@@ -733,8 +734,11 @@
 {
 	struct dhcp_packet packet;
 	uint32_t lease_time_sec;
+	char message_type = DHCPOFFER;
 
-	init_packet(&packet, oldpacket, DHCPOFFER);
+	if (!udhcp_get_option(oldpacket, DHCP_MESSAGE_TYPE))
+		message_type = 0;
+	init_packet(&packet, oldpacket, message_type);
 
 	/* If it is a static lease, use its IP */
 	packet.yiaddr = static_lease_nip;
@@ -785,8 +789,13 @@
 	}
 
 	lease_time_sec = select_lease_time(oldpacket);
-	udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
+	if (udhcp_get_option(oldpacket, DHCP_MESSAGE_TYPE))
+		udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
 	add_server_options(&packet);
+	if (!udhcp_get_option(oldpacket, DHCP_MESSAGE_TYPE) && udhcp_end_option(packet.options) > 63) {
+		bb_simple_error_msg("BOOTP BOOTREPLY would be too large, not sending");
+		return;
+	}
 
 	/* send_packet emits error message itself if it detects failure */
 	send_packet_verbose(&packet, "sending OFFER to %s");
@@ -1050,8 +1059,8 @@
 			continue;
 		}
 		msg_type = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
-		if (!msg_type || msg_type[0] < DHCP_MINTYPE || msg_type[0] > DHCP_MAXTYPE) {
-			bb_info_msg("no or bad message type option%s", ", ignoring packet");
+		if (msg_type && (msg_type[0] < DHCP_MINTYPE || msg_type[0] > DHCP_MAXTYPE)) {
+			bb_info_msg("bad message type option%s", ", ignoring packet");
 			continue;
 		}
 
@@ -1086,6 +1095,17 @@
 			move_from_unaligned32(requested_nip, requested_ip_opt);
 		}
 
+		/* Handle BOOTP clients */
+		if (!msg_type) {
+			log1("received %s", "BOOTP BOOTREQUEST");
+			if (!static_lease_nip) {
+				log1("no static lease for BOOTP client%s", ", ignoring packet");
+				continue;
+			}
+			send_offer(&packet, static_lease_nip, lease, requested_nip, arpping_ms);
+			continue;
+		}
+
 		switch (msg_type[0]) {
 
 		case DHCPDISCOVER:



More information about the busybox mailing list