3 Copyright (c) 2006 - 2008, 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.
18 EFI DHCP protocol implementation
24 #include "Dhcp4Impl.h"
26 UINT32 mDhcp4DefaultTimeout
[4] = { 4, 8, 16, 32 };
30 Send an initial DISCOVER or REQUEST message according to the
31 DHCP service's current state.
33 @param DhcpSb The DHCP service instance
35 @retval EFI_SUCCESS The request has been sent
36 @retval other Some error occurs when sending the request.
41 IN DHCP_SERVICE
*DhcpSb
46 ASSERT ((DhcpSb
->DhcpState
== Dhcp4Init
) || (DhcpSb
->DhcpState
== Dhcp4InitReboot
));
48 if (DhcpSb
->DhcpState
== Dhcp4Init
) {
49 DhcpSetState (DhcpSb
, Dhcp4Selecting
, FALSE
);
50 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_DISCOVER
, NULL
);
52 if (EFI_ERROR (Status
)) {
53 DhcpSb
->DhcpState
= Dhcp4Init
;
57 DhcpSetState (DhcpSb
, Dhcp4Rebooting
, FALSE
);
58 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_REQUEST
, NULL
);
60 if (EFI_ERROR (Status
)) {
61 DhcpSb
->DhcpState
= Dhcp4InitReboot
;
71 Call user provided callback function, and return the value the
72 function returns. If the user doesn't provide a callback, a
73 proper return value is selected to let the caller continue the
76 @param DhcpSb The DHCP service instance
77 @param Event The event as defined in the spec
78 @param Packet The current packet trigger the event
79 @param NewPacket The user's return new packet
81 @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
82 @retval EFI_SUCCESS The user function returns success.
83 @retval EFI_ABORTED The user function ask it to abort.
88 IN DHCP_SERVICE
*DhcpSb
,
89 IN EFI_DHCP4_EVENT Event
,
90 IN EFI_DHCP4_PACKET
*Packet
, OPTIONAL
91 OUT EFI_DHCP4_PACKET
**NewPacket OPTIONAL
94 EFI_DHCP4_CONFIG_DATA
*Config
;
97 if (NewPacket
!= NULL
) {
102 // If user doesn't provide the call back function, return the value
103 // that directs the client to continue the normal process.
104 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
105 // the offers and select a offer, EFI_NOT_READY tells the client to
106 // collect more offers.
108 Config
= &DhcpSb
->ActiveConfig
;
110 if (Config
->Dhcp4Callback
== NULL
) {
111 if (Event
== Dhcp4RcvdOffer
) {
112 return EFI_NOT_READY
;
118 Status
= Config
->Dhcp4Callback (
119 &DhcpSb
->ActiveChild
->Dhcp4Protocol
,
120 Config
->CallbackContext
,
121 (EFI_DHCP4_STATE
) DhcpSb
->DhcpState
,
128 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
129 // and EFI_ABORTED. If it returns values other than those, assume
130 // it to be EFI_ABORTED.
132 if ((Status
== EFI_SUCCESS
) || (Status
== EFI_NOT_READY
)) {
141 Notify the user about the operation result.
143 @param DhcpSb DHCP service instance
144 @param Which Which notify function to signal
151 IN DHCP_SERVICE
*DhcpSb
,
155 DHCP_PROTOCOL
*Child
;
157 if ((Child
= DhcpSb
->ActiveChild
) == NULL
) {
161 if ((Child
->CompletionEvent
!= NULL
) &&
162 ((Which
== DHCP_NOTIFY_COMPLETION
) || (Which
== DHCP_NOTIFY_ALL
))
165 gBS
->SignalEvent (Child
->CompletionEvent
);
166 Child
->CompletionEvent
= NULL
;
169 if ((Child
->RenewRebindEvent
!= NULL
) &&
170 ((Which
== DHCP_NOTIFY_RENEWREBIND
) || (Which
== DHCP_NOTIFY_ALL
))
173 gBS
->SignalEvent (Child
->RenewRebindEvent
);
174 Child
->RenewRebindEvent
= NULL
;
181 Set the DHCP state. If CallUser is true, it will try to notify
182 the user before change the state by DhcpNotifyUser. It returns
183 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
184 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
185 the return value of this function.
187 @param DhcpSb The DHCP service instance
188 @param State The new DHCP state to change to
189 @param CallUser Whether we need to call user
191 @retval EFI_SUCCESS The state is changed
192 @retval EFI_ABORTED The user asks to abort the DHCP process.
197 IN OUT DHCP_SERVICE
*DhcpSb
,
205 Status
= EFI_SUCCESS
;
207 if (State
== Dhcp4Renewing
) {
208 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRenewing
, NULL
, NULL
);
210 } else if (State
== Dhcp4Rebinding
) {
211 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRebinding
, NULL
, NULL
);
213 } else if (State
== Dhcp4Bound
) {
214 Status
= DhcpCallUser (DhcpSb
, Dhcp4BoundCompleted
, NULL
, NULL
);
218 if (EFI_ERROR (Status
)) {
224 // Update the retransmission timer during the state transition.
225 // This will clear the retry count. This is also why the rule
226 // first transit the state, then send packets.
228 if (State
== Dhcp4Selecting
) {
229 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.DiscoverTryCount
;
231 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.RequestTryCount
;
234 if (DhcpSb
->MaxRetries
== 0) {
235 DhcpSb
->MaxRetries
= 4;
238 DhcpSb
->CurRetry
= 0;
239 DhcpSb
->PacketToLive
= 0;
241 DhcpSb
->DhcpState
= State
;
247 Set the retransmit timer for the packet. It will select from either
248 the discover timeouts/request timeouts or the default timeout values.
250 @param DhcpSb The DHCP service instance.
256 DhcpSetTransmitTimer (
257 IN OUT DHCP_SERVICE
*DhcpSb
262 ASSERT (DhcpSb
->MaxRetries
> DhcpSb
->CurRetry
);
264 if (DhcpSb
->DhcpState
== Dhcp4Selecting
) {
265 Times
= DhcpSb
->ActiveConfig
.DiscoverTimeout
;
267 Times
= DhcpSb
->ActiveConfig
.RequestTimeout
;
271 Times
= mDhcp4DefaultTimeout
;
274 DhcpSb
->PacketToLive
= Times
[DhcpSb
->CurRetry
];
280 Compute the lease. If the server grants a permanent lease, just
281 process it as a normal timeout value since the lease will last
284 @param DhcpSb The DHCP service instance
285 @param Para The DHCP parameter extracted from the server's
293 IN OUT DHCP_SERVICE
*DhcpSb
,
294 IN DHCP_PARAMETER
*Para
297 ASSERT (Para
!= NULL
);
299 DhcpSb
->Lease
= Para
->Lease
;
300 DhcpSb
->T2
= Para
->T2
;
301 DhcpSb
->T1
= Para
->T1
;
303 if (DhcpSb
->Lease
== 0) {
304 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
307 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
308 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
311 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
312 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
318 Configure a UDP IO port to use the acquired lease address.
319 DHCP driver needs this port to unicast packet to the server
320 such as DHCP release.
322 @param UdpIo The UDP IO port to configure
323 @param Context Dhcp service instance.
325 @retval EFI_SUCCESS The UDP IO port is successfully configured.
326 @retval Others It failed to configure the port.
330 DhcpConfigLeaseIoPort (
331 IN UDP_IO_PORT
*UdpIo
,
335 EFI_UDP4_CONFIG_DATA UdpConfigData
;
336 EFI_IPv4_ADDRESS Subnet
;
337 EFI_IPv4_ADDRESS Gateway
;
338 DHCP_SERVICE
*DhcpSb
;
342 DhcpSb
= (DHCP_SERVICE
*) Context
;
344 UdpConfigData
.AcceptBroadcast
= FALSE
;
345 UdpConfigData
.AcceptPromiscuous
= FALSE
;
346 UdpConfigData
.AcceptAnyPort
= FALSE
;
347 UdpConfigData
.AllowDuplicatePort
= TRUE
;
348 UdpConfigData
.TypeOfService
= 0;
349 UdpConfigData
.TimeToLive
= 64;
350 UdpConfigData
.DoNotFragment
= FALSE
;
351 UdpConfigData
.ReceiveTimeout
= 1;
352 UdpConfigData
.TransmitTimeout
= 0;
354 UdpConfigData
.UseDefaultAddress
= FALSE
;
355 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
356 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
358 Ip
= HTONL (DhcpSb
->ClientAddr
);
359 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
361 Ip
= HTONL (DhcpSb
->Netmask
);
362 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
364 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
366 Status
= UdpIo
->Udp
->Configure (UdpIo
->Udp
, &UdpConfigData
);
368 if (EFI_ERROR (Status
)) {
373 // Add a default route if received from the server.
375 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
376 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
378 Ip
= HTONL (DhcpSb
->Para
->Router
);
379 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
381 UdpIo
->Udp
->Routes (UdpIo
->Udp
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
389 Update the lease states when a new lease is acquired. It will not only
390 save the acquired the address and lease time, it will also create a UDP
391 child to provide address resolution for the address.
393 @param DhcpSb The DHCP service instance
395 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
396 @retval EFI_SUCCESS The lease is recorded.
401 IN OUT DHCP_SERVICE
*DhcpSb
406 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
408 if (DhcpSb
->Para
!= NULL
) {
409 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
410 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
413 if (DhcpSb
->Netmask
== 0) {
414 Class
= NetGetIpClass (DhcpSb
->ClientAddr
);
415 DhcpSb
->Netmask
= gIp4AllMasks
[Class
<< 3];
418 if (DhcpSb
->LeaseIoPort
!= NULL
) {
419 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
423 // Create a UDP/IP child to provide ARP service for the Leased IP,
424 // and transmit unicast packet with it as source address. Don't
425 // start receive on this port, the queued packet will be timeout.
427 DhcpSb
->LeaseIoPort
= UdpIoCreatePort (
430 DhcpConfigLeaseIoPort
,
434 if (DhcpSb
->LeaseIoPort
== NULL
) {
435 return EFI_OUT_OF_RESOURCES
;
438 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
439 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
442 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
447 Clean up the DHCP related states, IoStatus isn't reset.
449 @param DhcpSb The DHCP instance service.
456 IN DHCP_SERVICE
*DhcpSb
459 DhcpSb
->DhcpState
= Dhcp4Init
;
460 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
461 DhcpSb
->ClientAddr
= 0;
462 DhcpSb
->ServerAddr
= 0;
464 if (DhcpSb
->LastOffer
!= NULL
) {
465 gBS
->FreePool (DhcpSb
->LastOffer
);
466 DhcpSb
->LastOffer
= NULL
;
469 if (DhcpSb
->Selected
!= NULL
) {
470 gBS
->FreePool (DhcpSb
->Selected
);
471 DhcpSb
->Selected
= NULL
;
474 if (DhcpSb
->Para
!= NULL
) {
475 gBS
->FreePool (DhcpSb
->Para
);
482 DhcpSb
->ExtraRefresh
= FALSE
;
484 if (DhcpSb
->LeaseIoPort
!= NULL
) {
485 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
486 DhcpSb
->LeaseIoPort
= NULL
;
489 if (DhcpSb
->LastPacket
!= NULL
) {
490 NetbufFree (DhcpSb
->LastPacket
);
491 DhcpSb
->LastPacket
= NULL
;
494 DhcpSb
->PacketToLive
= 0;
495 DhcpSb
->CurRetry
= 0;
496 DhcpSb
->MaxRetries
= 0;
497 DhcpSb
->LeaseLife
= 0;
502 Select a offer among all the offers collected. If the offer selected is
503 of BOOTP, the lease is recorded and user notified. If the offer is of
504 DHCP, it will request the offer from the server.
506 @param DhcpSb The DHCP service instance.
508 @retval EFI_SUCCESS One of the offer is selected.
513 IN DHCP_SERVICE
*DhcpSb
516 EFI_DHCP4_PACKET
*Selected
;
517 EFI_DHCP4_PACKET
*NewPacket
;
518 EFI_DHCP4_PACKET
*TempPacket
;
521 ASSERT (DhcpSb
->LastOffer
!= NULL
);
524 // User will cache previous offers if he wants to select
525 // from multiple offers. If user provides an invalid packet,
526 // use the last offer, otherwise use the provided packet.
529 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
531 if (EFI_ERROR (Status
)) {
535 Selected
= DhcpSb
->LastOffer
;
537 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
538 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
539 if (TempPacket
!= NULL
) {
540 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
541 gBS
->FreePool (Selected
);
542 Selected
= TempPacket
;
546 DhcpSb
->Selected
= Selected
;
547 DhcpSb
->LastOffer
= NULL
;
549 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
552 // A bootp offer has been selected, save the lease status,
553 // enter bound state then notify the user.
555 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
556 Status
= DhcpLeaseAcquired (DhcpSb
);
558 if (EFI_ERROR (Status
)) {
562 DhcpSb
->IoStatus
= EFI_SUCCESS
;
563 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
568 // Send a DHCP requests
570 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
572 if (EFI_ERROR (Status
)) {
576 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
581 Terminate the current address acquire. All the allocated resources
582 are released. Be careful when calling this function. A rule related
583 to this is: only call DhcpEndSession at the highest level, such as
584 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
586 @param DhcpSb The DHCP service instance
587 @param Status The result of the DHCP process.
594 IN DHCP_SERVICE
*DhcpSb
,
598 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
599 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
601 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
604 DhcpCleanLease (DhcpSb
);
606 DhcpSb
->IoStatus
= Status
;
607 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
612 Handle packets in DHCP select state.
614 @param DhcpSb The DHCP service instance
615 @param Packet The DHCP packet received
616 @param Para The DHCP parameter extracted from the packet. That
617 is, all the option value that we care.
619 @retval EFI_SUCCESS The packet is successfully processed.
620 @retval Others Some error occured.
625 IN DHCP_SERVICE
*DhcpSb
,
626 IN EFI_DHCP4_PACKET
*Packet
,
627 IN DHCP_PARAMETER
*Para
632 Status
= EFI_SUCCESS
;
635 // First validate the message:
636 // 1. the offer is a unicast
637 // 2. if it is a DHCP message, it must contains a server ID.
638 // Don't return a error for these two case otherwise the session is ended.
640 if (!DHCP_IS_BOOTP (Para
) &&
641 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))
647 // Call the user's callback. The action according to the return is as:
648 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
649 // 2. EFI_NOT_READY: wait for more offers
650 // 3. EFI_ABORTED: abort the address acquiring.
652 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
654 if (Status
== EFI_SUCCESS
) {
655 if (DhcpSb
->LastOffer
!= NULL
) {
656 gBS
->FreePool (DhcpSb
->LastOffer
);
659 DhcpSb
->LastOffer
= Packet
;
661 return DhcpChooseOffer (DhcpSb
);
663 } else if (Status
== EFI_NOT_READY
) {
664 if (DhcpSb
->LastOffer
!= NULL
) {
665 gBS
->FreePool (DhcpSb
->LastOffer
);
668 DhcpSb
->LastOffer
= Packet
;
670 } else if (Status
== EFI_ABORTED
) {
672 // DhcpInput will end the session upon error return. Remember
673 // only to call DhcpEndSession at the top level call.
681 gBS
->FreePool (Packet
);
687 Handle packets in DHCP request state.
689 @param DhcpSb The DHCP service instance
690 @param Packet The DHCP packet received
691 @param Para The DHCP parameter extracted from the packet. That
692 is, all the option value that we care.
694 @retval EFI_SUCCESS The packet is successfully processed.
695 @retval Others Some error occured.
700 IN DHCP_SERVICE
*DhcpSb
,
701 IN EFI_DHCP4_PACKET
*Packet
,
702 IN DHCP_PARAMETER
*Para
705 EFI_DHCP4_HEADER
*Head
;
706 EFI_DHCP4_HEADER
*Selected
;
710 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
712 Head
= &Packet
->Dhcp4
.Header
;
713 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
716 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
718 if (DHCP_IS_BOOTP (Para
) ||
719 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
720 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
723 Status
= EFI_SUCCESS
;
728 // Received a NAK, end the session no matter what the user returns
730 Status
= EFI_DEVICE_ERROR
;
732 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
733 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
738 // Check whether the ACK matches the selected offer
742 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
743 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
747 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
749 if (EFI_ERROR (Status
)) {
750 Message
= (UINT8
*) "Lease is denied upon received ACK";
755 // Record the lease, transit to BOUND state, then notify the user
757 Status
= DhcpLeaseAcquired (DhcpSb
);
759 if (EFI_ERROR (Status
)) {
760 Message
= (UINT8
*) "Lease is denied upon entering bound";
764 DhcpSb
->IoStatus
= EFI_SUCCESS
;
765 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
767 gBS
->FreePool (Packet
);
771 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
774 gBS
->FreePool (Packet
);
780 Handle packets in DHCP renew/rebound state.
782 @param DhcpSb The DHCP service instance
783 @param Packet The DHCP packet received
784 @param Para The DHCP parameter extracted from the packet. That
785 is, all the option value that we care.
787 @retval EFI_SUCCESS The packet is successfully processed.
788 @retval Others Some error occured.
792 DhcpHandleRenewRebind (
793 IN DHCP_SERVICE
*DhcpSb
,
794 IN EFI_DHCP4_PACKET
*Packet
,
795 IN DHCP_PARAMETER
*Para
798 EFI_DHCP4_HEADER
*Head
;
799 EFI_DHCP4_HEADER
*Selected
;
802 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
804 Head
= &Packet
->Dhcp4
.Header
;
805 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
808 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
810 if (DHCP_IS_BOOTP (Para
) ||
811 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
812 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
815 Status
= EFI_SUCCESS
;
820 // Received a NAK, ignore the user's return then terminate the process
822 Status
= EFI_DEVICE_ERROR
;
824 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
825 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
830 // The lease is different from the selected. Don't send a DECLINE
831 // since it isn't existed in the client's FSM.
833 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
837 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
839 if (EFI_ERROR (Status
)) {
844 // Record the lease, start timer for T1 and T2,
846 DhcpComputeLease (DhcpSb
, Para
);
847 DhcpSb
->LeaseLife
= 0;
848 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
850 if (DhcpSb
->ExtraRefresh
!= 0) {
851 DhcpSb
->ExtraRefresh
= FALSE
;
853 DhcpSb
->IoStatus
= EFI_SUCCESS
;
854 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
858 gBS
->FreePool (Packet
);
864 Handle packets in DHCP reboot state.
866 @param DhcpSb The DHCP service instance
867 @param Packet The DHCP packet received
868 @param Para The DHCP parameter extracted from the packet. That
869 is, all the option value that we care.
871 @retval EFI_SUCCESS The packet is successfully processed.
872 @retval Others Some error occured.
877 IN DHCP_SERVICE
*DhcpSb
,
878 IN EFI_DHCP4_PACKET
*Packet
,
879 IN DHCP_PARAMETER
*Para
882 EFI_DHCP4_HEADER
*Head
;
885 Head
= &Packet
->Dhcp4
.Header
;
888 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
890 if (DHCP_IS_BOOTP (Para
) ||
891 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
894 Status
= EFI_SUCCESS
;
899 // If a NAK is received, transit to INIT and try again.
901 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
902 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
904 DhcpSb
->ClientAddr
= 0;
905 DhcpSb
->DhcpState
= Dhcp4Init
;
907 Status
= DhcpInitRequest (DhcpSb
);
912 // Check whether the ACK matches the selected offer
914 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
915 Status
= EFI_DEVICE_ERROR
;
919 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
920 if (EFI_ERROR (Status
)) {
925 // OK, get the parameter from server, record the lease
927 DhcpSb
->Para
= AllocatePool (sizeof (DHCP_PARAMETER
));
929 if (DhcpSb
->Para
== NULL
) {
930 Status
= EFI_OUT_OF_RESOURCES
;
934 DhcpSb
->Selected
= Packet
;
935 CopyMem (DhcpSb
->Para
, Para
, sizeof (*DhcpSb
->Para
));
937 Status
= DhcpLeaseAcquired (DhcpSb
);
939 if (EFI_ERROR (Status
)) {
943 DhcpSb
->IoStatus
= EFI_SUCCESS
;
944 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
948 gBS
->FreePool (Packet
);
954 Handle the received DHCP packets. This function drives the DHCP
957 @param UdpPacket The UDP packets received.
958 @param Points The local/remote UDP access points
959 @param IoStatus The status of the UDP receive
960 @param Context The opaque parameter to the function.
973 DHCP_SERVICE
*DhcpSb
;
974 EFI_DHCP4_HEADER
*Head
;
975 EFI_DHCP4_PACKET
*Packet
;
976 DHCP_PARAMETER
*Para
;
981 DhcpSb
= (DHCP_SERVICE
*) Context
;
984 // Don't restart receive if error occurs or DHCP is destoried.
986 if (EFI_ERROR (IoStatus
)) {
988 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
989 NetbufFree (UdpPacket
);
993 ASSERT (UdpPacket
!= NULL
);
995 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
1000 // Validate the packet received
1002 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
1007 // Copy the DHCP message to a continuous memory block
1009 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
1010 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
1012 if (Packet
== NULL
) {
1017 Head
= &Packet
->Dhcp4
.Header
;
1018 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1020 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1025 // Is this packet the answer to our packet?
1027 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1028 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1029 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1034 // Validate the options and retrieve the interested options
1037 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1038 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1039 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1045 // Call the handler for each state. The handler should return
1046 // EFI_SUCCESS if the process can go on no matter whether the
1047 // packet is ignored or not. If the return is EFI_ERROR, the
1048 // session will be terminated. Packet's ownership is handled
1049 // over to the handlers. If operation succeeds, the handler
1050 // must notify the user. It isn't necessary to do if EFI_ERROR
1051 // is returned because the DhcpEndSession will notify the user.
1053 Status
= EFI_SUCCESS
;
1055 switch (DhcpSb
->DhcpState
) {
1056 case Dhcp4Selecting
:
1057 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1060 case Dhcp4Requesting
:
1061 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1064 case Dhcp4InitReboot
:
1068 // Ignore the packet in INITREBOOT, INIT and BOUND states
1070 gBS
->FreePool (Packet
);
1071 Status
= EFI_SUCCESS
;
1075 case Dhcp4Rebinding
:
1076 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1079 case Dhcp4Rebooting
:
1080 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1085 gBS
->FreePool (Para
);
1090 if (EFI_ERROR (Status
)) {
1091 NetbufFree (UdpPacket
);
1092 DhcpEndSession (DhcpSb
, Status
);
1097 NetbufFree (UdpPacket
);
1099 if (Packet
!= NULL
) {
1100 gBS
->FreePool (Packet
);
1103 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1105 if (EFI_ERROR (Status
)) {
1106 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1114 @param Arg The packet to release
1124 gBS
->FreePool (Arg
);
1129 Release the net buffer when packet is sent.
1131 @param UdpPacket The UDP packets received.
1132 @param Points The local/remote UDP access points
1133 @param IoStatus The status of the UDP receive
1134 @param Context The opaque parameter to the function.
1143 EFI_STATUS IoStatus
,
1147 NetbufFree (Packet
);
1153 Build and transmit a DHCP message according to the current states.
1154 This function implement the Table 5. of RFC 2131. Always transits
1155 the state (as defined in Figure 5. of the same RFC) before sending
1156 a DHCP message. The table is adjusted accordingly.
1158 @param DhcpSb The DHCP service instance
1159 @param Seed The seed packet which the new packet is based on
1160 @param Para The DHCP parameter of the Seed packet
1161 @param Type The message type to send
1162 @param Msg The human readable message to include in the packet
1165 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1166 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1167 @retval EFI_SUCCESS The message is sent
1168 @retval other Other error occurs
1173 IN DHCP_SERVICE
*DhcpSb
,
1174 IN EFI_DHCP4_PACKET
*Seed
,
1175 IN DHCP_PARAMETER
*Para
,
1180 EFI_DHCP4_CONFIG_DATA
*Config
;
1181 EFI_DHCP4_PACKET
*Packet
;
1182 EFI_DHCP4_PACKET
*NewPacket
;
1183 EFI_DHCP4_HEADER
*Head
;
1184 EFI_DHCP4_HEADER
*SeedHead
;
1186 UDP_POINTS EndPoint
;
1197 // Allocate a big enough memory block to hold the DHCP packet
1199 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1202 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1205 Packet
= AllocatePool (Len
);
1207 if (Packet
== NULL
) {
1208 return EFI_OUT_OF_RESOURCES
;
1212 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1215 // Fill in the DHCP header fields
1217 Config
= &DhcpSb
->ActiveConfig
;
1221 SeedHead
= &Seed
->Dhcp4
.Header
;
1224 Head
= &Packet
->Dhcp4
.Header
;
1225 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1227 Head
->OpCode
= BOOTP_REQUEST
;
1228 Head
->HwType
= DhcpSb
->HwType
;
1229 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1230 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1231 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1233 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1234 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1237 // Append the DHCP message type
1239 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1240 Buf
= Packet
->Dhcp4
.Option
;
1241 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1244 // Append the serverid option if necessary:
1245 // 1. DHCP decline message
1246 // 2. DHCP release message
1247 // 3. DHCP request to confirm one lease.
1249 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1250 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))
1253 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1255 IpAddr
= HTONL (Para
->ServerId
);
1256 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1260 // Append the requested IP option if necessary:
1261 // 1. DHCP request to use the previously allocated address
1262 // 2. DHCP request to confirm one lease
1263 // 3. DHCP decline to decline one lease
1267 if (Type
== DHCP_MSG_REQUEST
) {
1268 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1269 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1271 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1272 ASSERT (SeedHead
!= NULL
);
1273 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1276 } else if (Type
== DHCP_MSG_DECLINE
) {
1277 ASSERT (SeedHead
!= NULL
);
1278 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1282 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1286 // Append the Max Message Length option if it isn't a DECLINE
1287 // or RELEASE to direct the server use large messages instead of
1288 // override the BOOTFILE and SERVER fields in the message head.
1290 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1291 MaxMsg
= HTONS (0xFF00);
1292 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1296 // Append the user's message if it isn't NULL
1299 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1300 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1304 // Append the user configured options
1306 if (DhcpSb
->UserOptionLen
!= 0) {
1307 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1309 // We can't use any option other than the client ID from user
1310 // if it is a DHCP decline or DHCP release .
1312 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1313 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1317 Buf
= DhcpAppendOption (
1319 Config
->OptionList
[Index
]->OpCode
,
1320 Config
->OptionList
[Index
]->Length
,
1321 Config
->OptionList
[Index
]->Data
1326 *(Buf
++) = DHCP_TAG_EOP
;
1327 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1330 // OK, the message is built, call the user to override it.
1332 Status
= EFI_SUCCESS
;
1335 if (Type
== DHCP_MSG_DISCOVER
) {
1336 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1338 } else if (Type
== DHCP_MSG_REQUEST
) {
1339 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1341 } else if (Type
== DHCP_MSG_DECLINE
) {
1342 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1345 if (EFI_ERROR (Status
)) {
1346 gBS
->FreePool (Packet
);
1350 if (NewPacket
!= NULL
) {
1351 gBS
->FreePool (Packet
);
1356 // Save the Client Address will be sent out
1359 &DhcpSb
->ClientAddressSendOut
[0],
1360 &Packet
->Dhcp4
.Header
.ClientHwAddr
[0],
1361 Packet
->Dhcp4
.Header
.HwAddrLen
1366 // Wrap it into a netbuf then send it.
1368 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1369 Frag
.Len
= Packet
->Length
;
1370 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1373 gBS
->FreePool (Packet
);
1374 return EFI_OUT_OF_RESOURCES
;
1378 // Save it as the last sent packet for retransmission
1380 if (DhcpSb
->LastPacket
!= NULL
) {
1381 NetbufFree (DhcpSb
->LastPacket
);
1385 DhcpSb
->LastPacket
= Wrap
;
1386 DhcpSetTransmitTimer (DhcpSb
);
1389 // Broadcast the message, unless we know the server address.
1390 // Use the lease UdpIo port to send the unicast packet.
1392 EndPoint
.RemoteAddr
= 0xffffffff;
1393 EndPoint
.LocalAddr
= 0;
1394 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1395 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1396 UdpIo
= DhcpSb
->UdpIo
;
1398 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1399 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1400 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1401 UdpIo
= DhcpSb
->LeaseIoPort
;
1404 ASSERT (UdpIo
!= NULL
);
1405 Status
= UdpIoSendDatagram (UdpIo
, Wrap
, &EndPoint
, 0, DhcpOnPacketSent
, DhcpSb
);
1407 if (EFI_ERROR (Status
)) {
1409 return EFI_ACCESS_DENIED
;
1417 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1418 will be retransmitted.
1420 @param DhcpSb The DHCP service instance
1422 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1423 @retval EFI_SUCCESS The packet is retransmitted.
1428 IN DHCP_SERVICE
*DhcpSb
1432 UDP_POINTS EndPoint
;
1435 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1438 // Broadcast the message, unless we know the server address.
1440 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1441 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1442 EndPoint
.RemoteAddr
= 0xffffffff;
1443 EndPoint
.LocalAddr
= 0;
1444 UdpIo
= DhcpSb
->UdpIo
;
1446 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1447 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1448 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1449 UdpIo
= DhcpSb
->LeaseIoPort
;
1452 ASSERT (UdpIo
!= NULL
);
1454 NET_GET_REF (DhcpSb
->LastPacket
);
1455 Status
= UdpIoSendDatagram (
1464 if (EFI_ERROR (Status
)) {
1465 NET_PUT_REF (DhcpSb
->LastPacket
);
1466 return EFI_ACCESS_DENIED
;
1474 Each DHCP service has three timer. Two of them are count down timer.
1475 One for the packet retransmission. The other is to collect the offers.
1476 The third timer increaments the lease life which is compared to T1, T2,
1477 and lease to determine the time to renew and rebind the lease.
1478 DhcpOnTimerTick will be called once every second.
1480 @param Event The timer event
1481 @param Context The context, which is the DHCP service instance.
1493 DHCP_SERVICE
*DhcpSb
;
1494 DHCP_PROTOCOL
*Instance
;
1497 DhcpSb
= (DHCP_SERVICE
*) Context
;
1498 Instance
= DhcpSb
->ActiveChild
;
1501 // Check the retransmit timer
1503 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1506 // Select offer at each timeout if any offer received.
1508 if (DhcpSb
->DhcpState
== Dhcp4Selecting
&& DhcpSb
->LastOffer
!= NULL
) {
1510 Status
= DhcpChooseOffer (DhcpSb
);
1512 if (EFI_ERROR(Status
)) {
1513 FreePool (DhcpSb
->LastOffer
);
1514 DhcpSb
->LastOffer
= NULL
;
1520 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1522 // Still has another try
1524 DhcpRetransmit (DhcpSb
);
1525 DhcpSetTransmitTimer (DhcpSb
);
1527 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1530 // Retransmission failed, if the DHCP request is initiated by
1531 // user, adjust the current state according to the lease life.
1532 // Otherwise do nothing to wait the lease to timeout
1534 if (DhcpSb
->ExtraRefresh
!= 0) {
1535 Status
= EFI_SUCCESS
;
1537 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1538 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1540 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1541 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1543 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1544 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1551 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1552 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1560 // If an address has been acquired, check whether need to
1561 // refresh or whether it has expired.
1563 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1564 DhcpSb
->LeaseLife
++;
1567 // Don't timeout the lease, only count the life if user is
1568 // requesting extra renew/rebind. Adjust the state after that.
1570 if (DhcpSb
->ExtraRefresh
!= 0) {
1574 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1576 // Lease expires, end the session
1580 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1582 // T2 expires, transit to rebinding then send a REQUEST to any server
1584 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1588 Status
= DhcpSendMessage (
1596 if (EFI_ERROR (Status
)) {
1600 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1602 // T1 expires, transit to renewing, then send a REQUEST to the server
1604 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1608 Status
= DhcpSendMessage (
1616 if (EFI_ERROR (Status
)) {
1623 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1624 Instance
->Timeout
--;
1625 if (Instance
->Timeout
== 0) {
1626 PxeDhcpDone (Instance
);
1633 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);