2 EFI DHCP protocol implementation.
4 Copyright (c) 2006 - 2008, Intel Corporation.<BR>
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "Dhcp4Impl.h"
18 UINT32 mDhcp4DefaultTimeout
[4] = { 4, 8, 16, 32 };
22 Send an initial DISCOVER or REQUEST message according to the
23 DHCP service's current state.
25 @param[in] DhcpSb The DHCP service instance
27 @retval EFI_SUCCESS The request has been sent
28 @retval other Some error occurs when sending the request.
33 IN DHCP_SERVICE
*DhcpSb
38 ASSERT ((DhcpSb
->DhcpState
== Dhcp4Init
) || (DhcpSb
->DhcpState
== Dhcp4InitReboot
));
40 if (DhcpSb
->DhcpState
== Dhcp4Init
) {
41 DhcpSetState (DhcpSb
, Dhcp4Selecting
, FALSE
);
42 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_DISCOVER
, NULL
);
44 if (EFI_ERROR (Status
)) {
45 DhcpSb
->DhcpState
= Dhcp4Init
;
49 DhcpSetState (DhcpSb
, Dhcp4Rebooting
, FALSE
);
50 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_REQUEST
, NULL
);
52 if (EFI_ERROR (Status
)) {
53 DhcpSb
->DhcpState
= Dhcp4InitReboot
;
63 Call user provided callback function, and return the value the
64 function returns. If the user doesn't provide a callback, a
65 proper return value is selected to let the caller continue the
68 @param[in] DhcpSb The DHCP service instance
69 @param[in] Event The event as defined in the spec
70 @param[in] Packet The current packet trigger the event
71 @param[out] NewPacket The user's return new packet
73 @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
74 @retval EFI_SUCCESS The user function returns success.
75 @retval EFI_ABORTED The user function ask it to abort.
80 IN DHCP_SERVICE
*DhcpSb
,
81 IN EFI_DHCP4_EVENT Event
,
82 IN EFI_DHCP4_PACKET
*Packet
, OPTIONAL
83 OUT EFI_DHCP4_PACKET
**NewPacket OPTIONAL
86 EFI_DHCP4_CONFIG_DATA
*Config
;
89 if (NewPacket
!= NULL
) {
94 // If user doesn't provide the call back function, return the value
95 // that directs the client to continue the normal process.
96 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
97 // the offers and select a offer, EFI_NOT_READY tells the client to
98 // collect more offers.
100 Config
= &DhcpSb
->ActiveConfig
;
102 if (Config
->Dhcp4Callback
== NULL
) {
103 if (Event
== Dhcp4RcvdOffer
) {
104 return EFI_NOT_READY
;
110 Status
= Config
->Dhcp4Callback (
111 &DhcpSb
->ActiveChild
->Dhcp4Protocol
,
112 Config
->CallbackContext
,
113 (EFI_DHCP4_STATE
) DhcpSb
->DhcpState
,
120 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
121 // and EFI_ABORTED. If it returns values other than those, assume
122 // it to be EFI_ABORTED.
124 if ((Status
== EFI_SUCCESS
) || (Status
== EFI_NOT_READY
)) {
133 Notify the user about the operation result.
135 @param DhcpSb DHCP service instance
136 @param Which Which notify function to signal
141 IN DHCP_SERVICE
*DhcpSb
,
145 DHCP_PROTOCOL
*Child
;
147 if ((Child
= DhcpSb
->ActiveChild
) == NULL
) {
151 if ((Child
->CompletionEvent
!= NULL
) &&
152 ((Which
== DHCP_NOTIFY_COMPLETION
) || (Which
== DHCP_NOTIFY_ALL
))
155 gBS
->SignalEvent (Child
->CompletionEvent
);
156 Child
->CompletionEvent
= NULL
;
159 if ((Child
->RenewRebindEvent
!= NULL
) &&
160 ((Which
== DHCP_NOTIFY_RENEWREBIND
) || (Which
== DHCP_NOTIFY_ALL
))
163 gBS
->SignalEvent (Child
->RenewRebindEvent
);
164 Child
->RenewRebindEvent
= NULL
;
171 Set the DHCP state. If CallUser is true, it will try to notify
172 the user before change the state by DhcpNotifyUser. It returns
173 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
174 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
175 the return value of this function.
177 @param DhcpSb The DHCP service instance
178 @param State The new DHCP state to change to
179 @param CallUser Whether we need to call user
181 @retval EFI_SUCCESS The state is changed
182 @retval EFI_ABORTED The user asks to abort the DHCP process.
187 IN OUT DHCP_SERVICE
*DhcpSb
,
195 Status
= EFI_SUCCESS
;
197 if (State
== Dhcp4Renewing
) {
198 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRenewing
, NULL
, NULL
);
200 } else if (State
== Dhcp4Rebinding
) {
201 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRebinding
, NULL
, NULL
);
203 } else if (State
== Dhcp4Bound
) {
204 Status
= DhcpCallUser (DhcpSb
, Dhcp4BoundCompleted
, NULL
, NULL
);
208 if (EFI_ERROR (Status
)) {
214 // Update the retransmission timer during the state transition.
215 // This will clear the retry count. This is also why the rule
216 // first transit the state, then send packets.
218 if (State
== Dhcp4Selecting
) {
219 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.DiscoverTryCount
;
221 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.RequestTryCount
;
224 if (DhcpSb
->MaxRetries
== 0) {
225 DhcpSb
->MaxRetries
= 4;
228 DhcpSb
->CurRetry
= 0;
229 DhcpSb
->PacketToLive
= 0;
231 DhcpSb
->DhcpState
= State
;
237 Set the retransmit timer for the packet. It will select from either
238 the discover timeouts/request timeouts or the default timeout values.
240 @param DhcpSb The DHCP service instance.
244 DhcpSetTransmitTimer (
245 IN OUT DHCP_SERVICE
*DhcpSb
250 ASSERT (DhcpSb
->MaxRetries
> DhcpSb
->CurRetry
);
252 if (DhcpSb
->DhcpState
== Dhcp4Selecting
) {
253 Times
= DhcpSb
->ActiveConfig
.DiscoverTimeout
;
255 Times
= DhcpSb
->ActiveConfig
.RequestTimeout
;
259 Times
= mDhcp4DefaultTimeout
;
262 DhcpSb
->PacketToLive
= Times
[DhcpSb
->CurRetry
];
268 Compute the lease. If the server grants a permanent lease, just
269 process it as a normal timeout value since the lease will last
272 @param DhcpSb The DHCP service instance
273 @param Para The DHCP parameter extracted from the server's
278 IN OUT DHCP_SERVICE
*DhcpSb
,
279 IN DHCP_PARAMETER
*Para
282 ASSERT (Para
!= NULL
);
284 DhcpSb
->Lease
= Para
->Lease
;
285 DhcpSb
->T2
= Para
->T2
;
286 DhcpSb
->T1
= Para
->T1
;
288 if (DhcpSb
->Lease
== 0) {
289 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
292 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
293 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
296 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
297 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
303 Configure a UDP IO port to use the acquired lease address.
304 DHCP driver needs this port to unicast packet to the server
305 such as DHCP release.
307 @param[in] UdpIo The UDP IO port to configure
308 @param[in] Context Dhcp service instance.
310 @retval EFI_SUCCESS The UDP IO port is successfully configured.
311 @retval Others It failed to configure the port.
315 DhcpConfigLeaseIoPort (
316 IN UDP_IO_PORT
*UdpIo
,
320 EFI_UDP4_CONFIG_DATA UdpConfigData
;
321 EFI_IPv4_ADDRESS Subnet
;
322 EFI_IPv4_ADDRESS Gateway
;
323 DHCP_SERVICE
*DhcpSb
;
327 DhcpSb
= (DHCP_SERVICE
*) Context
;
329 UdpConfigData
.AcceptBroadcast
= FALSE
;
330 UdpConfigData
.AcceptPromiscuous
= FALSE
;
331 UdpConfigData
.AcceptAnyPort
= FALSE
;
332 UdpConfigData
.AllowDuplicatePort
= TRUE
;
333 UdpConfigData
.TypeOfService
= 0;
334 UdpConfigData
.TimeToLive
= 64;
335 UdpConfigData
.DoNotFragment
= FALSE
;
336 UdpConfigData
.ReceiveTimeout
= 1;
337 UdpConfigData
.TransmitTimeout
= 0;
339 UdpConfigData
.UseDefaultAddress
= FALSE
;
340 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
341 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
343 Ip
= HTONL (DhcpSb
->ClientAddr
);
344 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
346 Ip
= HTONL (DhcpSb
->Netmask
);
347 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
349 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
351 Status
= UdpIo
->Udp
->Configure (UdpIo
->Udp
, &UdpConfigData
);
353 if (EFI_ERROR (Status
)) {
358 // Add a default route if received from the server.
360 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
361 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
363 Ip
= HTONL (DhcpSb
->Para
->Router
);
364 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
366 UdpIo
->Udp
->Routes (UdpIo
->Udp
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
374 Update the lease states when a new lease is acquired. It will not only
375 save the acquired the address and lease time, it will also create a UDP
376 child to provide address resolution for the address.
378 @param DhcpSb The DHCP service instance
380 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
381 @retval EFI_SUCCESS The lease is recorded.
386 IN OUT DHCP_SERVICE
*DhcpSb
391 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
393 if (DhcpSb
->Para
!= NULL
) {
394 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
395 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
398 if (DhcpSb
->Netmask
== 0) {
399 Class
= NetGetIpClass (DhcpSb
->ClientAddr
);
400 DhcpSb
->Netmask
= gIp4AllMasks
[Class
<< 3];
403 if (DhcpSb
->LeaseIoPort
!= NULL
) {
404 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
408 // Create a UDP/IP child to provide ARP service for the Leased IP,
409 // and transmit unicast packet with it as source address. Don't
410 // start receive on this port, the queued packet will be timeout.
412 DhcpSb
->LeaseIoPort
= UdpIoCreatePort (
415 DhcpConfigLeaseIoPort
,
419 if (DhcpSb
->LeaseIoPort
== NULL
) {
420 return EFI_OUT_OF_RESOURCES
;
423 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
424 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
427 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
432 Clean up the DHCP related states, IoStatus isn't reset.
434 @param DhcpSb The DHCP instance service.
439 IN DHCP_SERVICE
*DhcpSb
442 DhcpSb
->DhcpState
= Dhcp4Init
;
443 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
444 DhcpSb
->ClientAddr
= 0;
445 DhcpSb
->ServerAddr
= 0;
447 if (DhcpSb
->LastOffer
!= NULL
) {
448 gBS
->FreePool (DhcpSb
->LastOffer
);
449 DhcpSb
->LastOffer
= NULL
;
452 if (DhcpSb
->Selected
!= NULL
) {
453 gBS
->FreePool (DhcpSb
->Selected
);
454 DhcpSb
->Selected
= NULL
;
457 if (DhcpSb
->Para
!= NULL
) {
458 gBS
->FreePool (DhcpSb
->Para
);
465 DhcpSb
->ExtraRefresh
= FALSE
;
467 if (DhcpSb
->LeaseIoPort
!= NULL
) {
468 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
469 DhcpSb
->LeaseIoPort
= NULL
;
472 if (DhcpSb
->LastPacket
!= NULL
) {
473 NetbufFree (DhcpSb
->LastPacket
);
474 DhcpSb
->LastPacket
= NULL
;
477 DhcpSb
->PacketToLive
= 0;
478 DhcpSb
->CurRetry
= 0;
479 DhcpSb
->MaxRetries
= 0;
480 DhcpSb
->LeaseLife
= 0;
485 Select a offer among all the offers collected. If the offer selected is
486 of BOOTP, the lease is recorded and user notified. If the offer is of
487 DHCP, it will request the offer from the server.
489 @param[in] DhcpSb The DHCP service instance.
491 @retval EFI_SUCCESS One of the offer is selected.
496 IN DHCP_SERVICE
*DhcpSb
499 EFI_DHCP4_PACKET
*Selected
;
500 EFI_DHCP4_PACKET
*NewPacket
;
501 EFI_DHCP4_PACKET
*TempPacket
;
504 ASSERT (DhcpSb
->LastOffer
!= NULL
);
507 // User will cache previous offers if he wants to select
508 // from multiple offers. If user provides an invalid packet,
509 // use the last offer, otherwise use the provided packet.
512 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
514 if (EFI_ERROR (Status
)) {
518 Selected
= DhcpSb
->LastOffer
;
520 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
521 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
522 if (TempPacket
!= NULL
) {
523 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
524 gBS
->FreePool (Selected
);
525 Selected
= TempPacket
;
529 DhcpSb
->Selected
= Selected
;
530 DhcpSb
->LastOffer
= NULL
;
532 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
535 // A bootp offer has been selected, save the lease status,
536 // enter bound state then notify the user.
538 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
539 Status
= DhcpLeaseAcquired (DhcpSb
);
541 if (EFI_ERROR (Status
)) {
545 DhcpSb
->IoStatus
= EFI_SUCCESS
;
546 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
551 // Send a DHCP requests
553 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
555 if (EFI_ERROR (Status
)) {
559 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
564 Terminate the current address acquire. All the allocated resources
565 are released. Be careful when calling this function. A rule related
566 to this is: only call DhcpEndSession at the highest level, such as
567 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
569 @param[in] DhcpSb The DHCP service instance
570 @param[in] Status The result of the DHCP process.
575 IN DHCP_SERVICE
*DhcpSb
,
579 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
580 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
582 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
585 DhcpCleanLease (DhcpSb
);
587 DhcpSb
->IoStatus
= Status
;
588 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
593 Handle packets in DHCP select state.
595 @param[in] DhcpSb The DHCP service instance
596 @param[in] Packet The DHCP packet received
597 @param[in] Para The DHCP parameter extracted from the packet. That
598 is, all the option value that we care.
600 @retval EFI_SUCCESS The packet is successfully processed.
601 @retval Others Some error occured.
606 IN DHCP_SERVICE
*DhcpSb
,
607 IN EFI_DHCP4_PACKET
*Packet
,
608 IN DHCP_PARAMETER
*Para
613 Status
= EFI_SUCCESS
;
616 // First validate the message:
617 // 1. the offer is a unicast
618 // 2. if it is a DHCP message, it must contains a server ID.
619 // Don't return a error for these two case otherwise the session is ended.
621 if (!DHCP_IS_BOOTP (Para
) &&
622 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))
628 // Call the user's callback. The action according to the return is as:
629 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
630 // 2. EFI_NOT_READY: wait for more offers
631 // 3. EFI_ABORTED: abort the address acquiring.
633 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
635 if (Status
== EFI_SUCCESS
) {
636 if (DhcpSb
->LastOffer
!= NULL
) {
637 gBS
->FreePool (DhcpSb
->LastOffer
);
640 DhcpSb
->LastOffer
= Packet
;
642 return DhcpChooseOffer (DhcpSb
);
644 } else if (Status
== EFI_NOT_READY
) {
645 if (DhcpSb
->LastOffer
!= NULL
) {
646 gBS
->FreePool (DhcpSb
->LastOffer
);
649 DhcpSb
->LastOffer
= Packet
;
651 } else if (Status
== EFI_ABORTED
) {
653 // DhcpInput will end the session upon error return. Remember
654 // only to call DhcpEndSession at the top level call.
662 gBS
->FreePool (Packet
);
668 Handle packets in DHCP request state.
670 @param[in] DhcpSb The DHCP service instance
671 @param[in] Packet The DHCP packet received
672 @param[in] Para The DHCP parameter extracted from the packet. That
673 is, all the option value that we care.
675 @retval EFI_SUCCESS The packet is successfully processed.
676 @retval Others Some error occured.
681 IN DHCP_SERVICE
*DhcpSb
,
682 IN EFI_DHCP4_PACKET
*Packet
,
683 IN DHCP_PARAMETER
*Para
686 EFI_DHCP4_HEADER
*Head
;
687 EFI_DHCP4_HEADER
*Selected
;
691 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
693 Head
= &Packet
->Dhcp4
.Header
;
694 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
697 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
699 if (DHCP_IS_BOOTP (Para
) ||
700 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
701 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
704 Status
= EFI_SUCCESS
;
709 // Received a NAK, end the session no matter what the user returns
711 Status
= EFI_DEVICE_ERROR
;
713 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
714 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
719 // Check whether the ACK matches the selected offer
723 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
724 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
728 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
730 if (EFI_ERROR (Status
)) {
731 Message
= (UINT8
*) "Lease is denied upon received ACK";
736 // Record the lease, transit to BOUND state, then notify the user
738 Status
= DhcpLeaseAcquired (DhcpSb
);
740 if (EFI_ERROR (Status
)) {
741 Message
= (UINT8
*) "Lease is denied upon entering bound";
745 DhcpSb
->IoStatus
= EFI_SUCCESS
;
746 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
748 gBS
->FreePool (Packet
);
752 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
755 gBS
->FreePool (Packet
);
761 Handle packets in DHCP renew/rebound state.
763 @param[in] DhcpSb The DHCP service instance
764 @param[in] Packet The DHCP packet received
765 @param[in] Para The DHCP parameter extracted from the packet. That
766 is, all the option value that we care.
768 @retval EFI_SUCCESS The packet is successfully processed.
769 @retval Others Some error occured.
773 DhcpHandleRenewRebind (
774 IN DHCP_SERVICE
*DhcpSb
,
775 IN EFI_DHCP4_PACKET
*Packet
,
776 IN DHCP_PARAMETER
*Para
779 EFI_DHCP4_HEADER
*Head
;
780 EFI_DHCP4_HEADER
*Selected
;
783 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
785 Head
= &Packet
->Dhcp4
.Header
;
786 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
789 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
791 if (DHCP_IS_BOOTP (Para
) ||
792 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
793 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
796 Status
= EFI_SUCCESS
;
801 // Received a NAK, ignore the user's return then terminate the process
803 Status
= EFI_DEVICE_ERROR
;
805 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
806 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
811 // The lease is different from the selected. Don't send a DECLINE
812 // since it isn't existed in the client's FSM.
814 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
818 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
820 if (EFI_ERROR (Status
)) {
825 // Record the lease, start timer for T1 and T2,
827 DhcpComputeLease (DhcpSb
, Para
);
828 DhcpSb
->LeaseLife
= 0;
829 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
831 if (DhcpSb
->ExtraRefresh
!= 0) {
832 DhcpSb
->ExtraRefresh
= FALSE
;
834 DhcpSb
->IoStatus
= EFI_SUCCESS
;
835 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
839 gBS
->FreePool (Packet
);
845 Handle packets in DHCP reboot state.
847 @param[in] DhcpSb The DHCP service instance
848 @param[in] Packet The DHCP packet received
849 @param[in] Para The DHCP parameter extracted from the packet. That
850 is, all the option value that we care.
852 @retval EFI_SUCCESS The packet is successfully processed.
853 @retval Others Some error occured.
858 IN DHCP_SERVICE
*DhcpSb
,
859 IN EFI_DHCP4_PACKET
*Packet
,
860 IN DHCP_PARAMETER
*Para
863 EFI_DHCP4_HEADER
*Head
;
866 Head
= &Packet
->Dhcp4
.Header
;
869 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
871 if (DHCP_IS_BOOTP (Para
) ||
872 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
875 Status
= EFI_SUCCESS
;
880 // If a NAK is received, transit to INIT and try again.
882 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
883 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
885 DhcpSb
->ClientAddr
= 0;
886 DhcpSb
->DhcpState
= Dhcp4Init
;
888 Status
= DhcpInitRequest (DhcpSb
);
893 // Check whether the ACK matches the selected offer
895 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
896 Status
= EFI_DEVICE_ERROR
;
900 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
901 if (EFI_ERROR (Status
)) {
906 // OK, get the parameter from server, record the lease
908 DhcpSb
->Para
= AllocatePool (sizeof (DHCP_PARAMETER
));
910 if (DhcpSb
->Para
== NULL
) {
911 Status
= EFI_OUT_OF_RESOURCES
;
915 DhcpSb
->Selected
= Packet
;
916 CopyMem (DhcpSb
->Para
, Para
, sizeof (*DhcpSb
->Para
));
918 Status
= DhcpLeaseAcquired (DhcpSb
);
920 if (EFI_ERROR (Status
)) {
924 DhcpSb
->IoStatus
= EFI_SUCCESS
;
925 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
929 gBS
->FreePool (Packet
);
935 Handle the received DHCP packets. This function drives the DHCP
938 @param UdpPacket The UDP packets received.
939 @param Points The local/remote UDP access points
940 @param IoStatus The status of the UDP receive
941 @param Context The opaque parameter to the function.
952 DHCP_SERVICE
*DhcpSb
;
953 EFI_DHCP4_HEADER
*Head
;
954 EFI_DHCP4_PACKET
*Packet
;
955 DHCP_PARAMETER
*Para
;
960 DhcpSb
= (DHCP_SERVICE
*) Context
;
963 // Don't restart receive if error occurs or DHCP is destoried.
965 if (EFI_ERROR (IoStatus
)) {
967 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
968 NetbufFree (UdpPacket
);
972 ASSERT (UdpPacket
!= NULL
);
974 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
979 // Validate the packet received
981 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
986 // Copy the DHCP message to a continuous memory block
988 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
989 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
991 if (Packet
== NULL
) {
996 Head
= &Packet
->Dhcp4
.Header
;
997 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
999 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1004 // Is this packet the answer to our packet?
1006 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1007 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1008 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1013 // Validate the options and retrieve the interested options
1016 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1017 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1018 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1024 // Call the handler for each state. The handler should return
1025 // EFI_SUCCESS if the process can go on no matter whether the
1026 // packet is ignored or not. If the return is EFI_ERROR, the
1027 // session will be terminated. Packet's ownership is handled
1028 // over to the handlers. If operation succeeds, the handler
1029 // must notify the user. It isn't necessary to do if EFI_ERROR
1030 // is returned because the DhcpEndSession will notify the user.
1032 Status
= EFI_SUCCESS
;
1034 switch (DhcpSb
->DhcpState
) {
1035 case Dhcp4Selecting
:
1036 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1039 case Dhcp4Requesting
:
1040 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1043 case Dhcp4InitReboot
:
1047 // Ignore the packet in INITREBOOT, INIT and BOUND states
1049 gBS
->FreePool (Packet
);
1050 Status
= EFI_SUCCESS
;
1054 case Dhcp4Rebinding
:
1055 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1058 case Dhcp4Rebooting
:
1059 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1064 gBS
->FreePool (Para
);
1069 if (EFI_ERROR (Status
)) {
1070 NetbufFree (UdpPacket
);
1071 DhcpEndSession (DhcpSb
, Status
);
1076 NetbufFree (UdpPacket
);
1078 if (Packet
!= NULL
) {
1079 gBS
->FreePool (Packet
);
1082 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1084 if (EFI_ERROR (Status
)) {
1085 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1093 @param[in] Arg The packet to release
1101 gBS
->FreePool (Arg
);
1106 Release the net buffer when packet is sent.
1108 @param UdpPacket The UDP packets received.
1109 @param Points The local/remote UDP access points
1110 @param IoStatus The status of the UDP receive
1111 @param Context The opaque parameter to the function.
1118 EFI_STATUS IoStatus
,
1122 NetbufFree (Packet
);
1128 Build and transmit a DHCP message according to the current states.
1129 This function implement the Table 5. of RFC 2131. Always transits
1130 the state (as defined in Figure 5. of the same RFC) before sending
1131 a DHCP message. The table is adjusted accordingly.
1133 @param[in] DhcpSb The DHCP service instance
1134 @param[in] Seed The seed packet which the new packet is based on
1135 @param[in] Para The DHCP parameter of the Seed packet
1136 @param[in] Type The message type to send
1137 @param[in] Msg The human readable message to include in the packet
1140 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1141 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1142 @retval EFI_SUCCESS The message is sent
1143 @retval other Other error occurs
1148 IN DHCP_SERVICE
*DhcpSb
,
1149 IN EFI_DHCP4_PACKET
*Seed
,
1150 IN DHCP_PARAMETER
*Para
,
1155 EFI_DHCP4_CONFIG_DATA
*Config
;
1156 EFI_DHCP4_PACKET
*Packet
;
1157 EFI_DHCP4_PACKET
*NewPacket
;
1158 EFI_DHCP4_HEADER
*Head
;
1159 EFI_DHCP4_HEADER
*SeedHead
;
1161 UDP_POINTS EndPoint
;
1172 // Allocate a big enough memory block to hold the DHCP packet
1174 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1177 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1180 Packet
= AllocatePool (Len
);
1182 if (Packet
== NULL
) {
1183 return EFI_OUT_OF_RESOURCES
;
1187 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1190 // Fill in the DHCP header fields
1192 Config
= &DhcpSb
->ActiveConfig
;
1196 SeedHead
= &Seed
->Dhcp4
.Header
;
1199 Head
= &Packet
->Dhcp4
.Header
;
1200 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1202 Head
->OpCode
= BOOTP_REQUEST
;
1203 Head
->HwType
= DhcpSb
->HwType
;
1204 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1205 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1206 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1208 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1209 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1212 // Append the DHCP message type
1214 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1215 Buf
= Packet
->Dhcp4
.Option
;
1216 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1219 // Append the serverid option if necessary:
1220 // 1. DHCP decline message
1221 // 2. DHCP release message
1222 // 3. DHCP request to confirm one lease.
1224 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1225 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))
1228 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1230 IpAddr
= HTONL (Para
->ServerId
);
1231 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1235 // Append the requested IP option if necessary:
1236 // 1. DHCP request to use the previously allocated address
1237 // 2. DHCP request to confirm one lease
1238 // 3. DHCP decline to decline one lease
1242 if (Type
== DHCP_MSG_REQUEST
) {
1243 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1244 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1246 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1247 ASSERT (SeedHead
!= NULL
);
1248 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1251 } else if (Type
== DHCP_MSG_DECLINE
) {
1252 ASSERT (SeedHead
!= NULL
);
1253 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1257 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1261 // Append the Max Message Length option if it isn't a DECLINE
1262 // or RELEASE to direct the server use large messages instead of
1263 // override the BOOTFILE and SERVER fields in the message head.
1265 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1266 MaxMsg
= HTONS (0xFF00);
1267 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1271 // Append the user's message if it isn't NULL
1274 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1275 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1279 // Append the user configured options
1281 if (DhcpSb
->UserOptionLen
!= 0) {
1282 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1284 // We can't use any option other than the client ID from user
1285 // if it is a DHCP decline or DHCP release .
1287 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1288 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1292 Buf
= DhcpAppendOption (
1294 Config
->OptionList
[Index
]->OpCode
,
1295 Config
->OptionList
[Index
]->Length
,
1296 Config
->OptionList
[Index
]->Data
1301 *(Buf
++) = DHCP_TAG_EOP
;
1302 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1305 // OK, the message is built, call the user to override it.
1307 Status
= EFI_SUCCESS
;
1310 if (Type
== DHCP_MSG_DISCOVER
) {
1311 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1313 } else if (Type
== DHCP_MSG_REQUEST
) {
1314 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1316 } else if (Type
== DHCP_MSG_DECLINE
) {
1317 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1320 if (EFI_ERROR (Status
)) {
1321 gBS
->FreePool (Packet
);
1325 if (NewPacket
!= NULL
) {
1326 gBS
->FreePool (Packet
);
1331 // Save the Client Address will be sent out
1334 &DhcpSb
->ClientAddressSendOut
[0],
1335 &Packet
->Dhcp4
.Header
.ClientHwAddr
[0],
1336 Packet
->Dhcp4
.Header
.HwAddrLen
1341 // Wrap it into a netbuf then send it.
1343 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1344 Frag
.Len
= Packet
->Length
;
1345 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1348 gBS
->FreePool (Packet
);
1349 return EFI_OUT_OF_RESOURCES
;
1353 // Save it as the last sent packet for retransmission
1355 if (DhcpSb
->LastPacket
!= NULL
) {
1356 NetbufFree (DhcpSb
->LastPacket
);
1360 DhcpSb
->LastPacket
= Wrap
;
1361 DhcpSetTransmitTimer (DhcpSb
);
1364 // Broadcast the message, unless we know the server address.
1365 // Use the lease UdpIo port to send the unicast packet.
1367 EndPoint
.RemoteAddr
= 0xffffffff;
1368 EndPoint
.LocalAddr
= 0;
1369 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1370 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1371 UdpIo
= DhcpSb
->UdpIo
;
1373 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1374 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1375 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1376 UdpIo
= DhcpSb
->LeaseIoPort
;
1379 ASSERT (UdpIo
!= NULL
);
1380 Status
= UdpIoSendDatagram (UdpIo
, Wrap
, &EndPoint
, 0, DhcpOnPacketSent
, DhcpSb
);
1382 if (EFI_ERROR (Status
)) {
1384 return EFI_ACCESS_DENIED
;
1392 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1393 will be retransmitted.
1395 @param[in] DhcpSb The DHCP service instance
1397 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1398 @retval EFI_SUCCESS The packet is retransmitted.
1403 IN DHCP_SERVICE
*DhcpSb
1407 UDP_POINTS EndPoint
;
1410 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1413 // Broadcast the message, unless we know the server address.
1415 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1416 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1417 EndPoint
.RemoteAddr
= 0xffffffff;
1418 EndPoint
.LocalAddr
= 0;
1419 UdpIo
= DhcpSb
->UdpIo
;
1421 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1422 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1423 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1424 UdpIo
= DhcpSb
->LeaseIoPort
;
1427 ASSERT (UdpIo
!= NULL
);
1429 NET_GET_REF (DhcpSb
->LastPacket
);
1430 Status
= UdpIoSendDatagram (
1439 if (EFI_ERROR (Status
)) {
1440 NET_PUT_REF (DhcpSb
->LastPacket
);
1441 return EFI_ACCESS_DENIED
;
1449 Each DHCP service has three timer. Two of them are count down timer.
1450 One for the packet retransmission. The other is to collect the offers.
1451 The third timer increaments the lease life which is compared to T1, T2,
1452 and lease to determine the time to renew and rebind the lease.
1453 DhcpOnTimerTick will be called once every second.
1455 @param[in] Event The timer event
1456 @param[in] Context The context, which is the DHCP service instance.
1466 DHCP_SERVICE
*DhcpSb
;
1467 DHCP_PROTOCOL
*Instance
;
1470 DhcpSb
= (DHCP_SERVICE
*) Context
;
1471 Instance
= DhcpSb
->ActiveChild
;
1474 // Check the retransmit timer
1476 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1479 // Select offer at each timeout if any offer received.
1481 if (DhcpSb
->DhcpState
== Dhcp4Selecting
&& DhcpSb
->LastOffer
!= NULL
) {
1483 Status
= DhcpChooseOffer (DhcpSb
);
1485 if (EFI_ERROR(Status
)) {
1486 FreePool (DhcpSb
->LastOffer
);
1487 DhcpSb
->LastOffer
= NULL
;
1493 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1495 // Still has another try
1497 DhcpRetransmit (DhcpSb
);
1498 DhcpSetTransmitTimer (DhcpSb
);
1500 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1503 // Retransmission failed, if the DHCP request is initiated by
1504 // user, adjust the current state according to the lease life.
1505 // Otherwise do nothing to wait the lease to timeout
1507 if (DhcpSb
->ExtraRefresh
!= 0) {
1508 Status
= EFI_SUCCESS
;
1510 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1511 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1513 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1514 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1516 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1517 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1524 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1525 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1533 // If an address has been acquired, check whether need to
1534 // refresh or whether it has expired.
1536 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1537 DhcpSb
->LeaseLife
++;
1540 // Don't timeout the lease, only count the life if user is
1541 // requesting extra renew/rebind. Adjust the state after that.
1543 if (DhcpSb
->ExtraRefresh
!= 0) {
1547 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1549 // Lease expires, end the session
1553 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1555 // T2 expires, transit to rebinding then send a REQUEST to any server
1557 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1561 Status
= DhcpSendMessage (
1569 if (EFI_ERROR (Status
)) {
1573 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1575 // T1 expires, transit to renewing, then send a REQUEST to the server
1577 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1581 Status
= DhcpSendMessage (
1589 if (EFI_ERROR (Status
)) {
1596 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1597 Instance
->Timeout
--;
1598 if (Instance
->Timeout
== 0) {
1599 PxeDhcpDone (Instance
);
1606 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);