summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2007-11-21 22:51:43 +0100
committerMichael Brown2007-11-21 22:51:43 +0100
commit0becbf5fba997b1806c65ee94c49e98f046ee799 (patch)
tree6fc7ed74699cd3985bd0a2d26dafd0f72f817af8
parentAdded netdev_nullify to natsemi_remove() (diff)
downloadipxe-0becbf5fba997b1806c65ee94c49e98f046ee799.tar.gz
ipxe-0becbf5fba997b1806c65ee94c49e98f046ee799.tar.xz
ipxe-0becbf5fba997b1806c65ee94c49e98f046ee799.zip
Add ProxyDHCP support.
-rw-r--r--src/include/gpxe/dhcp.h6
-rw-r--r--src/net/udp/dhcp.c79
2 files changed, 59 insertions, 26 deletions
diff --git a/src/include/gpxe/dhcp.h b/src/include/gpxe/dhcp.h
index 60d2ccff..c99dd576 100644
--- a/src/include/gpxe/dhcp.h
+++ b/src/include/gpxe/dhcp.h
@@ -12,6 +12,7 @@
#include <gpxe/in.h>
#include <gpxe/refcnt.h>
#include <gpxe/tables.h>
+#include <latch.h>
struct net_device;
struct job_interface;
@@ -505,13 +506,16 @@ dhcpopt_get ( struct dhcp_option_block *options ) {
/**
* Drop reference to DHCP options block
*
- * @v options DHCP options block
+ * @v options DHCP options block, or NULL
*/
static inline __attribute__ (( always_inline )) void
dhcpopt_put ( struct dhcp_option_block *options ) {
ref_put ( &options->refcnt );
}
+/** Maximum time that we will wait for ProxyDHCP offers */
+#define PROXYDHCP_WAIT_TIME ( TICKS_PER_SEC * 2 )
+
extern struct list_head dhcp_option_blocks;
extern unsigned long dhcp_num_option ( struct dhcp_option *option );
diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c
index 30804457..584c5488 100644
--- a/src/net/udp/dhcp.c
+++ b/src/net/udp/dhcp.c
@@ -693,10 +693,14 @@ struct dhcp_session {
* (e.g. @c DHCPDISCOVER).
*/
int state;
- /** Options obtained from server */
+ /** Options obtained from DHCP server */
struct dhcp_option_block *options;
+ /** Options obtained from ProxyDHCP server */
+ struct dhcp_option_block *proxy_options;
/** Retransmission timer */
struct retry_timer timer;
+ /** Session start time (in ticks) */
+ unsigned long start;
};
/**
@@ -710,6 +714,7 @@ static void dhcp_free ( struct refcnt *refcnt ) {
netdev_put ( dhcp->netdev );
dhcpopt_put ( dhcp->options );
+ dhcpopt_put ( dhcp->proxy_options );
free ( dhcp );
}
@@ -826,7 +831,10 @@ static int dhcp_deliver_raw ( struct xfer_interface *xfer,
container_of ( xfer, struct dhcp_session, xfer );
const struct dhcphdr *dhcphdr = data;
struct dhcp_option_block *options;
+ struct dhcp_option_block **store_options;
+ int is_proxy;
unsigned int msgtype;
+ unsigned long elapsed;
/* Check for matching transaction ID */
if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
@@ -843,41 +851,61 @@ static int dhcp_deliver_raw ( struct xfer_interface *xfer,
return -EINVAL;
}
- /* Determine message type */
+ /* Determine and verify message type */
+ is_proxy = ( dhcphdr->yiaddr.s_addr == 0 );
msgtype = find_dhcp_num_option ( options, DHCP_MESSAGE_TYPE );
- DBGC ( dhcp, "DHCP %p received %s\n",
- dhcp, dhcp_msgtype_name ( msgtype ) );
+ DBGC ( dhcp, "DHCP %p received %s%s\n", dhcp,
+ ( is_proxy ? "Proxy" : "" ), dhcp_msgtype_name ( msgtype ) );
+ if ( ( ( dhcp->state != DHCPDISCOVER ) || ( msgtype != DHCPOFFER ) ) &&
+ ( ( dhcp->state != DHCPREQUEST ) || ( msgtype != DHCPACK ) ) ) {
+ DBGC ( dhcp, "DHCP %p discarding %s while in %s state\n",
+ dhcp, dhcp_msgtype_name ( msgtype ),
+ dhcp_msgtype_name ( dhcp->state ) );
+ goto out_discard;
+ }
- /* Handle DHCP reply */
+ /* Update stored standard/ProxyDHCP options, if the new
+ * options have equal or higher priority than the
+ * currently-stored options.
+ */
+ store_options = ( is_proxy ? &dhcp->proxy_options : &dhcp->options );
+ if ( ( ! *store_options ) ||
+ ( find_dhcp_num_option ( options, DHCP_EB_PRIORITY ) >=
+ find_dhcp_num_option ( *store_options, DHCP_EB_PRIORITY ) ) ) {
+ dhcpopt_put ( *store_options );
+ *store_options = options;
+ } else {
+ dhcpopt_put ( options );
+ }
+
+ /* Handle DHCP response */
switch ( dhcp->state ) {
case DHCPDISCOVER:
- if ( msgtype != DHCPOFFER )
- goto out_discard;
- dhcp->state = DHCPREQUEST;
+ /* If we have received a valid standard DHCP response
+ * (i.e. one with an IP address), and we have allowed
+ * sufficient time for ProxyDHCP reponses, then
+ * transition to making the DHCPREQUEST.
+ */
+ elapsed = ( currticks() - dhcp->start );
+ if ( dhcp->options &&
+ ( elapsed > PROXYDHCP_WAIT_TIME ) ) {
+ stop_timer ( &dhcp->timer );
+ dhcp->state = DHCPREQUEST;
+ dhcp_send_request ( dhcp );
+ }
break;
case DHCPREQUEST:
- if ( msgtype != DHCPACK )
- goto out_discard;
- dhcp->state = DHCPACK;
+ /* DHCP finished; register options and exit */
+ if ( dhcp->proxy_options )
+ dhcp->register_options ( dhcp->netdev,
+ dhcp->proxy_options );
+ dhcp->register_options ( dhcp->netdev, dhcp->options );
+ dhcp_finished ( dhcp, 0 );
break;
default:
assert ( 0 );
- goto out_discard;
}
- /* Stop timer and update stored options */
- stop_timer ( &dhcp->timer );
- if ( dhcp->options )
- dhcpopt_put ( dhcp->options );
- dhcp->options = options;
-
- /* Transmit next packet, or terminate session */
- if ( dhcp->state < DHCPACK ) {
- dhcp_send_request ( dhcp );
- } else {
- dhcp->register_options ( dhcp->netdev, dhcp->options );
- dhcp_finished ( dhcp, 0 );
- }
return 0;
out_discard:
@@ -965,6 +993,7 @@ int start_dhcp ( struct job_interface *job, struct net_device *netdev,
dhcp->register_options = register_options;
dhcp->timer.expired = dhcp_timer_expired;
dhcp->state = DHCPDISCOVER;
+ dhcp->start = currticks();
/* Instantiate child objects and attach to our interfaces */
if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM,