2 EFI DHCP protocol implementation.
4 Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include "Dhcp4Impl.h"
12 UINT32 mDhcp4DefaultTimeout
[4] = { 4, 8, 16, 32 };
16 Send an initial DISCOVER or REQUEST message according to the
17 DHCP service's current state.
19 @param[in] DhcpSb The DHCP service instance
21 @retval EFI_SUCCESS The request has been sent
22 @retval other Some error occurs when sending the request.
27 IN DHCP_SERVICE
*DhcpSb
32 ASSERT ((DhcpSb
->DhcpState
== Dhcp4Init
) || (DhcpSb
->DhcpState
== Dhcp4InitReboot
));
35 // Clear initial time to make sure that elapsed-time is set to 0 for first Discover or REQUEST message.
37 DhcpSb
->ActiveChild
->ElaspedTime
= 0;
39 if (DhcpSb
->DhcpState
== Dhcp4Init
) {
40 DhcpSetState (DhcpSb
, Dhcp4Selecting
, FALSE
);
41 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_DISCOVER
, NULL
);
43 if (EFI_ERROR (Status
)) {
44 DhcpSb
->DhcpState
= Dhcp4Init
;
48 DhcpSetState (DhcpSb
, Dhcp4Rebooting
, FALSE
);
49 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_REQUEST
, NULL
);
51 if (EFI_ERROR (Status
)) {
52 DhcpSb
->DhcpState
= Dhcp4InitReboot
;
62 Call user provided callback function, and return the value the
63 function returns. If the user doesn't provide a callback, a
64 proper return value is selected to let the caller continue the
67 @param[in] DhcpSb The DHCP service instance
68 @param[in] Event The event as defined in the spec
69 @param[in] Packet The current packet trigger the event
70 @param[out] NewPacket The user's return new packet
72 @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
73 @retval EFI_SUCCESS The user function returns success.
74 @retval EFI_ABORTED The user function ask it to abort.
79 IN DHCP_SERVICE
*DhcpSb
,
80 IN EFI_DHCP4_EVENT Event
,
81 IN EFI_DHCP4_PACKET
*Packet
, OPTIONAL
82 OUT EFI_DHCP4_PACKET
**NewPacket OPTIONAL
85 EFI_DHCP4_CONFIG_DATA
*Config
;
88 if (NewPacket
!= NULL
) {
93 // If user doesn't provide the call back function, return the value
94 // that directs the client to continue the normal process.
95 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
96 // the offers and select a offer, EFI_NOT_READY tells the client to
97 // collect more offers.
99 Config
= &DhcpSb
->ActiveConfig
;
101 if (Config
->Dhcp4Callback
== NULL
) {
102 if (Event
== Dhcp4RcvdOffer
) {
103 return EFI_NOT_READY
;
109 Status
= Config
->Dhcp4Callback (
110 &DhcpSb
->ActiveChild
->Dhcp4Protocol
,
111 Config
->CallbackContext
,
112 (EFI_DHCP4_STATE
) DhcpSb
->DhcpState
,
119 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
120 // and EFI_ABORTED. If it returns values other than those, assume
121 // it to be EFI_ABORTED.
123 if ((Status
== EFI_SUCCESS
) || (Status
== EFI_NOT_READY
)) {
132 Notify the user about the operation result.
134 @param DhcpSb DHCP service instance
135 @param Which Which notify function to signal
140 IN DHCP_SERVICE
*DhcpSb
,
144 DHCP_PROTOCOL
*Child
;
146 if ((Child
= DhcpSb
->ActiveChild
) == NULL
) {
150 if ((Child
->CompletionEvent
!= NULL
) &&
151 ((Which
== DHCP_NOTIFY_COMPLETION
) || (Which
== DHCP_NOTIFY_ALL
))
154 gBS
->SignalEvent (Child
->CompletionEvent
);
155 Child
->CompletionEvent
= NULL
;
158 if ((Child
->RenewRebindEvent
!= NULL
) &&
159 ((Which
== DHCP_NOTIFY_RENEWREBIND
) || (Which
== DHCP_NOTIFY_ALL
))
162 gBS
->SignalEvent (Child
->RenewRebindEvent
);
163 Child
->RenewRebindEvent
= NULL
;
170 Set the DHCP state. If CallUser is true, it will try to notify
171 the user before change the state by DhcpNotifyUser. It returns
172 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
173 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
174 the return value of this function.
176 @param DhcpSb The DHCP service instance
177 @param State The new DHCP state to change to
178 @param CallUser Whether we need to call user
180 @retval EFI_SUCCESS The state is changed
181 @retval EFI_ABORTED The user asks to abort the DHCP process.
186 IN OUT DHCP_SERVICE
*DhcpSb
,
194 Status
= EFI_SUCCESS
;
196 if (State
== Dhcp4Renewing
) {
197 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRenewing
, NULL
, NULL
);
199 } else if (State
== Dhcp4Rebinding
) {
200 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRebinding
, NULL
, NULL
);
202 } else if (State
== Dhcp4Bound
) {
203 Status
= DhcpCallUser (DhcpSb
, Dhcp4BoundCompleted
, NULL
, NULL
);
207 if (EFI_ERROR (Status
)) {
213 // Update the retransmission timer during the state transition.
214 // This will clear the retry count. This is also why the rule
215 // first transit the state, then send packets.
217 if (State
== Dhcp4Selecting
) {
218 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.DiscoverTryCount
;
220 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.RequestTryCount
;
223 if (DhcpSb
->MaxRetries
== 0) {
224 DhcpSb
->MaxRetries
= 4;
227 DhcpSb
->CurRetry
= 0;
228 DhcpSb
->PacketToLive
= 0;
229 DhcpSb
->LastTimeout
= 0;
230 DhcpSb
->DhcpState
= State
;
236 Set the retransmit timer for the packet. It will select from either
237 the discover timeouts/request timeouts or the default timeout values.
239 @param DhcpSb The DHCP service instance.
243 DhcpSetTransmitTimer (
244 IN OUT DHCP_SERVICE
*DhcpSb
249 ASSERT (DhcpSb
->MaxRetries
> DhcpSb
->CurRetry
);
251 if (DhcpSb
->DhcpState
== Dhcp4Selecting
) {
252 Times
= DhcpSb
->ActiveConfig
.DiscoverTimeout
;
254 Times
= DhcpSb
->ActiveConfig
.RequestTimeout
;
258 Times
= mDhcp4DefaultTimeout
;
261 DhcpSb
->PacketToLive
= Times
[DhcpSb
->CurRetry
];
262 DhcpSb
->LastTimeout
= DhcpSb
->PacketToLive
;
268 Compute the lease. If the server grants a permanent lease, just
269 process it as a normal timeout value since the lease will last
272 @param DhcpSb The DHCP service instance
273 @param Para The DHCP parameter extracted from the server's
278 IN OUT DHCP_SERVICE
*DhcpSb
,
279 IN DHCP_PARAMETER
*Para
282 ASSERT (Para
!= NULL
);
284 DhcpSb
->Lease
= Para
->Lease
;
285 DhcpSb
->T2
= Para
->T2
;
286 DhcpSb
->T1
= Para
->T1
;
288 if (DhcpSb
->Lease
== 0) {
289 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
292 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
293 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
296 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
297 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
303 Configure a UDP IO port to use the acquired lease address.
304 DHCP driver needs this port to unicast packet to the server
305 such as DHCP release.
307 @param[in] UdpIo The UDP IO to configure
308 @param[in] Context Dhcp service instance.
310 @retval EFI_SUCCESS The UDP IO port is successfully configured.
311 @retval Others It failed to configure the port.
316 DhcpConfigLeaseIoPort (
321 EFI_UDP4_CONFIG_DATA UdpConfigData
;
322 EFI_IPv4_ADDRESS Subnet
;
323 EFI_IPv4_ADDRESS Gateway
;
324 DHCP_SERVICE
*DhcpSb
;
328 DhcpSb
= (DHCP_SERVICE
*) Context
;
330 UdpConfigData
.AcceptBroadcast
= FALSE
;
331 UdpConfigData
.AcceptPromiscuous
= FALSE
;
332 UdpConfigData
.AcceptAnyPort
= FALSE
;
333 UdpConfigData
.AllowDuplicatePort
= TRUE
;
334 UdpConfigData
.TypeOfService
= 0;
335 UdpConfigData
.TimeToLive
= 64;
336 UdpConfigData
.DoNotFragment
= FALSE
;
337 UdpConfigData
.ReceiveTimeout
= 1;
338 UdpConfigData
.TransmitTimeout
= 0;
340 UdpConfigData
.UseDefaultAddress
= FALSE
;
341 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
342 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
344 Ip
= HTONL (DhcpSb
->ClientAddr
);
345 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
347 Ip
= HTONL (DhcpSb
->Netmask
);
348 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
350 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
352 Status
= UdpIo
->Protocol
.Udp4
->Configure (UdpIo
->Protocol
.Udp4
, &UdpConfigData
);
354 if (EFI_ERROR (Status
)) {
359 // Add a default route if received from the server.
361 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
362 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
364 Ip
= HTONL (DhcpSb
->Para
->Router
);
365 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
367 UdpIo
->Protocol
.Udp4
->Routes (UdpIo
->Protocol
.Udp4
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
375 Update the lease states when a new lease is acquired. It will not only
376 save the acquired the address and lease time, it will also create a UDP
377 child to provide address resolution for the address.
379 @param DhcpSb The DHCP service instance
381 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
382 @retval EFI_SUCCESS The lease is recorded.
387 IN OUT DHCP_SERVICE
*DhcpSb
390 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
392 if (DhcpSb
->Para
!= NULL
) {
393 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
394 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
397 if (DhcpSb
->Netmask
== 0) {
401 if (DhcpSb
->LeaseIoPort
!= NULL
) {
402 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
406 // Create a UDP/IP child to provide ARP service for the Leased IP,
407 // and transmit unicast packet with it as source address. Don't
408 // start receive on this port, the queued packet will be timeout.
410 DhcpSb
->LeaseIoPort
= UdpIoCreateIo (
413 DhcpConfigLeaseIoPort
,
418 if (DhcpSb
->LeaseIoPort
== NULL
) {
419 return EFI_OUT_OF_RESOURCES
;
422 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
423 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
426 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
431 Clean up the DHCP related states, IoStatus isn't reset.
433 @param DhcpSb The DHCP instance service.
438 IN DHCP_SERVICE
*DhcpSb
441 DhcpSb
->DhcpState
= Dhcp4Init
;
442 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
443 DhcpSb
->ClientAddr
= 0;
445 DhcpSb
->ServerAddr
= 0;
447 if (DhcpSb
->LastOffer
!= NULL
) {
448 FreePool (DhcpSb
->LastOffer
);
449 DhcpSb
->LastOffer
= NULL
;
452 if (DhcpSb
->Selected
!= NULL
) {
453 FreePool (DhcpSb
->Selected
);
454 DhcpSb
->Selected
= NULL
;
457 if (DhcpSb
->Para
!= NULL
) {
458 FreePool (DhcpSb
->Para
);
465 DhcpSb
->ExtraRefresh
= FALSE
;
467 if (DhcpSb
->LeaseIoPort
!= NULL
) {
468 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
469 DhcpSb
->LeaseIoPort
= NULL
;
472 if (DhcpSb
->LastPacket
!= NULL
) {
473 FreePool (DhcpSb
->LastPacket
);
474 DhcpSb
->LastPacket
= NULL
;
477 DhcpSb
->PacketToLive
= 0;
478 DhcpSb
->LastTimeout
= 0;
479 DhcpSb
->CurRetry
= 0;
480 DhcpSb
->MaxRetries
= 0;
481 DhcpSb
->LeaseLife
= 0;
484 // Clean active config data.
486 DhcpCleanConfigure (&DhcpSb
->ActiveConfig
);
491 Select a offer among all the offers collected. If the offer selected is
492 of BOOTP, the lease is recorded and user notified. If the offer is of
493 DHCP, it will request the offer from the server.
495 @param[in] DhcpSb The DHCP service instance.
497 @retval EFI_SUCCESS One of the offer is selected.
502 IN DHCP_SERVICE
*DhcpSb
505 EFI_DHCP4_PACKET
*Selected
;
506 EFI_DHCP4_PACKET
*NewPacket
;
507 EFI_DHCP4_PACKET
*TempPacket
;
510 ASSERT (DhcpSb
->LastOffer
!= NULL
);
513 // User will cache previous offers if he wants to select
514 // from multiple offers. If user provides an invalid packet,
515 // use the last offer, otherwise use the provided packet.
518 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
520 if (EFI_ERROR (Status
)) {
524 Selected
= DhcpSb
->LastOffer
;
526 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
527 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
528 if (TempPacket
!= NULL
) {
529 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
531 Selected
= TempPacket
;
535 DhcpSb
->Selected
= Selected
;
536 DhcpSb
->LastOffer
= NULL
;
538 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
541 // A bootp offer has been selected, save the lease status,
542 // enter bound state then notify the user.
544 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
545 Status
= DhcpLeaseAcquired (DhcpSb
);
547 if (EFI_ERROR (Status
)) {
551 DhcpSb
->IoStatus
= EFI_SUCCESS
;
552 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
557 // Send a DHCP requests
559 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
561 if (EFI_ERROR (Status
)) {
565 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
570 Terminate the current address acquire. All the allocated resources
571 are released. Be careful when calling this function. A rule related
572 to this is: only call DhcpEndSession at the highest level, such as
573 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
575 @param[in] DhcpSb The DHCP service instance
576 @param[in] Status The result of the DHCP process.
581 IN DHCP_SERVICE
*DhcpSb
,
585 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
586 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
588 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
591 DhcpCleanLease (DhcpSb
);
593 DhcpSb
->IoStatus
= Status
;
594 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
599 Handle packets in DHCP select state.
601 @param[in] DhcpSb The DHCP service instance
602 @param[in] Packet The DHCP packet received
603 @param[in] Para The DHCP parameter extracted from the packet. That
604 is, all the option value that we care.
606 @retval EFI_SUCCESS The packet is successfully processed.
607 @retval Others Some error occurred.
612 IN DHCP_SERVICE
*DhcpSb
,
613 IN EFI_DHCP4_PACKET
*Packet
,
614 IN DHCP_PARAMETER
*Para
619 Status
= EFI_SUCCESS
;
622 // First validate the message:
623 // 1. the offer is a unicast
624 // 2. if it is a DHCP message, it must contains a server ID.
625 // Don't return a error for these two case otherwise the session is ended.
627 if (!DHCP_IS_BOOTP (Para
) &&
628 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))
634 // Call the user's callback. The action according to the return is as:
635 // 1. EFI_SUCCESS: stop waiting for more offers, select the offer now
636 // 2. EFI_NOT_READY: wait for more offers
637 // 3. EFI_ABORTED: abort the address acquiring.
639 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
641 if (Status
== EFI_SUCCESS
) {
642 if (DhcpSb
->LastOffer
!= NULL
) {
643 FreePool (DhcpSb
->LastOffer
);
646 DhcpSb
->LastOffer
= Packet
;
648 return DhcpChooseOffer (DhcpSb
);
650 } else if (Status
== EFI_NOT_READY
) {
651 if (DhcpSb
->LastOffer
!= NULL
) {
652 FreePool (DhcpSb
->LastOffer
);
655 DhcpSb
->LastOffer
= Packet
;
657 } else if (Status
== EFI_ABORTED
) {
659 // DhcpInput will end the session upon error return. Remember
660 // only to call DhcpEndSession at the top level call.
674 Handle packets in DHCP request state.
676 @param[in] DhcpSb The DHCP service instance
677 @param[in] Packet The DHCP packet received
678 @param[in] Para The DHCP parameter extracted from the packet. That
679 is, all the option value that we care.
681 @retval EFI_SUCCESS The packet is successfully processed.
682 @retval Others Some error occurred.
687 IN DHCP_SERVICE
*DhcpSb
,
688 IN EFI_DHCP4_PACKET
*Packet
,
689 IN DHCP_PARAMETER
*Para
692 EFI_DHCP4_HEADER
*Head
;
693 EFI_DHCP4_HEADER
*Selected
;
697 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
699 Head
= &Packet
->Dhcp4
.Header
;
700 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
703 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
705 if (DHCP_IS_BOOTP (Para
) ||
706 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
707 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
710 Status
= EFI_SUCCESS
;
715 // Received a NAK, end the session no matter what the user returns
717 Status
= EFI_DEVICE_ERROR
;
719 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
720 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
725 // Check whether the ACK matches the selected offer
729 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
730 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
734 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
736 if (EFI_ERROR (Status
)) {
737 Message
= (UINT8
*) "Lease is denied upon received ACK";
742 // Record the lease, transit to BOUND state, then notify the user
744 Status
= DhcpLeaseAcquired (DhcpSb
);
746 if (EFI_ERROR (Status
)) {
747 Message
= (UINT8
*) "Lease is denied upon entering bound";
751 DhcpSb
->IoStatus
= EFI_SUCCESS
;
752 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
758 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
767 Handle packets in DHCP renew/rebound state.
769 @param[in] DhcpSb The DHCP service instance
770 @param[in] Packet The DHCP packet received
771 @param[in] Para The DHCP parameter extracted from the packet. That
772 is, all the option value that we care.
774 @retval EFI_SUCCESS The packet is successfully processed.
775 @retval Others Some error occurred.
779 DhcpHandleRenewRebind (
780 IN DHCP_SERVICE
*DhcpSb
,
781 IN EFI_DHCP4_PACKET
*Packet
,
782 IN DHCP_PARAMETER
*Para
785 EFI_DHCP4_HEADER
*Head
;
786 EFI_DHCP4_HEADER
*Selected
;
789 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
791 Head
= &Packet
->Dhcp4
.Header
;
792 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
795 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
797 if (DHCP_IS_BOOTP (Para
) ||
798 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
799 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
802 Status
= EFI_SUCCESS
;
807 // Received a NAK, ignore the user's return then terminate the process
809 Status
= EFI_DEVICE_ERROR
;
811 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
812 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
817 // The lease is different from the selected. Don't send a DECLINE
818 // since it isn't existed in the client's FSM.
820 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
824 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
826 if (EFI_ERROR (Status
)) {
831 // Record the lease, start timer for T1 and T2,
833 DhcpComputeLease (DhcpSb
, Para
);
834 DhcpSb
->LeaseLife
= 0;
835 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
837 if (DhcpSb
->ExtraRefresh
!= 0) {
838 DhcpSb
->ExtraRefresh
= FALSE
;
840 DhcpSb
->IoStatus
= EFI_SUCCESS
;
841 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
851 Handle packets in DHCP reboot state.
853 @param[in] DhcpSb The DHCP service instance
854 @param[in] Packet The DHCP packet received
855 @param[in] Para The DHCP parameter extracted from the packet. That
856 is, all the option value that we care.
858 @retval EFI_SUCCESS The packet is successfully processed.
859 @retval Others Some error occurred.
864 IN DHCP_SERVICE
*DhcpSb
,
865 IN EFI_DHCP4_PACKET
*Packet
,
866 IN DHCP_PARAMETER
*Para
869 EFI_DHCP4_HEADER
*Head
;
872 Head
= &Packet
->Dhcp4
.Header
;
875 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
877 if (DHCP_IS_BOOTP (Para
) ||
878 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
881 Status
= EFI_SUCCESS
;
886 // If a NAK is received, transit to INIT and try again.
888 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
889 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
891 DhcpSb
->ClientAddr
= 0;
892 DhcpSb
->DhcpState
= Dhcp4Init
;
894 Status
= DhcpInitRequest (DhcpSb
);
899 // Check whether the ACK matches the selected offer
901 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
902 Status
= EFI_DEVICE_ERROR
;
906 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
907 if (EFI_ERROR (Status
)) {
912 // OK, get the parameter from server, record the lease
914 DhcpSb
->Para
= AllocateCopyPool (sizeof (DHCP_PARAMETER
), Para
);
915 if (DhcpSb
->Para
== NULL
) {
916 Status
= EFI_OUT_OF_RESOURCES
;
920 DhcpSb
->Selected
= Packet
;
921 Status
= DhcpLeaseAcquired (DhcpSb
);
922 if (EFI_ERROR (Status
)) {
926 DhcpSb
->IoStatus
= EFI_SUCCESS
;
927 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
937 Handle the received DHCP packets. This function drives the DHCP
940 @param UdpPacket The UDP packets received.
941 @param EndPoint The local/remote UDP access point
942 @param IoStatus The status of the UDP receive
943 @param Context The opaque parameter to the function.
950 UDP_END_POINT
*EndPoint
,
955 DHCP_SERVICE
*DhcpSb
;
956 EFI_DHCP4_HEADER
*Head
;
957 EFI_DHCP4_PACKET
*Packet
;
958 DHCP_PARAMETER
*Para
;
963 DhcpSb
= (DHCP_SERVICE
*) Context
;
966 // Don't restart receive if error occurs or DHCP is destroyed.
968 if (EFI_ERROR (IoStatus
)) {
970 } else if (DhcpSb
->ServiceState
== DHCP_DESTROY
) {
971 NetbufFree (UdpPacket
);
975 ASSERT (UdpPacket
!= NULL
);
977 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
982 // Validate the packet received
984 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
989 // Copy the DHCP message to a continuous memory block
991 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
992 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
994 if (Packet
== NULL
) {
999 Head
= &Packet
->Dhcp4
.Header
;
1000 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1002 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1007 // Is this packet the answer to our packet?
1009 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1010 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1011 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1016 // Validate the options and retrieve the interested options
1019 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1020 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1021 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1027 // Call the handler for each state. The handler should return
1028 // EFI_SUCCESS if the process can go on no matter whether the
1029 // packet is ignored or not. If the return is EFI_ERROR, the
1030 // session will be terminated. Packet's ownership is handled
1031 // over to the handlers. If operation succeeds, the handler
1032 // must notify the user. It isn't necessary to do if EFI_ERROR
1033 // is returned because the DhcpEndSession will notify the user.
1035 Status
= EFI_SUCCESS
;
1037 switch (DhcpSb
->DhcpState
) {
1038 case Dhcp4Selecting
:
1039 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1042 case Dhcp4Requesting
:
1043 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1046 case Dhcp4InitReboot
:
1050 // Ignore the packet in INITREBOOT, INIT and BOUND states
1053 Status
= EFI_SUCCESS
;
1057 case Dhcp4Rebinding
:
1058 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1061 case Dhcp4Rebooting
:
1062 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1072 if (EFI_ERROR (Status
)) {
1073 NetbufFree (UdpPacket
);
1074 UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1075 DhcpEndSession (DhcpSb
, Status
);
1080 NetbufFree (UdpPacket
);
1082 if (Packet
!= NULL
) {
1086 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1088 if (EFI_ERROR (Status
)) {
1089 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1094 Release the net buffer when packet is sent.
1096 @param UdpPacket The UDP packets received.
1097 @param EndPoint The local/remote UDP access point
1098 @param IoStatus The status of the UDP receive
1099 @param Context The opaque parameter to the function.
1106 UDP_END_POINT
*EndPoint
,
1107 EFI_STATUS IoStatus
,
1111 NetbufFree (Packet
);
1117 Build and transmit a DHCP message according to the current states.
1118 This function implement the Table 5. of RFC 2131. Always transits
1119 the state (as defined in Figure 5. of the same RFC) before sending
1120 a DHCP message. The table is adjusted accordingly.
1122 @param[in] DhcpSb The DHCP service instance
1123 @param[in] Seed The seed packet which the new packet is based on
1124 @param[in] Para The DHCP parameter of the Seed packet
1125 @param[in] Type The message type to send
1126 @param[in] Msg The human readable message to include in the packet
1129 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1130 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1131 @retval EFI_SUCCESS The message is sent
1132 @retval other Other error occurs
1137 IN DHCP_SERVICE
*DhcpSb
,
1138 IN EFI_DHCP4_PACKET
*Seed
,
1139 IN DHCP_PARAMETER
*Para
,
1144 EFI_DHCP4_CONFIG_DATA
*Config
;
1145 EFI_DHCP4_PACKET
*Packet
;
1146 EFI_DHCP4_PACKET
*NewPacket
;
1147 EFI_DHCP4_HEADER
*Head
;
1148 EFI_DHCP4_HEADER
*SeedHead
;
1150 UDP_END_POINT EndPoint
;
1161 // Allocate a big enough memory block to hold the DHCP packet
1163 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1166 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1169 Packet
= AllocatePool (Len
);
1171 if (Packet
== NULL
) {
1172 return EFI_OUT_OF_RESOURCES
;
1176 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1179 // Fill in the DHCP header fields
1181 Config
= &DhcpSb
->ActiveConfig
;
1185 SeedHead
= &Seed
->Dhcp4
.Header
;
1188 Head
= &Packet
->Dhcp4
.Header
;
1189 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1191 Head
->OpCode
= BOOTP_REQUEST
;
1192 Head
->HwType
= DhcpSb
->HwType
;
1193 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1194 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1195 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1197 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1198 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1200 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) {
1202 } else if ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
)) {
1204 // Use the same value as the original DHCPDISCOVER message.
1206 Head
->Seconds
= DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
;
1208 SetElapsedTime(&Head
->Seconds
, DhcpSb
->ActiveChild
);
1212 // Append the DHCP message type
1214 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1215 Buf
= Packet
->Dhcp4
.Option
;
1216 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_MSG_TYPE
, 1, &Type
);
1219 // Append the serverid option if necessary:
1220 // 1. DHCP decline message
1221 // 2. DHCP release message
1222 // 3. DHCP request to confirm one lease.
1224 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1225 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))
1228 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1230 IpAddr
= HTONL (Para
->ServerId
);
1231 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1235 // Append the requested IP option if necessary:
1236 // 1. DHCP request to use the previously allocated address
1237 // 2. DHCP request to confirm one lease
1238 // 3. DHCP decline to decline one lease
1242 if (Type
== DHCP_MSG_REQUEST
) {
1243 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1244 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1246 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1247 ASSERT (SeedHead
!= NULL
);
1248 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1251 } else if (Type
== DHCP_MSG_DECLINE
) {
1252 ASSERT (SeedHead
!= NULL
);
1253 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1257 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1261 // Append the Max Message Length option if it isn't a DECLINE
1262 // or RELEASE to direct the server use large messages instead of
1263 // override the BOOTFILE and SERVER fields in the message head.
1265 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1266 MaxMsg
= HTONS (0xFF00);
1267 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1271 // Append the user's message if it isn't NULL
1274 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1275 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1279 // Append the user configured options
1281 if (DhcpSb
->UserOptionLen
!= 0) {
1282 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1284 // We can't use any option other than the client ID from user
1285 // if it is a DHCP decline or DHCP release .
1287 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1288 (Config
->OptionList
[Index
]->OpCode
!= DHCP4_TAG_CLIENT_ID
)) {
1292 Buf
= DhcpAppendOption (
1294 Config
->OptionList
[Index
]->OpCode
,
1295 Config
->OptionList
[Index
]->Length
,
1296 Config
->OptionList
[Index
]->Data
1301 *(Buf
++) = DHCP4_TAG_EOP
;
1302 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1305 // OK, the message is built, call the user to override it.
1307 Status
= EFI_SUCCESS
;
1310 if (Type
== DHCP_MSG_DISCOVER
) {
1311 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1313 } else if (Type
== DHCP_MSG_REQUEST
) {
1314 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1316 } else if (Type
== DHCP_MSG_DECLINE
) {
1317 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1320 if (EFI_ERROR (Status
)) {
1325 if (NewPacket
!= NULL
) {
1331 // Save the Client Address will be sent out
1334 &DhcpSb
->ClientAddressSendOut
[0],
1335 &Packet
->Dhcp4
.Header
.ClientHwAddr
[0],
1336 Packet
->Dhcp4
.Header
.HwAddrLen
1340 // Wrap it into a netbuf then send it.
1342 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1343 Frag
.Len
= Packet
->Length
;
1344 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpDummyExtFree
, NULL
);
1348 return EFI_OUT_OF_RESOURCES
;
1352 // Save it as the last sent packet for retransmission
1354 if (DhcpSb
->LastPacket
!= NULL
) {
1355 FreePool (DhcpSb
->LastPacket
);
1358 DhcpSb
->LastPacket
= Packet
;
1359 DhcpSetTransmitTimer (DhcpSb
);
1362 // Broadcast the message, unless we know the server address.
1363 // Use the lease UdpIo port to send the unicast packet.
1365 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1366 EndPoint
.LocalAddr
.Addr
[0] = 0;
1367 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1368 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1369 UdpIo
= DhcpSb
->UdpIo
;
1371 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1372 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1373 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1374 UdpIo
= DhcpSb
->LeaseIoPort
;
1377 ASSERT (UdpIo
!= NULL
);
1379 Status
= UdpIoSendDatagram (
1388 if (EFI_ERROR (Status
)) {
1390 return EFI_ACCESS_DENIED
;
1398 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1399 will be retransmitted.
1401 @param[in] DhcpSb The DHCP service instance
1403 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1404 @retval EFI_SUCCESS The packet is retransmitted.
1409 IN DHCP_SERVICE
*DhcpSb
1413 UDP_END_POINT EndPoint
;
1418 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1421 // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.
1423 if (DhcpSb
->DhcpState
!= Dhcp4Requesting
) {
1424 SetElapsedTime(&DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
, DhcpSb
->ActiveChild
);
1428 // Wrap it into a netbuf then send it.
1430 Frag
.Bulk
= (UINT8
*) &DhcpSb
->LastPacket
->Dhcp4
.Header
;
1431 Frag
.Len
= DhcpSb
->LastPacket
->Length
;
1432 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpDummyExtFree
, NULL
);
1435 return EFI_OUT_OF_RESOURCES
;
1439 // Broadcast the message, unless we know the server address.
1441 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1442 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1443 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1444 EndPoint
.LocalAddr
.Addr
[0] = 0;
1445 UdpIo
= DhcpSb
->UdpIo
;
1447 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1448 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1449 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1450 UdpIo
= DhcpSb
->LeaseIoPort
;
1453 ASSERT (UdpIo
!= NULL
);
1455 Status
= UdpIoSendDatagram (
1464 if (EFI_ERROR (Status
)) {
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 increments 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[in] Event The timer event
1481 @param[in] 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 // 0xffff is the maximum supported value for elapsed time according to RFC.
1503 if (Instance
!= NULL
&& Instance
->ElaspedTime
< 0xffff) {
1504 Instance
->ElaspedTime
++;
1508 // Check the retransmit timer
1510 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1513 // Select offer at each timeout if any offer received.
1515 if (DhcpSb
->DhcpState
== Dhcp4Selecting
&& DhcpSb
->LastOffer
!= NULL
) {
1517 Status
= DhcpChooseOffer (DhcpSb
);
1519 if (EFI_ERROR(Status
)) {
1520 if (DhcpSb
->LastOffer
!= NULL
) {
1521 FreePool (DhcpSb
->LastOffer
);
1522 DhcpSb
->LastOffer
= NULL
;
1529 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1531 // Still has another try
1533 DhcpRetransmit (DhcpSb
);
1534 DhcpSetTransmitTimer (DhcpSb
);
1536 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1539 // Retransmission failed, if the DHCP request is initiated by
1540 // user, adjust the current state according to the lease life.
1541 // Otherwise do nothing to wait the lease to timeout
1543 if (DhcpSb
->ExtraRefresh
!= 0) {
1544 Status
= EFI_SUCCESS
;
1546 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1547 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1549 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1550 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1552 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1553 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1560 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1561 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1569 // If an address has been acquired, check whether need to
1570 // refresh or whether it has expired.
1572 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1573 DhcpSb
->LeaseLife
++;
1576 // Don't timeout the lease, only count the life if user is
1577 // requesting extra renew/rebind. Adjust the state after that.
1579 if (DhcpSb
->ExtraRefresh
!= 0) {
1583 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1585 // Lease expires, end the session
1589 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1591 // T2 expires, transit to rebinding then send a REQUEST to any server
1593 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1597 if (Instance
!= NULL
) {
1598 Instance
->ElaspedTime
= 0;
1601 Status
= DhcpSendMessage (
1609 if (EFI_ERROR (Status
)) {
1613 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1615 // T1 expires, transit to renewing, then send a REQUEST to the server
1617 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1621 if (Instance
!= NULL
) {
1622 Instance
->ElaspedTime
= 0;
1625 Status
= DhcpSendMessage (
1633 if (EFI_ERROR (Status
)) {
1641 // Iterate through all the DhcpSb Children.
1643 NET_LIST_FOR_EACH_SAFE (Entry
, Next
, &DhcpSb
->Children
) {
1644 Instance
= NET_LIST_USER_STRUCT (Entry
, DHCP_PROTOCOL
, Link
);
1645 Instance
->Timeout
--;
1646 if (Instance
->Timeout
== 0 && Instance
->Token
!= NULL
) {
1647 PxeDhcpDone (Instance
);
1654 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);