diff options
author | Michael Brown | 2007-11-21 22:51:43 +0100 |
---|---|---|
committer | Michael Brown | 2007-11-21 22:51:43 +0100 |
commit | 0becbf5fba997b1806c65ee94c49e98f046ee799 (patch) | |
tree | 6fc7ed74699cd3985bd0a2d26dafd0f72f817af8 | |
parent | Added netdev_nullify to natsemi_remove() (diff) | |
download | ipxe-0becbf5fba997b1806c65ee94c49e98f046ee799.tar.gz ipxe-0becbf5fba997b1806c65ee94c49e98f046ee799.tar.xz ipxe-0becbf5fba997b1806c65ee94c49e98f046ee799.zip |
Add ProxyDHCP support.
-rw-r--r-- | src/include/gpxe/dhcp.h | 6 | ||||
-rw-r--r-- | src/net/udp/dhcp.c | 79 |
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, |