2 EFI DHCP protocol implementation.
4 Copyright (c) 2006 - 2009, Intel Corporation.<BR>
5 All rights reserved. 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
));
40 if (DhcpSb
->DhcpState
== Dhcp4Init
) {
41 DhcpSetState (DhcpSb
, Dhcp4Selecting
, FALSE
);
42 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_DISCOVER
, NULL
);
44 if (EFI_ERROR (Status
)) {
45 DhcpSb
->DhcpState
= Dhcp4Init
;
49 DhcpSetState (DhcpSb
, Dhcp4Rebooting
, FALSE
);
50 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_REQUEST
, NULL
);
52 if (EFI_ERROR (Status
)) {
53 DhcpSb
->DhcpState
= Dhcp4InitReboot
;
63 Call user provided callback function, and return the value the
64 function returns. If the user doesn't provide a callback, a
65 proper return value is selected to let the caller continue the
68 @param[in] DhcpSb The DHCP service instance
69 @param[in] Event The event as defined in the spec
70 @param[in] Packet The current packet trigger the event
71 @param[out] NewPacket The user's return new packet
73 @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
74 @retval EFI_SUCCESS The user function returns success.
75 @retval EFI_ABORTED The user function ask it to abort.
80 IN DHCP_SERVICE
*DhcpSb
,
81 IN EFI_DHCP4_EVENT Event
,
82 IN EFI_DHCP4_PACKET
*Packet
, OPTIONAL
83 OUT EFI_DHCP4_PACKET
**NewPacket OPTIONAL
86 EFI_DHCP4_CONFIG_DATA
*Config
;
89 if (NewPacket
!= NULL
) {
94 // If user doesn't provide the call back function, return the value
95 // that directs the client to continue the normal process.
96 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
97 // the offers and select a offer, EFI_NOT_READY tells the client to
98 // collect more offers.
100 Config
= &DhcpSb
->ActiveConfig
;
102 if (Config
->Dhcp4Callback
== NULL
) {
103 if (Event
== Dhcp4RcvdOffer
) {
104 return EFI_NOT_READY
;
110 Status
= Config
->Dhcp4Callback (
111 &DhcpSb
->ActiveChild
->Dhcp4Protocol
,
112 Config
->CallbackContext
,
113 (EFI_DHCP4_STATE
) DhcpSb
->DhcpState
,
120 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
121 // and EFI_ABORTED. If it returns values other than those, assume
122 // it to be EFI_ABORTED.
124 if ((Status
== EFI_SUCCESS
) || (Status
== EFI_NOT_READY
)) {
133 Notify the user about the operation result.
135 @param DhcpSb DHCP service instance
136 @param Which Which notify function to signal
141 IN DHCP_SERVICE
*DhcpSb
,
145 DHCP_PROTOCOL
*Child
;
147 if ((Child
= DhcpSb
->ActiveChild
) == NULL
) {
151 if ((Child
->CompletionEvent
!= NULL
) &&
152 ((Which
== DHCP_NOTIFY_COMPLETION
) || (Which
== DHCP_NOTIFY_ALL
))
155 gBS
->SignalEvent (Child
->CompletionEvent
);
156 Child
->CompletionEvent
= NULL
;
159 if ((Child
->RenewRebindEvent
!= NULL
) &&
160 ((Which
== DHCP_NOTIFY_RENEWREBIND
) || (Which
== DHCP_NOTIFY_ALL
))
163 gBS
->SignalEvent (Child
->RenewRebindEvent
);
164 Child
->RenewRebindEvent
= NULL
;
171 Set the DHCP state. If CallUser is true, it will try to notify
172 the user before change the state by DhcpNotifyUser. It returns
173 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
174 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
175 the return value of this function.
177 @param DhcpSb The DHCP service instance
178 @param State The new DHCP state to change to
179 @param CallUser Whether we need to call user
181 @retval EFI_SUCCESS The state is changed
182 @retval EFI_ABORTED The user asks to abort the DHCP process.
187 IN OUT DHCP_SERVICE
*DhcpSb
,
195 Status
= EFI_SUCCESS
;
197 if (State
== Dhcp4Renewing
) {
198 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRenewing
, NULL
, NULL
);
200 } else if (State
== Dhcp4Rebinding
) {
201 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRebinding
, NULL
, NULL
);
203 } else if (State
== Dhcp4Bound
) {
204 Status
= DhcpCallUser (DhcpSb
, Dhcp4BoundCompleted
, NULL
, NULL
);
208 if (EFI_ERROR (Status
)) {
214 // Update the retransmission timer during the state transition.
215 // This will clear the retry count. This is also why the rule
216 // first transit the state, then send packets.
218 if (State
== Dhcp4Selecting
) {
219 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.DiscoverTryCount
;
221 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.RequestTryCount
;
224 if (DhcpSb
->MaxRetries
== 0) {
225 DhcpSb
->MaxRetries
= 4;
228 DhcpSb
->CurRetry
= 0;
229 DhcpSb
->PacketToLive
= 0;
230 DhcpSb
->LastTimeout
= 0;
231 DhcpSb
->DhcpState
= State
;
237 Set the retransmit timer for the packet. It will select from either
238 the discover timeouts/request timeouts or the default timeout values.
240 @param DhcpSb The DHCP service instance.
244 DhcpSetTransmitTimer (
245 IN OUT DHCP_SERVICE
*DhcpSb
250 ASSERT (DhcpSb
->MaxRetries
> DhcpSb
->CurRetry
);
252 if (DhcpSb
->DhcpState
== Dhcp4Selecting
) {
253 Times
= DhcpSb
->ActiveConfig
.DiscoverTimeout
;
255 Times
= DhcpSb
->ActiveConfig
.RequestTimeout
;
259 Times
= mDhcp4DefaultTimeout
;
262 DhcpSb
->PacketToLive
= Times
[DhcpSb
->CurRetry
];
263 DhcpSb
->LastTimeout
= DhcpSb
->PacketToLive
;
269 Compute the lease. If the server grants a permanent lease, just
270 process it as a normal timeout value since the lease will last
273 @param DhcpSb The DHCP service instance
274 @param Para The DHCP parameter extracted from the server's
279 IN OUT DHCP_SERVICE
*DhcpSb
,
280 IN DHCP_PARAMETER
*Para
283 ASSERT (Para
!= NULL
);
285 DhcpSb
->Lease
= Para
->Lease
;
286 DhcpSb
->T2
= Para
->T2
;
287 DhcpSb
->T1
= Para
->T1
;
289 if (DhcpSb
->Lease
== 0) {
290 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
293 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
294 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
297 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
298 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
304 Configure a UDP IO port to use the acquired lease address.
305 DHCP driver needs this port to unicast packet to the server
306 such as DHCP release.
308 @param[in] UdpIo The UDP IO port to configure
309 @param[in] Context Dhcp service instance.
311 @retval EFI_SUCCESS The UDP IO port is successfully configured.
312 @retval Others It failed to configure the port.
316 DhcpConfigLeaseIoPort (
317 IN UDP_IO_PORT
*UdpIo
,
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
->Udp
->Configure (UdpIo
->Udp
, &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
->Udp
->Routes (UdpIo
->Udp
, 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
392 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
394 if (DhcpSb
->Para
!= NULL
) {
395 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
396 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
399 if (DhcpSb
->Netmask
== 0) {
400 Class
= NetGetIpClass (DhcpSb
->ClientAddr
);
401 DhcpSb
->Netmask
= gIp4AllMasks
[Class
<< 3];
404 if (DhcpSb
->LeaseIoPort
!= NULL
) {
405 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
409 // Create a UDP/IP child to provide ARP service for the Leased IP,
410 // and transmit unicast packet with it as source address. Don't
411 // start receive on this port, the queued packet will be timeout.
413 DhcpSb
->LeaseIoPort
= UdpIoCreatePort (
416 DhcpConfigLeaseIoPort
,
420 if (DhcpSb
->LeaseIoPort
== NULL
) {
421 return EFI_OUT_OF_RESOURCES
;
424 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
425 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
428 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
433 Clean up the DHCP related states, IoStatus isn't reset.
435 @param DhcpSb The DHCP instance service.
440 IN DHCP_SERVICE
*DhcpSb
443 DhcpSb
->DhcpState
= Dhcp4Init
;
444 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
445 DhcpSb
->ClientAddr
= 0;
447 DhcpSb
->ServerAddr
= 0;
449 if (DhcpSb
->LastOffer
!= NULL
) {
450 FreePool (DhcpSb
->LastOffer
);
451 DhcpSb
->LastOffer
= NULL
;
454 if (DhcpSb
->Selected
!= NULL
) {
455 FreePool (DhcpSb
->Selected
);
456 DhcpSb
->Selected
= NULL
;
459 if (DhcpSb
->Para
!= NULL
) {
460 FreePool (DhcpSb
->Para
);
467 DhcpSb
->ExtraRefresh
= FALSE
;
469 if (DhcpSb
->LeaseIoPort
!= NULL
) {
470 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
471 DhcpSb
->LeaseIoPort
= NULL
;
474 if (DhcpSb
->LastPacket
!= NULL
) {
475 FreePool (DhcpSb
->LastPacket
);
476 DhcpSb
->LastPacket
= NULL
;
479 DhcpSb
->PacketToLive
= 0;
480 DhcpSb
->LastTimeout
= 0;
481 DhcpSb
->CurRetry
= 0;
482 DhcpSb
->MaxRetries
= 0;
483 DhcpSb
->LeaseLife
= 0;
488 Select a offer among all the offers collected. If the offer selected is
489 of BOOTP, the lease is recorded and user notified. If the offer is of
490 DHCP, it will request the offer from the server.
492 @param[in] DhcpSb The DHCP service instance.
494 @retval EFI_SUCCESS One of the offer is selected.
499 IN DHCP_SERVICE
*DhcpSb
502 EFI_DHCP4_PACKET
*Selected
;
503 EFI_DHCP4_PACKET
*NewPacket
;
504 EFI_DHCP4_PACKET
*TempPacket
;
507 ASSERT (DhcpSb
->LastOffer
!= NULL
);
510 // User will cache previous offers if he wants to select
511 // from multiple offers. If user provides an invalid packet,
512 // use the last offer, otherwise use the provided packet.
515 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
517 if (EFI_ERROR (Status
)) {
521 Selected
= DhcpSb
->LastOffer
;
523 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
524 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
525 if (TempPacket
!= NULL
) {
526 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
527 gBS
->FreePool (Selected
);
528 Selected
= TempPacket
;
532 DhcpSb
->Selected
= Selected
;
533 DhcpSb
->LastOffer
= NULL
;
535 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
538 // A bootp offer has been selected, save the lease status,
539 // enter bound state then notify the user.
541 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
542 Status
= DhcpLeaseAcquired (DhcpSb
);
544 if (EFI_ERROR (Status
)) {
548 DhcpSb
->IoStatus
= EFI_SUCCESS
;
549 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
554 // Send a DHCP requests
556 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
558 if (EFI_ERROR (Status
)) {
562 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
567 Terminate the current address acquire. All the allocated resources
568 are released. Be careful when calling this function. A rule related
569 to this is: only call DhcpEndSession at the highest level, such as
570 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
572 @param[in] DhcpSb The DHCP service instance
573 @param[in] Status The result of the DHCP process.
578 IN DHCP_SERVICE
*DhcpSb
,
582 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
583 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
585 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
588 DhcpCleanLease (DhcpSb
);
590 DhcpSb
->IoStatus
= Status
;
591 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
596 Handle packets in DHCP select state.
598 @param[in] DhcpSb The DHCP service instance
599 @param[in] Packet The DHCP packet received
600 @param[in] Para The DHCP parameter extracted from the packet. That
601 is, all the option value that we care.
603 @retval EFI_SUCCESS The packet is successfully processed.
604 @retval Others Some error occured.
609 IN DHCP_SERVICE
*DhcpSb
,
610 IN EFI_DHCP4_PACKET
*Packet
,
611 IN DHCP_PARAMETER
*Para
616 Status
= EFI_SUCCESS
;
619 // First validate the message:
620 // 1. the offer is a unicast
621 // 2. if it is a DHCP message, it must contains a server ID.
622 // Don't return a error for these two case otherwise the session is ended.
624 if (!DHCP_IS_BOOTP (Para
) &&
625 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))
631 // Call the user's callback. The action according to the return is as:
632 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
633 // 2. EFI_NOT_READY: wait for more offers
634 // 3. EFI_ABORTED: abort the address acquiring.
636 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
638 if (Status
== EFI_SUCCESS
) {
639 if (DhcpSb
->LastOffer
!= NULL
) {
640 FreePool (DhcpSb
->LastOffer
);
643 DhcpSb
->LastOffer
= Packet
;
645 return DhcpChooseOffer (DhcpSb
);
647 } else if (Status
== EFI_NOT_READY
) {
648 if (DhcpSb
->LastOffer
!= NULL
) {
649 FreePool (DhcpSb
->LastOffer
);
652 DhcpSb
->LastOffer
= Packet
;
654 } else if (Status
== EFI_ABORTED
) {
656 // DhcpInput will end the session upon error return. Remember
657 // only to call DhcpEndSession at the top level call.
665 gBS
->FreePool (Packet
);
671 Handle packets in DHCP request state.
673 @param[in] DhcpSb The DHCP service instance
674 @param[in] Packet The DHCP packet received
675 @param[in] Para The DHCP parameter extracted from the packet. That
676 is, all the option value that we care.
678 @retval EFI_SUCCESS The packet is successfully processed.
679 @retval Others Some error occured.
684 IN DHCP_SERVICE
*DhcpSb
,
685 IN EFI_DHCP4_PACKET
*Packet
,
686 IN DHCP_PARAMETER
*Para
689 EFI_DHCP4_HEADER
*Head
;
690 EFI_DHCP4_HEADER
*Selected
;
694 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
696 Head
= &Packet
->Dhcp4
.Header
;
697 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
700 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
702 if (DHCP_IS_BOOTP (Para
) ||
703 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
704 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
707 Status
= EFI_SUCCESS
;
712 // Received a NAK, end the session no matter what the user returns
714 Status
= EFI_DEVICE_ERROR
;
716 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
717 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
722 // Check whether the ACK matches the selected offer
726 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
727 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
731 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
733 if (EFI_ERROR (Status
)) {
734 Message
= (UINT8
*) "Lease is denied upon received ACK";
739 // Record the lease, transit to BOUND state, then notify the user
741 Status
= DhcpLeaseAcquired (DhcpSb
);
743 if (EFI_ERROR (Status
)) {
744 Message
= (UINT8
*) "Lease is denied upon entering bound";
748 DhcpSb
->IoStatus
= EFI_SUCCESS
;
749 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
751 gBS
->FreePool (Packet
);
755 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
758 gBS
->FreePool (Packet
);
764 Handle packets in DHCP renew/rebound state.
766 @param[in] DhcpSb The DHCP service instance
767 @param[in] Packet The DHCP packet received
768 @param[in] Para The DHCP parameter extracted from the packet. That
769 is, all the option value that we care.
771 @retval EFI_SUCCESS The packet is successfully processed.
772 @retval Others Some error occured.
776 DhcpHandleRenewRebind (
777 IN DHCP_SERVICE
*DhcpSb
,
778 IN EFI_DHCP4_PACKET
*Packet
,
779 IN DHCP_PARAMETER
*Para
782 EFI_DHCP4_HEADER
*Head
;
783 EFI_DHCP4_HEADER
*Selected
;
786 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
788 Head
= &Packet
->Dhcp4
.Header
;
789 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
792 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
794 if (DHCP_IS_BOOTP (Para
) ||
795 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
796 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
799 Status
= EFI_SUCCESS
;
804 // Received a NAK, ignore the user's return then terminate the process
806 Status
= EFI_DEVICE_ERROR
;
808 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
809 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
814 // The lease is different from the selected. Don't send a DECLINE
815 // since it isn't existed in the client's FSM.
817 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
821 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
823 if (EFI_ERROR (Status
)) {
828 // Record the lease, start timer for T1 and T2,
830 DhcpComputeLease (DhcpSb
, Para
);
831 DhcpSb
->LeaseLife
= 0;
832 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
834 if (DhcpSb
->ExtraRefresh
!= 0) {
835 DhcpSb
->ExtraRefresh
= FALSE
;
837 DhcpSb
->IoStatus
= EFI_SUCCESS
;
838 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
842 gBS
->FreePool (Packet
);
848 Handle packets in DHCP reboot state.
850 @param[in] DhcpSb The DHCP service instance
851 @param[in] Packet The DHCP packet received
852 @param[in] Para The DHCP parameter extracted from the packet. That
853 is, all the option value that we care.
855 @retval EFI_SUCCESS The packet is successfully processed.
856 @retval Others Some error occured.
861 IN DHCP_SERVICE
*DhcpSb
,
862 IN EFI_DHCP4_PACKET
*Packet
,
863 IN DHCP_PARAMETER
*Para
866 EFI_DHCP4_HEADER
*Head
;
869 Head
= &Packet
->Dhcp4
.Header
;
872 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
874 if (DHCP_IS_BOOTP (Para
) ||
875 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
878 Status
= EFI_SUCCESS
;
883 // If a NAK is received, transit to INIT and try again.
885 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
886 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
888 DhcpSb
->ClientAddr
= 0;
889 DhcpSb
->DhcpState
= Dhcp4Init
;
891 Status
= DhcpInitRequest (DhcpSb
);
896 // Check whether the ACK matches the selected offer
898 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
899 Status
= EFI_DEVICE_ERROR
;
903 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
904 if (EFI_ERROR (Status
)) {
909 // OK, get the parameter from server, record the lease
911 DhcpSb
->Para
= AllocateCopyPool (sizeof (DHCP_PARAMETER
), Para
);
912 if (DhcpSb
->Para
== NULL
) {
913 Status
= EFI_OUT_OF_RESOURCES
;
917 DhcpSb
->Selected
= Packet
;
918 Status
= DhcpLeaseAcquired (DhcpSb
);
919 if (EFI_ERROR (Status
)) {
923 DhcpSb
->IoStatus
= EFI_SUCCESS
;
924 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
928 gBS
->FreePool (Packet
);
934 Handle the received DHCP packets. This function drives the DHCP
937 @param UdpPacket The UDP packets received.
938 @param Points The local/remote UDP access points
939 @param IoStatus The status of the UDP receive
940 @param Context The opaque parameter to the function.
951 DHCP_SERVICE
*DhcpSb
;
952 EFI_DHCP4_HEADER
*Head
;
953 EFI_DHCP4_PACKET
*Packet
;
954 DHCP_PARAMETER
*Para
;
959 DhcpSb
= (DHCP_SERVICE
*) Context
;
962 // Don't restart receive if error occurs or DHCP is destoried.
964 if (EFI_ERROR (IoStatus
)) {
966 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
967 NetbufFree (UdpPacket
);
971 ASSERT (UdpPacket
!= NULL
);
973 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
978 // Validate the packet received
980 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
985 // Copy the DHCP message to a continuous memory block
987 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
988 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
990 if (Packet
== NULL
) {
995 Head
= &Packet
->Dhcp4
.Header
;
996 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
998 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1003 // Is this packet the answer to our packet?
1005 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1006 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1007 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1012 // Validate the options and retrieve the interested options
1015 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1016 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1017 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1023 // Call the handler for each state. The handler should return
1024 // EFI_SUCCESS if the process can go on no matter whether the
1025 // packet is ignored or not. If the return is EFI_ERROR, the
1026 // session will be terminated. Packet's ownership is handled
1027 // over to the handlers. If operation succeeds, the handler
1028 // must notify the user. It isn't necessary to do if EFI_ERROR
1029 // is returned because the DhcpEndSession will notify the user.
1031 Status
= EFI_SUCCESS
;
1033 switch (DhcpSb
->DhcpState
) {
1034 case Dhcp4Selecting
:
1035 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1038 case Dhcp4Requesting
:
1039 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1042 case Dhcp4InitReboot
:
1046 // Ignore the packet in INITREBOOT, INIT and BOUND states
1048 gBS
->FreePool (Packet
);
1049 Status
= EFI_SUCCESS
;
1053 case Dhcp4Rebinding
:
1054 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1057 case Dhcp4Rebooting
:
1058 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1068 if (EFI_ERROR (Status
)) {
1069 NetbufFree (UdpPacket
);
1070 UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1071 DhcpEndSession (DhcpSb
, Status
);
1076 NetbufFree (UdpPacket
);
1078 if (Packet
!= NULL
) {
1079 gBS
->FreePool (Packet
);
1082 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1084 if (EFI_ERROR (Status
)) {
1085 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1093 @param[in] Arg The packet to release
1101 gBS
->FreePool (Arg
);
1106 Release the net buffer when packet is sent.
1108 @param UdpPacket The UDP packets received.
1109 @param Points The local/remote UDP access points
1110 @param IoStatus The status of the UDP receive
1111 @param Context The opaque parameter to the function.
1118 EFI_STATUS IoStatus
,
1122 NetbufFree (Packet
);
1128 Build and transmit a DHCP message according to the current states.
1129 This function implement the Table 5. of RFC 2131. Always transits
1130 the state (as defined in Figure 5. of the same RFC) before sending
1131 a DHCP message. The table is adjusted accordingly.
1133 @param[in] DhcpSb The DHCP service instance
1134 @param[in] Seed The seed packet which the new packet is based on
1135 @param[in] Para The DHCP parameter of the Seed packet
1136 @param[in] Type The message type to send
1137 @param[in] Msg The human readable message to include in the packet
1140 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1141 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1142 @retval EFI_SUCCESS The message is sent
1143 @retval other Other error occurs
1148 IN DHCP_SERVICE
*DhcpSb
,
1149 IN EFI_DHCP4_PACKET
*Seed
,
1150 IN DHCP_PARAMETER
*Para
,
1155 EFI_DHCP4_CONFIG_DATA
*Config
;
1156 EFI_DHCP4_PACKET
*Packet
;
1157 EFI_DHCP4_PACKET
*NewPacket
;
1158 EFI_DHCP4_HEADER
*Head
;
1159 EFI_DHCP4_HEADER
*SeedHead
;
1161 UDP_POINTS EndPoint
;
1172 // Allocate a big enough memory block to hold the DHCP packet
1174 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1177 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1180 Packet
= AllocatePool (Len
);
1182 if (Packet
== NULL
) {
1183 return EFI_OUT_OF_RESOURCES
;
1187 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1190 // Fill in the DHCP header fields
1192 Config
= &DhcpSb
->ActiveConfig
;
1196 SeedHead
= &Seed
->Dhcp4
.Header
;
1199 Head
= &Packet
->Dhcp4
.Header
;
1200 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1202 Head
->OpCode
= BOOTP_REQUEST
;
1203 Head
->HwType
= DhcpSb
->HwType
;
1204 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1205 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1206 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1208 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1209 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1212 // Append the DHCP message type
1214 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1215 Buf
= Packet
->Dhcp4
.Option
;
1216 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_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
, DHCP_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
, DHCP_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
, DHCP_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
, DHCP_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
!= DHCP_TAG_CLIENT_ID
)) {
1292 Buf
= DhcpAppendOption (
1294 Config
->OptionList
[Index
]->OpCode
,
1295 Config
->OptionList
[Index
]->Length
,
1296 Config
->OptionList
[Index
]->Data
1301 *(Buf
++) = DHCP_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
)) {
1321 gBS
->FreePool (Packet
);
1325 if (NewPacket
!= NULL
) {
1326 gBS
->FreePool (Packet
);
1331 // Save the Client Address will be sent out
1334 &DhcpSb
->ClientAddressSendOut
[0],
1335 &Packet
->Dhcp4
.Header
.ClientHwAddr
[0],
1336 Packet
->Dhcp4
.Header
.HwAddrLen
1341 // Wrap it into a netbuf then send it.
1343 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1344 Frag
.Len
= Packet
->Length
;
1345 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1348 gBS
->FreePool (Packet
);
1349 return EFI_OUT_OF_RESOURCES
;
1353 // Save it as the last sent packet for retransmission
1355 if (DhcpSb
->LastPacket
!= NULL
) {
1356 FreePool (DhcpSb
->LastPacket
);
1359 DhcpSb
->LastPacket
= Packet
;
1360 DhcpSetTransmitTimer (DhcpSb
);
1363 // Broadcast the message, unless we know the server address.
1364 // Use the lease UdpIo port to send the unicast packet.
1366 EndPoint
.RemoteAddr
= 0xffffffff;
1367 EndPoint
.LocalAddr
= 0;
1368 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1369 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1370 UdpIo
= DhcpSb
->UdpIo
;
1372 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1373 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1374 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1375 UdpIo
= DhcpSb
->LeaseIoPort
;
1378 ASSERT (UdpIo
!= NULL
);
1381 Status
= UdpIoSendDatagram (
1390 if (EFI_ERROR (Status
)) {
1392 return EFI_ACCESS_DENIED
;
1400 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1401 will be retransmitted.
1403 @param[in] DhcpSb The DHCP service instance
1405 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1406 @retval EFI_SUCCESS The packet is retransmitted.
1411 IN DHCP_SERVICE
*DhcpSb
1415 UDP_POINTS EndPoint
;
1420 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1422 DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
= HTONS (*(UINT16
*)(&DhcpSb
->LastTimeout
));
1425 // Wrap it into a netbuf then send it.
1427 Frag
.Bulk
= (UINT8
*) &DhcpSb
->LastPacket
->Dhcp4
.Header
;
1428 Frag
.Len
= DhcpSb
->LastPacket
->Length
;
1429 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, DhcpSb
->LastPacket
);
1432 return EFI_OUT_OF_RESOURCES
;
1436 // Broadcast the message, unless we know the server address.
1438 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1439 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1440 EndPoint
.RemoteAddr
= 0xffffffff;
1441 EndPoint
.LocalAddr
= 0;
1442 UdpIo
= DhcpSb
->UdpIo
;
1444 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1445 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1446 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1447 UdpIo
= DhcpSb
->LeaseIoPort
;
1450 ASSERT (UdpIo
!= NULL
);
1453 Status
= UdpIoSendDatagram (
1462 if (EFI_ERROR (Status
)) {
1464 return EFI_ACCESS_DENIED
;
1472 Each DHCP service has three timer. Two of them are count down timer.
1473 One for the packet retransmission. The other is to collect the offers.
1474 The third timer increaments the lease life which is compared to T1, T2,
1475 and lease to determine the time to renew and rebind the lease.
1476 DhcpOnTimerTick will be called once every second.
1478 @param[in] Event The timer event
1479 @param[in] Context The context, which is the DHCP service instance.
1489 DHCP_SERVICE
*DhcpSb
;
1490 DHCP_PROTOCOL
*Instance
;
1493 DhcpSb
= (DHCP_SERVICE
*) Context
;
1494 Instance
= DhcpSb
->ActiveChild
;
1497 // Check the retransmit timer
1499 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1502 // Select offer at each timeout if any offer received.
1504 if (DhcpSb
->DhcpState
== Dhcp4Selecting
&& DhcpSb
->LastOffer
!= NULL
) {
1506 Status
= DhcpChooseOffer (DhcpSb
);
1508 if (EFI_ERROR(Status
)) {
1509 FreePool (DhcpSb
->LastOffer
);
1510 DhcpSb
->LastOffer
= NULL
;
1516 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1518 // Still has another try
1520 DhcpRetransmit (DhcpSb
);
1521 DhcpSetTransmitTimer (DhcpSb
);
1523 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1526 // Retransmission failed, if the DHCP request is initiated by
1527 // user, adjust the current state according to the lease life.
1528 // Otherwise do nothing to wait the lease to timeout
1530 if (DhcpSb
->ExtraRefresh
!= 0) {
1531 Status
= EFI_SUCCESS
;
1533 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1534 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1536 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1537 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1539 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1540 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1547 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1548 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1556 // If an address has been acquired, check whether need to
1557 // refresh or whether it has expired.
1559 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1560 DhcpSb
->LeaseLife
++;
1563 // Don't timeout the lease, only count the life if user is
1564 // requesting extra renew/rebind. Adjust the state after that.
1566 if (DhcpSb
->ExtraRefresh
!= 0) {
1570 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1572 // Lease expires, end the session
1576 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1578 // T2 expires, transit to rebinding then send a REQUEST to any server
1580 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1584 Status
= DhcpSendMessage (
1592 if (EFI_ERROR (Status
)) {
1596 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1598 // T1 expires, transit to renewing, then send a REQUEST to the server
1600 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1604 Status
= DhcpSendMessage (
1612 if (EFI_ERROR (Status
)) {
1619 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1620 Instance
->Timeout
--;
1621 if (Instance
->Timeout
== 0) {
1622 PxeDhcpDone (Instance
);
1629 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);