3 Copyright (c) 2006 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
25 IN PXE_DHCP4_PRIVATE_DATA
*Private
,
26 IN DHCP4_PACKET
*tx_pkt
,
27 IN DHCP4_PACKET
*rx_pkt
,
31 -2 = ignore, stop waiting
32 -1 = ignore, keep waiting
33 0 = accept, keep waiting
34 1 = accept, stop waiting
39 DHCP4_OP
*msg_type_op
;
44 // Verify parameters. Touch unused parameters to keep
50 if (Private
== NULL
|| rx_pkt
== NULL
) {
55 rx_pkt_size
= rx_pkt_size
;
58 // This may be a BOOTP Reply or DHCP Offer packet.
59 // If there is no DHCP magik number, assume that
60 // this is a BOOTP Reply packet.
62 magik
= htonl (DHCP4_MAGIK_NUMBER
);
64 while (!CompareMem (&rx_pkt
->dhcp4
.magik
, &magik
, 4)) {
66 // If there is no DHCP message type option, assume
67 // this is a BOOTP reply packet and cache it.
69 EfiStatus
= find_opt (rx_pkt
, DHCP4_MESSAGE_TYPE
, 0, &msg_type_op
);
71 if (EFI_ERROR (EfiStatus
)) {
75 // If there is a DHCP message type option, it must be a
78 if (msg_type_op
->len
!= 1) {
82 if (msg_type_op
->data
[0] != DHCP4_MESSAGE_TYPE_OFFER
) {
86 // There must be a server identifier option.
88 EfiStatus
= find_opt (
90 DHCP4_SERVER_IDENTIFIER
,
95 if (EFI_ERROR (EfiStatus
)) {
99 if (srvid_op
->len
!= 4) {
103 // Good DHCP offer packet.
108 // Good DHCP (or BOOTP) packet. Cache it!
110 EfiStatus
= gBS
->AllocatePool (
112 (Private
->offers
+ 1) * sizeof (DHCP4_PACKET
),
116 if (EFI_ERROR (EfiStatus
)) {
122 if (Private
->offers
!= 0) {
126 Private
->offers
* sizeof (DHCP4_PACKET
)
129 gBS
->FreePool (Private
->offer_list
);
132 CopyMem (&tmp
[Private
->offers
++], rx_pkt
, sizeof (DHCP4_PACKET
));
134 Private
->offer_list
= tmp
;
139 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
143 IN PXE_DHCP4_PRIVATE_DATA
*Private
,
144 IN DHCP4_PACKET
*tx_pkt
,
145 IN DHCP4_PACKET
*rx_pkt
,
149 -2 = ignore, stop waiting
150 -1 = ignore, keep waiting
151 0 = accept, keep waiting
152 1 = accept, stop waiting
155 EFI_STATUS EfiStatus
;
156 DHCP4_OP
*msg_type_op
;
160 DHCP4_OP
*lease_time_op
;
164 // Verify parameters. Touch unused parameters to
165 // keep compiler happy.
170 if (Private
== NULL
|| rx_pkt
== NULL
) {
175 rx_pkt_size
= rx_pkt_size
;
178 // This must be a DHCP Ack message.
180 magik
= htonl (DHCP4_MAGIK_NUMBER
);
182 if (CompareMem (&rx_pkt
->dhcp4
.magik
, &magik
, 4)) {
186 EfiStatus
= find_opt (rx_pkt
, DHCP4_MESSAGE_TYPE
, 0, &msg_type_op
);
188 if (EFI_ERROR (EfiStatus
)) {
192 if (msg_type_op
->len
!= 1) {
196 if (msg_type_op
->data
[0] != DHCP4_MESSAGE_TYPE_ACK
) {
200 // There must be a server identifier.
202 EfiStatus
= find_opt (rx_pkt
, DHCP4_SERVER_IDENTIFIER
, 0, &srvid_op
);
204 if (EFI_ERROR (EfiStatus
)) {
208 if (srvid_op
->len
!= 4) {
212 // There should be a renewal time.
213 // If there is not, we will default to the 7/8 of the rebinding time.
215 EfiStatus
= find_opt (rx_pkt
, DHCP4_RENEWAL_TIME
, 0, &renew_op
);
217 if (EFI_ERROR (EfiStatus
)) {
219 } else if (renew_op
->len
!= 4) {
223 // There should be a rebinding time.
224 // If there is not, we will default to 7/8 of the lease time.
226 EfiStatus
= find_opt (rx_pkt
, DHCP4_REBINDING_TIME
, 0, &rebind_op
);
228 if (EFI_ERROR (EfiStatus
)) {
230 } else if (rebind_op
->len
!= 4) {
234 // There should be a lease time.
235 // If there is not, we will default to one week.
237 EfiStatus
= find_opt (rx_pkt
, DHCP4_LEASE_TIME
, 0, &lease_time_op
);
239 if (EFI_ERROR (EfiStatus
)) {
240 lease_time_op
= NULL
;
241 } else if (lease_time_op
->len
!= 4) {
242 lease_time_op
= NULL
;
245 // Packet looks good. Double check the renew, rebind and lease times.
247 CopyMem (&Private
->ServerIp
, srvid_op
->data
, 4);
249 if (renew_op
!= NULL
) {
250 CopyMem (&Private
->RenewTime
, renew_op
->data
, 4);
251 Private
->RenewTime
= htonl (Private
->RenewTime
);
253 Private
->RenewTime
= 0;
256 if (rebind_op
!= NULL
) {
257 CopyMem (&Private
->RebindTime
, rebind_op
->data
, 4);
258 Private
->RebindTime
= htonl (Private
->RebindTime
);
260 Private
->RebindTime
= 0;
263 if (lease_time_op
!= NULL
) {
264 CopyMem (&Private
->LeaseTime
, lease_time_op
->data
, 4);
265 Private
->LeaseTime
= htonl (Private
->LeaseTime
);
267 Private
->LeaseTime
= 0;
270 if (Private
->LeaseTime
< 60) {
271 Private
->LeaseTime
= 7 * 86400;
274 if (Private
->RebindTime
< 52 || Private
->RebindTime
>= Private
->LeaseTime
) {
275 Private
->RebindTime
= Private
->LeaseTime
/ 2 + Private
->LeaseTime
/ 4 + Private
->LeaseTime
/ 8;
278 if (Private
->RenewTime
< 45 || Private
->RenewTime
>= Private
->RebindTime
) {
279 Private
->RenewTime
= Private
->RebindTime
/ 2 + Private
->RebindTime
/ 4 + Private
->RebindTime
/ 8;
285 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
289 IN EFI_PXE_DHCP4_PROTOCOL
*This
,
290 IN UINTN seconds_timeout
,
292 OUT DHCP4_PACKET
**OfferList
295 PXE_DHCP4_PRIVATE_DATA
*Private
;
297 EFI_IP_ADDRESS bcast_ip
;
298 EFI_STATUS EfiStatus
;
301 // Verify parameters and protocol state.
304 seconds_timeout
< DHCP4_MIN_SECONDS
||
305 seconds_timeout
> DHCP4_MAX_SECONDS
||
310 // Return parameters are not initialized when
311 // parameters are invalid!
313 return EFI_INVALID_PARAMETER
;
320 // Check protocol state.
322 if (This
->Data
== NULL
) {
323 return EFI_NOT_STARTED
;
326 if (!This
->Data
->SetupCompleted
) {
327 return EFI_NOT_READY
;
331 // Get pointer to our instance data.
333 Private
= PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This
);
335 if (Private
== NULL
) {
336 return EFI_INVALID_PARAMETER
;
339 if (Private
->PxeBc
== NULL
) {
340 return EFI_DEVICE_ERROR
;
343 // Setup variables...
346 Private
->offer_list
= NULL
;
348 EfiStatus
= gBS
->HandleProtocol (
350 &gEfiPxeDhcp4CallbackProtocolGuid
,
351 (VOID
*) &Private
->callback
354 if (EFI_ERROR (EfiStatus
)) {
355 Private
->callback
= NULL
;
358 Private
->function
= EFI_PXE_DHCP4_FUNCTION_INIT
;
361 // Increment the transaction ID.
366 CopyMem (&xid
, &This
->Data
->Discover
.dhcp4
.xid
, sizeof (UINT32
));
368 xid
= htonl (htonl (xid
) + 1);
370 CopyMem (&This
->Data
->Discover
.dhcp4
.xid
, &xid
, sizeof (UINT32
));
373 // Transmit discover and wait for offers...
375 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
377 EfiStatus
= tx_rx_udp (
383 &This
->Data
->Discover
,
389 if (EFI_ERROR (EfiStatus
)) {
390 if (Private
->offer_list
) {
391 gBS
->FreePool (Private
->offer_list
);
395 Private
->offer_list
= NULL
;
396 Private
->callback
= NULL
;
398 DEBUG ((EFI_D_ERROR
, "%r\n", EfiStatus
));
402 *Offers
= Private
->offers
;
403 *OfferList
= Private
->offer_list
;
406 Private
->offer_list
= NULL
;
407 Private
->callback
= NULL
;
409 This
->Data
->InitCompleted
= TRUE
;
410 This
->Data
->SelectCompleted
= FALSE
;
411 This
->Data
->IsBootp
= FALSE
;
412 This
->Data
->IsAck
= FALSE
;
417 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
421 IN EFI_PXE_DHCP4_PROTOCOL
*This
,
422 IN UINTN seconds_timeout
,
423 IN DHCP4_PACKET
*Offer
426 PXE_DHCP4_PRIVATE_DATA
*Private
;
427 EFI_STATUS EfiStatus
;
428 DHCP4_PACKET request
;
430 EFI_IP_ADDRESS bcast_ip
;
431 EFI_IP_ADDRESS zero_ip
;
432 EFI_IP_ADDRESS local_ip
;
440 // Verify parameters.
442 if (This
== NULL
|| seconds_timeout
< DHCP4_MIN_SECONDS
|| seconds_timeout
> DHCP4_MAX_SECONDS
|| Offer
== NULL
) {
443 return EFI_INVALID_PARAMETER
;
446 // Check protocol state.
448 if (This
->Data
== NULL
) {
449 return EFI_NOT_STARTED
;
452 if (!This
->Data
->SetupCompleted
) {
453 return EFI_NOT_READY
;
456 // Get pointer to instance data.
458 Private
= PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This
);
460 if (Private
== NULL
) {
461 return EFI_INVALID_PARAMETER
;
464 if (Private
->PxeBc
== NULL
) {
465 return EFI_DEVICE_ERROR
;
469 // Setup useful variables...
471 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
473 ZeroMem (&zero_ip
, sizeof (EFI_IP_ADDRESS
));
475 ZeroMem (&local_ip
, sizeof (EFI_IP_ADDRESS
));
476 local_ip
.v4
.Addr
[0] = 127;
477 local_ip
.v4
.Addr
[3] = 1;
479 This
->Data
->SelectCompleted
= FALSE
;
480 This
->Data
->IsBootp
= FALSE
;
481 This
->Data
->IsAck
= FALSE
;
483 EfiStatus
= gBS
->HandleProtocol (
485 &gEfiPxeDhcp4CallbackProtocolGuid
,
486 (VOID
*) &Private
->callback
489 if (EFI_ERROR (EfiStatus
)) {
490 Private
->callback
= NULL
;
493 Private
->function
= EFI_PXE_DHCP4_FUNCTION_SELECT
;
496 // Verify offer packet fields.
498 if (Offer
->dhcp4
.op
!= BOOTP_REPLY
) {
499 Private
->callback
= NULL
;
500 return EFI_INVALID_PARAMETER
;
503 if (Offer
->dhcp4
.htype
!= This
->Data
->Discover
.dhcp4
.htype
) {
504 Private
->callback
= NULL
;
505 return EFI_INVALID_PARAMETER
;
508 if (Offer
->dhcp4
.hlen
!= This
->Data
->Discover
.dhcp4
.hlen
) {
509 Private
->callback
= NULL
;
510 return EFI_INVALID_PARAMETER
;
513 if (CompareMem (&Offer
->dhcp4
.xid
, &This
->Data
->Discover
.dhcp4
.xid
, 4)) {
514 Private
->callback
= NULL
;
515 return EFI_INVALID_PARAMETER
;
518 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &bcast_ip
, 4)) {
519 Private
->callback
= NULL
;
520 return EFI_INVALID_PARAMETER
;
523 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &zero_ip
, 4)) {
524 Private
->callback
= NULL
;
525 return EFI_INVALID_PARAMETER
;
528 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &local_ip
, 4)) {
529 Private
->callback
= NULL
;
530 return EFI_INVALID_PARAMETER
;
534 &Offer
->dhcp4
.chaddr
,
535 &This
->Data
->Discover
.dhcp4
.chaddr
,
538 Private
->callback
= NULL
;
539 return EFI_INVALID_PARAMETER
;
542 // DHCP option checks
544 dhcp4_magik
= htonl (DHCP4_MAGIK_NUMBER
);
547 if (!CompareMem (&Offer
->dhcp4
.magik
, &dhcp4_magik
, 4)) {
549 // If present, DHCP message type must be offer.
551 EfiStatus
= find_opt (Offer
, DHCP4_MESSAGE_TYPE
, 0, &op
);
553 if (!EFI_ERROR (EfiStatus
)) {
554 if (op
->len
!= 1 || op
->data
[0] != DHCP4_MESSAGE_TYPE_OFFER
) {
555 Private
->callback
= NULL
;
556 return EFI_INVALID_PARAMETER
;
562 // If present, DHCP max message size must be valid.
564 EfiStatus
= find_opt (Offer
, DHCP4_MAX_MESSAGE_SIZE
, 0, &op
);
566 if (!EFI_ERROR (EfiStatus
)) {
567 if (op
->len
!= 2 || ((op
->data
[0] << 8) | op
->data
[1]) < DHCP4_DEFAULT_MAX_MESSAGE_SIZE
) {
568 Private
->callback
= NULL
;
569 return EFI_INVALID_PARAMETER
;
573 // If present, DHCP server identifier must be valid.
575 EfiStatus
= find_opt (Offer
, DHCP4_SERVER_IDENTIFIER
, 0, &op
);
577 if (!EFI_ERROR (EfiStatus
)) {
578 if (op
->len
!= 4 || !CompareMem (op
->data
, &bcast_ip
, 4) || !CompareMem (op
->data
, &zero_ip
, 4)) {
579 Private
->callback
= NULL
;
580 return EFI_INVALID_PARAMETER
;
584 // If present, DHCP subnet mask must be valid.
586 EfiStatus
= find_opt (
593 if (!EFI_ERROR (EfiStatus
)) {
595 Private
->callback
= NULL
;
596 return EFI_INVALID_PARAMETER
;
601 // Early out for BOOTP.
603 This
->Data
->IsBootp
= is_bootp
;
606 // Copy offer packet to instance data.
608 CopyMem (&This
->Data
->Offer
, Offer
, sizeof (DHCP4_PACKET
));
611 // Copy discover to request and offer to acknak.
614 &This
->Data
->Request
,
615 &This
->Data
->Discover
,
616 sizeof (DHCP4_PACKET
)
622 sizeof (DHCP4_PACKET
)
628 This
->Data
->SelectCompleted
= TRUE
;
629 This
->Data
->IsAck
= TRUE
;
631 Private
->callback
= NULL
;
635 // Copy discover packet contents to request packet.
637 CopyMem (&request
, &This
->Data
->Discover
, sizeof (DHCP4_PACKET
));
639 This
->Data
->IsAck
= FALSE
;
642 // Change DHCP message type from discover to request.
644 EfiStatus
= find_opt (&request
, DHCP4_MESSAGE_TYPE
, 0, &op
);
646 if (EFI_ERROR (EfiStatus
) && EfiStatus
!= EFI_NOT_FOUND
) {
647 Private
->callback
= NULL
;
648 return EFI_INVALID_PARAMETER
;
651 if (EfiStatus
== EFI_NOT_FOUND
) {
652 EfiStatus
= find_opt (&request
, DHCP4_END
, 0, &op
);
654 if (EFI_ERROR (EfiStatus
)) {
655 Private
->callback
= NULL
;
656 return EFI_INVALID_PARAMETER
;
659 op
->op
= DHCP4_MESSAGE_TYPE
;
662 op
->data
[1] = DHCP4_END
;
665 op
->data
[0] = DHCP4_MESSAGE_TYPE_REQUEST
;
668 // Copy server identifier option from offer to request.
670 EfiStatus
= find_opt (Offer
, DHCP4_SERVER_IDENTIFIER
, 0, &srvid
);
672 if (EFI_ERROR (EfiStatus
)) {
673 Private
->callback
= NULL
;
674 return EFI_INVALID_PARAMETER
;
677 if (srvid
->len
!= 4) {
678 Private
->callback
= NULL
;
679 return EFI_INVALID_PARAMETER
;
682 EfiStatus
= add_opt (&request
, srvid
);
684 if (EFI_ERROR (EfiStatus
)) {
685 DEBUG ((EFI_D_ERROR
, "%r\n", EfiStatus
));
686 Private
->callback
= NULL
;
690 // Add requested IP address option to request packet.
692 op
= (DHCP4_OP
*) buf
;
693 op
->op
= DHCP4_REQUESTED_IP_ADDRESS
;
695 CopyMem (op
->data
, &Offer
->dhcp4
.yiaddr
, 4);
697 EfiStatus
= add_opt (&request
, op
);
699 if (EFI_ERROR (EfiStatus
)) {
700 DEBUG ((EFI_D_ERROR
, "%r\n", EfiStatus
));
701 Private
->callback
= NULL
;
705 // Transimit DHCP request and wait for DHCP ack...
707 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
709 EfiStatus
= tx_rx_udp (
721 if (EFI_ERROR (EfiStatus
)) {
722 DEBUG ((EFI_D_ERROR
, "%r\n", EfiStatus
));
723 Private
->callback
= NULL
;
727 // Set Data->IsAck and return.
729 EfiStatus
= find_opt (&acknak
, DHCP4_MESSAGE_TYPE
, 0, &op
);
731 if (EFI_ERROR (EfiStatus
)) {
732 Private
->callback
= NULL
;
733 return EFI_DEVICE_ERROR
;
737 Private
->callback
= NULL
;
738 return EFI_DEVICE_ERROR
;
741 switch (op
->data
[0]) {
742 case DHCP4_MESSAGE_TYPE_ACK
:
743 This
->Data
->IsAck
= TRUE
;
746 case DHCP4_MESSAGE_TYPE_NAK
:
747 This
->Data
->IsAck
= FALSE
;
751 Private
->callback
= NULL
;
752 return EFI_DEVICE_ERROR
;
755 // Copy packets into instance data...
757 CopyMem (&This
->Data
->Offer
, Offer
, sizeof (DHCP4_PACKET
));
758 CopyMem (&This
->Data
->Request
, &request
, sizeof (DHCP4_PACKET
));
759 CopyMem (&This
->Data
->AckNak
, &acknak
, sizeof (DHCP4_PACKET
));
761 This
->Data
->SelectCompleted
= TRUE
;
763 Private
->callback
= NULL
;
767 /* eof - PxeDhcp4InitSelect.c */