2 EFI DHCP protocol implementation.
4 Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
11 UINT32 mDhcp4DefaultTimeout
[4] = { 4, 8, 16, 32 };
14 Send an initial DISCOVER or REQUEST message according to the
15 DHCP service's current state.
17 @param[in] DhcpSb The DHCP service instance
19 @retval EFI_SUCCESS The request has been sent
20 @retval other Some error occurs when sending the request.
25 IN DHCP_SERVICE
*DhcpSb
30 ASSERT ((DhcpSb
->DhcpState
== Dhcp4Init
) || (DhcpSb
->DhcpState
== Dhcp4InitReboot
));
33 // Clear initial time to make sure that elapsed-time is set to 0 for first Discover or REQUEST message.
35 DhcpSb
->ActiveChild
->ElaspedTime
= 0;
37 if (DhcpSb
->DhcpState
== Dhcp4Init
) {
38 DhcpSetState (DhcpSb
, Dhcp4Selecting
, FALSE
);
39 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_DISCOVER
, NULL
);
41 if (EFI_ERROR (Status
)) {
42 DhcpSb
->DhcpState
= Dhcp4Init
;
46 DhcpSetState (DhcpSb
, Dhcp4Rebooting
, FALSE
);
47 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_REQUEST
, NULL
);
49 if (EFI_ERROR (Status
)) {
50 DhcpSb
->DhcpState
= Dhcp4InitReboot
;
59 Call user provided callback function, and return the value the
60 function returns. If the user doesn't provide a callback, a
61 proper return value is selected to let the caller continue the
64 @param[in] DhcpSb The DHCP service instance
65 @param[in] Event The event as defined in the spec
66 @param[in] Packet The current packet trigger the event
67 @param[out] NewPacket The user's return new packet
69 @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
70 @retval EFI_SUCCESS The user function returns success.
71 @retval EFI_ABORTED The user function ask it to abort.
76 IN DHCP_SERVICE
*DhcpSb
,
77 IN EFI_DHCP4_EVENT Event
,
78 IN EFI_DHCP4_PACKET
*Packet OPTIONAL
,
79 OUT EFI_DHCP4_PACKET
**NewPacket OPTIONAL
82 EFI_DHCP4_CONFIG_DATA
*Config
;
85 if (NewPacket
!= NULL
) {
90 // If user doesn't provide the call back function, return the value
91 // that directs the client to continue the normal process.
92 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
93 // the offers and select a offer, EFI_NOT_READY tells the client to
94 // collect more offers.
96 Config
= &DhcpSb
->ActiveConfig
;
98 if (Config
->Dhcp4Callback
== NULL
) {
99 if (Event
== Dhcp4RcvdOffer
) {
100 return EFI_NOT_READY
;
106 Status
= Config
->Dhcp4Callback (
107 &DhcpSb
->ActiveChild
->Dhcp4Protocol
,
108 Config
->CallbackContext
,
109 (EFI_DHCP4_STATE
)DhcpSb
->DhcpState
,
116 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
117 // and EFI_ABORTED. If it returns values other than those, assume
118 // it to be EFI_ABORTED.
120 if ((Status
== EFI_SUCCESS
) || (Status
== EFI_NOT_READY
)) {
128 Notify the user about the operation result.
130 @param DhcpSb DHCP service instance
131 @param Which Which notify function to signal
136 IN DHCP_SERVICE
*DhcpSb
,
140 DHCP_PROTOCOL
*Child
;
142 if ((Child
= DhcpSb
->ActiveChild
) == NULL
) {
146 if ((Child
->CompletionEvent
!= NULL
) &&
147 ((Which
== DHCP_NOTIFY_COMPLETION
) || (Which
== DHCP_NOTIFY_ALL
))
150 gBS
->SignalEvent (Child
->CompletionEvent
);
151 Child
->CompletionEvent
= NULL
;
154 if ((Child
->RenewRebindEvent
!= NULL
) &&
155 ((Which
== DHCP_NOTIFY_RENEWREBIND
) || (Which
== DHCP_NOTIFY_ALL
))
158 gBS
->SignalEvent (Child
->RenewRebindEvent
);
159 Child
->RenewRebindEvent
= NULL
;
164 Set the DHCP state. If CallUser is true, it will try to notify
165 the user before change the state by DhcpNotifyUser. It returns
166 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
167 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
168 the return value of this function.
170 @param DhcpSb The DHCP service instance
171 @param State The new DHCP state to change to
172 @param CallUser Whether we need to call user
174 @retval EFI_SUCCESS The state is changed
175 @retval EFI_ABORTED The user asks to abort the DHCP process.
180 IN OUT DHCP_SERVICE
*DhcpSb
,
188 Status
= EFI_SUCCESS
;
190 if (State
== Dhcp4Renewing
) {
191 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRenewing
, NULL
, NULL
);
192 } else if (State
== Dhcp4Rebinding
) {
193 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRebinding
, NULL
, NULL
);
194 } else if (State
== Dhcp4Bound
) {
195 Status
= DhcpCallUser (DhcpSb
, Dhcp4BoundCompleted
, NULL
, NULL
);
198 if (EFI_ERROR (Status
)) {
204 // Update the retransmission timer during the state transition.
205 // This will clear the retry count. This is also why the rule
206 // first transit the state, then send packets.
208 if (State
== Dhcp4Selecting
) {
209 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.DiscoverTryCount
;
211 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.RequestTryCount
;
214 if (DhcpSb
->MaxRetries
== 0) {
215 DhcpSb
->MaxRetries
= 4;
218 DhcpSb
->CurRetry
= 0;
219 DhcpSb
->PacketToLive
= 0;
220 DhcpSb
->LastTimeout
= 0;
221 DhcpSb
->DhcpState
= State
;
226 Set the retransmit timer for the packet. It will select from either
227 the discover timeouts/request timeouts or the default timeout values.
229 @param DhcpSb The DHCP service instance.
233 DhcpSetTransmitTimer (
234 IN OUT DHCP_SERVICE
*DhcpSb
239 ASSERT (DhcpSb
->MaxRetries
> DhcpSb
->CurRetry
);
241 if (DhcpSb
->DhcpState
== Dhcp4Selecting
) {
242 Times
= DhcpSb
->ActiveConfig
.DiscoverTimeout
;
244 Times
= DhcpSb
->ActiveConfig
.RequestTimeout
;
248 Times
= mDhcp4DefaultTimeout
;
251 DhcpSb
->PacketToLive
= Times
[DhcpSb
->CurRetry
];
252 DhcpSb
->LastTimeout
= DhcpSb
->PacketToLive
;
258 Compute the lease. If the server grants a permanent lease, just
259 process it as a normal timeout value since the lease will last
262 @param DhcpSb The DHCP service instance
263 @param Para The DHCP parameter extracted from the server's
268 IN OUT DHCP_SERVICE
*DhcpSb
,
269 IN DHCP_PARAMETER
*Para
272 ASSERT (Para
!= NULL
);
274 DhcpSb
->Lease
= Para
->Lease
;
275 DhcpSb
->T2
= Para
->T2
;
276 DhcpSb
->T1
= Para
->T1
;
278 if (DhcpSb
->Lease
== 0) {
279 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
282 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
283 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
286 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
287 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
292 Configure a UDP IO port to use the acquired lease address.
293 DHCP driver needs this port to unicast packet to the server
294 such as DHCP release.
296 @param[in] UdpIo The UDP IO to configure
297 @param[in] Context Dhcp service instance.
299 @retval EFI_SUCCESS The UDP IO port is successfully configured.
300 @retval Others It failed to configure the port.
305 DhcpConfigLeaseIoPort (
310 EFI_UDP4_CONFIG_DATA UdpConfigData
;
311 EFI_IPv4_ADDRESS Subnet
;
312 EFI_IPv4_ADDRESS Gateway
;
313 DHCP_SERVICE
*DhcpSb
;
317 DhcpSb
= (DHCP_SERVICE
*)Context
;
319 UdpConfigData
.AcceptBroadcast
= FALSE
;
320 UdpConfigData
.AcceptPromiscuous
= FALSE
;
321 UdpConfigData
.AcceptAnyPort
= FALSE
;
322 UdpConfigData
.AllowDuplicatePort
= TRUE
;
323 UdpConfigData
.TypeOfService
= 0;
324 UdpConfigData
.TimeToLive
= 64;
325 UdpConfigData
.DoNotFragment
= FALSE
;
326 UdpConfigData
.ReceiveTimeout
= 1;
327 UdpConfigData
.TransmitTimeout
= 0;
329 UdpConfigData
.UseDefaultAddress
= FALSE
;
330 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
331 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
333 Ip
= HTONL (DhcpSb
->ClientAddr
);
334 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
336 Ip
= HTONL (DhcpSb
->Netmask
);
337 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
339 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
341 Status
= UdpIo
->Protocol
.Udp4
->Configure (UdpIo
->Protocol
.Udp4
, &UdpConfigData
);
343 if (EFI_ERROR (Status
)) {
348 // Add a default route if received from the server.
350 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
351 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
353 Ip
= HTONL (DhcpSb
->Para
->Router
);
354 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
356 UdpIo
->Protocol
.Udp4
->Routes (UdpIo
->Protocol
.Udp4
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
363 Update the lease states when a new lease is acquired. It will not only
364 save the acquired the address and lease time, it will also create a UDP
365 child to provide address resolution for the address.
367 @param DhcpSb The DHCP service instance
369 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
370 @retval EFI_SUCCESS The lease is recorded.
375 IN OUT DHCP_SERVICE
*DhcpSb
378 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
380 if (DhcpSb
->Para
!= NULL
) {
381 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
382 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
385 if (DhcpSb
->Netmask
== 0) {
389 if (DhcpSb
->LeaseIoPort
!= NULL
) {
390 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
394 // Create a UDP/IP child to provide ARP service for the Leased IP,
395 // and transmit unicast packet with it as source address. Don't
396 // start receive on this port, the queued packet will be timeout.
398 DhcpSb
->LeaseIoPort
= UdpIoCreateIo (
401 DhcpConfigLeaseIoPort
,
406 if (DhcpSb
->LeaseIoPort
== NULL
) {
407 return EFI_OUT_OF_RESOURCES
;
410 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
411 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
414 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
418 Clean up the DHCP related states, IoStatus isn't reset.
420 @param DhcpSb The DHCP instance service.
425 IN DHCP_SERVICE
*DhcpSb
428 DhcpSb
->DhcpState
= Dhcp4Init
;
429 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
430 DhcpSb
->ClientAddr
= 0;
432 DhcpSb
->ServerAddr
= 0;
434 if (DhcpSb
->LastOffer
!= NULL
) {
435 FreePool (DhcpSb
->LastOffer
);
436 DhcpSb
->LastOffer
= NULL
;
439 if (DhcpSb
->Selected
!= NULL
) {
440 FreePool (DhcpSb
->Selected
);
441 DhcpSb
->Selected
= NULL
;
444 if (DhcpSb
->Para
!= NULL
) {
445 FreePool (DhcpSb
->Para
);
452 DhcpSb
->ExtraRefresh
= FALSE
;
454 if (DhcpSb
->LeaseIoPort
!= NULL
) {
455 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
456 DhcpSb
->LeaseIoPort
= NULL
;
459 if (DhcpSb
->LastPacket
!= NULL
) {
460 FreePool (DhcpSb
->LastPacket
);
461 DhcpSb
->LastPacket
= NULL
;
464 DhcpSb
->PacketToLive
= 0;
465 DhcpSb
->LastTimeout
= 0;
466 DhcpSb
->CurRetry
= 0;
467 DhcpSb
->MaxRetries
= 0;
468 DhcpSb
->LeaseLife
= 0;
471 // Clean active config data.
473 DhcpCleanConfigure (&DhcpSb
->ActiveConfig
);
477 Select a offer among all the offers collected. If the offer selected is
478 of BOOTP, the lease is recorded and user notified. If the offer is of
479 DHCP, it will request the offer from the server.
481 @param[in] DhcpSb The DHCP service instance.
483 @retval EFI_SUCCESS One of the offer is selected.
488 IN DHCP_SERVICE
*DhcpSb
491 EFI_DHCP4_PACKET
*Selected
;
492 EFI_DHCP4_PACKET
*NewPacket
;
493 EFI_DHCP4_PACKET
*TempPacket
;
496 ASSERT (DhcpSb
->LastOffer
!= NULL
);
499 // User will cache previous offers if he wants to select
500 // from multiple offers. If user provides an invalid packet,
501 // use the last offer, otherwise use the provided packet.
504 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
506 if (EFI_ERROR (Status
)) {
510 Selected
= DhcpSb
->LastOffer
;
512 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
513 TempPacket
= (EFI_DHCP4_PACKET
*)AllocatePool (NewPacket
->Size
);
514 if (TempPacket
!= NULL
) {
515 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
517 Selected
= TempPacket
;
521 DhcpSb
->Selected
= Selected
;
522 DhcpSb
->LastOffer
= NULL
;
524 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
527 // A bootp offer has been selected, save the lease status,
528 // enter bound state then notify the user.
530 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
531 Status
= DhcpLeaseAcquired (DhcpSb
);
533 if (EFI_ERROR (Status
)) {
537 DhcpSb
->IoStatus
= EFI_SUCCESS
;
538 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
543 // Send a DHCP requests
545 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
547 if (EFI_ERROR (Status
)) {
551 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
555 Terminate the current address acquire. All the allocated resources
556 are released. Be careful when calling this function. A rule related
557 to this is: only call DhcpEndSession at the highest level, such as
558 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
560 @param[in] DhcpSb The DHCP service instance
561 @param[in] Status The result of the DHCP process.
566 IN DHCP_SERVICE
*DhcpSb
,
570 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
571 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
573 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
576 DhcpCleanLease (DhcpSb
);
578 DhcpSb
->IoStatus
= Status
;
579 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
583 Handle packets in DHCP select state.
585 @param[in] DhcpSb The DHCP service instance
586 @param[in] Packet The DHCP packet received
587 @param[in] Para The DHCP parameter extracted from the packet. That
588 is, all the option value that we care.
590 @retval EFI_SUCCESS The packet is successfully processed.
591 @retval Others Some error occurred.
596 IN DHCP_SERVICE
*DhcpSb
,
597 IN EFI_DHCP4_PACKET
*Packet
,
598 IN DHCP_PARAMETER
*Para
603 Status
= EFI_SUCCESS
;
606 // First validate the message:
607 // 1. the offer is a unicast
608 // 2. if it is a DHCP message, it must contains a server ID.
609 // Don't return a error for these two case otherwise the session is ended.
611 if (!DHCP_IS_BOOTP (Para
) &&
612 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))
619 // Call the user's callback. The action according to the return is as:
620 // 1. EFI_SUCCESS: stop waiting for more offers, select the offer now
621 // 2. EFI_NOT_READY: wait for more offers
622 // 3. EFI_ABORTED: abort the address acquiring.
624 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
626 if (Status
== EFI_SUCCESS
) {
627 if (DhcpSb
->LastOffer
!= NULL
) {
628 FreePool (DhcpSb
->LastOffer
);
631 DhcpSb
->LastOffer
= Packet
;
633 return DhcpChooseOffer (DhcpSb
);
634 } else if (Status
== EFI_NOT_READY
) {
635 if (DhcpSb
->LastOffer
!= NULL
) {
636 FreePool (DhcpSb
->LastOffer
);
639 DhcpSb
->LastOffer
= Packet
;
640 } else if (Status
== EFI_ABORTED
) {
642 // DhcpInput will end the session upon error return. Remember
643 // only to call DhcpEndSession at the top level call.
656 Handle packets in DHCP request state.
658 @param[in] DhcpSb The DHCP service instance
659 @param[in] Packet The DHCP packet received
660 @param[in] Para The DHCP parameter extracted from the packet. That
661 is, all the option value that we care.
663 @retval EFI_SUCCESS The packet is successfully processed.
664 @retval Others Some error occurred.
669 IN DHCP_SERVICE
*DhcpSb
,
670 IN EFI_DHCP4_PACKET
*Packet
,
671 IN DHCP_PARAMETER
*Para
674 EFI_DHCP4_HEADER
*Head
;
675 EFI_DHCP4_HEADER
*Selected
;
679 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
681 Head
= &Packet
->Dhcp4
.Header
;
682 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
685 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
687 if (DHCP_IS_BOOTP (Para
) ||
688 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
689 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
692 Status
= EFI_SUCCESS
;
697 // Received a NAK, end the session no matter what the user returns
699 Status
= EFI_DEVICE_ERROR
;
701 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
702 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
707 // Check whether the ACK matches the selected offer
711 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
712 Message
= (UINT8
*)"Lease confirmed isn't the same as that in the offer";
716 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
718 if (EFI_ERROR (Status
)) {
719 Message
= (UINT8
*)"Lease is denied upon received ACK";
724 // Record the lease, transit to BOUND state, then notify the user
726 Status
= DhcpLeaseAcquired (DhcpSb
);
728 if (EFI_ERROR (Status
)) {
729 Message
= (UINT8
*)"Lease is denied upon entering bound";
733 DhcpSb
->IoStatus
= EFI_SUCCESS
;
734 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
740 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
748 Handle packets in DHCP renew/rebound state.
750 @param[in] DhcpSb The DHCP service instance
751 @param[in] Packet The DHCP packet received
752 @param[in] Para The DHCP parameter extracted from the packet. That
753 is, all the option value that we care.
755 @retval EFI_SUCCESS The packet is successfully processed.
756 @retval Others Some error occurred.
760 DhcpHandleRenewRebind (
761 IN DHCP_SERVICE
*DhcpSb
,
762 IN EFI_DHCP4_PACKET
*Packet
,
763 IN DHCP_PARAMETER
*Para
766 EFI_DHCP4_HEADER
*Head
;
767 EFI_DHCP4_HEADER
*Selected
;
770 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
772 Head
= &Packet
->Dhcp4
.Header
;
773 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
776 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
778 if (DHCP_IS_BOOTP (Para
) ||
779 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
780 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
783 Status
= EFI_SUCCESS
;
788 // Received a NAK, ignore the user's return then terminate the process
790 Status
= EFI_DEVICE_ERROR
;
792 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
793 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
798 // The lease is different from the selected. Don't send a DECLINE
799 // since it isn't existed in the client's FSM.
801 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
805 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
807 if (EFI_ERROR (Status
)) {
812 // Record the lease, start timer for T1 and T2,
814 DhcpComputeLease (DhcpSb
, Para
);
815 DhcpSb
->LeaseLife
= 0;
816 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
818 if (DhcpSb
->ExtraRefresh
!= 0) {
819 DhcpSb
->ExtraRefresh
= FALSE
;
821 DhcpSb
->IoStatus
= EFI_SUCCESS
;
822 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
831 Handle packets in DHCP reboot state.
833 @param[in] DhcpSb The DHCP service instance
834 @param[in] Packet The DHCP packet received
835 @param[in] Para The DHCP parameter extracted from the packet. That
836 is, all the option value that we care.
838 @retval EFI_SUCCESS The packet is successfully processed.
839 @retval Others Some error occurred.
844 IN DHCP_SERVICE
*DhcpSb
,
845 IN EFI_DHCP4_PACKET
*Packet
,
846 IN DHCP_PARAMETER
*Para
849 EFI_DHCP4_HEADER
*Head
;
852 Head
= &Packet
->Dhcp4
.Header
;
855 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
857 if (DHCP_IS_BOOTP (Para
) ||
858 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
861 Status
= EFI_SUCCESS
;
866 // If a NAK is received, transit to INIT and try again.
868 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
869 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
871 DhcpSb
->ClientAddr
= 0;
872 DhcpSb
->DhcpState
= Dhcp4Init
;
874 Status
= DhcpInitRequest (DhcpSb
);
879 // Check whether the ACK matches the selected offer
881 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
882 Status
= EFI_DEVICE_ERROR
;
886 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
887 if (EFI_ERROR (Status
)) {
892 // OK, get the parameter from server, record the lease
894 DhcpSb
->Para
= AllocateCopyPool (sizeof (DHCP_PARAMETER
), Para
);
895 if (DhcpSb
->Para
== NULL
) {
896 Status
= EFI_OUT_OF_RESOURCES
;
900 DhcpSb
->Selected
= Packet
;
901 Status
= DhcpLeaseAcquired (DhcpSb
);
902 if (EFI_ERROR (Status
)) {
906 DhcpSb
->IoStatus
= EFI_SUCCESS
;
907 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
916 Handle the received DHCP packets. This function drives the DHCP
919 @param UdpPacket The UDP packets received.
920 @param EndPoint The local/remote UDP access point
921 @param IoStatus The status of the UDP receive
922 @param Context The opaque parameter to the function.
929 UDP_END_POINT
*EndPoint
,
934 DHCP_SERVICE
*DhcpSb
;
935 EFI_DHCP4_HEADER
*Head
;
936 EFI_DHCP4_PACKET
*Packet
;
937 DHCP_PARAMETER
*Para
;
942 DhcpSb
= (DHCP_SERVICE
*)Context
;
945 // Don't restart receive if error occurs or DHCP is destroyed.
947 if (EFI_ERROR (IoStatus
)) {
949 } else if (DhcpSb
->ServiceState
== DHCP_DESTROY
) {
950 NetbufFree (UdpPacket
);
954 ASSERT (UdpPacket
!= NULL
);
956 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
961 // Validate the packet received
963 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
968 // Copy the DHCP message to a continuous memory block
970 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
971 Packet
= (EFI_DHCP4_PACKET
*)AllocatePool (Len
);
973 if (Packet
== NULL
) {
978 Head
= &Packet
->Dhcp4
.Header
;
979 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*)Head
);
981 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
986 // Is this packet the answer to our packet?
988 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
989 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
990 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0))
996 // Validate the options and retrieve the interested options
999 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1000 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1001 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
)))
1007 // Call the handler for each state. The handler should return
1008 // EFI_SUCCESS if the process can go on no matter whether the
1009 // packet is ignored or not. If the return is EFI_ERROR, the
1010 // session will be terminated. Packet's ownership is handled
1011 // over to the handlers. If operation succeeds, the handler
1012 // must notify the user. It isn't necessary to do if EFI_ERROR
1013 // is returned because the DhcpEndSession will notify the user.
1015 Status
= EFI_SUCCESS
;
1017 switch (DhcpSb
->DhcpState
) {
1018 case Dhcp4Selecting
:
1019 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1022 case Dhcp4Requesting
:
1023 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1026 case Dhcp4InitReboot
:
1030 // Ignore the packet in INITREBOOT, INIT and BOUND states
1033 Status
= EFI_SUCCESS
;
1037 case Dhcp4Rebinding
:
1038 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1041 case Dhcp4Rebooting
:
1042 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1052 if (EFI_ERROR (Status
)) {
1053 NetbufFree (UdpPacket
);
1054 UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1055 DhcpEndSession (DhcpSb
, Status
);
1060 NetbufFree (UdpPacket
);
1062 if (Packet
!= NULL
) {
1066 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1068 if (EFI_ERROR (Status
)) {
1069 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1074 Release the net buffer when packet is sent.
1076 @param UdpPacket The UDP packets received.
1077 @param EndPoint The local/remote UDP access point
1078 @param IoStatus The status of the UDP receive
1079 @param Context The opaque parameter to the function.
1086 UDP_END_POINT
*EndPoint
,
1087 EFI_STATUS IoStatus
,
1091 NetbufFree (Packet
);
1095 Build and transmit a DHCP message according to the current states.
1096 This function implement the Table 5. of RFC 2131. Always transits
1097 the state (as defined in Figure 5. of the same RFC) before sending
1098 a DHCP message. The table is adjusted accordingly.
1100 @param[in] DhcpSb The DHCP service instance
1101 @param[in] Seed The seed packet which the new packet is based on
1102 @param[in] Para The DHCP parameter of the Seed packet
1103 @param[in] Type The message type to send
1104 @param[in] Msg The human readable message to include in the packet
1107 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1108 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1109 @retval EFI_SUCCESS The message is sent
1110 @retval other Other error occurs
1115 IN DHCP_SERVICE
*DhcpSb
,
1116 IN EFI_DHCP4_PACKET
*Seed
,
1117 IN DHCP_PARAMETER
*Para
,
1122 EFI_DHCP4_CONFIG_DATA
*Config
;
1123 EFI_DHCP4_PACKET
*Packet
;
1124 EFI_DHCP4_PACKET
*NewPacket
;
1125 EFI_DHCP4_HEADER
*Head
;
1126 EFI_DHCP4_HEADER
*SeedHead
;
1128 UDP_END_POINT EndPoint
;
1139 // Allocate a big enough memory block to hold the DHCP packet
1141 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1144 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*)Msg
);
1147 Packet
= AllocatePool (Len
);
1149 if (Packet
== NULL
) {
1150 return EFI_OUT_OF_RESOURCES
;
1154 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1157 // Fill in the DHCP header fields
1159 Config
= &DhcpSb
->ActiveConfig
;
1163 SeedHead
= &Seed
->Dhcp4
.Header
;
1166 Head
= &Packet
->Dhcp4
.Header
;
1167 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1169 Head
->OpCode
= BOOTP_REQUEST
;
1170 Head
->HwType
= DhcpSb
->HwType
;
1171 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1172 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1173 Head
->Reserved
= HTONS (0x8000); // Server, broadcast the message please.
1175 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1176 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1178 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) {
1180 } else if ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
)) {
1182 // Use the same value as the original DHCPDISCOVER message.
1184 Head
->Seconds
= DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
;
1186 SetElapsedTime (&Head
->Seconds
, DhcpSb
->ActiveChild
);
1190 // Append the DHCP message type
1192 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1193 Buf
= Packet
->Dhcp4
.Option
;
1194 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_MSG_TYPE
, 1, &Type
);
1197 // Append the serverid option if necessary:
1198 // 1. DHCP decline message
1199 // 2. DHCP release message
1200 // 3. DHCP request to confirm one lease.
1202 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1203 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))
1206 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1208 IpAddr
= HTONL (Para
->ServerId
);
1209 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_SERVER_ID
, 4, (UINT8
*)&IpAddr
);
1213 // Append the requested IP option if necessary:
1214 // 1. DHCP request to use the previously allocated address
1215 // 2. DHCP request to confirm one lease
1216 // 3. DHCP decline to decline one lease
1220 if (Type
== DHCP_MSG_REQUEST
) {
1221 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1222 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1223 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1224 ASSERT (SeedHead
!= NULL
);
1225 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1227 } else if (Type
== DHCP_MSG_DECLINE
) {
1228 ASSERT (SeedHead
!= NULL
);
1229 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1233 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_REQUEST_IP
, 4, (UINT8
*)&IpAddr
);
1237 // Append the Max Message Length option if it isn't a DECLINE
1238 // or RELEASE to direct the server use large messages instead of
1239 // override the BOOTFILE and SERVER fields in the message head.
1241 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1242 MaxMsg
= HTONS (0xFF00);
1243 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_MAXMSG
, 2, (UINT8
*)&MaxMsg
);
1247 // Append the user's message if it isn't NULL
1250 Len
= MIN ((UINT32
)AsciiStrLen ((CHAR8
*)Msg
), 255);
1251 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_MESSAGE
, (UINT16
)Len
, Msg
);
1255 // Append the user configured options
1257 if (DhcpSb
->UserOptionLen
!= 0) {
1258 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1260 // We can't use any option other than the client ID from user
1261 // if it is a DHCP decline or DHCP release .
1263 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1264 (Config
->OptionList
[Index
]->OpCode
!= DHCP4_TAG_CLIENT_ID
))
1269 Buf
= DhcpAppendOption (
1271 Config
->OptionList
[Index
]->OpCode
,
1272 Config
->OptionList
[Index
]->Length
,
1273 Config
->OptionList
[Index
]->Data
1278 *(Buf
++) = DHCP4_TAG_EOP
;
1279 Packet
->Length
+= (UINT32
)(Buf
- Packet
->Dhcp4
.Option
);
1282 // OK, the message is built, call the user to override it.
1284 Status
= EFI_SUCCESS
;
1287 if (Type
== DHCP_MSG_DISCOVER
) {
1288 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1289 } else if (Type
== DHCP_MSG_REQUEST
) {
1290 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1291 } else if (Type
== DHCP_MSG_DECLINE
) {
1292 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1295 if (EFI_ERROR (Status
)) {
1300 if (NewPacket
!= NULL
) {
1306 // Save the Client Address will be sent out
1309 &DhcpSb
->ClientAddressSendOut
[0],
1310 &Packet
->Dhcp4
.Header
.ClientHwAddr
[0],
1311 Packet
->Dhcp4
.Header
.HwAddrLen
1315 // Wrap it into a netbuf then send it.
1317 Frag
.Bulk
= (UINT8
*)&Packet
->Dhcp4
.Header
;
1318 Frag
.Len
= Packet
->Length
;
1319 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpDummyExtFree
, NULL
);
1323 return EFI_OUT_OF_RESOURCES
;
1327 // Save it as the last sent packet for retransmission
1329 if (DhcpSb
->LastPacket
!= NULL
) {
1330 FreePool (DhcpSb
->LastPacket
);
1333 DhcpSb
->LastPacket
= Packet
;
1334 DhcpSetTransmitTimer (DhcpSb
);
1337 // Broadcast the message, unless we know the server address.
1338 // Use the lease UdpIo port to send the unicast packet.
1340 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1341 EndPoint
.LocalAddr
.Addr
[0] = 0;
1342 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1343 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1344 UdpIo
= DhcpSb
->UdpIo
;
1346 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1347 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1348 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1349 UdpIo
= DhcpSb
->LeaseIoPort
;
1352 ASSERT (UdpIo
!= NULL
);
1354 Status
= UdpIoSendDatagram (
1363 if (EFI_ERROR (Status
)) {
1365 return EFI_ACCESS_DENIED
;
1372 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1373 will be retransmitted.
1375 @param[in] DhcpSb The DHCP service instance
1377 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1378 @retval EFI_SUCCESS The packet is retransmitted.
1383 IN DHCP_SERVICE
*DhcpSb
1387 UDP_END_POINT EndPoint
;
1392 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1395 // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.
1397 if (DhcpSb
->DhcpState
!= Dhcp4Requesting
) {
1398 SetElapsedTime (&DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
, DhcpSb
->ActiveChild
);
1402 // Wrap it into a netbuf then send it.
1404 Frag
.Bulk
= (UINT8
*)&DhcpSb
->LastPacket
->Dhcp4
.Header
;
1405 Frag
.Len
= DhcpSb
->LastPacket
->Length
;
1406 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpDummyExtFree
, NULL
);
1409 return EFI_OUT_OF_RESOURCES
;
1413 // Broadcast the message, unless we know the server address.
1415 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1416 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1417 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1418 EndPoint
.LocalAddr
.Addr
[0] = 0;
1419 UdpIo
= DhcpSb
->UdpIo
;
1421 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1422 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1423 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1424 UdpIo
= DhcpSb
->LeaseIoPort
;
1427 ASSERT (UdpIo
!= NULL
);
1429 Status
= UdpIoSendDatagram (
1438 if (EFI_ERROR (Status
)) {
1440 return EFI_ACCESS_DENIED
;
1447 Each DHCP service has three timer. Two of them are count down timer.
1448 One for the packet retransmission. The other is to collect the offers.
1449 The third timer increments the lease life which is compared to T1, T2,
1450 and lease to determine the time to renew and rebind the lease.
1451 DhcpOnTimerTick will be called once every second.
1453 @param[in] Event The timer event
1454 @param[in] Context The context, which is the DHCP service instance.
1466 DHCP_SERVICE
*DhcpSb
;
1467 DHCP_PROTOCOL
*Instance
;
1470 DhcpSb
= (DHCP_SERVICE
*)Context
;
1471 Instance
= DhcpSb
->ActiveChild
;
1474 // 0xffff is the maximum supported value for elapsed time according to RFC.
1476 if ((Instance
!= NULL
) && (Instance
->ElaspedTime
< 0xffff)) {
1477 Instance
->ElaspedTime
++;
1481 // Check the retransmit timer
1483 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1485 // Select offer at each timeout if any offer received.
1487 if ((DhcpSb
->DhcpState
== Dhcp4Selecting
) && (DhcpSb
->LastOffer
!= NULL
)) {
1488 Status
= DhcpChooseOffer (DhcpSb
);
1490 if (EFI_ERROR (Status
)) {
1491 if (DhcpSb
->LastOffer
!= NULL
) {
1492 FreePool (DhcpSb
->LastOffer
);
1493 DhcpSb
->LastOffer
= NULL
;
1500 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1502 // Still has another try
1504 DhcpRetransmit (DhcpSb
);
1505 DhcpSetTransmitTimer (DhcpSb
);
1506 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1508 // Retransmission failed, if the DHCP request is initiated by
1509 // user, adjust the current state according to the lease life.
1510 // Otherwise do nothing to wait the lease to timeout
1512 if (DhcpSb
->ExtraRefresh
!= 0) {
1513 Status
= EFI_SUCCESS
;
1515 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1516 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1517 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1518 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1519 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1520 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1525 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1526 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1534 // If an address has been acquired, check whether need to
1535 // refresh or whether it has expired.
1537 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1538 DhcpSb
->LeaseLife
++;
1541 // Don't timeout the lease, only count the life if user is
1542 // requesting extra renew/rebind. Adjust the state after that.
1544 if (DhcpSb
->ExtraRefresh
!= 0) {
1548 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1550 // Lease expires, end the session
1553 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1555 // T2 expires, transit to rebinding then send a REQUEST to any server
1557 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1561 if (Instance
!= NULL
) {
1562 Instance
->ElaspedTime
= 0;
1565 Status
= DhcpSendMessage (
1573 if (EFI_ERROR (Status
)) {
1576 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1578 // T1 expires, transit to renewing, then send a REQUEST to the server
1580 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1584 if (Instance
!= NULL
) {
1585 Instance
->ElaspedTime
= 0;
1588 Status
= DhcpSendMessage (
1596 if (EFI_ERROR (Status
)) {
1604 // Iterate through all the DhcpSb Children.
1606 NET_LIST_FOR_EACH_SAFE (Entry
, Next
, &DhcpSb
->Children
) {
1607 Instance
= NET_LIST_USER_STRUCT (Entry
, DHCP_PROTOCOL
, Link
);
1608 Instance
->Timeout
--;
1609 if ((Instance
->Timeout
== 0) && (Instance
->Token
!= NULL
)) {
1610 PxeDhcpDone (Instance
);
1617 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);