mirror of https://github.com/ipxe/ipxe.git
[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>
pull/448/head
parent
db6310c3e5
commit
e09e1142a3
|
@ -59,7 +59,8 @@ static void cachedhcp_init ( void ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Record cached DHCPACK */
|
/* 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 ) {
|
sizeof ( BOOTPLAYER_t ) ) ) != 0 ) {
|
||||||
DBGC ( colour, "CACHEDHCP could not record DHCPACK: %s\n",
|
DBGC ( colour, "CACHEDHCP could not record DHCPACK: %s\n",
|
||||||
strerror ( rc ) );
|
strerror ( rc ) );
|
||||||
|
|
|
@ -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 */
|
/** 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 */
|
/** Colour for debug messages */
|
||||||
#define colour &cached_dhcpack
|
#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 data DHCPACK packet buffer
|
||||||
* @v max_len Maximum possible length
|
* @v max_len Maximum possible length
|
||||||
* @ret rc Return status code
|
* @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 *dhcppkt;
|
||||||
struct dhcp_packet *tmp;
|
struct dhcp_packet *tmp;
|
||||||
struct dhcphdr *dhcphdr;
|
struct dhcphdr *dhcphdr;
|
||||||
|
unsigned int i;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
|
/* Free any existing cached packet */
|
||||||
|
cachedhcp_free ( cache );
|
||||||
|
|
||||||
/* Allocate and populate DHCP packet */
|
/* Allocate and populate DHCP packet */
|
||||||
dhcppkt = zalloc ( sizeof ( *dhcppkt ) + max_len );
|
dhcppkt = zalloc ( sizeof ( *dhcppkt ) + max_len );
|
||||||
if ( ! dhcppkt ) {
|
if ( ! dhcppkt ) {
|
||||||
DBGC ( colour, "CACHEDHCP could not allocate copy\n" );
|
DBGC ( colour, "CACHEDHCP %s could not allocate copy\n",
|
||||||
|
cache->name );
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
|
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
|
||||||
|
@ -80,10 +172,26 @@ int cachedhcp_record ( userptr_t data, size_t max_len ) {
|
||||||
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
|
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
|
||||||
dhcppkt_init ( dhcppkt, dhcphdr, len );
|
dhcppkt_init ( dhcppkt, dhcphdr, len );
|
||||||
|
|
||||||
/* Store as cached DHCPACK, and mark original copy as consumed */
|
/* Discard duplicate packets, since some PXE stacks (including
|
||||||
DBGC ( colour, "CACHEDHCP found cached DHCPACK at %#08lx+%#zx/%#zx\n",
|
* 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 );
|
user_to_phys ( data, 0 ), len, max_len );
|
||||||
cached_dhcpack = dhcppkt;
|
cache->dhcppkt = dhcppkt;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -94,14 +202,20 @@ int cachedhcp_record ( userptr_t data, size_t max_len ) {
|
||||||
*/
|
*/
|
||||||
static void cachedhcp_startup ( void ) {
|
static void cachedhcp_startup ( void ) {
|
||||||
|
|
||||||
/* If cached DHCP packet was not claimed by any network device
|
/* Apply cached ProxyDHCPOFFER, if any */
|
||||||
* during startup, then free it.
|
cachedhcp_apply ( &cached_proxydhcp, NULL );
|
||||||
*/
|
|
||||||
if ( cached_dhcpack ) {
|
/* Apply cached PXEBSACK, if any */
|
||||||
DBGC ( colour, "CACHEDHCP freeing unclaimed cached DHCPACK\n" );
|
cachedhcp_apply ( &cached_pxebs, NULL );
|
||||||
dhcppkt_put ( cached_dhcpack );
|
|
||||||
cached_dhcpack = 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 */
|
/** Cached DHCPACK startup function */
|
||||||
|
@ -117,38 +231,9 @@ struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_LATE ) = {
|
||||||
* @ret rc Return status code
|
* @ret rc Return status code
|
||||||
*/
|
*/
|
||||||
static int cachedhcp_probe ( struct net_device *netdev ) {
|
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 */
|
/* Apply cached DHCPACK to network device, if applicable */
|
||||||
if ( ! cached_dhcpack )
|
return cachedhcp_apply ( &cached_dhcpack, netdev );
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Cached DHCP packet network device driver */
|
/** Cached DHCP packet network device driver */
|
||||||
|
|
|
@ -12,6 +12,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <ipxe/uaccess.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 */
|
#endif /* _IPXE_CACHEDHCP_H */
|
||||||
|
|
|
@ -56,7 +56,7 @@ dhcppkt_put ( struct dhcp_packet *dhcppkt ) {
|
||||||
* @v dhcppkt DHCP packet
|
* @v dhcppkt DHCP packet
|
||||||
* @ret len Used length
|
* @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 ) +
|
return ( offsetof ( struct dhcphdr, options ) +
|
||||||
dhcppkt->options.used_len );
|
dhcppkt->options.used_len );
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,17 +75,40 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) {
|
||||||
|
|
||||||
/* Record DHCPACK, if present */
|
/* Record DHCPACK, if present */
|
||||||
if ( mode->DhcpAckReceived &&
|
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 ) ) {
|
sizeof ( mode->DhcpAck ) ) ) != 0 ) ) {
|
||||||
DBGC ( device, "EFI %s could not record DHCPACK: %s\n",
|
DBGC ( device, "EFI %s could not record DHCPACK: %s\n",
|
||||||
efi_handle_name ( device ), strerror ( rc ) );
|
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 */
|
/* Success */
|
||||||
rc = 0;
|
rc = 0;
|
||||||
|
|
||||||
err_record:
|
err_pxebs:
|
||||||
|
err_proxydhcp:
|
||||||
|
err_dhcpack:
|
||||||
err_ipv6:
|
err_ipv6:
|
||||||
bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid,
|
bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid,
|
||||||
efi_image_handle, NULL );
|
efi_image_handle, NULL );
|
||||||
|
|
Loading…
Reference in New Issue