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 NetCopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
363 Ip
= HTONL (DhcpSb
->Netmask
);
364 NetCopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
366 NetZeroMem (&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 NetZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
380 Ip
= HTONL (DhcpSb
->Para
->Router
);
381 NetCopyMem (&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 NetFreePool (DhcpSb
->LastOffer
);
469 DhcpSb
->LastOffer
= NULL
;
472 if (DhcpSb
->Selected
!= NULL
) {
473 NetFreePool (DhcpSb
->Selected
);
474 DhcpSb
->Selected
= NULL
;
477 if (DhcpSb
->Para
!= NULL
) {
478 NetFreePool (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
*) NetAllocatePool (NewPacket
->Size
);
549 if (TempPacket
!= NULL
) {
550 NetCopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
551 NetFreePool (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
642 EFI_DHCP4_HEADER
*Head
;
645 Status
= EFI_SUCCESS
;
648 // First validate the message:
649 // 1. the offer is a unicast
650 // 2. if it is a DHCP message, it must contains a server ID.
651 // Don't return a error for these two case otherwise the session is ended.
653 Head
= &Packet
->Dhcp4
.Header
;
655 if (!DHCP_IS_BOOTP (Para
) &&
656 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))) {
661 // Call the user's callback. The action according to the return is as:
662 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
663 // 2. EFI_NOT_READY: wait for more offers
664 // 3. EFI_ABORTED: abort the address acquiring.
666 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
668 if (Status
== EFI_SUCCESS
) {
669 if (DhcpSb
->LastOffer
!= NULL
) {
670 NetFreePool (DhcpSb
->LastOffer
);
673 DhcpSb
->LastOffer
= Packet
;
675 return DhcpChooseOffer (DhcpSb
);
677 } else if (Status
== EFI_NOT_READY
) {
678 if (DhcpSb
->LastOffer
!= NULL
) {
679 NetFreePool (DhcpSb
->LastOffer
);
682 DhcpSb
->LastOffer
= Packet
;
684 } else if (Status
== EFI_ABORTED
) {
686 // DhcpInput will end the session upon error return. Remember
687 // only to call DhcpEndSession at the top level call.
695 NetFreePool (Packet
);
701 Handle packets in DHCP request state.
703 @param DhcpSb The DHCP service instance
704 @param Packet The DHCP packet received
705 @param Para The DHCP parameter extracted from the packet. That
706 is, all the option value that we care.
708 @retval EFI_SUCCESS The packet is successfully processed.
709 @retval Others Some error occured.
715 IN DHCP_SERVICE
*DhcpSb
,
716 IN EFI_DHCP4_PACKET
*Packet
,
717 IN DHCP_PARAMETER
*Para
720 EFI_DHCP4_HEADER
*Head
;
721 EFI_DHCP4_HEADER
*Selected
;
725 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
727 Head
= &Packet
->Dhcp4
.Header
;
728 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
731 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
733 if (DHCP_IS_BOOTP (Para
) ||
734 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
735 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
737 Status
= EFI_SUCCESS
;
742 // Received a NAK, end the session no matter what the user returns
744 Status
= EFI_DEVICE_ERROR
;
746 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
747 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
752 // Check whether the ACK matches the selected offer
756 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
757 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
761 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
763 if (EFI_ERROR (Status
)) {
764 Message
= (UINT8
*) "Lease is denied upon received ACK";
769 // Record the lease, transit to BOUND state, then notify the user
771 Status
= DhcpLeaseAcquired (DhcpSb
);
773 if (EFI_ERROR (Status
)) {
774 Message
= (UINT8
*) "Lease is denied upon entering bound";
778 DhcpSb
->IoStatus
= EFI_SUCCESS
;
779 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
781 NetFreePool (Packet
);
785 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
788 NetFreePool (Packet
);
794 Handle packets in DHCP renew/rebound state.
796 @param DhcpSb The DHCP service instance
797 @param Packet The DHCP packet received
798 @param Para The DHCP parameter extracted from the packet. That
799 is, all the option value that we care.
801 @retval EFI_SUCCESS The packet is successfully processed.
802 @retval Others Some error occured.
807 DhcpHandleRenewRebind (
808 IN DHCP_SERVICE
*DhcpSb
,
809 IN EFI_DHCP4_PACKET
*Packet
,
810 IN DHCP_PARAMETER
*Para
813 EFI_DHCP4_HEADER
*Head
;
814 EFI_DHCP4_HEADER
*Selected
;
817 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
819 Head
= &Packet
->Dhcp4
.Header
;
820 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
823 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
825 if (DHCP_IS_BOOTP (Para
) ||
826 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
827 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
829 Status
= EFI_SUCCESS
;
834 // Received a NAK, ignore the user's return then terminate the process
836 Status
= EFI_DEVICE_ERROR
;
838 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
839 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
844 // The lease is different from the selected. Don't send a DECLINE
845 // since it isn't existed in the client's FSM.
847 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
851 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
853 if (EFI_ERROR (Status
)) {
858 // Record the lease, start timer for T1 and T2,
860 DhcpComputeLease (DhcpSb
, Para
);
861 DhcpSb
->LeaseLife
= 0;
862 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
864 if (DhcpSb
->ExtraRefresh
) {
865 DhcpSb
->ExtraRefresh
= FALSE
;
867 DhcpSb
->IoStatus
= EFI_SUCCESS
;
868 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
872 NetFreePool (Packet
);
878 Handle packets in DHCP reboot state.
880 @param DhcpSb The DHCP service instance
881 @param Packet The DHCP packet received
882 @param Para The DHCP parameter extracted from the packet. That
883 is, all the option value that we care.
885 @retval EFI_SUCCESS The packet is successfully processed.
886 @retval Others Some error occured.
892 IN DHCP_SERVICE
*DhcpSb
,
893 IN EFI_DHCP4_PACKET
*Packet
,
894 IN DHCP_PARAMETER
*Para
897 EFI_DHCP4_HEADER
*Head
;
900 Head
= &Packet
->Dhcp4
.Header
;
903 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
905 if (DHCP_IS_BOOTP (Para
) ||
906 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
908 Status
= EFI_SUCCESS
;
913 // If a NAK is received, transit to INIT and try again.
915 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
916 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
918 DhcpSb
->ClientAddr
= 0;
919 DhcpSb
->DhcpState
= Dhcp4Init
;
921 Status
= DhcpInitRequest (DhcpSb
);
926 // Check whether the ACK matches the selected offer
928 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
929 Status
= EFI_DEVICE_ERROR
;
933 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
934 if (EFI_ERROR (Status
)) {
939 // OK, get the parameter from server, record the lease
941 DhcpSb
->Para
= NetAllocatePool (sizeof (DHCP_PARAMETER
));
943 if (DhcpSb
->Para
== NULL
) {
944 Status
= EFI_OUT_OF_RESOURCES
;
948 DhcpSb
->Selected
= Packet
;
949 CopyMem (DhcpSb
->Para
, Para
, sizeof (*DhcpSb
->Para
));
951 Status
= DhcpLeaseAcquired (DhcpSb
);
953 if (EFI_ERROR (Status
)) {
957 DhcpSb
->IoStatus
= EFI_SUCCESS
;
958 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
962 NetFreePool (Packet
);
968 Handle the received DHCP packets. This function drivers the DHCP
971 @param UdpPacket The UDP packets received.
972 @param Points The local/remote UDP access points
973 @param IoStatus The status of the UDP receive
974 @param Context The opaque parameter to the function.
987 DHCP_SERVICE
*DhcpSb
;
988 EFI_DHCP4_HEADER
*Head
;
989 EFI_DHCP4_PACKET
*Packet
;
990 DHCP_PARAMETER
*Para
;
995 DhcpSb
= (DHCP_SERVICE
*) Context
;
998 // Don't restart receive if error occurs or DHCP is destoried.
1000 if (EFI_ERROR (IoStatus
)) {
1002 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
1003 NetbufFree (UdpPacket
);
1007 ASSERT (UdpPacket
!= NULL
);
1009 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
1014 // Validate the packet received
1016 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
1021 // Copy the DHCP message to a continuous memory block
1023 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
1024 Packet
= (EFI_DHCP4_PACKET
*) NetAllocatePool (Len
);
1026 if (Packet
== NULL
) {
1031 Head
= &Packet
->Dhcp4
.Header
;
1032 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1034 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1039 // Is this packet the answer to our packet?
1041 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1042 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1043 !NET_MAC_EQUAL (&DhcpSb
->Mac
, Head
->ClientHwAddr
, DhcpSb
->HwLen
)) {
1048 // Validate the options and retrieve the interested options
1051 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1052 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1053 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1059 // Call the handler for each state. The handler should return
1060 // EFI_SUCCESS if the process can go on no matter whether the
1061 // packet is ignored or not. If the return is EFI_ERROR, the
1062 // session will be terminated. Packet's ownership is handled
1063 // over to the handlers. If operation succeeds, the handler
1064 // must notify the user. It isn't necessary to do if EFI_ERROR
1065 // is returned because the DhcpEndSession will notify the user.
1067 Status
= EFI_SUCCESS
;
1069 switch (DhcpSb
->DhcpState
) {
1070 case Dhcp4Selecting
:
1071 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1074 case Dhcp4Requesting
:
1075 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1078 case Dhcp4InitReboot
:
1082 // Ignore the packet in INITREBOOT, INIT and BOUND states
1084 NetFreePool (Packet
);
1085 Status
= EFI_SUCCESS
;
1089 case Dhcp4Rebinding
:
1090 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1093 case Dhcp4Rebooting
:
1094 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1104 if (EFI_ERROR (Status
)) {
1105 NetbufFree (UdpPacket
);
1106 DhcpEndSession (DhcpSb
, Status
);
1111 NetbufFree (UdpPacket
);
1113 if (Packet
!= NULL
) {
1114 NetFreePool (Packet
);
1117 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1119 if (EFI_ERROR (Status
)) {
1120 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1128 @param Arg The packet to release
1143 Release the net buffer when packet is sent.
1145 @param UdpPacket The UDP packets received.
1146 @param Points The local/remote UDP access points
1147 @param IoStatus The status of the UDP receive
1148 @param Context The opaque parameter to the function.
1157 EFI_STATUS IoStatus
,
1161 NetbufFree (Packet
);
1167 Build and transmit a DHCP message according to the current states.
1168 This function implement the Table 5. of RFC 2131. Always transits
1169 the state (as defined in Figure 5. of the same RFC) before sending
1170 a DHCP message. The table is adjusted accordingly.
1172 @param DhcpSb The DHCP service instance
1173 @param Seed The seed packet which the new packet is based on
1174 @param Para The DHCP parameter of the Seed packet
1175 @param Type The message type to send
1176 @param Msg The human readable message to include in the packet
1179 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1180 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1181 @retval EFI_SUCCESS The message is sent
1186 IN DHCP_SERVICE
*DhcpSb
,
1187 IN EFI_DHCP4_PACKET
*Seed
,
1188 IN DHCP_PARAMETER
*Para
,
1193 EFI_DHCP4_CONFIG_DATA
*Config
;
1194 EFI_DHCP4_PACKET
*Packet
;
1195 EFI_DHCP4_PACKET
*NewPacket
;
1196 EFI_DHCP4_HEADER
*Head
;
1197 EFI_DHCP4_HEADER
*SeedHead
;
1199 UDP_POINTS EndPoint
;
1210 // Allocate a big enough memory block to hold the DHCP packet
1212 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1215 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1218 Packet
= NetAllocatePool (Len
);
1220 if (Packet
== NULL
) {
1221 return EFI_OUT_OF_RESOURCES
;
1225 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1228 // Fill in the DHCP header fields
1230 Config
= &DhcpSb
->ActiveConfig
;
1234 SeedHead
= &Seed
->Dhcp4
.Header
;
1237 Head
= &Packet
->Dhcp4
.Header
;
1238 NetZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1240 Head
->OpCode
= BOOTP_REQUEST
;
1241 Head
->HwType
= DhcpSb
->HwType
;
1242 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1243 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1244 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1246 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1247 NetCopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1250 // Append the DHCP message type
1252 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1253 Buf
= Packet
->Dhcp4
.Option
;
1254 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1257 // Append the serverid option if necessary:
1258 // 1. DHCP decline message
1259 // 2. DHCP release message
1260 // 3. DHCP request to confirm one lease.
1262 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1263 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))) {
1265 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1267 IpAddr
= HTONL (Para
->ServerId
);
1268 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1272 // Append the requested IP option if necessary:
1273 // 1. DHCP request to use the previously allocated address
1274 // 2. DHCP request to confirm one lease
1275 // 3. DHCP decline to decline one lease
1279 if (Type
== DHCP_MSG_REQUEST
) {
1280 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1281 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1283 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1284 ASSERT (SeedHead
!= NULL
);
1285 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1288 } else if (Type
== DHCP_MSG_DECLINE
) {
1289 ASSERT (SeedHead
!= NULL
);
1290 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1294 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1298 // Append the Max Message Length option if it isn't a DECLINE
1299 // or RELEASE to direct the server use large messages instead of
1300 // override the BOOTFILE and SERVER fields in the message head.
1302 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1303 MaxMsg
= HTONS (0xFF00);
1304 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1308 // Append the user's message if it isn't NULL
1311 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1312 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1316 // Append the user configured options
1318 if (DhcpSb
->UserOptionLen
!= 0) {
1319 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1321 // We can't use any option other than the client ID from user
1322 // if it is a DHCP decline or DHCP release .
1324 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1325 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1329 Buf
= DhcpAppendOption (
1331 Config
->OptionList
[Index
]->OpCode
,
1332 Config
->OptionList
[Index
]->Length
,
1333 Config
->OptionList
[Index
]->Data
1338 *(Buf
++) = DHCP_TAG_EOP
;
1339 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1342 // OK, the message is built, call the user to override it.
1344 Status
= EFI_SUCCESS
;
1347 if (Type
== DHCP_MSG_DISCOVER
) {
1348 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1350 } else if (Type
== DHCP_MSG_REQUEST
) {
1351 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1353 } else if (Type
== DHCP_MSG_DECLINE
) {
1354 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1357 if (EFI_ERROR (Status
)) {
1358 NetFreePool (Packet
);
1362 if (NewPacket
!= NULL
) {
1363 NetFreePool (Packet
);
1368 // Wrap it into a netbuf then send it.
1370 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1371 Frag
.Len
= Packet
->Length
;
1372 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1375 NetFreePool (Packet
);
1376 return EFI_OUT_OF_RESOURCES
;
1380 // Save it as the last sent packet for retransmission
1382 if (DhcpSb
->LastPacket
!= NULL
) {
1383 NetbufFree (DhcpSb
->LastPacket
);
1387 DhcpSb
->LastPacket
= Wrap
;
1388 DhcpSetTransmitTimer (DhcpSb
);
1391 // Broadcast the message, unless we know the server address.
1392 // Use the lease UdpIo port to send the unicast packet.
1394 EndPoint
.RemoteAddr
= 0xffffffff;
1395 EndPoint
.LocalAddr
= 0;
1396 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1397 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1398 UdpIo
= DhcpSb
->UdpIo
;
1400 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1401 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1402 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1403 UdpIo
= DhcpSb
->LeaseIoPort
;
1406 ASSERT (UdpIo
!= NULL
);
1407 Status
= UdpIoSendDatagram (UdpIo
, Wrap
, &EndPoint
, 0, DhcpOnPacketSent
, DhcpSb
);
1409 if (EFI_ERROR (Status
)) {
1411 return EFI_ACCESS_DENIED
;
1419 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1420 will be retransmitted.
1422 @param DhcpSb The DHCP service instance
1424 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1425 @retval EFI_SUCCESS The packet is retransmitted.
1430 IN DHCP_SERVICE
*DhcpSb
1434 UDP_POINTS EndPoint
;
1437 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1440 // Broadcast the message, unless we know the server address.
1442 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1443 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1444 EndPoint
.RemoteAddr
= 0xffffffff;
1445 EndPoint
.LocalAddr
= 0;
1446 UdpIo
= DhcpSb
->UdpIo
;
1448 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1449 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1450 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1451 UdpIo
= DhcpSb
->LeaseIoPort
;
1454 ASSERT (UdpIo
!= NULL
);
1456 NET_GET_REF (DhcpSb
->LastPacket
);
1457 Status
= UdpIoSendDatagram (
1466 if (EFI_ERROR (Status
)) {
1467 NET_PUT_REF (DhcpSb
->LastPacket
);
1468 return EFI_ACCESS_DENIED
;
1476 Each DHCP service has three timer. Two of them are count down timer.
1477 One for the packet retransmission. The other is to collect the offers.
1478 The third timer increaments the lease life which is compared to T1, T2,
1479 and lease to determine the time to renew and rebind the lease.
1480 DhcpOnTimerTick will be called once every second.
1482 @param Event The timer event
1483 @param Context The context, which is the DHCP service instance.
1495 DHCP_SERVICE
*DhcpSb
;
1496 DHCP_PROTOCOL
*Instance
;
1499 DhcpSb
= (DHCP_SERVICE
*) Context
;
1500 Instance
= DhcpSb
->ActiveChild
;
1503 // Check the time to wait offer
1505 if ((DhcpSb
->WaitOffer
> 0) && (--DhcpSb
->WaitOffer
== 0)) {
1507 // OK, offer collection finished, select a offer
1509 ASSERT (DhcpSb
->DhcpState
== Dhcp4Selecting
);
1511 if (DhcpSb
->LastOffer
== NULL
) {
1515 if (EFI_ERROR (DhcpChooseOffer (DhcpSb
))) {
1521 // Check the retransmit timer
1523 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1525 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1527 // Still has another try
1529 DhcpRetransmit (DhcpSb
);
1530 DhcpSetTransmitTimer (DhcpSb
);
1533 if (!DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1538 // Retransmission failed, if the DHCP request is initiated by
1539 // user, adjust the current state according to the lease life.
1540 // Otherwise do nothing to wait the lease to timeout
1542 if (DhcpSb
->ExtraRefresh
) {
1543 Status
= EFI_SUCCESS
;
1545 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1546 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1548 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1549 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1551 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1552 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1559 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1560 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1566 // If an address has been acquired, check whether need to
1567 // refresh or whether it has expired.
1569 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1570 DhcpSb
->LeaseLife
++;
1573 // Don't timeout the lease, only count the life if user is
1574 // requesting extra renew/rebind. Adjust the state after that.
1576 if (DhcpSb
->ExtraRefresh
) {
1580 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1582 // Lease expires, end the session
1586 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1588 // T2 expires, transit to rebinding then send a REQUEST to any server
1590 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1594 Status
= DhcpSendMessage (
1602 if (EFI_ERROR (Status
)) {
1606 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1608 // T1 expires, transit to renewing, then send a REQUEST to the server
1610 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1614 Status
= DhcpSendMessage (
1622 if (EFI_ERROR (Status
)) {
1631 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1632 Instance
->Timeout
--;
1633 if (Instance
->Timeout
== 0) {
1634 PxeDhcpDone (Instance
);
1641 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);