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: _______________________________________________ busybox mailing list busybox@busybox.net http://lists.busybox.net/mailman/listinfo/busybox