2 EFI DHCP protocol implementation.
4 Copyright (c) 2006 - 2010, 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
));
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 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.
317 DhcpConfigLeaseIoPort (
322 EFI_UDP4_CONFIG_DATA UdpConfigData
;
323 EFI_IPv4_ADDRESS Subnet
;
324 EFI_IPv4_ADDRESS Gateway
;
325 DHCP_SERVICE
*DhcpSb
;
329 DhcpSb
= (DHCP_SERVICE
*) Context
;
331 UdpConfigData
.AcceptBroadcast
= FALSE
;
332 UdpConfigData
.AcceptPromiscuous
= FALSE
;
333 UdpConfigData
.AcceptAnyPort
= FALSE
;
334 UdpConfigData
.AllowDuplicatePort
= TRUE
;
335 UdpConfigData
.TypeOfService
= 0;
336 UdpConfigData
.TimeToLive
= 64;
337 UdpConfigData
.DoNotFragment
= FALSE
;
338 UdpConfigData
.ReceiveTimeout
= 1;
339 UdpConfigData
.TransmitTimeout
= 0;
341 UdpConfigData
.UseDefaultAddress
= FALSE
;
342 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
343 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
345 Ip
= HTONL (DhcpSb
->ClientAddr
);
346 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
348 Ip
= HTONL (DhcpSb
->Netmask
);
349 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
351 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
353 Status
= UdpIo
->Protocol
.Udp4
->Configure (UdpIo
->Protocol
.Udp4
, &UdpConfigData
);
355 if (EFI_ERROR (Status
)) {
360 // Add a default route if received from the server.
362 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
363 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
365 Ip
= HTONL (DhcpSb
->Para
->Router
);
366 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
368 UdpIo
->Protocol
.Udp4
->Routes (UdpIo
->Protocol
.Udp4
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
376 Update the lease states when a new lease is acquired. It will not only
377 save the acquired the address and lease time, it will also create a UDP
378 child to provide address resolution for the address.
380 @param DhcpSb The DHCP service instance
382 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
383 @retval EFI_SUCCESS The lease is recorded.
388 IN OUT DHCP_SERVICE
*DhcpSb
393 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
395 if (DhcpSb
->Para
!= NULL
) {
396 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
397 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
400 if (DhcpSb
->Netmask
== 0) {
401 Class
= NetGetIpClass (DhcpSb
->ClientAddr
);
402 DhcpSb
->Netmask
= gIp4AllMasks
[Class
<< 3];
405 if (DhcpSb
->LeaseIoPort
!= NULL
) {
406 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
410 // Create a UDP/IP child to provide ARP service for the Leased IP,
411 // and transmit unicast packet with it as source address. Don't
412 // start receive on this port, the queued packet will be timeout.
414 DhcpSb
->LeaseIoPort
= UdpIoCreateIo (
417 DhcpConfigLeaseIoPort
,
422 if (DhcpSb
->LeaseIoPort
== NULL
) {
423 return EFI_OUT_OF_RESOURCES
;
426 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
427 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
430 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
435 Clean up the DHCP related states, IoStatus isn't reset.
437 @param DhcpSb The DHCP instance service.
442 IN DHCP_SERVICE
*DhcpSb
445 DhcpSb
->DhcpState
= Dhcp4Init
;
446 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
447 DhcpSb
->ClientAddr
= 0;
449 DhcpSb
->ServerAddr
= 0;
451 if (DhcpSb
->LastOffer
!= NULL
) {
452 FreePool (DhcpSb
->LastOffer
);
453 DhcpSb
->LastOffer
= NULL
;
456 if (DhcpSb
->Selected
!= NULL
) {
457 FreePool (DhcpSb
->Selected
);
458 DhcpSb
->Selected
= NULL
;
461 if (DhcpSb
->Para
!= NULL
) {
462 FreePool (DhcpSb
->Para
);
469 DhcpSb
->ExtraRefresh
= FALSE
;
471 if (DhcpSb
->LeaseIoPort
!= NULL
) {
472 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
473 DhcpSb
->LeaseIoPort
= NULL
;
476 if (DhcpSb
->LastPacket
!= NULL
) {
477 FreePool (DhcpSb
->LastPacket
);
478 DhcpSb
->LastPacket
= NULL
;
481 DhcpSb
->PacketToLive
= 0;
482 DhcpSb
->LastTimeout
= 0;
483 DhcpSb
->CurRetry
= 0;
484 DhcpSb
->MaxRetries
= 0;
485 DhcpSb
->LeaseLife
= 0;
488 // Clean active config data.
490 DhcpCleanConfigure (&DhcpSb
->ActiveConfig
);
495 Select a offer among all the offers collected. If the offer selected is
496 of BOOTP, the lease is recorded and user notified. If the offer is of
497 DHCP, it will request the offer from the server.
499 @param[in] DhcpSb The DHCP service instance.
501 @retval EFI_SUCCESS One of the offer is selected.
506 IN DHCP_SERVICE
*DhcpSb
509 EFI_DHCP4_PACKET
*Selected
;
510 EFI_DHCP4_PACKET
*NewPacket
;
511 EFI_DHCP4_PACKET
*TempPacket
;
514 ASSERT (DhcpSb
->LastOffer
!= NULL
);
517 // User will cache previous offers if he wants to select
518 // from multiple offers. If user provides an invalid packet,
519 // use the last offer, otherwise use the provided packet.
522 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
524 if (EFI_ERROR (Status
)) {
528 Selected
= DhcpSb
->LastOffer
;
530 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
531 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
532 if (TempPacket
!= NULL
) {
533 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
535 Selected
= TempPacket
;
539 DhcpSb
->Selected
= Selected
;
540 DhcpSb
->LastOffer
= NULL
;
542 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
545 // A bootp offer has been selected, save the lease status,
546 // enter bound state then notify the user.
548 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
549 Status
= DhcpLeaseAcquired (DhcpSb
);
551 if (EFI_ERROR (Status
)) {
555 DhcpSb
->IoStatus
= EFI_SUCCESS
;
556 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
561 // Send a DHCP requests
563 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
565 if (EFI_ERROR (Status
)) {
569 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
574 Terminate the current address acquire. All the allocated resources
575 are released. Be careful when calling this function. A rule related
576 to this is: only call DhcpEndSession at the highest level, such as
577 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
579 @param[in] DhcpSb The DHCP service instance
580 @param[in] Status The result of the DHCP process.
585 IN DHCP_SERVICE
*DhcpSb
,
589 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
590 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
592 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
595 DhcpCleanLease (DhcpSb
);
597 DhcpSb
->IoStatus
= Status
;
598 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
603 Handle packets in DHCP select state.
605 @param[in] DhcpSb The DHCP service instance
606 @param[in] Packet The DHCP packet received
607 @param[in] Para The DHCP parameter extracted from the packet. That
608 is, all the option value that we care.
610 @retval EFI_SUCCESS The packet is successfully processed.
611 @retval Others Some error occured.
616 IN DHCP_SERVICE
*DhcpSb
,
617 IN EFI_DHCP4_PACKET
*Packet
,
618 IN DHCP_PARAMETER
*Para
623 Status
= EFI_SUCCESS
;
626 // First validate the message:
627 // 1. the offer is a unicast
628 // 2. if it is a DHCP message, it must contains a server ID.
629 // Don't return a error for these two case otherwise the session is ended.
631 if (!DHCP_IS_BOOTP (Para
) &&
632 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))
638 // Call the user's callback. The action according to the return is as:
639 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
640 // 2. EFI_NOT_READY: wait for more offers
641 // 3. EFI_ABORTED: abort the address acquiring.
643 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
645 if (Status
== EFI_SUCCESS
) {
646 if (DhcpSb
->LastOffer
!= NULL
) {
647 FreePool (DhcpSb
->LastOffer
);
650 DhcpSb
->LastOffer
= Packet
;
652 return DhcpChooseOffer (DhcpSb
);
654 } else if (Status
== EFI_NOT_READY
) {
655 if (DhcpSb
->LastOffer
!= NULL
) {
656 FreePool (DhcpSb
->LastOffer
);
659 DhcpSb
->LastOffer
= Packet
;
661 } else if (Status
== EFI_ABORTED
) {
663 // DhcpInput will end the session upon error return. Remember
664 // only to call DhcpEndSession at the top level call.
678 Handle packets in DHCP request state.
680 @param[in] DhcpSb The DHCP service instance
681 @param[in] Packet The DHCP packet received
682 @param[in] Para The DHCP parameter extracted from the packet. That
683 is, all the option value that we care.
685 @retval EFI_SUCCESS The packet is successfully processed.
686 @retval Others Some error occured.
691 IN DHCP_SERVICE
*DhcpSb
,
692 IN EFI_DHCP4_PACKET
*Packet
,
693 IN DHCP_PARAMETER
*Para
696 EFI_DHCP4_HEADER
*Head
;
697 EFI_DHCP4_HEADER
*Selected
;
701 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
703 Head
= &Packet
->Dhcp4
.Header
;
704 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
707 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
709 if (DHCP_IS_BOOTP (Para
) ||
710 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
711 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
714 Status
= EFI_SUCCESS
;
719 // Received a NAK, end the session no matter what the user returns
721 Status
= EFI_DEVICE_ERROR
;
723 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
724 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
729 // Check whether the ACK matches the selected offer
733 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
734 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
738 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
740 if (EFI_ERROR (Status
)) {
741 Message
= (UINT8
*) "Lease is denied upon received ACK";
746 // Record the lease, transit to BOUND state, then notify the user
748 Status
= DhcpLeaseAcquired (DhcpSb
);
750 if (EFI_ERROR (Status
)) {
751 Message
= (UINT8
*) "Lease is denied upon entering bound";
755 DhcpSb
->IoStatus
= EFI_SUCCESS
;
756 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
762 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
771 Handle packets in DHCP renew/rebound state.
773 @param[in] DhcpSb The DHCP service instance
774 @param[in] Packet The DHCP packet received
775 @param[in] Para The DHCP parameter extracted from the packet. That
776 is, all the option value that we care.
778 @retval EFI_SUCCESS The packet is successfully processed.
779 @retval Others Some error occured.
783 DhcpHandleRenewRebind (
784 IN DHCP_SERVICE
*DhcpSb
,
785 IN EFI_DHCP4_PACKET
*Packet
,
786 IN DHCP_PARAMETER
*Para
789 EFI_DHCP4_HEADER
*Head
;
790 EFI_DHCP4_HEADER
*Selected
;
793 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
795 Head
= &Packet
->Dhcp4
.Header
;
796 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
799 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
801 if (DHCP_IS_BOOTP (Para
) ||
802 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
803 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
806 Status
= EFI_SUCCESS
;
811 // Received a NAK, ignore the user's return then terminate the process
813 Status
= EFI_DEVICE_ERROR
;
815 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
816 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
821 // The lease is different from the selected. Don't send a DECLINE
822 // since it isn't existed in the client's FSM.
824 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
828 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
830 if (EFI_ERROR (Status
)) {
835 // Record the lease, start timer for T1 and T2,
837 DhcpComputeLease (DhcpSb
, Para
);
838 DhcpSb
->LeaseLife
= 0;
839 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
841 if (DhcpSb
->ExtraRefresh
!= 0) {
842 DhcpSb
->ExtraRefresh
= FALSE
;
844 DhcpSb
->IoStatus
= EFI_SUCCESS
;
845 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
855 Handle packets in DHCP reboot state.
857 @param[in] DhcpSb The DHCP service instance
858 @param[in] Packet The DHCP packet received
859 @param[in] Para The DHCP parameter extracted from the packet. That
860 is, all the option value that we care.
862 @retval EFI_SUCCESS The packet is successfully processed.
863 @retval Others Some error occured.
868 IN DHCP_SERVICE
*DhcpSb
,
869 IN EFI_DHCP4_PACKET
*Packet
,
870 IN DHCP_PARAMETER
*Para
873 EFI_DHCP4_HEADER
*Head
;
876 Head
= &Packet
->Dhcp4
.Header
;
879 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
881 if (DHCP_IS_BOOTP (Para
) ||
882 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
885 Status
= EFI_SUCCESS
;
890 // If a NAK is received, transit to INIT and try again.
892 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
893 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
895 DhcpSb
->ClientAddr
= 0;
896 DhcpSb
->DhcpState
= Dhcp4Init
;
898 Status
= DhcpInitRequest (DhcpSb
);
903 // Check whether the ACK matches the selected offer
905 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
906 Status
= EFI_DEVICE_ERROR
;
910 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
911 if (EFI_ERROR (Status
)) {
916 // OK, get the parameter from server, record the lease
918 DhcpSb
->Para
= AllocateCopyPool (sizeof (DHCP_PARAMETER
), Para
);
919 if (DhcpSb
->Para
== NULL
) {
920 Status
= EFI_OUT_OF_RESOURCES
;
924 DhcpSb
->Selected
= Packet
;
925 Status
= DhcpLeaseAcquired (DhcpSb
);
926 if (EFI_ERROR (Status
)) {
930 DhcpSb
->IoStatus
= EFI_SUCCESS
;
931 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
941 Handle the received DHCP packets. This function drives the DHCP
944 @param UdpPacket The UDP packets received.
945 @param EndPoint The local/remote UDP access point
946 @param IoStatus The status of the UDP receive
947 @param Context The opaque parameter to the function.
954 UDP_END_POINT
*EndPoint
,
959 DHCP_SERVICE
*DhcpSb
;
960 EFI_DHCP4_HEADER
*Head
;
961 EFI_DHCP4_PACKET
*Packet
;
962 DHCP_PARAMETER
*Para
;
967 DhcpSb
= (DHCP_SERVICE
*) Context
;
970 // Don't restart receive if error occurs or DHCP is destoried.
972 if (EFI_ERROR (IoStatus
)) {
974 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
975 NetbufFree (UdpPacket
);
979 ASSERT (UdpPacket
!= NULL
);
981 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
986 // Validate the packet received
988 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
993 // Copy the DHCP message to a continuous memory block
995 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
996 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
998 if (Packet
== NULL
) {
1003 Head
= &Packet
->Dhcp4
.Header
;
1004 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1006 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1011 // Is this packet the answer to our packet?
1013 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1014 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1015 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1020 // Validate the options and retrieve the interested options
1023 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1024 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1025 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1031 // Call the handler for each state. The handler should return
1032 // EFI_SUCCESS if the process can go on no matter whether the
1033 // packet is ignored or not. If the return is EFI_ERROR, the
1034 // session will be terminated. Packet's ownership is handled
1035 // over to the handlers. If operation succeeds, the handler
1036 // must notify the user. It isn't necessary to do if EFI_ERROR
1037 // is returned because the DhcpEndSession will notify the user.
1039 Status
= EFI_SUCCESS
;
1041 switch (DhcpSb
->DhcpState
) {
1042 case Dhcp4Selecting
:
1043 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1046 case Dhcp4Requesting
:
1047 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1050 case Dhcp4InitReboot
:
1054 // Ignore the packet in INITREBOOT, INIT and BOUND states
1057 Status
= EFI_SUCCESS
;
1061 case Dhcp4Rebinding
:
1062 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1065 case Dhcp4Rebooting
:
1066 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1076 if (EFI_ERROR (Status
)) {
1077 NetbufFree (UdpPacket
);
1078 UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1079 DhcpEndSession (DhcpSb
, Status
);
1084 NetbufFree (UdpPacket
);
1086 if (Packet
!= NULL
) {
1090 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1092 if (EFI_ERROR (Status
)) {
1093 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1101 @param[in] Arg The packet to release
1115 Release the net buffer when packet is sent.
1117 @param UdpPacket The UDP packets received.
1118 @param EndPoint The local/remote UDP access point
1119 @param IoStatus The status of the UDP receive
1120 @param Context The opaque parameter to the function.
1127 UDP_END_POINT
*EndPoint
,
1128 EFI_STATUS IoStatus
,
1132 NetbufFree (Packet
);
1138 Build and transmit a DHCP message according to the current states.
1139 This function implement the Table 5. of RFC 2131. Always transits
1140 the state (as defined in Figure 5. of the same RFC) before sending
1141 a DHCP message. The table is adjusted accordingly.
1143 @param[in] DhcpSb The DHCP service instance
1144 @param[in] Seed The seed packet which the new packet is based on
1145 @param[in] Para The DHCP parameter of the Seed packet
1146 @param[in] Type The message type to send
1147 @param[in] Msg The human readable message to include in the packet
1150 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1151 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1152 @retval EFI_SUCCESS The message is sent
1153 @retval other Other error occurs
1158 IN DHCP_SERVICE
*DhcpSb
,
1159 IN EFI_DHCP4_PACKET
*Seed
,
1160 IN DHCP_PARAMETER
*Para
,
1165 EFI_DHCP4_CONFIG_DATA
*Config
;
1166 EFI_DHCP4_PACKET
*Packet
;
1167 EFI_DHCP4_PACKET
*NewPacket
;
1168 EFI_DHCP4_HEADER
*Head
;
1169 EFI_DHCP4_HEADER
*SeedHead
;
1171 UDP_END_POINT EndPoint
;
1182 // Allocate a big enough memory block to hold the DHCP packet
1184 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1187 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1190 Packet
= AllocatePool (Len
);
1192 if (Packet
== NULL
) {
1193 return EFI_OUT_OF_RESOURCES
;
1197 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1200 // Fill in the DHCP header fields
1202 Config
= &DhcpSb
->ActiveConfig
;
1206 SeedHead
= &Seed
->Dhcp4
.Header
;
1209 Head
= &Packet
->Dhcp4
.Header
;
1210 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1212 Head
->OpCode
= BOOTP_REQUEST
;
1213 Head
->HwType
= DhcpSb
->HwType
;
1214 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1215 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1216 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1218 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1219 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1222 // Append the DHCP message type
1224 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1225 Buf
= Packet
->Dhcp4
.Option
;
1226 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1229 // Append the serverid option if necessary:
1230 // 1. DHCP decline message
1231 // 2. DHCP release message
1232 // 3. DHCP request to confirm one lease.
1234 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1235 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))
1238 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1240 IpAddr
= HTONL (Para
->ServerId
);
1241 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1245 // Append the requested IP option if necessary:
1246 // 1. DHCP request to use the previously allocated address
1247 // 2. DHCP request to confirm one lease
1248 // 3. DHCP decline to decline one lease
1252 if (Type
== DHCP_MSG_REQUEST
) {
1253 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1254 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1256 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1257 ASSERT (SeedHead
!= NULL
);
1258 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1261 } else if (Type
== DHCP_MSG_DECLINE
) {
1262 ASSERT (SeedHead
!= NULL
);
1263 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1267 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1271 // Append the Max Message Length option if it isn't a DECLINE
1272 // or RELEASE to direct the server use large messages instead of
1273 // override the BOOTFILE and SERVER fields in the message head.
1275 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1276 MaxMsg
= HTONS (0xFF00);
1277 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1281 // Append the user's message if it isn't NULL
1284 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1285 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1289 // Append the user configured options
1291 if (DhcpSb
->UserOptionLen
!= 0) {
1292 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1294 // We can't use any option other than the client ID from user
1295 // if it is a DHCP decline or DHCP release .
1297 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1298 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1302 Buf
= DhcpAppendOption (
1304 Config
->OptionList
[Index
]->OpCode
,
1305 Config
->OptionList
[Index
]->Length
,
1306 Config
->OptionList
[Index
]->Data
1311 *(Buf
++) = DHCP_TAG_EOP
;
1312 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1315 // OK, the message is built, call the user to override it.
1317 Status
= EFI_SUCCESS
;
1320 if (Type
== DHCP_MSG_DISCOVER
) {
1321 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1323 } else if (Type
== DHCP_MSG_REQUEST
) {
1324 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1326 } else if (Type
== DHCP_MSG_DECLINE
) {
1327 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1330 if (EFI_ERROR (Status
)) {
1335 if (NewPacket
!= NULL
) {
1341 // Save the Client Address will be sent out
1344 &DhcpSb
->ClientAddressSendOut
[0],
1345 &Packet
->Dhcp4
.Header
.ClientHwAddr
[0],
1346 Packet
->Dhcp4
.Header
.HwAddrLen
1351 // Wrap it into a netbuf then send it.
1353 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1354 Frag
.Len
= Packet
->Length
;
1355 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1359 return EFI_OUT_OF_RESOURCES
;
1363 // Save it as the last sent packet for retransmission
1365 if (DhcpSb
->LastPacket
!= NULL
) {
1366 FreePool (DhcpSb
->LastPacket
);
1369 DhcpSb
->LastPacket
= Packet
;
1370 DhcpSetTransmitTimer (DhcpSb
);
1373 // Broadcast the message, unless we know the server address.
1374 // Use the lease UdpIo port to send the unicast packet.
1376 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1377 EndPoint
.LocalAddr
.Addr
[0] = 0;
1378 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1379 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1380 UdpIo
= DhcpSb
->UdpIo
;
1382 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1383 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1384 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1385 UdpIo
= DhcpSb
->LeaseIoPort
;
1388 ASSERT (UdpIo
!= NULL
);
1391 Status
= UdpIoSendDatagram (
1400 if (EFI_ERROR (Status
)) {
1402 return EFI_ACCESS_DENIED
;
1410 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1411 will be retransmitted.
1413 @param[in] DhcpSb The DHCP service instance
1415 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1416 @retval EFI_SUCCESS The packet is retransmitted.
1421 IN DHCP_SERVICE
*DhcpSb
1425 UDP_END_POINT EndPoint
;
1430 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1432 DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
= HTONS (*(UINT16
*)(&DhcpSb
->LastTimeout
));
1435 // Wrap it into a netbuf then send it.
1437 Frag
.Bulk
= (UINT8
*) &DhcpSb
->LastPacket
->Dhcp4
.Header
;
1438 Frag
.Len
= DhcpSb
->LastPacket
->Length
;
1439 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, DhcpSb
->LastPacket
);
1442 return EFI_OUT_OF_RESOURCES
;
1446 // Broadcast the message, unless we know the server address.
1448 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1449 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1450 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1451 EndPoint
.LocalAddr
.Addr
[0] = 0;
1452 UdpIo
= DhcpSb
->UdpIo
;
1454 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1455 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1456 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1457 UdpIo
= DhcpSb
->LeaseIoPort
;
1460 ASSERT (UdpIo
!= NULL
);
1463 Status
= UdpIoSendDatagram (
1472 if (EFI_ERROR (Status
)) {
1474 return EFI_ACCESS_DENIED
;
1482 Each DHCP service has three timer. Two of them are count down timer.
1483 One for the packet retransmission. The other is to collect the offers.
1484 The third timer increaments the lease life which is compared to T1, T2,
1485 and lease to determine the time to renew and rebind the lease.
1486 DhcpOnTimerTick will be called once every second.
1488 @param[in] Event The timer event
1489 @param[in] Context The context, which is the DHCP service instance.
1499 DHCP_SERVICE
*DhcpSb
;
1500 DHCP_PROTOCOL
*Instance
;
1503 DhcpSb
= (DHCP_SERVICE
*) Context
;
1504 Instance
= DhcpSb
->ActiveChild
;
1507 // Check the retransmit timer
1509 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1512 // Select offer at each timeout if any offer received.
1514 if (DhcpSb
->DhcpState
== Dhcp4Selecting
&& DhcpSb
->LastOffer
!= NULL
) {
1516 Status
= DhcpChooseOffer (DhcpSb
);
1518 if (EFI_ERROR(Status
)) {
1519 FreePool (DhcpSb
->LastOffer
);
1520 DhcpSb
->LastOffer
= NULL
;
1526 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1528 // Still has another try
1530 DhcpRetransmit (DhcpSb
);
1531 DhcpSetTransmitTimer (DhcpSb
);
1533 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1536 // Retransmission failed, if the DHCP request is initiated by
1537 // user, adjust the current state according to the lease life.
1538 // Otherwise do nothing to wait the lease to timeout
1540 if (DhcpSb
->ExtraRefresh
!= 0) {
1541 Status
= EFI_SUCCESS
;
1543 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1544 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1546 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1547 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1549 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1550 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1557 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1558 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1566 // If an address has been acquired, check whether need to
1567 // refresh or whether it has expired.
1569 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1570 DhcpSb
->LeaseLife
++;
1573 // Don't timeout the lease, only count the life if user is
1574 // requesting extra renew/rebind. Adjust the state after that.
1576 if (DhcpSb
->ExtraRefresh
!= 0) {
1580 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1582 // Lease expires, end the session
1586 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1588 // T2 expires, transit to rebinding then send a REQUEST to any server
1590 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1594 Status
= DhcpSendMessage (
1602 if (EFI_ERROR (Status
)) {
1606 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1608 // T1 expires, transit to renewing, then send a REQUEST to the server
1610 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1614 Status
= DhcpSendMessage (
1622 if (EFI_ERROR (Status
)) {
1629 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1630 Instance
->Timeout
--;
1631 if (Instance
->Timeout
== 0) {
1632 PxeDhcpDone (Instance
);
1639 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);