david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[pxe] Always retrieve cached DHCPACK and apply to relevant network device

When chainloading, always retrieve the cached DHCPACK packet from the
underlying PXE stack, and apply it as the original contents of the
"net<X>.dhcp" settings block.  This allows cached DHCP settings to be
used for any chainloaded iPXE binary (not just undionly.kkpxe).

This change eliminates the undocumented "use-cached" setting.  Issuing
the "dhcp" command will now always result in a fresh DHCP request.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2013-10-25 17:24:09 +01:00
parent 1aa67eba16
commit 10d19bd2ac
7 changed files with 239 additions and 198 deletions

View File

@ -0,0 +1,188 @@
/*
* Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <stdlib.h>
#include <ipxe/dhcppkt.h>
#include <ipxe/init.h>
#include <ipxe/netdevice.h>
#include <realmode.h>
#include <pxe_api.h>
/** @file
*
* Cached DHCP packet
*
*/
/** Cached DHCPACK physical address
*
* This can be set by the prefix.
*/
uint32_t __bss16 ( cached_dhcpack_phys );
#define cached_dhcpack_phys __use_data16 ( cached_dhcpack_phys )
/** Colour for debug messages */
#define colour &cached_dhcpack_phys
/** Cached DHCPACK */
static struct dhcp_packet *cached_dhcpack;
/**
* Cached DHCPACK startup function
*
*/
static void cachedhcp_startup ( void ) {
struct dhcp_packet *dhcppkt;
struct dhcp_packet *tmp;
struct dhcphdr *dhcphdr;
size_t len;
/* Do nothing if no cached DHCPACK is present */
if ( ! cached_dhcpack_phys ) {
DBGC ( colour, "CACHEDHCP found no cached DHCPACK\n" );
return;
}
/* No reliable way to determine length before parsing packet;
* start by assuming maximum length permitted by PXE.
*/
len = sizeof ( BOOTPLAYER_t );
/* Allocate and populate DHCP packet */
dhcppkt = zalloc ( sizeof ( *dhcppkt ) + len );
if ( ! dhcppkt ) {
DBGC ( colour, "CACHEDHCP could not allocate copy\n" );
return;
}
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
copy_from_user ( dhcphdr, phys_to_user ( cached_dhcpack_phys ), 0,
len );
dhcppkt_init ( dhcppkt, dhcphdr, len );
/* Resize packet to required length. If reallocation fails,
* just continue to use the original packet.
*/
len = dhcppkt_len ( dhcppkt );
tmp = realloc ( dhcppkt, ( sizeof ( *dhcppkt ) + len ) );
if ( tmp )
dhcppkt = tmp;
/* Reinitialise packet at new address */
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 %08x+%zx\n",
cached_dhcpack_phys, len );
cached_dhcpack = dhcppkt;
cached_dhcpack_phys = 0;
}
/**
* Cached DHCPACK shutdown function
*
* @v booting Shutting down in order to boot
*/
static void cachedhcp_shutdown ( int booting __unused ) {
/* If cached DHCP packet has not yet been claimed, free it */
if ( cached_dhcpack ) {
DBGC ( colour, "CACHEDHCP freeing unclaimed cached DHCPACK\n" );
dhcppkt_put ( cached_dhcpack );
cached_dhcpack = NULL;
}
}
/** Cached DHCPACK initialisation function */
struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
.startup = cachedhcp_startup,
.shutdown = cachedhcp_shutdown,
};
/**
* Apply cached DHCPACK to network device, if applicable
*
* @v netdev Network device
* @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;
}
/**
* Handle network device link state change
*
* @v netdev Network device
*/
static void cachedhcp_notify ( struct net_device *netdev __unused ) {
/* Nothing to do */
}
/**
* Handle network device removal
*
* @v netdev Network device
*/
static void cachedhcp_remove ( struct net_device *netdev __unused ) {
/* Nothing to do */
}
/** Cached DHCP packet network device driver */
struct net_driver cachedhcp_driver __net_driver = {
.name = "cachedhcp",
.probe = cachedhcp_probe,
.notify = cachedhcp_notify,
.remove = cachedhcp_remove,
};

View File

@ -1,70 +0,0 @@
/*
* Copyright (C) 2009 Joshua Oreman <oremanj@rwcr.net>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <string.h>
#include <ipxe/dhcp.h>
#include <ipxe/netdevice.h>
#include <undipreload.h>
#include <pxeparent.h>
#include <realmode.h>
#include <pxe_api.h>
/**
* Present cached DHCP packet if it exists
*/
void get_cached_dhcpack ( void ) {
struct undi_device *undi;
struct s_PXENV_GET_CACHED_INFO get_cached_info;
int rc;
/* Use preloaded UNDI device to get at PXE entry point */
undi = &preloaded_undi;
if ( ! undi->entry.segment ) {
DBG ( "PXEDHCP no preloaded UNDI device found\n" );
return;
}
/* Check that stack is available to get cached info */
if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
DBG ( "PXEDHCP stack was unloaded, no cache available\n" );
return;
}
/* Obtain cached DHCP packet */
memset ( &get_cached_info, 0, sizeof ( get_cached_info ) );
get_cached_info.PacketType = PXENV_PACKET_TYPE_DHCP_ACK;
if ( ( rc = pxeparent_call ( undi->entry, PXENV_GET_CACHED_INFO,
&get_cached_info,
sizeof ( get_cached_info ) ) ) != 0 ) {
DBG ( "PXEDHCP GET_CACHED_INFO failed: %s\n", strerror ( rc ) );
return;
}
DBG ( "PXEDHCP got cached info at %04x:%04x length %d\n",
get_cached_info.Buffer.segment, get_cached_info.Buffer.offset,
get_cached_info.BufferSize );
/* Present cached DHCP packet */
store_cached_dhcpack ( real_to_user ( get_cached_info.Buffer.segment,
get_cached_info.Buffer.offset ),
get_cached_info.BufferSize );
}

View File

@ -5,6 +5,8 @@ FILE_LICENCE ( GPL2_OR_LATER )
#define PXENV_UNDI_GET_IFACE_INFO 0x0013
#define PXENV_STOP_UNDI 0x0015
#define PXENV_UNLOAD_STACK 0x0070
#define PXENV_GET_CACHED_INFO 0x0071
#define PXENV_PACKET_TYPE_DHCP_ACK 0x0002
#define PXENV_FILE_CMDLINE 0x00e8
#define PXE_HACK_EB54 0x0001
@ -20,7 +22,18 @@ FILE_LICENCE ( GPL2_OR_LATER )
#define EB_MAGIC_1 ( 'E' + ( 't' << 8 ) + ( 'h' << 16 ) + ( 'e' << 24 ) )
#define EB_MAGIC_2 ( 'r' + ( 'b' << 8 ) + ( 'o' << 16 ) + ( 'o' << 24 ) )
/* Prefix memory layout:
*
* iPXE binary image
* Temporary stack
* Temporary copy of DHCPACK packet
* Temporary copy of command line
*/
#define PREFIX_STACK_SIZE 2048
#define PREFIX_TEMP_DHCPACK PREFIX_STACK_SIZE
#define PREFIX_TEMP_DHCPACK_SIZE ( 1260 /* sizeof ( BOOTPLAYER_t ) */ )
#define PREFIX_TEMP_CMDLINE ( PREFIX_TEMP_DHCPACK + PREFIX_TEMP_DHCPACK_SIZE )
#define PREFIX_TEMP_CMDLINE_SIZE 4096
/*****************************************************************************
* Entry point: set operating context, print welcome message
@ -382,6 +395,32 @@ get_iface_type:
99: movb $0x0a, %al
call print_character
/*****************************************************************************
* Get cached DHCP_ACK packet
*****************************************************************************
*/
get_dhcpack:
/* Issue PXENV_GET_CACHED_INFO */
xorl %esi, %esi
movw %ss, %si
movw %si, ( pxe_parameter_structure + 0x08 )
movw $PREFIX_TEMP_DHCPACK, ( pxe_parameter_structure + 0x06 )
movw $PREFIX_TEMP_DHCPACK_SIZE, ( pxe_parameter_structure +0x04 )
movw $PXENV_PACKET_TYPE_DHCP_ACK, ( pxe_parameter_structure + 0x02 )
movw $PXENV_GET_CACHED_INFO, %bx
call pxe_call
jnc 1f
call print_pxe_error
jmp 99f
1: /* Store physical address of packet */
shll $4, %esi
addl $PREFIX_TEMP_DHCPACK, %esi
movl %esi, pxe_cached_dhcpack
99:
.section ".prefix.data", "aw", @progbits
pxe_cached_dhcpack:
.long 0
.previous
/*****************************************************************************
* Check for a command line
@ -392,8 +431,8 @@ get_cmdline:
xorl %esi, %esi
movw %ss, %si
movw %si, ( pxe_parameter_structure + 0x06 )
movw $PREFIX_STACK_SIZE, ( pxe_parameter_structure + 0x04 )
movw $0xffff, ( pxe_parameter_structure + 0x02 )
movw $PREFIX_TEMP_CMDLINE, ( pxe_parameter_structure + 0x04 )
movw $PREFIX_TEMP_CMDLINE_SIZE, ( pxe_parameter_structure + 0x02 )
movw $PXENV_FILE_CMDLINE, %bx
call pxe_call
jc 99f /* Suppress errors; this is an iPXE extension API call */
@ -403,7 +442,7 @@ get_cmdline:
jz 99f
/* Record command line */
shll $4, %esi
addl $PREFIX_STACK_SIZE, %esi
addl $PREFIX_TEMP_CMDLINE, %esi
movl %esi, pxe_cmdline
99:
.section ".prefix.data", "aw", @progbits
@ -761,6 +800,9 @@ run_ipxe:
/* Retrieve PXE command line, if any */
movl pxe_cmdline, %esi
/* Retrieve cached DHCPACK, if any */
movl pxe_cached_dhcpack, %ecx
/* Jump to .text16 segment with %ds pointing to .data16 */
movw %bx, %ds
pushw %ax
@ -774,6 +816,9 @@ run_ipxe:
/* Store command-line pointer */
movl %esi, cmdline_phys
/* Store cached DHCPACK pointer */
movl %ecx, cached_dhcpack_phys
/* Run main program */
pushl $main
pushw %cs

View File

@ -397,14 +397,7 @@ struct dhcp_netdev_desc {
uint16_t device;
} __attribute__ (( packed ));
/** Use cached network settings
*
* Cached network settings may be available from a prior DHCP request
* (if running as a PXE NBP), non-volatile storage on the NIC, or
* settings set via the command line or an embedded image. If this
* flag is not set, it will be assumed that those sources are
* insufficient and that DHCP should still be run when autobooting.
*/
/** Use cached network settings (obsolete; do not reuse this value) */
#define DHCP_EB_USE_CACHED DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb2 )
/** BIOS drive number
@ -677,12 +670,4 @@ extern int start_dhcp ( struct interface *job, struct net_device *netdev );
extern int start_pxebs ( struct interface *job, struct net_device *netdev,
unsigned int pxe_type );
/* In environments that can provide cached DHCP packets, this function
* should look for such a packet and call store_cached_dhcpack() with
* it if it exists.
*/
extern void get_cached_dhcpack ( void );
extern void store_cached_dhcpack ( userptr_t data, size_t len );
#endif /* _IPXE_DHCP_H */

View File

@ -1,78 +0,0 @@
/*
* Copyright (C) 2009 Joshua Oreman <oremanj@rwcr.net>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ipxe/dhcp.h>
#include <ipxe/dhcppkt.h>
#include <ipxe/netdevice.h>
#include <ipxe/iobuf.h>
#include <ipxe/uaccess.h>
/** @file
*
* Cached DHCP packet handling
*
*/
/**
* Store cached DHCPACK packet
*
* @v data User pointer to cached DHCP packet data
* @v len Length of cached DHCP packet data
* @ret rc Return status code
*
* This function should be called by the architecture-specific
* get_cached_dhcpack() handler.
*/
void store_cached_dhcpack ( userptr_t data, size_t len ) {
struct dhcp_packet *dhcppkt;
struct dhcphdr *dhcphdr;
struct settings *parent;
int rc;
/* Create DHCP packet */
dhcppkt = zalloc ( sizeof ( *dhcppkt ) + len );
if ( ! dhcppkt )
return;
/* Fill in data for DHCP packet */
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( * dhcppkt ) );
copy_from_user ( dhcphdr, data, 0, len );
dhcppkt_init ( dhcppkt, dhcphdr, len );
DBG_HD ( dhcppkt->options.data, dhcppkt->options.used_len );
/* Register settings on the last opened network device.
* This will have the effect of registering cached settings
* with a network device when "dhcp netX" is performed for that
* device, which is usually what we want.
*/
parent = netdev_settings ( last_opened_netdev() );
if ( ( rc = register_settings ( &dhcppkt->settings, parent,
DHCP_SETTINGS_NAME ) ) != 0 )
DBG ( "DHCP could not register cached settings: %s\n",
strerror ( rc ) );
dhcppkt_put ( dhcppkt );
DBG ( "DHCP registered cached settings\n" );
}

View File

@ -107,14 +107,6 @@ struct setting user_class_setting __setting ( SETTING_HOST_EXTRA ) = {
.type = &setting_type_string,
};
/** Use cached network settings */
struct setting use_cached_setting __setting ( SETTING_MISC ) = {
.name = "use-cached",
.description = "Use cached settings",
.tag = DHCP_EB_USE_CACHED,
.type = &setting_type_uint8,
};
/**
* Most recent DHCP transaction ID
*
@ -1284,38 +1276,21 @@ static struct sockaddr dhcp_peer = {
.sa_family = AF_INET,
};
/**
* Get cached DHCPACK where none exists
*/
__weak void get_cached_dhcpack ( void ) { __keepme }
/**
* Start DHCP state machine on a network device
*
* @v job Job control interface
* @v netdev Network device
* @ret rc Return status code, or positive if cached
* @ret rc Return status code
*
* Starts DHCP on the specified network device. If successful, the
* DHCPACK (and ProxyDHCPACK, if applicable) will be registered as
* option sources.
*
* On a return of 0, a background job has been started to perform the
* DHCP request. Any nonzero return means the job has not been
* started; a positive return value indicates the success condition of
* having fetched the appropriate data from cached information.
*/
int start_dhcp ( struct interface *job, struct net_device *netdev ) {
struct dhcp_session *dhcp;
int rc;
/* Check for cached DHCP information */
get_cached_dhcpack();
if ( fetch_uintz_setting ( NULL, &use_cached_setting ) ) {
DBG ( "DHCP using cached network settings\n" );
return 1;
}
/* Allocate and initialise structure */
dhcp = zalloc ( sizeof ( *dhcp ) );
if ( ! dhcp )

View File

@ -51,12 +51,8 @@ int dhcp ( struct net_device *netdev ) {
/* Perform DHCP */
printf ( "DHCP (%s %s)", netdev->name,
netdev->ll_protocol->ntoa ( netdev->ll_addr ) );
if ( ( rc = start_dhcp ( &monojob, netdev ) ) == 0 ) {
if ( ( rc = start_dhcp ( &monojob, netdev ) ) == 0 )
rc = monojob_wait ( "" );
} else if ( rc > 0 ) {
printf ( " using cached\n" );
rc = 0;
}
return rc;
}