git: 495826f69d96 - main - stand: add ARCH and MSZ into DHCP requests
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 04 Aug 2026 02:29:19 UTC
The branch main has been updated by mav:
URL: https://cgit.FreeBSD.org/src/commit/?id=495826f69d96857bf0559516502e058ecab4ee4d
commit 495826f69d96857bf0559516502e058ecab4ee4d
Author: Alexander Motin <mav@FreeBSD.org>
AuthorDate: 2026-08-04 02:23:55 +0000
Commit: Alexander Motin <mav@FreeBSD.org>
CommitDate: 2026-08-04 02:23:55 +0000
stand: add ARCH and MSZ into DHCP requests
It should give DHCP servers more information for proper responses.
---
stand/efi/libefi/efinet.c | 18 ++++++++++++
stand/i386/libi386/pxe.c | 3 ++
stand/libsa/bootp.c | 75 ++++++++++++++++++++++++++++++++++++-----------
stand/libsa/bootp.h | 7 +++++
4 files changed, 86 insertions(+), 17 deletions(-)
diff --git a/stand/efi/libefi/efinet.c b/stand/efi/libefi/efinet.c
index e91b5f73ddf2..26a84e0e8d8e 100644
--- a/stand/efi/libefi/efinet.c
+++ b/stand/efi/libefi/efinet.c
@@ -570,6 +570,24 @@ efinet_dev_init(void)
int err, i, nifs;
extern struct devsw netdev;
+ /*
+ * Advertise our DHCP Client-System Architecture (RFC 4578). Many
+ * x86_64 UEFI firmwares report themselves as "EFI BC" (0x0007)
+ * rather than "EFI x86-64" (0x0009); match that convention for
+ * best DHCP-server config compatibility.
+ */
+#if defined(__amd64__)
+ bootp_client_arch = 0x0007; /* EFI BC */
+#elif defined(__i386__)
+ bootp_client_arch = 0x0006; /* EFI IA32 */
+#elif defined(__aarch64__)
+ bootp_client_arch = 0x000b; /* EFI 64-bit ARM */
+#elif defined(__arm__)
+ bootp_client_arch = 0x000a; /* EFI 32-bit ARM */
+#elif defined(__riscv)
+ bootp_client_arch = 0x001b; /* EFI 64-bit RISC-V */
+#endif
+
sz = 0;
handles = NULL;
status = BS->LocateHandle(ByProtocol, &sn_guid, NULL, &sz, NULL);
diff --git a/stand/i386/libi386/pxe.c b/stand/i386/libi386/pxe.c
index e0752eadccc2..dc65230acdc9 100644
--- a/stand/i386/libi386/pxe.c
+++ b/stand/i386/libi386/pxe.c
@@ -149,6 +149,9 @@ pxe_init(void)
if (pxenv_p == NULL)
return (0);
+ /* RFC 4578 § 2.1: BIOS PXE is a 32-bit "Standard PC BIOS" client. */
+ bootp_client_arch = 0x0000;
+
/* look for "PXENV+" */
if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) {
pxenv_p = NULL;
diff --git a/stand/libsa/bootp.c b/stand/libsa/bootp.c
index 927db86abdd8..6008f0bf8f11 100644
--- a/stand/libsa/bootp.c
+++ b/stand/libsa/bootp.c
@@ -87,33 +87,74 @@ struct in_addr dhcp_serverip;
struct bootp *bootp_response;
size_t bootp_response_size;
+/*
+ * DHCP Client-System Architecture per RFC 4578 (option 93). Each netif
+ * driver that has a spec-defined value for what it is sets this from
+ * its own init path (e.g. efinet_dev_init on UEFI, pxe_init on legacy
+ * BIOS PXE). BOOTP_ARCH_UNSET means the loader has no honest value to
+ * report (e.g. U-Boot or OpenFirmware, for which RFC 4578 has no
+ * assignment), and the option is omitted from the DHCP request rather
+ * than misrepresenting the client.
+ */
+#define BOOTP_ARCH_UNSET 0xFFFF
+uint16_t bootp_client_arch = BOOTP_ARCH_UNSET;
+
static void
bootp_fill_request(unsigned char *bp_vend)
{
+ int off = 0;
+
/*
* We are booting from PXE, we want to send the string
* 'PXEClient' to the DHCP server so you have the option of
* only responding to PXE aware dhcp requests.
*/
- bp_vend[0] = TAG_CLASSID;
- bp_vend[1] = 9;
- bcopy("PXEClient", &bp_vend[2], 9);
- bp_vend[11] = TAG_USER_CLASS;
+ bp_vend[off++] = TAG_CLASSID;
+ bp_vend[off++] = 9;
+ bcopy("PXEClient", &bp_vend[off], 9);
+ off += 9;
+
+ bp_vend[off++] = TAG_USER_CLASS;
/* len of each user class + number of user class */
- bp_vend[12] = 8;
+ bp_vend[off++] = 8;
/* len of the first user class */
- bp_vend[13] = 7;
- bcopy("FreeBSD", &bp_vend[14], 7);
- bp_vend[21] = TAG_PARAM_REQ;
- bp_vend[22] = 7;
- bp_vend[23] = TAG_ROOTPATH;
- bp_vend[24] = TAG_HOSTNAME;
- bp_vend[25] = TAG_SWAPSERVER;
- bp_vend[26] = TAG_GATEWAY;
- bp_vend[27] = TAG_SUBNET_MASK;
- bp_vend[28] = TAG_INTF_MTU;
- bp_vend[29] = TAG_SERVERID;
- bp_vend[30] = TAG_END;
+ bp_vend[off++] = 7;
+ bcopy("FreeBSD", &bp_vend[off], 7);
+ off += 7;
+
+ /*
+ * Client architecture (RFC 4578). Value is 2 bytes big-endian.
+ * Omit entirely for loaders that don't have a spec-defined value.
+ */
+ if (bootp_client_arch != BOOTP_ARCH_UNSET) {
+ bp_vend[off++] = TAG_CLIENT_ARCH;
+ bp_vend[off++] = 2;
+ bp_vend[off++] = (bootp_client_arch >> 8) & 0xff;
+ bp_vend[off++] = bootp_client_arch & 0xff;
+ }
+
+ /*
+ * Maximum DHCP message size we can accept (RFC 2132). The loader's
+ * UDP read path allocates dynamically per packet up to the interface
+ * MTU, so advertise ~MTU to unlock large option payloads that some
+ * servers would otherwise trim to the RFC 2131 § 4.4.1 576-byte
+ * default.
+ */
+ bp_vend[off++] = TAG_MAXSIZE;
+ bp_vend[off++] = 2;
+ bp_vend[off++] = (1472 >> 8) & 0xff;
+ bp_vend[off++] = 1472 & 0xff;
+
+ bp_vend[off++] = TAG_PARAM_REQ;
+ bp_vend[off++] = 7;
+ bp_vend[off++] = TAG_ROOTPATH;
+ bp_vend[off++] = TAG_HOSTNAME;
+ bp_vend[off++] = TAG_SWAPSERVER;
+ bp_vend[off++] = TAG_GATEWAY;
+ bp_vend[off++] = TAG_SUBNET_MASK;
+ bp_vend[off++] = TAG_INTF_MTU;
+ bp_vend[off++] = TAG_SERVERID;
+ bp_vend[off] = TAG_END;
}
/* Fetch required bootp infomation */
diff --git a/stand/libsa/bootp.h b/stand/libsa/bootp.h
index 57601e025bf5..577449a79292 100644
--- a/stand/libsa/bootp.h
+++ b/stand/libsa/bootp.h
@@ -107,6 +107,7 @@ struct bootp {
#define TAG_CLASSID ((unsigned char) 60)
#define TAG_CLIENTID ((unsigned char) 61)
#define TAG_USER_CLASS ((unsigned char) 77)
+#define TAG_CLIENT_ARCH ((unsigned char) 93) /* RFC 4578 */
#endif
#define TAG_END ((unsigned char) 255)
@@ -144,4 +145,10 @@ struct cmu_vend {
extern struct bootp *bootp_response;
extern size_t bootp_response_size;
+/*
+ * DHCP option 93 (Client-System Architecture, RFC 4578). Netif drivers
+ * with RFC 4578 defined identity set this so bootp() emits the option.
+ */
+extern uint16_t bootp_client_arch;
+
#endif /* _BOOTP_H_ */