3 Copyright (c) 2006 - 2008, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 EFI DHCP protocol implementation
24 #include "Dhcp4Impl.h"
26 UINT32 mDhcp4DefaultTimeout
[4] = { 4, 8, 16, 32 };
30 Send an initial DISCOVER or REQUEST message according to the
31 DHCP service's current state.
33 @param DhcpSb The DHCP service instance
35 @retval EFI_SUCCESS The request has been sent
40 IN DHCP_SERVICE
*DhcpSb
45 ASSERT ((DhcpSb
->DhcpState
== Dhcp4Init
) || (DhcpSb
->DhcpState
== Dhcp4InitReboot
));
47 if (DhcpSb
->DhcpState
== Dhcp4Init
) {
48 DhcpSetState (DhcpSb
, Dhcp4Selecting
, FALSE
);
49 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_DISCOVER
, NULL
);
51 if (EFI_ERROR (Status
)) {
52 DhcpSb
->DhcpState
= Dhcp4Init
;
56 DhcpSetState (DhcpSb
, Dhcp4Rebooting
, FALSE
);
57 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_REQUEST
, NULL
);
59 if (EFI_ERROR (Status
)) {
60 DhcpSb
->DhcpState
= Dhcp4InitReboot
;
70 Call user provided callback function, and return the value the
71 function returns. If the user doesn't provide a callback, a
72 proper return value is selected to let the caller continue the
75 @param DhcpSb The DHCP service instance
76 @param Event The event as defined in the spec
77 @param Packet The current packet trigger the event
78 @param NewPacket The user's return new packet
80 @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
81 @retval EFI_SUCCESS The user function returns success.
82 @retval EFI_ABORTED The user function ask it to abort.
87 IN DHCP_SERVICE
*DhcpSb
,
88 IN EFI_DHCP4_EVENT Event
,
89 IN EFI_DHCP4_PACKET
*Packet
, OPTIONAL
90 OUT EFI_DHCP4_PACKET
**NewPacket OPTIONAL
93 EFI_DHCP4_CONFIG_DATA
*Config
;
96 if (NewPacket
!= NULL
) {
101 // If user doesn't provide the call back function, return the value
102 // that directs the client to continue the normal process.
103 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
104 // the offers and select a offer, EFI_NOT_READY tells the client to
105 // collect more offers.
107 Config
= &DhcpSb
->ActiveConfig
;
109 if (Config
->Dhcp4Callback
== NULL
) {
110 if (Event
== Dhcp4RcvdOffer
) {
111 return EFI_NOT_READY
;
117 Status
= Config
->Dhcp4Callback (
118 &DhcpSb
->ActiveChild
->Dhcp4Protocol
,
119 Config
->CallbackContext
,
120 (EFI_DHCP4_STATE
) DhcpSb
->DhcpState
,
127 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
128 // and EFI_ABORTED. If it returns values other than those, assume
129 // it to be EFI_ABORTED.
131 if ((Status
== EFI_SUCCESS
) || (Status
== EFI_NOT_READY
)) {
140 Notify the user about the operation result.
142 @param DhcpSb DHCP service instance
143 @param Which which notify function to signal
150 IN DHCP_SERVICE
*DhcpSb
,
154 DHCP_PROTOCOL
*Child
;
156 if ((Child
= DhcpSb
->ActiveChild
) == NULL
) {
160 if ((Child
->CompletionEvent
!= NULL
) &&
161 ((Which
== DHCP_NOTIFY_COMPLETION
) || (Which
== DHCP_NOTIFY_ALL
))) {
163 gBS
->SignalEvent (Child
->CompletionEvent
);
164 Child
->CompletionEvent
= NULL
;
167 if ((Child
->RenewRebindEvent
!= NULL
) &&
168 ((Which
== DHCP_NOTIFY_RENEWREBIND
) || (Which
== DHCP_NOTIFY_ALL
))) {
170 gBS
->SignalEvent (Child
->RenewRebindEvent
);
171 Child
->RenewRebindEvent
= NULL
;
178 Set the DHCP state. If CallUser is true, it will try to notify
179 the user before change the state by DhcpNotifyUser. It returns
180 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
181 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
182 the return value of this function.
184 @param DhcpSb The DHCP service instance
185 @param State The new DHCP state to change to
186 @param CallUser Whether we need to call user
188 @retval EFI_SUCCESS The state is changed
189 @retval EFI_ABORTED The user asks to abort the DHCP process.
194 IN DHCP_SERVICE
*DhcpSb
,
202 Status
= EFI_SUCCESS
;
204 if (State
== Dhcp4Renewing
) {
205 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRenewing
, NULL
, NULL
);
207 } else if (State
== Dhcp4Rebinding
) {
208 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRebinding
, NULL
, NULL
);
210 } else if (State
== Dhcp4Bound
) {
211 Status
= DhcpCallUser (DhcpSb
, Dhcp4BoundCompleted
, NULL
, NULL
);
215 if (EFI_ERROR (Status
)) {
221 // Update the retransmission timer during the state transition.
222 // This will clear the retry count. This is also why the rule
223 // first transit the state, then send packets.
225 if (State
== Dhcp4Selecting
) {
226 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.DiscoverTryCount
;
228 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.RequestTryCount
;
231 if (DhcpSb
->MaxRetries
== 0) {
232 DhcpSb
->MaxRetries
= 4;
235 DhcpSb
->CurRetry
= 0;
236 DhcpSb
->PacketToLive
= 0;
238 DhcpSb
->DhcpState
= State
;
244 Set the retransmit timer for the packet. It will select from either
245 the discover timeouts/request timeouts or the default timeout values.
247 @param DhcpSb The DHCP service instance.
253 DhcpSetTransmitTimer (
254 IN DHCP_SERVICE
*DhcpSb
259 ASSERT (DhcpSb
->MaxRetries
> DhcpSb
->CurRetry
);
261 if (DhcpSb
->DhcpState
== Dhcp4Selecting
) {
262 Times
= DhcpSb
->ActiveConfig
.DiscoverTimeout
;
264 Times
= DhcpSb
->ActiveConfig
.RequestTimeout
;
268 Times
= mDhcp4DefaultTimeout
;
271 DhcpSb
->PacketToLive
= Times
[DhcpSb
->CurRetry
];
277 Compute the lease. If the server grants a permanent lease, just
278 process it as a normal timeout value since the lease will last
281 @param DhcpSb The DHCP service instance
282 @param Para The DHCP parameter extracted from the server's
290 IN DHCP_SERVICE
*DhcpSb
,
291 IN DHCP_PARAMETER
*Para
294 ASSERT (Para
!= NULL
);
296 DhcpSb
->Lease
= Para
->Lease
;
297 DhcpSb
->T2
= Para
->T2
;
298 DhcpSb
->T1
= Para
->T1
;
300 if (DhcpSb
->Lease
== 0) {
301 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
304 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
305 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
308 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
309 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
315 Configure a UDP IO port to use the acquired lease address.
316 DHCP driver needs this port to unicast packet to the server
317 such as DHCP release.
319 @param UdpIo The UDP IO port to configure
320 @param Context The opaque parameter to the function.
322 @retval EFI_SUCCESS The UDP IO port is successfully configured.
323 @retval Others It failed to configure the port.
327 DhcpConfigLeaseIoPort (
328 IN UDP_IO_PORT
*UdpIo
,
332 EFI_UDP4_CONFIG_DATA UdpConfigData
;
333 EFI_IPv4_ADDRESS Subnet
;
334 EFI_IPv4_ADDRESS Gateway
;
335 DHCP_SERVICE
*DhcpSb
;
339 DhcpSb
= (DHCP_SERVICE
*) Context
;
341 UdpConfigData
.AcceptBroadcast
= FALSE
;
342 UdpConfigData
.AcceptPromiscuous
= FALSE
;
343 UdpConfigData
.AcceptAnyPort
= FALSE
;
344 UdpConfigData
.AllowDuplicatePort
= TRUE
;
345 UdpConfigData
.TypeOfService
= 0;
346 UdpConfigData
.TimeToLive
= 64;
347 UdpConfigData
.DoNotFragment
= FALSE
;
348 UdpConfigData
.ReceiveTimeout
= 1;
349 UdpConfigData
.TransmitTimeout
= 0;
351 UdpConfigData
.UseDefaultAddress
= FALSE
;
352 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
353 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
355 Ip
= HTONL (DhcpSb
->ClientAddr
);
356 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
358 Ip
= HTONL (DhcpSb
->Netmask
);
359 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
361 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
363 Status
= UdpIo
->Udp
->Configure (UdpIo
->Udp
, &UdpConfigData
);
365 if (EFI_ERROR (Status
)) {
370 // Add a default route if received from the server.
372 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
373 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
375 Ip
= HTONL (DhcpSb
->Para
->Router
);
376 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
378 UdpIo
->Udp
->Routes (UdpIo
->Udp
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
386 Update the lease states when a new lease is acquired. It will not only
387 save the acquired the address and lease time, it will also create a UDP
388 child to provide address resolution for the address.
390 @param DhcpSb The DHCP service instance
392 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
393 @retval EFI_SUCCESS The lease is recorded.
398 IN DHCP_SERVICE
*DhcpSb
403 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
405 if (DhcpSb
->Para
!= NULL
) {
406 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
407 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
410 if (DhcpSb
->Netmask
== 0) {
411 Class
= NetGetIpClass (DhcpSb
->ClientAddr
);
412 DhcpSb
->Netmask
= gIp4AllMasks
[Class
<< 3];
415 if (DhcpSb
->LeaseIoPort
!= NULL
) {
416 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
420 // Create a UDP/IP child to provide ARP service for the Leased IP,
421 // and transmit unicast packet with it as source address. Don't
422 // start receive on this port, the queued packet will be timeout.
424 DhcpSb
->LeaseIoPort
= UdpIoCreatePort (
427 DhcpConfigLeaseIoPort
,
431 if (DhcpSb
->LeaseIoPort
== NULL
) {
432 return EFI_OUT_OF_RESOURCES
;
435 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
436 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
439 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
444 Clean up the DHCP related states, IoStatus isn't reset.
446 @param DhcpSb The DHCP instance service.
453 IN DHCP_SERVICE
*DhcpSb
456 DhcpSb
->DhcpState
= Dhcp4Init
;
457 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
458 DhcpSb
->ClientAddr
= 0;
459 DhcpSb
->ServerAddr
= 0;
461 if (DhcpSb
->LastOffer
!= NULL
) {
462 gBS
->FreePool (DhcpSb
->LastOffer
);
463 DhcpSb
->LastOffer
= NULL
;
466 if (DhcpSb
->Selected
!= NULL
) {
467 gBS
->FreePool (DhcpSb
->Selected
);
468 DhcpSb
->Selected
= NULL
;
471 if (DhcpSb
->Para
!= NULL
) {
472 gBS
->FreePool (DhcpSb
->Para
);
479 DhcpSb
->ExtraRefresh
= FALSE
;
481 if (DhcpSb
->LeaseIoPort
!= NULL
) {
482 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
483 DhcpSb
->LeaseIoPort
= NULL
;
486 if (DhcpSb
->LastPacket
!= NULL
) {
487 NetbufFree (DhcpSb
->LastPacket
);
488 DhcpSb
->LastPacket
= NULL
;
491 DhcpSb
->PacketToLive
= 0;
492 DhcpSb
->CurRetry
= 0;
493 DhcpSb
->MaxRetries
= 0;
494 DhcpSb
->LeaseLife
= 0;
499 Select a offer among all the offers collected. If the offer selected is
500 of BOOTP, the lease is recorded and user notified. If the offer is of
501 DHCP, it will request the offer from the server.
503 @param DhcpSb The DHCP service instance.
505 @retval EFI_SUCCESS One of the offer is selected.
510 IN DHCP_SERVICE
*DhcpSb
513 EFI_DHCP4_PACKET
*Selected
;
514 EFI_DHCP4_PACKET
*NewPacket
;
515 EFI_DHCP4_PACKET
*TempPacket
;
518 ASSERT (DhcpSb
->LastOffer
!= NULL
);
521 // User will cache previous offers if he wants to select
522 // from multiple offers. If user provides an invalid packet,
523 // use the last offer, otherwise use the provided packet.
526 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
528 if (EFI_ERROR (Status
)) {
532 Selected
= DhcpSb
->LastOffer
;
534 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
535 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
536 if (TempPacket
!= NULL
) {
537 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
538 gBS
->FreePool (Selected
);
539 Selected
= TempPacket
;
543 DhcpSb
->Selected
= Selected
;
544 DhcpSb
->LastOffer
= NULL
;
546 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
549 // A bootp offer has been selected, save the lease status,
550 // enter bound state then notify the user.
552 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
553 Status
= DhcpLeaseAcquired (DhcpSb
);
555 if (EFI_ERROR (Status
)) {
559 DhcpSb
->IoStatus
= EFI_SUCCESS
;
560 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
565 // Send a DHCP requests
567 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
569 if (EFI_ERROR (Status
)) {
573 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
578 Terminate the current address acquire. All the allocated resources
579 are released. Be careful when calling this function. A rule related
580 to this is: only call DhcpEndSession at the highest level, such as
581 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
583 @param DhcpSb The DHCP service instance
584 @param Status The result of the DHCP process.
591 IN DHCP_SERVICE
*DhcpSb
,
595 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
596 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
598 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
601 DhcpCleanLease (DhcpSb
);
603 DhcpSb
->IoStatus
= Status
;
604 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
609 Handle packets in DHCP select state.
611 @param DhcpSb The DHCP service instance
612 @param Packet The DHCP packet received
613 @param Para The DHCP parameter extracted from the packet. That
614 is, all the option value that we care.
616 @retval EFI_SUCCESS The packet is successfully processed.
617 @retval Others Some error occured.
622 IN DHCP_SERVICE
*DhcpSb
,
623 IN EFI_DHCP4_PACKET
*Packet
,
624 IN DHCP_PARAMETER
*Para
629 Status
= EFI_SUCCESS
;
632 // First validate the message:
633 // 1. the offer is a unicast
634 // 2. if it is a DHCP message, it must contains a server ID.
635 // Don't return a error for these two case otherwise the session is ended.
637 if (!DHCP_IS_BOOTP (Para
) &&
638 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))) {
643 // Call the user's callback. The action according to the return is as:
644 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
645 // 2. EFI_NOT_READY: wait for more offers
646 // 3. EFI_ABORTED: abort the address acquiring.
648 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
650 if (Status
== EFI_SUCCESS
) {
651 if (DhcpSb
->LastOffer
!= NULL
) {
652 gBS
->FreePool (DhcpSb
->LastOffer
);
655 DhcpSb
->LastOffer
= Packet
;
657 return DhcpChooseOffer (DhcpSb
);
659 } else if (Status
== EFI_NOT_READY
) {
660 if (DhcpSb
->LastOffer
!= NULL
) {
661 gBS
->FreePool (DhcpSb
->LastOffer
);
664 DhcpSb
->LastOffer
= Packet
;
666 } else if (Status
== EFI_ABORTED
) {
668 // DhcpInput will end the session upon error return. Remember
669 // only to call DhcpEndSession at the top level call.
677 gBS
->FreePool (Packet
);
683 Handle packets in DHCP request state.
685 @param DhcpSb The DHCP service instance
686 @param Packet The DHCP packet received
687 @param Para The DHCP parameter extracted from the packet. That
688 is, all the option value that we care.
690 @retval EFI_SUCCESS The packet is successfully processed.
691 @retval Others Some error occured.
696 IN DHCP_SERVICE
*DhcpSb
,
697 IN EFI_DHCP4_PACKET
*Packet
,
698 IN DHCP_PARAMETER
*Para
701 EFI_DHCP4_HEADER
*Head
;
702 EFI_DHCP4_HEADER
*Selected
;
706 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
708 Head
= &Packet
->Dhcp4
.Header
;
709 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
712 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
714 if (DHCP_IS_BOOTP (Para
) ||
715 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
716 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
718 Status
= EFI_SUCCESS
;
723 // Received a NAK, end the session no matter what the user returns
725 Status
= EFI_DEVICE_ERROR
;
727 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
728 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
733 // Check whether the ACK matches the selected offer
737 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
738 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
742 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
744 if (EFI_ERROR (Status
)) {
745 Message
= (UINT8
*) "Lease is denied upon received ACK";
750 // Record the lease, transit to BOUND state, then notify the user
752 Status
= DhcpLeaseAcquired (DhcpSb
);
754 if (EFI_ERROR (Status
)) {
755 Message
= (UINT8
*) "Lease is denied upon entering bound";
759 DhcpSb
->IoStatus
= EFI_SUCCESS
;
760 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
762 gBS
->FreePool (Packet
);
766 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
769 gBS
->FreePool (Packet
);
775 Handle packets in DHCP renew/rebound state.
777 @param DhcpSb The DHCP service instance
778 @param Packet The DHCP packet received
779 @param Para The DHCP parameter extracted from the packet. That
780 is, all the option value that we care.
782 @retval EFI_SUCCESS The packet is successfully processed.
783 @retval Others Some error occured.
787 DhcpHandleRenewRebind (
788 IN DHCP_SERVICE
*DhcpSb
,
789 IN EFI_DHCP4_PACKET
*Packet
,
790 IN DHCP_PARAMETER
*Para
793 EFI_DHCP4_HEADER
*Head
;
794 EFI_DHCP4_HEADER
*Selected
;
797 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
799 Head
= &Packet
->Dhcp4
.Header
;
800 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
803 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
805 if (DHCP_IS_BOOTP (Para
) ||
806 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
807 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
809 Status
= EFI_SUCCESS
;
814 // Received a NAK, ignore the user's return then terminate the process
816 Status
= EFI_DEVICE_ERROR
;
818 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
819 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
824 // The lease is different from the selected. Don't send a DECLINE
825 // since it isn't existed in the client's FSM.
827 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
831 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
833 if (EFI_ERROR (Status
)) {
838 // Record the lease, start timer for T1 and T2,
840 DhcpComputeLease (DhcpSb
, Para
);
841 DhcpSb
->LeaseLife
= 0;
842 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
844 if (DhcpSb
->ExtraRefresh
!= 0) {
845 DhcpSb
->ExtraRefresh
= FALSE
;
847 DhcpSb
->IoStatus
= EFI_SUCCESS
;
848 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
852 gBS
->FreePool (Packet
);
858 Handle packets in DHCP reboot state.
860 @param DhcpSb The DHCP service instance
861 @param Packet The DHCP packet received
862 @param Para The DHCP parameter extracted from the packet. That
863 is, all the option value that we care.
865 @retval EFI_SUCCESS The packet is successfully processed.
866 @retval Others Some error occured.
871 IN DHCP_SERVICE
*DhcpSb
,
872 IN EFI_DHCP4_PACKET
*Packet
,
873 IN DHCP_PARAMETER
*Para
876 EFI_DHCP4_HEADER
*Head
;
879 Head
= &Packet
->Dhcp4
.Header
;
882 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
884 if (DHCP_IS_BOOTP (Para
) ||
885 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
887 Status
= EFI_SUCCESS
;
892 // If a NAK is received, transit to INIT and try again.
894 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
895 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
897 DhcpSb
->ClientAddr
= 0;
898 DhcpSb
->DhcpState
= Dhcp4Init
;
900 Status
= DhcpInitRequest (DhcpSb
);
905 // Check whether the ACK matches the selected offer
907 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
908 Status
= EFI_DEVICE_ERROR
;
912 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
913 if (EFI_ERROR (Status
)) {
918 // OK, get the parameter from server, record the lease
920 DhcpSb
->Para
= AllocatePool (sizeof (DHCP_PARAMETER
));
922 if (DhcpSb
->Para
== NULL
) {
923 Status
= EFI_OUT_OF_RESOURCES
;
927 DhcpSb
->Selected
= Packet
;
928 CopyMem (DhcpSb
->Para
, Para
, sizeof (*DhcpSb
->Para
));
930 Status
= DhcpLeaseAcquired (DhcpSb
);
932 if (EFI_ERROR (Status
)) {
936 DhcpSb
->IoStatus
= EFI_SUCCESS
;
937 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
941 gBS
->FreePool (Packet
);
947 Handle the received DHCP packets. This function drivers the DHCP
950 @param UdpPacket The UDP packets received.
951 @param Points The local/remote UDP access points
952 @param IoStatus The status of the UDP receive
953 @param Context The opaque parameter to the function.
966 DHCP_SERVICE
*DhcpSb
;
967 EFI_DHCP4_HEADER
*Head
;
968 EFI_DHCP4_PACKET
*Packet
;
969 DHCP_PARAMETER
*Para
;
974 DhcpSb
= (DHCP_SERVICE
*) Context
;
977 // Don't restart receive if error occurs or DHCP is destoried.
979 if (EFI_ERROR (IoStatus
)) {
981 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
982 NetbufFree (UdpPacket
);
986 ASSERT (UdpPacket
!= NULL
);
988 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
993 // Validate the packet received
995 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
1000 // Copy the DHCP message to a continuous memory block
1002 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
1003 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
1005 if (Packet
== NULL
) {
1010 Head
= &Packet
->Dhcp4
.Header
;
1011 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1013 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1018 // Is this packet the answer to our packet?
1020 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1021 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1022 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1027 // Validate the options and retrieve the interested options
1030 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1031 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1032 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1038 // Call the handler for each state. The handler should return
1039 // EFI_SUCCESS if the process can go on no matter whether the
1040 // packet is ignored or not. If the return is EFI_ERROR, the
1041 // session will be terminated. Packet's ownership is handled
1042 // over to the handlers. If operation succeeds, the handler
1043 // must notify the user. It isn't necessary to do if EFI_ERROR
1044 // is returned because the DhcpEndSession will notify the user.
1046 Status
= EFI_SUCCESS
;
1048 switch (DhcpSb
->DhcpState
) {
1049 case Dhcp4Selecting
:
1050 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1053 case Dhcp4Requesting
:
1054 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1057 case Dhcp4InitReboot
:
1061 // Ignore the packet in INITREBOOT, INIT and BOUND states
1063 gBS
->FreePool (Packet
);
1064 Status
= EFI_SUCCESS
;
1068 case Dhcp4Rebinding
:
1069 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1072 case Dhcp4Rebooting
:
1073 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1078 gBS
->FreePool (Para
);
1083 if (EFI_ERROR (Status
)) {
1084 NetbufFree (UdpPacket
);
1085 DhcpEndSession (DhcpSb
, Status
);
1090 NetbufFree (UdpPacket
);
1092 if (Packet
!= NULL
) {
1093 gBS
->FreePool (Packet
);
1096 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1098 if (EFI_ERROR (Status
)) {
1099 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1107 @param Arg The packet to release
1117 gBS
->FreePool (Arg
);
1122 Release the net buffer when packet is sent.
1124 @param UdpPacket The UDP packets received.
1125 @param Points The local/remote UDP access points
1126 @param IoStatus The status of the UDP receive
1127 @param Context The opaque parameter to the function.
1136 EFI_STATUS IoStatus
,
1140 NetbufFree (Packet
);
1146 Build and transmit a DHCP message according to the current states.
1147 This function implement the Table 5. of RFC 2131. Always transits
1148 the state (as defined in Figure 5. of the same RFC) before sending
1149 a DHCP message. The table is adjusted accordingly.
1151 @param DhcpSb The DHCP service instance
1152 @param Seed The seed packet which the new packet is based on
1153 @param Para The DHCP parameter of the Seed packet
1154 @param Type The message type to send
1155 @param Msg The human readable message to include in the packet
1158 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1159 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1160 @retval EFI_SUCCESS The message is sent
1165 IN DHCP_SERVICE
*DhcpSb
,
1166 IN EFI_DHCP4_PACKET
*Seed
,
1167 IN DHCP_PARAMETER
*Para
,
1172 EFI_DHCP4_CONFIG_DATA
*Config
;
1173 EFI_DHCP4_PACKET
*Packet
;
1174 EFI_DHCP4_PACKET
*NewPacket
;
1175 EFI_DHCP4_HEADER
*Head
;
1176 EFI_DHCP4_HEADER
*SeedHead
;
1178 UDP_POINTS EndPoint
;
1189 // Allocate a big enough memory block to hold the DHCP packet
1191 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1194 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1197 Packet
= AllocatePool (Len
);
1199 if (Packet
== NULL
) {
1200 return EFI_OUT_OF_RESOURCES
;
1204 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1207 // Fill in the DHCP header fields
1209 Config
= &DhcpSb
->ActiveConfig
;
1213 SeedHead
= &Seed
->Dhcp4
.Header
;
1216 Head
= &Packet
->Dhcp4
.Header
;
1217 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1219 Head
->OpCode
= BOOTP_REQUEST
;
1220 Head
->HwType
= DhcpSb
->HwType
;
1221 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1222 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1223 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1225 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1226 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1229 // Append the DHCP message type
1231 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1232 Buf
= Packet
->Dhcp4
.Option
;
1233 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1236 // Append the serverid option if necessary:
1237 // 1. DHCP decline message
1238 // 2. DHCP release message
1239 // 3. DHCP request to confirm one lease.
1241 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1242 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))) {
1244 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1246 IpAddr
= HTONL (Para
->ServerId
);
1247 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1251 // Append the requested IP option if necessary:
1252 // 1. DHCP request to use the previously allocated address
1253 // 2. DHCP request to confirm one lease
1254 // 3. DHCP decline to decline one lease
1258 if (Type
== DHCP_MSG_REQUEST
) {
1259 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1260 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1262 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1263 ASSERT (SeedHead
!= NULL
);
1264 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1267 } else if (Type
== DHCP_MSG_DECLINE
) {
1268 ASSERT (SeedHead
!= NULL
);
1269 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1273 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1277 // Append the Max Message Length option if it isn't a DECLINE
1278 // or RELEASE to direct the server use large messages instead of
1279 // override the BOOTFILE and SERVER fields in the message head.
1281 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1282 MaxMsg
= HTONS (0xFF00);
1283 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1287 // Append the user's message if it isn't NULL
1290 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1291 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1295 // Append the user configured options
1297 if (DhcpSb
->UserOptionLen
!= 0) {
1298 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1300 // We can't use any option other than the client ID from user
1301 // if it is a DHCP decline or DHCP release .
1303 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1304 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1308 Buf
= DhcpAppendOption (
1310 Config
->OptionList
[Index
]->OpCode
,
1311 Config
->OptionList
[Index
]->Length
,
1312 Config
->OptionList
[Index
]->Data
1317 *(Buf
++) = DHCP_TAG_EOP
;
1318 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1321 // OK, the message is built, call the user to override it.
1323 Status
= EFI_SUCCESS
;
1326 if (Type
== DHCP_MSG_DISCOVER
) {
1327 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1329 } else if (Type
== DHCP_MSG_REQUEST
) {
1330 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1332 } else if (Type
== DHCP_MSG_DECLINE
) {
1333 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1336 if (EFI_ERROR (Status
)) {
1337 gBS
->FreePool (Packet
);
1341 if (NewPacket
!= NULL
) {
1342 gBS
->FreePool (Packet
);
1347 // Save the Client Address will be sent out
1349 CopyMem (&DhcpSb
->ClientAddressSendOut
[0], &Packet
->Dhcp4
.Header
.ClientHwAddr
[0], Packet
->Dhcp4
.Header
.HwAddrLen
);
1353 // Wrap it into a netbuf then send it.
1355 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1356 Frag
.Len
= Packet
->Length
;
1357 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1360 gBS
->FreePool (Packet
);
1361 return EFI_OUT_OF_RESOURCES
;
1365 // Save it as the last sent packet for retransmission
1367 if (DhcpSb
->LastPacket
!= NULL
) {
1368 NetbufFree (DhcpSb
->LastPacket
);
1372 DhcpSb
->LastPacket
= Wrap
;
1373 DhcpSetTransmitTimer (DhcpSb
);
1376 // Broadcast the message, unless we know the server address.
1377 // Use the lease UdpIo port to send the unicast packet.
1379 EndPoint
.RemoteAddr
= 0xffffffff;
1380 EndPoint
.LocalAddr
= 0;
1381 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1382 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1383 UdpIo
= DhcpSb
->UdpIo
;
1385 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1386 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1387 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1388 UdpIo
= DhcpSb
->LeaseIoPort
;
1391 ASSERT (UdpIo
!= NULL
);
1392 Status
= UdpIoSendDatagram (UdpIo
, Wrap
, &EndPoint
, 0, DhcpOnPacketSent
, DhcpSb
);
1394 if (EFI_ERROR (Status
)) {
1396 return EFI_ACCESS_DENIED
;
1404 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1405 will be retransmitted.
1407 @param DhcpSb The DHCP service instance
1409 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1410 @retval EFI_SUCCESS The packet is retransmitted.
1415 IN DHCP_SERVICE
*DhcpSb
1419 UDP_POINTS EndPoint
;
1422 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1425 // Broadcast the message, unless we know the server address.
1427 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1428 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1429 EndPoint
.RemoteAddr
= 0xffffffff;
1430 EndPoint
.LocalAddr
= 0;
1431 UdpIo
= DhcpSb
->UdpIo
;
1433 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1434 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1435 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1436 UdpIo
= DhcpSb
->LeaseIoPort
;
1439 ASSERT (UdpIo
!= NULL
);
1441 NET_GET_REF (DhcpSb
->LastPacket
);
1442 Status
= UdpIoSendDatagram (
1451 if (EFI_ERROR (Status
)) {
1452 NET_PUT_REF (DhcpSb
->LastPacket
);
1453 return EFI_ACCESS_DENIED
;
1461 Each DHCP service has three timer. Two of them are count down timer.
1462 One for the packet retransmission. The other is to collect the offers.
1463 The third timer increaments the lease life which is compared to T1, T2,
1464 and lease to determine the time to renew and rebind the lease.
1465 DhcpOnTimerTick will be called once every second.
1467 @param Event The timer event
1468 @param Context The context, which is the DHCP service instance.
1480 DHCP_SERVICE
*DhcpSb
;
1481 DHCP_PROTOCOL
*Instance
;
1484 DhcpSb
= (DHCP_SERVICE
*) Context
;
1485 Instance
= DhcpSb
->ActiveChild
;
1488 // Check the retransmit timer
1490 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1493 // Select offer at each timeout if any offer received.
1495 if (DhcpSb
->DhcpState
== Dhcp4Selecting
&& DhcpSb
->LastOffer
!= NULL
) {
1497 Status
= DhcpChooseOffer (DhcpSb
);
1499 if (EFI_ERROR(Status
)) {
1500 FreePool (DhcpSb
->LastOffer
);
1501 DhcpSb
->LastOffer
= NULL
;
1507 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1509 // Still has another try
1511 DhcpRetransmit (DhcpSb
);
1512 DhcpSetTransmitTimer (DhcpSb
);
1514 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1517 // Retransmission failed, if the DHCP request is initiated by
1518 // user, adjust the current state according to the lease life.
1519 // Otherwise do nothing to wait the lease to timeout
1521 if (DhcpSb
->ExtraRefresh
!= 0) {
1522 Status
= EFI_SUCCESS
;
1524 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1525 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1527 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1528 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1530 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1531 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1538 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1539 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1547 // If an address has been acquired, check whether need to
1548 // refresh or whether it has expired.
1550 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1551 DhcpSb
->LeaseLife
++;
1554 // Don't timeout the lease, only count the life if user is
1555 // requesting extra renew/rebind. Adjust the state after that.
1557 if (DhcpSb
->ExtraRefresh
!= 0) {
1561 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1563 // Lease expires, end the session
1567 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1569 // T2 expires, transit to rebinding then send a REQUEST to any server
1571 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1575 Status
= DhcpSendMessage (
1583 if (EFI_ERROR (Status
)) {
1587 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1589 // T1 expires, transit to renewing, then send a REQUEST to the server
1591 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1595 Status
= DhcpSendMessage (
1603 if (EFI_ERROR (Status
)) {
1610 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1611 Instance
->Timeout
--;
1612 if (Instance
->Timeout
== 0) {
1613 PxeDhcpDone (Instance
);
1620 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);