2 EFI DHCP protocol implementation.
4 Copyright (c) 2006 - 2009, 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;
230 DhcpSb
->LastTimeout
= 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
];
263 DhcpSb
->LastTimeout
= DhcpSb
->PacketToLive
;
269 Compute the lease. If the server grants a permanent lease, just
270 process it as a normal timeout value since the lease will last
273 @param DhcpSb The DHCP service instance
274 @param Para The DHCP parameter extracted from the server's
279 IN OUT DHCP_SERVICE
*DhcpSb
,
280 IN DHCP_PARAMETER
*Para
283 ASSERT (Para
!= NULL
);
285 DhcpSb
->Lease
= Para
->Lease
;
286 DhcpSb
->T2
= Para
->T2
;
287 DhcpSb
->T1
= Para
->T1
;
289 if (DhcpSb
->Lease
== 0) {
290 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
293 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
294 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
297 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
298 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
304 Configure a UDP IO port to use the acquired lease address.
305 DHCP driver needs this port to unicast packet to the server
306 such as DHCP release.
308 @param[in] UdpIo The UDP IO port to configure
309 @param[in] Context Dhcp service instance.
311 @retval EFI_SUCCESS The UDP IO port is successfully configured.
312 @retval Others It failed to configure the port.
316 DhcpConfigLeaseIoPort (
317 IN UDP_IO_PORT
*UdpIo
,
321 EFI_UDP4_CONFIG_DATA UdpConfigData
;
322 EFI_IPv4_ADDRESS Subnet
;
323 EFI_IPv4_ADDRESS Gateway
;
324 DHCP_SERVICE
*DhcpSb
;
328 DhcpSb
= (DHCP_SERVICE
*) Context
;
330 UdpConfigData
.AcceptBroadcast
= FALSE
;
331 UdpConfigData
.AcceptPromiscuous
= FALSE
;
332 UdpConfigData
.AcceptAnyPort
= FALSE
;
333 UdpConfigData
.AllowDuplicatePort
= TRUE
;
334 UdpConfigData
.TypeOfService
= 0;
335 UdpConfigData
.TimeToLive
= 64;
336 UdpConfigData
.DoNotFragment
= FALSE
;
337 UdpConfigData
.ReceiveTimeout
= 1;
338 UdpConfigData
.TransmitTimeout
= 0;
340 UdpConfigData
.UseDefaultAddress
= FALSE
;
341 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
342 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
344 Ip
= HTONL (DhcpSb
->ClientAddr
);
345 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
347 Ip
= HTONL (DhcpSb
->Netmask
);
348 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
350 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
352 Status
= UdpIo
->Udp
->Configure (UdpIo
->Udp
, &UdpConfigData
);
354 if (EFI_ERROR (Status
)) {
359 // Add a default route if received from the server.
361 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
362 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
364 Ip
= HTONL (DhcpSb
->Para
->Router
);
365 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
367 UdpIo
->Udp
->Routes (UdpIo
->Udp
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
375 Update the lease states when a new lease is acquired. It will not only
376 save the acquired the address and lease time, it will also create a UDP
377 child to provide address resolution for the address.
379 @param DhcpSb The DHCP service instance
381 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
382 @retval EFI_SUCCESS The lease is recorded.
387 IN OUT DHCP_SERVICE
*DhcpSb
392 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
394 if (DhcpSb
->Para
!= NULL
) {
395 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
396 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
399 if (DhcpSb
->Netmask
== 0) {
400 Class
= NetGetIpClass (DhcpSb
->ClientAddr
);
401 DhcpSb
->Netmask
= gIp4AllMasks
[Class
<< 3];
404 if (DhcpSb
->LeaseIoPort
!= NULL
) {
405 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
409 // Create a UDP/IP child to provide ARP service for the Leased IP,
410 // and transmit unicast packet with it as source address. Don't
411 // start receive on this port, the queued packet will be timeout.
413 DhcpSb
->LeaseIoPort
= UdpIoCreatePort (
416 DhcpConfigLeaseIoPort
,
420 if (DhcpSb
->LeaseIoPort
== NULL
) {
421 return EFI_OUT_OF_RESOURCES
;
424 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
425 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
428 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
433 Clean up the DHCP related states, IoStatus isn't reset.
435 @param DhcpSb The DHCP instance service.
440 IN DHCP_SERVICE
*DhcpSb
443 DhcpSb
->DhcpState
= Dhcp4Init
;
444 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
445 DhcpSb
->ClientAddr
= 0;
446 DhcpSb
->ServerAddr
= 0;
448 if (DhcpSb
->LastOffer
!= NULL
) {
449 gBS
->FreePool (DhcpSb
->LastOffer
);
450 DhcpSb
->LastOffer
= NULL
;
453 if (DhcpSb
->Selected
!= NULL
) {
454 gBS
->FreePool (DhcpSb
->Selected
);
455 DhcpSb
->Selected
= NULL
;
458 if (DhcpSb
->Para
!= NULL
) {
459 gBS
->FreePool (DhcpSb
->Para
);
466 DhcpSb
->ExtraRefresh
= FALSE
;
468 if (DhcpSb
->LeaseIoPort
!= NULL
) {
469 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
470 DhcpSb
->LeaseIoPort
= NULL
;
473 if (DhcpSb
->LastPacket
!= NULL
) {
474 FreePool (DhcpSb
->LastPacket
);
475 DhcpSb
->LastPacket
= NULL
;
478 DhcpSb
->PacketToLive
= 0;
479 DhcpSb
->LastTimeout
= 0;
480 DhcpSb
->CurRetry
= 0;
481 DhcpSb
->MaxRetries
= 0;
482 DhcpSb
->LeaseLife
= 0;
487 Select a offer among all the offers collected. If the offer selected is
488 of BOOTP, the lease is recorded and user notified. If the offer is of
489 DHCP, it will request the offer from the server.
491 @param[in] DhcpSb The DHCP service instance.
493 @retval EFI_SUCCESS One of the offer is selected.
498 IN DHCP_SERVICE
*DhcpSb
501 EFI_DHCP4_PACKET
*Selected
;
502 EFI_DHCP4_PACKET
*NewPacket
;
503 EFI_DHCP4_PACKET
*TempPacket
;
506 ASSERT (DhcpSb
->LastOffer
!= NULL
);
509 // User will cache previous offers if he wants to select
510 // from multiple offers. If user provides an invalid packet,
511 // use the last offer, otherwise use the provided packet.
514 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
516 if (EFI_ERROR (Status
)) {
520 Selected
= DhcpSb
->LastOffer
;
522 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
523 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
524 if (TempPacket
!= NULL
) {
525 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
526 gBS
->FreePool (Selected
);
527 Selected
= TempPacket
;
531 DhcpSb
->Selected
= Selected
;
532 DhcpSb
->LastOffer
= NULL
;
534 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
537 // A bootp offer has been selected, save the lease status,
538 // enter bound state then notify the user.
540 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
541 Status
= DhcpLeaseAcquired (DhcpSb
);
543 if (EFI_ERROR (Status
)) {
547 DhcpSb
->IoStatus
= EFI_SUCCESS
;
548 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
553 // Send a DHCP requests
555 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
557 if (EFI_ERROR (Status
)) {
561 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
566 Terminate the current address acquire. All the allocated resources
567 are released. Be careful when calling this function. A rule related
568 to this is: only call DhcpEndSession at the highest level, such as
569 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
571 @param[in] DhcpSb The DHCP service instance
572 @param[in] Status The result of the DHCP process.
577 IN DHCP_SERVICE
*DhcpSb
,
581 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
582 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
584 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
587 DhcpCleanLease (DhcpSb
);
589 DhcpSb
->IoStatus
= Status
;
590 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
595 Handle packets in DHCP select state.
597 @param[in] DhcpSb The DHCP service instance
598 @param[in] Packet The DHCP packet received
599 @param[in] Para The DHCP parameter extracted from the packet. That
600 is, all the option value that we care.
602 @retval EFI_SUCCESS The packet is successfully processed.
603 @retval Others Some error occured.
608 IN DHCP_SERVICE
*DhcpSb
,
609 IN EFI_DHCP4_PACKET
*Packet
,
610 IN DHCP_PARAMETER
*Para
615 Status
= EFI_SUCCESS
;
618 // First validate the message:
619 // 1. the offer is a unicast
620 // 2. if it is a DHCP message, it must contains a server ID.
621 // Don't return a error for these two case otherwise the session is ended.
623 if (!DHCP_IS_BOOTP (Para
) &&
624 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))
630 // Call the user's callback. The action according to the return is as:
631 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
632 // 2. EFI_NOT_READY: wait for more offers
633 // 3. EFI_ABORTED: abort the address acquiring.
635 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
637 if (Status
== EFI_SUCCESS
) {
638 if (DhcpSb
->LastOffer
!= NULL
) {
639 gBS
->FreePool (DhcpSb
->LastOffer
);
642 DhcpSb
->LastOffer
= Packet
;
644 return DhcpChooseOffer (DhcpSb
);
646 } else if (Status
== EFI_NOT_READY
) {
647 if (DhcpSb
->LastOffer
!= NULL
) {
648 gBS
->FreePool (DhcpSb
->LastOffer
);
651 DhcpSb
->LastOffer
= Packet
;
653 } else if (Status
== EFI_ABORTED
) {
655 // DhcpInput will end the session upon error return. Remember
656 // only to call DhcpEndSession at the top level call.
664 gBS
->FreePool (Packet
);
670 Handle packets in DHCP request state.
672 @param[in] DhcpSb The DHCP service instance
673 @param[in] Packet The DHCP packet received
674 @param[in] Para The DHCP parameter extracted from the packet. That
675 is, all the option value that we care.
677 @retval EFI_SUCCESS The packet is successfully processed.
678 @retval Others Some error occured.
683 IN DHCP_SERVICE
*DhcpSb
,
684 IN EFI_DHCP4_PACKET
*Packet
,
685 IN DHCP_PARAMETER
*Para
688 EFI_DHCP4_HEADER
*Head
;
689 EFI_DHCP4_HEADER
*Selected
;
693 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
695 Head
= &Packet
->Dhcp4
.Header
;
696 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
699 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
701 if (DHCP_IS_BOOTP (Para
) ||
702 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
703 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
706 Status
= EFI_SUCCESS
;
711 // Received a NAK, end the session no matter what the user returns
713 Status
= EFI_DEVICE_ERROR
;
715 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
716 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
721 // Check whether the ACK matches the selected offer
725 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
726 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
730 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
732 if (EFI_ERROR (Status
)) {
733 Message
= (UINT8
*) "Lease is denied upon received ACK";
738 // Record the lease, transit to BOUND state, then notify the user
740 Status
= DhcpLeaseAcquired (DhcpSb
);
742 if (EFI_ERROR (Status
)) {
743 Message
= (UINT8
*) "Lease is denied upon entering bound";
747 DhcpSb
->IoStatus
= EFI_SUCCESS
;
748 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
750 gBS
->FreePool (Packet
);
754 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
757 gBS
->FreePool (Packet
);
763 Handle packets in DHCP renew/rebound state.
765 @param[in] DhcpSb The DHCP service instance
766 @param[in] Packet The DHCP packet received
767 @param[in] Para The DHCP parameter extracted from the packet. That
768 is, all the option value that we care.
770 @retval EFI_SUCCESS The packet is successfully processed.
771 @retval Others Some error occured.
775 DhcpHandleRenewRebind (
776 IN DHCP_SERVICE
*DhcpSb
,
777 IN EFI_DHCP4_PACKET
*Packet
,
778 IN DHCP_PARAMETER
*Para
781 EFI_DHCP4_HEADER
*Head
;
782 EFI_DHCP4_HEADER
*Selected
;
785 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
787 Head
= &Packet
->Dhcp4
.Header
;
788 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
791 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
793 if (DHCP_IS_BOOTP (Para
) ||
794 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
795 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
798 Status
= EFI_SUCCESS
;
803 // Received a NAK, ignore the user's return then terminate the process
805 Status
= EFI_DEVICE_ERROR
;
807 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
808 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
813 // The lease is different from the selected. Don't send a DECLINE
814 // since it isn't existed in the client's FSM.
816 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
820 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
822 if (EFI_ERROR (Status
)) {
827 // Record the lease, start timer for T1 and T2,
829 DhcpComputeLease (DhcpSb
, Para
);
830 DhcpSb
->LeaseLife
= 0;
831 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
833 if (DhcpSb
->ExtraRefresh
!= 0) {
834 DhcpSb
->ExtraRefresh
= FALSE
;
836 DhcpSb
->IoStatus
= EFI_SUCCESS
;
837 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
841 gBS
->FreePool (Packet
);
847 Handle packets in DHCP reboot state.
849 @param[in] DhcpSb The DHCP service instance
850 @param[in] Packet The DHCP packet received
851 @param[in] Para The DHCP parameter extracted from the packet. That
852 is, all the option value that we care.
854 @retval EFI_SUCCESS The packet is successfully processed.
855 @retval Others Some error occured.
860 IN DHCP_SERVICE
*DhcpSb
,
861 IN EFI_DHCP4_PACKET
*Packet
,
862 IN DHCP_PARAMETER
*Para
865 EFI_DHCP4_HEADER
*Head
;
868 Head
= &Packet
->Dhcp4
.Header
;
871 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
873 if (DHCP_IS_BOOTP (Para
) ||
874 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
877 Status
= EFI_SUCCESS
;
882 // If a NAK is received, transit to INIT and try again.
884 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
885 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
887 DhcpSb
->ClientAddr
= 0;
888 DhcpSb
->DhcpState
= Dhcp4Init
;
890 Status
= DhcpInitRequest (DhcpSb
);
895 // Check whether the ACK matches the selected offer
897 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
898 Status
= EFI_DEVICE_ERROR
;
902 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
903 if (EFI_ERROR (Status
)) {
908 // OK, get the parameter from server, record the lease
910 DhcpSb
->Para
= AllocatePool (sizeof (DHCP_PARAMETER
));
912 if (DhcpSb
->Para
== NULL
) {
913 Status
= EFI_OUT_OF_RESOURCES
;
917 DhcpSb
->Selected
= Packet
;
918 CopyMem (DhcpSb
->Para
, Para
, sizeof (*DhcpSb
->Para
));
920 Status
= DhcpLeaseAcquired (DhcpSb
);
922 if (EFI_ERROR (Status
)) {
926 DhcpSb
->IoStatus
= EFI_SUCCESS
;
927 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
931 gBS
->FreePool (Packet
);
937 Handle the received DHCP packets. This function drives the DHCP
940 @param UdpPacket The UDP packets received.
941 @param Points The local/remote UDP access points
942 @param IoStatus The status of the UDP receive
943 @param Context The opaque parameter to the function.
954 DHCP_SERVICE
*DhcpSb
;
955 EFI_DHCP4_HEADER
*Head
;
956 EFI_DHCP4_PACKET
*Packet
;
957 DHCP_PARAMETER
*Para
;
962 DhcpSb
= (DHCP_SERVICE
*) Context
;
965 // Don't restart receive if error occurs or DHCP is destoried.
967 if (EFI_ERROR (IoStatus
)) {
969 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
970 NetbufFree (UdpPacket
);
974 ASSERT (UdpPacket
!= NULL
);
976 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
981 // Validate the packet received
983 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
988 // Copy the DHCP message to a continuous memory block
990 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
991 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
993 if (Packet
== NULL
) {
998 Head
= &Packet
->Dhcp4
.Header
;
999 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1001 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1006 // Is this packet the answer to our packet?
1008 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1009 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1010 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1015 // Validate the options and retrieve the interested options
1018 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1019 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1020 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1026 // Call the handler for each state. The handler should return
1027 // EFI_SUCCESS if the process can go on no matter whether the
1028 // packet is ignored or not. If the return is EFI_ERROR, the
1029 // session will be terminated. Packet's ownership is handled
1030 // over to the handlers. If operation succeeds, the handler
1031 // must notify the user. It isn't necessary to do if EFI_ERROR
1032 // is returned because the DhcpEndSession will notify the user.
1034 Status
= EFI_SUCCESS
;
1036 switch (DhcpSb
->DhcpState
) {
1037 case Dhcp4Selecting
:
1038 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1041 case Dhcp4Requesting
:
1042 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1045 case Dhcp4InitReboot
:
1049 // Ignore the packet in INITREBOOT, INIT and BOUND states
1051 gBS
->FreePool (Packet
);
1052 Status
= EFI_SUCCESS
;
1056 case Dhcp4Rebinding
:
1057 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1060 case Dhcp4Rebooting
:
1061 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1066 gBS
->FreePool (Para
);
1071 if (EFI_ERROR (Status
)) {
1072 NetbufFree (UdpPacket
);
1073 DhcpEndSession (DhcpSb
, Status
);
1078 NetbufFree (UdpPacket
);
1080 if (Packet
!= NULL
) {
1081 gBS
->FreePool (Packet
);
1084 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1086 if (EFI_ERROR (Status
)) {
1087 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1095 @param[in] Arg The packet to release
1103 gBS
->FreePool (Arg
);
1108 Release the net buffer when packet is sent.
1110 @param UdpPacket The UDP packets received.
1111 @param Points The local/remote UDP access points
1112 @param IoStatus The status of the UDP receive
1113 @param Context The opaque parameter to the function.
1120 EFI_STATUS IoStatus
,
1124 NetbufFree (Packet
);
1130 Build and transmit a DHCP message according to the current states.
1131 This function implement the Table 5. of RFC 2131. Always transits
1132 the state (as defined in Figure 5. of the same RFC) before sending
1133 a DHCP message. The table is adjusted accordingly.
1135 @param[in] DhcpSb The DHCP service instance
1136 @param[in] Seed The seed packet which the new packet is based on
1137 @param[in] Para The DHCP parameter of the Seed packet
1138 @param[in] Type The message type to send
1139 @param[in] Msg The human readable message to include in the packet
1142 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1143 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1144 @retval EFI_SUCCESS The message is sent
1145 @retval other Other error occurs
1150 IN DHCP_SERVICE
*DhcpSb
,
1151 IN EFI_DHCP4_PACKET
*Seed
,
1152 IN DHCP_PARAMETER
*Para
,
1157 EFI_DHCP4_CONFIG_DATA
*Config
;
1158 EFI_DHCP4_PACKET
*Packet
;
1159 EFI_DHCP4_PACKET
*NewPacket
;
1160 EFI_DHCP4_HEADER
*Head
;
1161 EFI_DHCP4_HEADER
*SeedHead
;
1163 UDP_POINTS EndPoint
;
1174 // Allocate a big enough memory block to hold the DHCP packet
1176 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1179 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1182 Packet
= AllocatePool (Len
);
1184 if (Packet
== NULL
) {
1185 return EFI_OUT_OF_RESOURCES
;
1189 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1192 // Fill in the DHCP header fields
1194 Config
= &DhcpSb
->ActiveConfig
;
1198 SeedHead
= &Seed
->Dhcp4
.Header
;
1201 Head
= &Packet
->Dhcp4
.Header
;
1202 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1204 Head
->OpCode
= BOOTP_REQUEST
;
1205 Head
->HwType
= DhcpSb
->HwType
;
1206 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1207 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1208 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1210 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1211 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1214 // Append the DHCP message type
1216 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1217 Buf
= Packet
->Dhcp4
.Option
;
1218 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1221 // Append the serverid option if necessary:
1222 // 1. DHCP decline message
1223 // 2. DHCP release message
1224 // 3. DHCP request to confirm one lease.
1226 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1227 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))
1230 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1232 IpAddr
= HTONL (Para
->ServerId
);
1233 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1237 // Append the requested IP option if necessary:
1238 // 1. DHCP request to use the previously allocated address
1239 // 2. DHCP request to confirm one lease
1240 // 3. DHCP decline to decline one lease
1244 if (Type
== DHCP_MSG_REQUEST
) {
1245 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1246 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1248 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1249 ASSERT (SeedHead
!= NULL
);
1250 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1253 } else if (Type
== DHCP_MSG_DECLINE
) {
1254 ASSERT (SeedHead
!= NULL
);
1255 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1259 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1263 // Append the Max Message Length option if it isn't a DECLINE
1264 // or RELEASE to direct the server use large messages instead of
1265 // override the BOOTFILE and SERVER fields in the message head.
1267 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1268 MaxMsg
= HTONS (0xFF00);
1269 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1273 // Append the user's message if it isn't NULL
1276 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1277 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1281 // Append the user configured options
1283 if (DhcpSb
->UserOptionLen
!= 0) {
1284 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1286 // We can't use any option other than the client ID from user
1287 // if it is a DHCP decline or DHCP release .
1289 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1290 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1294 Buf
= DhcpAppendOption (
1296 Config
->OptionList
[Index
]->OpCode
,
1297 Config
->OptionList
[Index
]->Length
,
1298 Config
->OptionList
[Index
]->Data
1303 *(Buf
++) = DHCP_TAG_EOP
;
1304 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1307 // OK, the message is built, call the user to override it.
1309 Status
= EFI_SUCCESS
;
1312 if (Type
== DHCP_MSG_DISCOVER
) {
1313 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1315 } else if (Type
== DHCP_MSG_REQUEST
) {
1316 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1318 } else if (Type
== DHCP_MSG_DECLINE
) {
1319 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1322 if (EFI_ERROR (Status
)) {
1323 gBS
->FreePool (Packet
);
1327 if (NewPacket
!= NULL
) {
1328 gBS
->FreePool (Packet
);
1333 // Save the Client Address will be sent out
1336 &DhcpSb
->ClientAddressSendOut
[0],
1337 &Packet
->Dhcp4
.Header
.ClientHwAddr
[0],
1338 Packet
->Dhcp4
.Header
.HwAddrLen
1343 // Wrap it into a netbuf then send it.
1345 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1346 Frag
.Len
= Packet
->Length
;
1347 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1350 gBS
->FreePool (Packet
);
1351 return EFI_OUT_OF_RESOURCES
;
1355 // Save it as the last sent packet for retransmission
1357 if (DhcpSb
->LastPacket
!= NULL
) {
1358 FreePool (DhcpSb
->LastPacket
);
1361 DhcpSb
->LastPacket
= Packet
;
1362 DhcpSetTransmitTimer (DhcpSb
);
1365 // Broadcast the message, unless we know the server address.
1366 // Use the lease UdpIo port to send the unicast packet.
1368 EndPoint
.RemoteAddr
= 0xffffffff;
1369 EndPoint
.LocalAddr
= 0;
1370 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1371 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1372 UdpIo
= DhcpSb
->UdpIo
;
1374 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1375 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1376 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1377 UdpIo
= DhcpSb
->LeaseIoPort
;
1380 ASSERT (UdpIo
!= NULL
);
1383 Status
= UdpIoSendDatagram (
1392 if (EFI_ERROR (Status
)) {
1394 return EFI_ACCESS_DENIED
;
1402 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1403 will be retransmitted.
1405 @param[in] DhcpSb The DHCP service instance
1407 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1408 @retval EFI_SUCCESS The packet is retransmitted.
1413 IN DHCP_SERVICE
*DhcpSb
1417 UDP_POINTS EndPoint
;
1422 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1424 DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
= HTONS (*(UINT16
*)(&DhcpSb
->LastTimeout
));
1427 // Wrap it into a netbuf then send it.
1429 Frag
.Bulk
= (UINT8
*) &DhcpSb
->LastPacket
->Dhcp4
.Header
;
1430 Frag
.Len
= DhcpSb
->LastPacket
->Length
;
1431 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, DhcpSb
->LastPacket
);
1434 return EFI_OUT_OF_RESOURCES
;
1438 // Broadcast the message, unless we know the server address.
1440 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1441 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1442 EndPoint
.RemoteAddr
= 0xffffffff;
1443 EndPoint
.LocalAddr
= 0;
1444 UdpIo
= DhcpSb
->UdpIo
;
1446 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1447 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1448 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1449 UdpIo
= DhcpSb
->LeaseIoPort
;
1452 ASSERT (UdpIo
!= NULL
);
1455 Status
= UdpIoSendDatagram (
1464 if (EFI_ERROR (Status
)) {
1466 return EFI_ACCESS_DENIED
;
1474 Each DHCP service has three timer. Two of them are count down timer.
1475 One for the packet retransmission. The other is to collect the offers.
1476 The third timer increaments the lease life which is compared to T1, T2,
1477 and lease to determine the time to renew and rebind the lease.
1478 DhcpOnTimerTick will be called once every second.
1480 @param[in] Event The timer event
1481 @param[in] Context The context, which is the DHCP service instance.
1491 DHCP_SERVICE
*DhcpSb
;
1492 DHCP_PROTOCOL
*Instance
;
1495 DhcpSb
= (DHCP_SERVICE
*) Context
;
1496 Instance
= DhcpSb
->ActiveChild
;
1499 // Check the retransmit timer
1501 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1504 // Select offer at each timeout if any offer received.
1506 if (DhcpSb
->DhcpState
== Dhcp4Selecting
&& DhcpSb
->LastOffer
!= NULL
) {
1508 Status
= DhcpChooseOffer (DhcpSb
);
1510 if (EFI_ERROR(Status
)) {
1511 FreePool (DhcpSb
->LastOffer
);
1512 DhcpSb
->LastOffer
= NULL
;
1518 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1520 // Still has another try
1522 DhcpRetransmit (DhcpSb
);
1523 DhcpSetTransmitTimer (DhcpSb
);
1525 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1528 // Retransmission failed, if the DHCP request is initiated by
1529 // user, adjust the current state according to the lease life.
1530 // Otherwise do nothing to wait the lease to timeout
1532 if (DhcpSb
->ExtraRefresh
!= 0) {
1533 Status
= EFI_SUCCESS
;
1535 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1536 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1538 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1539 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1541 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1542 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1549 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1550 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1558 // If an address has been acquired, check whether need to
1559 // refresh or whether it has expired.
1561 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1562 DhcpSb
->LeaseLife
++;
1565 // Don't timeout the lease, only count the life if user is
1566 // requesting extra renew/rebind. Adjust the state after that.
1568 if (DhcpSb
->ExtraRefresh
!= 0) {
1572 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1574 // Lease expires, end the session
1578 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1580 // T2 expires, transit to rebinding then send a REQUEST to any server
1582 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1586 Status
= DhcpSendMessage (
1594 if (EFI_ERROR (Status
)) {
1598 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1600 // T1 expires, transit to renewing, then send a REQUEST to the server
1602 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1606 Status
= DhcpSendMessage (
1614 if (EFI_ERROR (Status
)) {
1621 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1622 Instance
->Timeout
--;
1623 if (Instance
->Timeout
== 0) {
1624 PxeDhcpDone (Instance
);
1631 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);