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.
88 IN DHCP_SERVICE
*DhcpSb
,
89 IN EFI_DHCP4_EVENT Event
,
90 IN EFI_DHCP4_PACKET
*Packet
, OPTIONAL
91 OUT EFI_DHCP4_PACKET
**NewPacket OPTIONAL
94 EFI_DHCP4_CONFIG_DATA
*Config
;
97 if (NewPacket
!= NULL
) {
102 // If user doesn't provide the call back function, return the value
103 // that directs the client to continue the normal process.
104 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
105 // the offers and select a offer, EFI_NOT_READY tells the client to
106 // collect more offers.
108 Config
= &DhcpSb
->ActiveConfig
;
110 if (Config
->Dhcp4Callback
== NULL
) {
111 if (Event
== Dhcp4RcvdOffer
) {
112 return EFI_NOT_READY
;
118 Status
= Config
->Dhcp4Callback (
119 &DhcpSb
->ActiveChild
->Dhcp4Protocol
,
120 Config
->CallbackContext
,
121 (EFI_DHCP4_STATE
) DhcpSb
->DhcpState
,
128 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
129 // and EFI_ABORTED. If it returns values other than those, assume
130 // it to be EFI_ABORTED.
132 if ((Status
== EFI_SUCCESS
) || (Status
== EFI_NOT_READY
)) {
141 Notify the user about the operation result.
143 @param DhcpSb DHCP service instance
144 @param Which which notify function to signal
151 IN DHCP_SERVICE
*DhcpSb
,
155 DHCP_PROTOCOL
*Child
;
157 if ((Child
= DhcpSb
->ActiveChild
) == NULL
) {
161 if ((Child
->CompletionEvent
!= NULL
) &&
162 ((Which
== DHCP_NOTIFY_COMPLETION
) || (Which
== DHCP_NOTIFY_ALL
))) {
164 gBS
->SignalEvent (Child
->CompletionEvent
);
165 Child
->CompletionEvent
= NULL
;
168 if ((Child
->RenewRebindEvent
!= NULL
) &&
169 ((Which
== DHCP_NOTIFY_RENEWREBIND
) || (Which
== DHCP_NOTIFY_ALL
))) {
171 gBS
->SignalEvent (Child
->RenewRebindEvent
);
172 Child
->RenewRebindEvent
= NULL
;
179 Set the DHCP state. If CallUser is true, it will try to notify
180 the user before change the state by DhcpNotifyUser. It returns
181 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
182 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
183 the return value of this function.
185 @param DhcpSb The DHCP service instance
186 @param State The new DHCP state to change to
187 @param CallUser Whether we need to call user
189 @retval EFI_SUCCESS The state is changed
190 @retval EFI_ABORTED The user asks to abort the DHCP process.
195 IN DHCP_SERVICE
*DhcpSb
,
203 Status
= EFI_SUCCESS
;
205 if (State
== Dhcp4Renewing
) {
206 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRenewing
, NULL
, NULL
);
208 } else if (State
== Dhcp4Rebinding
) {
209 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRebinding
, NULL
, NULL
);
211 } else if (State
== Dhcp4Bound
) {
212 Status
= DhcpCallUser (DhcpSb
, Dhcp4BoundCompleted
, NULL
, NULL
);
216 if (EFI_ERROR (Status
)) {
222 // Update the retransmission timer during the state transition.
223 // This will clear the retry count. This is also why the rule
224 // first transit the state, then send packets.
226 if (State
== Dhcp4Selecting
) {
227 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.DiscoverTryCount
;
229 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.RequestTryCount
;
232 if (DhcpSb
->MaxRetries
== 0) {
233 DhcpSb
->MaxRetries
= 4;
236 DhcpSb
->CurRetry
= 0;
237 DhcpSb
->PacketToLive
= 0;
239 DhcpSb
->DhcpState
= State
;
245 Set the retransmit timer for the packet. It will select from either
246 the discover timeouts/request timeouts or the default timeout values.
248 @param DhcpSb The DHCP service instance.
255 DhcpSetTransmitTimer (
256 IN DHCP_SERVICE
*DhcpSb
261 ASSERT (DhcpSb
->MaxRetries
> DhcpSb
->CurRetry
);
263 if (DhcpSb
->DhcpState
== Dhcp4Selecting
) {
264 Times
= DhcpSb
->ActiveConfig
.DiscoverTimeout
;
266 Times
= DhcpSb
->ActiveConfig
.RequestTimeout
;
270 Times
= mDhcp4DefaultTimeout
;
273 DhcpSb
->PacketToLive
= Times
[DhcpSb
->CurRetry
];
279 Compute the lease. If the server grants a permanent lease, just
280 process it as a normal timeout value since the lease will last
283 @param DhcpSb The DHCP service instance
284 @param Para The DHCP parameter extracted from the server's
293 IN DHCP_SERVICE
*DhcpSb
,
294 IN DHCP_PARAMETER
*Para
297 ASSERT (Para
!= NULL
);
299 DhcpSb
->Lease
= Para
->Lease
;
300 DhcpSb
->T2
= Para
->T2
;
301 DhcpSb
->T1
= Para
->T1
;
303 if (DhcpSb
->Lease
== 0) {
304 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
307 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
308 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
311 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
312 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
318 Configure a UDP IO port to use the acquired lease address.
319 DHCP driver needs this port to unicast packet to the server
320 such as DHCP release.
322 @param UdpIo The UDP IO port to configure
323 @param Context The opaque parameter to the function.
325 @retval EFI_SUCCESS The UDP IO port is successfully configured.
326 @retval Others It failed to configure the port.
330 DhcpConfigLeaseIoPort (
331 IN UDP_IO_PORT
*UdpIo
,
335 EFI_UDP4_CONFIG_DATA UdpConfigData
;
336 EFI_IPv4_ADDRESS Subnet
;
337 EFI_IPv4_ADDRESS Gateway
;
338 DHCP_SERVICE
*DhcpSb
;
342 DhcpSb
= (DHCP_SERVICE
*) Context
;
344 UdpConfigData
.AcceptBroadcast
= FALSE
;
345 UdpConfigData
.AcceptPromiscuous
= FALSE
;
346 UdpConfigData
.AcceptAnyPort
= FALSE
;
347 UdpConfigData
.AllowDuplicatePort
= TRUE
;
348 UdpConfigData
.TypeOfService
= 0;
349 UdpConfigData
.TimeToLive
= 64;
350 UdpConfigData
.DoNotFragment
= FALSE
;
351 UdpConfigData
.ReceiveTimeout
= 1;
352 UdpConfigData
.TransmitTimeout
= 0;
354 UdpConfigData
.UseDefaultAddress
= FALSE
;
355 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
356 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
358 Ip
= HTONL (DhcpSb
->ClientAddr
);
359 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
361 Ip
= HTONL (DhcpSb
->Netmask
);
362 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
364 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
366 Status
= UdpIo
->Udp
->Configure (UdpIo
->Udp
, &UdpConfigData
);
368 if (EFI_ERROR (Status
)) {
373 // Add a default route if received from the server.
375 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
376 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
378 Ip
= HTONL (DhcpSb
->Para
->Router
);
379 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
381 UdpIo
->Udp
->Routes (UdpIo
->Udp
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
389 Update the lease states when a new lease is acquired. It will not only
390 save the acquired the address and lease time, it will also create a UDP
391 child to provide address resolution for the address.
393 @param DhcpSb The DHCP service instance
395 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
396 @retval EFI_SUCCESS The lease is recorded.
402 IN DHCP_SERVICE
*DhcpSb
407 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
409 if (DhcpSb
->Para
!= NULL
) {
410 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
411 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
414 if (DhcpSb
->Netmask
== 0) {
415 Class
= NetGetIpClass (DhcpSb
->ClientAddr
);
416 DhcpSb
->Netmask
= mIp4AllMasks
[Class
<< 3];
419 if (DhcpSb
->LeaseIoPort
!= NULL
) {
420 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
424 // Create a UDP/IP child to provide ARP service for the Leased IP,
425 // and transmit unicast packet with it as source address. Don't
426 // start receive on this port, the queued packet will be timeout.
428 DhcpSb
->LeaseIoPort
= UdpIoCreatePort (
431 DhcpConfigLeaseIoPort
,
435 if (DhcpSb
->LeaseIoPort
== NULL
) {
436 return EFI_OUT_OF_RESOURCES
;
439 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
440 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
443 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
448 Clean up the DHCP related states, IoStatus isn't reset.
450 @param DhcpSb The DHCP instance service.
457 IN DHCP_SERVICE
*DhcpSb
460 DhcpSb
->DhcpState
= Dhcp4Init
;
461 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
462 DhcpSb
->ClientAddr
= 0;
463 DhcpSb
->ServerAddr
= 0;
465 if (DhcpSb
->LastOffer
!= NULL
) {
466 gBS
->FreePool (DhcpSb
->LastOffer
);
467 DhcpSb
->LastOffer
= NULL
;
470 if (DhcpSb
->Selected
!= NULL
) {
471 gBS
->FreePool (DhcpSb
->Selected
);
472 DhcpSb
->Selected
= NULL
;
475 if (DhcpSb
->Para
!= NULL
) {
476 gBS
->FreePool (DhcpSb
->Para
);
483 DhcpSb
->ExtraRefresh
= FALSE
;
485 if (DhcpSb
->LeaseIoPort
!= NULL
) {
486 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
487 DhcpSb
->LeaseIoPort
= NULL
;
490 if (DhcpSb
->LastPacket
!= NULL
) {
491 NetbufFree (DhcpSb
->LastPacket
);
492 DhcpSb
->LastPacket
= NULL
;
495 DhcpSb
->PacketToLive
= 0;
496 DhcpSb
->CurRetry
= 0;
497 DhcpSb
->MaxRetries
= 0;
498 DhcpSb
->LeaseLife
= 0;
503 Select a offer among all the offers collected. If the offer selected is
504 of BOOTP, the lease is recorded and user notified. If the offer is of
505 DHCP, it will request the offer from the server.
507 @param DhcpSb The DHCP service instance.
509 @retval EFI_SUCCESS One of the offer is selected.
515 IN DHCP_SERVICE
*DhcpSb
518 EFI_DHCP4_PACKET
*Selected
;
519 EFI_DHCP4_PACKET
*NewPacket
;
520 EFI_DHCP4_PACKET
*TempPacket
;
523 ASSERT (DhcpSb
->LastOffer
!= NULL
);
526 // User will cache previous offers if he wants to select
527 // from multiple offers. If user provides an invalid packet,
528 // use the last offer, otherwise use the provided packet.
531 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
533 if (EFI_ERROR (Status
)) {
537 Selected
= DhcpSb
->LastOffer
;
539 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
540 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
541 if (TempPacket
!= NULL
) {
542 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
543 gBS
->FreePool (Selected
);
544 Selected
= TempPacket
;
548 DhcpSb
->Selected
= Selected
;
549 DhcpSb
->LastOffer
= NULL
;
551 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
554 // A bootp offer has been selected, save the lease status,
555 // enter bound state then notify the user.
557 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
558 Status
= DhcpLeaseAcquired (DhcpSb
);
560 if (EFI_ERROR (Status
)) {
564 DhcpSb
->IoStatus
= EFI_SUCCESS
;
565 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
570 // Send a DHCP requests
572 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
574 if (EFI_ERROR (Status
)) {
578 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
583 Terminate the current address acquire. All the allocated resources
584 are released. Be careful when calling this function. A rule related
585 to this is: only call DhcpEndSession at the highest level, such as
586 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
588 @param DhcpSb The DHCP service instance
589 @param Status The result of the DHCP process.
597 IN DHCP_SERVICE
*DhcpSb
,
601 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
602 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
604 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
607 DhcpCleanLease (DhcpSb
);
609 DhcpSb
->IoStatus
= Status
;
610 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
615 Handle packets in DHCP select state.
617 @param DhcpSb The DHCP service instance
618 @param Packet The DHCP packet received
619 @param Para The DHCP parameter extracted from the packet. That
620 is, all the option value that we care.
622 @retval EFI_SUCCESS The packet is successfully processed.
623 @retval Others Some error occured.
629 IN DHCP_SERVICE
*DhcpSb
,
630 IN EFI_DHCP4_PACKET
*Packet
,
631 IN DHCP_PARAMETER
*Para
636 Status
= EFI_SUCCESS
;
639 // First validate the message:
640 // 1. the offer is a unicast
641 // 2. if it is a DHCP message, it must contains a server ID.
642 // Don't return a error for these two case otherwise the session is ended.
644 if (!DHCP_IS_BOOTP (Para
) &&
645 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))) {
650 // Call the user's callback. The action according to the return is as:
651 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
652 // 2. EFI_NOT_READY: wait for more offers
653 // 3. EFI_ABORTED: abort the address acquiring.
655 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
657 if (Status
== EFI_SUCCESS
) {
658 if (DhcpSb
->LastOffer
!= NULL
) {
659 gBS
->FreePool (DhcpSb
->LastOffer
);
662 DhcpSb
->LastOffer
= Packet
;
664 return DhcpChooseOffer (DhcpSb
);
666 } else if (Status
== EFI_NOT_READY
) {
667 if (DhcpSb
->LastOffer
!= NULL
) {
668 gBS
->FreePool (DhcpSb
->LastOffer
);
671 DhcpSb
->LastOffer
= Packet
;
673 } else if (Status
== EFI_ABORTED
) {
675 // DhcpInput will end the session upon error return. Remember
676 // only to call DhcpEndSession at the top level call.
684 gBS
->FreePool (Packet
);
690 Handle packets in DHCP request state.
692 @param DhcpSb The DHCP service instance
693 @param Packet The DHCP packet received
694 @param Para The DHCP parameter extracted from the packet. That
695 is, all the option value that we care.
697 @retval EFI_SUCCESS The packet is successfully processed.
698 @retval Others Some error occured.
704 IN DHCP_SERVICE
*DhcpSb
,
705 IN EFI_DHCP4_PACKET
*Packet
,
706 IN DHCP_PARAMETER
*Para
709 EFI_DHCP4_HEADER
*Head
;
710 EFI_DHCP4_HEADER
*Selected
;
714 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
716 Head
= &Packet
->Dhcp4
.Header
;
717 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
720 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
722 if (DHCP_IS_BOOTP (Para
) ||
723 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
724 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
726 Status
= EFI_SUCCESS
;
731 // Received a NAK, end the session no matter what the user returns
733 Status
= EFI_DEVICE_ERROR
;
735 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
736 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
741 // Check whether the ACK matches the selected offer
745 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
746 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
750 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
752 if (EFI_ERROR (Status
)) {
753 Message
= (UINT8
*) "Lease is denied upon received ACK";
758 // Record the lease, transit to BOUND state, then notify the user
760 Status
= DhcpLeaseAcquired (DhcpSb
);
762 if (EFI_ERROR (Status
)) {
763 Message
= (UINT8
*) "Lease is denied upon entering bound";
767 DhcpSb
->IoStatus
= EFI_SUCCESS
;
768 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
770 gBS
->FreePool (Packet
);
774 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
777 gBS
->FreePool (Packet
);
783 Handle packets in DHCP renew/rebound state.
785 @param DhcpSb The DHCP service instance
786 @param Packet The DHCP packet received
787 @param Para The DHCP parameter extracted from the packet. That
788 is, all the option value that we care.
790 @retval EFI_SUCCESS The packet is successfully processed.
791 @retval Others Some error occured.
796 DhcpHandleRenewRebind (
797 IN DHCP_SERVICE
*DhcpSb
,
798 IN EFI_DHCP4_PACKET
*Packet
,
799 IN DHCP_PARAMETER
*Para
802 EFI_DHCP4_HEADER
*Head
;
803 EFI_DHCP4_HEADER
*Selected
;
806 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
808 Head
= &Packet
->Dhcp4
.Header
;
809 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
812 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
814 if (DHCP_IS_BOOTP (Para
) ||
815 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
816 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
818 Status
= EFI_SUCCESS
;
823 // Received a NAK, ignore the user's return then terminate the process
825 Status
= EFI_DEVICE_ERROR
;
827 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
828 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
833 // The lease is different from the selected. Don't send a DECLINE
834 // since it isn't existed in the client's FSM.
836 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
840 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
842 if (EFI_ERROR (Status
)) {
847 // Record the lease, start timer for T1 and T2,
849 DhcpComputeLease (DhcpSb
, Para
);
850 DhcpSb
->LeaseLife
= 0;
851 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
853 if (DhcpSb
->ExtraRefresh
) {
854 DhcpSb
->ExtraRefresh
= FALSE
;
856 DhcpSb
->IoStatus
= EFI_SUCCESS
;
857 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
861 gBS
->FreePool (Packet
);
867 Handle packets in DHCP reboot state.
869 @param DhcpSb The DHCP service instance
870 @param Packet The DHCP packet received
871 @param Para The DHCP parameter extracted from the packet. That
872 is, all the option value that we care.
874 @retval EFI_SUCCESS The packet is successfully processed.
875 @retval Others Some error occured.
881 IN DHCP_SERVICE
*DhcpSb
,
882 IN EFI_DHCP4_PACKET
*Packet
,
883 IN DHCP_PARAMETER
*Para
886 EFI_DHCP4_HEADER
*Head
;
889 Head
= &Packet
->Dhcp4
.Header
;
892 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
894 if (DHCP_IS_BOOTP (Para
) ||
895 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
897 Status
= EFI_SUCCESS
;
902 // If a NAK is received, transit to INIT and try again.
904 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
905 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
907 DhcpSb
->ClientAddr
= 0;
908 DhcpSb
->DhcpState
= Dhcp4Init
;
910 Status
= DhcpInitRequest (DhcpSb
);
915 // Check whether the ACK matches the selected offer
917 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
918 Status
= EFI_DEVICE_ERROR
;
922 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
923 if (EFI_ERROR (Status
)) {
928 // OK, get the parameter from server, record the lease
930 DhcpSb
->Para
= AllocatePool (sizeof (DHCP_PARAMETER
));
932 if (DhcpSb
->Para
== NULL
) {
933 Status
= EFI_OUT_OF_RESOURCES
;
937 DhcpSb
->Selected
= Packet
;
938 CopyMem (DhcpSb
->Para
, Para
, sizeof (*DhcpSb
->Para
));
940 Status
= DhcpLeaseAcquired (DhcpSb
);
942 if (EFI_ERROR (Status
)) {
946 DhcpSb
->IoStatus
= EFI_SUCCESS
;
947 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
951 gBS
->FreePool (Packet
);
957 Handle the received DHCP packets. This function drivers the DHCP
960 @param UdpPacket The UDP packets received.
961 @param Points The local/remote UDP access points
962 @param IoStatus The status of the UDP receive
963 @param Context The opaque parameter to the function.
976 DHCP_SERVICE
*DhcpSb
;
977 EFI_DHCP4_HEADER
*Head
;
978 EFI_DHCP4_PACKET
*Packet
;
979 DHCP_PARAMETER
*Para
;
984 DhcpSb
= (DHCP_SERVICE
*) Context
;
987 // Don't restart receive if error occurs or DHCP is destoried.
989 if (EFI_ERROR (IoStatus
)) {
991 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
992 NetbufFree (UdpPacket
);
996 ASSERT (UdpPacket
!= NULL
);
998 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
1003 // Validate the packet received
1005 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
1010 // Copy the DHCP message to a continuous memory block
1012 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
1013 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
1015 if (Packet
== NULL
) {
1020 Head
= &Packet
->Dhcp4
.Header
;
1021 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1023 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1028 // Is this packet the answer to our packet?
1030 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1031 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1032 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1037 // Validate the options and retrieve the interested options
1040 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1041 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1042 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1048 // Call the handler for each state. The handler should return
1049 // EFI_SUCCESS if the process can go on no matter whether the
1050 // packet is ignored or not. If the return is EFI_ERROR, the
1051 // session will be terminated. Packet's ownership is handled
1052 // over to the handlers. If operation succeeds, the handler
1053 // must notify the user. It isn't necessary to do if EFI_ERROR
1054 // is returned because the DhcpEndSession will notify the user.
1056 Status
= EFI_SUCCESS
;
1058 switch (DhcpSb
->DhcpState
) {
1059 case Dhcp4Selecting
:
1060 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1063 case Dhcp4Requesting
:
1064 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1067 case Dhcp4InitReboot
:
1071 // Ignore the packet in INITREBOOT, INIT and BOUND states
1073 gBS
->FreePool (Packet
);
1074 Status
= EFI_SUCCESS
;
1078 case Dhcp4Rebinding
:
1079 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1082 case Dhcp4Rebooting
:
1083 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1088 gBS
->FreePool (Para
);
1093 if (EFI_ERROR (Status
)) {
1094 NetbufFree (UdpPacket
);
1095 DhcpEndSession (DhcpSb
, Status
);
1100 NetbufFree (UdpPacket
);
1102 if (Packet
!= NULL
) {
1103 gBS
->FreePool (Packet
);
1106 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1108 if (EFI_ERROR (Status
)) {
1109 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1117 @param Arg The packet to release
1127 gBS
->FreePool (Arg
);
1132 Release the net buffer when packet is sent.
1134 @param UdpPacket The UDP packets received.
1135 @param Points The local/remote UDP access points
1136 @param IoStatus The status of the UDP receive
1137 @param Context The opaque parameter to the function.
1146 EFI_STATUS IoStatus
,
1150 NetbufFree (Packet
);
1156 Build and transmit a DHCP message according to the current states.
1157 This function implement the Table 5. of RFC 2131. Always transits
1158 the state (as defined in Figure 5. of the same RFC) before sending
1159 a DHCP message. The table is adjusted accordingly.
1161 @param DhcpSb The DHCP service instance
1162 @param Seed The seed packet which the new packet is based on
1163 @param Para The DHCP parameter of the Seed packet
1164 @param Type The message type to send
1165 @param Msg The human readable message to include in the packet
1168 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1169 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1170 @retval EFI_SUCCESS The message is sent
1175 IN DHCP_SERVICE
*DhcpSb
,
1176 IN EFI_DHCP4_PACKET
*Seed
,
1177 IN DHCP_PARAMETER
*Para
,
1182 EFI_DHCP4_CONFIG_DATA
*Config
;
1183 EFI_DHCP4_PACKET
*Packet
;
1184 EFI_DHCP4_PACKET
*NewPacket
;
1185 EFI_DHCP4_HEADER
*Head
;
1186 EFI_DHCP4_HEADER
*SeedHead
;
1188 UDP_POINTS EndPoint
;
1199 // Allocate a big enough memory block to hold the DHCP packet
1201 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1204 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1207 Packet
= AllocatePool (Len
);
1209 if (Packet
== NULL
) {
1210 return EFI_OUT_OF_RESOURCES
;
1214 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1217 // Fill in the DHCP header fields
1219 Config
= &DhcpSb
->ActiveConfig
;
1223 SeedHead
= &Seed
->Dhcp4
.Header
;
1226 Head
= &Packet
->Dhcp4
.Header
;
1227 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1229 Head
->OpCode
= BOOTP_REQUEST
;
1230 Head
->HwType
= DhcpSb
->HwType
;
1231 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1232 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1233 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1235 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1236 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1239 // Append the DHCP message type
1241 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1242 Buf
= Packet
->Dhcp4
.Option
;
1243 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1246 // Append the serverid option if necessary:
1247 // 1. DHCP decline message
1248 // 2. DHCP release message
1249 // 3. DHCP request to confirm one lease.
1251 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1252 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))) {
1254 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1256 IpAddr
= HTONL (Para
->ServerId
);
1257 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1261 // Append the requested IP option if necessary:
1262 // 1. DHCP request to use the previously allocated address
1263 // 2. DHCP request to confirm one lease
1264 // 3. DHCP decline to decline one lease
1268 if (Type
== DHCP_MSG_REQUEST
) {
1269 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1270 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1272 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1273 ASSERT (SeedHead
!= NULL
);
1274 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1277 } else if (Type
== DHCP_MSG_DECLINE
) {
1278 ASSERT (SeedHead
!= NULL
);
1279 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1283 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1287 // Append the Max Message Length option if it isn't a DECLINE
1288 // or RELEASE to direct the server use large messages instead of
1289 // override the BOOTFILE and SERVER fields in the message head.
1291 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1292 MaxMsg
= HTONS (0xFF00);
1293 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1297 // Append the user's message if it isn't NULL
1300 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1301 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1305 // Append the user configured options
1307 if (DhcpSb
->UserOptionLen
!= 0) {
1308 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1310 // We can't use any option other than the client ID from user
1311 // if it is a DHCP decline or DHCP release .
1313 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1314 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1318 Buf
= DhcpAppendOption (
1320 Config
->OptionList
[Index
]->OpCode
,
1321 Config
->OptionList
[Index
]->Length
,
1322 Config
->OptionList
[Index
]->Data
1327 *(Buf
++) = DHCP_TAG_EOP
;
1328 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1331 // OK, the message is built, call the user to override it.
1333 Status
= EFI_SUCCESS
;
1336 if (Type
== DHCP_MSG_DISCOVER
) {
1337 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1339 } else if (Type
== DHCP_MSG_REQUEST
) {
1340 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1342 } else if (Type
== DHCP_MSG_DECLINE
) {
1343 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1346 if (EFI_ERROR (Status
)) {
1347 gBS
->FreePool (Packet
);
1351 if (NewPacket
!= NULL
) {
1352 gBS
->FreePool (Packet
);
1357 // Save the Client Address will be sent out
1359 CopyMem (&DhcpSb
->ClientAddressSendOut
[0], &Packet
->Dhcp4
.Header
.ClientHwAddr
[0], Packet
->Dhcp4
.Header
.HwAddrLen
);
1363 // Wrap it into a netbuf then send it.
1365 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1366 Frag
.Len
= Packet
->Length
;
1367 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1370 gBS
->FreePool (Packet
);
1371 return EFI_OUT_OF_RESOURCES
;
1375 // Save it as the last sent packet for retransmission
1377 if (DhcpSb
->LastPacket
!= NULL
) {
1378 NetbufFree (DhcpSb
->LastPacket
);
1382 DhcpSb
->LastPacket
= Wrap
;
1383 DhcpSetTransmitTimer (DhcpSb
);
1386 // Broadcast the message, unless we know the server address.
1387 // Use the lease UdpIo port to send the unicast packet.
1389 EndPoint
.RemoteAddr
= 0xffffffff;
1390 EndPoint
.LocalAddr
= 0;
1391 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1392 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1393 UdpIo
= DhcpSb
->UdpIo
;
1395 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1396 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1397 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1398 UdpIo
= DhcpSb
->LeaseIoPort
;
1401 ASSERT (UdpIo
!= NULL
);
1402 Status
= UdpIoSendDatagram (UdpIo
, Wrap
, &EndPoint
, 0, DhcpOnPacketSent
, DhcpSb
);
1404 if (EFI_ERROR (Status
)) {
1406 return EFI_ACCESS_DENIED
;
1414 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1415 will be retransmitted.
1417 @param DhcpSb The DHCP service instance
1419 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1420 @retval EFI_SUCCESS The packet is retransmitted.
1425 IN DHCP_SERVICE
*DhcpSb
1429 UDP_POINTS EndPoint
;
1432 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1435 // Broadcast the message, unless we know the server address.
1437 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1438 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1439 EndPoint
.RemoteAddr
= 0xffffffff;
1440 EndPoint
.LocalAddr
= 0;
1441 UdpIo
= DhcpSb
->UdpIo
;
1443 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1444 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1445 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1446 UdpIo
= DhcpSb
->LeaseIoPort
;
1449 ASSERT (UdpIo
!= NULL
);
1451 NET_GET_REF (DhcpSb
->LastPacket
);
1452 Status
= UdpIoSendDatagram (
1461 if (EFI_ERROR (Status
)) {
1462 NET_PUT_REF (DhcpSb
->LastPacket
);
1463 return EFI_ACCESS_DENIED
;
1471 Each DHCP service has three timer. Two of them are count down timer.
1472 One for the packet retransmission. The other is to collect the offers.
1473 The third timer increaments the lease life which is compared to T1, T2,
1474 and lease to determine the time to renew and rebind the lease.
1475 DhcpOnTimerTick will be called once every second.
1477 @param Event The timer event
1478 @param 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
) {
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
) {
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
);