2 EFI DHCP protocol implementation.
4 Copyright (c) 2006 - 2010, 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 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 (
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
->Protocol
.Udp4
->Configure (UdpIo
->Protocol
.Udp4
, &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
->Protocol
.Udp4
->Routes (UdpIo
->Protocol
.Udp4
, 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 UdpIoFreeIo (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
= UdpIoCreateIo (
416 DhcpConfigLeaseIoPort
,
421 if (DhcpSb
->LeaseIoPort
== NULL
) {
422 return EFI_OUT_OF_RESOURCES
;
425 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
426 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
429 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
434 Clean up the DHCP related states, IoStatus isn't reset.
436 @param DhcpSb The DHCP instance service.
441 IN DHCP_SERVICE
*DhcpSb
444 DhcpSb
->DhcpState
= Dhcp4Init
;
445 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
446 DhcpSb
->ClientAddr
= 0;
448 DhcpSb
->ServerAddr
= 0;
450 if (DhcpSb
->LastOffer
!= NULL
) {
451 FreePool (DhcpSb
->LastOffer
);
452 DhcpSb
->LastOffer
= NULL
;
455 if (DhcpSb
->Selected
!= NULL
) {
456 FreePool (DhcpSb
->Selected
);
457 DhcpSb
->Selected
= NULL
;
460 if (DhcpSb
->Para
!= NULL
) {
461 FreePool (DhcpSb
->Para
);
468 DhcpSb
->ExtraRefresh
= FALSE
;
470 if (DhcpSb
->LeaseIoPort
!= NULL
) {
471 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
472 DhcpSb
->LeaseIoPort
= NULL
;
475 if (DhcpSb
->LastPacket
!= NULL
) {
476 FreePool (DhcpSb
->LastPacket
);
477 DhcpSb
->LastPacket
= NULL
;
480 DhcpSb
->PacketToLive
= 0;
481 DhcpSb
->LastTimeout
= 0;
482 DhcpSb
->CurRetry
= 0;
483 DhcpSb
->MaxRetries
= 0;
484 DhcpSb
->LeaseLife
= 0;
487 // Clean active config data.
489 DhcpCleanConfigure (&DhcpSb
->ActiveConfig
);
494 Select a offer among all the offers collected. If the offer selected is
495 of BOOTP, the lease is recorded and user notified. If the offer is of
496 DHCP, it will request the offer from the server.
498 @param[in] DhcpSb The DHCP service instance.
500 @retval EFI_SUCCESS One of the offer is selected.
505 IN DHCP_SERVICE
*DhcpSb
508 EFI_DHCP4_PACKET
*Selected
;
509 EFI_DHCP4_PACKET
*NewPacket
;
510 EFI_DHCP4_PACKET
*TempPacket
;
513 ASSERT (DhcpSb
->LastOffer
!= NULL
);
516 // User will cache previous offers if he wants to select
517 // from multiple offers. If user provides an invalid packet,
518 // use the last offer, otherwise use the provided packet.
521 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
523 if (EFI_ERROR (Status
)) {
527 Selected
= DhcpSb
->LastOffer
;
529 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
530 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
531 if (TempPacket
!= NULL
) {
532 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
534 Selected
= TempPacket
;
538 DhcpSb
->Selected
= Selected
;
539 DhcpSb
->LastOffer
= NULL
;
541 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
544 // A bootp offer has been selected, save the lease status,
545 // enter bound state then notify the user.
547 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
548 Status
= DhcpLeaseAcquired (DhcpSb
);
550 if (EFI_ERROR (Status
)) {
554 DhcpSb
->IoStatus
= EFI_SUCCESS
;
555 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
560 // Send a DHCP requests
562 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
564 if (EFI_ERROR (Status
)) {
568 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
573 Terminate the current address acquire. All the allocated resources
574 are released. Be careful when calling this function. A rule related
575 to this is: only call DhcpEndSession at the highest level, such as
576 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
578 @param[in] DhcpSb The DHCP service instance
579 @param[in] Status The result of the DHCP process.
584 IN DHCP_SERVICE
*DhcpSb
,
588 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
589 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
591 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
594 DhcpCleanLease (DhcpSb
);
596 DhcpSb
->IoStatus
= Status
;
597 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
602 Handle packets in DHCP select state.
604 @param[in] DhcpSb The DHCP service instance
605 @param[in] Packet The DHCP packet received
606 @param[in] Para The DHCP parameter extracted from the packet. That
607 is, all the option value that we care.
609 @retval EFI_SUCCESS The packet is successfully processed.
610 @retval Others Some error occured.
615 IN DHCP_SERVICE
*DhcpSb
,
616 IN EFI_DHCP4_PACKET
*Packet
,
617 IN DHCP_PARAMETER
*Para
622 Status
= EFI_SUCCESS
;
625 // First validate the message:
626 // 1. the offer is a unicast
627 // 2. if it is a DHCP message, it must contains a server ID.
628 // Don't return a error for these two case otherwise the session is ended.
630 if (!DHCP_IS_BOOTP (Para
) &&
631 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))
637 // Call the user's callback. The action according to the return is as:
638 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
639 // 2. EFI_NOT_READY: wait for more offers
640 // 3. EFI_ABORTED: abort the address acquiring.
642 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
644 if (Status
== EFI_SUCCESS
) {
645 if (DhcpSb
->LastOffer
!= NULL
) {
646 FreePool (DhcpSb
->LastOffer
);
649 DhcpSb
->LastOffer
= Packet
;
651 return DhcpChooseOffer (DhcpSb
);
653 } else if (Status
== EFI_NOT_READY
) {
654 if (DhcpSb
->LastOffer
!= NULL
) {
655 FreePool (DhcpSb
->LastOffer
);
658 DhcpSb
->LastOffer
= Packet
;
660 } else if (Status
== EFI_ABORTED
) {
662 // DhcpInput will end the session upon error return. Remember
663 // only to call DhcpEndSession at the top level call.
677 Handle packets in DHCP request state.
679 @param[in] DhcpSb The DHCP service instance
680 @param[in] Packet The DHCP packet received
681 @param[in] Para The DHCP parameter extracted from the packet. That
682 is, all the option value that we care.
684 @retval EFI_SUCCESS The packet is successfully processed.
685 @retval Others Some error occured.
690 IN DHCP_SERVICE
*DhcpSb
,
691 IN EFI_DHCP4_PACKET
*Packet
,
692 IN DHCP_PARAMETER
*Para
695 EFI_DHCP4_HEADER
*Head
;
696 EFI_DHCP4_HEADER
*Selected
;
700 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
702 Head
= &Packet
->Dhcp4
.Header
;
703 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
706 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
708 if (DHCP_IS_BOOTP (Para
) ||
709 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
710 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
713 Status
= EFI_SUCCESS
;
718 // Received a NAK, end the session no matter what the user returns
720 Status
= EFI_DEVICE_ERROR
;
722 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
723 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
728 // Check whether the ACK matches the selected offer
732 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
733 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
737 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
739 if (EFI_ERROR (Status
)) {
740 Message
= (UINT8
*) "Lease is denied upon received ACK";
745 // Record the lease, transit to BOUND state, then notify the user
747 Status
= DhcpLeaseAcquired (DhcpSb
);
749 if (EFI_ERROR (Status
)) {
750 Message
= (UINT8
*) "Lease is denied upon entering bound";
754 DhcpSb
->IoStatus
= EFI_SUCCESS
;
755 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
761 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
770 Handle packets in DHCP renew/rebound state.
772 @param[in] DhcpSb The DHCP service instance
773 @param[in] Packet The DHCP packet received
774 @param[in] Para The DHCP parameter extracted from the packet. That
775 is, all the option value that we care.
777 @retval EFI_SUCCESS The packet is successfully processed.
778 @retval Others Some error occured.
782 DhcpHandleRenewRebind (
783 IN DHCP_SERVICE
*DhcpSb
,
784 IN EFI_DHCP4_PACKET
*Packet
,
785 IN DHCP_PARAMETER
*Para
788 EFI_DHCP4_HEADER
*Head
;
789 EFI_DHCP4_HEADER
*Selected
;
792 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
794 Head
= &Packet
->Dhcp4
.Header
;
795 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
798 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
800 if (DHCP_IS_BOOTP (Para
) ||
801 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
802 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
805 Status
= EFI_SUCCESS
;
810 // Received a NAK, ignore the user's return then terminate the process
812 Status
= EFI_DEVICE_ERROR
;
814 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
815 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
820 // The lease is different from the selected. Don't send a DECLINE
821 // since it isn't existed in the client's FSM.
823 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
827 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
829 if (EFI_ERROR (Status
)) {
834 // Record the lease, start timer for T1 and T2,
836 DhcpComputeLease (DhcpSb
, Para
);
837 DhcpSb
->LeaseLife
= 0;
838 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
840 if (DhcpSb
->ExtraRefresh
!= 0) {
841 DhcpSb
->ExtraRefresh
= FALSE
;
843 DhcpSb
->IoStatus
= EFI_SUCCESS
;
844 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
854 Handle packets in DHCP reboot state.
856 @param[in] DhcpSb The DHCP service instance
857 @param[in] Packet The DHCP packet received
858 @param[in] Para The DHCP parameter extracted from the packet. That
859 is, all the option value that we care.
861 @retval EFI_SUCCESS The packet is successfully processed.
862 @retval Others Some error occured.
867 IN DHCP_SERVICE
*DhcpSb
,
868 IN EFI_DHCP4_PACKET
*Packet
,
869 IN DHCP_PARAMETER
*Para
872 EFI_DHCP4_HEADER
*Head
;
875 Head
= &Packet
->Dhcp4
.Header
;
878 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
880 if (DHCP_IS_BOOTP (Para
) ||
881 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
884 Status
= EFI_SUCCESS
;
889 // If a NAK is received, transit to INIT and try again.
891 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
892 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
894 DhcpSb
->ClientAddr
= 0;
895 DhcpSb
->DhcpState
= Dhcp4Init
;
897 Status
= DhcpInitRequest (DhcpSb
);
902 // Check whether the ACK matches the selected offer
904 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
905 Status
= EFI_DEVICE_ERROR
;
909 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
910 if (EFI_ERROR (Status
)) {
915 // OK, get the parameter from server, record the lease
917 DhcpSb
->Para
= AllocateCopyPool (sizeof (DHCP_PARAMETER
), Para
);
918 if (DhcpSb
->Para
== NULL
) {
919 Status
= EFI_OUT_OF_RESOURCES
;
923 DhcpSb
->Selected
= Packet
;
924 Status
= DhcpLeaseAcquired (DhcpSb
);
925 if (EFI_ERROR (Status
)) {
929 DhcpSb
->IoStatus
= EFI_SUCCESS
;
930 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
940 Handle the received DHCP packets. This function drives the DHCP
943 @param UdpPacket The UDP packets received.
944 @param EndPoint The local/remote UDP access point
945 @param IoStatus The status of the UDP receive
946 @param Context The opaque parameter to the function.
952 UDP_END_POINT
*EndPoint
,
957 DHCP_SERVICE
*DhcpSb
;
958 EFI_DHCP4_HEADER
*Head
;
959 EFI_DHCP4_PACKET
*Packet
;
960 DHCP_PARAMETER
*Para
;
965 DhcpSb
= (DHCP_SERVICE
*) Context
;
968 // Don't restart receive if error occurs or DHCP is destoried.
970 if (EFI_ERROR (IoStatus
)) {
972 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
973 NetbufFree (UdpPacket
);
977 ASSERT (UdpPacket
!= NULL
);
979 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
984 // Validate the packet received
986 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
991 // Copy the DHCP message to a continuous memory block
993 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
994 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
996 if (Packet
== NULL
) {
1001 Head
= &Packet
->Dhcp4
.Header
;
1002 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1004 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1009 // Is this packet the answer to our packet?
1011 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1012 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1013 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1018 // Validate the options and retrieve the interested options
1021 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1022 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1023 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1029 // Call the handler for each state. The handler should return
1030 // EFI_SUCCESS if the process can go on no matter whether the
1031 // packet is ignored or not. If the return is EFI_ERROR, the
1032 // session will be terminated. Packet's ownership is handled
1033 // over to the handlers. If operation succeeds, the handler
1034 // must notify the user. It isn't necessary to do if EFI_ERROR
1035 // is returned because the DhcpEndSession will notify the user.
1037 Status
= EFI_SUCCESS
;
1039 switch (DhcpSb
->DhcpState
) {
1040 case Dhcp4Selecting
:
1041 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1044 case Dhcp4Requesting
:
1045 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1048 case Dhcp4InitReboot
:
1052 // Ignore the packet in INITREBOOT, INIT and BOUND states
1055 Status
= EFI_SUCCESS
;
1059 case Dhcp4Rebinding
:
1060 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1063 case Dhcp4Rebooting
:
1064 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1074 if (EFI_ERROR (Status
)) {
1075 NetbufFree (UdpPacket
);
1076 UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1077 DhcpEndSession (DhcpSb
, Status
);
1082 NetbufFree (UdpPacket
);
1084 if (Packet
!= NULL
) {
1088 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1090 if (EFI_ERROR (Status
)) {
1091 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1099 @param[in] Arg The packet to release
1112 Release the net buffer when packet is sent.
1114 @param UdpPacket The UDP packets received.
1115 @param EndPoint The local/remote UDP access point
1116 @param IoStatus The status of the UDP receive
1117 @param Context The opaque parameter to the function.
1123 UDP_END_POINT
*EndPoint
,
1124 EFI_STATUS IoStatus
,
1128 NetbufFree (Packet
);
1134 Build and transmit a DHCP message according to the current states.
1135 This function implement the Table 5. of RFC 2131. Always transits
1136 the state (as defined in Figure 5. of the same RFC) before sending
1137 a DHCP message. The table is adjusted accordingly.
1139 @param[in] DhcpSb The DHCP service instance
1140 @param[in] Seed The seed packet which the new packet is based on
1141 @param[in] Para The DHCP parameter of the Seed packet
1142 @param[in] Type The message type to send
1143 @param[in] Msg The human readable message to include in the packet
1146 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1147 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1148 @retval EFI_SUCCESS The message is sent
1149 @retval other Other error occurs
1154 IN DHCP_SERVICE
*DhcpSb
,
1155 IN EFI_DHCP4_PACKET
*Seed
,
1156 IN DHCP_PARAMETER
*Para
,
1161 EFI_DHCP4_CONFIG_DATA
*Config
;
1162 EFI_DHCP4_PACKET
*Packet
;
1163 EFI_DHCP4_PACKET
*NewPacket
;
1164 EFI_DHCP4_HEADER
*Head
;
1165 EFI_DHCP4_HEADER
*SeedHead
;
1167 UDP_END_POINT EndPoint
;
1178 // Allocate a big enough memory block to hold the DHCP packet
1180 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1183 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1186 Packet
= AllocatePool (Len
);
1188 if (Packet
== NULL
) {
1189 return EFI_OUT_OF_RESOURCES
;
1193 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1196 // Fill in the DHCP header fields
1198 Config
= &DhcpSb
->ActiveConfig
;
1202 SeedHead
= &Seed
->Dhcp4
.Header
;
1205 Head
= &Packet
->Dhcp4
.Header
;
1206 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1208 Head
->OpCode
= BOOTP_REQUEST
;
1209 Head
->HwType
= DhcpSb
->HwType
;
1210 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1211 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1212 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1214 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1215 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1218 // Append the DHCP message type
1220 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1221 Buf
= Packet
->Dhcp4
.Option
;
1222 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1225 // Append the serverid option if necessary:
1226 // 1. DHCP decline message
1227 // 2. DHCP release message
1228 // 3. DHCP request to confirm one lease.
1230 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1231 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))
1234 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1236 IpAddr
= HTONL (Para
->ServerId
);
1237 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1241 // Append the requested IP option if necessary:
1242 // 1. DHCP request to use the previously allocated address
1243 // 2. DHCP request to confirm one lease
1244 // 3. DHCP decline to decline one lease
1248 if (Type
== DHCP_MSG_REQUEST
) {
1249 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1250 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1252 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1253 ASSERT (SeedHead
!= NULL
);
1254 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1257 } else if (Type
== DHCP_MSG_DECLINE
) {
1258 ASSERT (SeedHead
!= NULL
);
1259 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1263 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1267 // Append the Max Message Length option if it isn't a DECLINE
1268 // or RELEASE to direct the server use large messages instead of
1269 // override the BOOTFILE and SERVER fields in the message head.
1271 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1272 MaxMsg
= HTONS (0xFF00);
1273 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1277 // Append the user's message if it isn't NULL
1280 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1281 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1285 // Append the user configured options
1287 if (DhcpSb
->UserOptionLen
!= 0) {
1288 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1290 // We can't use any option other than the client ID from user
1291 // if it is a DHCP decline or DHCP release .
1293 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1294 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1298 Buf
= DhcpAppendOption (
1300 Config
->OptionList
[Index
]->OpCode
,
1301 Config
->OptionList
[Index
]->Length
,
1302 Config
->OptionList
[Index
]->Data
1307 *(Buf
++) = DHCP_TAG_EOP
;
1308 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1311 // OK, the message is built, call the user to override it.
1313 Status
= EFI_SUCCESS
;
1316 if (Type
== DHCP_MSG_DISCOVER
) {
1317 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1319 } else if (Type
== DHCP_MSG_REQUEST
) {
1320 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1322 } else if (Type
== DHCP_MSG_DECLINE
) {
1323 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1326 if (EFI_ERROR (Status
)) {
1331 if (NewPacket
!= NULL
) {
1337 // Save the Client Address will be sent out
1340 &DhcpSb
->ClientAddressSendOut
[0],
1341 &Packet
->Dhcp4
.Header
.ClientHwAddr
[0],
1342 Packet
->Dhcp4
.Header
.HwAddrLen
1347 // Wrap it into a netbuf then send it.
1349 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1350 Frag
.Len
= Packet
->Length
;
1351 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1355 return EFI_OUT_OF_RESOURCES
;
1359 // Save it as the last sent packet for retransmission
1361 if (DhcpSb
->LastPacket
!= NULL
) {
1362 FreePool (DhcpSb
->LastPacket
);
1365 DhcpSb
->LastPacket
= Packet
;
1366 DhcpSetTransmitTimer (DhcpSb
);
1369 // Broadcast the message, unless we know the server address.
1370 // Use the lease UdpIo port to send the unicast packet.
1372 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1373 EndPoint
.LocalAddr
.Addr
[0] = 0;
1374 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1375 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1376 UdpIo
= DhcpSb
->UdpIo
;
1378 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1379 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1380 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1381 UdpIo
= DhcpSb
->LeaseIoPort
;
1384 ASSERT (UdpIo
!= NULL
);
1387 Status
= UdpIoSendDatagram (
1396 if (EFI_ERROR (Status
)) {
1398 return EFI_ACCESS_DENIED
;
1406 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1407 will be retransmitted.
1409 @param[in] DhcpSb The DHCP service instance
1411 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1412 @retval EFI_SUCCESS The packet is retransmitted.
1417 IN DHCP_SERVICE
*DhcpSb
1421 UDP_END_POINT EndPoint
;
1426 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1428 DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
= HTONS (*(UINT16
*)(&DhcpSb
->LastTimeout
));
1431 // Wrap it into a netbuf then send it.
1433 Frag
.Bulk
= (UINT8
*) &DhcpSb
->LastPacket
->Dhcp4
.Header
;
1434 Frag
.Len
= DhcpSb
->LastPacket
->Length
;
1435 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, DhcpSb
->LastPacket
);
1438 return EFI_OUT_OF_RESOURCES
;
1442 // Broadcast the message, unless we know the server address.
1444 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1445 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1446 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1447 EndPoint
.LocalAddr
.Addr
[0] = 0;
1448 UdpIo
= DhcpSb
->UdpIo
;
1450 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1451 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1452 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1453 UdpIo
= DhcpSb
->LeaseIoPort
;
1456 ASSERT (UdpIo
!= NULL
);
1459 Status
= UdpIoSendDatagram (
1468 if (EFI_ERROR (Status
)) {
1470 return EFI_ACCESS_DENIED
;
1478 Each DHCP service has three timer. Two of them are count down timer.
1479 One for the packet retransmission. The other is to collect the offers.
1480 The third timer increaments the lease life which is compared to T1, T2,
1481 and lease to determine the time to renew and rebind the lease.
1482 DhcpOnTimerTick will be called once every second.
1484 @param[in] Event The timer event
1485 @param[in] 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 retransmit timer
1505 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1508 // Select offer at each timeout if any offer received.
1510 if (DhcpSb
->DhcpState
== Dhcp4Selecting
&& DhcpSb
->LastOffer
!= NULL
) {
1512 Status
= DhcpChooseOffer (DhcpSb
);
1514 if (EFI_ERROR(Status
)) {
1515 FreePool (DhcpSb
->LastOffer
);
1516 DhcpSb
->LastOffer
= NULL
;
1522 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1524 // Still has another try
1526 DhcpRetransmit (DhcpSb
);
1527 DhcpSetTransmitTimer (DhcpSb
);
1529 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1532 // Retransmission failed, if the DHCP request is initiated by
1533 // user, adjust the current state according to the lease life.
1534 // Otherwise do nothing to wait the lease to timeout
1536 if (DhcpSb
->ExtraRefresh
!= 0) {
1537 Status
= EFI_SUCCESS
;
1539 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1540 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1542 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1543 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1545 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1546 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1553 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1554 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1562 // If an address has been acquired, check whether need to
1563 // refresh or whether it has expired.
1565 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1566 DhcpSb
->LeaseLife
++;
1569 // Don't timeout the lease, only count the life if user is
1570 // requesting extra renew/rebind. Adjust the state after that.
1572 if (DhcpSb
->ExtraRefresh
!= 0) {
1576 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1578 // Lease expires, end the session
1582 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1584 // T2 expires, transit to rebinding then send a REQUEST to any server
1586 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1590 Status
= DhcpSendMessage (
1598 if (EFI_ERROR (Status
)) {
1602 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1604 // T1 expires, transit to renewing, then send a REQUEST to the server
1606 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1610 Status
= DhcpSendMessage (
1618 if (EFI_ERROR (Status
)) {
1625 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1626 Instance
->Timeout
--;
1627 if (Instance
->Timeout
== 0) {
1628 PxeDhcpDone (Instance
);
1635 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);