git: 495826f69d96 - main - stand: add ARCH and MSZ into DHCP requests

Alexander Motin <[email protected]> Tue, 04 Aug 2026 02:29:19 +0000
Newsgroups gmane.os.freebsd.devel.cvs.src,gmane.os.freebsd.current.scm
Message-ID <[email protected]>
The branch main has been updated by mav:

URL: https://cgit.FreeBSD.org/src/commit/?id=495826f69d96857bf0559516502e058ecab4ee4d

commit 495826f69d96857bf0559516502e058ecab4ee4d
Author:     Alexander Motin <[email protected]>
AuthorDate: 2026-08-04 02:23:55 +0000
Commit:     Alexander Motin <[email protected]>
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_ */