3 Copyright (c) 2004, 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 // #define DebugPrint(x) Aprint x
28 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
36 IN PXE_DHCP4_PRIVATE_DATA
*Private
,
37 IN DHCP4_PACKET
*tx_pkt
,
38 IN DHCP4_PACKET
*rx_pkt
,
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 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
152 IN PXE_DHCP4_PRIVATE_DATA
*Private
,
153 IN DHCP4_PACKET
*tx_pkt
,
154 IN DHCP4_PACKET
*rx_pkt
,
158 EFI_STATUS EfiStatus
;
159 DHCP4_OP
*msg_type_op
;
163 DHCP4_OP
*lease_time_op
;
167 // Verify parameters. Touch unused parameters to
168 // keep compiler happy.
173 if (Private
== NULL
|| rx_pkt
== NULL
) {
178 rx_pkt_size
= rx_pkt_size
;
181 // This must be a DHCP Ack message.
183 magik
= htonl (DHCP4_MAGIK_NUMBER
);
185 if (CompareMem (&rx_pkt
->dhcp4
.magik
, &magik
, 4)) {
189 EfiStatus
= find_opt (rx_pkt
, DHCP4_MESSAGE_TYPE
, 0, &msg_type_op
);
191 if (EFI_ERROR (EfiStatus
)) {
195 if (msg_type_op
->len
!= 1) {
199 if (msg_type_op
->data
[0] != DHCP4_MESSAGE_TYPE_ACK
) {
203 // There must be a server identifier.
205 EfiStatus
= find_opt (rx_pkt
, DHCP4_SERVER_IDENTIFIER
, 0, &srvid_op
);
207 if (EFI_ERROR (EfiStatus
)) {
211 if (srvid_op
->len
!= 4) {
215 // There should be a renewal time.
216 // If there is not, we will default to the 7/8 of the rebinding time.
218 EfiStatus
= find_opt (rx_pkt
, DHCP4_RENEWAL_TIME
, 0, &renew_op
);
220 if (EFI_ERROR (EfiStatus
)) {
222 } else if (renew_op
->len
!= 4) {
226 // There should be a rebinding time.
227 // If there is not, we will default to 7/8 of the lease time.
229 EfiStatus
= find_opt (rx_pkt
, DHCP4_REBINDING_TIME
, 0, &rebind_op
);
231 if (EFI_ERROR (EfiStatus
)) {
233 } else if (rebind_op
->len
!= 4) {
237 // There should be a lease time.
238 // If there is not, we will default to one week.
240 EfiStatus
= find_opt (rx_pkt
, DHCP4_LEASE_TIME
, 0, &lease_time_op
);
242 if (EFI_ERROR (EfiStatus
)) {
243 lease_time_op
= NULL
;
244 } else if (lease_time_op
->len
!= 4) {
245 lease_time_op
= NULL
;
248 // Packet looks good. Double check the renew, rebind and lease times.
250 CopyMem (&Private
->ServerIp
, srvid_op
->data
, 4);
252 if (renew_op
!= NULL
) {
253 CopyMem (&Private
->RenewTime
, renew_op
->data
, 4);
254 Private
->RenewTime
= htonl (Private
->RenewTime
);
256 Private
->RenewTime
= 0;
259 if (rebind_op
!= NULL
) {
260 CopyMem (&Private
->RebindTime
, rebind_op
->data
, 4);
261 Private
->RebindTime
= htonl (Private
->RebindTime
);
263 Private
->RebindTime
= 0;
266 if (lease_time_op
!= NULL
) {
267 CopyMem (&Private
->LeaseTime
, lease_time_op
->data
, 4);
268 Private
->LeaseTime
= htonl (Private
->LeaseTime
);
270 Private
->LeaseTime
= 0;
273 if (Private
->LeaseTime
< 60) {
274 Private
->LeaseTime
= 7 * 86400;
277 if (Private
->RebindTime
< 52 || Private
->RebindTime
>= Private
->LeaseTime
) {
278 Private
->RebindTime
= Private
->LeaseTime
/ 2 + Private
->LeaseTime
/ 4 + Private
->LeaseTime
/ 8;
281 if (Private
->RenewTime
< 45 || Private
->RenewTime
>= Private
->RebindTime
) {
282 Private
->RenewTime
= Private
->RebindTime
/ 2 + Private
->RebindTime
/ 4 + Private
->RebindTime
/ 8;
288 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
292 IN EFI_PXE_DHCP4_PROTOCOL
*This
,
293 IN UINTN seconds_timeout
,
295 OUT DHCP4_PACKET
**OfferList
298 PXE_DHCP4_PRIVATE_DATA
*Private
;
300 EFI_IP_ADDRESS bcast_ip
;
301 EFI_STATUS EfiStatus
;
304 // Verify parameters and protocol state.
307 seconds_timeout
< DHCP4_MIN_SECONDS
||
308 seconds_timeout
> DHCP4_MAX_SECONDS
||
313 // Return parameters are not initialized when
314 // parameters are invalid!
316 return EFI_INVALID_PARAMETER
;
323 // Check protocol state.
325 if (This
->Data
== NULL
) {
326 return EFI_NOT_STARTED
;
329 if (!This
->Data
->SetupCompleted
) {
330 return EFI_NOT_READY
;
334 if (!is_good_discover (&This
->Data
->Discover
)) {
336 // %%TBD - check discover packet fields
341 // Get pointer to our instance data.
343 Private
= PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This
);
345 if (Private
== NULL
) {
346 return EFI_INVALID_PARAMETER
;
349 if (Private
->PxeBc
== NULL
) {
350 return EFI_DEVICE_ERROR
;
353 // Setup variables...
356 Private
->offer_list
= NULL
;
358 EfiStatus
= gBS
->HandleProtocol (
360 &gEfiPxeDhcp4CallbackProtocolGuid
,
361 (VOID
*) &Private
->callback
364 if (EFI_ERROR (EfiStatus
)) {
365 Private
->callback
= NULL
;
368 Private
->function
= EFI_PXE_DHCP4_FUNCTION_INIT
;
371 // Increment the transaction ID.
376 CopyMem (&xid
, &This
->Data
->Discover
.dhcp4
.xid
, sizeof (UINT32
));
378 xid
= htonl (htonl (xid
) + 1);
380 CopyMem (&This
->Data
->Discover
.dhcp4
.xid
, &xid
, sizeof (UINT32
));
383 // Transmit discover and wait for offers...
385 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
387 EfiStatus
= tx_rx_udp (
393 &This
->Data
->Discover
,
399 if (EFI_ERROR (EfiStatus
)) {
400 if (Private
->offer_list
) {
401 gBS
->FreePool (Private
->offer_list
);
405 Private
->offer_list
= NULL
;
406 Private
->callback
= NULL
;
408 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
412 *Offers
= Private
->offers
;
413 *OfferList
= Private
->offer_list
;
416 Private
->offer_list
= NULL
;
417 Private
->callback
= NULL
;
419 This
->Data
->InitCompleted
= TRUE
;
420 This
->Data
->SelectCompleted
= FALSE
;
421 This
->Data
->IsBootp
= FALSE
;
422 This
->Data
->IsAck
= FALSE
;
427 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
431 IN EFI_PXE_DHCP4_PROTOCOL
*This
,
432 IN UINTN seconds_timeout
,
433 IN DHCP4_PACKET
*Offer
436 PXE_DHCP4_PRIVATE_DATA
*Private
;
437 EFI_STATUS EfiStatus
;
438 DHCP4_PACKET request
;
440 EFI_IP_ADDRESS bcast_ip
;
441 EFI_IP_ADDRESS zero_ip
;
442 EFI_IP_ADDRESS local_ip
;
450 // Verify parameters.
452 if (This
== NULL
|| seconds_timeout
< DHCP4_MIN_SECONDS
|| seconds_timeout
> DHCP4_MAX_SECONDS
|| Offer
== NULL
) {
453 return EFI_INVALID_PARAMETER
;
456 // Check protocol state.
458 if (This
->Data
== NULL
) {
459 return EFI_NOT_STARTED
;
462 if (!This
->Data
->SetupCompleted
) {
463 return EFI_NOT_READY
;
466 // Get pointer to instance data.
468 Private
= PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This
);
470 if (Private
== NULL
) {
471 return EFI_INVALID_PARAMETER
;
474 if (Private
->PxeBc
== NULL
) {
475 return EFI_DEVICE_ERROR
;
479 if (!is_good_discover (&This
->Data
->Discover
)) {
481 // %%TBD - check discover packet fields
486 // Setup useful variables...
488 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
490 ZeroMem (&zero_ip
, sizeof (EFI_IP_ADDRESS
));
492 ZeroMem (&local_ip
, sizeof (EFI_IP_ADDRESS
));
493 local_ip
.v4
.Addr
[0] = 127;
494 local_ip
.v4
.Addr
[3] = 1;
496 This
->Data
->SelectCompleted
= FALSE
;
497 This
->Data
->IsBootp
= FALSE
;
498 This
->Data
->IsAck
= FALSE
;
500 EfiStatus
= gBS
->HandleProtocol (
502 &gEfiPxeDhcp4CallbackProtocolGuid
,
503 (VOID
*) &Private
->callback
506 if (EFI_ERROR (EfiStatus
)) {
507 Private
->callback
= NULL
;
510 Private
->function
= EFI_PXE_DHCP4_FUNCTION_SELECT
;
513 // Verify offer packet fields.
515 if (Offer
->dhcp4
.op
!= BOOTP_REPLY
) {
516 Private
->callback
= NULL
;
517 return EFI_INVALID_PARAMETER
;
520 if (Offer
->dhcp4
.htype
!= This
->Data
->Discover
.dhcp4
.htype
) {
521 Private
->callback
= NULL
;
522 return EFI_INVALID_PARAMETER
;
525 if (Offer
->dhcp4
.hlen
!= This
->Data
->Discover
.dhcp4
.hlen
) {
526 Private
->callback
= NULL
;
527 return EFI_INVALID_PARAMETER
;
530 if (CompareMem (&Offer
->dhcp4
.xid
, &This
->Data
->Discover
.dhcp4
.xid
, 4)) {
531 Private
->callback
= NULL
;
532 return EFI_INVALID_PARAMETER
;
535 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &bcast_ip
, 4)) {
536 Private
->callback
= NULL
;
537 return EFI_INVALID_PARAMETER
;
540 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &zero_ip
, 4)) {
541 Private
->callback
= NULL
;
542 return EFI_INVALID_PARAMETER
;
545 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &local_ip
, 4)) {
546 Private
->callback
= NULL
;
547 return EFI_INVALID_PARAMETER
;
551 &Offer
->dhcp4
.chaddr
,
552 &This
->Data
->Discover
.dhcp4
.chaddr
,
555 Private
->callback
= NULL
;
556 return EFI_INVALID_PARAMETER
;
559 // DHCP option checks
561 dhcp4_magik
= htonl (DHCP4_MAGIK_NUMBER
);
564 if (!CompareMem (&Offer
->dhcp4
.magik
, &dhcp4_magik
, 4)) {
566 // If present, DHCP message type must be offer.
568 EfiStatus
= find_opt (Offer
, DHCP4_MESSAGE_TYPE
, 0, &op
);
570 if (!EFI_ERROR (EfiStatus
)) {
571 if (op
->len
!= 1 || op
->data
[0] != DHCP4_MESSAGE_TYPE_OFFER
) {
572 Private
->callback
= NULL
;
573 return EFI_INVALID_PARAMETER
;
579 // If present, DHCP max message size must be valid.
581 EfiStatus
= find_opt (Offer
, DHCP4_MAX_MESSAGE_SIZE
, 0, &op
);
583 if (!EFI_ERROR (EfiStatus
)) {
584 if (op
->len
!= 2 || ((op
->data
[0] << 8) | op
->data
[1]) < DHCP4_DEFAULT_MAX_MESSAGE_SIZE
) {
585 Private
->callback
= NULL
;
586 return EFI_INVALID_PARAMETER
;
590 // If present, DHCP server identifier must be valid.
592 EfiStatus
= find_opt (Offer
, DHCP4_SERVER_IDENTIFIER
, 0, &op
);
594 if (!EFI_ERROR (EfiStatus
)) {
595 if (op
->len
!= 4 || !CompareMem (op
->data
, &bcast_ip
, 4) || !CompareMem (op
->data
, &zero_ip
, 4)) {
596 Private
->callback
= NULL
;
597 return EFI_INVALID_PARAMETER
;
601 // If present, DHCP subnet mask must be valid.
603 EfiStatus
= find_opt (
610 if (!EFI_ERROR (EfiStatus
)) {
612 Private
->callback
= NULL
;
613 return EFI_INVALID_PARAMETER
;
618 // Early out for BOOTP.
620 This
->Data
->IsBootp
= is_bootp
;
623 // Copy offer packet to instance data.
625 CopyMem (&This
->Data
->Offer
, Offer
, sizeof (DHCP4_PACKET
));
628 // Copy discover to request and offer to acknak.
631 &This
->Data
->Request
,
632 &This
->Data
->Discover
,
633 sizeof (DHCP4_PACKET
)
639 sizeof (DHCP4_PACKET
)
645 This
->Data
->SelectCompleted
= TRUE
;
646 This
->Data
->IsAck
= TRUE
;
648 Private
->callback
= NULL
;
652 // Copy discover packet contents to request packet.
654 CopyMem (&request
, &This
->Data
->Discover
, sizeof (DHCP4_PACKET
));
656 This
->Data
->IsAck
= FALSE
;
659 // Change DHCP message type from discover to request.
661 EfiStatus
= find_opt (&request
, DHCP4_MESSAGE_TYPE
, 0, &op
);
663 if (EFI_ERROR (EfiStatus
) && EfiStatus
!= EFI_NOT_FOUND
) {
664 Private
->callback
= NULL
;
665 return EFI_INVALID_PARAMETER
;
668 if (EfiStatus
== EFI_NOT_FOUND
) {
669 EfiStatus
= find_opt (&request
, DHCP4_END
, 0, &op
);
671 if (EFI_ERROR (EfiStatus
)) {
672 Private
->callback
= NULL
;
673 return EFI_INVALID_PARAMETER
;
676 op
->op
= DHCP4_MESSAGE_TYPE
;
679 op
->data
[1] = DHCP4_END
;
682 op
->data
[0] = DHCP4_MESSAGE_TYPE_REQUEST
;
685 // Copy server identifier option from offer to request.
687 EfiStatus
= find_opt (Offer
, DHCP4_SERVER_IDENTIFIER
, 0, &srvid
);
689 if (EFI_ERROR (EfiStatus
)) {
690 Private
->callback
= NULL
;
691 return EFI_INVALID_PARAMETER
;
694 if (srvid
->len
!= 4) {
695 Private
->callback
= NULL
;
696 return EFI_INVALID_PARAMETER
;
699 EfiStatus
= add_opt (&request
, srvid
);
701 if (EFI_ERROR (EfiStatus
)) {
702 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
703 Private
->callback
= NULL
;
707 // Add requested IP address option to request packet.
709 op
= (DHCP4_OP
*) buf
;
710 op
->op
= DHCP4_REQUESTED_IP_ADDRESS
;
712 CopyMem (op
->data
, &Offer
->dhcp4
.yiaddr
, 4);
714 EfiStatus
= add_opt (&request
, op
);
716 if (EFI_ERROR (EfiStatus
)) {
717 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
718 Private
->callback
= NULL
;
722 // Transimit DHCP request and wait for DHCP ack...
724 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
726 EfiStatus
= tx_rx_udp (
738 if (EFI_ERROR (EfiStatus
)) {
739 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
740 Private
->callback
= NULL
;
744 // Set Data->IsAck and return.
746 EfiStatus
= find_opt (&acknak
, DHCP4_MESSAGE_TYPE
, 0, &op
);
748 if (EFI_ERROR (EfiStatus
)) {
749 Private
->callback
= NULL
;
750 return EFI_DEVICE_ERROR
;
754 Private
->callback
= NULL
;
755 return EFI_DEVICE_ERROR
;
758 switch (op
->data
[0]) {
759 case DHCP4_MESSAGE_TYPE_ACK
:
760 This
->Data
->IsAck
= TRUE
;
763 case DHCP4_MESSAGE_TYPE_NAK
:
764 This
->Data
->IsAck
= FALSE
;
768 Private
->callback
= NULL
;
769 return EFI_DEVICE_ERROR
;
772 // Copy packets into instance data...
774 CopyMem (&This
->Data
->Offer
, Offer
, sizeof (DHCP4_PACKET
));
775 CopyMem (&This
->Data
->Request
, &request
, sizeof (DHCP4_PACKET
));
776 CopyMem (&This
->Data
->AckNak
, &acknak
, sizeof (DHCP4_PACKET
));
778 This
->Data
->SelectCompleted
= TRUE
;
780 Private
->callback
= NULL
;
784 /* eof - PxeDhcp4InitSelect.c */