@@ -231,9 +231,9 @@ struct dhcp_session {
231231 int no_pxedhcp ;
232232 /** ProxyDHCP server */
233233 struct in_addr proxy_server ;
234- /** ProxyDHCP port */
235- uint16_t proxy_port ;
236- /** ProxyDHCP server priority */
234+ /** ProxyDHCP offer */
235+ struct dhcp_packet * proxy_offer ;
236+ /** ProxyDHCP offer priority */
237237 int proxy_priority ;
238238
239239 /** PXE Boot Server type */
@@ -259,6 +259,7 @@ static void dhcp_free ( struct refcnt *refcnt ) {
259259 container_of ( refcnt , struct dhcp_session , refcnt );
260260
261261 netdev_put ( dhcp -> netdev );
262+ dhcppkt_put ( dhcp -> proxy_offer );
262263 free ( dhcp );
263264}
264265
@@ -297,6 +298,28 @@ static void dhcp_set_state ( struct dhcp_session *dhcp,
297298 start_timer_nodelay ( & dhcp -> timer );
298299}
299300
301+ /**
302+ * Check if DHCP packet contains PXE options
303+ *
304+ * @v dhcppkt DHCP packet
305+ * @ret has_pxeopts DHCP packet contains PXE options
306+ *
307+ * It is assumed that the packet is already known to contain option 60
308+ * set to "PXEClient".
309+ */
310+ static int dhcp_has_pxeopts ( struct dhcp_packet * dhcppkt ) {
311+
312+ /* Check for a boot filename */
313+ if ( dhcppkt_fetch ( dhcppkt , DHCP_BOOTFILE_NAME , NULL , 0 ) > 0 )
314+ return 1 ;
315+
316+ /* Check for a PXE boot menu */
317+ if ( dhcppkt_fetch ( dhcppkt , DHCP_PXE_BOOT_MENU , NULL , 0 ) > 0 )
318+ return 1 ;
319+
320+ return 0 ;
321+ }
322+
300323/****************************************************************************
301324 *
302325 * DHCP state machine
@@ -340,8 +363,6 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
340363 char vci [9 ]; /* "PXEClient" */
341364 int vci_len ;
342365 int has_pxeclient ;
343- int pxeopts_len ;
344- int has_pxeopts ;
345366 int8_t priority = 0 ;
346367 uint8_t no_pxedhcp = 0 ;
347368 unsigned long elapsed ;
@@ -362,12 +383,10 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
362383 vci , sizeof ( vci ) );
363384 has_pxeclient = ( ( vci_len >= ( int ) sizeof ( vci ) ) &&
364385 ( strncmp ( "PXEClient" , vci , sizeof (vci ) ) == 0 ));
365-
366- /* Identify presence of PXE-specific options */
367- pxeopts_len = dhcppkt_fetch ( dhcppkt , DHCP_PXE_BOOT_MENU , NULL , 0 );
368- has_pxeopts = ( pxeopts_len >= 0 );
369- if ( has_pxeclient )
370- DBGC ( dhcp , "%s" , ( has_pxeopts ? " pxe" : " proxy" ) );
386+ if ( has_pxeclient ) {
387+ DBGC ( dhcp , "%s" ,
388+ ( dhcp_has_pxeopts ( dhcppkt ) ? " pxe" : " proxy" ) );
389+ }
371390
372391 /* Identify priority */
373392 dhcppkt_fetch ( dhcppkt , DHCP_EB_PRIORITY , & priority ,
@@ -393,17 +412,11 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
393412 }
394413
395414 /* Select as ProxyDHCP offer, if applicable */
396- if ( has_pxeclient && ( msgtype == DHCPOFFER ) &&
415+ if ( server_id . s_addr && has_pxeclient &&
397416 ( priority >= dhcp -> proxy_priority ) ) {
398- /* If the offer already includes the PXE options, then
399- * assume that we can send the ProxyDHCPREQUEST to
400- * port 67 (since the DHCPDISCOVER that triggered this
401- * ProxyDHCPOFFER was sent to port 67). Otherwise,
402- * send the ProxyDHCPREQUEST to port 4011.
403- */
417+ dhcppkt_put ( dhcp -> proxy_offer );
404418 dhcp -> proxy_server = server_id ;
405- dhcp -> proxy_port = ( has_pxeopts ? htons ( BOOTPS_PORT )
406- : htons ( PXE_PORT ) );
419+ dhcp -> proxy_offer = dhcppkt_get ( dhcppkt );
407420 dhcp -> proxy_priority = priority ;
408421 }
409422
@@ -421,7 +434,7 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
421434
422435 /* If we can't yet transition to DHCPREQUEST, do nothing */
423436 elapsed = ( currticks () - dhcp -> start );
424- if ( ! ( dhcp -> no_pxedhcp || dhcp -> proxy_server . s_addr ||
437+ if ( ! ( dhcp -> no_pxedhcp || dhcp -> proxy_offer ||
425438 ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) )
426439 return ;
427440
@@ -507,6 +520,7 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
507520 struct in_addr server_id ) {
508521 struct in_addr ip ;
509522 struct settings * parent ;
523+ struct settings * settings ;
510524 int rc ;
511525
512526 DBGC ( dhcp , "DHCP %p %s from %s:%d" , dhcp ,
@@ -536,21 +550,36 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
536550
537551 /* Register settings */
538552 parent = netdev_settings ( dhcp -> netdev );
539- if ( ( rc = register_settings ( & dhcppkt -> settings , parent ) ) != 0 ){
553+ settings = & dhcppkt -> settings ;
554+ if ( ( rc = register_settings ( settings , parent ) ) != 0 ) {
540555 DBGC ( dhcp , "DHCP %p could not register settings: %s\n" ,
541556 dhcp , strerror ( rc ) );
542557 dhcp_finished ( dhcp , rc );
543558 return ;
544559 }
545560
546- /* Start ProxyDHCPREQUEST if applicable */
547- if ( dhcp -> proxy_server .s_addr /* Have ProxyDHCP server */ &&
548- ( ! dhcp -> no_pxedhcp ) /* ProxyDHCP not disabled */ &&
549- ( /* ProxyDHCP server is not just the DHCP server itself */
550- ( dhcp -> proxy_server .s_addr != dhcp -> server .s_addr ) ||
551- ( dhcp -> proxy_port != htons ( BOOTPS_PORT ) ) ) ) {
552- dhcp_set_state ( dhcp , & dhcp_state_proxy );
553- return ;
561+ /* Perform ProxyDHCP if applicable */
562+ if ( dhcp -> proxy_offer /* Have ProxyDHCP offer */ &&
563+ ( ! dhcp -> no_pxedhcp ) /* ProxyDHCP not disabled */ ) {
564+ if ( dhcp_has_pxeopts ( dhcp -> proxy_offer ) ) {
565+ /* PXE options already present; register settings
566+ * without performing a ProxyDHCPREQUEST
567+ */
568+ settings = & dhcp -> proxy_offer -> settings ;
569+ settings -> name = PROXYDHCP_SETTINGS_NAME ;
570+ if ( ( rc = register_settings ( settings ,
571+ NULL ) ) != 0 ) {
572+ DBGC ( dhcp , "DHCP %p could not register "
573+ "proxy settings: %s\n" ,
574+ dhcp , strerror ( rc ) );
575+ dhcp_finished ( dhcp , rc );
576+ return ;
577+ }
578+ } else {
579+ /* PXE options not present; use a ProxyDHCPREQUEST */
580+ dhcp_set_state ( dhcp , & dhcp_state_proxy );
581+ return ;
582+ }
554583 }
555584
556585 /* Terminate DHCP */
@@ -590,8 +619,8 @@ static int dhcp_proxy_tx ( struct dhcp_session *dhcp,
590619 struct sockaddr_in * peer ) {
591620 int rc ;
592621
593- DBGC ( dhcp , "DHCP %p ProxyDHCP REQUEST to %s:%d \n" , dhcp ,
594- inet_ntoa ( dhcp -> proxy_server ), ntohs ( dhcp -> proxy_port ) );
622+ DBGC ( dhcp , "DHCP %p ProxyDHCP REQUEST to %s\n" , dhcp ,
623+ inet_ntoa ( dhcp -> proxy_server ) );
595624
596625 /* Set server ID */
597626 if ( ( rc = dhcppkt_store ( dhcppkt , DHCP_SERVER_IDENTIFIER ,
@@ -601,7 +630,7 @@ static int dhcp_proxy_tx ( struct dhcp_session *dhcp,
601630
602631 /* Set server address */
603632 peer -> sin_addr = dhcp -> proxy_server ;
604- peer -> sin_port = dhcp -> proxy_port ;
633+ peer -> sin_port = htons ( PXE_PORT ) ;
605634
606635 return 0 ;
607636}
@@ -619,6 +648,7 @@ static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
619648 struct dhcp_packet * dhcppkt ,
620649 struct sockaddr_in * peer , uint8_t msgtype ,
621650 struct in_addr server_id ) {
651+ struct settings * settings = & dhcppkt -> settings ;
622652 int rc ;
623653
624654 DBGC ( dhcp , "DHCP %p %s from %s:%d" , dhcp ,
@@ -629,7 +659,7 @@ static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
629659 DBGC ( dhcp , "\n" );
630660
631661 /* Filter out unacceptable responses */
632- if ( peer -> sin_port != dhcp -> proxy_port )
662+ if ( peer -> sin_port != ntohs ( PXE_PORT ) )
633663 return ;
634664 if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) )
635665 return ;
@@ -638,9 +668,9 @@ static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
638668 return ;
639669
640670 /* Register settings */
641- dhcppkt -> settings . name = PROXYDHCP_SETTINGS_NAME ;
642- if ( ( rc = register_settings ( & dhcppkt -> settings , NULL ) ) != 0 ) {
643- DBGC ( dhcp , "DHCP %p could not register settings: %s\n" ,
671+ settings -> name = PROXYDHCP_SETTINGS_NAME ;
672+ if ( ( rc = register_settings ( settings , NULL ) ) != 0 ) {
673+ DBGC ( dhcp , "DHCP %p could not register proxy settings: %s\n" ,
644674 dhcp , strerror ( rc ) );
645675 dhcp_finished ( dhcp , rc );
646676 return ;
0 commit comments