3 Copyright (c) 2006, 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.
24 // #define DebugPrint(x) Aprint x
26 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
30 IN PXE_DHCP4_PRIVATE_DATA
*Private
,
31 IN DHCP4_PACKET
*tx_pkt
,
32 IN DHCP4_PACKET
*rx_pkt
,
36 -2 = ignore, stop waiting
37 -1 = ignore, keep waiting
38 0 = accept, keep waiting
39 1 = accept, stop waiting
44 DHCP4_OP
*msg_type_op
;
49 // Verify parameters. Touch unused parameters to keep
55 if (Private
== NULL
|| rx_pkt
== NULL
) {
60 rx_pkt_size
= rx_pkt_size
;
63 // This may be a BOOTP Reply or DHCP Offer packet.
64 // If there is no DHCP magik number, assume that
65 // this is a BOOTP Reply packet.
67 magik
= htonl (DHCP4_MAGIK_NUMBER
);
69 while (!CompareMem (&rx_pkt
->dhcp4
.magik
, &magik
, 4)) {
71 // If there is no DHCP message type option, assume
72 // this is a BOOTP reply packet and cache it.
74 EfiStatus
= find_opt (rx_pkt
, DHCP4_MESSAGE_TYPE
, 0, &msg_type_op
);
76 if (EFI_ERROR (EfiStatus
)) {
80 // If there is a DHCP message type option, it must be a
83 if (msg_type_op
->len
!= 1) {
87 if (msg_type_op
->data
[0] != DHCP4_MESSAGE_TYPE_OFFER
) {
91 // There must be a server identifier option.
93 EfiStatus
= find_opt (
95 DHCP4_SERVER_IDENTIFIER
,
100 if (EFI_ERROR (EfiStatus
)) {
104 if (srvid_op
->len
!= 4) {
108 // Good DHCP offer packet.
113 // Good DHCP (or BOOTP) packet. Cache it!
115 EfiStatus
= gBS
->AllocatePool (
117 (Private
->offers
+ 1) * sizeof (DHCP4_PACKET
),
121 if (EFI_ERROR (EfiStatus
)) {
127 if (Private
->offers
!= 0) {
131 Private
->offers
* sizeof (DHCP4_PACKET
)
134 gBS
->FreePool (Private
->offer_list
);
137 CopyMem (&tmp
[Private
->offers
++], rx_pkt
, sizeof (DHCP4_PACKET
));
139 Private
->offer_list
= tmp
;
144 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
148 IN PXE_DHCP4_PRIVATE_DATA
*Private
,
149 IN DHCP4_PACKET
*tx_pkt
,
150 IN DHCP4_PACKET
*rx_pkt
,
154 -2 = ignore, stop waiting
155 -1 = ignore, keep waiting
156 0 = accept, keep waiting
157 1 = accept, stop waiting
160 EFI_STATUS EfiStatus
;
161 DHCP4_OP
*msg_type_op
;
165 DHCP4_OP
*lease_time_op
;
169 // Verify parameters. Touch unused parameters to
170 // keep compiler happy.
175 if (Private
== NULL
|| rx_pkt
== NULL
) {
180 rx_pkt_size
= rx_pkt_size
;
183 // This must be a DHCP Ack message.
185 magik
= htonl (DHCP4_MAGIK_NUMBER
);
187 if (CompareMem (&rx_pkt
->dhcp4
.magik
, &magik
, 4)) {
191 EfiStatus
= find_opt (rx_pkt
, DHCP4_MESSAGE_TYPE
, 0, &msg_type_op
);
193 if (EFI_ERROR (EfiStatus
)) {
197 if (msg_type_op
->len
!= 1) {
201 if (msg_type_op
->data
[0] != DHCP4_MESSAGE_TYPE_ACK
) {
205 // There must be a server identifier.
207 EfiStatus
= find_opt (rx_pkt
, DHCP4_SERVER_IDENTIFIER
, 0, &srvid_op
);
209 if (EFI_ERROR (EfiStatus
)) {
213 if (srvid_op
->len
!= 4) {
217 // There should be a renewal time.
218 // If there is not, we will default to the 7/8 of the rebinding time.
220 EfiStatus
= find_opt (rx_pkt
, DHCP4_RENEWAL_TIME
, 0, &renew_op
);
222 if (EFI_ERROR (EfiStatus
)) {
224 } else if (renew_op
->len
!= 4) {
228 // There should be a rebinding time.
229 // If there is not, we will default to 7/8 of the lease time.
231 EfiStatus
= find_opt (rx_pkt
, DHCP4_REBINDING_TIME
, 0, &rebind_op
);
233 if (EFI_ERROR (EfiStatus
)) {
235 } else if (rebind_op
->len
!= 4) {
239 // There should be a lease time.
240 // If there is not, we will default to one week.
242 EfiStatus
= find_opt (rx_pkt
, DHCP4_LEASE_TIME
, 0, &lease_time_op
);
244 if (EFI_ERROR (EfiStatus
)) {
245 lease_time_op
= NULL
;
246 } else if (lease_time_op
->len
!= 4) {
247 lease_time_op
= NULL
;
250 // Packet looks good. Double check the renew, rebind and lease times.
252 CopyMem (&Private
->ServerIp
, srvid_op
->data
, 4);
254 if (renew_op
!= NULL
) {
255 CopyMem (&Private
->RenewTime
, renew_op
->data
, 4);
256 Private
->RenewTime
= htonl (Private
->RenewTime
);
258 Private
->RenewTime
= 0;
261 if (rebind_op
!= NULL
) {
262 CopyMem (&Private
->RebindTime
, rebind_op
->data
, 4);
263 Private
->RebindTime
= htonl (Private
->RebindTime
);
265 Private
->RebindTime
= 0;
268 if (lease_time_op
!= NULL
) {
269 CopyMem (&Private
->LeaseTime
, lease_time_op
->data
, 4);
270 Private
->LeaseTime
= htonl (Private
->LeaseTime
);
272 Private
->LeaseTime
= 0;
275 if (Private
->LeaseTime
< 60) {
276 Private
->LeaseTime
= 7 * 86400;
279 if (Private
->RebindTime
< 52 || Private
->RebindTime
>= Private
->LeaseTime
) {
280 Private
->RebindTime
= Private
->LeaseTime
/ 2 + Private
->LeaseTime
/ 4 + Private
->LeaseTime
/ 8;
283 if (Private
->RenewTime
< 45 || Private
->RenewTime
>= Private
->RebindTime
) {
284 Private
->RenewTime
= Private
->RebindTime
/ 2 + Private
->RebindTime
/ 4 + Private
->RebindTime
/ 8;
290 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
294 IN EFI_PXE_DHCP4_PROTOCOL
*This
,
295 IN UINTN seconds_timeout
,
297 OUT DHCP4_PACKET
**OfferList
300 PXE_DHCP4_PRIVATE_DATA
*Private
;
302 EFI_IP_ADDRESS bcast_ip
;
303 EFI_STATUS EfiStatus
;
306 // Verify parameters and protocol state.
309 seconds_timeout
< DHCP4_MIN_SECONDS
||
310 seconds_timeout
> DHCP4_MAX_SECONDS
||
315 // Return parameters are not initialized when
316 // parameters are invalid!
318 return EFI_INVALID_PARAMETER
;
325 // Check protocol state.
327 if (This
->Data
== NULL
) {
328 return EFI_NOT_STARTED
;
331 if (!This
->Data
->SetupCompleted
) {
332 return EFI_NOT_READY
;
336 if (!is_good_discover (&This
->Data
->Discover
)) {
338 // %%TBD - check discover packet fields
343 // Get pointer to our instance data.
345 Private
= PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This
);
347 if (Private
== NULL
) {
348 return EFI_INVALID_PARAMETER
;
351 if (Private
->PxeBc
== NULL
) {
352 return EFI_DEVICE_ERROR
;
355 // Setup variables...
358 Private
->offer_list
= NULL
;
360 EfiStatus
= gBS
->HandleProtocol (
362 &gEfiPxeDhcp4CallbackProtocolGuid
,
363 (VOID
*) &Private
->callback
366 if (EFI_ERROR (EfiStatus
)) {
367 Private
->callback
= NULL
;
370 Private
->function
= EFI_PXE_DHCP4_FUNCTION_INIT
;
373 // Increment the transaction ID.
378 CopyMem (&xid
, &This
->Data
->Discover
.dhcp4
.xid
, sizeof (UINT32
));
380 xid
= htonl (htonl (xid
) + 1);
382 CopyMem (&This
->Data
->Discover
.dhcp4
.xid
, &xid
, sizeof (UINT32
));
385 // Transmit discover and wait for offers...
387 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
389 EfiStatus
= tx_rx_udp (
395 &This
->Data
->Discover
,
401 if (EFI_ERROR (EfiStatus
)) {
402 if (Private
->offer_list
) {
403 gBS
->FreePool (Private
->offer_list
);
407 Private
->offer_list
= NULL
;
408 Private
->callback
= NULL
;
410 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
414 *Offers
= Private
->offers
;
415 *OfferList
= Private
->offer_list
;
418 Private
->offer_list
= NULL
;
419 Private
->callback
= NULL
;
421 This
->Data
->InitCompleted
= TRUE
;
422 This
->Data
->SelectCompleted
= FALSE
;
423 This
->Data
->IsBootp
= FALSE
;
424 This
->Data
->IsAck
= FALSE
;
429 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
433 IN EFI_PXE_DHCP4_PROTOCOL
*This
,
434 IN UINTN seconds_timeout
,
435 IN DHCP4_PACKET
*Offer
438 PXE_DHCP4_PRIVATE_DATA
*Private
;
439 EFI_STATUS EfiStatus
;
440 DHCP4_PACKET request
;
442 EFI_IP_ADDRESS bcast_ip
;
443 EFI_IP_ADDRESS zero_ip
;
444 EFI_IP_ADDRESS local_ip
;
452 // Verify parameters.
454 if (This
== NULL
|| seconds_timeout
< DHCP4_MIN_SECONDS
|| seconds_timeout
> DHCP4_MAX_SECONDS
|| Offer
== NULL
) {
455 return EFI_INVALID_PARAMETER
;
458 // Check protocol state.
460 if (This
->Data
== NULL
) {
461 return EFI_NOT_STARTED
;
464 if (!This
->Data
->SetupCompleted
) {
465 return EFI_NOT_READY
;
468 // Get pointer to instance data.
470 Private
= PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This
);
472 if (Private
== NULL
) {
473 return EFI_INVALID_PARAMETER
;
476 if (Private
->PxeBc
== NULL
) {
477 return EFI_DEVICE_ERROR
;
481 if (!is_good_discover (&This
->Data
->Discover
)) {
483 // %%TBD - check discover packet fields
488 // Setup useful variables...
490 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
492 ZeroMem (&zero_ip
, sizeof (EFI_IP_ADDRESS
));
494 ZeroMem (&local_ip
, sizeof (EFI_IP_ADDRESS
));
495 local_ip
.v4
.Addr
[0] = 127;
496 local_ip
.v4
.Addr
[3] = 1;
498 This
->Data
->SelectCompleted
= FALSE
;
499 This
->Data
->IsBootp
= FALSE
;
500 This
->Data
->IsAck
= FALSE
;
502 EfiStatus
= gBS
->HandleProtocol (
504 &gEfiPxeDhcp4CallbackProtocolGuid
,
505 (VOID
*) &Private
->callback
508 if (EFI_ERROR (EfiStatus
)) {
509 Private
->callback
= NULL
;
512 Private
->function
= EFI_PXE_DHCP4_FUNCTION_SELECT
;
515 // Verify offer packet fields.
517 if (Offer
->dhcp4
.op
!= BOOTP_REPLY
) {
518 Private
->callback
= NULL
;
519 return EFI_INVALID_PARAMETER
;
522 if (Offer
->dhcp4
.htype
!= This
->Data
->Discover
.dhcp4
.htype
) {
523 Private
->callback
= NULL
;
524 return EFI_INVALID_PARAMETER
;
527 if (Offer
->dhcp4
.hlen
!= This
->Data
->Discover
.dhcp4
.hlen
) {
528 Private
->callback
= NULL
;
529 return EFI_INVALID_PARAMETER
;
532 if (CompareMem (&Offer
->dhcp4
.xid
, &This
->Data
->Discover
.dhcp4
.xid
, 4)) {
533 Private
->callback
= NULL
;
534 return EFI_INVALID_PARAMETER
;
537 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &bcast_ip
, 4)) {
538 Private
->callback
= NULL
;
539 return EFI_INVALID_PARAMETER
;
542 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &zero_ip
, 4)) {
543 Private
->callback
= NULL
;
544 return EFI_INVALID_PARAMETER
;
547 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &local_ip
, 4)) {
548 Private
->callback
= NULL
;
549 return EFI_INVALID_PARAMETER
;
553 &Offer
->dhcp4
.chaddr
,
554 &This
->Data
->Discover
.dhcp4
.chaddr
,
557 Private
->callback
= NULL
;
558 return EFI_INVALID_PARAMETER
;
561 // DHCP option checks
563 dhcp4_magik
= htonl (DHCP4_MAGIK_NUMBER
);
566 if (!CompareMem (&Offer
->dhcp4
.magik
, &dhcp4_magik
, 4)) {
568 // If present, DHCP message type must be offer.
570 EfiStatus
= find_opt (Offer
, DHCP4_MESSAGE_TYPE
, 0, &op
);
572 if (!EFI_ERROR (EfiStatus
)) {
573 if (op
->len
!= 1 || op
->data
[0] != DHCP4_MESSAGE_TYPE_OFFER
) {
574 Private
->callback
= NULL
;
575 return EFI_INVALID_PARAMETER
;
581 // If present, DHCP max message size must be valid.
583 EfiStatus
= find_opt (Offer
, DHCP4_MAX_MESSAGE_SIZE
, 0, &op
);
585 if (!EFI_ERROR (EfiStatus
)) {
586 if (op
->len
!= 2 || ((op
->data
[0] << 8) | op
->data
[1]) < DHCP4_DEFAULT_MAX_MESSAGE_SIZE
) {
587 Private
->callback
= NULL
;
588 return EFI_INVALID_PARAMETER
;
592 // If present, DHCP server identifier must be valid.
594 EfiStatus
= find_opt (Offer
, DHCP4_SERVER_IDENTIFIER
, 0, &op
);
596 if (!EFI_ERROR (EfiStatus
)) {
597 if (op
->len
!= 4 || !CompareMem (op
->data
, &bcast_ip
, 4) || !CompareMem (op
->data
, &zero_ip
, 4)) {
598 Private
->callback
= NULL
;
599 return EFI_INVALID_PARAMETER
;
603 // If present, DHCP subnet mask must be valid.
605 EfiStatus
= find_opt (
612 if (!EFI_ERROR (EfiStatus
)) {
614 Private
->callback
= NULL
;
615 return EFI_INVALID_PARAMETER
;
620 // Early out for BOOTP.
622 This
->Data
->IsBootp
= is_bootp
;
625 // Copy offer packet to instance data.
627 CopyMem (&This
->Data
->Offer
, Offer
, sizeof (DHCP4_PACKET
));
630 // Copy discover to request and offer to acknak.
633 &This
->Data
->Request
,
634 &This
->Data
->Discover
,
635 sizeof (DHCP4_PACKET
)
641 sizeof (DHCP4_PACKET
)
647 This
->Data
->SelectCompleted
= TRUE
;
648 This
->Data
->IsAck
= TRUE
;
650 Private
->callback
= NULL
;
654 // Copy discover packet contents to request packet.
656 CopyMem (&request
, &This
->Data
->Discover
, sizeof (DHCP4_PACKET
));
658 This
->Data
->IsAck
= FALSE
;
661 // Change DHCP message type from discover to request.
663 EfiStatus
= find_opt (&request
, DHCP4_MESSAGE_TYPE
, 0, &op
);
665 if (EFI_ERROR (EfiStatus
) && EfiStatus
!= EFI_NOT_FOUND
) {
666 Private
->callback
= NULL
;
667 return EFI_INVALID_PARAMETER
;
670 if (EfiStatus
== EFI_NOT_FOUND
) {
671 EfiStatus
= find_opt (&request
, DHCP4_END
, 0, &op
);
673 if (EFI_ERROR (EfiStatus
)) {
674 Private
->callback
= NULL
;
675 return EFI_INVALID_PARAMETER
;
678 op
->op
= DHCP4_MESSAGE_TYPE
;
681 op
->data
[1] = DHCP4_END
;
684 op
->data
[0] = DHCP4_MESSAGE_TYPE_REQUEST
;
687 // Copy server identifier option from offer to request.
689 EfiStatus
= find_opt (Offer
, DHCP4_SERVER_IDENTIFIER
, 0, &srvid
);
691 if (EFI_ERROR (EfiStatus
)) {
692 Private
->callback
= NULL
;
693 return EFI_INVALID_PARAMETER
;
696 if (srvid
->len
!= 4) {
697 Private
->callback
= NULL
;
698 return EFI_INVALID_PARAMETER
;
701 EfiStatus
= add_opt (&request
, srvid
);
703 if (EFI_ERROR (EfiStatus
)) {
704 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
705 Private
->callback
= NULL
;
709 // Add requested IP address option to request packet.
711 op
= (DHCP4_OP
*) buf
;
712 op
->op
= DHCP4_REQUESTED_IP_ADDRESS
;
714 CopyMem (op
->data
, &Offer
->dhcp4
.yiaddr
, 4);
716 EfiStatus
= add_opt (&request
, op
);
718 if (EFI_ERROR (EfiStatus
)) {
719 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
720 Private
->callback
= NULL
;
724 // Transimit DHCP request and wait for DHCP ack...
726 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
728 EfiStatus
= tx_rx_udp (
740 if (EFI_ERROR (EfiStatus
)) {
741 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
742 Private
->callback
= NULL
;
746 // Set Data->IsAck and return.
748 EfiStatus
= find_opt (&acknak
, DHCP4_MESSAGE_TYPE
, 0, &op
);
750 if (EFI_ERROR (EfiStatus
)) {
751 Private
->callback
= NULL
;
752 return EFI_DEVICE_ERROR
;
756 Private
->callback
= NULL
;
757 return EFI_DEVICE_ERROR
;
760 switch (op
->data
[0]) {
761 case DHCP4_MESSAGE_TYPE_ACK
:
762 This
->Data
->IsAck
= TRUE
;
765 case DHCP4_MESSAGE_TYPE_NAK
:
766 This
->Data
->IsAck
= FALSE
;
770 Private
->callback
= NULL
;
771 return EFI_DEVICE_ERROR
;
774 // Copy packets into instance data...
776 CopyMem (&This
->Data
->Offer
, Offer
, sizeof (DHCP4_PACKET
));
777 CopyMem (&This
->Data
->Request
, &request
, sizeof (DHCP4_PACKET
));
778 CopyMem (&This
->Data
->AckNak
, &acknak
, sizeof (DHCP4_PACKET
));
780 This
->Data
->SelectCompleted
= TRUE
;
782 Private
->callback
= NULL
;
786 /* eof - PxeDhcp4InitSelect.c */