Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.busybox.net/busybox.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2023-06-12 14:12:23 +0300
committerDenys Vlasenko <vda.linux@googlemail.com>2023-06-12 14:22:47 +0300
commit8f0e4c42c630126daafd834175b86d2b28f5b798 (patch)
treed75f761222980bf52a954be41cb7a05b3d4b71ff
parent4bd70463c7e7511aea9de58876f0beacbcec1779 (diff)
udhcpd: optional BOOTP support
from Adam Goldman <adamg@pobox.com> 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. 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. function old new delta .rodata 105247 105321 +74 udhcpd_main 1520 1591 +71 send_offer 419 470 +51 init_packet 81 97 +16 udhcp_init_header 75 88 +13 udhcp_scan_options 192 203 +11 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 6/0 up/down: 236/0) Total: 236 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--networking/udhcp/Config.src7
-rw-r--r--networking/udhcp/common.c10
-rw-r--r--networking/udhcp/common.h14
-rw-r--r--networking/udhcp/dhcpc.c2
-rw-r--r--networking/udhcp/dhcpd.c49
-rw-r--r--networking/udhcp/packet.c9
6 files changed, 76 insertions, 15 deletions
diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src
index 7ba7f48fc..d9c501c18 100644
--- a/networking/udhcp/Config.src
+++ b/networking/udhcp/Config.src
@@ -10,6 +10,13 @@ config UDHCPD
udhcpd is a DHCP server geared primarily toward embedded systems,
while striving to be fully functional and RFC compliant.
+config FEATURE_UDHCPD_BOOTP
+ bool "Answer to BOOTP requests as well"
+ default y
+ depends on UDHCPD
+ help
+ Support old BOOTP protocol too.
+
config FEATURE_UDHCPD_BASE_IP_ON_MAC
bool "Select IP address based on client MAC"
default n
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index ae818db05..ad580f38d 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -252,6 +252,14 @@ uint8_t* FAST_FUNC udhcp_scan_options(struct dhcp_packet *packet, struct dhcp_sc
/* option bytes: [code][len][data1][data2]..[dataLEN] */
while (1) {
if (scan_state->rem <= 0) {
+ if (ENABLE_FEATURE_UDHCPD_BOOTP && scan_state->rem == 0) {
+ /* DHCP requires END option to be present.
+ * We are here if packet fails this condition
+ * (options[] are zero-padded to the end).
+ * Assume BOOTP packet without further checks.
+ */
+ break; /* return NULL */
+ }
complain:
bb_simple_error_msg("bad packet, malformed option field");
return NULL;
@@ -278,7 +286,7 @@ uint8_t* FAST_FUNC udhcp_scan_options(struct dhcp_packet *packet, struct dhcp_sc
scan_state->rem = sizeof(packet->sname);
continue;
}
- break;
+ break; /* return NULL */
}
if (scan_state->rem <= OPT_LEN) /* [len] byte exists? */
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index 49a0b593d..3ef371a7c 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -20,8 +20,11 @@ extern const uint8_t MAC_BCAST_ADDR[6] ALIGN2; /* six all-ones */
/*** DHCP packet ***/
+#define RFC1048_MAGIC 0x63825363
+/* RFC 1048 still uses BOOTP's small buffer (4 byte cookie + 60 the rest) */
+#define RFC1048_OPTIONS_BUFSIZE 60
+
/* DHCP protocol. See RFC 2131 */
-#define DHCP_MAGIC 0x63825363
#define DHCP_OPTIONS_BUFSIZE 308
#define BOOTREQUEST 1
#define BOOTREPLY 2
@@ -57,8 +60,10 @@ struct dhcp_packet {
* such as 'unix' or 'gateway'; this means 'boot the named program
* configured for my machine'"
*/
- /* BOOTP fields end here, BOOTP says optional uint8_t vend[64] follows */
- uint32_t cookie; /* DHCP magic bytes: 99,130,83,99 decimal */
+ /* BOOTP fields end here, BOOTP says optional uint8_t vend[64] follows. */
+ /* RFC 1048 defined this cookie value and options 0-12 and 255. */
+ /* DHCP extended it and required option 255 (END) to be always present. */
+ uint32_t cookie; /* RFC 1048 magic bytes: 99,130,83,99 decimal */
uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS];
};
#define DHCP_PKT_SNAME_LEN 64
@@ -200,6 +205,9 @@ struct dhcp_scan_state {
#define SNAME_FIELD 2
/* DHCP_MESSAGE_TYPE values */
+#if ENABLE_FEATURE_UDHCPD_BOOTP
+#define MSGTYPE_BOOTP 0 /* there was no TYPE option in client's packet, assuming BOOTP */
+#endif
#define DHCPDISCOVER 1 /* client -> server */
#define DHCPOFFER 2 /* client <- server */
#define DHCPREQUEST 3 /* client -> server */
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index c757fb37c..200a2fb8a 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -971,7 +971,7 @@ static NOINLINE int d4_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
}
skip_udp_sum_check:
- if (packet.data.cookie != htonl(DHCP_MAGIC)) {
+ if (packet.data.cookie != htonl(RFC1048_MAGIC)) {
log1s("packet with bad magic, ignoring");
return -2;
}
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index 66750e2e6..2904119e5 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -649,7 +649,8 @@ static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacke
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_FEATURE_UDHCPD_BOOTP(if (type != MSGTYPE_BOOTP))
+ udhcp_add_simple_option(packet, DHCP_SERVER_ID, server_data.server_nip);
}
/* Fill options field, siaddr_nip, and sname and boot_file fields.
@@ -725,7 +726,12 @@ static uint32_t select_lease_time(struct dhcp_packet *packet)
/* We got a DHCP DISCOVER. Send an OFFER. */
/* NOINLINE: limit stack usage in caller */
-static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
+#if !ENABLE_FEATURE_UDHCPD_BOOTP
+#define send_offer(is_dhcp_client, ...) \
+ send_offer(__VA_ARGS__)
+#endif
+static NOINLINE void send_offer(void *is_dhcp_client,
+ struct dhcp_packet *oldpacket,
uint32_t static_lease_nip,
struct dyn_lease *lease,
uint32_t requested_nip,
@@ -734,7 +740,12 @@ static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
struct dhcp_packet packet;
uint32_t lease_time_sec;
+#if ENABLE_FEATURE_UDHCPD_BOOTP
+ init_packet(&packet, oldpacket, is_dhcp_client ? DHCPOFFER : MSGTYPE_BOOTP);
+#else
+ enum { is_dhcp_client = 1 };
init_packet(&packet, oldpacket, DHCPOFFER);
+#endif
/* If it is a static lease, use its IP */
packet.yiaddr = static_lease_nip;
@@ -784,9 +795,16 @@ static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
}
}
- lease_time_sec = select_lease_time(oldpacket);
- udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
+ if (is_dhcp_client) {
+ lease_time_sec = select_lease_time(oldpacket);
+ udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
+ }
+/* TODO: pass "is_dhcp_client" to add_server_options(), avoid adding confusing options to BOOTP clients? */
add_server_options(&packet);
+ if (!is_dhcp_client && udhcp_end_option(packet.options) >= RFC1048_OPTIONS_BUFSIZE) {
+ bb_simple_error_msg("BOOTP reply 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 +1068,12 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
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 (
+ IF_FEATURE_UDHCPD_BOOTP( msg_type && )
+ IF_NOT_FEATURE_UDHCPD_BOOTP( !msg_type || )
+ (msg_type[0] < DHCP_MINTYPE || msg_type[0] > DHCP_MAXTYPE)
+ ) {
+ bb_info_msg("bad message type option%s", ", ignoring packet");
continue;
}
@@ -1086,12 +1108,25 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
move_from_unaligned32(requested_nip, requested_ip_opt);
}
+#if ENABLE_FEATURE_UDHCPD_BOOTP
+ /* Handle old BOOTP clients */
+ if (!msg_type) {
+ log1("received %s", "BOOTP BOOTREQUEST");
+ if (!static_lease_nip) {
+ bb_info_msg("no static lease for BOOTP client%s", ", ignoring packet");
+ continue;
+ }
+ send_offer(msg_type, &packet, static_lease_nip, lease, requested_nip, arpping_ms);
+ continue;
+ }
+#endif
+
switch (msg_type[0]) {
case DHCPDISCOVER:
log1("received %s", "DISCOVER");
- send_offer(&packet, static_lease_nip, lease, requested_nip, arpping_ms);
+ send_offer(msg_type, &packet, static_lease_nip, lease, requested_nip, arpping_ms);
break;
case DHCPREQUEST:
diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c
index 529978189..f9dc11d01 100644
--- a/networking/udhcp/packet.c
+++ b/networking/udhcp/packet.c
@@ -18,6 +18,8 @@ void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type)
memset(packet, 0, sizeof(*packet));
packet->op = BOOTREQUEST; /* if client to a server */
switch (type) {
+ IF_FEATURE_UDHCPD_BOOTP(case MSGTYPE_BOOTP:)
+ /* reply to a BOOTP (not DHCP) client */
case DHCPOFFER:
case DHCPACK:
case DHCPNAK:
@@ -25,10 +27,11 @@ void FAST_FUNC udhcp_init_header(struct dhcp_packet *packet, char type)
}
packet->htype = 1; /* ethernet */
packet->hlen = 6;
- packet->cookie = htonl(DHCP_MAGIC);
+ packet->cookie = htonl(RFC1048_MAGIC);
if (DHCP_END != 0)
packet->options[0] = DHCP_END;
- udhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
+ IF_FEATURE_UDHCPD_BOOTP(if (type != MSGTYPE_BOOTP))
+ udhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
}
#endif
@@ -90,7 +93,7 @@ int FAST_FUNC udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd)
}
if (bytes < offsetof(struct dhcp_packet, options)
- || packet->cookie != htonl(DHCP_MAGIC)
+ || packet->cookie != htonl(RFC1048_MAGIC)
) {
bb_simple_info_msg("packet with bad magic, ignoring");
return -2;