3 Copyright (c) 2006 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 EFI DHCP protocol implementation
24 #include "Dhcp4Impl.h"
26 UINT32 mDhcp4DefaultTimeout
[4] = { 4, 8, 16, 32 };
30 Send an initial DISCOVER or REQUEST message according to the
31 DHCP service's current state.
33 @param DhcpSb The DHCP service instance
35 @retval EFI_SUCCESS The request has been sent
40 IN DHCP_SERVICE
*DhcpSb
45 ASSERT ((DhcpSb
->DhcpState
== Dhcp4Init
) || (DhcpSb
->DhcpState
== Dhcp4InitReboot
));
47 if (DhcpSb
->DhcpState
== Dhcp4Init
) {
48 DhcpSetState (DhcpSb
, Dhcp4Selecting
, FALSE
);
49 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_DISCOVER
, NULL
);
51 if (EFI_ERROR (Status
)) {
52 DhcpSb
->DhcpState
= Dhcp4Init
;
56 DhcpSb
->WaitOffer
= DHCP_WAIT_OFFER
;
58 DhcpSetState (DhcpSb
, Dhcp4Rebooting
, FALSE
);
59 Status
= DhcpSendMessage (DhcpSb
, NULL
, NULL
, DHCP_MSG_REQUEST
, NULL
);
61 if (EFI_ERROR (Status
)) {
62 DhcpSb
->DhcpState
= Dhcp4InitReboot
;
72 Call user provided callback function, and return the value the
73 function returns. If the user doesn't provide a callback, a
74 proper return value is selected to let the caller continue the
77 @param DhcpSb The DHCP service instance
78 @param Event The event as defined in the spec
79 @param Packet The current packet trigger the event
80 @param NewPacket The user's return new packet
82 @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
83 @retval EFI_SUCCESS The user function returns success.
84 @retval EFI_ABORTED The user function ask it to abort.
90 IN DHCP_SERVICE
*DhcpSb
,
91 IN EFI_DHCP4_EVENT Event
,
92 IN EFI_DHCP4_PACKET
*Packet
, OPTIONAL
93 OUT EFI_DHCP4_PACKET
**NewPacket OPTIONAL
96 EFI_DHCP4_CONFIG_DATA
*Config
;
99 if (NewPacket
!= NULL
) {
104 // If user doesn't provide the call back function, return the value
105 // that directs the client to continue the normal process.
106 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
107 // the offers and select a offer, EFI_NOT_READY tells the client to
108 // collect more offers.
110 Config
= &DhcpSb
->ActiveConfig
;
112 if (Config
->Dhcp4Callback
== NULL
) {
113 if (Event
== Dhcp4RcvdOffer
) {
114 return EFI_NOT_READY
;
120 Status
= Config
->Dhcp4Callback (
121 &DhcpSb
->ActiveChild
->Dhcp4Protocol
,
122 Config
->CallbackContext
,
130 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
131 // and EFI_ABORTED. If it returns values other than those, assume
132 // it to be EFI_ABORTED.
134 if ((Status
== EFI_SUCCESS
) || (Status
== EFI_NOT_READY
)) {
143 Notify the user about the operation result.
145 @param DhcpSb DHCP service instance
146 @param Which which notify function to signal
153 IN DHCP_SERVICE
*DhcpSb
,
157 DHCP_PROTOCOL
*Child
;
159 if ((Child
= DhcpSb
->ActiveChild
) == NULL
) {
163 if ((Child
->CompletionEvent
!= NULL
) &&
164 ((Which
== DHCP_NOTIFY_COMPLETION
) || (Which
== DHCP_NOTIFY_ALL
))) {
166 gBS
->SignalEvent (Child
->CompletionEvent
);
167 Child
->CompletionEvent
= NULL
;
170 if ((Child
->RenewRebindEvent
!= NULL
) &&
171 ((Which
== DHCP_NOTIFY_RENEWREBIND
) || (Which
== DHCP_NOTIFY_ALL
))) {
173 gBS
->SignalEvent (Child
->RenewRebindEvent
);
174 Child
->RenewRebindEvent
= NULL
;
181 Set the DHCP state. If CallUser is true, it will try to notify
182 the user before change the state by DhcpNotifyUser. It returns
183 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
184 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
185 the return value of this function.
187 @param DhcpSb The DHCP service instance
188 @param State The new DHCP state to change to
189 @param CallUser Whether we need to call user
191 @retval EFI_SUCCESS The state is changed
192 @retval EFI_ABORTED The user asks to abort the DHCP process.
197 IN DHCP_SERVICE
*DhcpSb
,
205 Status
= EFI_SUCCESS
;
207 if (State
== Dhcp4Renewing
) {
208 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRenewing
, NULL
, NULL
);
210 } else if (State
== Dhcp4Rebinding
) {
211 Status
= DhcpCallUser (DhcpSb
, Dhcp4EnterRebinding
, NULL
, NULL
);
213 } else if (State
== Dhcp4Bound
) {
214 Status
= DhcpCallUser (DhcpSb
, Dhcp4BoundCompleted
, NULL
, NULL
);
218 if (EFI_ERROR (Status
)) {
224 // Update the retransmission timer during the state transition.
225 // This will clear the retry count. This is also why the rule
226 // first transit the state, then send packets.
228 if (State
== Dhcp4Init
) {
229 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.DiscoverTryCount
;
231 DhcpSb
->MaxRetries
= DhcpSb
->ActiveConfig
.RequestTryCount
;
234 if (DhcpSb
->MaxRetries
== 0) {
235 DhcpSb
->MaxRetries
= 4;
238 DhcpSb
->CurRetry
= 0;
239 DhcpSb
->PacketToLive
= 0;
241 DhcpSb
->DhcpState
= State
;
247 Set the retransmit timer for the packet. It will select from either
248 the discover timeouts/request timeouts or the default timeout values.
250 @param DhcpSb The DHCP service instance.
257 DhcpSetTransmitTimer (
258 IN DHCP_SERVICE
*DhcpSb
263 ASSERT (DhcpSb
->MaxRetries
> DhcpSb
->CurRetry
);
265 if (DhcpSb
->DhcpState
== Dhcp4Init
) {
266 Times
= DhcpSb
->ActiveConfig
.DiscoverTimeout
;
268 Times
= DhcpSb
->ActiveConfig
.RequestTimeout
;
272 Times
= mDhcp4DefaultTimeout
;
275 DhcpSb
->PacketToLive
= Times
[DhcpSb
->CurRetry
];
280 Compute the lease. If the server grants a permanent lease, just
281 process it as a normal timeout value since the lease will last
284 @param DhcpSb The DHCP service instance
285 @param Para The DHCP parameter extracted from the server's
294 IN DHCP_SERVICE
*DhcpSb
,
295 IN DHCP_PARAMETER
*Para
298 ASSERT (Para
!= NULL
);
300 DhcpSb
->Lease
= Para
->Lease
;
301 DhcpSb
->T2
= Para
->T2
;
302 DhcpSb
->T1
= Para
->T1
;
304 if (DhcpSb
->Lease
== 0) {
305 DhcpSb
->Lease
= DHCP_DEFAULT_LEASE
;
308 if ((DhcpSb
->T2
== 0) || (DhcpSb
->T2
>= Para
->Lease
)) {
309 DhcpSb
->T2
= Para
->Lease
- (Para
->Lease
>> 3);
312 if ((DhcpSb
->T1
== 0) || (DhcpSb
->T1
>= Para
->T2
)) {
313 DhcpSb
->T1
= DhcpSb
->Lease
>> 1;
319 Configure a UDP IO port to use the acquired lease address.
320 DHCP driver needs this port to unicast packet to the server
321 such as DHCP release.
323 @param UdpIo The UDP IO port to configure
324 @param Context The opaque parameter to the function.
326 @retval EFI_SUCCESS The UDP IO port is successfully configured.
327 @retval Others It failed to configure the port.
331 DhcpConfigLeaseIoPort (
332 IN UDP_IO_PORT
*UdpIo
,
336 EFI_UDP4_CONFIG_DATA UdpConfigData
;
337 EFI_IPv4_ADDRESS Subnet
;
338 EFI_IPv4_ADDRESS Gateway
;
339 DHCP_SERVICE
*DhcpSb
;
343 DhcpSb
= (DHCP_SERVICE
*) Context
;
345 UdpConfigData
.AcceptBroadcast
= FALSE
;
346 UdpConfigData
.AcceptPromiscuous
= FALSE
;
347 UdpConfigData
.AcceptAnyPort
= FALSE
;
348 UdpConfigData
.AllowDuplicatePort
= TRUE
;
349 UdpConfigData
.TypeOfService
= 0;
350 UdpConfigData
.TimeToLive
= 64;
351 UdpConfigData
.DoNotFragment
= FALSE
;
352 UdpConfigData
.ReceiveTimeout
= 1;
353 UdpConfigData
.TransmitTimeout
= 0;
355 UdpConfigData
.UseDefaultAddress
= FALSE
;
356 UdpConfigData
.StationPort
= DHCP_CLIENT_PORT
;
357 UdpConfigData
.RemotePort
= DHCP_SERVER_PORT
;
359 Ip
= HTONL (DhcpSb
->ClientAddr
);
360 NetCopyMem (&UdpConfigData
.StationAddress
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
362 Ip
= HTONL (DhcpSb
->Netmask
);
363 NetCopyMem (&UdpConfigData
.SubnetMask
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
365 NetZeroMem (&UdpConfigData
.RemoteAddress
, sizeof (EFI_IPv4_ADDRESS
));
367 Status
= UdpIo
->Udp
->Configure (UdpIo
->Udp
, &UdpConfigData
);
369 if (EFI_ERROR (Status
)) {
374 // Add a default route if received from the server.
376 if ((DhcpSb
->Para
!= NULL
) && (DhcpSb
->Para
->Router
!= 0)) {
377 NetZeroMem (&Subnet
, sizeof (EFI_IPv4_ADDRESS
));
379 Ip
= HTONL (DhcpSb
->Para
->Router
);
380 NetCopyMem (&Gateway
, &Ip
, sizeof (EFI_IPv4_ADDRESS
));
382 UdpIo
->Udp
->Routes (UdpIo
->Udp
, FALSE
, &Subnet
, &Subnet
, &Gateway
);
390 Update the lease states when a new lease is acquired. It will not only
391 save the acquired the address and lease time, it will also create a UDP
392 child to provide address resolution for the address.
394 @param DhcpSb The DHCP service instance
396 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
397 @retval EFI_SUCCESS The lease is recorded.
403 IN DHCP_SERVICE
*DhcpSb
408 DhcpSb
->ClientAddr
= EFI_NTOHL (DhcpSb
->Selected
->Dhcp4
.Header
.YourAddr
);
410 if (DhcpSb
->Para
!= NULL
) {
411 DhcpSb
->Netmask
= DhcpSb
->Para
->NetMask
;
412 DhcpSb
->ServerAddr
= DhcpSb
->Para
->ServerId
;
415 if (DhcpSb
->Netmask
== 0) {
416 Class
= NetGetIpClass (DhcpSb
->ClientAddr
);
417 DhcpSb
->Netmask
= mIp4AllMasks
[Class
<< 3];
420 if (DhcpSb
->LeaseIoPort
!= NULL
) {
421 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
425 // Create a UDP/IP child to provide ARP service for the Leased IP,
426 // and transmit unicast packet with it as source address. Don't
427 // start receive on this port, the queued packet will be timeout.
429 DhcpSb
->LeaseIoPort
= UdpIoCreatePort (
432 DhcpConfigLeaseIoPort
,
436 if (DhcpSb
->LeaseIoPort
== NULL
) {
437 return EFI_OUT_OF_RESOURCES
;
440 if (!DHCP_IS_BOOTP (DhcpSb
->Para
)) {
441 DhcpComputeLease (DhcpSb
, DhcpSb
->Para
);
444 return DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
449 Clean up the DHCP related states, IoStatus isn't reset.
451 @param DhcpSb The DHCP instance service.
458 IN DHCP_SERVICE
*DhcpSb
461 DhcpSb
->DhcpState
= Dhcp4Init
;
462 DhcpSb
->Xid
= DhcpSb
->Xid
+ 1;
463 DhcpSb
->ClientAddr
= 0;
464 DhcpSb
->ServerAddr
= 0;
466 if (DhcpSb
->LastOffer
!= NULL
) {
467 NetFreePool (DhcpSb
->LastOffer
);
468 DhcpSb
->LastOffer
= NULL
;
471 if (DhcpSb
->Selected
!= NULL
) {
472 NetFreePool (DhcpSb
->Selected
);
473 DhcpSb
->Selected
= NULL
;
476 if (DhcpSb
->Para
!= NULL
) {
477 NetFreePool (DhcpSb
->Para
);
484 DhcpSb
->ExtraRefresh
= FALSE
;
486 if (DhcpSb
->LeaseIoPort
!= NULL
) {
487 UdpIoFreePort (DhcpSb
->LeaseIoPort
);
488 DhcpSb
->LeaseIoPort
= NULL
;
491 if (DhcpSb
->LastPacket
!= NULL
) {
492 NetbufFree (DhcpSb
->LastPacket
);
493 DhcpSb
->LastPacket
= NULL
;
496 DhcpSb
->PacketToLive
= 0;
497 DhcpSb
->CurRetry
= 0;
498 DhcpSb
->MaxRetries
= 0;
499 DhcpSb
->WaitOffer
= 0;
500 DhcpSb
->LeaseLife
= 0;
505 Select a offer among all the offers collected. If the offer selected is
506 of BOOTP, the lease is recorded and user notified. If the offer is of
507 DHCP, it will request the offer from the server.
509 @param DhcpSb The DHCP service instance.
511 @retval EFI_SUCCESS One of the offer is selected.
517 IN DHCP_SERVICE
*DhcpSb
520 EFI_DHCP4_PACKET
*Selected
;
521 EFI_DHCP4_PACKET
*NewPacket
;
524 ASSERT (DhcpSb
->LastOffer
!= NULL
);
527 // Stop waiting more offers
529 DhcpSb
->WaitOffer
= 0;
532 // User will cache previous offers if he wants to select
533 // from multiple offers. If user provides an invalid packet,
534 // use the last offer, otherwise use the provided packet.
537 Status
= DhcpCallUser (DhcpSb
, Dhcp4SelectOffer
, DhcpSb
->LastOffer
, &NewPacket
);
539 if (EFI_ERROR (Status
)) {
543 Selected
= DhcpSb
->LastOffer
;
545 if (NewPacket
!= NULL
) {
546 if (EFI_ERROR (DhcpValidateOptions (NewPacket
, NULL
))) {
547 NetFreePool (NewPacket
);
549 NetFreePool (Selected
);
550 Selected
= NewPacket
;
554 DhcpSb
->Selected
= Selected
;
555 DhcpSb
->LastOffer
= NULL
;
557 DhcpValidateOptions (Selected
, &DhcpSb
->Para
);
560 // A bootp offer has been selected, save the lease status,
561 // enter bound state then notify the user.
563 if (DHCP_IS_BOOTP (DhcpSb
->Para
)) {
564 Status
= DhcpLeaseAcquired (DhcpSb
);
566 if (EFI_ERROR (Status
)) {
570 DhcpSb
->IoStatus
= EFI_SUCCESS
;
571 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
576 // Send a DHCP requests
578 Status
= DhcpSetState (DhcpSb
, Dhcp4Requesting
, TRUE
);
580 if (EFI_ERROR (Status
)) {
584 return DhcpSendMessage (DhcpSb
, Selected
, DhcpSb
->Para
, DHCP_MSG_REQUEST
, NULL
);
589 Terminate the current address acquire. All the allocated resources
590 are released. Be careful when calling this function. A rule related
591 to this is: only call DhcpEndSession at the highest level, such as
592 DhcpInput, DhcpOnTimerTick...At the other level, just return error.
594 @param DhcpSb The DHCP service instance
595 @param Status The result of the DHCP process.
603 IN DHCP_SERVICE
*DhcpSb
,
607 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
608 DhcpCallUser (DhcpSb
, Dhcp4AddressLost
, NULL
, NULL
);
610 DhcpCallUser (DhcpSb
, Dhcp4Fail
, NULL
, NULL
);
613 DhcpCleanLease (DhcpSb
);
615 DhcpSb
->IoStatus
= Status
;
616 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_ALL
);
621 Handle packets in DHCP select state.
623 @param DhcpSb The DHCP service instance
624 @param Packet The DHCP packet received
625 @param Para The DHCP parameter extracted from the packet. That
626 is, all the option value that we care.
628 @retval EFI_SUCCESS The packet is successfully processed.
629 @retval Others Some error occured.
635 IN DHCP_SERVICE
*DhcpSb
,
636 IN EFI_DHCP4_PACKET
*Packet
,
637 IN DHCP_PARAMETER
*Para
640 EFI_DHCP4_HEADER
*Head
;
643 Status
= EFI_SUCCESS
;
646 // First validate the message:
647 // 1. the offer is a unicast
648 // 2. if it is a DHCP message, it must contains a server ID.
649 // Don't return a error for these two case otherwise the session is ended.
651 Head
= &Packet
->Dhcp4
.Header
;
653 if (!Ip4IsUnicast (EFI_NTOHL (Head
->YourAddr
), (Para
== NULL
? 0 : Para
->NetMask
))) {
657 if (!DHCP_IS_BOOTP (Para
) &&
658 ((Para
->DhcpType
!= DHCP_MSG_OFFER
) || (Para
->ServerId
== 0))) {
663 // Call the user's callback. The action according to the return is as:
664 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
665 // 2. EFI_NOT_READY: wait for more offers
666 // 3. EFI_ABORTED: abort the address acquiring.
668 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdOffer
, Packet
, NULL
);
670 if (Status
== EFI_SUCCESS
) {
671 if (DhcpSb
->LastOffer
!= NULL
) {
672 NetFreePool (DhcpSb
->LastOffer
);
675 DhcpSb
->LastOffer
= Packet
;
677 return DhcpChooseOffer (DhcpSb
);
679 } else if (Status
== EFI_NOT_READY
) {
680 if (DhcpSb
->LastOffer
!= NULL
) {
681 NetFreePool (DhcpSb
->LastOffer
);
684 DhcpSb
->LastOffer
= Packet
;
686 } else if (Status
== EFI_ABORTED
) {
688 // DhcpInput will end the session upon error return. Remember
689 // only to call DhcpEndSession at the top level call.
697 NetFreePool (Packet
);
703 Handle packets in DHCP request state.
705 @param DhcpSb The DHCP service instance
706 @param Packet The DHCP packet received
707 @param Para The DHCP parameter extracted from the packet. That
708 is, all the option value that we care.
710 @retval EFI_SUCCESS The packet is successfully processed.
711 @retval Others Some error occured.
717 IN DHCP_SERVICE
*DhcpSb
,
718 IN EFI_DHCP4_PACKET
*Packet
,
719 IN DHCP_PARAMETER
*Para
722 EFI_DHCP4_HEADER
*Head
;
723 EFI_DHCP4_HEADER
*Selected
;
727 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
729 Head
= &Packet
->Dhcp4
.Header
;
730 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
733 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
735 if (DHCP_IS_BOOTP (Para
) ||
736 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
737 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
739 Status
= EFI_SUCCESS
;
744 // Received a NAK, end the session no matter what the user returns
746 Status
= EFI_DEVICE_ERROR
;
748 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
749 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
754 // Check whether the ACK matches the selected offer
758 if (!EFI_IP4_EQUAL (Head
->YourAddr
, Selected
->YourAddr
)) {
759 Message
= "Lease confirmed isn't the same as that in the offer";
763 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
765 if (EFI_ERROR (Status
)) {
766 Message
= "Lease is denied upon received ACK";
771 // Record the lease, transit to BOUND state, then notify the user
773 Status
= DhcpLeaseAcquired (DhcpSb
);
775 if (EFI_ERROR (Status
)) {
776 Message
= "Lease is denied upon entering bound";
780 DhcpSb
->IoStatus
= EFI_SUCCESS
;
781 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
783 NetFreePool (Packet
);
787 DhcpSendMessage (DhcpSb
, DhcpSb
->Selected
, DhcpSb
->Para
, DHCP_MSG_DECLINE
, Message
);
790 NetFreePool (Packet
);
796 Handle packets in DHCP renew/rebound state.
798 @param DhcpSb The DHCP service instance
799 @param Packet The DHCP packet received
800 @param Para The DHCP parameter extracted from the packet. That
801 is, all the option value that we care.
803 @retval EFI_SUCCESS The packet is successfully processed.
804 @retval Others Some error occured.
809 DhcpHandleRenewRebind (
810 IN DHCP_SERVICE
*DhcpSb
,
811 IN EFI_DHCP4_PACKET
*Packet
,
812 IN DHCP_PARAMETER
*Para
815 EFI_DHCP4_HEADER
*Head
;
816 EFI_DHCP4_HEADER
*Selected
;
819 ASSERT (!DHCP_IS_BOOTP (DhcpSb
->Para
));
821 Head
= &Packet
->Dhcp4
.Header
;
822 Selected
= &DhcpSb
->Selected
->Dhcp4
.Header
;
825 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
827 if (DHCP_IS_BOOTP (Para
) ||
828 (Para
->ServerId
!= DhcpSb
->Para
->ServerId
) ||
829 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
831 Status
= EFI_SUCCESS
;
836 // Received a NAK, ignore the user's return then terminate the process
838 Status
= EFI_DEVICE_ERROR
;
840 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
841 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
846 // The lease is different from the selected. Don't send a DECLINE
847 // since it isn't existed in the client's FSM.
849 if (!EFI_IP4_EQUAL (Head
->YourAddr
, Selected
->YourAddr
)) {
853 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
855 if (EFI_ERROR (Status
)) {
860 // Record the lease, start timer for T1 and T2,
862 DhcpComputeLease (DhcpSb
, Para
);
863 DhcpSb
->LeaseLife
= 0;
864 DhcpSetState (DhcpSb
, Dhcp4Bound
, TRUE
);
866 if (DhcpSb
->ExtraRefresh
) {
867 DhcpSb
->ExtraRefresh
= FALSE
;
869 DhcpSb
->IoStatus
= EFI_SUCCESS
;
870 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
874 NetFreePool (Packet
);
880 Handle packets in DHCP reboot state.
882 @param DhcpSb The DHCP service instance
883 @param Packet The DHCP packet received
884 @param Para The DHCP parameter extracted from the packet. That
885 is, all the option value that we care.
887 @retval EFI_SUCCESS The packet is successfully processed.
888 @retval Others Some error occured.
894 IN DHCP_SERVICE
*DhcpSb
,
895 IN EFI_DHCP4_PACKET
*Packet
,
896 IN DHCP_PARAMETER
*Para
899 EFI_DHCP4_HEADER
*Head
;
902 Head
= &Packet
->Dhcp4
.Header
;
905 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
907 if (DHCP_IS_BOOTP (Para
) ||
908 ((Para
->DhcpType
!= DHCP_MSG_ACK
) && (Para
->DhcpType
!= DHCP_MSG_NAK
))) {
910 Status
= EFI_SUCCESS
;
915 // If a NAK is received, transit to INIT and try again.
917 if (Para
->DhcpType
== DHCP_MSG_NAK
) {
918 DhcpCallUser (DhcpSb
, Dhcp4RcvdNak
, Packet
, NULL
);
920 DhcpSb
->ClientAddr
= 0;
921 DhcpSb
->DhcpState
= Dhcp4Init
;
923 Status
= DhcpInitRequest (DhcpSb
);
928 // Check whether the ACK matches the selected offer
930 if (EFI_NTOHL (Head
->YourAddr
) != DhcpSb
->ClientAddr
) {
931 Status
= EFI_DEVICE_ERROR
;
935 Status
= DhcpCallUser (DhcpSb
, Dhcp4RcvdAck
, Packet
, NULL
);
936 if (EFI_ERROR (Status
)) {
941 // OK, get the parameter from server, record the lease
943 DhcpSb
->Para
= NetAllocatePool (sizeof (DHCP_PARAMETER
));
945 if (DhcpSb
->Para
== NULL
) {
946 Status
= EFI_OUT_OF_RESOURCES
;
950 DhcpSb
->Selected
= Packet
;
951 CopyMem (DhcpSb
->Para
, Para
, sizeof (DHCP_PARAMETER
));
953 Status
= DhcpLeaseAcquired (DhcpSb
);
955 if (EFI_ERROR (Status
)) {
959 DhcpSb
->IoStatus
= EFI_SUCCESS
;
960 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_COMPLETION
);
964 NetFreePool (Packet
);
970 Handle the received DHCP packets. This function drivers the DHCP
973 @param UdpPacket The UDP packets received.
974 @param Points The local/remote UDP access points
975 @param IoStatus The status of the UDP receive
976 @param Context The opaque parameter to the function.
989 DHCP_SERVICE
*DhcpSb
;
990 EFI_DHCP4_HEADER
*Head
;
991 EFI_DHCP4_PACKET
*Packet
;
992 DHCP_PARAMETER
*Para
;
997 DhcpSb
= (DHCP_SERVICE
*) Context
;
1000 // Don't restart receive if error occurs or DHCP is destoried.
1002 if (EFI_ERROR (IoStatus
)) {
1004 } else if (DhcpSb
->ServiceState
== DHCP_DESTORY
) {
1005 NetbufFree (UdpPacket
);
1009 ASSERT (UdpPacket
!= NULL
);
1011 if (DhcpSb
->DhcpState
== Dhcp4Stopped
) {
1016 // Validate the packet received
1018 if (UdpPacket
->TotalSize
< sizeof (EFI_DHCP4_HEADER
)) {
1023 // Copy the DHCP message to a continuous memory block
1025 Len
= sizeof (EFI_DHCP4_PACKET
) + UdpPacket
->TotalSize
- sizeof (EFI_DHCP4_HEADER
);
1026 Packet
= (EFI_DHCP4_PACKET
*) NetAllocatePool (Len
);
1028 if (Packet
== NULL
) {
1033 Head
= &Packet
->Dhcp4
.Header
;
1034 Packet
->Length
= NetbufCopy (UdpPacket
, 0, UdpPacket
->TotalSize
, (UINT8
*) Head
);
1036 if (Packet
->Length
!= UdpPacket
->TotalSize
) {
1041 // Is this packet the answer to our packet?
1043 if ((Head
->OpCode
!= BOOTP_REPLY
) ||
1044 (NTOHL (Head
->Xid
) != DhcpSb
->Xid
) ||
1045 !NET_MAC_EQUAL (&DhcpSb
->Mac
, Head
->ClientHwAddr
, DhcpSb
->HwLen
)) {
1050 // Validate the options and retrieve the interested options
1053 if ((Packet
->Length
> sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
)) &&
1054 (Packet
->Dhcp4
.Magik
== DHCP_OPTION_MAGIC
) &&
1055 EFI_ERROR (DhcpValidateOptions (Packet
, &Para
))) {
1061 // Call the handler for each state. The handler should return
1062 // EFI_SUCCESS if the process can go on no matter whether the
1063 // packet is ignored or not. If the return is EFI_ERROR, the
1064 // session will be terminated. Packet's ownership is handled
1065 // over to the handlers. If operation succeeds, the handler
1066 // must notify the user. It isn't necessary to do if EFI_ERROR
1067 // is returned because the DhcpEndSession will notify the user.
1069 Status
= EFI_SUCCESS
;
1071 switch (DhcpSb
->DhcpState
) {
1072 case Dhcp4Selecting
:
1073 Status
= DhcpHandleSelect (DhcpSb
, Packet
, Para
);
1076 case Dhcp4Requesting
:
1077 Status
= DhcpHandleRequest (DhcpSb
, Packet
, Para
);
1080 case Dhcp4InitReboot
:
1084 // Ignore the packet in INITREBOOT, INIT and BOUND states
1086 NetFreePool (Packet
);
1087 Status
= EFI_SUCCESS
;
1091 case Dhcp4Rebinding
:
1092 Status
= DhcpHandleRenewRebind (DhcpSb
, Packet
, Para
);
1095 case Dhcp4Rebooting
:
1096 Status
= DhcpHandleReboot (DhcpSb
, Packet
, Para
);
1106 if (EFI_ERROR (Status
)) {
1107 NetbufFree (UdpPacket
);
1108 DhcpEndSession (DhcpSb
, Status
);
1113 NetbufFree (UdpPacket
);
1115 if (Packet
!= NULL
) {
1116 NetFreePool (Packet
);
1119 Status
= UdpIoRecvDatagram (DhcpSb
->UdpIo
, DhcpInput
, DhcpSb
, 0);
1121 if (EFI_ERROR (Status
)) {
1122 DhcpEndSession (DhcpSb
, EFI_DEVICE_ERROR
);
1130 @param Arg The packet to release
1145 Release the net buffer when packet is sent.
1147 @param UdpPacket The UDP packets received.
1148 @param Points The local/remote UDP access points
1149 @param IoStatus The status of the UDP receive
1150 @param Context The opaque parameter to the function.
1159 EFI_STATUS IoStatus
,
1163 NetbufFree (Packet
);
1169 Build and transmit a DHCP message according to the current states.
1170 This function implement the Table 5. of RFC 2131. Always transits
1171 the state (as defined in Figure 5. of the same RFC) before sending
1172 a DHCP message. The table is adjusted accordingly.
1174 @param DhcpSb The DHCP service instance
1175 @param Seed The seed packet which the new packet is based on
1176 @param Para The DHCP parameter of the Seed packet
1177 @param Type The message type to send
1178 @param Msg The human readable message to include in the packet
1181 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet
1182 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP
1183 @retval EFI_SUCCESS The message is sent
1188 IN DHCP_SERVICE
*DhcpSb
,
1189 IN EFI_DHCP4_PACKET
*Seed
,
1190 IN DHCP_PARAMETER
*Para
,
1195 EFI_DHCP4_CONFIG_DATA
*Config
;
1196 EFI_DHCP4_PACKET
*Packet
;
1197 EFI_DHCP4_PACKET
*NewPacket
;
1198 EFI_DHCP4_HEADER
*Head
;
1199 EFI_DHCP4_HEADER
*SeedHead
;
1201 UDP_POINTS EndPoint
;
1212 // Allocate a big enough memory block to hold the DHCP packet
1214 Len
= sizeof (EFI_DHCP4_PACKET
) + 128 + DhcpSb
->UserOptionLen
;
1217 Len
+= (UINT32
)AsciiStrLen (Msg
);
1220 Packet
= NetAllocatePool (Len
);
1222 if (Packet
== NULL
) {
1223 return EFI_OUT_OF_RESOURCES
;
1227 Packet
->Length
= sizeof (EFI_DHCP4_HEADER
) + sizeof (UINT32
);
1230 // Fill in the DHCP header fields
1232 Config
= &DhcpSb
->ActiveConfig
;
1236 SeedHead
= &Seed
->Dhcp4
.Header
;
1239 Head
= &Packet
->Dhcp4
.Header
;
1240 NetZeroMem (Head
, sizeof (EFI_DHCP4_HEADER
));
1242 Head
->OpCode
= BOOTP_REQUEST
;
1243 Head
->HwType
= DhcpSb
->HwType
;
1244 Head
->HwAddrLen
= DhcpSb
->HwLen
;
1245 Head
->Xid
= HTONL (DhcpSb
->Xid
);
1246 Head
->Reserved
= HTONS (0x8000); //Server, broadcast the message please.
1248 EFI_IP4 (Head
->ClientAddr
) = HTONL (DhcpSb
->ClientAddr
);
1249 NetCopyMem (Head
->ClientHwAddr
, DhcpSb
->Mac
.Addr
, DhcpSb
->HwLen
);
1252 // Append the DHCP message type
1254 Packet
->Dhcp4
.Magik
= DHCP_OPTION_MAGIC
;
1255 Buf
= Packet
->Dhcp4
.Option
;
1256 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_TYPE
, 1, &Type
);
1259 // Append the serverid option if necessary:
1260 // 1. DHCP decline message
1261 // 2. DHCP release message
1262 // 3. DHCP request to confirm one lease.
1264 if ((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
) ||
1265 ((Type
== DHCP_MSG_REQUEST
) && (DhcpSb
->DhcpState
== Dhcp4Requesting
))) {
1267 ASSERT ((Para
!= NULL
) && (Para
->ServerId
!= 0));
1269 IpAddr
= HTONL (Para
->ServerId
);
1270 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_SERVER_ID
, 4, (UINT8
*) &IpAddr
);
1274 // Append the requested IP option if necessary:
1275 // 1. DHCP request to use the previously allocated address
1276 // 2. DHCP request to confirm one lease
1277 // 3. DHCP decline to decline one lease
1281 if (Type
== DHCP_MSG_REQUEST
) {
1282 if (DhcpSb
->DhcpState
== Dhcp4Rebooting
) {
1283 IpAddr
= EFI_IP4 (Config
->ClientAddress
);
1285 } else if (DhcpSb
->DhcpState
== Dhcp4Requesting
) {
1286 ASSERT (SeedHead
!= NULL
);
1287 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1290 } else if (Type
== DHCP_MSG_DECLINE
) {
1291 ASSERT (SeedHead
!= NULL
);
1292 IpAddr
= EFI_IP4 (SeedHead
->YourAddr
);
1296 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_REQUEST_IP
, 4, (UINT8
*) &IpAddr
);
1300 // Append the Max Message Length option if it isn't a DECLINE
1301 // or RELEASE to direct the server use large messages instead of
1302 // override the BOOTFILE and SERVER fields in the message head.
1304 if ((Type
!= DHCP_MSG_DECLINE
) && (Type
!= DHCP_MSG_RELEASE
)) {
1305 MaxMsg
= HTONS (0xFF00);
1306 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MAXMSG
, 2, (UINT8
*) &MaxMsg
);
1310 // Append the user's message if it isn't NULL
1313 Len
= NET_MIN ((UINT32
) AsciiStrLen (Msg
), 255);
1314 Buf
= DhcpAppendOption (Buf
, DHCP_TAG_MESSAGE
, (UINT16
) Len
, Msg
);
1318 // Append the user configured options
1320 if (DhcpSb
->UserOptionLen
!= 0) {
1321 for (Index
= 0; Index
< Config
->OptionCount
; Index
++) {
1323 // We can't use any option other than the client ID from user
1324 // if it is a DHCP decline or DHCP release .
1326 if (((Type
== DHCP_MSG_DECLINE
) || (Type
== DHCP_MSG_RELEASE
)) &&
1327 (Config
->OptionList
[Index
]->OpCode
!= DHCP_TAG_CLIENT_ID
)) {
1331 Buf
= DhcpAppendOption (
1333 Config
->OptionList
[Index
]->OpCode
,
1334 Config
->OptionList
[Index
]->Length
,
1335 Config
->OptionList
[Index
]->Data
1340 *(Buf
++) = DHCP_TAG_EOP
;
1341 Packet
->Length
+= (UINT32
) (Buf
- Packet
->Dhcp4
.Option
);
1344 // OK, the message is built, call the user to override it.
1346 Status
= EFI_SUCCESS
;
1349 if (Type
== DHCP_MSG_DISCOVER
) {
1350 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDiscover
, Packet
, &NewPacket
);
1352 } else if (Type
== DHCP_MSG_REQUEST
) {
1353 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendRequest
, Packet
, &NewPacket
);
1355 } else if (Type
== DHCP_MSG_DECLINE
) {
1356 Status
= DhcpCallUser (DhcpSb
, Dhcp4SendDecline
, Packet
, &NewPacket
);
1359 if (EFI_ERROR (Status
)) {
1360 NetFreePool (Packet
);
1364 if (NewPacket
!= NULL
) {
1365 NetFreePool (Packet
);
1370 // Wrap it into a netbuf then send it.
1372 Frag
.Bulk
= (UINT8
*) &Packet
->Dhcp4
.Header
;
1373 Frag
.Len
= Packet
->Length
;
1374 Wrap
= NetbufFromExt (&Frag
, 1, 0, 0, DhcpReleasePacket
, Packet
);
1377 NetFreePool (Packet
);
1378 return EFI_OUT_OF_RESOURCES
;
1382 // Save it as the last sent packet for retransmission
1384 if (DhcpSb
->LastPacket
!= NULL
) {
1385 NetbufFree (DhcpSb
->LastPacket
);
1389 DhcpSb
->LastPacket
= Wrap
;
1390 DhcpSetTransmitTimer (DhcpSb
);
1393 // Broadcast the message, unless we know the server address.
1394 // Use the lease UdpIo port to send the unicast packet.
1396 EndPoint
.RemoteAddr
= 0xffffffff;
1397 EndPoint
.LocalAddr
= 0;
1398 EndPoint
.RemotePort
= DHCP_SERVER_PORT
;
1399 EndPoint
.LocalPort
= DHCP_CLIENT_PORT
;
1400 UdpIo
= DhcpSb
->UdpIo
;
1402 if ((DhcpSb
->DhcpState
== Dhcp4Renewing
) || (Type
== DHCP_MSG_RELEASE
)) {
1403 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1404 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1405 UdpIo
= DhcpSb
->LeaseIoPort
;
1408 ASSERT (UdpIo
!= NULL
);
1409 Status
= UdpIoSendDatagram (UdpIo
, Wrap
, &EndPoint
, 0, DhcpOnPacketSent
, DhcpSb
);
1411 if (EFI_ERROR (Status
)) {
1413 return EFI_ACCESS_DENIED
;
1421 Retransmit a saved packet. Only DISCOVER and REQUEST messages
1422 will be retransmitted.
1424 @param DhcpSb The DHCP service instance
1426 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port
1427 @retval EFI_SUCCESS The packet is retransmitted.
1432 IN DHCP_SERVICE
*DhcpSb
1436 UDP_POINTS EndPoint
;
1439 ASSERT (DhcpSb
->LastPacket
!= NULL
);
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
= 0xffffffff;
1447 EndPoint
.LocalAddr
= 0;
1448 UdpIo
= DhcpSb
->UdpIo
;
1450 if (DhcpSb
->DhcpState
== Dhcp4Renewing
) {
1451 EndPoint
.RemoteAddr
= DhcpSb
->ServerAddr
;
1452 EndPoint
.LocalAddr
= DhcpSb
->ClientAddr
;
1453 UdpIo
= DhcpSb
->LeaseIoPort
;
1456 ASSERT (UdpIo
!= NULL
);
1458 NET_GET_REF (DhcpSb
->LastPacket
);
1459 Status
= UdpIoSendDatagram (
1468 if (EFI_ERROR (Status
)) {
1469 NET_PUT_REF (DhcpSb
->LastPacket
);
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 Event The timer event
1485 @param Context The context, which is the DHCP service instance.
1497 DHCP_SERVICE
*DhcpSb
;
1500 DhcpSb
= (DHCP_SERVICE
*) Context
;
1503 // Check the retransmit timer first
1505 if ((DhcpSb
->PacketToLive
> 0) && (--DhcpSb
->PacketToLive
== 0)) {
1507 if (++DhcpSb
->CurRetry
< DhcpSb
->MaxRetries
) {
1509 // Still has another try
1511 DhcpRetransmit (DhcpSb
);
1512 DhcpSetTransmitTimer (DhcpSb
);
1515 if (!DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1520 // Retransmission failed, if the DHCP request is initiated by
1521 // user, adjust the current state according to the lease life.
1522 // Otherwise do nothing to wait the lease to timeout
1524 if (DhcpSb
->ExtraRefresh
) {
1525 Status
= EFI_SUCCESS
;
1527 if (DhcpSb
->LeaseLife
< DhcpSb
->T1
) {
1528 Status
= DhcpSetState (DhcpSb
, Dhcp4Bound
, FALSE
);
1530 } else if (DhcpSb
->LeaseLife
< DhcpSb
->T2
) {
1531 Status
= DhcpSetState (DhcpSb
, Dhcp4Renewing
, FALSE
);
1533 } else if (DhcpSb
->LeaseLife
< DhcpSb
->Lease
) {
1534 Status
= DhcpSetState (DhcpSb
, Dhcp4Rebinding
, FALSE
);
1541 DhcpSb
->IoStatus
= EFI_TIMEOUT
;
1542 DhcpNotifyUser (DhcpSb
, DHCP_NOTIFY_RENEWREBIND
);
1547 if ((DhcpSb
->WaitOffer
> 0) && (--DhcpSb
->WaitOffer
== 0)) {
1549 // OK, offer collection finished, select a offer
1551 ASSERT (DhcpSb
->DhcpState
== Dhcp4Selecting
);
1553 if (DhcpSb
->LastOffer
== NULL
) {
1557 if (EFI_ERROR (DhcpChooseOffer (DhcpSb
))) {
1563 // If an address has been acquired, check whether need to
1564 // refresh or whether it has expired.
1566 if (DHCP_CONNECTED (DhcpSb
->DhcpState
)) {
1567 DhcpSb
->LeaseLife
++;
1570 // Don't timeout the lease, only count the life if user is
1571 // requesting extra renew/rebind. Adjust the state after that.
1573 if (DhcpSb
->ExtraRefresh
) {
1577 if (DhcpSb
->LeaseLife
== DhcpSb
->Lease
) {
1579 // Lease expires, end the session
1583 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T2
) {
1585 // T2 expires, transit to rebinding then send a REQUEST to any server
1587 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Rebinding
, TRUE
))) {
1591 Status
= DhcpSendMessage (
1599 if (EFI_ERROR (Status
)) {
1603 } else if (DhcpSb
->LeaseLife
== DhcpSb
->T1
) {
1605 // T1 expires, transit to renewing, then send a REQUEST to the server
1607 if (EFI_ERROR (DhcpSetState (DhcpSb
, Dhcp4Renewing
, TRUE
))) {
1611 Status
= DhcpSendMessage (
1619 if (EFI_ERROR (Status
)) {
1628 DhcpEndSession (DhcpSb
, EFI_TIMEOUT
);