3 Copyright (c) 2006 - 2007, 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
];
275 if (DhcpSb
->DhcpState
== Dhcp4Selecting
) {
276 DhcpSb
->WaitOffer
= DhcpSb
->PacketToLive
;
281 Compute the lease. If the server grants a permanent lease, just
282 process it as a normal timeout value since the lease will last
285 @param DhcpSb The DHCP service instance
286 @param Para The DHCP parameter extracted from the server's
295 IN DHCP_SERVICE
*DhcpSb
,
296 IN DHCP_PARAMETER
*Para
299 ASSERT (Para
!= NULL
);
301 DhcpSb
->Lease
= Para
->Lease
;
302 DhcpSb
->T2
= Para
->T2
;
303 DhcpSb
->T1
= Para
->T1
;
305 if (DhcpSb
->Lease
== 0) {
306 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
309 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
310 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
313 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
314 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
320 Configure a UDP IO port to use the acquired lease address.
321 DHCP driver needs this port to unicast packet to the server
322 such as DHCP release.
324 @param UdpIo The UDP IO port to configure
325 @param Context The opaque parameter to the function.
327 @retval EFI_SUCCESS The UDP IO port is successfully configured.
328 @retval Others It failed to configure the port.
332 DhcpConfigLeaseIoPort (
333 IN UDP_IO_PORT
*UdpIo
,
337 EFI_UDP4_CONFIG_DATA UdpConfigData
;
338 EFI_IPv4_ADDRESS Subnet
;
339 EFI_IPv4_ADDRESS Gateway
;
340 DHCP_SERVICE
*DhcpSb
;
344 DhcpSb
= (DHCP_SERVICE
*) Context
;
346 UdpConfigData
.AcceptBroadcast
= FALSE
;
347 UdpConfigData
.AcceptPromiscuous
= FALSE
;
348 UdpConfigData
.AcceptAnyPort
= FALSE
;
349 UdpConfigData
.AllowDuplicatePort
= TRUE
;
350 UdpConfigData
.TypeOfService
= 0;
351 UdpConfigData
.TimeToLive
= 64;
352 UdpConfigData
.DoNotFragment
= FALSE
;
353 UdpConfigData
.ReceiveTimeout
= 1;
354 UdpConfigData
.TransmitTimeout
= 0;
356 UdpConfigData
.UseDefaultAddress
= FALSE
;
357 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
358 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
360 Ip
= HTONL (DhcpSb
->ClientAddr
);
361 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
363 Ip
= HTONL (DhcpSb
->Netmask
);
364 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
366 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
368 Status
= UdpIo
->Udp
->Configure (UdpIo
->Udp
, &UdpConfigData
);
370 if (EFI_ERROR (Status
)) {
375 // Add a default route if received from the server.
377 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
378 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
380 Ip
= HTONL (DhcpSb
->Para
->Router
);
381 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
383 UdpIo
->Udp
->Routes (UdpIo
->Udp
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
391 Update the lease states when a new lease is acquired. It will not only
392 save the acquired the address and lease time, it will also create a UDP
393 child to provide address resolution for the address.
395 @param DhcpSb The DHCP service instance
397 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
398 @retval EFI_SUCCESS The lease is recorded.
404 IN DHCP_SERVICE
*DhcpSb
409 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
411 if (DhcpSb
->Para
!= NULL
) {
412 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
413 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
416 if (DhcpSb
->Netmask
== 0) {
417 Class
= NetGetIpClass (DhcpSb
->ClientAddr
);
418 DhcpSb
->Netmask
= mIp4AllMasks
[Class
<< 3];
421 if (DhcpSb
->LeaseIoPort
!= NULL
) {
422 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
426 // Create a UDP/IP child to provide ARP service for the Leased IP,
427 // and transmit unicast packet with it as source address. Don't
428 // start receive on this port, the queued packet will be timeout.
430 DhcpSb
->LeaseIoPort
= UdpIoCreatePort (
433 DhcpConfigLeaseIoPort
,
437 if (DhcpSb
->LeaseIoPort
== NULL
) {
438 return EFI_OUT_OF_RESOURCES
;
441 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
442 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
445 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
450 Clean up the DHCP related states, IoStatus isn't reset.
452 @param DhcpSb The DHCP instance service.
459 IN DHCP_SERVICE
*DhcpSb
462 DhcpSb
->DhcpState
= Dhcp4Init
;
463 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
464 DhcpSb
->ClientAddr
= 0;
465 DhcpSb
->ServerAddr
= 0;
467 if (DhcpSb
->LastOffer
!= NULL
) {
468 gBS
->FreePool (DhcpSb
->LastOffer
);
469 DhcpSb
->LastOffer
= NULL
;
472 if (DhcpSb
->Selected
!= NULL
) {
473 gBS
->FreePool (DhcpSb
->Selected
);
474 DhcpSb
->Selected
= NULL
;
477 if (DhcpSb
->Para
!= NULL
) {
478 gBS
->FreePool (DhcpSb
->Para
);
485 DhcpSb
->ExtraRefresh
= FALSE
;
487 if (DhcpSb
->LeaseIoPort
!= NULL
) {
488 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
489 DhcpSb
->LeaseIoPort
= NULL
;
492 if (DhcpSb
->LastPacket
!= NULL
) {
493 NetbufFree (DhcpSb
->LastPacket
);
494 DhcpSb
->LastPacket
= NULL
;
497 DhcpSb
->PacketToLive
= 0;
498 DhcpSb
->CurRetry
= 0;
499 DhcpSb
->MaxRetries
= 0;
500 DhcpSb
->WaitOffer
= 0;
501 DhcpSb
->LeaseLife
= 0;
506 Select a offer among all the offers collected. If the offer selected is
507 of BOOTP, the lease is recorded and user notified. If the offer is of
508 DHCP, it will request the offer from the server.
510 @param DhcpSb The DHCP service instance.
512 @retval EFI_SUCCESS One of the offer is selected.
518 IN DHCP_SERVICE
*DhcpSb
521 EFI_DHCP4_PACKET
*Selected
;
522 EFI_DHCP4_PACKET
*NewPacket
;
523 EFI_DHCP4_PACKET
*TempPacket
;
526 ASSERT (DhcpSb
->LastOffer
!= NULL
);
529 // Stop waiting more offers
531 DhcpSb
->WaitOffer
= 0;
534 // User will cache previous offers if he wants to select
535 // from multiple offers. If user provides an invalid packet,
536 // use the last offer, otherwise use the provided packet.
539 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
541 if (EFI_ERROR (Status
)) {
545 Selected
= DhcpSb
->LastOffer
;
547 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
548 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
549 if (TempPacket
!= NULL
) {
550 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
551 gBS
->FreePool (Selected
);
552 Selected
= TempPacket
;
556 DhcpSb
->Selected
= Selected
;
557 DhcpSb
->LastOffer
= NULL
;
559 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
562 // A bootp offer has been selected, save the lease status,
563 // enter bound state then notify the user.
565 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
566 Status
= DhcpLeaseAcquired (DhcpSb
);
568 if (EFI_ERROR (Status
)) {
572 DhcpSb
->IoStatus
= EFI_SUCCESS
;
573 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
578 // Send a DHCP requests
580 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
582 if (EFI_ERROR (Status
)) {
586 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
591 Terminate the current address acquire. All the allocated resources
592 are released. Be careful when calling this function. A rule related
593 to this is: only call DhcpEndSession at the highest level, such as
594 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
596 @param DhcpSb The DHCP service instance
597 @param Status The result of the DHCP process.
605 IN DHCP_SERVICE
*DhcpSb
,
609 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
610 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
612 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
615 DhcpCleanLease (DhcpSb
);
617 DhcpSb
->IoStatus
= Status
;
618 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
623 Handle packets in DHCP select state.
625 @param DhcpSb The DHCP service instance
626 @param Packet The DHCP packet received
627 @param Para The DHCP parameter extracted from the packet. That
628 is, all the option value that we care.
630 @retval EFI_SUCCESS The packet is successfully processed.
631 @retval Others Some error occured.
637 IN DHCP_SERVICE
*DhcpSb
,
638 IN EFI_DHCP4_PACKET
*Packet
,
639 IN DHCP_PARAMETER
*Para
644 Status
= EFI_SUCCESS
;
647 // First validate the message:
648 // 1. the offer is a unicast
649 // 2. if it is a DHCP message, it must contains a server ID.
650 // Don't return a error for these two case otherwise the session is ended.
652 if (!DHCP_IS_BOOTP (Para
) &&
653 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))) {
658 // Call the user's callback. The action according to the return is as:
659 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
660 // 2. EFI_NOT_READY: wait for more offers
661 // 3. EFI_ABORTED: abort the address acquiring.
663 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
665 if (Status
== EFI_SUCCESS
) {
666 if (DhcpSb
->LastOffer
!= NULL
) {
667 gBS
->FreePool (DhcpSb
->LastOffer
);
670 DhcpSb
->LastOffer
= Packet
;
672 return DhcpChooseOffer (DhcpSb
);
674 } else if (Status
== EFI_NOT_READY
) {
675 if (DhcpSb
->LastOffer
!= NULL
) {
676 gBS
->FreePool (DhcpSb
->LastOffer
);
679 DhcpSb
->LastOffer
= Packet
;
681 } else if (Status
== EFI_ABORTED
) {
683 // DhcpInput will end the session upon error return. Remember
684 // only to call DhcpEndSession at the top level call.
692 gBS
->FreePool (Packet
);
698 Handle packets in DHCP request state.
700 @param DhcpSb The DHCP service instance
701 @param Packet The DHCP packet received
702 @param Para The DHCP parameter extracted from the packet. That
703 is, all the option value that we care.
705 @retval EFI_SUCCESS The packet is successfully processed.
706 @retval Others Some error occured.
712 IN DHCP_SERVICE
*DhcpSb
,
713 IN EFI_DHCP4_PACKET
*Packet
,
714 IN DHCP_PARAMETER
*Para
717 EFI_DHCP4_HEADER
*Head
;
718 EFI_DHCP4_HEADER
*Selected
;
722 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
724 Head
= &Packet
->Dhcp4
.Header
;
725 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
728 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
730 if (DHCP_IS_BOOTP (Para
) ||
731 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
732 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
734 Status
= EFI_SUCCESS
;
739 // Received a NAK, end the session no matter what the user returns
741 Status
= EFI_DEVICE_ERROR
;
743 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
744 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
749 // Check whether the ACK matches the selected offer
753 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
754 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
758 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
760 if (EFI_ERROR (Status
)) {
761 Message
= (UINT8
*) "Lease is denied upon received ACK";
766 // Record the lease, transit to BOUND state, then notify the user
768 Status
= DhcpLeaseAcquired (DhcpSb
);
770 if (EFI_ERROR (Status
)) {
771 Message
= (UINT8
*) "Lease is denied upon entering bound";
775 DhcpSb
->IoStatus
= EFI_SUCCESS
;
776 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
778 gBS
->FreePool (Packet
);
782 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
785 gBS
->FreePool (Packet
);
791 Handle packets in DHCP renew/rebound state.
793 @param DhcpSb The DHCP service instance
794 @param Packet The DHCP packet received
795 @param Para The DHCP parameter extracted from the packet. That
796 is, all the option value that we care.
798 @retval EFI_SUCCESS The packet is successfully processed.
799 @retval Others Some error occured.
804 DhcpHandleRenewRebind (
805 IN DHCP_SERVICE
*DhcpSb
,
806 IN EFI_DHCP4_PACKET
*Packet
,
807 IN DHCP_PARAMETER
*Para
810 EFI_DHCP4_HEADER
*Head
;
811 EFI_DHCP4_HEADER
*Selected
;
814 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
816 Head
= &Packet
->Dhcp4
.Header
;
817 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
820 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
822 if (DHCP_IS_BOOTP (Para
) ||
823 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
824 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
826 Status
= EFI_SUCCESS
;
831 // Received a NAK, ignore the user's return then terminate the process
833 Status
= EFI_DEVICE_ERROR
;
835 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
836 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
841 // The lease is different from the selected. Don't send a DECLINE
842 // since it isn't existed in the client's FSM.
844 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
848 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
850 if (EFI_ERROR (Status
)) {
855 // Record the lease, start timer for T1 and T2,
857 DhcpComputeLease (DhcpSb
, Para
);
858 DhcpSb
->LeaseLife
= 0;
859 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
861 if (DhcpSb
->ExtraRefresh
) {
862 DhcpSb
->ExtraRefresh
= FALSE
;
864 DhcpSb
->IoStatus
= EFI_SUCCESS
;
865 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
869 gBS
->FreePool (Packet
);
875 Handle packets in DHCP reboot state.
877 @param DhcpSb The DHCP service instance
878 @param Packet The DHCP packet received
879 @param Para The DHCP parameter extracted from the packet. That
880 is, all the option value that we care.
882 @retval EFI_SUCCESS The packet is successfully processed.
883 @retval Others Some error occured.
889 IN DHCP_SERVICE
*DhcpSb
,
890 IN EFI_DHCP4_PACKET
*Packet
,
891 IN DHCP_PARAMETER
*Para
894 EFI_DHCP4_HEADER
*Head
;
897 Head
= &Packet
->Dhcp4
.Header
;
900 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
902 if (DHCP_IS_BOOTP (Para
) ||
903 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
905 Status
= EFI_SUCCESS
;
910 // If a NAK is received, transit to INIT and try again.
912 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
913 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
915 DhcpSb
->ClientAddr
= 0;
916 DhcpSb
->DhcpState
= Dhcp4Init
;
918 Status
= DhcpInitRequest (DhcpSb
);
923 // Check whether the ACK matches the selected offer
925 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
926 Status
= EFI_DEVICE_ERROR
;
930 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
931 if (EFI_ERROR (Status
)) {
936 // OK, get the parameter from server, record the lease
938 DhcpSb
->Para
= AllocatePool (sizeof (DHCP_PARAMETER
));
940 if (DhcpSb
->Para
== NULL
) {
941 Status
= EFI_OUT_OF_RESOURCES
;
945 DhcpSb
->Selected
= Packet
;
946 CopyMem (DhcpSb
->Para
, Para
, sizeof (*DhcpSb
->Para
));
948 Status
= DhcpLeaseAcquired (DhcpSb
);
950 if (EFI_ERROR (Status
)) {
954 DhcpSb
->IoStatus
= EFI_SUCCESS
;
955 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
959 gBS
->FreePool (Packet
);
965 Handle the received DHCP packets. This function drivers the DHCP
968 @param UdpPacket The UDP packets received.
969 @param Points The local/remote UDP access points
970 @param IoStatus The status of the UDP receive
971 @param Context The opaque parameter to the function.
984 DHCP_SERVICE
*DhcpSb
;
985 EFI_DHCP4_HEADER
*Head
;
986 EFI_DHCP4_PACKET
*Packet
;
987 DHCP_PARAMETER
*Para
;
992 DhcpSb
= (DHCP_SERVICE
*) Context
;
995 // Don't restart receive if error occurs or DHCP is destoried.
997 if (EFI_ERROR (IoStatus
)) {
999 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
1000 NetbufFree (UdpPacket
);
1004 ASSERT (UdpPacket
!= NULL
);
1006 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
1011 // Validate the packet received
1013 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
1018 // Copy the DHCP message to a continuous memory block
1020 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
1021 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
1023 if (Packet
== NULL
) {
1028 Head
= &Packet
->Dhcp4
.Header
;
1029 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1031 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1036 // Is this packet the answer to our packet?
1038 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1039 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1040 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1045 // Validate the options and retrieve the interested options
1048 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1049 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1050 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1056 // Call the handler for each state. The handler should return
1057 // EFI_SUCCESS if the process can go on no matter whether the
1058 // packet is ignored or not. If the return is EFI_ERROR, the
1059 // session will be terminated. Packet's ownership is handled
1060 // over to the handlers. If operation succeeds, the handler
1061 // must notify the user. It isn't necessary to do if EFI_ERROR
1062 // is returned because the DhcpEndSession will notify the user.
1064 Status
= EFI_SUCCESS
;
1066 switch (DhcpSb
->DhcpState
) {
1067 case Dhcp4Selecting
:
1068 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1071 case Dhcp4Requesting
:
1072 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1075 case Dhcp4InitReboot
:
1079 // Ignore the packet in INITREBOOT, INIT and BOUND states
1081 gBS
->FreePool (Packet
);
1082 Status
= EFI_SUCCESS
;
1086 case Dhcp4Rebinding
:
1087 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1090 case Dhcp4Rebooting
:
1091 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1096 gBS
->FreePool (Para
);
1101 if (EFI_ERROR (Status
)) {
1102 NetbufFree (UdpPacket
);
1103 DhcpEndSession (DhcpSb
, Status
);
1108 NetbufFree (UdpPacket
);
1110 if (Packet
!= NULL
) {
1111 gBS
->FreePool (Packet
);
1114 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1116 if (EFI_ERROR (Status
)) {
1117 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1125 @param Arg The packet to release
1135 gBS
->FreePool (Arg
);
1140 Release the net buffer when packet is sent.
1142 @param UdpPacket The UDP packets received.
1143 @param Points The local/remote UDP access points
1144 @param IoStatus The status of the UDP receive
1145 @param Context The opaque parameter to the function.
1154 EFI_STATUS IoStatus
,
1158 NetbufFree (Packet
);
1164 Build and transmit a DHCP message according to the current states.
1165 This function implement the Table 5. of RFC 2131. Always transits
1166 the state (as defined in Figure 5. of the same RFC) before sending
1167 a DHCP message. The table is adjusted accordingly.
1169 @param DhcpSb The DHCP service instance
1170 @param Seed The seed packet which the new packet is based on
1171 @param Para The DHCP parameter of the Seed packet
1172 @param Type The message type to send
1173 @param Msg The human readable message to include in the packet
1176 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1177 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1178 @retval EFI_SUCCESS The message is sent
1183 IN DHCP_SERVICE
*DhcpSb
,
1184 IN EFI_DHCP4_PACKET
*Seed
,
1185 IN DHCP_PARAMETER
*Para
,
1190 EFI_DHCP4_CONFIG_DATA
*Config
;
1191 EFI_DHCP4_PACKET
*Packet
;
1192 EFI_DHCP4_PACKET
*NewPacket
;
1193 EFI_DHCP4_HEADER
*Head
;
1194 EFI_DHCP4_HEADER
*SeedHead
;
1196 UDP_POINTS EndPoint
;
1207 // Allocate a big enough memory block to hold the DHCP packet
1209 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1212 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1215 Packet
= AllocatePool (Len
);
1217 if (Packet
== NULL
) {
1218 return EFI_OUT_OF_RESOURCES
;
1222 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1225 // Fill in the DHCP header fields
1227 Config
= &DhcpSb
->ActiveConfig
;
1231 SeedHead
= &Seed
->Dhcp4
.Header
;
1234 Head
= &Packet
->Dhcp4
.Header
;
1235 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1237 Head
->OpCode
= BOOTP_REQUEST
;
1238 Head
->HwType
= DhcpSb
->HwType
;
1239 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1240 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1241 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1243 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1244 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1247 // Append the DHCP message type
1249 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1250 Buf
= Packet
->Dhcp4
.Option
;
1251 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1254 // Append the serverid option if necessary:
1255 // 1. DHCP decline message
1256 // 2. DHCP release message
1257 // 3. DHCP request to confirm one lease.
1259 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1260 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))) {
1262 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1264 IpAddr
= HTONL (Para
->ServerId
);
1265 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1269 // Append the requested IP option if necessary:
1270 // 1. DHCP request to use the previously allocated address
1271 // 2. DHCP request to confirm one lease
1272 // 3. DHCP decline to decline one lease
1276 if (Type
== DHCP_MSG_REQUEST
) {
1277 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1278 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1280 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1281 ASSERT (SeedHead
!= NULL
);
1282 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1285 } else if (Type
== DHCP_MSG_DECLINE
) {
1286 ASSERT (SeedHead
!= NULL
);
1287 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1291 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1295 // Append the Max Message Length option if it isn't a DECLINE
1296 // or RELEASE to direct the server use large messages instead of
1297 // override the BOOTFILE and SERVER fields in the message head.
1299 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1300 MaxMsg
= HTONS (0xFF00);
1301 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1305 // Append the user's message if it isn't NULL
1308 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1309 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1313 // Append the user configured options
1315 if (DhcpSb
->UserOptionLen
!= 0) {
1316 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1318 // We can't use any option other than the client ID from user
1319 // if it is a DHCP decline or DHCP release .
1321 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1322 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1326 Buf
= DhcpAppendOption (
1328 Config
->OptionList
[Index
]->OpCode
,
1329 Config
->OptionList
[Index
]->Length
,
1330 Config
->OptionList
[Index
]->Data
1335 *(Buf
++) = DHCP_TAG_EOP
;
1336 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1339 // OK, the message is built, call the user to override it.
1341 Status
= EFI_SUCCESS
;
1344 if (Type
== DHCP_MSG_DISCOVER
) {
1345 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1347 } else if (Type
== DHCP_MSG_REQUEST
) {
1348 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1350 } else if (Type
== DHCP_MSG_DECLINE
) {
1351 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1354 if (EFI_ERROR (Status
)) {
1355 gBS
->FreePool (Packet
);
1359 if (NewPacket
!= NULL
) {
1360 gBS
->FreePool (Packet
);
1365 // Save the Client Address will be sent out
1367 CopyMem (&DhcpSb
->ClientAddressSendOut
[0], &Packet
->Dhcp4
.Header
.ClientHwAddr
[0], Packet
->Dhcp4
.Header
.HwAddrLen
);
1371 // Wrap it into a netbuf then send it.
1373 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1374 Frag
.Len
= Packet
->Length
;
1375 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1378 gBS
->FreePool (Packet
);
1379 return EFI_OUT_OF_RESOURCES
;
1383 // Save it as the last sent packet for retransmission
1385 if (DhcpSb
->LastPacket
!= NULL
) {
1386 NetbufFree (DhcpSb
->LastPacket
);
1390 DhcpSb
->LastPacket
= Wrap
;
1391 DhcpSetTransmitTimer (DhcpSb
);
1394 // Broadcast the message, unless we know the server address.
1395 // Use the lease UdpIo port to send the unicast packet.
1397 EndPoint
.RemoteAddr
= 0xffffffff;
1398 EndPoint
.LocalAddr
= 0;
1399 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1400 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1401 UdpIo
= DhcpSb
->UdpIo
;
1403 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1404 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1405 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1406 UdpIo
= DhcpSb
->LeaseIoPort
;
1409 ASSERT (UdpIo
!= NULL
);
1410 Status
= UdpIoSendDatagram (UdpIo
, Wrap
, &EndPoint
, 0, DhcpOnPacketSent
, DhcpSb
);
1412 if (EFI_ERROR (Status
)) {
1414 return EFI_ACCESS_DENIED
;
1422 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1423 will be retransmitted.
1425 @param DhcpSb The DHCP service instance
1427 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1428 @retval EFI_SUCCESS The packet is retransmitted.
1433 IN DHCP_SERVICE
*DhcpSb
1437 UDP_POINTS EndPoint
;
1440 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1443 // Broadcast the message, unless we know the server address.
1445 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1446 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1447 EndPoint
.RemoteAddr
= 0xffffffff;
1448 EndPoint
.LocalAddr
= 0;
1449 UdpIo
= DhcpSb
->UdpIo
;
1451 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1452 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1453 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1454 UdpIo
= DhcpSb
->LeaseIoPort
;
1457 ASSERT (UdpIo
!= NULL
);
1459 NET_GET_REF (DhcpSb
->LastPacket
);
1460 Status
= UdpIoSendDatagram (
1469 if (EFI_ERROR (Status
)) {
1470 NET_PUT_REF (DhcpSb
->LastPacket
);
1471 return EFI_ACCESS_DENIED
;
1479 Each DHCP service has three timer. Two of them are count down timer.
1480 One for the packet retransmission. The other is to collect the offers.
1481 The third timer increaments the lease life which is compared to T1, T2,
1482 and lease to determine the time to renew and rebind the lease.
1483 DhcpOnTimerTick will be called once every second.
1485 @param Event The timer event
1486 @param Context The context, which is the DHCP service instance.
1498 DHCP_SERVICE
*DhcpSb
;
1499 DHCP_PROTOCOL
*Instance
;
1502 DhcpSb
= (DHCP_SERVICE
*) Context
;
1503 Instance
= DhcpSb
->ActiveChild
;
1506 // Check the time to wait offer
1508 if ((DhcpSb
->WaitOffer
> 0) && (--DhcpSb
->WaitOffer
== 0)) {
1510 // OK, offer collection finished, select a offer
1512 ASSERT (DhcpSb
->DhcpState
== Dhcp4Selecting
);
1514 if (DhcpSb
->LastOffer
== NULL
) {
1518 if (EFI_ERROR (DhcpChooseOffer (DhcpSb
))) {
1524 // Check the retransmit timer
1526 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1528 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1530 // Still has another try
1532 DhcpRetransmit (DhcpSb
);
1533 DhcpSetTransmitTimer (DhcpSb
);
1536 if (!DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1541 // Retransmission failed, if the DHCP request is initiated by
1542 // user, adjust the current state according to the lease life.
1543 // Otherwise do nothing to wait the lease to timeout
1545 if (DhcpSb
->ExtraRefresh
) {
1546 Status
= EFI_SUCCESS
;
1548 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1549 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1551 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1552 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1554 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1555 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1562 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1563 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1569 // If an address has been acquired, check whether need to
1570 // refresh or whether it has expired.
1572 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1573 DhcpSb
->LeaseLife
++;
1576 // Don't timeout the lease, only count the life if user is
1577 // requesting extra renew/rebind. Adjust the state after that.
1579 if (DhcpSb
->ExtraRefresh
) {
1583 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1585 // Lease expires, end the session
1589 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1591 // T2 expires, transit to rebinding then send a REQUEST to any server
1593 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1597 Status
= DhcpSendMessage (
1605 if (EFI_ERROR (Status
)) {
1609 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1611 // T1 expires, transit to renewing, then send a REQUEST to the server
1613 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1617 Status
= DhcpSendMessage (
1625 if (EFI_ERROR (Status
)) {
1634 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1635 Instance
->Timeout
--;
1636 if (Instance
->Timeout
== 0) {
1637 PxeDhcpDone (Instance
);
1644 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);