2 EFI DHCP protocol implementation.
4 Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
5 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
));
41 // Clear initial time to make sure that elapsed-time is set to 0 for first Discover or REQUEST message.
43 DhcpSb
->ActiveChild
->ElaspedTime
= 0;
45 if (DhcpSb
->DhcpState
== Dhcp4Init
) {
46 DhcpSetState (DhcpSb
, Dhcp4Selecting
, FALSE
);
47 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_DISCOVER
, NULL
);
49 if (EFI_ERROR (Status
)) {
50 DhcpSb
->DhcpState
= Dhcp4Init
;
54 DhcpSetState (DhcpSb
, Dhcp4Rebooting
, FALSE
);
55 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_REQUEST
, NULL
);
57 if (EFI_ERROR (Status
)) {
58 DhcpSb
->DhcpState
= Dhcp4InitReboot
;
68 Call user provided callback function, and return the value the
69 function returns. If the user doesn't provide a callback, a
70 proper return value is selected to let the caller continue the
73 @param[in] DhcpSb The DHCP service instance
74 @param[in] Event The event as defined in the spec
75 @param[in] Packet The current packet trigger the event
76 @param[out] NewPacket The user's return new packet
78 @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
79 @retval EFI_SUCCESS The user function returns success.
80 @retval EFI_ABORTED The user function ask it to abort.
85 IN DHCP_SERVICE
*DhcpSb
,
86 IN EFI_DHCP4_EVENT Event
,
87 IN EFI_DHCP4_PACKET
*Packet
, OPTIONAL
88 OUT EFI_DHCP4_PACKET
**NewPacket OPTIONAL
91 EFI_DHCP4_CONFIG_DATA
*Config
;
94 if (NewPacket
!= NULL
) {
99 // If user doesn't provide the call back function, return the value
100 // that directs the client to continue the normal process.
101 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
102 // the offers and select a offer, EFI_NOT_READY tells the client to
103 // collect more offers.
105 Config
= &DhcpSb
->ActiveConfig
;
107 if (Config
->Dhcp4Callback
== NULL
) {
108 if (Event
== Dhcp4RcvdOffer
) {
109 return EFI_NOT_READY
;
115 Status
= Config
->Dhcp4Callback (
116 &DhcpSb
->ActiveChild
->Dhcp4Protocol
,
117 Config
->CallbackContext
,
118 (EFI_DHCP4_STATE
) DhcpSb
->DhcpState
,
125 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
126 // and EFI_ABORTED. If it returns values other than those, assume
127 // it to be EFI_ABORTED.
129 if ((Status
== EFI_SUCCESS
) || (Status
== EFI_NOT_READY
)) {
138 Notify the user about the operation result.
140 @param DhcpSb DHCP service instance
141 @param Which Which notify function to signal
146 IN DHCP_SERVICE
*DhcpSb
,
150 DHCP_PROTOCOL
*Child
;
152 if ((Child
= DhcpSb
->ActiveChild
) == NULL
) {
156 if ((Child
->CompletionEvent
!= NULL
) &&
157 ((Which
== DHCP_NOTIFY_COMPLETION
) || (Which
== DHCP_NOTIFY_ALL
))
160 gBS
->SignalEvent (Child
->CompletionEvent
);
161 Child
->CompletionEvent
= NULL
;
164 if ((Child
->RenewRebindEvent
!= NULL
) &&
165 ((Which
== DHCP_NOTIFY_RENEWREBIND
) || (Which
== DHCP_NOTIFY_ALL
))
168 gBS
->SignalEvent (Child
->RenewRebindEvent
);
169 Child
->RenewRebindEvent
= NULL
;
176 Set the DHCP state. If CallUser is true, it will try to notify
177 the user before change the state by DhcpNotifyUser. It returns
178 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
179 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
180 the return value of this function.
182 @param DhcpSb The DHCP service instance
183 @param State The new DHCP state to change to
184 @param CallUser Whether we need to call user
186 @retval EFI_SUCCESS The state is changed
187 @retval EFI_ABORTED The user asks to abort the DHCP process.
192 IN OUT DHCP_SERVICE
*DhcpSb
,
200 Status
= EFI_SUCCESS
;
202 if (State
== Dhcp4Renewing
) {
203 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRenewing
, NULL
, NULL
);
205 } else if (State
== Dhcp4Rebinding
) {
206 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRebinding
, NULL
, NULL
);
208 } else if (State
== Dhcp4Bound
) {
209 Status
= DhcpCallUser (DhcpSb
, Dhcp4BoundCompleted
, NULL
, NULL
);
213 if (EFI_ERROR (Status
)) {
219 // Update the retransmission timer during the state transition.
220 // This will clear the retry count. This is also why the rule
221 // first transit the state, then send packets.
223 if (State
== Dhcp4Selecting
) {
224 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.DiscoverTryCount
;
226 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.RequestTryCount
;
229 if (DhcpSb
->MaxRetries
== 0) {
230 DhcpSb
->MaxRetries
= 4;
233 DhcpSb
->CurRetry
= 0;
234 DhcpSb
->PacketToLive
= 0;
235 DhcpSb
->LastTimeout
= 0;
236 DhcpSb
->DhcpState
= State
;
242 Set the retransmit timer for the packet. It will select from either
243 the discover timeouts/request timeouts or the default timeout values.
245 @param DhcpSb The DHCP service instance.
249 DhcpSetTransmitTimer (
250 IN OUT DHCP_SERVICE
*DhcpSb
255 ASSERT (DhcpSb
->MaxRetries
> DhcpSb
->CurRetry
);
257 if (DhcpSb
->DhcpState
== Dhcp4Selecting
) {
258 Times
= DhcpSb
->ActiveConfig
.DiscoverTimeout
;
260 Times
= DhcpSb
->ActiveConfig
.RequestTimeout
;
264 Times
= mDhcp4DefaultTimeout
;
267 DhcpSb
->PacketToLive
= Times
[DhcpSb
->CurRetry
];
268 DhcpSb
->LastTimeout
= DhcpSb
->PacketToLive
;
274 Compute the lease. If the server grants a permanent lease, just
275 process it as a normal timeout value since the lease will last
278 @param DhcpSb The DHCP service instance
279 @param Para The DHCP parameter extracted from the server's
284 IN OUT DHCP_SERVICE
*DhcpSb
,
285 IN DHCP_PARAMETER
*Para
288 ASSERT (Para
!= NULL
);
290 DhcpSb
->Lease
= Para
->Lease
;
291 DhcpSb
->T2
= Para
->T2
;
292 DhcpSb
->T1
= Para
->T1
;
294 if (DhcpSb
->Lease
== 0) {
295 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
298 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
299 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
302 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
303 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
309 Configure a UDP IO port to use the acquired lease address.
310 DHCP driver needs this port to unicast packet to the server
311 such as DHCP release.
313 @param[in] UdpIo The UDP IO to configure
314 @param[in] Context Dhcp service instance.
316 @retval EFI_SUCCESS The UDP IO port is successfully configured.
317 @retval Others It failed to configure the port.
322 DhcpConfigLeaseIoPort (
327 EFI_UDP4_CONFIG_DATA UdpConfigData
;
328 EFI_IPv4_ADDRESS Subnet
;
329 EFI_IPv4_ADDRESS Gateway
;
330 DHCP_SERVICE
*DhcpSb
;
334 DhcpSb
= (DHCP_SERVICE
*) Context
;
336 UdpConfigData
.AcceptBroadcast
= FALSE
;
337 UdpConfigData
.AcceptPromiscuous
= FALSE
;
338 UdpConfigData
.AcceptAnyPort
= FALSE
;
339 UdpConfigData
.AllowDuplicatePort
= TRUE
;
340 UdpConfigData
.TypeOfService
= 0;
341 UdpConfigData
.TimeToLive
= 64;
342 UdpConfigData
.DoNotFragment
= FALSE
;
343 UdpConfigData
.ReceiveTimeout
= 1;
344 UdpConfigData
.TransmitTimeout
= 0;
346 UdpConfigData
.UseDefaultAddress
= FALSE
;
347 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
348 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
350 Ip
= HTONL (DhcpSb
->ClientAddr
);
351 CopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
353 Ip
= HTONL (DhcpSb
->Netmask
);
354 CopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
356 ZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
358 Status
= UdpIo
->Protocol
.Udp4
->Configure (UdpIo
->Protocol
.Udp4
, &UdpConfigData
);
360 if (EFI_ERROR (Status
)) {
365 // Add a default route if received from the server.
367 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
368 ZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
370 Ip
= HTONL (DhcpSb
->Para
->Router
);
371 CopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
373 UdpIo
->Protocol
.Udp4
->Routes (UdpIo
->Protocol
.Udp4
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
381 Update the lease states when a new lease is acquired. It will not only
382 save the acquired the address and lease time, it will also create a UDP
383 child to provide address resolution for the address.
385 @param DhcpSb The DHCP service instance
387 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
388 @retval EFI_SUCCESS The lease is recorded.
393 IN OUT DHCP_SERVICE
*DhcpSb
398 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
400 if (DhcpSb
->Para
!= NULL
) {
401 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
402 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
405 if (DhcpSb
->Netmask
== 0) {
406 Class
= NetGetIpClass (DhcpSb
->ClientAddr
);
407 DhcpSb
->Netmask
= gIp4AllMasks
[Class
<< 3];
410 if (DhcpSb
->LeaseIoPort
!= NULL
) {
411 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
415 // Create a UDP/IP child to provide ARP service for the Leased IP,
416 // and transmit unicast packet with it as source address. Don't
417 // start receive on this port, the queued packet will be timeout.
419 DhcpSb
->LeaseIoPort
= UdpIoCreateIo (
422 DhcpConfigLeaseIoPort
,
427 if (DhcpSb
->LeaseIoPort
== NULL
) {
428 return EFI_OUT_OF_RESOURCES
;
431 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
432 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
435 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
440 Clean up the DHCP related states, IoStatus isn't reset.
442 @param DhcpSb The DHCP instance service.
447 IN DHCP_SERVICE
*DhcpSb
450 DhcpSb
->DhcpState
= Dhcp4Init
;
451 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
452 DhcpSb
->ClientAddr
= 0;
454 DhcpSb
->ServerAddr
= 0;
456 if (DhcpSb
->LastOffer
!= NULL
) {
457 FreePool (DhcpSb
->LastOffer
);
458 DhcpSb
->LastOffer
= NULL
;
461 if (DhcpSb
->Selected
!= NULL
) {
462 FreePool (DhcpSb
->Selected
);
463 DhcpSb
->Selected
= NULL
;
466 if (DhcpSb
->Para
!= NULL
) {
467 FreePool (DhcpSb
->Para
);
474 DhcpSb
->ExtraRefresh
= FALSE
;
476 if (DhcpSb
->LeaseIoPort
!= NULL
) {
477 UdpIoFreeIo (DhcpSb
->LeaseIoPort
);
478 DhcpSb
->LeaseIoPort
= NULL
;
481 if (DhcpSb
->LastPacket
!= NULL
) {
482 FreePool (DhcpSb
->LastPacket
);
483 DhcpSb
->LastPacket
= NULL
;
486 DhcpSb
->PacketToLive
= 0;
487 DhcpSb
->LastTimeout
= 0;
488 DhcpSb
->CurRetry
= 0;
489 DhcpSb
->MaxRetries
= 0;
490 DhcpSb
->LeaseLife
= 0;
493 // Clean active config data.
495 DhcpCleanConfigure (&DhcpSb
->ActiveConfig
);
500 Select a offer among all the offers collected. If the offer selected is
501 of BOOTP, the lease is recorded and user notified. If the offer is of
502 DHCP, it will request the offer from the server.
504 @param[in] DhcpSb The DHCP service instance.
506 @retval EFI_SUCCESS One of the offer is selected.
511 IN DHCP_SERVICE
*DhcpSb
514 EFI_DHCP4_PACKET
*Selected
;
515 EFI_DHCP4_PACKET
*NewPacket
;
516 EFI_DHCP4_PACKET
*TempPacket
;
519 ASSERT (DhcpSb
->LastOffer
!= NULL
);
522 // User will cache previous offers if he wants to select
523 // from multiple offers. If user provides an invalid packet,
524 // use the last offer, otherwise use the provided packet.
527 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
529 if (EFI_ERROR (Status
)) {
533 Selected
= DhcpSb
->LastOffer
;
535 if ((NewPacket
!= NULL
) && !EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
536 TempPacket
= (EFI_DHCP4_PACKET
*) AllocatePool (NewPacket
->Size
);
537 if (TempPacket
!= NULL
) {
538 CopyMem (TempPacket
, NewPacket
, NewPacket
->Size
);
540 Selected
= TempPacket
;
544 DhcpSb
->Selected
= Selected
;
545 DhcpSb
->LastOffer
= NULL
;
547 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
550 // A bootp offer has been selected, save the lease status,
551 // enter bound state then notify the user.
553 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
554 Status
= DhcpLeaseAcquired (DhcpSb
);
556 if (EFI_ERROR (Status
)) {
560 DhcpSb
->IoStatus
= EFI_SUCCESS
;
561 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
566 // Send a DHCP requests
568 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
570 if (EFI_ERROR (Status
)) {
574 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
579 Terminate the current address acquire. All the allocated resources
580 are released. Be careful when calling this function. A rule related
581 to this is: only call DhcpEndSession at the highest level, such as
582 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
584 @param[in] DhcpSb The DHCP service instance
585 @param[in] Status The result of the DHCP process.
590 IN DHCP_SERVICE
*DhcpSb
,
594 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
595 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
597 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
600 DhcpCleanLease (DhcpSb
);
602 DhcpSb
->IoStatus
= Status
;
603 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
608 Handle packets in DHCP select state.
610 @param[in] DhcpSb The DHCP service instance
611 @param[in] Packet The DHCP packet received
612 @param[in] Para The DHCP parameter extracted from the packet. That
613 is, all the option value that we care.
615 @retval EFI_SUCCESS The packet is successfully processed.
616 @retval Others Some error occured.
621 IN DHCP_SERVICE
*DhcpSb
,
622 IN EFI_DHCP4_PACKET
*Packet
,
623 IN DHCP_PARAMETER
*Para
628 Status
= EFI_SUCCESS
;
631 // First validate the message:
632 // 1. the offer is a unicast
633 // 2. if it is a DHCP message, it must contains a server ID.
634 // Don't return a error for these two case otherwise the session is ended.
636 if (!DHCP_IS_BOOTP (Para
) &&
637 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))
643 // Call the user's callback. The action according to the return is as:
644 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
645 // 2. EFI_NOT_READY: wait for more offers
646 // 3. EFI_ABORTED: abort the address acquiring.
648 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
650 if (Status
== EFI_SUCCESS
) {
651 if (DhcpSb
->LastOffer
!= NULL
) {
652 FreePool (DhcpSb
->LastOffer
);
655 DhcpSb
->LastOffer
= Packet
;
657 return DhcpChooseOffer (DhcpSb
);
659 } else if (Status
== EFI_NOT_READY
) {
660 if (DhcpSb
->LastOffer
!= NULL
) {
661 FreePool (DhcpSb
->LastOffer
);
664 DhcpSb
->LastOffer
= Packet
;
666 } else if (Status
== EFI_ABORTED
) {
668 // DhcpInput will end the session upon error return. Remember
669 // only to call DhcpEndSession at the top level call.
683 Handle packets in DHCP request state.
685 @param[in] DhcpSb The DHCP service instance
686 @param[in] Packet The DHCP packet received
687 @param[in] Para The DHCP parameter extracted from the packet. That
688 is, all the option value that we care.
690 @retval EFI_SUCCESS The packet is successfully processed.
691 @retval Others Some error occured.
696 IN DHCP_SERVICE
*DhcpSb
,
697 IN EFI_DHCP4_PACKET
*Packet
,
698 IN DHCP_PARAMETER
*Para
701 EFI_DHCP4_HEADER
*Head
;
702 EFI_DHCP4_HEADER
*Selected
;
706 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
708 Head
= &Packet
->Dhcp4
.Header
;
709 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
712 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
714 if (DHCP_IS_BOOTP (Para
) ||
715 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
716 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
719 Status
= EFI_SUCCESS
;
724 // Received a NAK, end the session no matter what the user returns
726 Status
= EFI_DEVICE_ERROR
;
728 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
729 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
734 // Check whether the ACK matches the selected offer
738 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
739 Message
= (UINT8
*) "Lease confirmed isn't the same as that in the offer";
743 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
745 if (EFI_ERROR (Status
)) {
746 Message
= (UINT8
*) "Lease is denied upon received ACK";
751 // Record the lease, transit to BOUND state, then notify the user
753 Status
= DhcpLeaseAcquired (DhcpSb
);
755 if (EFI_ERROR (Status
)) {
756 Message
= (UINT8
*) "Lease is denied upon entering bound";
760 DhcpSb
->IoStatus
= EFI_SUCCESS
;
761 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
767 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
776 Handle packets in DHCP renew/rebound state.
778 @param[in] DhcpSb The DHCP service instance
779 @param[in] Packet The DHCP packet received
780 @param[in] Para The DHCP parameter extracted from the packet. That
781 is, all the option value that we care.
783 @retval EFI_SUCCESS The packet is successfully processed.
784 @retval Others Some error occured.
788 DhcpHandleRenewRebind (
789 IN DHCP_SERVICE
*DhcpSb
,
790 IN EFI_DHCP4_PACKET
*Packet
,
791 IN DHCP_PARAMETER
*Para
794 EFI_DHCP4_HEADER
*Head
;
795 EFI_DHCP4_HEADER
*Selected
;
798 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
800 Head
= &Packet
->Dhcp4
.Header
;
801 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
804 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
806 if (DHCP_IS_BOOTP (Para
) ||
807 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
808 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
811 Status
= EFI_SUCCESS
;
816 // Received a NAK, ignore the user's return then terminate the process
818 Status
= EFI_DEVICE_ERROR
;
820 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
821 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
826 // The lease is different from the selected. Don't send a DECLINE
827 // since it isn't existed in the client's FSM.
829 if (!EFI_IP4_EQUAL (&Head
->YourAddr
, &Selected
->YourAddr
)) {
833 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
835 if (EFI_ERROR (Status
)) {
840 // Record the lease, start timer for T1 and T2,
842 DhcpComputeLease (DhcpSb
, Para
);
843 DhcpSb
->LeaseLife
= 0;
844 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
846 if (DhcpSb
->ExtraRefresh
!= 0) {
847 DhcpSb
->ExtraRefresh
= FALSE
;
849 DhcpSb
->IoStatus
= EFI_SUCCESS
;
850 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
860 Handle packets in DHCP reboot state.
862 @param[in] DhcpSb The DHCP service instance
863 @param[in] Packet The DHCP packet received
864 @param[in] Para The DHCP parameter extracted from the packet. That
865 is, all the option value that we care.
867 @retval EFI_SUCCESS The packet is successfully processed.
868 @retval Others Some error occured.
873 IN DHCP_SERVICE
*DhcpSb
,
874 IN EFI_DHCP4_PACKET
*Packet
,
875 IN DHCP_PARAMETER
*Para
878 EFI_DHCP4_HEADER
*Head
;
881 Head
= &Packet
->Dhcp4
.Header
;
884 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
886 if (DHCP_IS_BOOTP (Para
) ||
887 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))
890 Status
= EFI_SUCCESS
;
895 // If a NAK is received, transit to INIT and try again.
897 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
898 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
900 DhcpSb
->ClientAddr
= 0;
901 DhcpSb
->DhcpState
= Dhcp4Init
;
903 Status
= DhcpInitRequest (DhcpSb
);
908 // Check whether the ACK matches the selected offer
910 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
911 Status
= EFI_DEVICE_ERROR
;
915 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
916 if (EFI_ERROR (Status
)) {
921 // OK, get the parameter from server, record the lease
923 DhcpSb
->Para
= AllocateCopyPool (sizeof (DHCP_PARAMETER
), Para
);
924 if (DhcpSb
->Para
== NULL
) {
925 Status
= EFI_OUT_OF_RESOURCES
;
929 DhcpSb
->Selected
= Packet
;
930 Status
= DhcpLeaseAcquired (DhcpSb
);
931 if (EFI_ERROR (Status
)) {
935 DhcpSb
->IoStatus
= EFI_SUCCESS
;
936 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
946 Handle the received DHCP packets. This function drives the DHCP
949 @param UdpPacket The UDP packets received.
950 @param EndPoint The local/remote UDP access point
951 @param IoStatus The status of the UDP receive
952 @param Context The opaque parameter to the function.
959 UDP_END_POINT
*EndPoint
,
964 DHCP_SERVICE
*DhcpSb
;
965 EFI_DHCP4_HEADER
*Head
;
966 EFI_DHCP4_PACKET
*Packet
;
967 DHCP_PARAMETER
*Para
;
972 DhcpSb
= (DHCP_SERVICE
*) Context
;
975 // Don't restart receive if error occurs or DHCP is destroyed.
977 if (EFI_ERROR (IoStatus
)) {
979 } else if (DhcpSb
->ServiceState
== DHCP_DESTROY
) {
980 NetbufFree (UdpPacket
);
984 ASSERT (UdpPacket
!= NULL
);
986 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
991 // Validate the packet received
993 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
998 // Copy the DHCP message to a continuous memory block
1000 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
1001 Packet
= (EFI_DHCP4_PACKET
*) AllocatePool (Len
);
1003 if (Packet
== NULL
) {
1008 Head
= &Packet
->Dhcp4
.Header
;
1009 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1011 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1016 // Is this packet the answer to our packet?
1018 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1019 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1020 (CompareMem (DhcpSb
->ClientAddressSendOut
, Head
->ClientHwAddr
, Head
->HwAddrLen
) != 0)) {
1025 // Validate the options and retrieve the interested options
1028 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1029 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1030 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1036 // Call the handler for each state. The handler should return
1037 // EFI_SUCCESS if the process can go on no matter whether the
1038 // packet is ignored or not. If the return is EFI_ERROR, the
1039 // session will be terminated. Packet's ownership is handled
1040 // over to the handlers. If operation succeeds, the handler
1041 // must notify the user. It isn't necessary to do if EFI_ERROR
1042 // is returned because the DhcpEndSession will notify the user.
1044 Status
= EFI_SUCCESS
;
1046 switch (DhcpSb
->DhcpState
) {
1047 case Dhcp4Selecting
:
1048 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1051 case Dhcp4Requesting
:
1052 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1055 case Dhcp4InitReboot
:
1059 // Ignore the packet in INITREBOOT, INIT and BOUND states
1062 Status
= EFI_SUCCESS
;
1066 case Dhcp4Rebinding
:
1067 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1070 case Dhcp4Rebooting
:
1071 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1081 if (EFI_ERROR (Status
)) {
1082 NetbufFree (UdpPacket
);
1083 UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1084 DhcpEndSession (DhcpSb
, Status
);
1089 NetbufFree (UdpPacket
);
1091 if (Packet
!= NULL
) {
1095 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1097 if (EFI_ERROR (Status
)) {
1098 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1106 @param[in] Arg The packet to release
1120 Release the net buffer when packet is sent.
1122 @param UdpPacket The UDP packets received.
1123 @param EndPoint The local/remote UDP access point
1124 @param IoStatus The status of the UDP receive
1125 @param Context The opaque parameter to the function.
1132 UDP_END_POINT
*EndPoint
,
1133 EFI_STATUS IoStatus
,
1137 NetbufFree (Packet
);
1143 Build and transmit a DHCP message according to the current states.
1144 This function implement the Table 5. of RFC 2131. Always transits
1145 the state (as defined in Figure 5. of the same RFC) before sending
1146 a DHCP message. The table is adjusted accordingly.
1148 @param[in] DhcpSb The DHCP service instance
1149 @param[in] Seed The seed packet which the new packet is based on
1150 @param[in] Para The DHCP parameter of the Seed packet
1151 @param[in] Type The message type to send
1152 @param[in] Msg The human readable message to include in the packet
1155 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1156 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1157 @retval EFI_SUCCESS The message is sent
1158 @retval other Other error occurs
1163 IN DHCP_SERVICE
*DhcpSb
,
1164 IN EFI_DHCP4_PACKET
*Seed
,
1165 IN DHCP_PARAMETER
*Para
,
1170 EFI_DHCP4_CONFIG_DATA
*Config
;
1171 EFI_DHCP4_PACKET
*Packet
;
1172 EFI_DHCP4_PACKET
*NewPacket
;
1173 EFI_DHCP4_HEADER
*Head
;
1174 EFI_DHCP4_HEADER
*SeedHead
;
1176 UDP_END_POINT EndPoint
;
1187 // Allocate a big enough memory block to hold the DHCP packet
1189 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1192 Len
+= (UINT32
)AsciiStrLen ((CHAR8
*) Msg
);
1195 Packet
= AllocatePool (Len
);
1197 if (Packet
== NULL
) {
1198 return EFI_OUT_OF_RESOURCES
;
1202 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1205 // Fill in the DHCP header fields
1207 Config
= &DhcpSb
->ActiveConfig
;
1211 SeedHead
= &Seed
->Dhcp4
.Header
;
1214 Head
= &Packet
->Dhcp4
.Header
;
1215 ZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1217 Head
->OpCode
= BOOTP_REQUEST
;
1218 Head
->HwType
= DhcpSb
->HwType
;
1219 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1220 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1221 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1223 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1224 CopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1226 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) {
1228 } else if ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
)) {
1230 // Use the same value as the original DHCPDISCOVER message.
1232 Head
->Seconds
= DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
;
1234 SetElapsedTime(&Head
->Seconds
, DhcpSb
->ActiveChild
);
1238 // Append the DHCP message type
1240 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1241 Buf
= Packet
->Dhcp4
.Option
;
1242 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1245 // Append the serverid option if necessary:
1246 // 1. DHCP decline message
1247 // 2. DHCP release message
1248 // 3. DHCP request to confirm one lease.
1250 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1251 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))
1254 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1256 IpAddr
= HTONL (Para
->ServerId
);
1257 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1261 // Append the requested IP option if necessary:
1262 // 1. DHCP request to use the previously allocated address
1263 // 2. DHCP request to confirm one lease
1264 // 3. DHCP decline to decline one lease
1268 if (Type
== DHCP_MSG_REQUEST
) {
1269 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1270 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1272 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1273 ASSERT (SeedHead
!= NULL
);
1274 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1277 } else if (Type
== DHCP_MSG_DECLINE
) {
1278 ASSERT (SeedHead
!= NULL
);
1279 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1283 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1287 // Append the Max Message Length option if it isn't a DECLINE
1288 // or RELEASE to direct the server use large messages instead of
1289 // override the BOOTFILE and SERVER fields in the message head.
1291 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1292 MaxMsg
= HTONS (0xFF00);
1293 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1297 // Append the user's message if it isn't NULL
1300 Len
= MIN ((UINT32
) AsciiStrLen ((CHAR8
*) Msg
), 255);
1301 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1305 // Append the user configured options
1307 if (DhcpSb
->UserOptionLen
!= 0) {
1308 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1310 // We can't use any option other than the client ID from user
1311 // if it is a DHCP decline or DHCP release .
1313 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1314 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1318 Buf
= DhcpAppendOption (
1320 Config
->OptionList
[Index
]->OpCode
,
1321 Config
->OptionList
[Index
]->Length
,
1322 Config
->OptionList
[Index
]->Data
1327 *(Buf
++) = DHCP_TAG_EOP
;
1328 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1331 // OK, the message is built, call the user to override it.
1333 Status
= EFI_SUCCESS
;
1336 if (Type
== DHCP_MSG_DISCOVER
) {
1337 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1339 } else if (Type
== DHCP_MSG_REQUEST
) {
1340 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1342 } else if (Type
== DHCP_MSG_DECLINE
) {
1343 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1346 if (EFI_ERROR (Status
)) {
1351 if (NewPacket
!= NULL
) {
1357 // Save the Client Address will be sent out
1360 &DhcpSb
->ClientAddressSendOut
[0],
1361 &Packet
->Dhcp4
.Header
.ClientHwAddr
[0],
1362 Packet
->Dhcp4
.Header
.HwAddrLen
1367 // Wrap it into a netbuf then send it.
1369 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1370 Frag
.Len
= Packet
->Length
;
1371 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1375 return EFI_OUT_OF_RESOURCES
;
1379 // Save it as the last sent packet for retransmission
1381 if (DhcpSb
->LastPacket
!= NULL
) {
1382 FreePool (DhcpSb
->LastPacket
);
1385 DhcpSb
->LastPacket
= Packet
;
1386 DhcpSetTransmitTimer (DhcpSb
);
1389 // Broadcast the message, unless we know the server address.
1390 // Use the lease UdpIo port to send the unicast packet.
1392 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1393 EndPoint
.LocalAddr
.Addr
[0] = 0;
1394 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1395 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1396 UdpIo
= DhcpSb
->UdpIo
;
1398 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1399 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1400 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1401 UdpIo
= DhcpSb
->LeaseIoPort
;
1404 ASSERT (UdpIo
!= NULL
);
1407 Status
= UdpIoSendDatagram (
1416 if (EFI_ERROR (Status
)) {
1418 return EFI_ACCESS_DENIED
;
1426 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1427 will be retransmitted.
1429 @param[in] DhcpSb The DHCP service instance
1431 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1432 @retval EFI_SUCCESS The packet is retransmitted.
1437 IN DHCP_SERVICE
*DhcpSb
1441 UDP_END_POINT EndPoint
;
1446 ASSERT (DhcpSb
->LastPacket
!= NULL
);
1449 // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.
1451 if (DhcpSb
->DhcpState
!= Dhcp4Requesting
) {
1452 SetElapsedTime(&DhcpSb
->LastPacket
->Dhcp4
.Header
.Seconds
, DhcpSb
->ActiveChild
);
1456 // Wrap it into a netbuf then send it.
1458 Frag
.Bulk
= (UINT8
*) &DhcpSb
->LastPacket
->Dhcp4
.Header
;
1459 Frag
.Len
= DhcpSb
->LastPacket
->Length
;
1460 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, DhcpSb
->LastPacket
);
1463 return EFI_OUT_OF_RESOURCES
;
1467 // Broadcast the message, unless we know the server address.
1469 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1470 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1471 EndPoint
.RemoteAddr
.Addr
[0] = 0xffffffff;
1472 EndPoint
.LocalAddr
.Addr
[0] = 0;
1473 UdpIo
= DhcpSb
->UdpIo
;
1475 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1476 EndPoint
.RemoteAddr
.Addr
[0] = DhcpSb
->ServerAddr
;
1477 EndPoint
.LocalAddr
.Addr
[0] = DhcpSb
->ClientAddr
;
1478 UdpIo
= DhcpSb
->LeaseIoPort
;
1481 ASSERT (UdpIo
!= NULL
);
1484 Status
= UdpIoSendDatagram (
1493 if (EFI_ERROR (Status
)) {
1495 return EFI_ACCESS_DENIED
;
1503 Each DHCP service has three timer. Two of them are count down timer.
1504 One for the packet retransmission. The other is to collect the offers.
1505 The third timer increaments the lease life which is compared to T1, T2,
1506 and lease to determine the time to renew and rebind the lease.
1507 DhcpOnTimerTick will be called once every second.
1509 @param[in] Event The timer event
1510 @param[in] Context The context, which is the DHCP service instance.
1520 DHCP_SERVICE
*DhcpSb
;
1521 DHCP_PROTOCOL
*Instance
;
1524 DhcpSb
= (DHCP_SERVICE
*) Context
;
1525 Instance
= DhcpSb
->ActiveChild
;
1528 // 0xffff is the maximum supported value for elapsed time according to RFC.
1530 if (Instance
!= NULL
&& Instance
->ElaspedTime
< 0xffff) {
1531 Instance
->ElaspedTime
++;
1535 // Check the retransmit timer
1537 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1540 // Select offer at each timeout if any offer received.
1542 if (DhcpSb
->DhcpState
== Dhcp4Selecting
&& DhcpSb
->LastOffer
!= NULL
) {
1544 Status
= DhcpChooseOffer (DhcpSb
);
1546 if (EFI_ERROR(Status
)) {
1547 if (DhcpSb
->LastOffer
!= NULL
) {
1548 FreePool (DhcpSb
->LastOffer
);
1549 DhcpSb
->LastOffer
= NULL
;
1556 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1558 // Still has another try
1560 DhcpRetransmit (DhcpSb
);
1561 DhcpSetTransmitTimer (DhcpSb
);
1563 } else if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1566 // Retransmission failed, if the DHCP request is initiated by
1567 // user, adjust the current state according to the lease life.
1568 // Otherwise do nothing to wait the lease to timeout
1570 if (DhcpSb
->ExtraRefresh
!= 0) {
1571 Status
= EFI_SUCCESS
;
1573 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1574 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1576 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1577 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1579 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1580 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1587 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1588 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1596 // If an address has been acquired, check whether need to
1597 // refresh or whether it has expired.
1599 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1600 DhcpSb
->LeaseLife
++;
1603 // Don't timeout the lease, only count the life if user is
1604 // requesting extra renew/rebind. Adjust the state after that.
1606 if (DhcpSb
->ExtraRefresh
!= 0) {
1610 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1612 // Lease expires, end the session
1616 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1618 // T2 expires, transit to rebinding then send a REQUEST to any server
1620 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1624 if (Instance
!= NULL
) {
1625 Instance
->ElaspedTime
= 0;
1628 Status
= DhcpSendMessage (
1636 if (EFI_ERROR (Status
)) {
1640 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1642 // T1 expires, transit to renewing, then send a REQUEST to the server
1644 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1648 if (Instance
!= NULL
) {
1649 Instance
->ElaspedTime
= 0;
1652 Status
= DhcpSendMessage (
1660 if (EFI_ERROR (Status
)) {
1667 if ((Instance
!= NULL
) && (Instance
->Token
!= NULL
)) {
1668 Instance
->Timeout
--;
1669 if (Instance
->Timeout
== 0) {
1670 PxeDhcpDone (Instance
);
1677 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);