summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2021-07-23 12:32:04 +0200
committerMichael Brown2021-07-27 14:50:36 +0200
commite09e1142a3bd8bdb702efc92994c419a53e9933b (patch)
tree590fa32d0a9512493575ab56f91f68cd954b7a4c
parent[efi] Use zero for PCI vendor/device IDs when no applicable ID exists (diff)
downloadipxe-e09e1142a3bd8bdb702efc92994c419a53e9933b.tar.gz
ipxe-e09e1142a3bd8bdb702efc92994c419a53e9933b.tar.xz
ipxe-e09e1142a3bd8bdb702efc92994c419a53e9933b.zip
[efi] Record cached ProxyDHCPOFFER and PXEBSACK, if present
Commit cd3de55 ("[efi] Record cached DHCPACK from loaded image's device handle, if present") added the ability for a chainloaded UEFI iPXE to reuse an IPv4 address and DHCP options previously obtained by a built-in PXE stack, without needing to perform a second DHCP request. Extend this to also record the cached ProxyDHCPOFFER and PXEBSACK obtained from the EFI_PXE_BASE_CODE_PROTOCOL instance installed on the loaded image's device handle, if present. This allows a chainloaded UEFI iPXE to reuse a boot filename or other options that were provided via a ProxyDHCP or PXE boot server mechanism, rather than by standard DHCP. Tested-by: Andreas Hammarskjöld <junior@2PintSoftware.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/arch/x86/interface/pcbios/bios_cachedhcp.c3
-rw-r--r--src/core/cachedhcp.c175
-rw-r--r--src/include/ipxe/cachedhcp.h9
-rw-r--r--src/include/ipxe/dhcppkt.h2
-rw-r--r--src/interface/efi/efi_cachedhcp.c29
5 files changed, 167 insertions, 51 deletions
diff --git a/src/arch/x86/interface/pcbios/bios_cachedhcp.c b/src/arch/x86/interface/pcbios/bios_cachedhcp.c
index 3d38699f..277c40d6 100644
--- a/src/arch/x86/interface/pcbios/bios_cachedhcp.c
+++ b/src/arch/x86/interface/pcbios/bios_cachedhcp.c
@@ -59,7 +59,8 @@ static void cachedhcp_init ( void ) {
}
/* Record cached DHCPACK */
- if ( ( rc = cachedhcp_record ( phys_to_user ( cached_dhcpack_phys ),
+ if ( ( rc = cachedhcp_record ( &cached_dhcpack,
+ phys_to_user ( cached_dhcpack_phys ),
sizeof ( BOOTPLAYER_t ) ) ) != 0 ) {
DBGC ( colour, "CACHEDHCP could not record DHCPACK: %s\n",
strerror ( rc ) );
diff --git a/src/core/cachedhcp.c b/src/core/cachedhcp.c
index 0e7da4bf..2fa9b0c7 100644
--- a/src/core/cachedhcp.c
+++ b/src/core/cachedhcp.c
@@ -37,29 +37,121 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
+/** A cached DHCP packet */
+struct cached_dhcp_packet {
+ /** Settings block name */
+ const char *name;
+ /** DHCP packet (if any) */
+ struct dhcp_packet *dhcppkt;
+};
+
/** Cached DHCPACK */
-static struct dhcp_packet *cached_dhcpack;
+struct cached_dhcp_packet cached_dhcpack = {
+ .name = DHCP_SETTINGS_NAME,
+};
+
+/** Cached ProxyDHCPOFFER */
+struct cached_dhcp_packet cached_proxydhcp = {
+ .name = PROXYDHCP_SETTINGS_NAME,
+};
+
+/** Cached PXEBSACK */
+struct cached_dhcp_packet cached_pxebs = {
+ .name = PXEBS_SETTINGS_NAME,
+};
+
+/** List of cached DHCP packets */
+static struct cached_dhcp_packet *cached_packets[] = {
+ &cached_dhcpack,
+ &cached_proxydhcp,
+ &cached_pxebs,
+};
/** Colour for debug messages */
#define colour &cached_dhcpack
/**
- * Record cached DHCPACK
+ * Free cached DHCP packet
*
+ * @v cache Cached DHCP packet
+ */
+static void cachedhcp_free ( struct cached_dhcp_packet *cache ) {
+
+ dhcppkt_put ( cache->dhcppkt );
+ cache->dhcppkt = NULL;
+}
+
+/**
+ * Apply cached DHCP packet settings
+ *
+ * @v cache Cached DHCP packet
+ * @v netdev Network device, or NULL
+ * @ret rc Return status code
+ */
+static int cachedhcp_apply ( struct cached_dhcp_packet *cache,
+ struct net_device *netdev ) {
+ struct settings *settings;
+ int rc;
+
+ /* Do nothing if cache is empty */
+ if ( ! cache->dhcppkt )
+ return 0;
+
+ /* Do nothing unless cached packet's MAC address matches this
+ * network device, if specified.
+ */
+ if ( netdev ) {
+ if ( memcmp ( netdev->ll_addr, cache->dhcppkt->dhcphdr->chaddr,
+ netdev->ll_protocol->ll_addr_len ) != 0 ) {
+ DBGC ( colour, "CACHEDHCP %s does not match %s\n",
+ cache->name, netdev->name );
+ return 0;
+ }
+ DBGC ( colour, "CACHEDHCP %s is for %s\n",
+ cache->name, netdev->name );
+ }
+
+ /* Select appropriate parent settings block */
+ settings = ( netdev ? netdev_settings ( netdev ) : NULL );
+
+ /* Register settings */
+ if ( ( rc = register_settings ( &cache->dhcppkt->settings, settings,
+ cache->name ) ) != 0 ) {
+ DBGC ( colour, "CACHEDHCP %s could not register settings: %s\n",
+ cache->name, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Free cached DHCP packet */
+ cachedhcp_free ( cache );
+
+ return 0;
+}
+
+/**
+ * Record cached DHCP packet
+ *
+ * @v cache Cached DHCP packet
* @v data DHCPACK packet buffer
* @v max_len Maximum possible length
* @ret rc Return status code
*/
-int cachedhcp_record ( userptr_t data, size_t max_len ) {
+int cachedhcp_record ( struct cached_dhcp_packet *cache, userptr_t data,
+ size_t max_len ) {
struct dhcp_packet *dhcppkt;
struct dhcp_packet *tmp;
struct dhcphdr *dhcphdr;
+ unsigned int i;
size_t len;
+ /* Free any existing cached packet */
+ cachedhcp_free ( cache );
+
/* Allocate and populate DHCP packet */
dhcppkt = zalloc ( sizeof ( *dhcppkt ) + max_len );
if ( ! dhcppkt ) {
- DBGC ( colour, "CACHEDHCP could not allocate copy\n" );
+ DBGC ( colour, "CACHEDHCP %s could not allocate copy\n",
+ cache->name );
return -ENOMEM;
}
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
@@ -80,10 +172,26 @@ int cachedhcp_record ( userptr_t data, size_t max_len ) {
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
dhcppkt_init ( dhcppkt, dhcphdr, len );
- /* Store as cached DHCPACK, and mark original copy as consumed */
- DBGC ( colour, "CACHEDHCP found cached DHCPACK at %#08lx+%#zx/%#zx\n",
+ /* Discard duplicate packets, since some PXE stacks (including
+ * iPXE itself) will report the DHCPACK packet as the PXEBSACK
+ * if no separate PXEBSACK exists.
+ */
+ for ( i = 0 ; i < ( sizeof ( cached_packets ) /
+ sizeof ( cached_packets[0] ) ) ; i++ ) {
+ tmp = cached_packets[i]->dhcppkt;
+ if ( tmp && ( dhcppkt_len ( tmp ) == len ) &&
+ ( memcmp ( tmp->dhcphdr, dhcppkt->dhcphdr, len ) == 0 ) ) {
+ DBGC ( colour, "CACHEDHCP %s duplicates %s\n",
+ cache->name, cached_packets[i]->name );
+ dhcppkt_put ( dhcppkt );
+ return -EEXIST;
+ }
+ }
+
+ /* Store as cached packet */
+ DBGC ( colour, "CACHEDHCP %s at %#08lx+%#zx/%#zx\n", cache->name,
user_to_phys ( data, 0 ), len, max_len );
- cached_dhcpack = dhcppkt;
+ cache->dhcppkt = dhcppkt;
return 0;
}
@@ -94,14 +202,20 @@ int cachedhcp_record ( userptr_t data, size_t max_len ) {
*/
static void cachedhcp_startup ( void ) {
- /* If cached DHCP packet was not claimed by any network device
- * during startup, then free it.
- */
- if ( cached_dhcpack ) {
- DBGC ( colour, "CACHEDHCP freeing unclaimed cached DHCPACK\n" );
- dhcppkt_put ( cached_dhcpack );
- cached_dhcpack = NULL;
+ /* Apply cached ProxyDHCPOFFER, if any */
+ cachedhcp_apply ( &cached_proxydhcp, NULL );
+
+ /* Apply cached PXEBSACK, if any */
+ cachedhcp_apply ( &cached_pxebs, NULL );
+
+ /* Free any remaining cached packets */
+ if ( cached_dhcpack.dhcppkt ) {
+ DBGC ( colour, "CACHEDHCP %s unclaimed\n",
+ cached_dhcpack.name );
}
+ cachedhcp_free ( &cached_dhcpack );
+ cachedhcp_free ( &cached_proxydhcp );
+ cachedhcp_free ( &cached_pxebs );
}
/** Cached DHCPACK startup function */
@@ -117,38 +231,9 @@ struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_LATE ) = {
* @ret rc Return status code
*/
static int cachedhcp_probe ( struct net_device *netdev ) {
- struct ll_protocol *ll_protocol = netdev->ll_protocol;
- int rc;
- /* Do nothing unless we have a cached DHCPACK */
- if ( ! cached_dhcpack )
- return 0;
-
- /* Do nothing unless cached DHCPACK's MAC address matches this
- * network device.
- */
- if ( memcmp ( netdev->ll_addr, cached_dhcpack->dhcphdr->chaddr,
- ll_protocol->ll_addr_len ) != 0 ) {
- DBGC ( colour, "CACHEDHCP cached DHCPACK does not match %s\n",
- netdev->name );
- return 0;
- }
- DBGC ( colour, "CACHEDHCP cached DHCPACK is for %s\n", netdev->name );
-
- /* Register as DHCP settings for this network device */
- if ( ( rc = register_settings ( &cached_dhcpack->settings,
- netdev_settings ( netdev ),
- DHCP_SETTINGS_NAME ) ) != 0 ) {
- DBGC ( colour, "CACHEDHCP could not register settings: %s\n",
- strerror ( rc ) );
- return rc;
- }
-
- /* Claim cached DHCPACK */
- dhcppkt_put ( cached_dhcpack );
- cached_dhcpack = NULL;
-
- return 0;
+ /* Apply cached DHCPACK to network device, if applicable */
+ return cachedhcp_apply ( &cached_dhcpack, netdev );
}
/** Cached DHCP packet network device driver */
diff --git a/src/include/ipxe/cachedhcp.h b/src/include/ipxe/cachedhcp.h
index 7765c645..39ce7454 100644
--- a/src/include/ipxe/cachedhcp.h
+++ b/src/include/ipxe/cachedhcp.h
@@ -12,6 +12,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <ipxe/uaccess.h>
-extern int cachedhcp_record ( userptr_t data, size_t max_len );
+struct cached_dhcp_packet;
+
+extern struct cached_dhcp_packet cached_dhcpack;
+extern struct cached_dhcp_packet cached_proxydhcp;
+extern struct cached_dhcp_packet cached_pxebs;
+
+extern int cachedhcp_record ( struct cached_dhcp_packet *cache, userptr_t data,
+ size_t max_len );
#endif /* _IPXE_CACHEDHCP_H */
diff --git a/src/include/ipxe/dhcppkt.h b/src/include/ipxe/dhcppkt.h
index f13dfc93..86075960 100644
--- a/src/include/ipxe/dhcppkt.h
+++ b/src/include/ipxe/dhcppkt.h
@@ -56,7 +56,7 @@ dhcppkt_put ( struct dhcp_packet *dhcppkt ) {
* @v dhcppkt DHCP packet
* @ret len Used length
*/
-static inline int dhcppkt_len ( struct dhcp_packet *dhcppkt ) {
+static inline size_t dhcppkt_len ( struct dhcp_packet *dhcppkt ) {
return ( offsetof ( struct dhcphdr, options ) +
dhcppkt->options.used_len );
}
diff --git a/src/interface/efi/efi_cachedhcp.c b/src/interface/efi/efi_cachedhcp.c
index 14b531d0..1d4b98fd 100644
--- a/src/interface/efi/efi_cachedhcp.c
+++ b/src/interface/efi/efi_cachedhcp.c
@@ -75,17 +75,40 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) {
/* Record DHCPACK, if present */
if ( mode->DhcpAckReceived &&
- ( ( rc = cachedhcp_record ( virt_to_user ( &mode->DhcpAck ),
+ ( ( rc = cachedhcp_record ( &cached_dhcpack,
+ virt_to_user ( &mode->DhcpAck ),
sizeof ( mode->DhcpAck ) ) ) != 0 ) ) {
DBGC ( device, "EFI %s could not record DHCPACK: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
- goto err_record;
+ goto err_dhcpack;
+ }
+
+ /* Record ProxyDHCPOFFER, if present */
+ if ( mode->ProxyOfferReceived &&
+ ( ( rc = cachedhcp_record ( &cached_proxydhcp,
+ virt_to_user ( &mode->ProxyOffer ),
+ sizeof ( mode->ProxyOffer ) ) ) != 0)){
+ DBGC ( device, "EFI %s could not record ProxyDHCPOFFER: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ goto err_proxydhcp;
+ }
+
+ /* Record PxeBSACK, if present */
+ if ( mode->PxeReplyReceived &&
+ ( ( rc = cachedhcp_record ( &cached_pxebs,
+ virt_to_user ( &mode->PxeReply ),
+ sizeof ( mode->PxeReply ) ) ) != 0)){
+ DBGC ( device, "EFI %s could not record PXEBSACK: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ goto err_pxebs;
}
/* Success */
rc = 0;
- err_record:
+ err_pxebs:
+ err_proxydhcp:
+ err_dhcpack:
err_ipv6:
bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid,
efi_image_handle, NULL );