2 EFI DHCP protocol implementation.
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "Dhcp4Impl.h"
18 UINT32 mDhcp4DefaultTimeout
[4] = { 4, 8, 16, 32 };
22 Send an initial DISCOVER or REQUEST message according to the
23 DHCP service's current state.
25 @param[in] DhcpSb The DHCP service instance
27 @retval EFI_SUCCESS The request has been sent
28 @retval other Some error occurs when sending the request.
33 IN DHCP_SERVICE
*DhcpSb
38 ASSERT ((DhcpSb
->DhcpState
== Dhcp4Init
) || (DhcpSb
->DhcpState
== Dhcp4InitReboot
));
41 // Clear initial time to make sure that elapsed-time is set to 0 for first Discover or REQUEST message.
43 DhcpSb
->ActiveChild
->ElaspedTime
= 0;
45 if (DhcpSb
->DhcpState
== Dhcp4Init
) {
46 DhcpSetState (DhcpSb
, Dhcp4Selecting
, FALSE
);
47 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_DISCOVER
, NULL
);
49 if (EFI_ERROR (Status
)) {
50 DhcpSb
->DhcpState
= Dhcp4Init
;
54 DhcpSetState (DhcpSb
, Dhcp4Rebooting
, FALSE
);
55 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_REQUEST
, NULL
);
57 if (EFI_ERROR (Status
)) {
58 DhcpSb
->DhcpState
= Dhcp4InitReboot
;
68 Call user provided callback function, and return the value the
69 function returns. If the user doesn't provide a callback, a
70 proper return value is selected to let the caller continue the
73 @param[in] DhcpSb The DHCP service instance
74 @param[in] Event The event as defined in the spec
75 @param[in] Packet The current packet trigger the event
76 @param[out] NewPacket The user's return new packet
78 @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
79 @retval EFI_SUCCESS The user function returns success.
80 @retval EFI_ABORTED The user function ask it to abort.
85 IN DHCP_SERVICE
*DhcpSb
,
86 IN EFI_DHCP4_EVENT Event
,
87 IN EFI_DHCP4_PACKET
*Packet
, OPTIONAL
88 OUT EFI_DHCP4_PACKET
**NewPacket OPTIONAL
91 EFI_DHCP4_CONFIG_DATA
*Config
;
94 if (NewPacket
!= NULL
) {
99 // If user doesn't provide the call back function, return the value
100 // that directs the client to continue the normal process.
101 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
102 // the offers and select a offer, EFI_NOT_READY tells the client to
103 // collect more offers.
105 Config
= &DhcpSb
->ActiveConfig
;
107 if (Config
->Dhcp4Callback
== NULL
) {
108 if (Event
== Dhcp4RcvdOffer
) {
109 return EFI_NOT_READY
;
115 Status
= Config
->Dhcp4Callback (
116 &DhcpSb
->ActiveChild
->Dhcp4Protocol
,
117 Config
->CallbackContext
,
118 (EFI_DHCP4_STATE
) DhcpSb
->DhcpState
,
125 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
126 // and EFI_ABORTED. If it returns values other than those, assume
127 // it to be EFI_ABORTED.
129 if ((Status
== EFI_SUCCESS
) || (Status
== EFI_NOT_READY
)) {
138 Notify the user about the operation result.
140 @param DhcpSb DHCP service instance
141 @param Which Which notify function to signal
146 IN DHCP_SERVICE
*DhcpSb
,
150 DHCP_PROTOCOL
*Child
;
152 if ((Child
= DhcpSb
->ActiveChild
) == NULL
) {
156 if ((Child
->CompletionEvent
!= NULL
) &&
157 ((Which
== DHCP_NOTIFY_COMPLETION
) || (Which
== DHCP_NOTIFY_ALL
))
160 gBS
->SignalEvent (Child
->CompletionEvent
);
161 Child
->CompletionEvent
= NULL
;
164 if ((Child
->RenewRebindEvent
!= NULL
) &&
165 ((Which
== DHCP_NOTIFY_RENEWREBIND
) || (Which
== DHCP_NOTIFY_ALL
))
168 gBS
->SignalEvent (Child
->RenewRebindEvent
);
169 Child
->RenewRebindEvent
= NULL
;
176 Set the DHCP state. If CallUser is true, it will try to notify
177 the user before change the state by DhcpNotifyUser. It returns
178 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
179 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
180 the return value of this function.
182 @param DhcpSb The DHCP service instance
183 @param State The new DHCP state to change to
184 @param CallUser Whether we need to call user
186 @retval EFI_SUCCESS The state is changed
187 @retval EFI_ABORTED The user asks to abort the DHCP process.
192 IN OUT DHCP_SERVICE
*DhcpSb
,
200 Status
= EFI_SUCCESS
;
202 if (State
== Dhcp4Renewing
) {
203 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRenewing
, NULL
, NULL
);
205 } else if (State
== Dhcp4Rebinding
) {
206 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRebinding
, NULL
, NULL
);
208 } else if (State
== Dhcp4Bound
) {
209 Status
= DhcpCallUser (DhcpSb
, Dhcp4BoundCompleted
, NULL
, NULL
);
213 if (EFI_ERROR (Status
)) {
219 // Update the retransmission timer during the state transition.
220 // This will clear the retry count. This is also why the rule
221 // first transit the state, then send packets.
223 if (State
== Dhcp4Selecting
) {
224 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.DiscoverTryCount
;
226 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.RequestTryCount
;
229 if (DhcpSb
->MaxRetries
== 0) {
230 DhcpSb
->MaxRetries
= 4;
233 DhcpSb
->CurRetry
= 0;
234 DhcpSb
->PacketToLive
= 0;
235 DhcpSb
->LastTimeout
= 0;
236 DhcpSb
->DhcpState
= State
;
242 Set the retransmit timer for the packet. It will select from either
243 the discover timeouts/request timeouts or the default timeout values.
245 @param DhcpSb The DHCP service instance.
249 DhcpSetTransmitTimer (
250 IN OUT DHCP_SERVICE
*DhcpSb
255 ASSERT (DhcpSb
->MaxRetries
> DhcpSb
->CurRetry
);
257 if (DhcpSb
->DhcpState
== Dhcp4Selecting
) {
258 Times
= DhcpSb
->ActiveConfig
.DiscoverTimeout
;
260 Times
= DhcpSb
->ActiveConfig
.RequestTimeout
;
264 Times
= mDhcp4DefaultTimeout
;
267 DhcpSb
->PacketToLive
= Times
[DhcpSb
->CurRetry
];
268 DhcpSb
->LastTimeout
= DhcpSb
->PacketToLive
;
274 Compute the lease. If the server grants a permanent lease, just
275 process it as a normal timeout value since the lease will last
278 @param DhcpSb The DHCP service instance
279 @param Para The DHCP parameter extracted from the server's
284 IN OUT DHCP_SERVICE
*DhcpSb
,
285 IN DHCP_PARAMETER
*Para
288 ASSERT (Para
!= NULL
);
290 DhcpSb
->Lease
= Para
->Lease
;
291 DhcpSb
->T2
= Para
->T2
;
292 DhcpSb
->T1
= Para
->T1
;
294 if (DhcpSb
->Lease
== 0) {
295 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
298 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
299 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
302 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
303 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
309 Configure a UDP IO port to use the acquired lease address.
310 DHCP driver needs this port to unicast packet to the server
311 such as DHCP release.
313 @param[in] UdpIo The UDP IO to configure
314 @param[in] Context Dhcp service instance.
316 @retval EFI_SUCCESS The UDP IO port is successfully configured.
317 @retval Others It failed to configure the port.
322 DhcpConfigLeaseIoPort (
327 EFI_UDP4_CONFIG_DATA UdpConfigData
;
328 EFI_IPv4_ADDRESS Subnet
;
329 EFI_IPv4_ADDRESS Gateway
;
330 DHCP_SERVICE
*DhcpSb
;
334 DhcpSb
= (DHCP_SERVICE
*) Context
;
336 UdpConfigData
.AcceptBroadcast
= FALSE
;
337 UdpConfigData
.AcceptPromiscuous
= FALSE
;
338 UdpConfigData
.AcceptAnyPort
= FALSE
;
339 UdpConfigData
.AllowDuplicatePort
= TRUE
;
340 UdpConfigData
.TypeOfService
= 0;
341 UdpConfigData
.TimeToLive
= 64;
342 UdpConfigData
.DoNotFragment
= FALSE
;
343 UdpConfigData
.ReceiveTimeout
= 1;
344 UdpConfigData
.TransmitTimeout
= 0;
346 UdpConfigData
.UseDefaultAddress
= FALSE
;
347 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
348 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
350 Ip
= HTONL (DhcpSb
->ClientAddr
);
351 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
353 Ip
= HTONL (DhcpSb
->Netmask
);
354 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
356 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
358 Status
= UdpIo
->Protocol
.Udp4
->Configure (UdpIo
->Protocol
.Udp4
, &UdpConfigData
);
360 if (EFI_ERROR (Status
)) {
365 // Add a default route if received from the server.
367 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
368 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
370 Ip
= HTONL (DhcpSb
->Para
->Router
);
371 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
373 UdpIo
->Protocol
.Udp4
->Routes (UdpIo
->Protocol
.Udp4
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
381 Update the lease states when a new lease is acquired. It will not only
382 save the acquired the address and lease time, it will also create a UDP
383 child to provide address resolution for the address.
385 @param DhcpSb The DHCP service instance
387 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
388 @retval EFI_SUCCESS The lease is recorded.
393 IN OUT DHCP_SERVICE
*DhcpSb
398 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
400 if (DhcpSb
->Para
!= NULL
) {
401 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
402 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
405 if (DhcpSb
->Netmask
== 0) {
406 Class
= NetGetIpClass (DhcpSb
->ClientAddr
);
407 ASSERT (Class
< IP4_ADDR_CLASSE
);
408 DhcpSb
->Netmask
= gIp4AllMasks
[Class
<< 3];
411 if (DhcpSb
->LeaseIoPort
!= NULL
) {
412 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
416 // Create a UDP/IP child to provide ARP service for the Leased IP,
417 // and transmit unicast packet with it as source address. Don't
418 // start receive on this port, the queued packet will be timeout.
420 DhcpSb
->LeaseIoPort
= UdpIoCreateIo (
423 DhcpConfigLeaseIoPort
,
428 if (DhcpSb
->LeaseIoPort
== NULL
) {
429 return EFI_OUT_OF_RESOURCES
;
432 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
433 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
436 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
441 Clean up the DHCP related states, IoStatus isn't reset.
443 @param DhcpSb The DHCP instance service.
448 IN DHCP_SERVICE
*DhcpSb
451 DhcpSb
->DhcpState
= Dhcp4Init
;
452 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
453 DhcpSb
->ClientAddr
= 0;
455 DhcpSb
->ServerAddr
= 0;
457 if (DhcpSb
->LastOffer
!= NULL
) {
458 FreePool (DhcpSb
->LastOffer
);
459 DhcpSb
->LastOffer
= NULL
;
462 if (DhcpSb
->Selected
!= NULL
) {
463 FreePool (DhcpSb
->Selected
);
464 DhcpSb
->Selected
= NULL
;
467 if (DhcpSb
->Para
!= NULL
) {
468 FreePool (DhcpSb
->Para
);
475 DhcpSb
->ExtraRefresh
= FALSE
;
477 if (DhcpSb
->LeaseIoPort
!= NULL
) {
478 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
479 DhcpSb
->LeaseIoPort
= NULL
;
482 if (DhcpSb
->LastPacket
!= NULL
) {
483 FreePool (DhcpSb
->LastPacket
);
484 DhcpSb
->LastPacket
= NULL
;
487 DhcpSb
->PacketToLive
= 0;
488 DhcpSb
->LastTimeout
= 0;
489 DhcpSb
->CurRetry
= 0;
490 DhcpSb
->MaxRetries
= 0;
491 DhcpSb
->LeaseLife
= 0;
494 // Clean active config data.
496 DhcpCleanConfigure (&DhcpSb
->ActiveConfig
);
501 Select a offer among all the offers collected. If the offer selected is
502 of BOOTP, the lease is recorded and user notified. If the offer is of
503 DHCP, it will request the offer from the server.
505 @param[in] DhcpSb The DHCP service instance.
507 @retval EFI_SUCCESS One of the offer is selected.
512 IN DHCP_SERVICE
*DhcpSb
515 EFI_DHCP4_PACKET
*Selected
;
516 EFI_DHCP4_PACKET
*NewPacket
;
517 EFI_DHCP4_PACKET
*TempPacket
;
520 ASSERT (DhcpSb
->LastOffer
!= NULL
);
523 // User will cache previous offers if he wants to select
524 // from multiple offers. If user provides an invalid packet,
525 // use the last offer, otherwise use the provided packet.
528 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
530 if (EFI_ERROR (Status
)) {
534 Selected
= DhcpSb
->LastOffer
;
536 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
537 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
538 if (TempPacket
!= NULL
) {
539 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
541 Selected
= TempPacket
;
545 DhcpSb
->Selected
= Selected
;
546 DhcpSb
->LastOffer
= NULL
;
548 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
551 // A bootp offer has been selected, save the lease status,
552 // enter bound state then notify the user.
554 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
555 Status
= DhcpLeaseAcquired (DhcpSb
);
557 if (EFI_ERROR (Status
)) {
561 DhcpSb
->IoStatus
= EFI_SUCCESS
;
562 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
567 // Send a DHCP requests
569 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
571 if (EFI_ERROR (Status
)) {
575 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
580 Terminate the current address acquire. All the allocated resources
581 are released. Be careful when calling this function. A rule related
582 to this is: only call DhcpEndSession at the highest level, such as
583 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
585 @param[in] DhcpSb The DHCP service instance
586 @param[in] Status The result of the DHCP process.
591 IN DHCP_SERVICE
*DhcpSb
,
595 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
596 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
598 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
601 DhcpCleanLease (DhcpSb
);
603 DhcpSb
->IoStatus
= Status
;
604 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
609 Handle packets in DHCP select state.
611 @param[in] DhcpSb The DHCP service instance
612 @param[in] Packet The DHCP packet received
613 @param[in] Para The DHCP parameter extracted from the packet. That
614 is, all the option value that we care.
616 @retval EFI_SUCCESS The packet is successfully processed.
617 @retval Others Some error occured.
622 IN DHCP_SERVICE
*DhcpSb
,
623 IN EFI_DHCP4_PACKET
*Packet
,
624 IN DHCP_PARAMETER
*Para
629 Status
= EFI_SUCCESS
;
632 // First validate the message:
633 // 1. the offer is a unicast
634 // 2. if it is a DHCP message, it must contains a server ID.
635 // Don't return a error for these two case otherwise the session is ended.
637 if (!DHCP_IS_BOOTP (Para
) &&
638 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))
644 // Call the user's callback. The action according to the return is as:
645 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
646 // 2. EFI_NOT_READY: wait for more offers
647 // 3. EFI_ABORTED: abort the address acquiring.
649 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
651 if (Status
== EFI_SUCCESS
) {
652 if (DhcpSb
->LastOffer
!= NULL
) {
653 FreePool (DhcpSb
->LastOffer
);
656 DhcpSb
->LastOffer
= Packet
;
658 return DhcpChooseOffer (DhcpSb
);
660 } else if (Status
== EFI_NOT_READY
) {
661 if (DhcpSb
->LastOffer
!= NULL
) {
662 FreePool (DhcpSb
->LastOffer
);
665 DhcpSb
->LastOffer
= Packet
;
667 } else if (Status
== EFI_ABORTED
) {
669 // DhcpInput will end the session upon error return. Remember
670 // only to call DhcpEndSession at the top level call.
684 Handle packets in DHCP request state.
686 @param[in] DhcpSb The DHCP service instance
687 @param[in] Packet The DHCP packet received
688 @param[in] Para The DHCP parameter extracted from the packet. That
689 is, all the option value that we care.
691 @retval EFI_SUCCESS The packet is successfully processed.
692 @retval Others Some error occured.
697 IN DHCP_SERVICE
*DhcpSb
,
698 IN EFI_DHCP4_PACKET
*Packet
,
699 IN DHCP_PARAMETER
*Para
702 EFI_DHCP4_HEADER
*Head
;
703 EFI_DHCP4_HEADER
*Selected
;
707 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
709 Head
= &Packet
->Dhcp4
.Header
;
710 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
713 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
715 if (DHCP_IS_BOOTP (Para
) ||
716 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
717 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
720 Status
= EFI_SUCCESS
;
725 // Received a NAK, end the session no matter what the user returns
727 Status
= EFI_DEVICE_ERROR
;
729 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
730 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
735 // Check whether the ACK matches the selected offer
739 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
740 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
744 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
746 if (EFI_ERROR (Status
)) {
747 Message
= (UINT8
*) "Lease is denied upon received ACK";
752 // Record the lease, transit to BOUND state, then notify the user
754 Status
= DhcpLeaseAcquired (DhcpSb
);
756 if (EFI_ERROR (Status
)) {
757 Message
= (UINT8
*) "Lease is denied upon entering bound";
761 DhcpSb
->IoStatus
= EFI_SUCCESS
;
762 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
768 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
777 Handle packets in DHCP renew/rebound state.
779 @param[in] DhcpSb The DHCP service instance
780 @param[in] Packet The DHCP packet received
781 @param[in] Para The DHCP parameter extracted from the packet. That
782 is, all the option value that we care.
784 @retval EFI_SUCCESS The packet is successfully processed.
785 @retval Others Some error occured.
789 DhcpHandleRenewRebind (
790 IN DHCP_SERVICE
*DhcpSb
,
791 IN EFI_DHCP4_PACKET
*Packet
,
792 IN DHCP_PARAMETER
*Para
795 EFI_DHCP4_HEADER
*Head
;
796 EFI_DHCP4_HEADER
*Selected
;
799 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
801 Head
= &Packet
->Dhcp4
.Header
;
802 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
805 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
807 if (DHCP_IS_BOOTP (Para
) ||
808 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
809 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
812 Status
= EFI_SUCCESS
;
817 // Received a NAK, ignore the user's return then terminate the process
819 Status
= EFI_DEVICE_ERROR
;
821 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
822 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
827 // The lease is different from the selected. Don't send a DECLINE
828 // since it isn't existed in the client's FSM.
830 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
834 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
836 if (EFI_ERROR (Status
)) {
841 // Record the lease, start timer for T1 and T2,
843 DhcpComputeLease (DhcpSb
, Para
);
844 DhcpSb
->LeaseLife
= 0;
845 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
847 if (DhcpSb
->ExtraRefresh
!= 0) {
848 DhcpSb
->ExtraRefresh
= FALSE
;
850 DhcpSb
->IoStatus
= EFI_SUCCESS
;
851 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
861 Handle packets in DHCP reboot state.
863 @param[in] DhcpSb The DHCP service instance
864 @param[in] Packet The DHCP packet received
865 @param[in] Para The DHCP parameter extracted from the packet. That
866 is, all the option value that we care.
868 @retval EFI_SUCCESS The packet is successfully processed.
869 @retval Others Some error occured.
874 IN DHCP_SERVICE
*DhcpSb
,
875 IN EFI_DHCP4_PACKET
*Packet
,
876 IN DHCP_PARAMETER
*Para
879 EFI_DHCP4_HEADER
*Head
;
882 Head
= &Packet
->Dhcp4
.Header
;
885 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
887 if (DHCP_IS_BOOTP (Para
) ||
888 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
891 Status
= EFI_SUCCESS
;
896 // If a NAK is received, transit to INIT and try again.
898 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
899 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
901 DhcpSb
->ClientAddr
= 0;
902 DhcpSb
->DhcpState
= Dhcp4Init
;
904 Status
= DhcpInitRequest (DhcpSb
);
909 // Check whether the ACK matches the selected offer
911 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
912 Status
= EFI_DEVICE_ERROR
;
916 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
917 if (EFI_ERROR (Status
)) {
922 // OK, get the parameter from server, record the lease
924 DhcpSb
->Para
= AllocateCopyPool (sizeof (DHCP_PARAMETER
), Para
);
925 if (DhcpSb
->Para
== NULL
) {
926 Status
= EFI_OUT_OF_RESOURCES
;
930 DhcpSb
->Selected
= Packet
;
931 Status
= DhcpLeaseAcquired (DhcpSb
);
932 if (EFI_ERROR (Status
)) {
936 DhcpSb
->IoStatus
= EFI_SUCCESS
;
937 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
947 Handle the received DHCP packets. This function drives the DHCP
950 @param UdpPacket The UDP packets received.
951 @param EndPoint The local/remote UDP access point
952 @param IoStatus The status of the UDP receive
953 @param Context The opaque parameter to the function.
960 UDP_END_POINT
*EndPoint
,
965 DHCP_SERVICE
*DhcpSb
;
966 EFI_DHCP4_HEADER
*Head
;
967 EFI_DHCP4_PACKET
*Packet
;
968 DHCP_PARAMETER
*Para
;
973 DhcpSb
= (DHCP_SERVICE
*) Context
;
976 // Don't restart receive if error occurs or DHCP is destroyed.
978 if (EFI_ERROR (IoStatus
)) {
980 } else if (DhcpSb
->ServiceState
== DHCP_DESTROY
) {
981 NetbufFree (UdpPacket
);
985 ASSERT (UdpPacket
!= NULL
);
987 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
992 // Validate the packet received
994 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
999 // Copy the DHCP message to a continuous memory block
1001 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
1002 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
1004 if (Packet
== NULL
) {
1009 Head
= &Packet
->Dhcp4
.Header
;
1010 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1012 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1017 // Is this packet the answer to our packet?
1019 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1020 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1021 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1026 // Validate the options and retrieve the interested options
1029 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1030 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1031 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1037 // Call the handler for each state. The handler should return
1038 // EFI_SUCCESS if the process can go on no matter whether the
1039 // packet is ignored or not. If the return is EFI_ERROR, the
1040 // session will be terminated. Packet's ownership is handled
1041 // over to the handlers. If operation succeeds, the handler
1042 // must notify the user. It isn't necessary to do if EFI_ERROR
1043 // is returned because the DhcpEndSession will notify the user.
1045 Status
= EFI_SUCCESS
;
1047 switch (DhcpSb
->DhcpState
) {
1048 case Dhcp4Selecting
:
1049 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1052 case Dhcp4Requesting
:
1053 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1056 case Dhcp4InitReboot
:
1060 // Ignore the packet in INITREBOOT, INIT and BOUND states
1063 Status
= EFI_SUCCESS
;
1067 case Dhcp4Rebinding
:
1068 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1071 case Dhcp4Rebooting
:
1072 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1082 if (EFI_ERROR (Status
)) {
1083 NetbufFree (UdpPacket
);
1084 UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1085 DhcpEndSession (DhcpSb
, Status
);
1090 NetbufFree (UdpPacket
);
1092 if (Packet
!= NULL
) {
1096 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1098 if (EFI_ERROR (Status
)) {
1099 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1107 @param[in] Arg The packet to release
1121 Release the net buffer when packet is sent.
1123 @param UdpPacket The UDP packets received.
1124 @param EndPoint The local/remote UDP access point
1125 @param IoStatus The status of the UDP receive
1126 @param Context The opaque parameter to the function.
1133 UDP_END_POINT
*EndPoint
,
1134 EFI_STATUS IoStatus
,
1138 NetbufFree (Packet
);
1144 Build and transmit a DHCP message according to the current states.
1145 This function implement the Table 5. of RFC 2131. Always transits
1146 the state (as defined in Figure 5. of the same RFC) before sending
1147 a DHCP message. The table is adjusted accordingly.
1149 @param[in] DhcpSb The DHCP service instance
1150 @param[in] Seed The seed packet which the new packet is based on
1151 @param[in] Para The DHCP parameter of the Seed packet
1152 @param[in] Type The message type to send
1153 @param[in] Msg The human readable message to include in the packet
1156 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1157 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1158 @retval EFI_SUCCESS The message is sent
1159 @retval other Other error occurs
1164 IN DHCP_SERVICE
*DhcpSb
,
1165 IN EFI_DHCP4_PACKET
*Seed
,
1166 IN DHCP_PARAMETER
*Para
,
1171 EFI_DHCP4_CONFIG_DATA
*Config
;
1172 EFI_DHCP4_PACKET
*Packet
;
1173 EFI_DHCP4_PACKET
*NewPacket
;
1174 EFI_DHCP4_HEADER
*Head
;
1175 EFI_DHCP4_HEADER
*SeedHead
;
1177 UDP_END_POINT EndPoint
;
1188 // Allocate a big enough memory block to hold the DHCP packet
1190 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1193 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1196 Packet
= AllocatePool (Len
);
1198 if (Packet
== NULL
) {
1199 return EFI_OUT_OF_RESOURCES
;
1203 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1206 // Fill in the DHCP header fields
1208 Config
= &DhcpSb
->ActiveConfig
;
1212 SeedHead
= &Seed
->Dhcp4
.Header
;
1215 Head
= &Packet
->Dhcp4
.Header
;
1216 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1218 Head
->OpCode
= BOOTP_REQUEST
;
1219 Head
->HwType
= DhcpSb
->HwType
;
1220 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1221 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1222 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1224 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1225 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1227 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) {
1229 } else if ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
)) {
1231 // Use the same value as the original DHCPDISCOVER message.
1233 Head
->Seconds
= DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
;
1235 SetElapsedTime(&Head
->Seconds
, DhcpSb
->ActiveChild
);
1239 // Append the DHCP message type
1241 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1242 Buf
= Packet
->Dhcp4
.Option
;
1243 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_MSG_TYPE
, 1, &Type
);
1246 // Append the serverid option if necessary:
1247 // 1. DHCP decline message
1248 // 2. DHCP release message
1249 // 3. DHCP request to confirm one lease.
1251 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1252 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))
1255 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1257 IpAddr
= HTONL (Para
->ServerId
);
1258 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1262 // Append the requested IP option if necessary:
1263 // 1. DHCP request to use the previously allocated address
1264 // 2. DHCP request to confirm one lease
1265 // 3. DHCP decline to decline one lease
1269 if (Type
== DHCP_MSG_REQUEST
) {
1270 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1271 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1273 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1274 ASSERT (SeedHead
!= NULL
);
1275 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1278 } else if (Type
== DHCP_MSG_DECLINE
) {
1279 ASSERT (SeedHead
!= NULL
);
1280 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1284 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1288 // Append the Max Message Length option if it isn't a DECLINE
1289 // or RELEASE to direct the server use large messages instead of
1290 // override the BOOTFILE and SERVER fields in the message head.
1292 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1293 MaxMsg
= HTONS (0xFF00);
1294 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1298 // Append the user's message if it isn't NULL
1301 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1302 Buf
= DhcpAppendOption (Buf
, DHCP4_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1306 // Append the user configured options
1308 if (DhcpSb
->UserOptionLen
!= 0) {
1309 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1311 // We can't use any option other than the client ID from user
1312 // if it is a DHCP decline or DHCP release .
1314 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1315 (Config
->OptionList
[Index
]->OpCode
!= DHCP4_TAG_CLIENT_ID
)) {
1319 Buf
= DhcpAppendOption (
1321 Config
->OptionList
[Index
]->OpCode
,
1322 Config
->OptionList
[Index
]->Length
,
1323 Config
->OptionList
[Index
]->Data
1328 *(Buf
++) = DHCP4_TAG_EOP
;
1329 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1332 // OK, the message is built, call the user to override it.
1334 Status
= EFI_SUCCESS
;
1337 if (Type
== DHCP_MSG_DISCOVER
) {
1338 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1340 } else if (Type
== DHCP_MSG_REQUEST
) {
1341 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1343 } else if (Type
== DHCP_MSG_DECLINE
) {
1344 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1347 if (EFI_ERROR (Status
)) {
1352 if (NewPacket
!= NULL
) {
1358 // Save the Client Address will be sent out
1361 &DhcpSb
->ClientAddressSendOut
[0],
1362 &Packet
->Dhcp4
.Header
.ClientHwAddr
[0],
1363 Packet
->Dhcp4
.Header
.HwAddrLen
1368 // Wrap it into a netbuf then send it.
1370 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1371 Frag
.Len
= Packet
->Length
;
1372 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1376 return EFI_OUT_OF_RESOURCES
;
1380 // Save it as the last sent packet for retransmission
1382 if (DhcpSb
->LastPacket
!= NULL
) {
1383 FreePool (DhcpSb
->LastPacket
);
1386 DhcpSb
->LastPacket
= Packet
;
1387 DhcpSetTransmitTimer (DhcpSb
);
1390 // Broadcast the message, unless we know the server address.
1391 // Use the lease UdpIo port to send the unicast packet.
1393 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1394 EndPoint
.LocalAddr
.Addr
[0] = 0;
1395 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1396 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1397 UdpIo
= DhcpSb
->UdpIo
;
1399 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1400 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1401 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1402 UdpIo
= DhcpSb
->LeaseIoPort
;
1405 ASSERT (UdpIo
!= NULL
);
1408 Status
= UdpIoSendDatagram (
1417 if (EFI_ERROR (Status
)) {
1419 return EFI_ACCESS_DENIED
;
1427 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1428 will be retransmitted.
1430 @param[in] DhcpSb The DHCP service instance
1432 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1433 @retval EFI_SUCCESS The packet is retransmitted.
1438 IN DHCP_SERVICE
*DhcpSb
1442 UDP_END_POINT EndPoint
;
1447 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1450 // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.
1452 if (DhcpSb
->DhcpState
!= Dhcp4Requesting
) {
1453 SetElapsedTime(&DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
, DhcpSb
->ActiveChild
);
1457 // Wrap it into a netbuf then send it.
1459 Frag
.Bulk
= (UINT8
*) &DhcpSb
->LastPacket
->Dhcp4
.Header
;
1460 Frag
.Len
= DhcpSb
->LastPacket
->Length
;
1461 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, DhcpSb
->LastPacket
);
1464 return EFI_OUT_OF_RESOURCES
;
1468 // Broadcast the message, unless we know the server address.
1470 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1471 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1472 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1473 EndPoint
.LocalAddr
.Addr
[0] = 0;
1474 UdpIo
= DhcpSb
->UdpIo
;
1476 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1477 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1478 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1479 UdpIo
= DhcpSb
->LeaseIoPort
;
1482 ASSERT (UdpIo
!= NULL
);
1485 Status
= UdpIoSendDatagram (
1494 if (EFI_ERROR (Status
)) {
1496 return EFI_ACCESS_DENIED
;
1504 Each DHCP service has three timer. Two of them are count down timer.
1505 One for the packet retransmission. The other is to collect the offers.
1506 The third timer increaments the lease life which is compared to T1, T2,
1507 and lease to determine the time to renew and rebind the lease.
1508 DhcpOnTimerTick will be called once every second.
1510 @param[in] Event The timer event
1511 @param[in] Context The context, which is the DHCP service instance.
1523 DHCP_SERVICE
*DhcpSb
;
1524 DHCP_PROTOCOL
*Instance
;
1527 DhcpSb
= (DHCP_SERVICE
*) Context
;
1528 Instance
= DhcpSb
->ActiveChild
;
1531 // 0xffff is the maximum supported value for elapsed time according to RFC.
1533 if (Instance
!= NULL
&& Instance
->ElaspedTime
< 0xffff) {
1534 Instance
->ElaspedTime
++;
1538 // Check the retransmit timer
1540 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1543 // Select offer at each timeout if any offer received.
1545 if (DhcpSb
->DhcpState
== Dhcp4Selecting
&& DhcpSb
->LastOffer
!= NULL
) {
1547 Status
= DhcpChooseOffer (DhcpSb
);
1549 if (EFI_ERROR(Status
)) {
1550 if (DhcpSb
->LastOffer
!= NULL
) {
1551 FreePool (DhcpSb
->LastOffer
);
1552 DhcpSb
->LastOffer
= NULL
;
1559 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1561 // Still has another try
1563 DhcpRetransmit (DhcpSb
);
1564 DhcpSetTransmitTimer (DhcpSb
);
1566 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1569 // Retransmission failed, if the DHCP request is initiated by
1570 // user, adjust the current state according to the lease life.
1571 // Otherwise do nothing to wait the lease to timeout
1573 if (DhcpSb
->ExtraRefresh
!= 0) {
1574 Status
= EFI_SUCCESS
;
1576 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1577 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1579 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1580 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1582 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1583 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1590 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1591 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1599 // If an address has been acquired, check whether need to
1600 // refresh or whether it has expired.
1602 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1603 DhcpSb
->LeaseLife
++;
1606 // Don't timeout the lease, only count the life if user is
1607 // requesting extra renew/rebind. Adjust the state after that.
1609 if (DhcpSb
->ExtraRefresh
!= 0) {
1613 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1615 // Lease expires, end the session
1619 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1621 // T2 expires, transit to rebinding then send a REQUEST to any server
1623 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1627 if (Instance
!= NULL
) {
1628 Instance
->ElaspedTime
= 0;
1631 Status
= DhcpSendMessage (
1639 if (EFI_ERROR (Status
)) {
1643 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1645 // T1 expires, transit to renewing, then send a REQUEST to the server
1647 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1651 if (Instance
!= NULL
) {
1652 Instance
->ElaspedTime
= 0;
1655 Status
= DhcpSendMessage (
1663 if (EFI_ERROR (Status
)) {
1671 // Iterate through all the DhcpSb Children.
1673 NET_LIST_FOR_EACH_SAFE (Entry
, Next
, &DhcpSb
->Children
) {
1674 Instance
= NET_LIST_USER_STRUCT (Entry
, DHCP_PROTOCOL
, Link
);
1676 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1677 Instance
->Timeout
--;
1678 if (Instance
->Timeout
== 0) {
1679 PxeDhcpDone (Instance
);
1687 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);