|
@@ -206,6 +206,36 @@ static struct dhcp_session_state dhcp_state_request;
|
206
|
206
|
static struct dhcp_session_state dhcp_state_proxy;
|
207
|
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
|
239
|
/** A DHCP session */
|
210
|
240
|
struct dhcp_session {
|
211
|
241
|
/** Reference counter */
|
|
@@ -222,22 +252,6 @@ struct dhcp_session {
|
222
|
252
|
/** State of the session */
|
223
|
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
|
255
|
/** PXE Boot Server type */
|
242
|
256
|
uint16_t pxe_type;
|
243
|
257
|
/** List of PXE Boot Servers to attempt */
|
|
@@ -249,6 +263,11 @@ struct dhcp_session {
|
249
|
263
|
struct retry_timer timer;
|
250
|
264
|
/** Start time of the current state (in ticks) */
|
251
|
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,6 +278,12 @@ struct dhcp_session {
|
259
|
278
|
static void dhcp_free ( struct refcnt *refcnt ) {
|
260
|
279
|
struct dhcp_session *dhcp =
|
261
|
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
|
288
|
netdev_put ( dhcp->netdev );
|
264
|
289
|
free ( dhcp );
|
|
@@ -303,6 +328,35 @@ static void dhcp_set_state ( struct dhcp_session *dhcp,
|
303
|
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
|
362
|
* DHCP state machine
|
|
@@ -330,7 +384,7 @@ static int dhcp_discovery_tx ( struct dhcp_session *dhcp,
|
330
|
384
|
}
|
331
|
385
|
|
332
|
386
|
/**
|
333
|
|
- * Handle received packet during DHCP discovery
|
|
387
|
+ * Handle received DHCPOFFER during any state
|
334
|
388
|
*
|
335
|
389
|
* @v dhcp DHCP session
|
336
|
390
|
* @v dhcppkt DHCP packet
|
|
@@ -338,19 +392,17 @@ static int dhcp_discovery_tx ( struct dhcp_session *dhcp,
|
338
|
392
|
* @v msgtype DHCP message type
|
339
|
393
|
* @v server_id DHCP server ID
|
340
|
394
|
*/
|
341
|
|
-static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
|
342
|
|
- struct dhcp_packet *dhcppkt,
|
343
|
|
- struct sockaddr_in *peer, uint8_t msgtype,
|
344
|
|
- struct in_addr server_id ) {
|
345
|
|
- struct in_addr ip;
|
|
395
|
+static void dhcp_rx_offer ( struct dhcp_session *dhcp,
|
|
396
|
+ struct dhcp_packet *dhcppkt,
|
|
397
|
+ struct sockaddr_in *peer, uint8_t msgtype,
|
|
398
|
+ struct in_addr server_id ) {
|
346
|
399
|
char vci[9]; /* "PXEClient" */
|
347
|
400
|
int vci_len;
|
348
|
401
|
int has_pxeclient;
|
349
|
402
|
int pxeopts_len;
|
350
|
403
|
int has_pxeopts;
|
351
|
|
- int8_t priority = 0;
|
352
|
|
- uint8_t no_pxedhcp = 0;
|
353
|
|
- unsigned long elapsed;
|
|
404
|
+ struct dhcp_offer *offer;
|
|
405
|
+ int i;
|
354
|
406
|
|
355
|
407
|
DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
|
356
|
408
|
dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
|
|
@@ -359,9 +411,27 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
|
359
|
411
|
DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
|
360
|
412
|
|
361
|
413
|
/* Identify offered IP address */
|
362
|
|
- ip = dhcppkt->dhcphdr->yiaddr;
|
363
|
|
- if ( ip.s_addr )
|
364
|
|
- DBGC ( dhcp, " for %s", inet_ntoa ( ip ) );
|
|
414
|
+ if ( dhcppkt->dhcphdr->yiaddr.s_addr )
|
|
415
|
+ DBGC ( dhcp, " for %s", inet_ntoa ( dhcppkt->dhcphdr->yiaddr ));
|
|
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
|
436
|
/* Identify "PXEClient" vendor class */
|
367
|
437
|
vci_len = dhcppkt_fetch ( dhcppkt, DHCP_VENDOR_CLASS_ID,
|
|
@@ -375,43 +445,53 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
|
375
|
445
|
if ( has_pxeclient )
|
376
|
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
|
455
|
/* Identify priority */
|
379
|
|
- dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority,
|
380
|
|
- sizeof ( priority ) );
|
381
|
|
- if ( priority )
|
382
|
|
- DBGC ( dhcp, " pri %d", priority );
|
|
456
|
+ dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &offer->priority,
|
|
457
|
+ sizeof ( offer->priority ) );
|
|
458
|
+ if ( offer->priority )
|
|
459
|
+ DBGC ( dhcp, " pri %d", offer->priority );
|
383
|
460
|
|
384
|
461
|
/* Identify ignore-PXE flag */
|
385
|
|
- dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &no_pxedhcp,
|
386
|
|
- sizeof ( no_pxedhcp ) );
|
387
|
|
- if ( no_pxedhcp )
|
|
462
|
+ dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &offer->no_pxedhcp,
|
|
463
|
+ sizeof ( offer->no_pxedhcp ) );
|
|
464
|
+ if ( offer->no_pxedhcp )
|
388
|
465
|
DBGC ( dhcp, " nopxe" );
|
389
|
466
|
DBGC ( dhcp, "\n" );
|
390
|
467
|
|
391
|
|
- /* Select as DHCP offer, if applicable */
|
392
|
|
- if ( ip.s_addr && ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
|
393
|
|
- ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) &&
|
394
|
|
- ( priority >= dhcp->priority ) ) {
|
395
|
|
- dhcp->offer = ip;
|
396
|
|
- dhcp->server = server_id;
|
397
|
|
- dhcp->priority = priority;
|
398
|
|
- dhcp->no_pxedhcp = no_pxedhcp;
|
399
|
|
- }
|
|
468
|
+ /* Determine roles this offer can fill */
|
|
469
|
+ if ( offer->ip.s_addr &&
|
|
470
|
+ ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
|
|
471
|
+ ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) )
|
|
472
|
+ offer->valid |= DHCP_OFFER_IP;
|
400
|
473
|
|
401
|
|
- /* Select as ProxyDHCP offer, if applicable */
|
402
|
|
- if ( has_pxeclient && ( msgtype == DHCPOFFER ) &&
|
403
|
|
- ( priority >= dhcp->proxy_priority ) ) {
|
404
|
|
- /* If the offer already includes the PXE options, then
|
405
|
|
- * assume that we can send the ProxyDHCPREQUEST to
|
406
|
|
- * port 67 (since the DHCPDISCOVER that triggered this
|
407
|
|
- * ProxyDHCPOFFER was sent to port 67). Otherwise,
|
408
|
|
- * send the ProxyDHCPREQUEST to port 4011.
|
409
|
|
- */
|
410
|
|
- dhcp->proxy_server = server_id;
|
411
|
|
- dhcp->proxy_port = ( has_pxeopts ? htons ( BOOTPS_PORT )
|
412
|
|
- : htons ( PXE_PORT ) );
|
413
|
|
- dhcp->proxy_priority = priority;
|
414
|
|
- }
|
|
474
|
+ if ( has_pxeclient && ( msgtype == DHCPOFFER ) )
|
|
475
|
+ offer->valid |= DHCP_OFFER_PXE;
|
|
476
|
+}
|
|
477
|
+
|
|
478
|
+/**
|
|
479
|
+ * Handle received packet during DHCP discovery
|
|
480
|
+ *
|
|
481
|
+ * @v dhcp DHCP session
|
|
482
|
+ * @v dhcppkt DHCP packet
|
|
483
|
+ * @v peer DHCP server address
|
|
484
|
+ * @v msgtype DHCP message type
|
|
485
|
+ * @v server_id DHCP server ID
|
|
486
|
+ */
|
|
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
|
496
|
/* We can exit the discovery state when we have a valid
|
417
|
497
|
* DHCPOFFER, and either:
|
|
@@ -422,12 +502,14 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp,
|
422
|
502
|
*/
|
423
|
503
|
|
424
|
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
|
507
|
return;
|
427
|
508
|
|
428
|
509
|
/* If we can't yet transition to DHCPREQUEST, do nothing */
|
429
|
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
|
513
|
( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) )
|
432
|
514
|
return;
|
433
|
515
|
|
|
@@ -444,7 +526,8 @@ static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) {
|
444
|
526
|
unsigned long elapsed = ( currticks() - dhcp->start );
|
445
|
527
|
|
446
|
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
|
531
|
dhcp_set_state ( dhcp, &dhcp_state_request );
|
449
|
532
|
return;
|
450
|
533
|
}
|
|
@@ -474,21 +557,23 @@ static int dhcp_request_tx ( struct dhcp_session *dhcp,
|
474
|
557
|
struct dhcp_packet *dhcppkt,
|
475
|
558
|
struct sockaddr_in *peer ) {
|
476
|
559
|
int rc;
|
|
560
|
+ struct dhcp_offer *offer;
|
|
561
|
+
|
|
562
|
+ offer = dhcp->current_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_IP );
|
477
|
563
|
|
478
|
564
|
DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d",
|
479
|
|
- dhcp, inet_ntoa ( dhcp->server ), BOOTPS_PORT );
|
480
|
|
- DBGC ( dhcp, " for %s\n", inet_ntoa ( dhcp->offer ) );
|
|
565
|
+ dhcp, inet_ntoa ( offer->server ), BOOTPS_PORT );
|
|
566
|
+ DBGC ( dhcp, " for %s\n", inet_ntoa ( offer->ip ) );
|
481
|
567
|
|
482
|
568
|
/* Set server ID */
|
483
|
569
|
if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
|
484
|
|
- &dhcp->server,
|
485
|
|
- sizeof ( dhcp->server ) ) ) != 0 )
|
|
570
|
+ &offer->server,
|
|
571
|
+ sizeof ( offer->server ) ) ) != 0 )
|
486
|
572
|
return rc;
|
487
|
573
|
|
488
|
574
|
/* Set requested IP address */
|
489
|
575
|
if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
|
490
|
|
- &dhcp->offer,
|
491
|
|
- sizeof ( dhcp->offer ) ) ) != 0 )
|
|
576
|
+ &offer->ip, sizeof ( offer->ip ) ) ) != 0 )
|
492
|
577
|
return rc;
|
493
|
578
|
|
494
|
579
|
/* Set server address */
|
|
@@ -514,6 +599,18 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
|
514
|
599
|
struct in_addr ip;
|
515
|
600
|
struct settings *parent;
|
516
|
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
|
615
|
DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
|
519
|
616
|
dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
|
|
@@ -532,7 +629,7 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
|
532
|
629
|
return;
|
533
|
630
|
if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
|
534
|
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
|
633
|
return;
|
537
|
634
|
|
538
|
635
|
/* Record assigned address */
|
|
@@ -547,18 +644,31 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
|
547
|
644
|
return;
|
548
|
645
|
}
|
549
|
646
|
|
550
|
|
- /* Start ProxyDHCPREQUEST if applicable */
|
551
|
|
- if ( dhcp->proxy_server.s_addr /* Have ProxyDHCP server */ &&
|
552
|
|
- ( ! dhcp->no_pxedhcp ) /* ProxyDHCP not disabled */ &&
|
553
|
|
- ( /* ProxyDHCP server is not just the DHCP server itself */
|
554
|
|
- ( dhcp->proxy_server.s_addr != dhcp->server.s_addr ) ||
|
555
|
|
- ( dhcp->proxy_port != htons ( BOOTPS_PORT ) ) ) ) {
|
|
647
|
+ /* Locate best source of PXE settings */
|
|
648
|
+ pxe_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_PXE );
|
|
649
|
+
|
|
650
|
+ if ( ( ! pxe_offer ) || /* No PXE available */
|
|
651
|
+ /* IP offer instructs us to ignore PXE */
|
|
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
|
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,19 +703,22 @@ static int dhcp_proxy_tx ( struct dhcp_session *dhcp,
|
593
|
703
|
struct dhcp_packet *dhcppkt,
|
594
|
704
|
struct sockaddr_in *peer ) {
|
595
|
705
|
int rc;
|
|
706
|
+ struct dhcp_offer *offer;
|
|
707
|
+
|
|
708
|
+ offer = dhcp->current_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_PXE );
|
596
|
709
|
|
597
|
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
|
713
|
/* Set server ID */
|
601
|
714
|
if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
|
602
|
|
- &dhcp->proxy_server,
|
603
|
|
- sizeof ( dhcp->proxy_server ) ) ) != 0 )
|
|
715
|
+ &offer->server,
|
|
716
|
+ sizeof ( offer->server ) ) ) != 0 )
|
604
|
717
|
return rc;
|
605
|
718
|
|
606
|
719
|
/* Set server address */
|
607
|
|
- peer->sin_addr = dhcp->proxy_server;
|
608
|
|
- peer->sin_port = dhcp->proxy_port;
|
|
720
|
+ peer->sin_addr = offer->server;
|
|
721
|
+ peer->sin_port = htons ( PXE_PORT );
|
609
|
722
|
|
610
|
723
|
return 0;
|
611
|
724
|
}
|
|
@@ -625,6 +738,13 @@ static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
|
625
|
738
|
struct in_addr server_id ) {
|
626
|
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
|
748
|
DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp,
|
629
|
749
|
dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ),
|
630
|
750
|
ntohs ( peer->sin_port ) );
|
|
@@ -633,12 +753,12 @@ static void dhcp_proxy_rx ( struct dhcp_session *dhcp,
|
633
|
753
|
DBGC ( dhcp, "\n" );
|
634
|
754
|
|
635
|
755
|
/* Filter out unacceptable responses */
|
636
|
|
- if ( peer->sin_port != dhcp->proxy_port )
|
|
756
|
+ if ( peer->sin_port != htons ( PXE_PORT ) )
|
637
|
757
|
return;
|
638
|
|
- if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) )
|
|
758
|
+ if ( msgtype != DHCPACK && msgtype != DHCPOFFER )
|
639
|
759
|
return;
|
640
|
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
|
762
|
return;
|
643
|
763
|
|
644
|
764
|
/* Register settings */
|
|
@@ -664,6 +784,28 @@ static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) {
|
664
|
784
|
|
665
|
785
|
/* Give up waiting for ProxyDHCP before we reach the failure point */
|
666
|
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
|
809
|
dhcp_finished ( dhcp, 0 );
|
668
|
810
|
return;
|
669
|
811
|
}
|