Skip to content

Commit 831106a

Browse files
committed
[dhcp] Omit ProxyDHCPREQUEST if PXE options are present in ProxyDHCPOFFER
Some ProxyDHCP implementations seem to violate the PXE specification by expecting the client to retain options from the ProxyDHCPOFFER rather than issuing a separate ProxyDHCPREQUEST. Work around such broken clients by retaining the ProxyDHCPOFFER packet, and proceeding to a ProxyDHCPREQUEST only if the ProxyDHCPOFFER does not already contain PXE options. Signed-off-by: Michael Brown <mcb30@ipxe.org>
1 parent ba6aca3 commit 831106a

File tree

1 file changed

+67
-37
lines changed

1 file changed

+67
-37
lines changed

src/net/udp/dhcp.c

Lines changed: 67 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)