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 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
35 IN PXE_DHCP4_PRIVATE_DATA
*Private
,
36 IN DHCP4_PACKET
*tx_pkt
,
37 IN DHCP4_PACKET
*rx_pkt
,
43 DHCP4_OP
*msg_type_op
;
48 // Verify parameters. Touch unused parameters to keep
54 if (Private
== NULL
|| rx_pkt
== NULL
) {
59 rx_pkt_size
= rx_pkt_size
;
62 // This may be a BOOTP Reply or DHCP Offer packet.
63 // If there is no DHCP magik number, assume that
64 // this is a BOOTP Reply packet.
66 magik
= htonl (DHCP4_MAGIK_NUMBER
);
68 while (!CompareMem (&rx_pkt
->dhcp4
.magik
, &magik
, 4)) {
70 // If there is no DHCP message type option, assume
71 // this is a BOOTP reply packet and cache it.
73 EfiStatus
= find_opt (rx_pkt
, DHCP4_MESSAGE_TYPE
, 0, &msg_type_op
);
75 if (EFI_ERROR (EfiStatus
)) {
79 // If there is a DHCP message type option, it must be a
82 if (msg_type_op
->len
!= 1) {
86 if (msg_type_op
->data
[0] != DHCP4_MESSAGE_TYPE_OFFER
) {
90 // There must be a server identifier option.
92 EfiStatus
= find_opt (
94 DHCP4_SERVER_IDENTIFIER
,
99 if (EFI_ERROR (EfiStatus
)) {
103 if (srvid_op
->len
!= 4) {
107 // Good DHCP offer packet.
112 // Good DHCP (or BOOTP) packet. Cache it!
114 EfiStatus
= gBS
->AllocatePool (
116 (Private
->offers
+ 1) * sizeof (DHCP4_PACKET
),
120 if (EFI_ERROR (EfiStatus
)) {
126 if (Private
->offers
!= 0) {
130 Private
->offers
* sizeof (DHCP4_PACKET
)
133 gBS
->FreePool (Private
->offer_list
);
136 CopyMem (&tmp
[Private
->offers
++], rx_pkt
, sizeof (DHCP4_PACKET
));
138 Private
->offer_list
= tmp
;
143 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
149 acknak_verify_initselect (
150 IN PXE_DHCP4_PRIVATE_DATA
*Private
,
151 IN DHCP4_PACKET
*tx_pkt
,
152 IN DHCP4_PACKET
*rx_pkt
,
156 EFI_STATUS EfiStatus
;
157 DHCP4_OP
*msg_type_op
;
161 DHCP4_OP
*lease_time_op
;
165 // Verify parameters. Touch unused parameters to
166 // keep compiler happy.
171 if (Private
== NULL
|| rx_pkt
== NULL
) {
176 rx_pkt_size
= rx_pkt_size
;
179 // This must be a DHCP Ack message.
181 magik
= htonl (DHCP4_MAGIK_NUMBER
);
183 if (CompareMem (&rx_pkt
->dhcp4
.magik
, &magik
, 4)) {
187 EfiStatus
= find_opt (rx_pkt
, DHCP4_MESSAGE_TYPE
, 0, &msg_type_op
);
189 if (EFI_ERROR (EfiStatus
)) {
193 if (msg_type_op
->len
!= 1) {
197 if (msg_type_op
->data
[0] != DHCP4_MESSAGE_TYPE_ACK
) {
201 // There must be a server identifier.
203 EfiStatus
= find_opt (rx_pkt
, DHCP4_SERVER_IDENTIFIER
, 0, &srvid_op
);
205 if (EFI_ERROR (EfiStatus
)) {
209 if (srvid_op
->len
!= 4) {
213 // There should be a renewal time.
214 // If there is not, we will default to the 7/8 of the rebinding time.
216 EfiStatus
= find_opt (rx_pkt
, DHCP4_RENEWAL_TIME
, 0, &renew_op
);
218 if (EFI_ERROR (EfiStatus
)) {
220 } else if (renew_op
->len
!= 4) {
224 // There should be a rebinding time.
225 // If there is not, we will default to 7/8 of the lease time.
227 EfiStatus
= find_opt (rx_pkt
, DHCP4_REBINDING_TIME
, 0, &rebind_op
);
229 if (EFI_ERROR (EfiStatus
)) {
231 } else if (rebind_op
->len
!= 4) {
235 // There should be a lease time.
236 // If there is not, we will default to one week.
238 EfiStatus
= find_opt (rx_pkt
, DHCP4_LEASE_TIME
, 0, &lease_time_op
);
240 if (EFI_ERROR (EfiStatus
)) {
241 lease_time_op
= NULL
;
242 } else if (lease_time_op
->len
!= 4) {
243 lease_time_op
= NULL
;
246 // Packet looks good. Double check the renew, rebind and lease times.
248 CopyMem (&Private
->ServerIp
, srvid_op
->data
, 4);
250 if (renew_op
!= NULL
) {
251 CopyMem (&Private
->RenewTime
, renew_op
->data
, 4);
252 Private
->RenewTime
= htonl (Private
->RenewTime
);
254 Private
->RenewTime
= 0;
257 if (rebind_op
!= NULL
) {
258 CopyMem (&Private
->RebindTime
, rebind_op
->data
, 4);
259 Private
->RebindTime
= htonl (Private
->RebindTime
);
261 Private
->RebindTime
= 0;
264 if (lease_time_op
!= NULL
) {
265 CopyMem (&Private
->LeaseTime
, lease_time_op
->data
, 4);
266 Private
->LeaseTime
= htonl (Private
->LeaseTime
);
268 Private
->LeaseTime
= 0;
271 if (Private
->LeaseTime
< 60) {
272 Private
->LeaseTime
= 7 * 86400;
275 if (Private
->RebindTime
< 52 || Private
->RebindTime
>= Private
->LeaseTime
) {
276 Private
->RebindTime
= Private
->LeaseTime
/ 2 + Private
->LeaseTime
/ 4 + Private
->LeaseTime
/ 8;
279 if (Private
->RenewTime
< 45 || Private
->RenewTime
>= Private
->RebindTime
) {
280 Private
->RenewTime
= Private
->RebindTime
/ 2 + Private
->RebindTime
/ 4 + Private
->RebindTime
/ 8;
286 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
290 IN EFI_PXE_DHCP4_PROTOCOL
*This
,
291 IN UINTN seconds_timeout
,
293 OUT DHCP4_PACKET
**OfferList
296 PXE_DHCP4_PRIVATE_DATA
*Private
;
298 EFI_IP_ADDRESS bcast_ip
;
299 EFI_STATUS EfiStatus
;
302 // Verify parameters and protocol state.
305 seconds_timeout
< DHCP4_MIN_SECONDS
||
306 seconds_timeout
> DHCP4_MAX_SECONDS
||
311 // Return parameters are not initialized when
312 // parameters are invalid!
314 return EFI_INVALID_PARAMETER
;
321 // Check protocol state.
323 if (This
->Data
== NULL
) {
324 return EFI_NOT_STARTED
;
327 if (!This
->Data
->SetupCompleted
) {
328 return EFI_NOT_READY
;
332 // Get pointer to our instance data.
334 Private
= PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This
);
336 if (Private
== NULL
) {
337 return EFI_INVALID_PARAMETER
;
340 if (Private
->PxeBc
== NULL
) {
341 return EFI_DEVICE_ERROR
;
344 // Setup variables...
347 Private
->offer_list
= NULL
;
349 EfiStatus
= gBS
->HandleProtocol (
351 &gEfiPxeDhcp4CallbackProtocolGuid
,
352 (VOID
*) &Private
->callback
355 if (EFI_ERROR (EfiStatus
)) {
356 Private
->callback
= NULL
;
359 Private
->function
= EFI_PXE_DHCP4_FUNCTION_INIT
;
362 // Increment the transaction ID.
367 CopyMem (&xid
, &This
->Data
->Discover
.dhcp4
.xid
, sizeof (UINT32
));
369 xid
= htonl (htonl (xid
) + 1);
371 CopyMem (&This
->Data
->Discover
.dhcp4
.xid
, &xid
, sizeof (UINT32
));
374 // Transmit discover and wait for offers...
376 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
378 EfiStatus
= tx_rx_udp (
384 &This
->Data
->Discover
,
390 if (EFI_ERROR (EfiStatus
)) {
391 if (Private
->offer_list
) {
392 gBS
->FreePool (Private
->offer_list
);
396 Private
->offer_list
= NULL
;
397 Private
->callback
= NULL
;
399 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
403 *Offers
= Private
->offers
;
404 *OfferList
= Private
->offer_list
;
407 Private
->offer_list
= NULL
;
408 Private
->callback
= NULL
;
410 This
->Data
->InitCompleted
= TRUE
;
411 This
->Data
->SelectCompleted
= FALSE
;
412 This
->Data
->IsBootp
= FALSE
;
413 This
->Data
->IsAck
= FALSE
;
418 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
422 IN EFI_PXE_DHCP4_PROTOCOL
*This
,
423 IN UINTN seconds_timeout
,
424 IN DHCP4_PACKET
*Offer
427 PXE_DHCP4_PRIVATE_DATA
*Private
;
428 EFI_STATUS EfiStatus
;
429 DHCP4_PACKET request
;
431 EFI_IP_ADDRESS bcast_ip
;
432 EFI_IP_ADDRESS zero_ip
;
433 EFI_IP_ADDRESS local_ip
;
441 // Verify parameters.
443 if (This
== NULL
|| seconds_timeout
< DHCP4_MIN_SECONDS
|| seconds_timeout
> DHCP4_MAX_SECONDS
|| Offer
== NULL
) {
444 return EFI_INVALID_PARAMETER
;
447 // Check protocol state.
449 if (This
->Data
== NULL
) {
450 return EFI_NOT_STARTED
;
453 if (!This
->Data
->SetupCompleted
) {
454 return EFI_NOT_READY
;
457 // Get pointer to instance data.
459 Private
= PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This
);
461 if (Private
== NULL
) {
462 return EFI_INVALID_PARAMETER
;
465 if (Private
->PxeBc
== NULL
) {
466 return EFI_DEVICE_ERROR
;
470 // Setup useful variables...
472 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
474 ZeroMem (&zero_ip
, sizeof (EFI_IP_ADDRESS
));
476 ZeroMem (&local_ip
, sizeof (EFI_IP_ADDRESS
));
477 local_ip
.v4
.Addr
[0] = 127;
478 local_ip
.v4
.Addr
[3] = 1;
480 This
->Data
->SelectCompleted
= FALSE
;
481 This
->Data
->IsBootp
= FALSE
;
482 This
->Data
->IsAck
= FALSE
;
484 EfiStatus
= gBS
->HandleProtocol (
486 &gEfiPxeDhcp4CallbackProtocolGuid
,
487 (VOID
*) &Private
->callback
490 if (EFI_ERROR (EfiStatus
)) {
491 Private
->callback
= NULL
;
494 Private
->function
= EFI_PXE_DHCP4_FUNCTION_SELECT
;
497 // Verify offer packet fields.
499 if (Offer
->dhcp4
.op
!= BOOTP_REPLY
) {
500 Private
->callback
= NULL
;
501 return EFI_INVALID_PARAMETER
;
504 if (Offer
->dhcp4
.htype
!= This
->Data
->Discover
.dhcp4
.htype
) {
505 Private
->callback
= NULL
;
506 return EFI_INVALID_PARAMETER
;
509 if (Offer
->dhcp4
.hlen
!= This
->Data
->Discover
.dhcp4
.hlen
) {
510 Private
->callback
= NULL
;
511 return EFI_INVALID_PARAMETER
;
514 if (CompareMem (&Offer
->dhcp4
.xid
, &This
->Data
->Discover
.dhcp4
.xid
, 4)) {
515 Private
->callback
= NULL
;
516 return EFI_INVALID_PARAMETER
;
519 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &bcast_ip
, 4)) {
520 Private
->callback
= NULL
;
521 return EFI_INVALID_PARAMETER
;
524 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &zero_ip
, 4)) {
525 Private
->callback
= NULL
;
526 return EFI_INVALID_PARAMETER
;
529 if (!CompareMem (&Offer
->dhcp4
.yiaddr
, &local_ip
, 4)) {
530 Private
->callback
= NULL
;
531 return EFI_INVALID_PARAMETER
;
535 &Offer
->dhcp4
.chaddr
,
536 &This
->Data
->Discover
.dhcp4
.chaddr
,
539 Private
->callback
= NULL
;
540 return EFI_INVALID_PARAMETER
;
543 // DHCP option checks
545 dhcp4_magik
= htonl (DHCP4_MAGIK_NUMBER
);
548 if (!CompareMem (&Offer
->dhcp4
.magik
, &dhcp4_magik
, 4)) {
550 // If present, DHCP message type must be offer.
552 EfiStatus
= find_opt (Offer
, DHCP4_MESSAGE_TYPE
, 0, &op
);
554 if (!EFI_ERROR (EfiStatus
)) {
555 if (op
->len
!= 1 || op
->data
[0] != DHCP4_MESSAGE_TYPE_OFFER
) {
556 Private
->callback
= NULL
;
557 return EFI_INVALID_PARAMETER
;
563 // If present, DHCP max message size must be valid.
565 EfiStatus
= find_opt (Offer
, DHCP4_MAX_MESSAGE_SIZE
, 0, &op
);
567 if (!EFI_ERROR (EfiStatus
)) {
568 if (op
->len
!= 2 || ((op
->data
[0] << 8) | op
->data
[1]) < DHCP4_DEFAULT_MAX_MESSAGE_SIZE
) {
569 Private
->callback
= NULL
;
570 return EFI_INVALID_PARAMETER
;
574 // If present, DHCP server identifier must be valid.
576 EfiStatus
= find_opt (Offer
, DHCP4_SERVER_IDENTIFIER
, 0, &op
);
578 if (!EFI_ERROR (EfiStatus
)) {
579 if (op
->len
!= 4 || !CompareMem (op
->data
, &bcast_ip
, 4) || !CompareMem (op
->data
, &zero_ip
, 4)) {
580 Private
->callback
= NULL
;
581 return EFI_INVALID_PARAMETER
;
585 // If present, DHCP subnet mask must be valid.
587 EfiStatus
= find_opt (
594 if (!EFI_ERROR (EfiStatus
)) {
596 Private
->callback
= NULL
;
597 return EFI_INVALID_PARAMETER
;
602 // Early out for BOOTP.
604 This
->Data
->IsBootp
= is_bootp
;
607 // Copy offer packet to instance data.
609 CopyMem (&This
->Data
->Offer
, Offer
, sizeof (DHCP4_PACKET
));
612 // Copy discover to request and offer to acknak.
615 &This
->Data
->Request
,
616 &This
->Data
->Discover
,
617 sizeof (DHCP4_PACKET
)
623 sizeof (DHCP4_PACKET
)
629 This
->Data
->SelectCompleted
= TRUE
;
630 This
->Data
->IsAck
= TRUE
;
632 Private
->callback
= NULL
;
636 // Copy discover packet contents to request packet.
638 CopyMem (&request
, &This
->Data
->Discover
, sizeof (DHCP4_PACKET
));
640 This
->Data
->IsAck
= FALSE
;
643 // Change DHCP message type from discover to request.
645 EfiStatus
= find_opt (&request
, DHCP4_MESSAGE_TYPE
, 0, &op
);
647 if (EFI_ERROR (EfiStatus
) && EfiStatus
!= EFI_NOT_FOUND
) {
648 Private
->callback
= NULL
;
649 return EFI_INVALID_PARAMETER
;
652 if (EfiStatus
== EFI_NOT_FOUND
) {
653 EfiStatus
= find_opt (&request
, DHCP4_END
, 0, &op
);
655 if (EFI_ERROR (EfiStatus
)) {
656 Private
->callback
= NULL
;
657 return EFI_INVALID_PARAMETER
;
660 op
->op
= DHCP4_MESSAGE_TYPE
;
663 op
->data
[1] = DHCP4_END
;
666 op
->data
[0] = DHCP4_MESSAGE_TYPE_REQUEST
;
669 // Copy server identifier option from offer to request.
671 EfiStatus
= find_opt (Offer
, DHCP4_SERVER_IDENTIFIER
, 0, &srvid
);
673 if (EFI_ERROR (EfiStatus
)) {
674 Private
->callback
= NULL
;
675 return EFI_INVALID_PARAMETER
;
678 if (srvid
->len
!= 4) {
679 Private
->callback
= NULL
;
680 return EFI_INVALID_PARAMETER
;
683 EfiStatus
= add_opt (&request
, srvid
);
685 if (EFI_ERROR (EfiStatus
)) {
686 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
687 Private
->callback
= NULL
;
691 // Add requested IP address option to request packet.
693 op
= (DHCP4_OP
*) buf
;
694 op
->op
= DHCP4_REQUESTED_IP_ADDRESS
;
696 CopyMem (op
->data
, &Offer
->dhcp4
.yiaddr
, 4);
698 EfiStatus
= add_opt (&request
, op
);
700 if (EFI_ERROR (EfiStatus
)) {
701 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
702 Private
->callback
= NULL
;
706 // Transimit DHCP request and wait for DHCP ack...
708 SetMem (&bcast_ip
, sizeof (EFI_IP_ADDRESS
), 0xFF);
710 EfiStatus
= tx_rx_udp (
718 &acknak_verify_initselect
,
722 if (EFI_ERROR (EfiStatus
)) {
723 DebugPrint (("%a:%d:%r\n", __FILE__
, __LINE__
, EfiStatus
));
724 Private
->callback
= NULL
;
728 // Set Data->IsAck and return.
730 EfiStatus
= find_opt (&acknak
, DHCP4_MESSAGE_TYPE
, 0, &op
);
732 if (EFI_ERROR (EfiStatus
)) {
733 Private
->callback
= NULL
;
734 return EFI_DEVICE_ERROR
;
738 Private
->callback
= NULL
;
739 return EFI_DEVICE_ERROR
;
742 switch (op
->data
[0]) {
743 case DHCP4_MESSAGE_TYPE_ACK
:
744 This
->Data
->IsAck
= TRUE
;
747 case DHCP4_MESSAGE_TYPE_NAK
:
748 This
->Data
->IsAck
= FALSE
;
752 Private
->callback
= NULL
;
753 return EFI_DEVICE_ERROR
;
756 // Copy packets into instance data...
758 CopyMem (&This
->Data
->Offer
, Offer
, sizeof (DHCP4_PACKET
));
759 CopyMem (&This
->Data
->Request
, &request
, sizeof (DHCP4_PACKET
));
760 CopyMem (&This
->Data
->AckNak
, &acknak
, sizeof (DHCP4_PACKET
));
762 This
->Data
->SelectCompleted
= TRUE
;
764 Private
->callback
= NULL
;
768 /* eof - PxeDhcp4InitSelect.c */