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 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 (
321 EFI_UDP4_CONFIG_DATA UdpConfigData
;
322 EFI_IPv4_ADDRESS Subnet
;
323 EFI_IPv4_ADDRESS Gateway
;
324 DHCP_SERVICE
*DhcpSb
;
328 DhcpSb
= (DHCP_SERVICE
*) Context
;
330 UdpConfigData
.AcceptBroadcast
= FALSE
;
331 UdpConfigData
.AcceptPromiscuous
= FALSE
;
332 UdpConfigData
.AcceptAnyPort
= FALSE
;
333 UdpConfigData
.AllowDuplicatePort
= TRUE
;
334 UdpConfigData
.TypeOfService
= 0;
335 UdpConfigData
.TimeToLive
= 64;
336 UdpConfigData
.DoNotFragment
= FALSE
;
337 UdpConfigData
.ReceiveTimeout
= 1;
338 UdpConfigData
.TransmitTimeout
= 0;
340 UdpConfigData
.UseDefaultAddress
= FALSE
;
341 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
342 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
344 Ip
= HTONL (DhcpSb
->ClientAddr
);
345 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
347 Ip
= HTONL (DhcpSb
->Netmask
);
348 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
350 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
352 Status
= UdpIo
->Protocol
.Udp4
->Configure (UdpIo
->Protocol
.Udp4
, &UdpConfigData
);
354 if (EFI_ERROR (Status
)) {
359 // Add a default route if received from the server.
361 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
362 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
364 Ip
= HTONL (DhcpSb
->Para
->Router
);
365 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
367 UdpIo
->Protocol
.Udp4
->Routes (UdpIo
->Protocol
.Udp4
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
375 Update the lease states when a new lease is acquired. It will not only
376 save the acquired the address and lease time, it will also create a UDP
377 child to provide address resolution for the address.
379 @param DhcpSb The DHCP service instance
381 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
382 @retval EFI_SUCCESS The lease is recorded.
387 IN OUT DHCP_SERVICE
*DhcpSb
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 UdpIoFreeIo (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
= UdpIoCreateIo (
416 DhcpConfigLeaseIoPort
,
421 if (DhcpSb
->LeaseIoPort
== NULL
) {
422 return EFI_OUT_OF_RESOURCES
;
425 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
426 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
429 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
434 Clean up the DHCP related states, IoStatus isn't reset.
436 @param DhcpSb The DHCP instance service.
441 IN DHCP_SERVICE
*DhcpSb
444 DhcpSb
->DhcpState
= Dhcp4Init
;
445 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
446 DhcpSb
->ClientAddr
= 0;
448 DhcpSb
->ServerAddr
= 0;
450 if (DhcpSb
->LastOffer
!= NULL
) {
451 FreePool (DhcpSb
->LastOffer
);
452 DhcpSb
->LastOffer
= NULL
;
455 if (DhcpSb
->Selected
!= NULL
) {
456 FreePool (DhcpSb
->Selected
);
457 DhcpSb
->Selected
= NULL
;
460 if (DhcpSb
->Para
!= NULL
) {
461 FreePool (DhcpSb
->Para
);
468 DhcpSb
->ExtraRefresh
= FALSE
;
470 if (DhcpSb
->LeaseIoPort
!= NULL
) {
471 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
472 DhcpSb
->LeaseIoPort
= NULL
;
475 if (DhcpSb
->LastPacket
!= NULL
) {
476 FreePool (DhcpSb
->LastPacket
);
477 DhcpSb
->LastPacket
= NULL
;
480 DhcpSb
->PacketToLive
= 0;
481 DhcpSb
->LastTimeout
= 0;
482 DhcpSb
->CurRetry
= 0;
483 DhcpSb
->MaxRetries
= 0;
484 DhcpSb
->LeaseLife
= 0;
489 Select a offer among all the offers collected. If the offer selected is
490 of BOOTP, the lease is recorded and user notified. If the offer is of
491 DHCP, it will request the offer from the server.
493 @param[in] DhcpSb The DHCP service instance.
495 @retval EFI_SUCCESS One of the offer is selected.
500 IN DHCP_SERVICE
*DhcpSb
503 EFI_DHCP4_PACKET
*Selected
;
504 EFI_DHCP4_PACKET
*NewPacket
;
505 EFI_DHCP4_PACKET
*TempPacket
;
508 ASSERT (DhcpSb
->LastOffer
!= NULL
);
511 // User will cache previous offers if he wants to select
512 // from multiple offers. If user provides an invalid packet,
513 // use the last offer, otherwise use the provided packet.
516 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
518 if (EFI_ERROR (Status
)) {
522 Selected
= DhcpSb
->LastOffer
;
524 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
525 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
526 if (TempPacket
!= NULL
) {
527 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
529 Selected
= TempPacket
;
533 DhcpSb
->Selected
= Selected
;
534 DhcpSb
->LastOffer
= NULL
;
536 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
539 // A bootp offer has been selected, save the lease status,
540 // enter bound state then notify the user.
542 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
543 Status
= DhcpLeaseAcquired (DhcpSb
);
545 if (EFI_ERROR (Status
)) {
549 DhcpSb
->IoStatus
= EFI_SUCCESS
;
550 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
555 // Send a DHCP requests
557 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
559 if (EFI_ERROR (Status
)) {
563 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
568 Terminate the current address acquire. All the allocated resources
569 are released. Be careful when calling this function. A rule related
570 to this is: only call DhcpEndSession at the highest level, such as
571 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
573 @param[in] DhcpSb The DHCP service instance
574 @param[in] Status The result of the DHCP process.
579 IN DHCP_SERVICE
*DhcpSb
,
583 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
584 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
586 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
589 DhcpCleanLease (DhcpSb
);
591 DhcpSb
->IoStatus
= Status
;
592 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
597 Handle packets in DHCP select state.
599 @param[in] DhcpSb The DHCP service instance
600 @param[in] Packet The DHCP packet received
601 @param[in] Para The DHCP parameter extracted from the packet. That
602 is, all the option value that we care.
604 @retval EFI_SUCCESS The packet is successfully processed.
605 @retval Others Some error occured.
610 IN DHCP_SERVICE
*DhcpSb
,
611 IN EFI_DHCP4_PACKET
*Packet
,
612 IN DHCP_PARAMETER
*Para
617 Status
= EFI_SUCCESS
;
620 // First validate the message:
621 // 1. the offer is a unicast
622 // 2. if it is a DHCP message, it must contains a server ID.
623 // Don't return a error for these two case otherwise the session is ended.
625 if (!DHCP_IS_BOOTP (Para
) &&
626 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))
632 // Call the user's callback. The action according to the return is as:
633 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
634 // 2. EFI_NOT_READY: wait for more offers
635 // 3. EFI_ABORTED: abort the address acquiring.
637 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
639 if (Status
== EFI_SUCCESS
) {
640 if (DhcpSb
->LastOffer
!= NULL
) {
641 FreePool (DhcpSb
->LastOffer
);
644 DhcpSb
->LastOffer
= Packet
;
646 return DhcpChooseOffer (DhcpSb
);
648 } else if (Status
== EFI_NOT_READY
) {
649 if (DhcpSb
->LastOffer
!= NULL
) {
650 FreePool (DhcpSb
->LastOffer
);
653 DhcpSb
->LastOffer
= Packet
;
655 } else if (Status
== EFI_ABORTED
) {
657 // DhcpInput will end the session upon error return. Remember
658 // only to call DhcpEndSession at the top level call.
672 Handle packets in DHCP request state.
674 @param[in] DhcpSb The DHCP service instance
675 @param[in] Packet The DHCP packet received
676 @param[in] Para The DHCP parameter extracted from the packet. That
677 is, all the option value that we care.
679 @retval EFI_SUCCESS The packet is successfully processed.
680 @retval Others Some error occured.
685 IN DHCP_SERVICE
*DhcpSb
,
686 IN EFI_DHCP4_PACKET
*Packet
,
687 IN DHCP_PARAMETER
*Para
690 EFI_DHCP4_HEADER
*Head
;
691 EFI_DHCP4_HEADER
*Selected
;
695 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
697 Head
= &Packet
->Dhcp4
.Header
;
698 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
701 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
703 if (DHCP_IS_BOOTP (Para
) ||
704 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
705 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
708 Status
= EFI_SUCCESS
;
713 // Received a NAK, end the session no matter what the user returns
715 Status
= EFI_DEVICE_ERROR
;
717 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
718 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
723 // Check whether the ACK matches the selected offer
727 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
728 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
732 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
734 if (EFI_ERROR (Status
)) {
735 Message
= (UINT8
*) "Lease is denied upon received ACK";
740 // Record the lease, transit to BOUND state, then notify the user
742 Status
= DhcpLeaseAcquired (DhcpSb
);
744 if (EFI_ERROR (Status
)) {
745 Message
= (UINT8
*) "Lease is denied upon entering bound";
749 DhcpSb
->IoStatus
= EFI_SUCCESS
;
750 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
756 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
765 Handle packets in DHCP renew/rebound state.
767 @param[in] DhcpSb The DHCP service instance
768 @param[in] Packet The DHCP packet received
769 @param[in] Para The DHCP parameter extracted from the packet. That
770 is, all the option value that we care.
772 @retval EFI_SUCCESS The packet is successfully processed.
773 @retval Others Some error occured.
777 DhcpHandleRenewRebind (
778 IN DHCP_SERVICE
*DhcpSb
,
779 IN EFI_DHCP4_PACKET
*Packet
,
780 IN DHCP_PARAMETER
*Para
783 EFI_DHCP4_HEADER
*Head
;
784 EFI_DHCP4_HEADER
*Selected
;
787 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
789 Head
= &Packet
->Dhcp4
.Header
;
790 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
793 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
795 if (DHCP_IS_BOOTP (Para
) ||
796 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
797 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
800 Status
= EFI_SUCCESS
;
805 // Received a NAK, ignore the user's return then terminate the process
807 Status
= EFI_DEVICE_ERROR
;
809 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
810 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
815 // The lease is different from the selected. Don't send a DECLINE
816 // since it isn't existed in the client's FSM.
818 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
822 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
824 if (EFI_ERROR (Status
)) {
829 // Record the lease, start timer for T1 and T2,
831 DhcpComputeLease (DhcpSb
, Para
);
832 DhcpSb
->LeaseLife
= 0;
833 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
835 if (DhcpSb
->ExtraRefresh
!= 0) {
836 DhcpSb
->ExtraRefresh
= FALSE
;
838 DhcpSb
->IoStatus
= EFI_SUCCESS
;
839 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
849 Handle packets in DHCP reboot state.
851 @param[in] DhcpSb The DHCP service instance
852 @param[in] Packet The DHCP packet received
853 @param[in] Para The DHCP parameter extracted from the packet. That
854 is, all the option value that we care.
856 @retval EFI_SUCCESS The packet is successfully processed.
857 @retval Others Some error occured.
862 IN DHCP_SERVICE
*DhcpSb
,
863 IN EFI_DHCP4_PACKET
*Packet
,
864 IN DHCP_PARAMETER
*Para
867 EFI_DHCP4_HEADER
*Head
;
870 Head
= &Packet
->Dhcp4
.Header
;
873 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
875 if (DHCP_IS_BOOTP (Para
) ||
876 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
879 Status
= EFI_SUCCESS
;
884 // If a NAK is received, transit to INIT and try again.
886 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
887 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
889 DhcpSb
->ClientAddr
= 0;
890 DhcpSb
->DhcpState
= Dhcp4Init
;
892 Status
= DhcpInitRequest (DhcpSb
);
897 // Check whether the ACK matches the selected offer
899 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
900 Status
= EFI_DEVICE_ERROR
;
904 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
905 if (EFI_ERROR (Status
)) {
910 // OK, get the parameter from server, record the lease
912 DhcpSb
->Para
= AllocateCopyPool (sizeof (DHCP_PARAMETER
), Para
);
913 if (DhcpSb
->Para
== NULL
) {
914 Status
= EFI_OUT_OF_RESOURCES
;
918 DhcpSb
->Selected
= Packet
;
919 Status
= DhcpLeaseAcquired (DhcpSb
);
920 if (EFI_ERROR (Status
)) {
924 DhcpSb
->IoStatus
= EFI_SUCCESS
;
925 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
935 Handle the received DHCP packets. This function drives the DHCP
938 @param UdpPacket The UDP packets received.
939 @param EndPoint The local/remote UDP access point
940 @param IoStatus The status of the UDP receive
941 @param Context The opaque parameter to the function.
947 UDP_END_POINT
*EndPoint
,
952 DHCP_SERVICE
*DhcpSb
;
953 EFI_DHCP4_HEADER
*Head
;
954 EFI_DHCP4_PACKET
*Packet
;
955 DHCP_PARAMETER
*Para
;
960 DhcpSb
= (DHCP_SERVICE
*) Context
;
963 // Don't restart receive if error occurs or DHCP is destoried.
965 if (EFI_ERROR (IoStatus
)) {
967 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
968 NetbufFree (UdpPacket
);
972 ASSERT (UdpPacket
!= NULL
);
974 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
979 // Validate the packet received
981 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
986 // Copy the DHCP message to a continuous memory block
988 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
989 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
991 if (Packet
== NULL
) {
996 Head
= &Packet
->Dhcp4
.Header
;
997 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
999 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1004 // Is this packet the answer to our packet?
1006 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1007 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1008 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1013 // Validate the options and retrieve the interested options
1016 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1017 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1018 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1024 // Call the handler for each state. The handler should return
1025 // EFI_SUCCESS if the process can go on no matter whether the
1026 // packet is ignored or not. If the return is EFI_ERROR, the
1027 // session will be terminated. Packet's ownership is handled
1028 // over to the handlers. If operation succeeds, the handler
1029 // must notify the user. It isn't necessary to do if EFI_ERROR
1030 // is returned because the DhcpEndSession will notify the user.
1032 Status
= EFI_SUCCESS
;
1034 switch (DhcpSb
->DhcpState
) {
1035 case Dhcp4Selecting
:
1036 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1039 case Dhcp4Requesting
:
1040 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1043 case Dhcp4InitReboot
:
1047 // Ignore the packet in INITREBOOT, INIT and BOUND states
1050 Status
= EFI_SUCCESS
;
1054 case Dhcp4Rebinding
:
1055 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1058 case Dhcp4Rebooting
:
1059 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1069 if (EFI_ERROR (Status
)) {
1070 NetbufFree (UdpPacket
);
1071 UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1072 DhcpEndSession (DhcpSb
, Status
);
1077 NetbufFree (UdpPacket
);
1079 if (Packet
!= NULL
) {
1083 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1085 if (EFI_ERROR (Status
)) {
1086 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1094 @param[in] Arg The packet to release
1107 Release the net buffer when packet is sent.
1109 @param UdpPacket The UDP packets received.
1110 @param EndPoint The local/remote UDP access point
1111 @param IoStatus The status of the UDP receive
1112 @param Context The opaque parameter to the function.
1118 UDP_END_POINT
*EndPoint
,
1119 EFI_STATUS IoStatus
,
1123 NetbufFree (Packet
);
1129 Build and transmit a DHCP message according to the current states.
1130 This function implement the Table 5. of RFC 2131. Always transits
1131 the state (as defined in Figure 5. of the same RFC) before sending
1132 a DHCP message. The table is adjusted accordingly.
1134 @param[in] DhcpSb The DHCP service instance
1135 @param[in] Seed The seed packet which the new packet is based on
1136 @param[in] Para The DHCP parameter of the Seed packet
1137 @param[in] Type The message type to send
1138 @param[in] Msg The human readable message to include in the packet
1141 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1142 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1143 @retval EFI_SUCCESS The message is sent
1144 @retval other Other error occurs
1149 IN DHCP_SERVICE
*DhcpSb
,
1150 IN EFI_DHCP4_PACKET
*Seed
,
1151 IN DHCP_PARAMETER
*Para
,
1156 EFI_DHCP4_CONFIG_DATA
*Config
;
1157 EFI_DHCP4_PACKET
*Packet
;
1158 EFI_DHCP4_PACKET
*NewPacket
;
1159 EFI_DHCP4_HEADER
*Head
;
1160 EFI_DHCP4_HEADER
*SeedHead
;
1162 UDP_END_POINT EndPoint
;
1173 // Allocate a big enough memory block to hold the DHCP packet
1175 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1178 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1181 Packet
= AllocatePool (Len
);
1183 if (Packet
== NULL
) {
1184 return EFI_OUT_OF_RESOURCES
;
1188 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1191 // Fill in the DHCP header fields
1193 Config
= &DhcpSb
->ActiveConfig
;
1197 SeedHead
= &Seed
->Dhcp4
.Header
;
1200 Head
= &Packet
->Dhcp4
.Header
;
1201 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1203 Head
->OpCode
= BOOTP_REQUEST
;
1204 Head
->HwType
= DhcpSb
->HwType
;
1205 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1206 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1207 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1209 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1210 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1213 // Append the DHCP message type
1215 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1216 Buf
= Packet
->Dhcp4
.Option
;
1217 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1220 // Append the serverid option if necessary:
1221 // 1. DHCP decline message
1222 // 2. DHCP release message
1223 // 3. DHCP request to confirm one lease.
1225 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1226 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))
1229 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1231 IpAddr
= HTONL (Para
->ServerId
);
1232 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1236 // Append the requested IP option if necessary:
1237 // 1. DHCP request to use the previously allocated address
1238 // 2. DHCP request to confirm one lease
1239 // 3. DHCP decline to decline one lease
1243 if (Type
== DHCP_MSG_REQUEST
) {
1244 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1245 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1247 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1248 ASSERT (SeedHead
!= NULL
);
1249 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1252 } else if (Type
== DHCP_MSG_DECLINE
) {
1253 ASSERT (SeedHead
!= NULL
);
1254 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1258 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1262 // Append the Max Message Length option if it isn't a DECLINE
1263 // or RELEASE to direct the server use large messages instead of
1264 // override the BOOTFILE and SERVER fields in the message head.
1266 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1267 MaxMsg
= HTONS (0xFF00);
1268 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1272 // Append the user's message if it isn't NULL
1275 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1276 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1280 // Append the user configured options
1282 if (DhcpSb
->UserOptionLen
!= 0) {
1283 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1285 // We can't use any option other than the client ID from user
1286 // if it is a DHCP decline or DHCP release .
1288 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1289 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1293 Buf
= DhcpAppendOption (
1295 Config
->OptionList
[Index
]->OpCode
,
1296 Config
->OptionList
[Index
]->Length
,
1297 Config
->OptionList
[Index
]->Data
1302 *(Buf
++) = DHCP_TAG_EOP
;
1303 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1306 // OK, the message is built, call the user to override it.
1308 Status
= EFI_SUCCESS
;
1311 if (Type
== DHCP_MSG_DISCOVER
) {
1312 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1314 } else if (Type
== DHCP_MSG_REQUEST
) {
1315 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1317 } else if (Type
== DHCP_MSG_DECLINE
) {
1318 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1321 if (EFI_ERROR (Status
)) {
1326 if (NewPacket
!= NULL
) {
1332 // Save the Client Address will be sent out
1335 &DhcpSb
->ClientAddressSendOut
[0],
1336 &Packet
->Dhcp4
.Header
.ClientHwAddr
[0],
1337 Packet
->Dhcp4
.Header
.HwAddrLen
1342 // Wrap it into a netbuf then send it.
1344 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1345 Frag
.Len
= Packet
->Length
;
1346 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1350 return EFI_OUT_OF_RESOURCES
;
1354 // Save it as the last sent packet for retransmission
1356 if (DhcpSb
->LastPacket
!= NULL
) {
1357 FreePool (DhcpSb
->LastPacket
);
1360 DhcpSb
->LastPacket
= Packet
;
1361 DhcpSetTransmitTimer (DhcpSb
);
1364 // Broadcast the message, unless we know the server address.
1365 // Use the lease UdpIo port to send the unicast packet.
1367 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1368 EndPoint
.LocalAddr
.Addr
[0] = 0;
1369 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1370 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1371 UdpIo
= DhcpSb
->UdpIo
;
1373 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1374 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1375 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1376 UdpIo
= DhcpSb
->LeaseIoPort
;
1379 ASSERT (UdpIo
!= NULL
);
1382 Status
= UdpIoSendDatagram (
1391 if (EFI_ERROR (Status
)) {
1393 return EFI_ACCESS_DENIED
;
1401 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1402 will be retransmitted.
1404 @param[in] DhcpSb The DHCP service instance
1406 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1407 @retval EFI_SUCCESS The packet is retransmitted.
1412 IN DHCP_SERVICE
*DhcpSb
1416 UDP_END_POINT EndPoint
;
1421 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1423 DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
= HTONS (*(UINT16
*)(&DhcpSb
->LastTimeout
));
1426 // Wrap it into a netbuf then send it.
1428 Frag
.Bulk
= (UINT8
*) &DhcpSb
->LastPacket
->Dhcp4
.Header
;
1429 Frag
.Len
= DhcpSb
->LastPacket
->Length
;
1430 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, DhcpSb
->LastPacket
);
1433 return EFI_OUT_OF_RESOURCES
;
1437 // Broadcast the message, unless we know the server address.
1439 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1440 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1441 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1442 EndPoint
.LocalAddr
.Addr
[0] = 0;
1443 UdpIo
= DhcpSb
->UdpIo
;
1445 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1446 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1447 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1448 UdpIo
= DhcpSb
->LeaseIoPort
;
1451 ASSERT (UdpIo
!= NULL
);
1454 Status
= UdpIoSendDatagram (
1463 if (EFI_ERROR (Status
)) {
1465 return EFI_ACCESS_DENIED
;
1473 Each DHCP service has three timer. Two of them are count down timer.
1474 One for the packet retransmission. The other is to collect the offers.
1475 The third timer increaments the lease life which is compared to T1, T2,
1476 and lease to determine the time to renew and rebind the lease.
1477 DhcpOnTimerTick will be called once every second.
1479 @param[in] Event The timer event
1480 @param[in] Context The context, which is the DHCP service instance.
1490 DHCP_SERVICE
*DhcpSb
;
1491 DHCP_PROTOCOL
*Instance
;
1494 DhcpSb
= (DHCP_SERVICE
*) Context
;
1495 Instance
= DhcpSb
->ActiveChild
;
1498 // Check the retransmit timer
1500 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1503 // Select offer at each timeout if any offer received.
1505 if (DhcpSb
->DhcpState
== Dhcp4Selecting
&& DhcpSb
->LastOffer
!= NULL
) {
1507 Status
= DhcpChooseOffer (DhcpSb
);
1509 if (EFI_ERROR(Status
)) {
1510 FreePool (DhcpSb
->LastOffer
);
1511 DhcpSb
->LastOffer
= NULL
;
1517 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1519 // Still has another try
1521 DhcpRetransmit (DhcpSb
);
1522 DhcpSetTransmitTimer (DhcpSb
);
1524 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1527 // Retransmission failed, if the DHCP request is initiated by
1528 // user, adjust the current state according to the lease life.
1529 // Otherwise do nothing to wait the lease to timeout
1531 if (DhcpSb
->ExtraRefresh
!= 0) {
1532 Status
= EFI_SUCCESS
;
1534 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1535 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1537 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1538 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1540 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1541 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1548 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1549 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1557 // If an address has been acquired, check whether need to
1558 // refresh or whether it has expired.
1560 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1561 DhcpSb
->LeaseLife
++;
1564 // Don't timeout the lease, only count the life if user is
1565 // requesting extra renew/rebind. Adjust the state after that.
1567 if (DhcpSb
->ExtraRefresh
!= 0) {
1571 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1573 // Lease expires, end the session
1577 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1579 // T2 expires, transit to rebinding then send a REQUEST to any server
1581 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1585 Status
= DhcpSendMessage (
1593 if (EFI_ERROR (Status
)) {
1597 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1599 // T1 expires, transit to renewing, then send a REQUEST to the server
1601 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1605 Status
= DhcpSendMessage (
1613 if (EFI_ERROR (Status
)) {
1620 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1621 Instance
->Timeout
--;
1622 if (Instance
->Timeout
== 0) {
1623 PxeDhcpDone (Instance
);
1630 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);