Browse Source

[dhcp] Keep multiple DHCP offers received, and use them intelligently

Instead of keeping only the best IP and PXE offers, store all of them,
and pick the best to use just before a request is sent. This allows
priority differentiation to work even when lower-priority offers
provide PXE options, and improves robustness at sites with broken PXE
servers intermingled with working ones: when a ProxyDHCP request times
out, instead of giving up, we try the next PXE offer we've received.
It also allows us to avoid breaking up combined IP+PXE offers, which
can be important with some firewall configurations. This behavior
matches that of most vendor PXE ROMs.

Store a reference to the DHCPOFFER packet in the offer structure, so
that when registering settings after a successful ACK we can register
the proxy PXE settings we originally received; this removes the need
for a nonstandard duplicate REQUEST/ACK to port 67 of proxy servers
like dnsmasq that provide PXE options in the OFFER.

Total cost: 450 bytes uncompressed.

Signed-off-by: Marty Connor <mdc@etherboot.org>
tags/v1.0.0-rc1
Joshua Oreman 15 years ago
parent
commit
5efc2fcb60
1 changed files with 228 additions and 86 deletions
  1. 228
    86
      src/net/udp/dhcp.c

+ 228
- 86
src/net/udp/dhcp.c View File

206
 static struct dhcp_session_state dhcp_state_proxy;
206
 static struct dhcp_session_state dhcp_state_proxy;
207
 static struct dhcp_session_state dhcp_state_pxebs;
207
 static struct dhcp_session_state dhcp_state_pxebs;
208
 
208
 
209
+/** DHCP offer is valid for IP lease */
210
+#define DHCP_OFFER_IP	1
211
+
212
+/** DHCP offer is valid for PXE options */
213
+#define DHCP_OFFER_PXE	2
214
+
215
+/** A DHCP offer */
216
+struct dhcp_offer {
217
+	/** IP address of server granting offer */
218
+	struct in_addr server;
219
+
220
+	/** IP address being offered, or 0.0.0.0 for a pure proxy */
221
+	struct in_addr ip;
222
+
223
+	/** DHCP packet containing PXE options; NULL if missing or proxied */
224
+	struct dhcp_packet *pxe;
225
+
226
+	/** Valid uses for this offer, a combination of DHCP_OFFER bits */
227
+	uint8_t valid;
228
+
229
+	/** Priority of this offer */
230
+	int8_t priority;
231
+
232
+	/** Whether to ignore PXE DHCP extensions */
233
+	uint8_t no_pxedhcp;
234
+};
235
+
236
+/** Maximum number of DHCP offers to queue */
237
+#define DHCP_MAX_OFFERS   6
238
+
209
 /** A DHCP session */
239
 /** A DHCP session */
210
 struct dhcp_session {
240
 struct dhcp_session {
211
 	/** Reference counter */
241
 	/** Reference counter */
222
 	/** State of the session */
252
 	/** State of the session */
223
 	struct dhcp_session_state *state;
253
 	struct dhcp_session_state *state;
224
 
254
 
225
-	/** Offered IP address */
226
-	struct in_addr offer;
227
-	/** DHCP server */
228
-	struct in_addr server;
229
-	/** DHCP offer priority */
230
-	int priority;
231
-
232
-	/** ProxyDHCP protocol extensions should be ignored */
233
-	int no_pxedhcp;
234
-	/** ProxyDHCP server */
235
-	struct in_addr proxy_server;
236
-	/** ProxyDHCP port */
237
-	uint16_t proxy_port;
238
-	/** ProxyDHCP server priority */
239
-	int proxy_priority;
240
-
241
 	/** PXE Boot Server type */
255
 	/** PXE Boot Server type */
242
 	uint16_t pxe_type;
256
 	uint16_t pxe_type;
243
 	/** List of PXE Boot Servers to attempt */
257
 	/** List of PXE Boot Servers to attempt */
249
 	struct retry_timer timer;
263
 	struct retry_timer timer;
250
 	/** Start time of the current state (in ticks) */
264
 	/** Start time of the current state (in ticks) */
251
 	unsigned long start;
265
 	unsigned long start;
266
+
267
+	/** DHCP offer just requested */
268
+	struct dhcp_offer *current_offer;
269
+	/** List of DHCP offers received */
270
+	struct dhcp_offer offers[DHCP_MAX_OFFERS];
252
 };
271
 };
253
 
272
 
254
 /**
273
 /**
259
 static void dhcp_free ( struct refcnt *refcnt ) {
278
 static void dhcp_free ( struct refcnt *refcnt ) {
260
 	struct dhcp_session *dhcp =
279
 	struct dhcp_session *dhcp =
261
 		container_of ( refcnt, struct dhcp_session, refcnt );
280
 		container_of ( refcnt, struct dhcp_session, refcnt );
281
+	int i;
282
+
283
+	for ( i = 0 ; i < DHCP_MAX_OFFERS ; i++ ) {
284
+		if ( dhcp->offers[i].pxe )
285
+			dhcppkt_put ( dhcp->offers[i].pxe );
286
+	}
262
 
287
 
263
 	netdev_put ( dhcp->netdev );
288
 	netdev_put ( dhcp->netdev );
264
 	free ( dhcp );
289
 	free ( dhcp );
303
 	start_timer_nodelay ( &dhcp->timer );
328
 	start_timer_nodelay ( &dhcp->timer );
304
 }
329
 }
305
 
330
 
331
+/**
332
+ * Determine next DHCP offer to try
333
+ *
334
+ * @v dhcp		DHCP session
335
+ * @v type		DHCP offer type
336
+ * @ret offer		Next DHCP offer to try
337
+ *
338
+ * Offers are ranked by priority, then by completeness (combined
339
+ * IP+PXE are tried before @a type alone), then by order of receipt.
340
+ */
341
+static struct dhcp_offer * dhcp_next_offer ( struct dhcp_session *dhcp,
342
+					     uint8_t type ) {
343
+
344
+	struct dhcp_offer *offer;
345
+	struct dhcp_offer *best = NULL;
346
+
347
+	for ( offer = dhcp->offers ; offer < dhcp->offers + DHCP_MAX_OFFERS ;
348
+	      offer++ ) {
349
+		if ( ( offer->valid & type ) &&
350
+		     ( ( best == NULL ) ||
351
+		       ( offer->priority > best->priority ) ||
352
+		       ( ( offer->priority == best->priority ) &&
353
+			 ( offer->valid & ~best->valid ) ) ) )
354
+			best = offer;
355
+	}
356
+
357
+	return best;
358
+}
359
+
306
 /****************************************************************************
360
 /****************************************************************************
307
  *
361
  *
308
  * DHCP state machine
362
  * DHCP state machine
330
 }
384
 }
331
 
385
 
332
 /**
386
 /**
333
- * Handle received packet during DHCP discovery
387
+ * Handle received DHCPOFFER during any state
334
  *
388
  *
335
  * @v dhcp		DHCP session
389
  * @v dhcp		DHCP session
336
  * @v dhcppkt		DHCP packet
390
  * @v dhcppkt		DHCP packet
338
  * @v msgtype		DHCP message type
392
  * @v msgtype		DHCP message type
339
  * @v server_id		DHCP server ID
393
  * @v server_id		DHCP server ID
340
  */
394
  */
341
-static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
395
+static void dhcp_rx_offer ( struct dhcp_session *dhcp,
342
-				struct dhcp_packet *dhcppkt,
396
+			    struct dhcp_packet *dhcppkt,
343
-				struct sockaddr_in *peer, uint8_t msgtype,
397
+			    struct sockaddr_in *peer, uint8_t msgtype,
344
-				struct in_addr server_id ) {
398
+			    struct in_addr server_id ) {
345
-	struct in_addr ip;
346
 	char vci[9]; /* "PXEClient" */
399
 	char vci[9]; /* "PXEClient" */
347
 	int vci_len;
400
 	int vci_len;
348
 	int has_pxeclient;
401
 	int has_pxeclient;
349
 	int pxeopts_len;
402
 	int pxeopts_len;
350
 	int has_pxeopts;
403
 	int has_pxeopts;
351
-	int8_t priority = 0;
404
+	struct dhcp_offer *offer;
352
-	uint8_t no_pxedhcp = 0;
405
+	int i;
353
-	unsigned long elapsed;
354
 
406
 
355
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
407
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
356
 	       dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
408
 	       dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
359
 		DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
411
 		DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
360
 
412
 
361
 	/* Identify offered IP address */
413
 	/* Identify offered IP address */
362
-	ip = dhcppkt->dhcphdr->yiaddr;
414
+	if ( dhcppkt->dhcphdr->yiaddr.s_addr )
363
-	if ( ip.s_addr )
415
+		DBGC ( dhcp, " for %s", inet_ntoa ( dhcppkt->dhcphdr->yiaddr ));
364
-		DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
416
+
417
+	/* Enqueue an offer to be filled in */
418
+	for ( i = 0 ; i < DHCP_MAX_OFFERS ; i++ ) {
419
+		if ( dhcp->offers[i].server.s_addr == server_id.s_addr ) {
420
+			DBGC ( dhcp, " dup\n" );
421
+			return;
422
+		}
423
+
424
+		if ( ! dhcp->offers[i].valid )
425
+			break;
426
+	}
427
+	if ( i == DHCP_MAX_OFFERS ) {
428
+		DBGC ( dhcp, " dropped\n" );
429
+		return;
430
+	}
431
+
432
+	offer = &dhcp->offers[i];
433
+	offer->server = server_id;
434
+	offer->ip = dhcppkt->dhcphdr->yiaddr;
365
 
435
 
366
 	/* Identify "PXEClient" vendor class */
436
 	/* Identify "PXEClient" vendor class */
367
 	vci_len = dhcppkt_fetch ( dhcppkt, DHCP_VENDOR_CLASS_ID,
437
 	vci_len = dhcppkt_fetch ( dhcppkt, DHCP_VENDOR_CLASS_ID,
375
 	if ( has_pxeclient )
445
 	if ( has_pxeclient )
376
 		DBGC ( dhcp, "%s", ( has_pxeopts ? " pxe" : " proxy" ) );
446
 		DBGC ( dhcp, "%s", ( has_pxeopts ? " pxe" : " proxy" ) );
377
 
447
 
448
+	if ( has_pxeclient && has_pxeopts ) {
449
+		/* Save reference to packet for future use */
450
+		if ( offer->pxe )
451
+			dhcppkt_put ( offer->pxe );
452
+		offer->pxe = dhcppkt_get ( dhcppkt );
453
+	}
454
+
378
 	/* Identify priority */
455
 	/* Identify priority */
379
-	dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority,
456
+	dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &offer->priority,
380
-			sizeof ( priority ) );
457
+			sizeof ( offer->priority ) );
381
-	if ( priority )
458
+	if ( offer->priority )
382
-		DBGC ( dhcp, " pri %d", priority );
459
+		DBGC ( dhcp, " pri %d", offer->priority );
383
 
460
 
384
 	/* Identify ignore-PXE flag */
461
 	/* Identify ignore-PXE flag */
385
-	dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &no_pxedhcp,
462
+	dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &offer->no_pxedhcp,
386
-			sizeof ( no_pxedhcp ) );
463
+			sizeof ( offer->no_pxedhcp ) );
387
-	if ( no_pxedhcp )
464
+	if ( offer->no_pxedhcp )
388
 		DBGC ( dhcp, " nopxe" );
465
 		DBGC ( dhcp, " nopxe" );
389
 	DBGC ( dhcp, "\n" );
466
 	DBGC ( dhcp, "\n" );
390
 
467
 
391
-	/* Select as DHCP offer, if applicable */
468
+	/* Determine roles this offer can fill */
392
-	if ( ip.s_addr && ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
469
+	if ( offer->ip.s_addr &&
393
-	     ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) &&
470
+	     ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
394
-	     ( priority >= dhcp->priority ) ) {
471
+	     ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) )
395
-		dhcp->offer = ip;
472
+		offer->valid |= DHCP_OFFER_IP;
396
-		dhcp->server = server_id;
397
-		dhcp->priority = priority;
398
-		dhcp->no_pxedhcp = no_pxedhcp;
399
-	}
400
 
473
 
401
-	/* Select as ProxyDHCP offer, if applicable */
474
+	if ( has_pxeclient && ( msgtype == DHCPOFFER ) )
402
-	if ( has_pxeclient && ( msgtype == DHCPOFFER ) &&
475
+		offer->valid |= DHCP_OFFER_PXE;
403
-	     ( priority >= dhcp->proxy_priority ) ) {
476
+}
404
-		/* If the offer already includes the PXE options, then
477
+
405
-		 * assume that we can send the ProxyDHCPREQUEST to
478
+/**
406
-		 * port 67 (since the DHCPDISCOVER that triggered this
479
+ * Handle received packet during DHCP discovery
407
-		 * ProxyDHCPOFFER was sent to port 67).  Otherwise,
480
+ *
408
-		 * send the ProxyDHCPREQUEST to port 4011.
481
+ * @v dhcp		DHCP session
409
-		 */
482
+ * @v dhcppkt		DHCP packet
410
-		dhcp->proxy_server = server_id;
483
+ * @v peer		DHCP server address
411
-		dhcp->proxy_port = ( has_pxeopts ? htons ( BOOTPS_PORT )
484
+ * @v msgtype		DHCP message type
412
-				     : htons ( PXE_PORT ) );
485
+ * @v server_id		DHCP server ID
413
-		dhcp->proxy_priority = priority;
486
+ */
414
-	}
487
+static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
488
+				struct dhcp_packet *dhcppkt,
489
+				struct sockaddr_in *peer, uint8_t msgtype,
490
+				struct in_addr server_id ) {
491
+	unsigned long elapsed;
492
+	struct dhcp_offer *ip_offer;
493
+
494
+	dhcp_rx_offer ( dhcp, dhcppkt, peer, msgtype, server_id );
415
 
495
 
416
 	/* We can exit the discovery state when we have a valid
496
 	/* We can exit the discovery state when we have a valid
417
 	 * DHCPOFFER, and either:
497
 	 * DHCPOFFER, and either:
422
 	 */
502
 	 */
423
 
503
 
424
 	/* If we don't yet have a DHCPOFFER, do nothing */
504
 	/* If we don't yet have a DHCPOFFER, do nothing */
425
-	if ( ! dhcp->offer.s_addr )
505
+	ip_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_IP );
506
+	if ( ! ip_offer )
426
 		return;
507
 		return;
427
 
508
 
428
 	/* If we can't yet transition to DHCPREQUEST, do nothing */
509
 	/* If we can't yet transition to DHCPREQUEST, do nothing */
429
 	elapsed = ( currticks() - dhcp->start );
510
 	elapsed = ( currticks() - dhcp->start );
430
-	if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_server.s_addr ||
511
+	if ( ! ( ip_offer->no_pxedhcp ||
512
+		 dhcp_next_offer ( dhcp, DHCP_OFFER_PXE ) ||
431
 		 ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) )
513
 		 ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) )
432
 		return;
514
 		return;
433
 
515
 
444
 	unsigned long elapsed = ( currticks() - dhcp->start );
526
 	unsigned long elapsed = ( currticks() - dhcp->start );
445
 
527
 
446
 	/* Give up waiting for ProxyDHCP before we reach the failure point */
528
 	/* Give up waiting for ProxyDHCP before we reach the failure point */
447
-	if ( dhcp->offer.s_addr && ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) {
529
+	if ( dhcp_next_offer ( dhcp, DHCP_OFFER_IP ) &&
530
+	     ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) {
448
 		dhcp_set_state ( dhcp, &dhcp_state_request );
531
 		dhcp_set_state ( dhcp, &dhcp_state_request );
449
 		return;
532
 		return;
450
 	}
533
 	}
474
 			     struct dhcp_packet *dhcppkt,
557
 			     struct dhcp_packet *dhcppkt,
475
 			     struct sockaddr_in *peer ) {
558
 			     struct sockaddr_in *peer ) {
476
 	int rc;
559
 	int rc;
560
+	struct dhcp_offer *offer;
561
+
562
+	offer = dhcp->current_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_IP );
477
 
563
 
478
 	DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d",
564
 	DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d",
479
-	       dhcp, inet_ntoa ( dhcp->server ), BOOTPS_PORT );
565
+	       dhcp, inet_ntoa ( offer->server ), BOOTPS_PORT );
480
-	DBGC ( dhcp, " for %s\n", inet_ntoa ( dhcp->offer ) );
566
+	DBGC ( dhcp, " for %s\n", inet_ntoa ( offer->ip ) );
481
 
567
 
482
 	/* Set server ID */
568
 	/* Set server ID */
483
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
569
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
484
-				    &dhcp->server,
570
+				    &offer->server,
485
-				    sizeof ( dhcp->server ) ) ) != 0 )
571
+				    sizeof ( offer->server ) ) ) != 0 )
486
 		return rc;
572
 		return rc;
487
 
573
 
488
 	/* Set requested IP address */
574
 	/* Set requested IP address */
489
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
575
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
490
-				    &dhcp->offer,
576
+				    &offer->ip, sizeof ( offer->ip ) ) ) != 0 )
491
-				    sizeof ( dhcp->offer ) ) ) != 0 )
492
 		return rc;
577
 		return rc;
493
 
578
 
494
 	/* Set server address */
579
 	/* Set server address */
514
 	struct in_addr ip;
599
 	struct in_addr ip;
515
 	struct settings *parent;
600
 	struct settings *parent;
516
 	int rc;
601
 	int rc;
602
+	struct dhcp_offer *pxe_offer;
603
+
604
+	if ( msgtype == DHCPOFFER ) {
605
+		dhcp_rx_offer ( dhcp, dhcppkt, peer, msgtype, server_id );
606
+		if ( dhcp_next_offer ( dhcp, DHCP_OFFER_IP ) !=
607
+		     dhcp->current_offer ) {
608
+			/* Restart due to higher-priority offer received */
609
+			DBGC ( dhcp, "DHCP %p re-requesting\n", dhcp );
610
+			dhcp_set_state ( dhcp, &dhcp_state_request );
611
+		}
612
+		return;
613
+	}
517
 
614
 
518
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
615
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
519
 	       dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
616
 	       dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
532
 		return;
629
 		return;
533
 	if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
630
 	if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
534
 		return;
631
 		return;
535
-	if ( server_id.s_addr != dhcp->server.s_addr )
632
+	if ( server_id.s_addr != dhcp->current_offer->server.s_addr )
536
 		return;
633
 		return;
537
 
634
 
538
 	/* Record assigned address */
635
 	/* Record assigned address */
547
 		return;
644
 		return;
548
 	}
645
 	}
549
 
646
 
550
-	/* Start ProxyDHCPREQUEST if applicable */
647
+	/* Locate best source of PXE settings */
551
-	if ( dhcp->proxy_server.s_addr /* Have ProxyDHCP server */ &&
648
+	pxe_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_PXE );
552
-	     ( ! dhcp->no_pxedhcp ) /* ProxyDHCP not disabled */ &&
649
+
553
-	     ( /* ProxyDHCP server is not just the DHCP server itself */
650
+	if ( ( ! pxe_offer ) || /* No PXE available */
554
-	       ( dhcp->proxy_server.s_addr != dhcp->server.s_addr ) ||
651
+	     /* IP offer instructs us to ignore PXE */
555
-	       ( dhcp->proxy_port != htons ( BOOTPS_PORT ) ) ) ) {
652
+	     dhcp->current_offer->no_pxedhcp ||
653
+	     /* PXE settings already registered with IP offer */
654
+	     ( ( dhcp->current_offer == pxe_offer ) && ( pxe_offer->pxe ) ) ) {
655
+
656
+		/* Terminate DHCP */
657
+		dhcp_finished ( dhcp, 0 );
658
+
659
+	} else if ( pxe_offer->pxe ) {
660
+		/* Register PXE settings and terminate DHCP */
661
+		pxe_offer->pxe->settings.name = PROXYDHCP_SETTINGS_NAME;
662
+		if ( ( rc = register_settings ( &pxe_offer->pxe->settings,
663
+						NULL ) ) != 0 ) {
664
+			DBGC ( dhcp, "DHCP %p could not register settings: "
665
+			       "%s\n", dhcp, strerror ( rc ) );
666
+		}
667
+		dhcp_finished ( dhcp, rc );
668
+	} else {
669
+		/* Start ProxyDHCP */
556
 		dhcp_set_state ( dhcp, &dhcp_state_proxy );
670
 		dhcp_set_state ( dhcp, &dhcp_state_proxy );
557
-		return;
558
 	}
671
 	}
559
-
560
-	/* Terminate DHCP */
561
-	dhcp_finished ( dhcp, 0 );
562
 }
672
 }
563
 
673
 
564
 /**
674
 /**
593
 			   struct dhcp_packet *dhcppkt,
703
 			   struct dhcp_packet *dhcppkt,
594
 			   struct sockaddr_in *peer ) {
704
 			   struct sockaddr_in *peer ) {
595
 	int rc;
705
 	int rc;
706
+	struct dhcp_offer *offer;
707
+
708
+	offer = dhcp->current_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_PXE );
596
 
709
 
597
 	DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s:%d\n", dhcp,
710
 	DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s:%d\n", dhcp,
598
-	       inet_ntoa ( dhcp->proxy_server ), ntohs ( dhcp->proxy_port ) );
711
+	       inet_ntoa ( offer->server ), PXE_PORT );
599
 
712
 
600
 	/* Set server ID */
713
 	/* Set server ID */
601
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
714
 	if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
602
-				    &dhcp->proxy_server,
715
+				    &offer->server,
603
-				    sizeof ( dhcp->proxy_server ) ) ) != 0 )
716
+				    sizeof ( offer->server ) ) )  != 0 )
604
 		return rc;
717
 		return rc;
605
 
718
 
606
 	/* Set server address */
719
 	/* Set server address */
607
-	peer->sin_addr = dhcp->proxy_server;
720
+	peer->sin_addr = offer->server;
608
-	peer->sin_port = dhcp->proxy_port;
721
+	peer->sin_port = htons ( PXE_PORT );
609
 
722
 
610
 	return 0;
723
 	return 0;
611
 }
724
 }
625
 			    struct in_addr server_id ) {
738
 			    struct in_addr server_id ) {
626
 	int rc;
739
 	int rc;
627
 
740
 
741
+	/* Enqueue last-minute DHCPOFFERs for use in case of failure */
742
+	if ( peer->sin_port == htons ( BOOTPS_PORT ) &&
743
+	     msgtype == DHCPOFFER ) {
744
+		dhcp_rx_offer ( dhcp, dhcppkt, peer, msgtype, server_id );
745
+		return;
746
+	}
747
+
628
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
748
 	DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
629
 	       dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
749
 	       dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
630
 	       ntohs ( peer->sin_port ) );
750
 	       ntohs ( peer->sin_port ) );
633
 	DBGC ( dhcp, "\n" );
753
 	DBGC ( dhcp, "\n" );
634
 
754
 
635
 	/* Filter out unacceptable responses */
755
 	/* Filter out unacceptable responses */
636
-	if ( peer->sin_port != dhcp->proxy_port )
756
+	if ( peer->sin_port != htons ( PXE_PORT ) )
637
 		return;
757
 		return;
638
-	if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) )
758
+	if ( msgtype != DHCPACK && msgtype != DHCPOFFER )
639
 		return;
759
 		return;
640
 	if ( server_id.s_addr /* Linux PXE server omits server ID */ &&
760
 	if ( server_id.s_addr /* Linux PXE server omits server ID */ &&
641
-	     ( server_id.s_addr != dhcp->proxy_server.s_addr ) )
761
+	     ( server_id.s_addr != dhcp->current_offer->server.s_addr ) )
642
 		return;
762
 		return;
643
 
763
 
644
 	/* Register settings */
764
 	/* Register settings */
664
 
784
 
665
 	/* Give up waiting for ProxyDHCP before we reach the failure point */
785
 	/* Give up waiting for ProxyDHCP before we reach the failure point */
666
 	if ( elapsed > PROXYDHCP_MAX_TIMEOUT ) {
786
 	if ( elapsed > PROXYDHCP_MAX_TIMEOUT ) {
787
+
788
+		/* Mark failed offer as unsuitable for ProxyDHCP */
789
+		dhcp->current_offer->valid &= ~DHCP_OFFER_PXE;
790
+
791
+		/* Prefer not to use only half of a PXE+IP offer if we
792
+		 * have other offers available
793
+		 */
794
+		dhcp->current_offer->priority = -1;
795
+
796
+		/* If we have any other PXE offers we can try, go back
797
+		 * to DHCPREQUEST (since they might not be proxied
798
+		 * offers, or might be coupled to a new IP address).
799
+		 * We should probably DHCPRELEASE our old IP, but the
800
+		 * standard does not require it.
801
+		 */
802
+		if ( dhcp_next_offer ( dhcp, DHCP_OFFER_PXE ) ) {
803
+			dhcp->local.sin_addr.s_addr = 0;
804
+			dhcp_set_state ( dhcp, &dhcp_state_request );
805
+			return;
806
+		}
807
+
808
+		/* No possibilities left; finish without PXE options */
667
 		dhcp_finished ( dhcp, 0 );
809
 		dhcp_finished ( dhcp, 0 );
668
 		return;
810
 		return;
669
 	}
811
 	}

Loading…
Cancel
Save