Browse Source

[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>
tags/v1.20.1
Michael Brown 13 years ago
parent
commit
831106a875
1 changed files with 67 additions and 37 deletions
  1. 67
    37
      src/net/udp/dhcp.c

+ 67
- 37
src/net/udp/dhcp.c View File

231
 	int no_pxedhcp;
231
 	int no_pxedhcp;
232
 	/** ProxyDHCP server */
232
 	/** ProxyDHCP server */
233
 	struct in_addr proxy_server;
233
 	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 */
237
 	int proxy_priority;
237
 	int proxy_priority;
238
 
238
 
239
 	/** PXE Boot Server type */
239
 	/** PXE Boot Server type */
259
 		container_of ( refcnt, struct dhcp_session, refcnt );
259
 		container_of ( refcnt, struct dhcp_session, refcnt );
260
 
260
 
261
 	netdev_put ( dhcp->netdev );
261
 	netdev_put ( dhcp->netdev );
262
+	dhcppkt_put ( dhcp->proxy_offer );
262
 	free ( dhcp );
263
 	free ( dhcp );
263
 }
264
 }
264
 
265
 
297
 	start_timer_nodelay ( &dhcp->timer );
298
 	start_timer_nodelay ( &dhcp->timer );
298
 }
299
 }
299
 
300
 
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
+
300
 /****************************************************************************
323
 /****************************************************************************
301
  *
324
  *
302
  * DHCP state machine
325
  * DHCP state machine
340
 	char vci[9]; /* "PXEClient" */
363
 	char vci[9]; /* "PXEClient" */
341
 	int vci_len;
364
 	int vci_len;
342
 	int has_pxeclient;
365
 	int has_pxeclient;
343
-	int pxeopts_len;
344
-	int has_pxeopts;
345
 	int8_t priority = 0;
366
 	int8_t priority = 0;
346
 	uint8_t no_pxedhcp = 0;
367
 	uint8_t no_pxedhcp = 0;
347
 	unsigned long elapsed;
368
 	unsigned long elapsed;
362
 				  vci, sizeof ( vci ) );
383
 				  vci, sizeof ( vci ) );
363
 	has_pxeclient = ( ( vci_len >= ( int ) sizeof ( vci ) ) &&
384
 	has_pxeclient = ( ( vci_len >= ( int ) sizeof ( vci ) ) &&
364
 			  ( strncmp ( "PXEClient", vci, sizeof (vci) ) == 0 ));
385
 			  ( 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
+	}
371
 
390
 
372
 	/* Identify priority */
391
 	/* Identify priority */
373
 	dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority,
392
 	dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority,
393
 	}
412
 	}
394
 
413
 
395
 	/* Select as ProxyDHCP offer, if applicable */
414
 	/* Select as ProxyDHCP offer, if applicable */
396
-	if ( has_pxeclient && ( msgtype == DHCPOFFER ) &&
415
+	if ( server_id.s_addr && has_pxeclient &&
397
 	     ( priority >= dhcp->proxy_priority ) ) {
416
 	     ( 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 );
404
 		dhcp->proxy_server = server_id;
418
 		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 );
407
 		dhcp->proxy_priority = priority;
420
 		dhcp->proxy_priority = priority;
408
 	}
421
 	}
409
 
422
 
421
 
434
 
422
 	/* If we can't yet transition to DHCPREQUEST, do nothing */
435
 	/* If we can't yet transition to DHCPREQUEST, do nothing */
423
 	elapsed = ( currticks() - dhcp->start );
436
 	elapsed = ( currticks() - dhcp->start );
424
-	if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_server.s_addr ||
437
+	if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_offer ||
425
 		 ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) )
438
 		 ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) )
426
 		return;
439
 		return;
427
 
440
 
507
 			      struct in_addr server_id ) {
520
 			      struct in_addr server_id ) {
508
 	struct in_addr ip;
521
 	struct in_addr ip;
509
 	struct settings *parent;
522
 	struct settings *parent;
523
+	struct settings *settings;
510
 	int rc;
524
 	int rc;
511
 
525
 
512
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
526
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
536
 
550
 
537
 	/* Register settings */
551
 	/* Register settings */
538
 	parent = netdev_settings ( dhcp->netdev );
552
 	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 ) {
540
 		DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
555
 		DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
541
 		       dhcp, strerror ( rc ) );
556
 		       dhcp, strerror ( rc ) );
542
 		dhcp_finished ( dhcp, rc );
557
 		dhcp_finished ( dhcp, rc );
543
 		return;
558
 		return;
544
 	}
559
 	}
545
 
560
 
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
+		}
554
 	}
583
 	}
555
 
584
 
556
 	/* Terminate DHCP */
585
 	/* Terminate DHCP */
590
 			   struct sockaddr_in *peer ) {
619
 			   struct sockaddr_in *peer ) {
591
 	int rc;
620
 	int rc;
592
 
621
 
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 ) );
595
 
624
 
596
 	/* Set server ID */
625
 	/* Set server ID */
597
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
626
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
601
 
630
 
602
 	/* Set server address */
631
 	/* Set server address */
603
 	peer->sin_addr = dhcp->proxy_server;
632
 	peer->sin_addr = dhcp->proxy_server;
604
-	peer->sin_port = dhcp->proxy_port;
633
+	peer->sin_port = htons ( PXE_PORT );
605
 
634
 
606
 	return 0;
635
 	return 0;
607
 }
636
 }
619
 			    struct dhcp_packet *dhcppkt,
648
 			    struct dhcp_packet *dhcppkt,
620
 			    struct sockaddr_in *peer, uint8_t msgtype,
649
 			    struct sockaddr_in *peer, uint8_t msgtype,
621
 			    struct in_addr server_id ) {
650
 			    struct in_addr server_id ) {
651
+	struct settings *settings = &dhcppkt->settings;
622
 	int rc;
652
 	int rc;
623
 
653
 
624
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
654
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
629
 	DBGC ( dhcp, "\n" );
659
 	DBGC ( dhcp, "\n" );
630
 
660
 
631
 	/* Filter out unacceptable responses */
661
 	/* Filter out unacceptable responses */
632
-	if ( peer->sin_port != dhcp->proxy_port )
662
+	if ( peer->sin_port != ntohs ( PXE_PORT ) )
633
 		return;
663
 		return;
634
 	if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) )
664
 	if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) )
635
 		return;
665
 		return;
638
 		return;
668
 		return;
639
 
669
 
640
 	/* Register settings */
670
 	/* 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",
644
 		       dhcp, strerror ( rc ) );
674
 		       dhcp, strerror ( rc ) );
645
 		dhcp_finished ( dhcp, rc );
675
 		dhcp_finished ( dhcp, rc );
646
 		return;
676
 		return;

Loading…
Cancel
Save