[PATCH] udhcpd: Per-client boot_file

Adam Goldman adamg at pobox.com
Sat Jun 24 23:23:14 UTC 2023


This patch adds the ability to specify a different boot_file for each 
client. This is useful if clients have different CPU architectures or if 
some clients are legacy BIOS and some are EFI.

It adds a config file option of the form 
"for xx:xx:xx:xx:xx:xx boot_file foo" which sets the boot_file to "foo" 
for the client with the MAC address xx:xx:xx:xx:xx:xx. If no such line 
exists, the global boot_file will be used. The syntax of the option is 
intended so that in the future, per-client options other than boot_file 
could be added.


--- networking/udhcp/dhcpd.h.orig	2023-01-03 06:17:01.000000000 -0800
+++ networking/udhcp/dhcpd.h	2023-04-16 00:56:09.000000000 -0700
@@ -16,6 +16,7 @@
 
 
 struct static_lease;
+struct boot_file_override;
 
 struct server_data_t {
 	char *interface;                /* interface to use */
@@ -51,6 +52,7 @@
 	char *sname;                    /* bootp server name */
 	char *boot_file;                /* bootp boot file option */
 	struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */
+	struct boot_file_override *boot_file_overrides; /* Boot files for specific clients */
 } FIX_ALIASING;
 
 #define server_data (*(struct server_data_t*)bb_common_bufsiz1)
--- networking/udhcp/dhcpd.c.orig	2023-01-03 06:17:01.000000000 -0800
+++ networking/udhcp/dhcpd.c	2023-05-28 17:13:51.000000000 -0700
@@ -63,6 +63,12 @@
 	uint8_t opt[1];
 };
 
+struct boot_file_override {
+	struct boot_file_override *next;
+	char *boot_file;
+	uint8_t mac[6];
+};
+
 /* Takes the address of the pointer to the static_leases linked list,
  * address to a 6 byte mac address,
  * 4 byte IP address */
@@ -105,6 +111,33 @@
 #endif
 }
 
+static void add_boot_file_override(struct boot_file_override **bfo_pp,
+		uint8_t *mac,
+		const char *boot_file)
+{
+	struct boot_file_override *bfo;
+
+	/* Find the tail of the list */
+	while ((bfo = *bfo_pp) != NULL) {
+		bfo_pp = &bfo->next;
+	}
+
+	/* Add new node */
+	*bfo_pp = bfo = xzalloc(sizeof(*bfo));
+	memcpy(bfo->mac, mac, 6);
+	bfo->boot_file = strdup(boot_file);
+
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
+	if (dhcp_verbose >= 2) {
+		bb_info_msg("boot file override: mac:%02x:%02x:%02x:%02x:%02x:%02x boot_file:%s",
+			bfo->mac[0], bfo->mac[1], bfo->mac[2],
+			bfo->mac[3], bfo->mac[4], bfo->mac[5],
+			bfo->boot_file
+		);
+	}
+#endif
+}
+
 /* Find static lease IP by mac */
 static uint32_t get_static_nip_by_mac(void *mac)
 {
@@ -380,6 +413,33 @@
 	return 1;
 }
 
+static int FAST_FUNC read_bfo(const char *const_line, void *arg)
+{
+	char *line;
+	char *mac_string;
+	char *specifier;
+	struct ether_addr mac_bytes;
+	char *boot_file;
+
+	/* Read mac */
+	line = (char *) const_line;
+	mac_string = strtok_r(line, " \t", &line);
+	if (!mac_string || !ether_aton_r(mac_string, &mac_bytes))
+		return 0;
+
+	specifier = strtok_r(NULL, " \t", &line);
+	if (!specifier || strcasecmp(specifier, "boot_file")!=0)
+		return 0;
+
+	boot_file = strtok_r(NULL, " \t", &line);
+	if (!boot_file)
+		return 0;
+
+	add_boot_file_override(arg, (uint8_t*) &mac_bytes, boot_file);
+
+	return 1;
+}
+
 static int FAST_FUNC read_optset(const char *line, void *arg)
 {
 	return udhcp_str2optset(line, arg,
@@ -420,6 +480,7 @@
 	{"sname"        , read_str        , OFS(sname        ), NULL},
 	{"boot_file"    , read_str        , OFS(boot_file    ), NULL},
 	{"static_lease" , read_staticlease, OFS(static_leases), ""},
+	{"for"          , read_bfo        , OFS(boot_file_overrides), ""},
 };
 enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
 
@@ -659,6 +720,7 @@
 {
 	struct option_set *config_opts;
 	uint8_t *client_hostname_opt;
+	char *boot_file;
 
 	client_hostname_opt = NULL;
 	if (packet->yiaddr) { /* if we aren't from send_inform()... */
@@ -704,8 +766,21 @@
 
 	if (server_data.sname)
 		strncpy((char*)packet->sname, server_data.sname, sizeof(packet->sname) - 1);
-	if (server_data.boot_file)
-		strncpy((char*)packet->file, server_data.boot_file, sizeof(packet->file) - 1);
+
+	boot_file = server_data.boot_file;
+	if (packet->yiaddr) { /* if we aren't from send_inform()... */
+		struct boot_file_override *bfo = server_data.boot_file_overrides;
+		while (bfo) {
+			if (memcmp(bfo->mac, packet->chaddr, 6) == 0) {
+				boot_file = bfo->boot_file;
+				break;
+			}
+			bfo = bfo->next;
+		}
+	}
+
+	if (boot_file)
+		strncpy((char*)packet->file, boot_file, sizeof(packet->file) - 1);
 }
 
 static uint32_t select_lease_time(struct dhcp_packet *packet)
--- examples/udhcp/udhcpd.conf.orig	2023-05-28 17:17:10.000000000 -0700
+++ examples/udhcp/udhcpd.conf	2023-05-28 17:30:19.000000000 -0700
@@ -61,6 +61,11 @@
 #static_lease 00:60:08:11:CE:4E 192.168.0.54
 #static_lease 00:60:08:11:CE:3E 192.168.0.44 optional_hostname
 
+# The boot_file can be specified on a client-by-client basis. If there is
+# no matching "for" line for a client, the global boot_file will be used.
+#for 12:34:56:78:90:00 boot_file syslinux32.efi
+#for 98:76:54:32:10:00 boot_file syslinux64.efi
+
 # The remainder of options are DHCP options and can be specified with the
 # keyword 'opt' or 'option'. If an option can take multiple items, such
 # as the dns option, they can be listed on the same line, or multiple



More information about the busybox mailing list