Skip to content

Commit

Permalink
[pxe] Always retrieve cached DHCPACK and apply to relevant network de…
Browse files Browse the repository at this point in the history
…vice

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>
  • Loading branch information
mcb30 committed Oct 25, 2013
1 parent 1aa67eb commit 10d19bd
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 198 deletions.
188 changes: 188 additions & 0 deletions src/arch/i386/core/cachedhcp.c
@@ -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,
};
70 changes: 0 additions & 70 deletions src/arch/i386/interface/pxeparent/pxeparent_dhcp.c

This file was deleted.

51 changes: 48 additions & 3 deletions src/arch/i386/prefix/pxeprefix.S
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 */
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
17 changes: 1 addition & 16 deletions src/include/ipxe/dhcp.h
Expand Up @@ -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
Expand Down Expand Up @@ -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 */

0 comments on commit 10d19bd

Please sign in to comment.